Root/src/linkapp.cpp

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 "linkapp.h"
22
23#include "debug.h"
24#include "delegate.h"
25#include "gmenu2x.h"
26#include "menu.h"
27#include "selector.h"
28#include "surface.h"
29#include "textmanualdialog.h"
30#include "utilities.h"
31
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <sys/ioctl.h>
35#include <signal.h>
36#include <stdlib.h>
37#include <unistd.h>
38#include <fcntl.h>
39
40#include <fstream>
41#include <sstream>
42
43#if defined(PLATFORM_A320) || defined(PLATFORM_GCW0)
44#include <linux/vt.h>
45#endif
46
47#ifdef HAVE_LIBOPK
48#include <opk.h>
49#endif
50
51#ifdef HAVE_LIBXDGMIME
52#include <xdgmime.h>
53#endif
54
55using namespace std;
56
57static const char *tokens[] = { "%f", "%F", "%u", "%U", };
58
59#ifdef HAVE_LIBOPK
60LinkApp::LinkApp(GMenu2X *gmenu2x_, const char* linkfile,
61            struct OPK *opk, const char *metadata_)
62#else
63LinkApp::LinkApp(GMenu2X *gmenu2x_, const char* linkfile)
64#endif
65    : Link(gmenu2x_, BIND(&LinkApp::start))
66{
67    manual = "";
68    file = linkfile;
69#ifdef ENABLE_CPUFREQ
70    setClock(gmenu2x->getDefaultAppClock());
71#endif
72    selectordir = "";
73    selectorfilter = "*";
74    icon = iconPath = "";
75    selectorbrowser = true;
76    editable = true;
77    edited = false;
78#if defined(PLATFORM_A320) || defined(PLATFORM_GCW0)
79    consoleApp = false;
80#endif
81
82#ifdef HAVE_LIBOPK
83    isOPK = !!opk;
84
85    if (isOPK) {
86        string::size_type pos;
87        const char *key, *val;
88        size_t lkey, lval;
89        int ret;
90
91        metadata.assign(metadata_);
92        opkFile = file;
93        pos = file.rfind('/');
94        opkMount = file.substr(pos+1);
95        pos = opkMount.rfind('.');
96        opkMount = opkMount.substr(0, pos);
97
98        file = gmenu2x->getHome() + "/sections/";
99
100        while ((ret = opk_read_pair(opk, &key, &lkey, &val, &lval))) {
101            if (ret < 0) {
102                ERROR("Unable to read meta-data\n");
103                break;
104            }
105
106            char buf[lval + 1];
107            sprintf(buf, "%.*s", lval, val);
108
109            if (!strncmp(key, "Categories", lkey)) {
110                category = buf;
111
112                pos = category.find(';');
113                if (pos != category.npos)
114                    category = category.substr(0, pos);
115                file += category + '/' + opkMount;
116
117            } else if ((!strncmp(key, "Name", lkey) && title.empty())
118                        || !strncmp(key, ("Name[" + gmenu2x->tr["Lng"] +
119                                "]").c_str(), lkey)) {
120                title = buf;
121
122            } else if ((!strncmp(key, "Comment", lkey) && description.empty())
123                        || !strncmp(key, ("Comment[" +
124                                gmenu2x->tr["Lng"] + "]").c_str(), lkey)) {
125                description = buf;
126
127#if defined(PLATFORM_A320) || defined(PLATFORM_GCW0)
128            } else if (!strncmp(key, "Terminal", lkey)) {
129                consoleApp = !strncmp(val, "true", lval);
130#endif
131
132            } else if (!strncmp(key, "X-OD-Manual", lkey)) {
133                manual = buf;
134
135            } else if (!strncmp(key, "Icon", lkey)) {
136                /* Read the icon from the OPK only
137                 * if it doesn't exist on the skin */
138                this->icon = gmenu2x->sc.getSkinFilePath("icons/" + (string) buf + ".png");
139                if (this->icon.empty())
140                    this->icon = (string) linkfile + '#' + buf + ".png";
141                iconPath = this->icon;
142                updateSurfaces();
143
144                continue;
145            }
146
147#ifdef HAVE_LIBXDGMIME
148            if (!strncmp(key, "MimeType", lkey)) {
149                string mimetypes = buf;
150                selectorfilter = "";
151
152                while ((pos = mimetypes.find(';')) != mimetypes.npos) {
153                    int nb = 16;
154                    char *extensions[nb];
155                    string mimetype = mimetypes.substr(0, pos);
156                    mimetypes = mimetypes.substr(pos + 1);
157
158                    nb = xdg_mime_get_extensions_from_mime_type(
159                                mimetype.c_str(), extensions, nb);
160
161                    while (nb--) {
162                        selectorfilter += (string) extensions[nb] + ',';
163                        free(extensions[nb]);
164                    }
165                }
166
167                /* Remove last comma */
168                if (!selectorfilter.empty()) {
169                    selectorfilter.erase(selectorfilter.end());
170                    DEBUG("Compatible extensions: %s\n", selectorfilter.c_str());
171                }
172
173                continue;
174            }
175#endif /* HAVE_LIBXDGMIME */
176        }
177
178        opkMount = (string) "/mnt/" + opkMount + '/';
179        edited = true;
180    }
181#endif /* HAVE_LIBOPK */
182
183    string line;
184    ifstream infile (file.c_str(), ios_base::in);
185    while (getline(infile, line, '\n')) {
186        line = trim(line);
187        if (line.empty()) continue;
188        if (line[0]=='#') continue;
189
190        string::size_type position = line.find("=");
191        string name = trim(line.substr(0,position));
192        string value = trim(line.substr(position+1));
193
194        if (name == "clock") {
195            setClock( atoi(value.c_str()) );
196        } else if (name == "selectordir") {
197            setSelectorDir( value );
198        } else if (name == "selectorbrowser") {
199            if (value=="false") selectorbrowser = false;
200        } else if (name == "selectorscreens") {
201            setSelectorScreens( value );
202        } else if (name == "selectoraliases") {
203            setAliasFile( value );
204        } else if (!isOpk()) {
205            if (name == "title") {
206                title = value;
207            } else if (name == "description") {
208                description = value;
209            } else if (name == "icon") {
210                setIcon(value);
211            } else if (name == "exec") {
212                exec = value;
213            } else if (name == "params") {
214                params = value;
215            } else if (name == "manual") {
216                manual = value;
217#if defined(PLATFORM_A320) || defined(PLATFORM_GCW0)
218            } else if (name == "consoleapp") {
219                if (value == "true") consoleApp = true;
220#endif
221            } else if (name == "selectorfilter") {
222                setSelectorFilter( value );
223            } else if (name == "editable") {
224                if (value == "false")
225                    editable = false;
226            } else
227                WARNING("Unrecognized option: '%s'\n", name.c_str());
228        } else
229            WARNING("Unrecognized option: '%s'\n", name.c_str());
230    }
231    infile.close();
232
233    if (iconPath.empty()) searchIcon();
234}
235
236void LinkApp::loadIcon() {
237    if (icon.compare(0, 5, "skin:") == 0) {
238        string linkIcon = gmenu2x->sc.getSkinFilePath(
239                icon.substr(5, string::npos));
240        if (!fileExists(linkIcon))
241            searchIcon();
242        else
243            setIconPath(linkIcon);
244
245    } else if (!fileExists(icon)) {
246        searchIcon();
247    }
248}
249
250const string &LinkApp::searchIcon() {
251    if (!iconPath.empty())
252        return iconPath;
253
254    string execicon = exec;
255    string::size_type pos = exec.rfind(".");
256    if (pos != string::npos) execicon = exec.substr(0,pos);
257    execicon += ".png";
258    string exectitle = execicon;
259    pos = execicon.rfind("/");
260    if (pos != string::npos)
261        string exectitle = execicon.substr(pos+1,execicon.length());
262
263    if (!gmenu2x->sc.getSkinFilePath("icons/"+exectitle).empty())
264        iconPath = gmenu2x->sc.getSkinFilePath("icons/"+exectitle);
265    else if (fileExists(execicon))
266        iconPath = execicon;
267    else
268        iconPath = gmenu2x->sc.getSkinFilePath("icons/generic.png");
269
270    return iconPath;
271}
272
273int LinkApp::clock() {
274    return iclock;
275}
276
277const string &LinkApp::clockStr(int maxClock) {
278    if (iclock>maxClock) setClock(maxClock);
279    return sclock;
280}
281
282void LinkApp::setClock(int mhz) {
283    iclock = mhz;
284    stringstream ss;
285    sclock = "";
286    ss << iclock << "MHz";
287    ss >> sclock;
288
289    edited = true;
290}
291
292bool LinkApp::targetExists()
293{
294    return fileExists(exec);
295}
296
297bool LinkApp::save() {
298    if (!edited) return false;
299
300    DEBUG("Saving file: %s\n", file.c_str());
301
302    ofstream f(file.c_str());
303    if (f.is_open()) {
304        if (!isOpk()) {
305            if (!title.empty() ) f << "title=" << title << endl;
306            if (!description.empty() ) f << "description=" << description << endl;
307            if (!icon.empty() ) f << "icon=" << icon << endl;
308            if (!exec.empty() ) f << "exec=" << exec << endl;
309            if (!params.empty() ) f << "params=" << params << endl;
310            if (!manual.empty() ) f << "manual=" << manual << endl;
311#if defined(PLATFORM_A320) || defined(PLATFORM_GCW0)
312            if (consoleApp ) f << "consoleapp=true" << endl;
313#endif
314            if (selectorfilter != "*") f << "selectorfilter=" << selectorfilter << endl;
315        }
316        if (iclock != 0 ) f << "clock=" << iclock << endl;
317        if (!selectordir.empty() ) f << "selectordir=" << selectordir << endl;
318        if (!selectorbrowser ) f << "selectorbrowser=false" << endl;
319        if (!selectorscreens.empty() ) f << "selectorscreens=" << selectorscreens << endl;
320        if (!aliasfile.empty() ) f << "selectoraliases=" << aliasfile << endl;
321        f.close();
322        sync();
323        return true;
324    } else
325        ERROR("Error while opening the file '%s' for write.\n", file.c_str());
326    return false;
327}
328
329void LinkApp::drawRun() {
330    //Darkened background
331    gmenu2x->s->box(0, 0, gmenu2x->resX, gmenu2x->resY, 0,0,0,150);
332
333    string text = gmenu2x->tr.translate("Launching $1",getTitle().c_str(),NULL);
334    int textW = gmenu2x->font->getTextWidth(text);
335    int boxW = 62+textW;
336    int halfBoxW = boxW/2;
337
338    //outer box
339    gmenu2x->s->box(gmenu2x->halfX-2-halfBoxW, gmenu2x->halfY-23, halfBoxW*2+5, 47, gmenu2x->skinConfColors[COLOR_MESSAGE_BOX_BG]);
340    //inner rectangle
341    gmenu2x->s->rectangle(gmenu2x->halfX-halfBoxW, gmenu2x->halfY-21, boxW, 42, gmenu2x->skinConfColors[COLOR_MESSAGE_BOX_BORDER]);
342
343    int x = gmenu2x->halfX+10-halfBoxW;
344    /*if (!getIcon().empty())
345        gmenu2x->sc[getIcon()]->blit(gmenu2x->s,x,104);
346    else
347        gmenu2x->sc["icons/generic.png"]->blit(gmenu2x->s,x,104);*/
348    iconSurface->blit(gmenu2x->s,x,gmenu2x->halfY-16);
349    gmenu2x->s->write( gmenu2x->font, text, x+42, gmenu2x->halfY+1, Font::HAlignLeft, Font::VAlignMiddle );
350    gmenu2x->s->flip();
351}
352
353void LinkApp::start() {
354    if (!selectordir.empty())
355        selector();
356    else
357        gmenu2x->queueLaunch(this, "");
358}
359
360void LinkApp::showManual() {
361    if (manual.empty())
362        return;
363
364#ifdef HAVE_LIBOPK
365    if (isOPK) {
366        vector<string> readme;
367        char *token, *ptr;
368        struct OPK *opk;
369        int err;
370        void *buf;
371        size_t len;
372
373        opk = opk_open(opkFile.c_str());
374        if (!opk) {
375            WARNING("Unable to open OPK to read manual\n");
376            return;
377        }
378
379        err = opk_extract_file(opk, manual.c_str(), &buf, &len);
380        if (err < 0) {
381            WARNING("Unable to read manual from OPK\n");
382            return;
383        }
384        opk_close(opk);
385
386        ptr = (char *) buf;
387        while((token = strchr(ptr, '\n'))) {
388            *token = '\0';
389
390            string str(ptr);
391            readme.push_back(str);
392            ptr = token + 1;
393        }
394
395        /* Add the last line */
396        string str(ptr);
397        readme.push_back(str);
398        free(buf);
399
400        if (manual.substr(manual.size()-8,8)==".man.txt") {
401            TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), &readme);
402            tmd.exec();
403        } else {
404            TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), &readme);
405            td.exec();
406        }
407        return;
408    }
409#endif
410    if (!fileExists(manual))
411        return;
412
413    // Png manuals
414    if (manual.substr(manual.size()-8,8)==".man.png") {
415#ifdef ENABLE_CPUFREQ
416        //Raise the clock to speed-up the loading of the manual
417        gmenu2x->setSafeMaxClock();
418#endif
419
420        Surface *pngman = Surface::loadImage(manual);
421        if (!pngman) {
422            return;
423        }
424        Surface *bg = Surface::loadImage(gmenu2x->confStr["wallpaper"]);
425        if (!bg) {
426            bg = Surface::emptySurface(gmenu2x->s->width(), gmenu2x->s->height());
427        }
428        bg->convertToDisplayFormat();
429
430        stringstream ss;
431        string pageStatus;
432
433        bool close = false, repaint = true;
434        int page = 0, pagecount = pngman->width() / 320;
435
436        ss << pagecount;
437        string spagecount;
438        ss >> spagecount;
439
440#ifdef ENABLE_CPUFREQ
441        //Lower the clock
442        gmenu2x->setMenuClock();
443#endif
444
445        while (!close) {
446            if (repaint) {
447                bg->blit(gmenu2x->s, 0, 0);
448                pngman->blit(gmenu2x->s, -page*320, 0);
449
450                gmenu2x->drawBottomBar(gmenu2x->s);
451                gmenu2x->drawButton(gmenu2x->s, "start", gmenu2x->tr["Exit"],
452                gmenu2x->drawButton(gmenu2x->s, "cancel", "",
453                gmenu2x->drawButton(gmenu2x->s, "right", gmenu2x->tr["Change page"],
454                gmenu2x->drawButton(gmenu2x->s, "left", "", 5)-10))-10);
455
456                ss.clear();
457                ss << page+1;
458                ss >> pageStatus;
459                pageStatus = gmenu2x->tr["Page"]+": "+pageStatus+"/"+spagecount;
460                gmenu2x->s->write(gmenu2x->font, pageStatus, 310, 230, Font::HAlignRight, Font::VAlignMiddle);
461
462                gmenu2x->s->flip();
463                repaint = false;
464            }
465
466            switch(gmenu2x->input.waitForPressedButton()) {
467                case InputManager::SETTINGS:
468                case InputManager::CANCEL:
469                    close = true;
470                    break;
471                case InputManager::LEFT:
472                    if (page > 0) {
473                        page--;
474                        repaint = true;
475                    }
476                    break;
477                case InputManager::RIGHT:
478                    if (page < pagecount-1) {
479                        page++;
480                        repaint=true;
481                    }
482                    break;
483                default:
484                    break;
485            }
486        }
487        delete bg;
488        return;
489    }
490
491    // Txt manuals
492    if (manual.substr(manual.size()-8,8)==".man.txt") {
493        vector<string> txtman;
494
495        string line;
496        ifstream infile(manual.c_str(), ios_base::in);
497        if (infile.is_open()) {
498            while (getline(infile, line, '\n')) txtman.push_back(line);
499            infile.close();
500
501            TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), &txtman);
502            tmd.exec();
503        }
504
505        return;
506    }
507
508    //Readmes
509    vector<string> readme;
510
511    string line;
512    ifstream infile(manual.c_str(), ios_base::in);
513    if (infile.is_open()) {
514        while (getline(infile, line, '\n')) readme.push_back(line);
515        infile.close();
516
517        TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), &readme);
518        td.exec();
519    }
520}
521
522void LinkApp::selector(int startSelection, const string &selectorDir) {
523    //Run selector interface
524    Selector sel(gmenu2x, this, selectorDir);
525    int selection = sel.exec(startSelection);
526    if (selection!=-1) {
527        const string &selectedDir = sel.getDir();
528        if (!selectedDir.empty()) {
529            selectordir = selectedDir;
530        }
531        gmenu2x->writeTmp(selection, selectedDir);
532        gmenu2x->queueLaunch(this, selectedDir + sel.getFile());
533    }
534}
535
536void LinkApp::launch(const string &selectedFile) {
537    save();
538
539    if (!isOpk()) {
540        //Set correct working directory
541        string::size_type pos = exec.rfind("/");
542        if (pos != string::npos) {
543            string wd = exec.substr(0, pos + 1);
544            chdir(wd.c_str());
545            exec = wd + exec.substr(pos + 1);
546            DEBUG("Changed working directory to %s\n", wd.c_str());
547        }
548    }
549
550    if (!selectedFile.empty()) {
551        string path = selectedFile;
552
553        if (params.empty()) {
554            params = path;
555        } else {
556            unsigned int i;
557            string::size_type pos;
558
559            for (i = 0; i < sizeof(tokens) / sizeof(tokens[0]); i++)
560                while((pos = params.find(tokens[i])) != params.npos)
561                    params.replace(pos, 2, path);
562        }
563    }
564
565    if (gmenu2x->confInt["outputLogs"]
566#if defined(PLATFORM_A320) || defined(PLATFORM_GCW0)
567                && !consoleApp
568#endif
569    ) {
570        int fd = open(LOG_FILE, O_WRONLY | O_TRUNC | O_CREAT, 0644);
571        if (fd < 0) {
572            ERROR("Unable to open log file for write: %s\n", LOG_FILE);
573        } else {
574            fflush(stdout);
575            dup2(fd, STDOUT_FILENO);
576            dup2(fd, STDERR_FILENO);
577            close(fd);
578        }
579    }
580
581    gmenu2x->saveSelection();
582
583    if (selectedFile.empty()) {
584        gmenu2x->writeTmp();
585    }
586#ifdef ENABLE_CPUFREQ
587    if (clock() != gmenu2x->confInt["menuClock"]) {
588        gmenu2x->setClock(clock());
589    }
590#endif
591    gmenu2x->quit();
592
593#if defined(PLATFORM_A320) || defined(PLATFORM_GCW0)
594    if (consoleApp) {
595        /* Enable the framebuffer console */
596        char c = '1';
597        int fd = open("/sys/devices/virtual/vtconsole/vtcon1/bind", O_WRONLY);
598        if (fd < 0) {
599            WARNING("Unable to open fbcon handle\n");
600        } else {
601            write(fd, &c, 1);
602            close(fd);
603        }
604
605        fd = open("/dev/tty1", O_RDWR);
606        if (fd < 0) {
607            WARNING("Unable to open tty1 handle\n");
608        } else {
609            if (ioctl(fd, VT_ACTIVATE, 1) < 0)
610                WARNING("Unable to activate tty1\n");
611            close(fd);
612        }
613    }
614#endif
615
616    if (isOpk()) {
617#ifdef HAVE_LIBOPK
618        execlp("opkrun", "opkrun", "-m", metadata.c_str(), opkFile.c_str(),
619                    !params.empty() ? params.c_str() : NULL,
620                    NULL);
621#endif
622    } else {
623        std::string command = exec + " " + params;
624        INFO("Executing '%s' (%s)\n", title.c_str(), command.c_str());
625        execlp("/bin/sh", "/bin/sh", "-c", command.c_str(), NULL);
626    }
627
628    //if execution continues then something went wrong and as we already called SDL_Quit we cannot continue
629    //try relaunching gmenu2x
630    gmenu2x->main();
631}
632
633const string &LinkApp::getExec() {
634    return exec;
635}
636
637void LinkApp::setExec(const string &exec) {
638    this->exec = exec;
639    edited = true;
640}
641
642const string &LinkApp::getParams() {
643    return params;
644}
645
646void LinkApp::setParams(const string &params) {
647    this->params = params;
648    edited = true;
649}
650
651const string &LinkApp::getManual() {
652    return manual;
653}
654
655void LinkApp::setManual(const string &manual) {
656    this->manual = manual;
657    edited = true;
658}
659
660const string &LinkApp::getSelectorDir() {
661    return selectordir;
662}
663
664void LinkApp::setSelectorDir(const string &selectordir) {
665    this->selectordir = selectordir;
666    if (!selectordir.empty() && selectordir[selectordir.length() - 1] != '/') {
667        this->selectordir += "/";
668    }
669    edited = true;
670}
671
672bool LinkApp::getSelectorBrowser() {
673    return selectorbrowser;
674}
675
676void LinkApp::setSelectorBrowser(bool value) {
677    selectorbrowser = value;
678    edited = true;
679}
680
681const string &LinkApp::getSelectorFilter() {
682    return selectorfilter;
683}
684
685void LinkApp::setSelectorFilter(const string &selectorfilter) {
686    this->selectorfilter = selectorfilter;
687    edited = true;
688}
689
690const string &LinkApp::getSelectorScreens() {
691    return selectorscreens;
692}
693
694void LinkApp::setSelectorScreens(const string &selectorscreens) {
695    this->selectorscreens = selectorscreens;
696    edited = true;
697}
698
699const string &LinkApp::getAliasFile() {
700    return aliasfile;
701}
702
703void LinkApp::setAliasFile(const string &aliasfile) {
704    if (fileExists(aliasfile)) {
705        this->aliasfile = aliasfile;
706        edited = true;
707    }
708}
709
710void LinkApp::renameFile(const string &name) {
711    file = name;
712}
713

Archive Download this file



interactive