Root/src/gmenu2x.cpp

Source at commit 3d3e0fa created 8 years 1 month ago.
By Ayla, Read config file(s) present on both system and user-specific directories.
1/***************************************************************************
2 * Copyright (C) 2006 by Massimiliano Torromeo *
3 * massimiliano.torromeo@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21#include <iostream>
22#include <sstream>
23#include <fstream>
24#include <algorithm>
25#include <stdlib.h>
26#include <unistd.h>
27#include <math.h>
28#include <SDL.h>
29#include <SDL_gfxPrimitives.h>
30#include <signal.h>
31
32#include <sys/statvfs.h>
33#include <errno.h>
34
35#include "gp2x.h"
36#include <sys/fcntl.h> //for battery
37
38#ifdef PLATFORM_DINGUX
39# define UNLOCK_VT
40# include <sys/ioctl.h>
41# include <linux/vt.h>
42# include <linux/kd.h>
43#endif
44
45//for browsing the filesystem
46#include <sys/stat.h>
47#include <sys/types.h>
48#include <dirent.h>
49
50//for soundcard
51#include <sys/ioctl.h>
52#include <linux/soundcard.h>
53
54#include "linkapp.h"
55#include "linkaction.h"
56#include "menu.h"
57#include "asfont.h"
58#include "surface.h"
59#include "filedialog.h"
60#include "gmenu2x.h"
61#include "filelister.h"
62
63#include "iconbutton.h"
64#include "messagebox.h"
65#include "inputdialog.h"
66#include "settingsdialog.h"
67#include "wallpaperdialog.h"
68#include "textdialog.h"
69#include "menusettingint.h"
70#include "menusettingbool.h"
71#include "menusettingrgba.h"
72#include "menusettingstring.h"
73#include "menusettingmultistring.h"
74#include "menusettingfile.h"
75#include "menusettingimage.h"
76#include "menusettingdir.h"
77
78#include "debug.h"
79
80#include <sys/mman.h>
81
82#ifdef PLATFORM_PANDORA
83//#include <pnd_container.h>
84//#include <pnd_conf.h>
85//#include <pnd_discovery.h>
86#endif
87
88#ifdef _CARD_ROOT
89const char *CARD_ROOT = _CARD_ROOT;
90#else
91const char *CARD_ROOT = "/card/"; //Note: Add a trailing /!
92#endif
93const int CARD_ROOT_LEN = 5;
94
95static GMenu2X *app;
96static string gmenu2x_home;
97
98using namespace std;
99using namespace fastdelegate;
100
101// Note: Keep this in sync with the enum!
102static const char *colorNames[NUM_COLORS] = {
103    "topBarBg",
104    "bottomBarBg",
105    "selectionBg",
106    "messageBoxBg",
107    "messageBoxBorder",
108    "messageBoxSelection",
109};
110
111static enum color stringToColor(const string &name)
112{
113    for (unsigned int i = 0; i < NUM_COLORS; i++) {
114        if (strcmp(colorNames[i], name.c_str()) == 0) {
115            return (enum color)i;
116        }
117    }
118    return (enum color)-1;
119}
120
121static const char *colorToString(enum color c)
122{
123    return colorNames[c];
124}
125
126static void quit_all(int err) {
127    delete app;
128    exit(err);
129}
130
131#ifdef UNLOCK_VT
132
133#define FB_TTY "/dev/tty%i"
134static void unlockVT()
135{
136    int i;
137    int fd;
138    char tty[10];
139
140    for (i=0; i < 10; i++) {
141        int mode;
142
143        sprintf(tty, FB_TTY, i);
144        fd = open(tty, O_RDWR);
145        if (fd < 0)
146          continue;
147
148        if (ioctl(fd, KDGETMODE, &mode) < 0) {
149            WARNING("Unable to get mode for tty %i.\n", i);
150            close(fd);
151            return;
152        }
153
154        if (mode != KD_TEXT)
155          break;
156
157        close(fd);
158    }
159
160    if (i==10) {
161        DEBUG("No graphic tty found.\n");
162        return;
163    }
164
165    DEBUG("Graphic tty found on %s.\n", tty);
166
167    if (ioctl(fd, KDSETMODE, KD_TEXT) < 0)
168        WARNING("unable to set keyboard mode.\n");
169
170    if (ioctl(fd, VT_UNLOCKSWITCH, 1) < 0)
171        WARNING("unable to unlock terminal.\n");
172
173    close(fd);
174}
175#endif
176
177const string GMenu2X::getHome(void)
178{
179    return gmenu2x_home;
180}
181
182int main(int /*argc*/, char * /*argv*/[]) {
183    INFO("----\nGMenu2X starting: If you read this message in the logs, check http://gmenu2x.sourceforge.net/page/Troubleshooting for a solution\n----\n");
184
185    signal(SIGINT, &quit_all);
186    signal(SIGSEGV,&quit_all);
187    signal(SIGTERM,&quit_all);
188
189    char *home = getenv("HOME");
190    if (home == NULL) {
191        ERROR("Unable to find gmenu2x home directory. The $HOME variable is not defined.\n");
192        return 1;
193    }
194
195    gmenu2x_home = (string)home + (string)"/.gmenu2x";
196    if (!fileExists(gmenu2x_home) && mkdir(gmenu2x_home.c_str(), 0770) < 0) {
197        ERROR("Unable to create gmenu2x home directory.\n");
198        return 1;
199    }
200
201    DEBUG("Home path: %s.\n", gmenu2x_home.c_str());
202
203    app = new GMenu2X();
204    DEBUG("Starting main()\n");
205    app->main();
206
207    return 0;
208}
209
210void GMenu2X::init() {
211#ifdef PLATFORM_GP2X
212/* gp2x_mem = open("/dev/mem", O_RDWR);
213    gp2x_memregs=(unsigned short *)mmap(0, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_mem, 0xc0000000);
214    MEM_REG=&gp2x_memregs[0];
215    if (f200) {
216        //if wm97xx fails to open, set f200 to false to prevent any further access to the touchscreen
217        f200 = ts.init();
218    }*/
219#else
220    batteryHandle = fopen("/sys/class/power_supply/battery/capacity", "r");
221    usbHandle = fopen("/sys/class/power_supply/USB/online", "r");
222    acHandle = fopen("/sys/class/power_supply/ac/online", "r");
223    backlightHandle =
224        fopen("/sys/class/backlight/gpm940b0-bl/brightness", "w+");
225#endif
226}
227
228void GMenu2X::deinit() {
229#ifdef PLATFORM_GP2X
230/* if (gp2x_mem!=0) {
231        gp2x_memregs[0x28DA>>1]=0x4AB;
232        gp2x_memregs[0x290C>>1]=640;
233        close(gp2x_mem);
234    }
235
236    if (f200) ts.deinit();*/
237#else
238    if (batteryHandle) fclose(batteryHandle);
239    if (backlightHandle) fclose(backlightHandle);
240    if (usbHandle) fclose(usbHandle);
241    if (acHandle) fclose(acHandle);
242#endif
243}
244
245void GMenu2X::tvout_on(bool /*pal*/) {
246#ifdef PLATFORM_GP2X
247/* if (gp2x_mem!=0) {
248// Ioctl_Dummy_t *msg;
249// int TVHandle = ioctl(SDL_videofd, FBMMSP2CTRL, msg);
250        if (cx25874!=0) gp2x_tvout_off();
251        //if tv-out is enabled without cx25874 open, stop
252        //if (gp2x_memregs[0x2800>>1]&0x100) return;
253        cx25874 = open("/dev/cx25874",O_RDWR);
254        ioctl(cx25874, _IOW('v', 0x02, unsigned char), pal ? 4 : 3);
255        gp2x_memregs[0x2906>>1]=512;
256        gp2x_memregs[0x28E4>>1]=gp2x_memregs[0x290C>>1];
257        gp2x_memregs[0x28E8>>1]=239;
258    }*/
259#endif
260}
261
262void GMenu2X::tvout_off() {
263#ifdef PLATFORM_GP2X
264/* if (gp2x_mem!=0) {
265        close(cx25874);
266        cx25874 = 0;
267        gp2x_memregs[0x2906>>1]=1024;
268    }*/
269#endif
270}
271
272
273GMenu2X::GMenu2X()
274{
275#ifdef PLATFORM_GP2X
276    //Detect firmware version and type
277    if (fileExists("/etc/open2x")) {
278        fwType = "open2x";
279        fwVersion = "";
280    } else {
281        fwType = "gph";
282        fwVersion = "";
283    }
284
285    //open2x
286    savedVolumeMode = 0;
287    volumeMode = VOLUME_MODE_NORMAL;
288    volumeScalerNormal = VOLUME_SCALER_NORMAL;
289    volumeScalerPhones = VOLUME_SCALER_PHONES;
290
291    o2x_usb_net_on_boot = false;
292    o2x_usb_net_ip = "";
293    o2x_ftp_on_boot = false;
294    o2x_telnet_on_boot = false;
295    o2x_gp2xjoy_on_boot = false;
296    o2x_usb_host_on_boot = false;
297    o2x_usb_hid_on_boot = false;
298    o2x_usb_storage_on_boot = false;
299#endif
300
301    usbnet = samba = inet = web = false;
302    useSelectionPng = false;
303
304    //load config data
305    readConfig();
306
307#ifdef PLATFORM_GP2X
308    if (fwType=="open2x") {
309        readConfigOpen2x();
310        // VOLUME MODIFIER
311        switch(volumeMode) {
312            case VOLUME_MODE_MUTE: setVolumeScaler(VOLUME_SCALER_MUTE); break;
313            case VOLUME_MODE_PHONES: setVolumeScaler(volumeScalerPhones); break;
314            case VOLUME_MODE_NORMAL: setVolumeScaler(volumeScalerNormal); break;
315        }
316    } else
317        readCommonIni();
318#endif
319
320    halfX = resX/2;
321    halfY = resY/2;
322    bottomBarIconY = resY-18;
323    bottomBarTextY = resY-10;
324
325    path = "";
326    getExePath();
327
328    batteryHandle = 0;
329    backlightHandle = 0;
330    usbHandle = 0;
331    acHandle = 0;
332
333    init();
334
335#ifdef PLATFORM_GP2X
336    cx25874 = 0;
337
338    //Fix tv-out
339/* if (gp2x_mem!=0) {
340        if (gp2x_memregs[0x2800>>1]&0x100) {
341            gp2x_memregs[0x2906>>1]=512;
342            //gp2x_memregs[0x290C>>1]=640;
343            gp2x_memregs[0x28E4>>1]=gp2x_memregs[0x290C>>1];
344        }
345        gp2x_memregs[0x28E8>>1]=239;
346    }*/
347#endif
348
349#ifdef UNLOCK_VT
350    unlockVT();
351#endif
352
353    /* Do not clear the screen on exit.
354     * This may require an SDL patch available at
355     * https://github.com/mthuurne/opendingux-buildroot/blob
356     * /opendingux-2010.11/package/sdl/sdl-fbcon-clear-onexit.patch
357     */
358    setenv("SDL_FBCON_DONT_CLEAR", "1", 0);
359
360    //Screen
361    if( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_JOYSTICK|SDL_INIT_TIMER)<0 ) {
362        ERROR("Could not initialize SDL: %s\n", SDL_GetError());
363        quit();
364    }
365
366    s = Surface::openOutputSurface(resX, resY, confInt["videoBpp"]);
367
368    bg = NULL;
369    font = NULL;
370    menu = NULL;
371    setSkin(confStr["skin"], false);
372    initMenu();
373
374    if (!fileExists(confStr["wallpaper"])) {
375        DEBUG("No wallpaper defined; we will take the default one.\n");
376        confStr["wallpaper"] = DEFAULT_WALLPAPER_PATH;
377    }
378
379    initBG();
380
381    /* If a user-specified input.conf file exists, we load it;
382     * otherwise, we load the default one. */
383    const char *input_file = (getHome() + "/input.conf").c_str();
384    if (fileExists(input_file)) {
385        DEBUG("Loading user-specific input.conf file: %s.\n", input_file);
386    } else {
387        input_file = GMENU2X_SYSTEM_DIR "/input.conf";
388        DEBUG("Loading system input.conf file: %s.\n", input_file);
389    }
390
391    input.init(input_file);
392    PowerSaver::getInstance()->setScreenTimeout( confInt["backlightTimeout"] );
393    setInputSpeed();
394    initServices();
395    setBacklight(confInt["backlight"]);
396    setVolume(confInt["globalVolume"]);
397    applyDefaultTimings();
398    setClock(confInt["menuClock"]);
399    //recover last session
400    readTmp();
401    if (lastSelectorElement>-1 && menu->selLinkApp()!=NULL && (!menu->selLinkApp()->getSelectorDir().empty() || !lastSelectorDir.empty()))
402        menu->selLinkApp()->selector(lastSelectorElement,lastSelectorDir);
403
404}
405
406GMenu2X::~GMenu2X() {
407    writeConfig();
408
409#ifdef PLATFORM_GP2X
410    if (fwType=="open2x") writeConfigOpen2x();
411#endif
412
413    quit();
414
415    delete menu;
416    delete font;
417}
418
419void GMenu2X::quit() {
420    fflush(NULL);
421    sc.clear();
422    delete s;
423
424    SDL_Quit();
425
426    unsetenv("SDL_FBCON_DONT_CLEAR");
427
428#ifdef PLATFORM_GP2X
429/* if (gp2x_mem!=0) {
430        //Fix tv-out
431        if (gp2x_memregs[0x2800>>1]&0x100) {
432            gp2x_memregs[0x2906>>1]=512;
433            gp2x_memregs[0x28E4>>1]=gp2x_memregs[0x290C>>1];
434        }
435        gp2x_deinit();
436    }*/
437#endif
438}
439
440void GMenu2X::initBG() {
441    sc.del("bgmain");
442
443    // Load wallpaper.
444    delete bg;
445    bg = Surface::loadImage(confStr["wallpaper"]);
446    if (!bg) {
447        bg = Surface::emptySurface(resX, resY);
448    }
449
450    drawTopBar(bg);
451    drawBottomBar(bg);
452
453    Surface *bgmain = new Surface(bg);
454    sc.add(bgmain,"bgmain");
455
456    Surface *sd = Surface::loadImage("imgs/sd.png", confStr["skin"]);
457    if (sd) sd->blit(bgmain, 3, bottomBarIconY);
458    string df = getDiskFree();
459    bgmain->write(font, df, 22, bottomBarTextY, ASFont::HAlignLeft, ASFont::VAlignMiddle);
460    delete sd;
461
462    Surface *volume = Surface::loadImage("imgs/volume.png", confStr["skin"]);
463    volumeX = 27+font->getTextWidth(df);
464    if (volume) volume->blit(bgmain, volumeX, bottomBarIconY);
465    volumeX += 19;
466    delete volume;
467
468    Surface *cpu = Surface::loadImage("imgs/cpu.png", confStr["skin"]);
469    cpuX = volumeX+font->getTextWidth("100")+5;
470    if (cpu) cpu->blit(bgmain, cpuX, bottomBarIconY);
471    cpuX += 19;
472    manualX = cpuX+font->getTextWidth("300Mhz")+5;
473    delete cpu;
474
475    int serviceX = resX-38;
476    if (usbnet) {
477        if (web) {
478            Surface *webserver = Surface::loadImage(
479                "imgs/webserver.png", confStr["skin"]);
480            if (webserver) webserver->blit(bgmain, serviceX, bottomBarIconY);
481            serviceX -= 19;
482            delete webserver;
483        }
484        if (samba) {
485            Surface *sambaS = Surface::loadImage(
486                "imgs/samba.png", confStr["skin"]);
487            if (sambaS) sambaS->blit(bgmain, serviceX, bottomBarIconY);
488            serviceX -= 19;
489            delete sambaS;
490        }
491        if (inet) {
492            Surface *inetS = Surface::loadImage("imgs/inet.png", confStr["skin"]);
493            if (inetS) inetS->blit(bgmain, serviceX, bottomBarIconY);
494            serviceX -= 19;
495            delete inetS;
496        }
497    }
498
499    bgmain->convertToDisplayFormat();
500}
501
502void GMenu2X::initFont() {
503    if (font != NULL) {
504        delete font;
505        font = NULL;
506    }
507
508    string fontFile = sc.getSkinFilePath("imgs/font.png");
509    if (fontFile.empty()) {
510        ERROR("Font png not found!\n");
511        quit();
512        exit(-1);
513    }
514    font = new ASFont(fontFile);
515}
516
517void GMenu2X::initMenu() {
518    //Menu structure handler
519    menu = new Menu(this);
520    for (uint i=0; i<menu->getSections().size(); i++) {
521        //Add virtual links in the applications section
522        if (menu->getSections()[i]=="applications") {
523            menu->addActionLink(i,"Explorer",MakeDelegate(this,&GMenu2X::explorer),tr["Launch an application"],"skin:icons/explorer.png");
524        }
525
526        //Add virtual links in the setting section
527        else if (menu->getSections()[i]=="settings") {
528            menu->addActionLink(i,"GMenu2X",MakeDelegate(this,&GMenu2X::options),tr["Configure GMenu2X's options"],"skin:icons/configure.png");
529            menu->addActionLink(i,tr["Skin"],MakeDelegate(this,&GMenu2X::skinMenu),tr["Configure skin"],"skin:icons/skin.png");
530            menu->addActionLink(i,tr["Wallpaper"],MakeDelegate(this,&GMenu2X::changeWallpaper),tr["Change GMenu2X wallpaper"],"skin:icons/wallpaper.png");
531#ifdef PLATFORM_GP2X
532            if (fwType=="open2x")
533                menu->addActionLink(i,"Open2x",MakeDelegate(this,&GMenu2X::settingsOpen2x),tr["Configure Open2x system settings"],"skin:icons/o2xconfigure.png");
534/* menu->addActionLink(i,"TV",MakeDelegate(this,&GMenu2X::toggleTvOut),tr["Activate/deactivate tv-out"],"skin:icons/tv.png");
535            menu->addActionLink(i,"USB Sd",MakeDelegate(this,&GMenu2X::activateSdUsb),tr["Activate Usb on SD"],"skin:icons/usb.png");
536            if (fwType=="gph" && !f200)
537                menu->addActionLink(i,"USB Nand",MakeDelegate(this,&GMenu2X::activateNandUsb),tr["Activate Usb on Nand"],"skin:icons/usb.png");
538            //menu->addActionLink(i,"USB Root",MakeDelegate(this,&GMenu2X::activateRootUsb),tr["Activate Usb on the root of the Gp2x Filesystem"],"skin:icons/usb.png");*/
539#endif
540            if (fileExists(getHome()+"/log.txt"))
541                menu->addActionLink(i,tr["Log Viewer"],MakeDelegate(this,&GMenu2X::viewLog),tr["Displays last launched program's output"],"skin:icons/ebook.png");
542            menu->addActionLink(i,tr["About"],MakeDelegate(this,&GMenu2X::about),tr["Info about GMenu2X"],"skin:icons/about.png");
543        }
544    }
545
546    menu->setSectionIndex(confInt["section"]);
547    menu->setLinkIndex(confInt["link"]);
548
549    menu->loadIcons();
550
551    //DEBUG
552    //menu->addLink( CARD_ROOT, "sample.pxml", "applications" );
553}
554
555void GMenu2X::about() {
556    vector<string> text;
557    split(text,"GMenu2X is developed by Massimiliano \"Ryo\" Torromeo, and is released under the GPL-v2 license.\n\
558Website: http://gmenu2x.sourceforge.net\n\
559E-Mail & PayPal account: massimiliano.torromeo@gmail.com\n\
560\n\
561Thanks goes to...\n\
562\n\
563 Contributors\n\
564----\n\
565NoidZ for his gp2x' buttons graphics\n\
566\n\
567 Beta testers\n\
568----\n\
569Goemon4, PokeParadox, PSyMastR and Tripmonkey_uk\n\
570\n\
571 Translators\n\
572----\n\
573English & Italian by me\n\
574French by Yodaz\n\
575Danish by claus\n\
576Dutch by superfly\n\
577Spanish by pedator\n\
578Portuguese (Portugal) by NightShadow\n\
579Slovak by Jozef\n\
580Swedish by Esslan and Micket\n\
581German by fusion_power, johnnysnet and Waldteufel\n\
582Finnish by Jontte and Atte\n\
583Norwegian by cowai\n\
584Russian by XaMMaX90\n\
585\n\
586 Donors\n\
587----\n\
588EvilDragon (www.gp2x.de)\n\
589Tecnologie Creative (www.tecnologiecreative.it)\n\
590TelcoLou\n\
591gaterooze\n\
592deepmenace\n\
593superfly\n\
594halo9\n\
595sbock\n\
596b._.o._.b\n\
597Jacopastorius\n\
598lorystorm90\n\
599and all the anonymous donors...\n\
600(If I missed to list you or if you want to be removed, contact me.)","\n");
601    TextDialog td(this, "GMenu2X", tr.translate("Version $1 (Build date: $2)","0.10-test4",__DATE__,NULL), "icons/about.png", &text);
602    td.exec();
603}
604
605void GMenu2X::viewLog() {
606    string logfile = getHome()+"/log.txt";
607    if (fileExists(logfile)) {
608        ifstream inf(logfile.c_str(), ios_base::in);
609        if (inf.is_open()) {
610            vector<string> log;
611
612            string line;
613            while (getline(inf, line, '\n'))
614                log.push_back(line);
615            inf.close();
616
617            TextDialog td(this, tr["Log Viewer"], tr["Displays last launched program's output"], "icons/ebook.png", &log);
618            td.exec();
619
620            MessageBox mb(this, tr["Do you want to delete the log file?"], "icons/ebook.png");
621            mb.setButton(ACCEPT, tr["Yes"]);
622            mb.setButton(CLEAR, tr["No"]);
623            if (mb.exec() == ACCEPT) {
624                ledOn();
625                unlink(logfile.c_str());
626                sync();
627                menu->deleteSelectedLink();
628                ledOff();
629            }
630        }
631    }
632}
633
634void GMenu2X::readConfig() {
635    string conffile = GMENU2X_SYSTEM_DIR "/gmenu2x.conf";
636    readConfig(conffile);
637
638    conffile = getHome() + "/gmenu2x.conf";
639    readConfig(conffile);
640}
641
642void GMenu2X::readConfig(string conffile) {
643    if (fileExists(conffile)) {
644        ifstream inf(conffile.c_str(), ios_base::in);
645        if (inf.is_open()) {
646            string line;
647            while (getline(inf, line, '\n')) {
648                string::size_type pos = line.find("=");
649                string name = trim(line.substr(0,pos));
650                string value = trim(line.substr(pos+1,line.length()));
651
652                if (value.length()>1 && value.at(0)=='"' && value.at(value.length()-1)=='"')
653                    confStr[name] = value.substr(1,value.length()-2);
654                else
655                    confInt[name] = atoi(value.c_str());
656            }
657            inf.close();
658        }
659    }
660    if (!confStr["lang"].empty())
661        tr.setLang(confStr["lang"]);
662
663    if (!confStr["wallpaper"].empty() && !fileExists(confStr["wallpaper"]))
664        confStr["wallpaper"] = "";
665
666    if (confStr["skin"].empty() || SurfaceCollection::getSkinPath(confStr["skin"]).empty())
667        confStr["skin"] = "Default";
668
669    evalIntConf( &confInt["outputLogs"], 0, 0,1 );
670    evalIntConf( &confInt["maxClock"], 430, 30, 500 );
671    evalIntConf( &confInt["menuClock"], 200, 30, 430 );
672    evalIntConf( &confInt["globalVolume"], 67, 0,100 );
673    evalIntConf( &confInt["backlightTimeout"], 15, 0,120 );
674    evalIntConf( &confInt["backlight"], 100, 5,100 );
675    evalIntConf( &confInt["videoBpp"], 32,32,32 ); // 8,16
676
677    if (confStr["tvoutEncoding"] != "PAL") confStr["tvoutEncoding"] = "NTSC";
678    resX = constrain( confInt["resolutionX"], 320,1920 );
679    resY = constrain( confInt["resolutionY"], 240,1200 );
680}
681
682void GMenu2X::writeConfig() {
683    ledOn();
684    string conffile = getHome() + "/gmenu2x.conf";
685    ofstream inf(conffile.c_str());
686    if (inf.is_open()) {
687        ConfStrHash::iterator endS = confStr.end();
688        for(ConfStrHash::iterator curr = confStr.begin(); curr != endS; curr++)
689            inf << curr->first << "=\"" << curr->second << "\"" << endl;
690
691        ConfIntHash::iterator endI = confInt.end();
692        for(ConfIntHash::iterator curr = confInt.begin(); curr != endI; curr++)
693            inf << curr->first << "=" << curr->second << endl;
694
695        inf.close();
696        sync();
697    }
698    ledOff();
699}
700
701#ifdef PLATFORM_GP2X
702void GMenu2X::readConfigOpen2x() {
703    string conffile = "/etc/config/open2x.conf";
704    if (fileExists(conffile)) {
705        ifstream inf(conffile.c_str(), ios_base::in);
706        if (inf.is_open()) {
707            string line;
708            while (getline(inf, line, '\n')) {
709                string::size_type pos = line.find("=");
710                string name = trim(line.substr(0,pos));
711                string value = trim(line.substr(pos+1,line.length()));
712
713                if (name=="USB_NET_ON_BOOT") o2x_usb_net_on_boot = value == "y" ? true : false;
714                else if (name=="USB_NET_IP") o2x_usb_net_ip = value;
715                else if (name=="TELNET_ON_BOOT") o2x_telnet_on_boot = value == "y" ? true : false;
716                else if (name=="FTP_ON_BOOT") o2x_ftp_on_boot = value == "y" ? true : false;
717                else if (name=="GP2XJOY_ON_BOOT") o2x_gp2xjoy_on_boot = value == "y" ? true : false;
718                else if (name=="USB_HOST_ON_BOOT") o2x_usb_host_on_boot = value == "y" ? true : false;
719                else if (name=="USB_HID_ON_BOOT") o2x_usb_hid_on_boot = value == "y" ? true : false;
720                else if (name=="USB_STORAGE_ON_BOOT") o2x_usb_storage_on_boot = value == "y" ? true : false;
721                else if (name=="VOLUME_MODE") volumeMode = savedVolumeMode = constrain( atoi(value.c_str()), 0, 2);
722                else if (name=="PHONES_VALUE") volumeScalerPhones = constrain( atoi(value.c_str()), 0, 100);
723                else if (name=="NORMAL_VALUE") volumeScalerNormal = constrain( atoi(value.c_str()), 0, 150);
724            }
725            inf.close();
726        }
727    }
728}
729void GMenu2X::writeConfigOpen2x() {
730    ledOn();
731    string conffile = "/etc/config/open2x.conf";
732    ofstream inf(conffile.c_str());
733    if (inf.is_open()) {
734        inf << "USB_NET_ON_BOOT=" << ( o2x_usb_net_on_boot ? "y" : "n" ) << endl;
735        inf << "USB_NET_IP=" << o2x_usb_net_ip << endl;
736        inf << "TELNET_ON_BOOT=" << ( o2x_telnet_on_boot ? "y" : "n" ) << endl;
737        inf << "FTP_ON_BOOT=" << ( o2x_ftp_on_boot ? "y" : "n" ) << endl;
738        inf << "GP2XJOY_ON_BOOT=" << ( o2x_gp2xjoy_on_boot ? "y" : "n" ) << endl;
739        inf << "USB_HOST_ON_BOOT=" << ( (o2x_usb_host_on_boot || o2x_usb_hid_on_boot || o2x_usb_storage_on_boot) ? "y" : "n" ) << endl;
740        inf << "USB_HID_ON_BOOT=" << ( o2x_usb_hid_on_boot ? "y" : "n" ) << endl;
741        inf << "USB_STORAGE_ON_BOOT=" << ( o2x_usb_storage_on_boot ? "y" : "n" ) << endl;
742        inf << "VOLUME_MODE=" << volumeMode << endl;
743        if (volumeScalerPhones != VOLUME_SCALER_PHONES) inf << "PHONES_VALUE=" << volumeScalerPhones << endl;
744        if (volumeScalerNormal != VOLUME_SCALER_NORMAL) inf << "NORMAL_VALUE=" << volumeScalerNormal << endl;
745        inf.close();
746        sync();
747    }
748    ledOff();
749}
750#endif
751
752void GMenu2X::writeSkinConfig() {
753    ledOn();
754
755    string conffile = getHome() + "/skins/";
756    if (!fileExists(conffile))
757      mkdir(conffile.c_str(), 0770);
758    conffile = conffile + confStr["skin"];
759    if (!fileExists(conffile))
760      mkdir(conffile.c_str(), 0770);
761    conffile = conffile + "/skin.conf";
762
763    ofstream inf(conffile.c_str());
764    if (inf.is_open()) {
765        ConfStrHash::iterator endS = skinConfStr.end();
766        for(ConfStrHash::iterator curr = skinConfStr.begin(); curr != endS; curr++)
767            inf << curr->first << "=\"" << curr->second << "\"" << endl;
768
769        ConfIntHash::iterator endI = skinConfInt.end();
770        for(ConfIntHash::iterator curr = skinConfInt.begin(); curr != endI; curr++)
771            inf << curr->first << "=" << curr->second << endl;
772
773        int i;
774        for (i = 0; i < NUM_COLORS; ++i) {
775            inf << colorToString((enum color)i) << "=#";
776            inf.width(2); inf.fill('0');
777            inf << right << hex << skinConfColors[i].r;
778            inf.width(2); inf.fill('0');
779            inf << right << hex << skinConfColors[i].g;
780            inf.width(2); inf.fill('0');
781            inf << right << hex << skinConfColors[i].b;
782            inf.width(2); inf.fill('0');
783            inf << right << hex << skinConfColors[i].a << endl;
784        }
785
786        inf.close();
787        sync();
788    }
789    ledOff();
790}
791
792#ifdef PLATFORM_GP2X
793void GMenu2X::readCommonIni() {
794    if (fileExists("/usr/gp2x/common.ini")) {
795        ifstream inf("/usr/gp2x/common.ini", ios_base::in);
796        if (inf.is_open()) {
797            string line;
798            string section = "";
799            while (getline(inf, line, '\n')) {
800                line = trim(line);
801                if (line[0]=='[' && line[line.length()-1]==']') {
802                    section = line.substr(1,line.length()-2);
803                } else {
804                    string::size_type pos = line.find("=");
805                    string name = trim(line.substr(0,pos));
806                    string value = trim(line.substr(pos+1,line.length()));
807
808                    if (section=="usbnet") {
809                        if (name=="enable")
810                            usbnet = value=="true" ? true : false;
811                        else if (name=="ip")
812                            ip = value;
813
814                    } else if (section=="server") {
815                        if (name=="inet")
816                            inet = value=="true" ? true : false;
817                        else if (name=="samba")
818                            samba = value=="true" ? true : false;
819                        else if (name=="web")
820                            web = value=="true" ? true : false;
821                    }
822                }
823            }
824            inf.close();
825        }
826    }
827}
828
829void GMenu2X::writeCommonIni() {}
830#endif
831
832void GMenu2X::readTmp() {
833    lastSelectorElement = -1;
834    if (fileExists("/tmp/gmenu2x.tmp")) {
835        ifstream inf("/tmp/gmenu2x.tmp", ios_base::in);
836        if (inf.is_open()) {
837            string line;
838            string section = "";
839            while (getline(inf, line, '\n')) {
840                string::size_type pos = line.find("=");
841                string name = trim(line.substr(0,pos));
842                string value = trim(line.substr(pos+1,line.length()));
843
844                if (name=="section")
845                    menu->setSectionIndex(atoi(value.c_str()));
846                else if (name=="link")
847                    menu->setLinkIndex(atoi(value.c_str()));
848                else if (name=="selectorelem")
849                    lastSelectorElement = atoi(value.c_str());
850                else if (name=="selectordir")
851                    lastSelectorDir = value;
852            }
853            inf.close();
854        }
855    }
856}
857
858void GMenu2X::writeTmp(int selelem, const string &selectordir) {
859    string conffile = "/tmp/gmenu2x.tmp";
860    ofstream inf(conffile.c_str());
861    if (inf.is_open()) {
862        inf << "section=" << menu->selSectionIndex() << endl;
863        inf << "link=" << menu->selLinkIndex() << endl;
864        if (selelem>-1)
865            inf << "selectorelem=" << selelem << endl;
866        if (selectordir!="")
867            inf << "selectordir=" << selectordir << endl;
868        inf.close();
869        sync();
870    }
871}
872
873void GMenu2X::initServices() {
874#ifdef PLATFORM_GP2X
875/* if (usbnet) {
876        string services = "scripts/services.sh "+ip+" "+(inet?"on":"off")+" "+(samba?"on":"off")+" "+(web?"on":"off")+" &";
877        system(services.c_str());
878    }*/
879#endif
880}
881
882void GMenu2X::ledOn() {
883#ifdef PLATFORM_GP2X
884// if (gp2x_mem!=0 && !f200) gp2x_memregs[0x106E >> 1] ^= 16;
885    //SDL_SYS_JoystickGp2xSys(joy.joystick, BATT_LED_ON);
886#endif
887}
888
889void GMenu2X::ledOff() {
890#ifdef PLATFORM_GP2X
891// if (gp2x_mem!=0 && !f200) gp2x_memregs[0x106E >> 1] ^= 16;
892    //SDL_SYS_JoystickGp2xSys(joy.joystick, BATT_LED_OFF);
893#endif
894}
895
896void GMenu2X::setBacklight(int val)
897{
898    if (backlightHandle) {
899        fprintf(backlightHandle, "%d", (val * 255) / 100);
900        fflush(backlightHandle);
901        rewind(backlightHandle);
902    }
903}
904
905int GMenu2X::getBackLight()
906{
907    int val = 255;
908    if (backlightHandle) {
909        fscanf(backlightHandle, "%d", &val);
910        rewind(backlightHandle);
911        val = (val * 100) / 255;
912        if (val < 0) val = 0; else if (val > 100) val = 100;
913    }
914    return val;
915}
916
917void GMenu2X::main() {
918    uint linksPerPage = linkColumns*linkRows;
919    int linkSpacingX = (resX-10 - linkColumns*skinConfInt["linkWidth"])/linkColumns;
920    int linkSpacingY = (resY-35 - skinConfInt["topBarHeight"] - linkRows*skinConfInt["linkHeight"])/linkRows;
921    uint sectionLinkPadding = (skinConfInt["topBarHeight"] - 32 - font->getLineHeight()) / 3;
922
923    bool quit = false;
924    int x,y, offset = menu->sectionLinks()->size()>linksPerPage ? 2 : 6;
925#ifdef PLATFORM_GP2X
926    int helpBoxHeight = fwType=="open2x" ? 154 : 139;
927#else
928    int helpBoxHeight = 154;
929#endif
930    uint i;
931    long tickBattery = -60000, tickNow;
932    string batteryIcon = "imgs/battery/0.png";
933    stringstream ss;
934    uint sectionsCoordX = 24;
935    SDL_Rect re = {0,0,0,0};
936    bool helpDisplayed = false;
937#ifdef WITH_DEBUG
938    //framerate
939    long tickFPS = SDL_GetTicks();
940    int drawn_frames = 0;
941    string fps = "";
942#endif
943
944    IconButton btnContextMenu(this,"skin:imgs/menu.png");
945    btnContextMenu.setPosition(resX-38, bottomBarIconY);
946    btnContextMenu.setAction(MakeDelegate(this, &GMenu2X::contextMenu));
947
948    if (!fileExists(CARD_ROOT))
949        CARD_ROOT = "/";
950
951    while (!quit) {
952        tickNow = SDL_GetTicks();
953
954        //Background
955        sc["bgmain"]->blit(s,0,0);
956
957        //Sections
958        sectionsCoordX = halfX - (constrain((uint)menu->getSections().size(), 0 , linkColumns) * skinConfInt["linkWidth"]) / 2;
959        if (menu->firstDispSection()>0)
960            sc.skinRes("imgs/l_enabled.png")->blit(s,0,0);
961        else
962            sc.skinRes("imgs/l_disabled.png")->blit(s,0,0);
963        if (menu->firstDispSection()+linkColumns<menu->getSections().size())
964            sc.skinRes("imgs/r_enabled.png")->blit(s,resX-10,0);
965        else
966            sc.skinRes("imgs/r_disabled.png")->blit(s,resX-10,0);
967        for (i=menu->firstDispSection(); i<menu->getSections().size() && i<menu->firstDispSection()+linkColumns; i++) {
968            string sectionIcon = "skin:sections/"+menu->getSections()[i]+".png";
969            x = (i-menu->firstDispSection())*skinConfInt["linkWidth"]+sectionsCoordX;
970            if (menu->selSectionIndex()==(int)i)
971                s->box(x, 0, skinConfInt["linkWidth"],
972                skinConfInt["topBarHeight"], skinConfColors[COLOR_SELECTION_BG]);
973            x += skinConfInt["linkWidth"]/2;
974            if (sc.exists(sectionIcon))
975                sc[sectionIcon]->blit(s,x-16,sectionLinkPadding,32,32);
976            else
977                sc.skinRes("icons/section.png")->blit(s,x-16,sectionLinkPadding);
978            s->write( font, menu->getSections()[i], x, skinConfInt["topBarHeight"]-sectionLinkPadding, ASFont::HAlignCenter, ASFont::VAlignBottom );
979        }
980
981        //Links
982        s->setClipRect(offset,skinConfInt["topBarHeight"],resX-9,resY-74); //32*2+10
983        for (i=menu->firstDispRow()*linkColumns; i<(menu->firstDispRow()*linkColumns)+linksPerPage && i<menu->sectionLinks()->size(); i++) {
984            int ir = i-menu->firstDispRow()*linkColumns;
985            x = (ir%linkColumns)*(skinConfInt["linkWidth"]+linkSpacingX)+offset;
986            y = ir/linkColumns*(skinConfInt["linkHeight"]+linkSpacingY)+skinConfInt["topBarHeight"]+2;
987            menu->sectionLinks()->at(i)->setPosition(x,y);
988
989            if (i==(uint)menu->selLinkIndex())
990                menu->sectionLinks()->at(i)->paintHover();
991
992            menu->sectionLinks()->at(i)->paint();
993        }
994        s->clearClipRect();
995
996        drawScrollBar(linkRows,menu->sectionLinks()->size()/linkColumns + ((menu->sectionLinks()->size()%linkColumns==0) ? 0 : 1),menu->firstDispRow(),43,resY-81);
997
998        /*
999        switch(volumeMode) {
1000            case VOLUME_MODE_MUTE: sc.skinRes("imgs/mute.png")->blit(s,279,bottomBarIconY); break;
1001            case VOLUME_MODE_PHONES: sc.skinRes("imgs/phones.png")->blit(s,279,bottomBarIconY); break;
1002            default: sc.skinRes("imgs/volume.png")->blit(s,279,bottomBarIconY); break;
1003        }
1004        */
1005
1006        if (menu->selLink()!=NULL) {
1007            s->write ( font, menu->selLink()->getDescription(), halfX, resY-19, ASFont::HAlignCenter, ASFont::VAlignBottom );
1008            if (menu->selLinkApp()!=NULL) {
1009                s->write ( font, menu->selLinkApp()->clockStr(confInt["maxClock"]), cpuX, bottomBarTextY, ASFont::HAlignLeft, ASFont::VAlignMiddle );
1010                s->write ( font, menu->selLinkApp()->volumeStr(), volumeX, bottomBarTextY, ASFont::HAlignLeft, ASFont::VAlignMiddle );
1011                //Manual indicator
1012                if (!menu->selLinkApp()->getManual().empty())
1013                    sc.skinRes("imgs/manual.png")->blit(s,manualX,bottomBarIconY);
1014            }
1015        }
1016
1017        if (ts.initialized()) {
1018            btnContextMenu.paint();
1019        }
1020        //check battery status every 60 seconds
1021        if (tickNow-tickBattery >= 60000) {
1022            tickBattery = tickNow;
1023            unsigned short battlevel = getBatteryLevel();
1024            if (battlevel>5) {
1025                batteryIcon = "imgs/battery/ac.png";
1026            } else {
1027                ss.clear();
1028                ss << battlevel;
1029                ss >> batteryIcon;
1030                batteryIcon = "imgs/battery/"+batteryIcon+".png";
1031            }
1032        }
1033        sc.skinRes(batteryIcon)->blit( s, resX-19, bottomBarIconY );
1034        //s->write( font, tr[batstr.c_str()], 20, 170 );
1035        //On Screen Help
1036
1037
1038        if (helpDisplayed) {
1039            s->box(10,50,300,143, skinConfColors[COLOR_MESSAGE_BOX_BG]);
1040            s->rectangle( 12,52,296,helpBoxHeight,
1041            skinConfColors[COLOR_MESSAGE_BOX_BORDER] );
1042            s->write( font, tr["CONTROLS"], 20, 60 );
1043            s->write( font, tr["B, Stick press: Launch link / Confirm action"], 20, 80 );
1044            s->write( font, tr["L, R: Change section"], 20, 95 );
1045            s->write( font, tr["Y: Show manual/readme"], 20, 110 );
1046            s->write( font, tr["VOLUP, VOLDOWN: Change cpu clock"], 20, 125 );
1047            s->write( font, tr["A+VOLUP, A+VOLDOWN: Change volume"], 20, 140 );
1048            s->write( font, tr["SELECT: Show contextual menu"], 20, 155 );
1049            s->write( font, tr["START: Show options menu"], 20, 170 );
1050#ifdef PLATFORM_GP2X
1051            if (fwType=="open2x") s->write( font, tr["X: Toggle speaker mode"], 20, 185 );
1052#endif
1053        }
1054
1055#ifdef WITH_DEBUG
1056        //framerate
1057        drawn_frames++;
1058        if (tickNow-tickFPS>=1000) {
1059            ss.clear();
1060            ss << drawn_frames*(tickNow-tickFPS+1)/1000;
1061            ss >> fps;
1062            tickFPS = tickNow;
1063            drawn_frames = 0;
1064        }
1065        s->write( font, fps+" FPS", resX-1,1 ,ASFont::HAlignRight );
1066#endif
1067
1068        s->flip();
1069
1070        //touchscreen
1071        if (ts.initialized()) {
1072            ts.poll();
1073            btnContextMenu.handleTS();
1074            re.x = 0; re.y = 0; re.h = skinConfInt["topBarHeight"]; re.w = resX;
1075            if (ts.pressed() && ts.inRect(re)) {
1076                re.w = skinConfInt["linkWidth"];
1077                sectionsCoordX = halfX - (constrain((uint)menu->getSections().size(), 0 , linkColumns) * skinConfInt["linkWidth"]) / 2;
1078                for (i=menu->firstDispSection(); !ts.handled() && i<menu->getSections().size() && i<menu->firstDispSection()+linkColumns; i++) {
1079                    re.x = (i-menu->firstDispSection())*re.w+sectionsCoordX;
1080
1081                    if (ts.inRect(re)) {
1082                        menu->setSectionIndex(i);
1083                        ts.setHandled();
1084                    }
1085                }
1086            }
1087
1088            i=menu->firstDispRow()*linkColumns;
1089            while ( i<(menu->firstDispRow()*linkColumns)+linksPerPage && i<menu->sectionLinks()->size()) {
1090                if (menu->sectionLinks()->at(i)->isPressed())
1091                    menu->setLinkIndex(i);
1092                if (menu->sectionLinks()->at(i)->handleTS())
1093                    i = menu->sectionLinks()->size();
1094                i++;
1095            }
1096        }
1097
1098        switch (input.waitForPressedButton()) {
1099            case ACCEPT:
1100                if (menu->selLink() != NULL) menu->selLink()->run();
1101                break;
1102            case CANCEL:
1103                helpDisplayed = ! helpDisplayed;
1104                break;
1105            case SETTINGS:
1106                options();
1107                break;
1108            case MENU:
1109                contextMenu();
1110                break;
1111            case UP:
1112                menu->linkUp();
1113                break;
1114            case DOWN:
1115                menu->linkDown();
1116                break;
1117            case LEFT:
1118                menu->linkLeft();
1119                break;
1120            case RIGHT:
1121                menu->linkRight();
1122                break;
1123            case MANUAL:
1124                {
1125                LinkApp* app = menu->selLinkApp();
1126                if ( app ) { app->showManual(); }
1127                }
1128                break;
1129            case ALTLEFT:
1130                menu->decSectionIndex();
1131                offset = menu->sectionLinks()->size()>linksPerPage ? 2 : 6;
1132                break;
1133            case ALTRIGHT:
1134                menu->incSectionIndex();
1135                offset = menu->sectionLinks()->size()>linksPerPage ? 2 : 6;
1136                break;
1137            default:
1138                break;
1139        }
1140
1141        /*
1142        while (!input.update())
1143            usleep(LOOP_DELAY);
1144        if ( input[ACCEPT] && menu->selLink()!=NULL ) menu->selLink()->run();
1145        else if ( input[SETTINGS] ) options();
1146        else if ( input[MENU] ) contextMenu();
1147        // VOLUME SCALE MODIFIER
1148        else if ( fwType=="open2x" && input[CLEAR] ) {
1149            volumeMode = constrain(volumeMode-1, -VOLUME_MODE_MUTE-1, VOLUME_MODE_NORMAL);
1150            if(volumeMode < VOLUME_MODE_MUTE)
1151                volumeMode = VOLUME_MODE_NORMAL;
1152            switch(volumeMode) {
1153                case VOLUME_MODE_MUTE: setVolumeScaler(VOLUME_SCALER_MUTE); break;
1154                case VOLUME_MODE_PHONES: setVolumeScaler(volumeScalerPhones); break;
1155                case VOLUME_MODE_NORMAL: setVolumeScaler(volumeScalerNormal); break;
1156            }
1157            setVolume(confInt["globalVolume"]);
1158        }
1159        // LINK NAVIGATION
1160        else if ( input[ALTLEFTEFT ] ) menu->linkLeft();
1161        else if ( input[ALTRIGHTIGHT] ) menu->linkRight();
1162        else if ( input[UP ] ) menu->linkUp();
1163        else if ( input[DOWN ] ) menu->linkDown();
1164        // SELLINKAPP SELECTED
1165        else if (menu->selLinkApp()!=NULL) {
1166            if ( input[MANUAL] ) menu->selLinkApp()->showManual();
1167            else if ( input.isActive(CANCEL) ) {
1168                // VOLUME
1169                if ( input[VOLDOWN] && !input.isActive(VOLUP) )
1170                    menu->selLinkApp()->setVolume( constrain(menu->selLinkApp()->volume()-1,0,100) );
1171                if ( input[VOLUP] && !input.isActive(VOLDOWN) )
1172                    menu->selLinkApp()->setVolume( constrain(menu->selLinkApp()->volume()+1,0,100) );;
1173                if ( input.isActive(VOLUP) && input.isActive(VOLDOWN) ) menu->selLinkApp()->setVolume(-1);
1174            } else {
1175                // CLOCK
1176                if ( input[VOLDOWN] && !input.isActive(VOLUP) )
1177                    menu->selLinkApp()->setClock( constrain(menu->selLinkApp()->clock()-1,200,confInt["maxClock"]) );
1178                if ( input[VOLUP] && !input.isActive(VOLDOWN) )
1179                    menu->selLinkApp()->setClock( constrain(menu->selLinkApp()->clock()+1,200,confInt["maxClock"]) );
1180                if ( input.isActive(VOLUP) && input.isActive(VOLDOWN) ) menu->selLinkApp()->setClock(336);
1181            }
1182        }
1183        if ( input.isActive(CANCEL) ) {
1184            if (input.isActive(ALTLEFT) && input.isActive(ALTRIGHT))
1185                saveScreenshot();
1186        } else {
1187            // SECTIONS
1188            if ( input[ALTLEFT ] ) {
1189                menu->decSectionIndex();
1190                offset = menu->sectionLinks()->size()>linksPerPage ? 2 : 6;
1191            } else if ( input[ALTRIGHT ] ) {
1192                menu->incSectionIndex();
1193                offset = menu->sectionLinks()->size()>linksPerPage ? 2 : 6;
1194            }
1195        }
1196        */
1197    }
1198}
1199
1200void GMenu2X::explorer() {
1201    FileDialog fd(this,tr["Select an application"],".gpu,.dge,.sh,");
1202    if (fd.exec()) {
1203        if (confInt["saveSelection"] && (confInt["section"]!=menu->selSectionIndex() || confInt["link"]!=menu->selLinkIndex()))
1204            writeConfig();
1205#ifdef PLATFORM_GP2X
1206        if (fwType == "open2x" && savedVolumeMode != volumeMode)
1207            writeConfigOpen2x();
1208#endif
1209
1210        //string command = cmdclean(fd.path()+"/"+fd.file) + "; sync & cd "+cmdclean(getExePath())+"; exec ./gmenu2x";
1211        string command = cmdclean(fd.getPath()+"/"+fd.getFile());
1212        chdir(fd.getPath().c_str());
1213        quit();
1214        setClock(200);
1215        execlp("/bin/sh","/bin/sh","-c",command.c_str(),NULL);
1216
1217        //if execution continues then something went wrong and as we already called SDL_Quit we cannot continue
1218        //try relaunching gmenu2x
1219        ERROR("Error executing selected application, re-launching gmenu2x\n");
1220        chdir(getExePath().c_str());
1221        execlp("./gmenu2x", "./gmenu2x", NULL);
1222    }
1223}
1224
1225void GMenu2X::options() {
1226    int curMenuClock = confInt["menuClock"];
1227    int curGlobalVolume = confInt["globalVolume"];
1228    //G
1229    int prevbacklight = confInt["backlight"];
1230    bool showRootFolder = fileExists(CARD_ROOT);
1231
1232    FileLister fl_tr(getHome() + "/translations");
1233    fl_tr.browse();
1234    fl_tr.setPath(GMENU2X_SYSTEM_DIR "/translations", false);
1235    fl_tr.browse(false);
1236
1237    fl_tr.insertFile("English");
1238    string lang = tr.lang();
1239
1240    vector<string> encodings;
1241    encodings.push_back("NTSC");
1242    encodings.push_back("PAL");
1243
1244    SettingsDialog sd(this, input, ts, tr["Settings"]);
1245    sd.addSetting(new MenuSettingMultiString(this,tr["Language"],tr["Set the language used by GMenu2X"],&lang,&fl_tr.getFiles()));
1246    sd.addSetting(new MenuSettingBool(this,tr["Save last selection"],tr["Save the last selected link and section on exit"],&confInt["saveSelection"]));
1247    sd.addSetting(new MenuSettingInt(this,tr["Clock for GMenu2X"],tr["Set the cpu working frequency when running GMenu2X"],&confInt["menuClock"],200,430));
1248    sd.addSetting(new MenuSettingInt(this,tr["Maximum overclock"],tr["Set the maximum overclock for launching links"],&confInt["maxClock"],200,430));
1249    sd.addSetting(new MenuSettingInt(this,tr["Global Volume"],tr["Set the default volume for the gp2x soundcard"],&confInt["globalVolume"],0,100));
1250    sd.addSetting(new MenuSettingBool(this,tr["Output logs"],tr["Logs the output of the links. Use the Log Viewer to read them."],&confInt["outputLogs"]));
1251    //G
1252    sd.addSetting(new MenuSettingInt(this,tr["Lcd Backlight"],tr["Set dingoo's Lcd Backlight value (default: 100)"],&confInt["backlight"],5,100));
1253    sd.addSetting(new MenuSettingInt(this,tr["Screen Timeout"],tr["Set screen's backlight timeout in seconds"],&confInt["backlightTimeout"],0,120));
1254// sd.addSetting(new MenuSettingMultiString(this,tr["Tv-Out encoding"],tr["Encoding of the tv-out signal"],&confStr["tvoutEncoding"],&encodings));
1255    sd.addSetting(new MenuSettingBool(this,tr["Show root"],tr["Show root folder in the file selection dialogs"],&showRootFolder));
1256
1257    if (sd.exec() && sd.edited()) {
1258        //G
1259        if (prevbacklight != confInt["backlight"]) setBacklight(confInt["backlight"]);
1260        if (curMenuClock!=confInt["menuClock"]) setClock(confInt["menuClock"]);
1261        if (curGlobalVolume!=confInt["globalVolume"]) setVolume(confInt["globalVolume"]);
1262        PowerSaver::getInstance()->setScreenTimeout( confInt["backlightTimeout"] );
1263        if (lang == "English") lang = "";
1264        if (lang != tr.lang()) {
1265            tr.setLang(lang);
1266            confStr["lang"] = lang;
1267        }
1268        /*if (fileExists(CARD_ROOT) && !showRootFolder)
1269            unlink(CARD_ROOT);
1270        else if (!fileExists(CARD_ROOT) && showRootFolder)
1271            symlink("/", CARD_ROOT);*/
1272        //WARNING: the above might be dangerous with CARD_ROOT set to /
1273        writeConfig();
1274    }
1275}
1276
1277#ifdef PLATFORM_GP2X
1278void GMenu2X::settingsOpen2x() {
1279    SettingsDialog sd(this, input, ts, tr["Open2x Settings"]);
1280    sd.addSetting(new MenuSettingBool(this,tr["USB net on boot"],tr["Allow USB networking to be started at boot time"],&o2x_usb_net_on_boot));
1281    sd.addSetting(new MenuSettingString(this,tr["USB net IP"],tr["IP address to be used for USB networking"],&o2x_usb_net_ip));
1282    sd.addSetting(new MenuSettingBool(this,tr["Telnet on boot"],tr["Allow telnet to be started at boot time"],&o2x_telnet_on_boot));
1283    sd.addSetting(new MenuSettingBool(this,tr["FTP on boot"],tr["Allow FTP to be started at boot time"],&o2x_ftp_on_boot));
1284    sd.addSetting(new MenuSettingBool(this,tr["GP2XJOY on boot"],tr["Create a js0 device for GP2X controls"],&o2x_gp2xjoy_on_boot));
1285    sd.addSetting(new MenuSettingBool(this,tr["USB host on boot"],tr["Allow USB host to be started at boot time"],&o2x_usb_host_on_boot));
1286    sd.addSetting(new MenuSettingBool(this,tr["USB HID on boot"],tr["Allow USB HID to be started at boot time"],&o2x_usb_hid_on_boot));
1287    sd.addSetting(new MenuSettingBool(this,tr["USB storage on boot"],tr["Allow USB storage to be started at boot time"],&o2x_usb_storage_on_boot));
1288    //sd.addSetting(new MenuSettingInt(this,tr["Speaker Mode on boot"],tr["Set Speaker mode. 0 = Mute, 1 = Phones, 2 = Speaker"],&volumeMode,0,2));
1289    sd.addSetting(new MenuSettingInt(this,tr["Speaker Scaler"],tr["Set the Speaker Mode scaling 0-150\% (default is 100\%)"],&volumeScalerNormal,0,150));
1290    sd.addSetting(new MenuSettingInt(this,tr["Headphones Scaler"],tr["Set the Headphones Mode scaling 0-100\% (default is 65\%)"],&volumeScalerPhones,0,100));
1291
1292    if (sd.exec() && sd.edited()) {
1293        writeConfigOpen2x();
1294        switch(volumeMode) {
1295            case VOLUME_MODE_MUTE: setVolumeScaler(VOLUME_SCALER_MUTE); break;
1296            case VOLUME_MODE_PHONES: setVolumeScaler(volumeScalerPhones); break;
1297            case VOLUME_MODE_NORMAL: setVolumeScaler(volumeScalerNormal); break;
1298        }
1299        setVolume(confInt["globalVolume"]);
1300    }
1301}
1302#endif
1303
1304void GMenu2X::skinMenu() {
1305    FileLister fl_sk(getHome() + "/skins", true, false);
1306    fl_sk.addExclude("..");
1307    fl_sk.browse();
1308    fl_sk.setPath(GMENU2X_SYSTEM_DIR "/skins", false);
1309    fl_sk.browse(false);
1310
1311    string curSkin = confStr["skin"];
1312
1313    SettingsDialog sd(this, input, ts, tr["Skin"]);
1314    sd.addSetting(new MenuSettingMultiString(this,tr["Skin"],tr["Set the skin used by GMenu2X"],&confStr["skin"],&fl_sk.getDirectories()));
1315    sd.addSetting(new MenuSettingRGBA(this,tr["Top Bar Color"],tr["Color of the top bar"],&skinConfColors[COLOR_TOP_BAR_BG]));
1316    sd.addSetting(new MenuSettingRGBA(this,tr["Bottom Bar Color"],tr["Color of the bottom bar"],&skinConfColors[COLOR_BOTTOM_BAR_BG]));
1317    sd.addSetting(new MenuSettingRGBA(this,tr["Selection Color"],tr["Color of the selection and other interface details"],&skinConfColors[COLOR_SELECTION_BG]));
1318    sd.addSetting(new MenuSettingRGBA(this,tr["Message Box Color"],tr["Background color of the message box"],&skinConfColors[COLOR_MESSAGE_BOX_BG]));
1319    sd.addSetting(new MenuSettingRGBA(this,tr["Message Box Border Color"],tr["Border color of the message box"],&skinConfColors[COLOR_MESSAGE_BOX_BORDER]));
1320    sd.addSetting(new MenuSettingRGBA(this,tr["Message Box Selection Color"],tr["Color of the selection of the message box"],&skinConfColors[COLOR_MESSAGE_BOX_SELECTION]));
1321
1322    if (sd.exec() && sd.edited()) {
1323        if (curSkin != confStr["skin"]) {
1324            setSkin(confStr["skin"]);
1325            writeConfig();
1326        }
1327        writeSkinConfig();
1328        initBG();
1329    }
1330}
1331
1332void GMenu2X::toggleTvOut() {
1333#ifdef PLATFORM_GP2X
1334/* if (cx25874!=0)
1335        gp2x_tvout_off();
1336    else
1337        gp2x_tvout_on(confStr["tvoutEncoding"] == "PAL");*/
1338#endif
1339}
1340
1341void GMenu2X::setSkin(const string &skin, bool setWallpaper) {
1342    confStr["skin"] = skin;
1343
1344    //Clear previous skin settings
1345    skinConfStr.clear();
1346    skinConfInt.clear();
1347
1348    DEBUG("GMenu2X: setting new skin %s.\n", skin.c_str());
1349
1350    //clear collection and change the skin path
1351    sc.clear();
1352    sc.setSkin(skin);
1353
1354    //reset colors to the default values
1355    skinConfColors[COLOR_TOP_BAR_BG] = (RGBAColor){255,255,255,130};
1356    skinConfColors[COLOR_BOTTOM_BAR_BG] = (RGBAColor){255,255,255,130};
1357    skinConfColors[COLOR_SELECTION_BG] = (RGBAColor){255,255,255,130};
1358    skinConfColors[COLOR_MESSAGE_BOX_BG] = (RGBAColor){255,255,255,255};
1359    skinConfColors[COLOR_MESSAGE_BOX_BORDER] = (RGBAColor){80,80,80,255};
1360    skinConfColors[COLOR_MESSAGE_BOX_SELECTION] = (RGBAColor){160,160,160,255};
1361
1362    /* Load skin settings from user directory if present,
1363     * or from the system directory. */
1364    string skinconfname = getHome() + "/skins/" + skin + "/skin.conf";
1365    if (!fileExists(skinconfname))
1366      skinconfname = GMENU2X_SYSTEM_DIR "/skins/" + skin + "/skin.conf";
1367
1368    if (fileExists(skinconfname)) {
1369        ifstream skinconf(skinconfname.c_str(), ios_base::in);
1370        if (skinconf.is_open()) {
1371            string line;
1372            while (getline(skinconf, line, '\n')) {
1373                line = trim(line);
1374                DEBUG("skinconf: '%s'\n", line.c_str());
1375                string::size_type pos = line.find("=");
1376                string name = trim(line.substr(0,pos));
1377                string value = trim(line.substr(pos+1,line.length()));
1378
1379                if (value.length()>0) {
1380                    if (value.length()>1 && value.at(0)=='"' && value.at(value.length()-1)=='"')
1381                        skinConfStr[name] = value.substr(1,value.length()-2);
1382                    else if (value.at(0) == '#')
1383                        skinConfColors[stringToColor(name)] = strtorgba( value.substr(1,value.length()) );
1384                    else
1385                        skinConfInt[name] = atoi(value.c_str());
1386                }
1387            }
1388            skinconf.close();
1389
1390            if (setWallpaper && !skinConfStr["wallpaper"].empty() && fileExists("skins/"+skin+"/wallpapers/"+skinConfStr["wallpaper"]))
1391                confStr["wallpaper"] = "skins/"+skin+"/wallpapers/"+skinConfStr["wallpaper"];
1392        }
1393    }
1394
1395    evalIntConf( &skinConfInt["topBarHeight"], 40, 32,120 );
1396    evalIntConf( &skinConfInt["linkHeight"], 40, 32,120 );
1397    evalIntConf( &skinConfInt["linkWidth"], 60, 32,120 );
1398
1399    //recalculate some coordinates based on the new element sizes
1400    linkColumns = (resX-10)/skinConfInt["linkWidth"];
1401    linkRows = (resY-35-skinConfInt["topBarHeight"])/skinConfInt["linkHeight"];
1402
1403    if (menu != NULL) menu->loadIcons();
1404
1405    //Selection png
1406    useSelectionPng = sc.addSkinRes("imgs/selection.png", false) != NULL;
1407
1408    //font
1409    initFont();
1410}
1411
1412/*
1413void GMenu2X::activateSdUsb() {
1414    if (usbnet) {
1415        MessageBox mb(this,tr["Operation not permitted."]+"\n"+tr["You should disable Usb Networking to do this."]);
1416        mb.exec();
1417    } else {
1418        system("scripts/usbon.sh sd");
1419        MessageBox mb(this,tr["USB Enabled (SD)"],"icons/usb.png");
1420        mb.setButton(ACCEPT, tr["Turn off"]);
1421        mb.exec();
1422        system("scripts/usboff.sh sd");
1423    }
1424}
1425
1426void GMenu2X::activateNandUsb() {
1427    if (usbnet) {
1428        MessageBox mb(this,tr["Operation not permitted."]+"\n"+tr["You should disable Usb Networking to do this."]);
1429        mb.exec();
1430    } else {
1431        system("scripts/usbon.sh nand");
1432        MessageBox mb(this,tr["USB Enabled (Nand)"],"icons/usb.png");
1433        mb.setButton(ACCEPT, tr["Turn off"]);
1434        mb.exec();
1435        system("scripts/usboff.sh nand");
1436    }
1437}
1438
1439void GMenu2X::activateRootUsb() {
1440    if (usbnet) {
1441        MessageBox mb(this,tr["Operation not permitted."]+"\n"+tr["You should disable Usb Networking to do this."]);
1442        mb.exec();
1443    } else {
1444        system("scripts/usbon.sh root");
1445        MessageBox mb(this,tr["USB Enabled (Root)"],"icons/usb.png");
1446        mb.setButton(ACCEPT, tr["Turn off"]);
1447        mb.exec();
1448        system("scripts/usboff.sh root");
1449    }
1450}
1451*/
1452void GMenu2X::contextMenu() {
1453    vector<MenuOption> voices;
1454    {
1455    MenuOption opt = {tr.translate("Add link in $1",menu->selSection().c_str(),NULL), MakeDelegate(this, &GMenu2X::addLink)};
1456    voices.push_back(opt);
1457    }
1458
1459    if (menu->selLinkApp()!=NULL) {
1460        {
1461        MenuOption opt = {tr.translate("Edit $1",menu->selLink()->getTitle().c_str(),NULL), MakeDelegate(this, &GMenu2X::editLink)};
1462        voices.push_back(opt);
1463        }{
1464        MenuOption opt = {tr.translate("Delete $1 link",menu->selLink()->getTitle().c_str(),NULL), MakeDelegate(this, &GMenu2X::deleteLink)};
1465        voices.push_back(opt);
1466        }
1467    }
1468
1469    {
1470    MenuOption opt = {tr["Add section"], MakeDelegate(this, &GMenu2X::addSection)};
1471    voices.push_back(opt);
1472    }{
1473    MenuOption opt = {tr["Rename section"], MakeDelegate(this, &GMenu2X::renameSection)};
1474    voices.push_back(opt);
1475    }{
1476    MenuOption opt = {tr["Delete section"], MakeDelegate(this, &GMenu2X::deleteSection)};
1477    voices.push_back(opt);
1478    }{
1479    MenuOption opt = {tr["Scan for applications and games"], MakeDelegate(this, &GMenu2X::scanner)};
1480    voices.push_back(opt);
1481    }
1482
1483    bool close = false;
1484    uint i, sel=0, fadeAlpha=0;
1485
1486    int h = font->getHeight();
1487    SDL_Rect box;
1488    box.h = (h+2)*voices.size()+8;
1489    box.w = 0;
1490    for (i=0; i<voices.size(); i++) {
1491        int w = font->getTextWidth(voices[i].text);
1492        if (w>box.w) box.w = w;
1493    }
1494    box.w += 23;
1495    box.x = halfX - box.w/2;
1496    box.y = halfY - box.h/2;
1497
1498    SDL_Rect selbox = {box.x+4, 0, box.w-8, h+2};
1499    long tickNow, tickStart = SDL_GetTicks();
1500
1501    Surface bg(s);
1502    /*//Darken background
1503    bg.box(0, 0, resX, resY, 0,0,0,150);
1504    bg.box(box.x, box.y, box.w, box.h, skinConfColors["messageBoxBg"]);
1505    bg.rectangle( box.x+2, box.y+2, box.w-4, box.h-4, skinConfColors["messageBoxBorder"] );*/
1506
1507    bevent_t event;
1508    while (!close) {
1509        tickNow = SDL_GetTicks();
1510
1511        selbox.y = box.y+4+(h+2)*sel;
1512        bg.blit(s,0,0);
1513
1514        if (fadeAlpha<200) fadeAlpha = intTransition(0,200,tickStart,500,tickNow);
1515        s->box(0, 0, resX, resY, 0,0,0,fadeAlpha);
1516        s->box(box.x, box.y, box.w, box.h, skinConfColors[COLOR_MESSAGE_BOX_BG]);
1517        s->rectangle( box.x+2, box.y+2, box.w-4, box.h-4, skinConfColors[COLOR_MESSAGE_BOX_BORDER] );
1518
1519
1520        //draw selection rect
1521        s->box( selbox.x, selbox.y, selbox.w, selbox.h, skinConfColors[COLOR_MESSAGE_BOX_SELECTION] );
1522        for (i=0; i<voices.size(); i++)
1523            s->write( font, voices[i].text, box.x+12, box.y+5+(h+2)*i, ASFont::HAlignLeft, ASFont::VAlignTop );
1524        s->flip();
1525
1526        //touchscreen
1527        if (ts.initialized()) {
1528            ts.poll();
1529            if (ts.released()) {
1530                if (!ts.inRect(box))
1531                    close = true;
1532                else if (ts.getX() >= selbox.x
1533                      && ts.getX() <= selbox.x + selbox.w)
1534                    for (i=0; i<voices.size(); i++) {
1535                        selbox.y = box.y+4+(h+2)*i;
1536                        if (ts.getY() >= selbox.y
1537                         && ts.getY() <= selbox.y + selbox.h) {
1538                            voices[i].action();
1539                            close = true;
1540                            i = voices.size();
1541                        }
1542                    }
1543            } else if (ts.pressed() && ts.inRect(box)) {
1544                for (i=0; i<voices.size(); i++) {
1545                    selbox.y = box.y+4+(h+2)*i;
1546                    if (ts.getY() >= selbox.y
1547                     && ts.getY() <= selbox.y + selbox.h) {
1548                        sel = i;
1549                        i = voices.size();
1550                    }
1551                }
1552            }
1553        }
1554
1555        
1556        if (fadeAlpha < 200) {
1557            if (!input.pollEvent(&event) || event.state != PRESSED) continue;
1558        } else {
1559            event.button = input.waitForPressedButton();
1560        }
1561
1562        switch(event.button) {
1563            case MENU:
1564                close = true;
1565                break;
1566            case UP:
1567                sel = max(0, sel-1);
1568                break;
1569            case DOWN:
1570                sel = min((int)voices.size()-1, sel+1);
1571                break;
1572            case ACCEPT:
1573                voices[sel].action();
1574                close = true;
1575                break;
1576            default:
1577                break;
1578        }
1579    }
1580}
1581
1582void GMenu2X::changeWallpaper() {
1583    WallpaperDialog wp(this);
1584    if (wp.exec() && confStr["wallpaper"] != wp.wallpaper) {
1585        confStr["wallpaper"] = wp.wallpaper;
1586        initBG();
1587        writeConfig();
1588    }
1589}
1590
1591void GMenu2X::addLink() {
1592    FileDialog fd(this,tr["Select an application"]);
1593    if (fd.exec()) {
1594        ledOn();
1595        menu->addLink(fd.getPath(), fd.getFile());
1596        sync();
1597        ledOff();
1598    }
1599}
1600
1601void GMenu2X::editLink() {
1602    if (menu->selLinkApp()==NULL) return;
1603
1604    vector<string> pathV;
1605    split(pathV,menu->selLinkApp()->getFile(),"/");
1606    string oldSection = "";
1607    if (pathV.size()>1)
1608        oldSection = pathV[pathV.size()-2];
1609    string newSection = oldSection;
1610
1611    string linkTitle = menu->selLinkApp()->getTitle();
1612    string linkDescription = menu->selLinkApp()->getDescription();
1613    string linkIcon = menu->selLinkApp()->getIcon();
1614    string linkManual = menu->selLinkApp()->getManual();
1615    string linkParams = menu->selLinkApp()->getParams();
1616    string linkSelFilter = menu->selLinkApp()->getSelectorFilter();
1617    string linkSelDir = menu->selLinkApp()->getSelectorDir();
1618    bool linkSelBrowser = menu->selLinkApp()->getSelectorBrowser();
1619    bool linkUseRamTimings = menu->selLinkApp()->getUseRamTimings();
1620    string linkSelScreens = menu->selLinkApp()->getSelectorScreens();
1621    string linkSelAliases = menu->selLinkApp()->getAliasFile();
1622    int linkClock = menu->selLinkApp()->clock();
1623    int linkVolume = menu->selLinkApp()->volume();
1624    //G
1625    //int linkGamma = menu->selLinkApp()->gamma();
1626    int linkBacklight = menu->selLinkApp()->backlight();
1627
1628    string diagTitle = tr.translate("Edit link: $1",linkTitle.c_str(),NULL);
1629    string diagIcon = menu->selLinkApp()->getIconPath();
1630
1631    SettingsDialog sd(this, input, ts, diagTitle, diagIcon);
1632    sd.addSetting(new MenuSettingString(this,tr["Title"],tr["Link title"],&linkTitle, diagTitle,diagIcon));
1633    sd.addSetting(new MenuSettingString(this,tr["Description"],tr["Link description"],&linkDescription, diagTitle,diagIcon));
1634    sd.addSetting(new MenuSettingMultiString(this,tr["Section"],tr["The section this link belongs to"],&newSection,&menu->getSections()));
1635    sd.addSetting(new MenuSettingImage(this,tr["Icon"],tr.translate("Select an icon for the link: $1",linkTitle.c_str(),NULL),&linkIcon,".png,.bmp,.jpg,.jpeg"));
1636    sd.addSetting(new MenuSettingFile(this,tr["Manual"],tr["Select a graphic/textual manual or a readme"],&linkManual,".man.png,.txt"));
1637    sd.addSetting(new MenuSettingInt(this,tr["Clock (default: 336)"],tr["Cpu clock frequency to set when launching this link"],&linkClock,200,confInt["maxClock"]));
1638// sd.addSetting(new MenuSettingBool(this,tr["Tweak RAM Timings"],tr["This usually speeds up the application at the cost of stability"],&linkUseRamTimings));
1639    sd.addSetting(new MenuSettingInt(this,tr["Volume (default: -1)"],tr["Volume to set for this link"],&linkVolume,-1,100));
1640    sd.addSetting(new MenuSettingInt(this,tr["Backlight (default: -1)"],tr["LCD backlight value to set when launching this link"],&linkBacklight,-1,100));
1641    sd.addSetting(new MenuSettingString(this,tr["Parameters"],tr["Parameters to pass to the application"],&linkParams, diagTitle,diagIcon));
1642    sd.addSetting(new MenuSettingDir(this,tr["Selector Directory"],tr["Directory to scan for the selector"],&linkSelDir));
1643    sd.addSetting(new MenuSettingBool(this,tr["Selector Browser"],tr["Allow the selector to change directory"],&linkSelBrowser));
1644    sd.addSetting(new MenuSettingString(this,tr["Selector Filter"],tr["Selector filter (Separate values with a comma)"],&linkSelFilter, diagTitle,diagIcon));
1645    sd.addSetting(new MenuSettingDir(this,tr["Selector Screenshots"],tr["Directory of the screenshots for the selector"],&linkSelScreens));
1646    sd.addSetting(new MenuSettingFile(this,tr["Selector Aliases"],tr["File containing a list of aliases for the selector"],&linkSelAliases));
1647    //G
1648    sd.addSetting(new MenuSettingBool(this,tr["Wrapper"],tr["Explicitly relaunch GMenu2X after execution"],&menu->selLinkApp()->needsWrapperRef()));
1649    sd.addSetting(new MenuSettingBool(this,tr["Don't Leave"],tr["Don't quit GMenu2X when launching this link"],&menu->selLinkApp()->runsInBackgroundRef()));
1650
1651    if (sd.exec() && sd.edited()) {
1652        ledOn();
1653
1654        menu->selLinkApp()->setTitle(linkTitle);
1655        menu->selLinkApp()->setDescription(linkDescription);
1656        menu->selLinkApp()->setIcon(linkIcon);
1657        menu->selLinkApp()->setManual(linkManual);
1658        menu->selLinkApp()->setParams(linkParams);
1659        menu->selLinkApp()->setSelectorFilter(linkSelFilter);
1660        menu->selLinkApp()->setSelectorDir(linkSelDir);
1661        menu->selLinkApp()->setSelectorBrowser(linkSelBrowser);
1662        menu->selLinkApp()->setUseRamTimings(linkUseRamTimings);
1663        menu->selLinkApp()->setSelectorScreens(linkSelScreens);
1664        menu->selLinkApp()->setAliasFile(linkSelAliases);
1665        menu->selLinkApp()->setClock(linkClock);
1666        menu->selLinkApp()->setVolume(linkVolume);
1667        //G
1668        if ((linkBacklight < 5) && (linkBacklight > -1))
1669            linkBacklight = 5;
1670        menu->selLinkApp()->setBacklight(linkBacklight);
1671
1672        INFO("New Section: '%s'\n", newSection.c_str());
1673
1674        //if section changed move file and update link->file
1675        if (oldSection!=newSection) {
1676            vector<string>::const_iterator newSectionIndex = find(menu->getSections().begin(),menu->getSections().end(),newSection);
1677            if (newSectionIndex==menu->getSections().end()) return;
1678            string newFileName = "sections/"+newSection+"/"+linkTitle;
1679            uint x=2;
1680            while (fileExists(newFileName)) {
1681                string id = "";
1682                stringstream ss; ss << x; ss >> id;
1683                newFileName = "sections/"+newSection+"/"+linkTitle+id;
1684                x++;
1685            }
1686            rename(menu->selLinkApp()->getFile().c_str(),newFileName.c_str());
1687            menu->selLinkApp()->renameFile(newFileName);
1688
1689            INFO("New section index: %i.\n", newSectionIndex - menu->getSections().begin());
1690
1691            menu->linkChangeSection(menu->selLinkIndex(), menu->selSectionIndex(), newSectionIndex - menu->getSections().begin());
1692        }
1693        menu->selLinkApp()->save();
1694        sync();
1695
1696        ledOff();
1697    }
1698}
1699
1700void GMenu2X::deleteLink() {
1701    if (menu->selLinkApp()!=NULL) {
1702        MessageBox mb(this, tr.translate("Deleting $1",menu->selLink()->getTitle().c_str(),NULL)+"\n"+tr["Are you sure?"], menu->selLink()->getIconPath());
1703        mb.setButton(ACCEPT, tr["Yes"]);
1704        mb.setButton(CLEAR, tr["No"]);
1705        if (mb.exec() == ACCEPT) {
1706            ledOn();
1707            menu->deleteSelectedLink();
1708            sync();
1709            ledOff();
1710        }
1711    }
1712}
1713
1714void GMenu2X::addSection() {
1715    InputDialog id(this, input, ts, tr["Insert a name for the new section"]);
1716    if (id.exec()) {
1717        //only if a section with the same name does not exist
1718        if (find(menu->getSections().begin(), menu->getSections().end(), id.getInput())
1719                == menu->getSections().end()) {
1720            //section directory doesn't exists
1721            ledOn();
1722            if (menu->addSection(id.getInput())) {
1723                menu->setSectionIndex( menu->getSections().size()-1 ); //switch to the new section
1724                sync();
1725            }
1726            ledOff();
1727        }
1728    }
1729}
1730
1731void GMenu2X::renameSection() {
1732    InputDialog id(this, input, ts, tr["Insert a new name for this section"],menu->selSection());
1733    if (id.exec()) {
1734        //only if a section with the same name does not exist & !samename
1735        if (menu->selSection() != id.getInput()
1736         && find(menu->getSections().begin(),menu->getSections().end(), id.getInput())
1737                == menu->getSections().end()) {
1738            //section directory doesn't exists
1739            string newsectiondir = getHome() + "/sections/" + id.getInput();
1740            string sectiondir = getHome() + "/sections/" + menu->selSection();
1741            ledOn();
1742
1743            if (!rename(sectiondir.c_str(), newsectiondir.c_str())) {
1744                string oldpng = menu->selSection() + ".png";
1745                string newpng = id.getInput() + ".png";
1746                string oldicon = sc.getSkinFilePath(oldpng);
1747                string newicon = sc.getSkinFilePath(newpng);
1748
1749                if (!oldicon.empty() && newicon.empty()) {
1750                    newicon = oldicon;
1751                    newicon.replace(newicon.find(oldpng), oldpng.length(), newpng);
1752
1753                    if (!fileExists(newicon)) {
1754                        rename(oldicon.c_str(), newicon.c_str());
1755                        sc.move("skin:"+oldpng, "skin:"+newpng);
1756                    }
1757                }
1758                menu->renameSection(menu->selSectionIndex(), id.getInput());
1759                sync();
1760            }
1761            ledOff();
1762        }
1763    }
1764}
1765
1766void GMenu2X::deleteSection() {
1767    MessageBox mb(this,tr["You will lose all the links in this section."]+"\n"+tr["Are you sure?"]);
1768    mb.setButton(ACCEPT, tr["Yes"]);
1769    mb.setButton(CLEAR, tr["No"]);
1770    if (mb.exec() == ACCEPT) {
1771        ledOn();
1772        if (rmtree(getHome() + "/sections/" + menu->selSection())) {
1773            menu->deleteSelectedSection();
1774            sync();
1775        }
1776        ledOff();
1777    }
1778}
1779
1780void GMenu2X::scanner() {
1781    Surface scanbg(bg);
1782    drawButton(&scanbg, "x", tr["Exit"],
1783    drawButton(&scanbg, "b", "", 5)-10);
1784    scanbg.write(font,tr["Link Scanner"],halfX,7,ASFont::HAlignCenter,ASFont::VAlignMiddle);
1785    scanbg.convertToDisplayFormat();
1786
1787    uint lineY = 42;
1788
1789#ifdef PLATFORM_PANDORA
1790    //char *configpath = pnd_conf_query_searchpath();
1791#else
1792    if (confInt["menuClock"]<430) {
1793        setClock(336);
1794        scanbg.write(font,tr["Raising cpu clock to 336Mhz"],5,lineY);
1795        scanbg.blit(s,0,0);
1796        s->flip();
1797        lineY += 26;
1798    }
1799
1800    scanbg.write(font,tr["Scanning SD filesystem..."],5,lineY);
1801    scanbg.blit(s,0,0);
1802    s->flip();
1803    lineY += 26;
1804
1805    vector<string> files;
1806    scanPath(CARD_ROOT, &files);
1807
1808    const char *nandpath = NULL;
1809
1810#if defined(PLATFORM_GP2X)
1811    //Onyl gph firmware has nand
1812    if (fwType=="gph" && !isF200())
1813        nandpath = "/boot/local/nand";
1814#elif defined(PLATFORM_DINGUX)
1815    nandpath = "/media/ccnandb1";
1816#endif
1817
1818    if (nandpath) {
1819        scanbg.write(font,tr["Scanning NAND filesystem..."],5,lineY);
1820        scanbg.blit(s,0,0);
1821        s->flip();
1822        lineY += 26;
1823        scanPath(nandpath, &files);
1824    }
1825
1826    stringstream ss;
1827    ss << files.size();
1828    string str = "";
1829    ss >> str;
1830    scanbg.write(font,tr.translate("$1 files found.",str.c_str(),NULL),5,lineY);
1831    lineY += 26;
1832    scanbg.write(font,tr["Creating links..."],5,lineY);
1833    scanbg.blit(s,0,0);
1834    s->flip();
1835    lineY += 26;
1836
1837    string path, file;
1838    string::size_type pos;
1839    uint linkCount = 0;
1840
1841    ledOn();
1842    for (uint i = 0; i<files.size(); i++) {
1843        pos = files[i].rfind("/");
1844        if (pos!=string::npos && pos>0) {
1845            path = files[i].substr(0, pos+1);
1846            file = files[i].substr(pos+1, files[i].length());
1847            if (menu->addLink(path,file,"found "+file.substr(file.length()-3,3)))
1848                linkCount++;
1849        }
1850    }
1851
1852    ss.clear();
1853    ss << linkCount;
1854    ss >> str;
1855    scanbg.write(font,tr.translate("$1 links created.",str.c_str(),NULL),5,lineY);
1856    scanbg.blit(s,0,0);
1857    s->flip();
1858    lineY += 26;
1859
1860    if (confInt["menuClock"]<430) {
1861        setClock(confInt["menuClock"]);
1862        scanbg.write(font,tr["Decreasing cpu clock"],5,lineY);
1863        scanbg.blit(s,0,0);
1864        s->flip();
1865        lineY += 26;
1866    }
1867
1868    sync();
1869    ledOff();
1870#endif
1871
1872    buttontype_t button;
1873    do {
1874        button = input.waitForPressedButton();
1875    } while ((button != SETTINGS)
1876                && (button != ACCEPT)
1877                && (button != CLEAR));
1878
1879    /*
1880    bevent_t event;
1881    do {
1882        input.getEvent(&event, true);
1883    } while ((event.state != PRESSED) ||
1884                ( (event.button != SETTINGS)
1885                && (event.button != ACCEPT)
1886                && (event.button != CLEAR)));
1887                */
1888}
1889
1890void GMenu2X::scanPath(string path, vector<string> *files) {
1891    DIR *dirp;
1892    struct stat st;
1893    struct dirent *dptr;
1894    string filepath, ext;
1895
1896    if (path[path.length()-1]!='/') path += "/";
1897    if ((dirp = opendir(path.c_str())) == NULL) return;
1898
1899    while ((dptr = readdir(dirp))) {
1900        if (dptr->d_name[0]=='.')
1901            continue;
1902        filepath = path+dptr->d_name;
1903        int statRet = stat(filepath.c_str(), &st);
1904        if (S_ISDIR(st.st_mode))
1905            scanPath(filepath, files);
1906        if (statRet != -1) {
1907            ext = filepath.substr(filepath.length()-4,4);
1908#ifdef PLATFORM_GP2X
1909            if (ext==".gpu")
1910#elif defined(PLATFORM_DINGUX) || defined(PLATFORM_NANONOTE)
1911            if (ext==".dge")
1912#else
1913            if (ext==".pxml")
1914#endif
1915                files->push_back(filepath);
1916        }
1917    }
1918
1919    closedir(dirp);
1920}
1921
1922unsigned short GMenu2X::getBatteryLevel() {
1923#ifdef PLATFORM_GP2X
1924/* if (batteryHandle<=0) return 0;
1925
1926    if (f200) {
1927        MMSP2ADC val;
1928        read(batteryHandle, &val, sizeof(MMSP2ADC));
1929
1930        if (val.batt==0) return 5;
1931        if (val.batt==1) return 3;
1932        if (val.batt==2) return 1;
1933        if (val.batt==3) return 0;
1934    } else {
1935        int battval = 0;
1936        unsigned short cbv, min=900, max=0;
1937
1938        for (int i = 0; i < BATTERY_READS; i ++) {
1939            if ( read(batteryHandle, &cbv, 2) == 2) {
1940                battval += cbv;
1941                if (cbv>max) max = cbv;
1942                if (cbv<min) min = cbv;
1943            }
1944        }
1945
1946        battval -= min+max;
1947        battval /= BATTERY_READS-2;
1948
1949        if (battval>=850) return 6;
1950        if (battval>780) return 5;
1951        if (battval>740) return 4;
1952        if (battval>700) return 3;
1953        if (battval>690) return 2;
1954        if (battval>680) return 1;
1955    }*/
1956
1957#else
1958    if (!batteryHandle) return 0;
1959    int battval = 0;
1960    char battvalcstr[5];
1961    fscanf(batteryHandle, "%s", &battvalcstr[0]);
1962    rewind(batteryHandle);
1963    battval = atoi(battvalcstr);
1964    if (battval>90) return 5;
1965    if (battval>70) return 4;
1966    if (battval>50) return 3;
1967    if (battval>30) return 2;
1968    if (battval>10) return 1;
1969
1970    if (!usbHandle) return 0;
1971    int usbval = 0;
1972    char usbvalcstr[5];
1973    fscanf(usbHandle, "%s", &usbvalcstr[0]);
1974    rewind(usbHandle);
1975    usbval = atoi(usbvalcstr);
1976    if (usbval==1) return 6;
1977
1978    return 0;
1979//#else
1980// return 6; //AC Power
1981#endif
1982}
1983
1984void GMenu2X::setInputSpeed() {
1985    /*
1986    input.setInterval(150);
1987    input.setInterval(30, VOLDOWN);
1988    input.setInterval(30, VOLUP );
1989    input.setInterval(30, CANCEL );
1990    input.setInterval(500, SETTINGS );
1991    input.setInterval(500, MENU );
1992    input.setInterval(300, CLEAR );
1993    input.setInterval(300, MANUAL );
1994    input.setInterval(1000,ACCEPT );
1995    //joy.setInterval(1000,ACTION_CLICK );
1996    input.setInterval(300, ALTLEFT );
1997    input.setInterval(300, ALTRIGHT );
1998    */
1999    SDL_EnableKeyRepeat(1,150);
2000}
2001
2002void GMenu2X::applyRamTimings() {
2003#ifdef PLATFORM_GP2X
2004    // 6 4 1 1 1 2 2
2005/* if (gp2x_mem!=0) {
2006        int tRC = 5, tRAS = 3, tWR = 0, tMRD = 0, tRFC = 0, tRP = 1, tRCD = 1;
2007        gp2x_memregs[0x3802>>1] = ((tMRD & 0xF) << 12) | ((tRFC & 0xF) << 8) | ((tRP & 0xF) << 4) | (tRCD & 0xF);
2008        gp2x_memregs[0x3804>>1] = ((tRC & 0xF) << 8) | ((tRAS & 0xF) << 4) | (tWR & 0xF);
2009    }*/
2010#endif
2011}
2012
2013void GMenu2X::applyDefaultTimings() {
2014#ifdef PLATFORM_GP2X
2015    // 8 16 3 8 8 8 8
2016/* if (gp2x_mem!=0) {
2017        int tRC = 7, tRAS = 15, tWR = 2, tMRD = 7, tRFC = 7, tRP = 7, tRCD = 7;
2018        gp2x_memregs[0x3802>>1] = ((tMRD & 0xF) << 12) | ((tRFC & 0xF) << 8) | ((tRP & 0xF) << 4) | (tRCD & 0xF);
2019        gp2x_memregs[0x3804>>1] = ((tRC & 0xF) << 8) | ((tRAS & 0xF) << 4) | (tWR & 0xF);
2020    }*/
2021#endif
2022}
2023
2024
2025
2026void GMenu2X::setClock(unsigned mhz) {
2027    mhz = constrain(mhz, 30, confInt["maxClock"]);
2028#if defined(PLATFORM_DINGUX) || defined(PLATFORM_NANONOTE)
2029    jz_cpuspeed(mhz);
2030#endif
2031}
2032
2033void GMenu2X::setGamma(int /*gamma*/) {
2034#ifdef PLATFORM_GP2X
2035/* float fgamma = (float)constrain(gamma,1,100)/10;
2036    fgamma = 1 / fgamma;
2037    MEM_REG[0x2880>>1]&=~(1<<12);
2038    MEM_REG[0x295C>>1]=0;
2039
2040    for (int i=0; i<256; i++) {
2041        unsigned char g = (unsigned char)(255.0*pow(i/255.0,fgamma));
2042        unsigned short s = (g<<8) | g;
2043        MEM_REG[0x295E>>1]= s;
2044        MEM_REG[0x295E>>1]= g;
2045    }*/
2046#endif
2047}
2048
2049int GMenu2X::getVolume() {
2050    unsigned long mixer;
2051        int basevolume = -1;
2052    mixer = open("/dev/mixer", O_RDONLY);
2053    if(mixer)
2054    {
2055        if (ioctl(mixer, SOUND_MIXER_READ_VOLUME, &basevolume) == -1) {
2056            ERROR("Failed opening mixer for read - VOLUME\n");
2057        }
2058        close(mixer);
2059        if(basevolume != -1)
2060            return (basevolume>>8) & basevolume ;
2061    }
2062    return basevolume;
2063}
2064
2065void GMenu2X::setVolume(int vol) {
2066    unsigned long mixer;
2067    int newvolume = vol;
2068    int oss_volume = newvolume | (newvolume << 8); // set volume for both channels
2069    mixer = open("/dev/mixer", O_WRONLY);
2070    if(mixer)
2071    {
2072        if (ioctl(mixer, SOUND_MIXER_WRITE_VOLUME, &oss_volume) == -1) {
2073            ERROR("Failed opening mixer for write - VOLUME\n");
2074        }
2075        close(mixer);
2076    }
2077
2078}
2079
2080void GMenu2X::setVolumeScaler(int scale) {
2081    scale = constrain(scale,0,MAX_VOLUME_SCALE_FACTOR);
2082    unsigned long soundDev = open("/dev/mixer", O_WRONLY);
2083    if (soundDev) {
2084        ioctl(soundDev, SOUND_MIXER_PRIVATE2, &scale);
2085        close(soundDev);
2086    }
2087}
2088
2089int GMenu2X::getVolumeScaler() {
2090    int currentscalefactor = -1;
2091    unsigned long soundDev = open("/dev/mixer", O_RDONLY);
2092    if (soundDev) {
2093        ioctl(soundDev, SOUND_MIXER_PRIVATE1, &currentscalefactor);
2094        close(soundDev);
2095    }
2096    return currentscalefactor;
2097}
2098
2099const string &GMenu2X::getExePath() {
2100    if (path.empty()) {
2101        char buf[255];
2102        memset(buf, 0, 255);
2103        int l = readlink("/proc/self/exe", buf, 255);
2104
2105        path = buf;
2106        path = path.substr(0,l);
2107        l = path.rfind("/");
2108        path = path.substr(0,l+1);
2109    }
2110    return path;
2111}
2112
2113string GMenu2X::getDiskFree() {
2114    stringstream ss;
2115    string df = "";
2116    struct statvfs b;
2117
2118    int ret = statvfs(CARD_ROOT, &b);
2119    if (ret==0) {
2120        // Make sure that the multiplication happens in 64 bits.
2121        unsigned long long free =
2122            ((unsigned long long)b.f_bfree * b.f_bsize) / 1048576;
2123        unsigned long long total =
2124            ((unsigned long long)b.f_blocks * b.f_frsize) / 1048576;
2125        ss << free << "/" << total << "MB";
2126        ss >> df;
2127    } else WARNING("statvfs failed with error '%s'.\n", strerror(errno));
2128    return df;
2129}
2130
2131int GMenu2X::drawButton(Button *btn, int x, int y) {
2132    if (y<0) y = resY+y;
2133    btn->setPosition(x, y-7);
2134    btn->paint();
2135    return x+btn->getRect().w+6;
2136}
2137
2138int GMenu2X::drawButton(Surface *s, const string &btn, const string &text, int x, int y) {
2139    if (y<0) y = resY+y;
2140    SDL_Rect re = {x, y-7, 0, 16};
2141    if (sc.skinRes("imgs/buttons/"+btn+".png") != NULL) {
2142        sc["imgs/buttons/"+btn+".png"]->blit(s, x, y-7);
2143        re.w = sc["imgs/buttons/"+btn+".png"]->width() + 3;
2144        s->write(font, text, x+re.w, y, ASFont::HAlignLeft, ASFont::VAlignMiddle);
2145        re.w += font->getTextWidth(text);
2146    }
2147    return x+re.w+6;
2148}
2149
2150int GMenu2X::drawButtonRight(Surface *s, const string &btn, const string &text, int x, int y) {
2151    if (y<0) y = resY+y;
2152    if (sc.skinRes("imgs/buttons/"+btn+".png") != NULL) {
2153        x -= 16;
2154        sc["imgs/buttons/"+btn+".png"]->blit(s, x, y-7);
2155        x -= 3;
2156        s->write(font, text, x, y, ASFont::HAlignRight, ASFont::VAlignMiddle);
2157        return x-6-font->getTextWidth(text);
2158    }
2159    return x-6;
2160}
2161
2162void GMenu2X::drawScrollBar(uint pagesize, uint totalsize, uint pagepos, uint top, uint height) {
2163    if (totalsize<=pagesize) return;
2164
2165    s->rectangle(resX-8, top, 7, height, skinConfColors[COLOR_SELECTION_BG]);
2166
2167    //internal bar total height = height-2
2168    //bar size
2169    uint bs = (height-2) * pagesize / totalsize;
2170    //bar y position
2171    uint by = (height-2) * pagepos / totalsize;
2172    by = top+2+by;
2173    if (by+bs>top+height-2) by = top+height-2-bs;
2174
2175
2176    s->box(resX-6, by, 3, bs, skinConfColors[COLOR_SELECTION_BG]);
2177}
2178
2179void GMenu2X::drawTopBar(Surface *s) {
2180    if (s==NULL) s = this->s;
2181
2182    Surface *bar = sc.skinRes("imgs/topbar.png", false);
2183    if (bar != NULL)
2184        bar->blit(s, 0, 0);
2185    else
2186        s->box(0, 0, resX, skinConfInt["topBarHeight"],
2187        skinConfColors[COLOR_TOP_BAR_BG]);
2188}
2189
2190void GMenu2X::drawBottomBar(Surface *s) {
2191    if (s==NULL) s = this->s;
2192
2193    Surface *bar = sc.skinRes("imgs/bottombar.png", false);
2194    if (bar != NULL)
2195        bar->blit(s, 0, resY-bar->height());
2196    else
2197        s->box(0, resY-20, resX, 20, skinConfColors[COLOR_BOTTOM_BAR_BG]);
2198}
2199

Archive Download this file



interactive