mirror of
git://projects.qi-hardware.com/gmenu2x.git
synced 2024-07-02 18:47:19 +03:00
LinkApp::drawRun() assumes the layers below are already painted when it is called, but this was not the case. With single buffering, the previous frame was still there so it still looked good, but with double buffering the buffer typically contains an outdated screen. Long term I think the launch should happen at the outermost scope, so all destructors get a chance to run. This commit is a small step in that direction, by exiting the main loop before launching.
777 lines
20 KiB
C++
777 lines
20 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2006 by Massimiliano Torromeo *
|
|
* massimiliano.torromeo@gmail.com *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
|
|
#include "linkapp.h"
|
|
|
|
#include "debug.h"
|
|
#include "delegate.h"
|
|
#include "gmenu2x.h"
|
|
#include "menu.h"
|
|
#include "selector.h"
|
|
#include "surface.h"
|
|
#include "textmanualdialog.h"
|
|
#include "utilities.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <fstream>
|
|
#include <sstream>
|
|
|
|
#if defined(PLATFORM_A320) || defined(PLATFORM_GCW0)
|
|
#include <linux/vt.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBOPK
|
|
#include <opk.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBXDGMIME
|
|
#include <xdgmime.h>
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
static const char *tokens[] = { "%f", "%F", "%u", "%U", };
|
|
|
|
#ifdef HAVE_LIBOPK
|
|
LinkApp::LinkApp(GMenu2X *gmenu2x_, Touchscreen &ts, InputManager &inputMgr_,
|
|
const char* linkfile, struct OPK *opk)
|
|
#else
|
|
LinkApp::LinkApp(GMenu2X *gmenu2x_, Touchscreen &ts, InputManager &inputMgr_,
|
|
const char* linkfile)
|
|
#endif
|
|
: Link(gmenu2x_, ts, BIND(&LinkApp::start))
|
|
, inputMgr(inputMgr_)
|
|
{
|
|
manual = "";
|
|
file = linkfile;
|
|
#ifdef ENABLE_CPUFREQ
|
|
setClock(gmenu2x->getDefaultAppClock());
|
|
#endif
|
|
selectordir = "";
|
|
selectorfilter = "*";
|
|
icon = iconPath = "";
|
|
selectorbrowser = true;
|
|
editable = true;
|
|
edited = false;
|
|
#if defined(PLATFORM_A320) || defined(PLATFORM_GCW0)
|
|
consoleApp = false;
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBOPK
|
|
isOPK = !!opk;
|
|
if (isOPK) {
|
|
string::size_type pos;
|
|
const char *key, *val;
|
|
size_t lkey, lval;
|
|
int ret;
|
|
|
|
opkFile = file;
|
|
pos = file.rfind('/');
|
|
opkMount = file.substr(pos+1);
|
|
pos = opkMount.rfind('.');
|
|
opkMount = opkMount.substr(0, pos);
|
|
|
|
file = gmenu2x->getHome() + "/sections/";
|
|
|
|
while ((ret = opk_read_pair(opk, &key, &lkey, &val, &lval))) {
|
|
if (ret < 0) {
|
|
ERROR("Unable to read meta-data\n");
|
|
break;
|
|
}
|
|
|
|
char buf[lval + 1];
|
|
sprintf(buf, "%.*s", lval, val);
|
|
|
|
if (!strncmp(key, "Categories", lkey)) {
|
|
category = buf;
|
|
|
|
pos = category.find(';');
|
|
if (pos != category.npos)
|
|
category = category.substr(0, pos);
|
|
file += category + '/' + opkMount;
|
|
|
|
} else if ((!strncmp(key, "Name", lkey) && title.empty())
|
|
|| !strncmp(key, ("Name[" + gmenu2x->tr["Lng"] +
|
|
"]").c_str(), lkey)) {
|
|
title = buf;
|
|
|
|
} else if ((!strncmp(key, "Comment", lkey) && description.empty())
|
|
|| !strncmp(key, ("Comment[" +
|
|
gmenu2x->tr["Lng"] + "]").c_str(), lkey)) {
|
|
description = buf;
|
|
|
|
#if defined(PLATFORM_A320) || defined(PLATFORM_GCW0)
|
|
} else if (!strncmp(key, "Terminal", lkey)) {
|
|
consoleApp = !strncmp(val, "true", lval);
|
|
#endif
|
|
|
|
} else if (!strncmp(key, "X-OD-Manual", lkey)) {
|
|
manual = buf;
|
|
|
|
} else if (!strncmp(key, "Icon", lkey)) {
|
|
/* Read the icon from the OPK only
|
|
* if it doesn't exist on the skin */
|
|
this->icon = gmenu2x->sc.getSkinFilePath("icons/" + (string) buf + ".png");
|
|
if (this->icon.empty())
|
|
this->icon = (string) linkfile + '#' + buf + ".png";
|
|
iconPath = this->icon;
|
|
updateSurfaces();
|
|
|
|
} else if (!strncmp(key, "Exec", lkey)) {
|
|
string tmp = buf;
|
|
pos = tmp.find(' ');
|
|
exec = tmp.substr(0, pos);
|
|
|
|
if (pos != tmp.npos) {
|
|
unsigned int i;
|
|
|
|
params = tmp.substr(pos + 1);
|
|
|
|
for (i = 0; i < sizeof(tokens) / sizeof(tokens[0]); i++) {
|
|
if (params.find(tokens[i]) != params.npos) {
|
|
selectordir = CARD_ROOT;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
#ifdef HAVE_LIBXDGMIME
|
|
if (!strncmp(key, "MimeType", lkey)) {
|
|
string mimetypes = buf;
|
|
selectorfilter = "";
|
|
|
|
while ((pos = mimetypes.find(';')) != mimetypes.npos) {
|
|
int nb = 16;
|
|
char *extensions[nb];
|
|
string mimetype = mimetypes.substr(0, pos);
|
|
mimetypes = mimetypes.substr(pos + 1);
|
|
|
|
nb = xdg_mime_get_extensions_from_mime_type(
|
|
mimetype.c_str(), extensions, nb);
|
|
|
|
while (nb--) {
|
|
selectorfilter += (string) extensions[nb] + ',';
|
|
free(extensions[nb]);
|
|
}
|
|
}
|
|
|
|
/* Remove last comma */
|
|
if (!selectorfilter.empty()) {
|
|
selectorfilter.erase(selectorfilter.end());
|
|
DEBUG("Compatible extensions: %s\n", selectorfilter.c_str());
|
|
}
|
|
|
|
continue;
|
|
}
|
|
#endif /* HAVE_LIBXDGMIME */
|
|
}
|
|
|
|
opkMount = (string) "/mnt/" + opkMount + '/';
|
|
edited = true;
|
|
}
|
|
#endif /* HAVE_LIBOPK */
|
|
|
|
string line;
|
|
ifstream infile (file.c_str(), ios_base::in);
|
|
while (getline(infile, line, '\n')) {
|
|
line = trim(line);
|
|
if (line.empty()) continue;
|
|
if (line[0]=='#') continue;
|
|
|
|
string::size_type position = line.find("=");
|
|
string name = trim(line.substr(0,position));
|
|
string value = trim(line.substr(position+1));
|
|
|
|
if (name == "clock") {
|
|
setClock( atoi(value.c_str()) );
|
|
} else if (name == "selectordir") {
|
|
setSelectorDir( value );
|
|
} else if (name == "selectorbrowser") {
|
|
if (value=="false") selectorbrowser = false;
|
|
} else if (name == "selectorscreens") {
|
|
setSelectorScreens( value );
|
|
} else if (name == "selectoraliases") {
|
|
setAliasFile( value );
|
|
} else if (!isOpk()) {
|
|
if (name == "title") {
|
|
title = value;
|
|
} else if (name == "description") {
|
|
description = value;
|
|
} else if (name == "icon") {
|
|
setIcon(value);
|
|
} else if (name == "exec") {
|
|
exec = value;
|
|
} else if (name == "params") {
|
|
params = value;
|
|
} else if (name == "manual") {
|
|
manual = value;
|
|
#if defined(PLATFORM_A320) || defined(PLATFORM_GCW0)
|
|
} else if (name == "consoleapp") {
|
|
if (value == "true") consoleApp = true;
|
|
#endif
|
|
} else if (name == "selectorfilter") {
|
|
setSelectorFilter( value );
|
|
} else if (name == "editable") {
|
|
if (value == "false")
|
|
editable = false;
|
|
} else
|
|
WARNING("Unrecognized option: '%s'\n", name.c_str());
|
|
} else
|
|
WARNING("Unrecognized option: '%s'\n", name.c_str());
|
|
}
|
|
infile.close();
|
|
|
|
if (iconPath.empty()) searchIcon();
|
|
}
|
|
|
|
void LinkApp::loadIcon() {
|
|
if (icon.compare(0, 5, "skin:") == 0) {
|
|
string linkIcon = gmenu2x->sc.getSkinFilePath(
|
|
icon.substr(5, string::npos));
|
|
if (!fileExists(linkIcon))
|
|
searchIcon();
|
|
else
|
|
setIconPath(linkIcon);
|
|
|
|
} else if (!fileExists(icon)) {
|
|
searchIcon();
|
|
}
|
|
}
|
|
|
|
const string &LinkApp::searchIcon() {
|
|
if (!iconPath.empty())
|
|
return iconPath;
|
|
|
|
string execicon = exec;
|
|
string::size_type pos = exec.rfind(".");
|
|
if (pos != string::npos) execicon = exec.substr(0,pos);
|
|
execicon += ".png";
|
|
string exectitle = execicon;
|
|
pos = execicon.rfind("/");
|
|
if (pos != string::npos)
|
|
string exectitle = execicon.substr(pos+1,execicon.length());
|
|
|
|
if (!gmenu2x->sc.getSkinFilePath("icons/"+exectitle).empty())
|
|
iconPath = gmenu2x->sc.getSkinFilePath("icons/"+exectitle);
|
|
else if (fileExists(execicon))
|
|
iconPath = execicon;
|
|
else
|
|
iconPath = gmenu2x->sc.getSkinFilePath("icons/generic.png");
|
|
|
|
return iconPath;
|
|
}
|
|
|
|
int LinkApp::clock() {
|
|
return iclock;
|
|
}
|
|
|
|
const string &LinkApp::clockStr(int maxClock) {
|
|
if (iclock>maxClock) setClock(maxClock);
|
|
return sclock;
|
|
}
|
|
|
|
void LinkApp::setClock(int mhz) {
|
|
iclock = mhz;
|
|
stringstream ss;
|
|
sclock = "";
|
|
ss << iclock << "MHz";
|
|
ss >> sclock;
|
|
|
|
edited = true;
|
|
}
|
|
|
|
bool LinkApp::targetExists()
|
|
{
|
|
return fileExists(exec);
|
|
}
|
|
|
|
bool LinkApp::save() {
|
|
if (!edited) return false;
|
|
|
|
DEBUG("Saving file: %s\n", file.c_str());
|
|
|
|
ofstream f(file.c_str());
|
|
if (f.is_open()) {
|
|
if (!isOpk()) {
|
|
if (!title.empty() ) f << "title=" << title << endl;
|
|
if (!description.empty() ) f << "description=" << description << endl;
|
|
if (!icon.empty() ) f << "icon=" << icon << endl;
|
|
if (!exec.empty() ) f << "exec=" << exec << endl;
|
|
if (!params.empty() ) f << "params=" << params << endl;
|
|
if (!manual.empty() ) f << "manual=" << manual << endl;
|
|
#if defined(PLATFORM_A320) || defined(PLATFORM_GCW0)
|
|
if (consoleApp ) f << "consoleapp=true" << endl;
|
|
#endif
|
|
if (selectorfilter != "*") f << "selectorfilter=" << selectorfilter << endl;
|
|
}
|
|
if (iclock != 0 ) f << "clock=" << iclock << endl;
|
|
if (!selectordir.empty() ) f << "selectordir=" << selectordir << endl;
|
|
if (!selectorbrowser ) f << "selectorbrowser=false" << endl;
|
|
if (!selectorscreens.empty() ) f << "selectorscreens=" << selectorscreens << endl;
|
|
if (!aliasfile.empty() ) f << "selectoraliases=" << aliasfile << endl;
|
|
f.close();
|
|
sync();
|
|
return true;
|
|
} else
|
|
ERROR("Error while opening the file '%s' for write.\n", file.c_str());
|
|
return false;
|
|
}
|
|
|
|
void LinkApp::drawRun() {
|
|
//Darkened background
|
|
gmenu2x->s->box(0, 0, gmenu2x->resX, gmenu2x->resY, 0,0,0,150);
|
|
|
|
string text = gmenu2x->tr.translate("Launching $1",getTitle().c_str(),NULL);
|
|
int textW = gmenu2x->font->getTextWidth(text);
|
|
int boxW = 62+textW;
|
|
int halfBoxW = boxW/2;
|
|
|
|
//outer box
|
|
gmenu2x->s->box(gmenu2x->halfX-2-halfBoxW, gmenu2x->halfY-23, halfBoxW*2+5, 47, gmenu2x->skinConfColors[COLOR_MESSAGE_BOX_BG]);
|
|
//inner rectangle
|
|
gmenu2x->s->rectangle(gmenu2x->halfX-halfBoxW, gmenu2x->halfY-21, boxW, 42, gmenu2x->skinConfColors[COLOR_MESSAGE_BOX_BORDER]);
|
|
|
|
int x = gmenu2x->halfX+10-halfBoxW;
|
|
/*if (!getIcon().empty())
|
|
gmenu2x->sc[getIcon()]->blit(gmenu2x->s,x,104);
|
|
else
|
|
gmenu2x->sc["icons/generic.png"]->blit(gmenu2x->s,x,104);*/
|
|
iconSurface->blit(gmenu2x->s,x,gmenu2x->halfY-16);
|
|
gmenu2x->s->write( gmenu2x->font, text, x+42, gmenu2x->halfY+1, Font::HAlignLeft, Font::VAlignMiddle );
|
|
gmenu2x->s->flip();
|
|
}
|
|
|
|
void LinkApp::start() {
|
|
if (!selectordir.empty())
|
|
selector();
|
|
else
|
|
gmenu2x->queueLaunch(this, "");
|
|
}
|
|
|
|
void LinkApp::showManual() {
|
|
if (manual.empty())
|
|
return;
|
|
|
|
#ifdef HAVE_LIBOPK
|
|
if (isOPK) {
|
|
vector<string> readme;
|
|
char *token, *ptr;
|
|
struct OPK *opk;
|
|
int err;
|
|
void *buf;
|
|
size_t len;
|
|
|
|
opk = opk_open(opkFile.c_str());
|
|
if (!opk) {
|
|
WARNING("Unable to open OPK to read manual\n");
|
|
return;
|
|
}
|
|
|
|
err = opk_extract_file(opk, manual.c_str(), &buf, &len);
|
|
if (err < 0) {
|
|
WARNING("Unable to read manual from OPK\n");
|
|
return;
|
|
}
|
|
opk_close(opk);
|
|
|
|
ptr = (char *) buf;
|
|
while((token = strchr(ptr, '\n'))) {
|
|
*token = '\0';
|
|
|
|
string str(ptr);
|
|
readme.push_back(str);
|
|
ptr = token + 1;
|
|
}
|
|
|
|
/* Add the last line */
|
|
string str(ptr);
|
|
readme.push_back(str);
|
|
free(buf);
|
|
|
|
if (manual.substr(manual.size()-8,8)==".man.txt") {
|
|
TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), &readme);
|
|
tmd.exec();
|
|
} else {
|
|
TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), &readme);
|
|
td.exec();
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
if (!fileExists(manual))
|
|
return;
|
|
|
|
// Png manuals
|
|
if (manual.substr(manual.size()-8,8)==".man.png") {
|
|
#ifdef ENABLE_CPUFREQ
|
|
//Raise the clock to speed-up the loading of the manual
|
|
gmenu2x->setSafeMaxClock();
|
|
#endif
|
|
|
|
Surface *pngman = Surface::loadImage(manual);
|
|
if (!pngman) {
|
|
return;
|
|
}
|
|
Surface *bg = Surface::loadImage(gmenu2x->confStr["wallpaper"]);
|
|
if (!bg) {
|
|
bg = Surface::emptySurface(gmenu2x->s->width(), gmenu2x->s->height());
|
|
}
|
|
bg->convertToDisplayFormat();
|
|
|
|
stringstream ss;
|
|
string pageStatus;
|
|
|
|
bool close = false, repaint = true;
|
|
int page = 0, pagecount = pngman->width() / 320;
|
|
|
|
ss << pagecount;
|
|
string spagecount;
|
|
ss >> spagecount;
|
|
|
|
#ifdef ENABLE_CPUFREQ
|
|
//Lower the clock
|
|
gmenu2x->setMenuClock();
|
|
#endif
|
|
|
|
while (!close) {
|
|
if (repaint) {
|
|
bg->blit(gmenu2x->s, 0, 0);
|
|
pngman->blit(gmenu2x->s, -page*320, 0);
|
|
|
|
gmenu2x->drawBottomBar(gmenu2x->s);
|
|
gmenu2x->drawButton(gmenu2x->s, "start", gmenu2x->tr["Exit"],
|
|
gmenu2x->drawButton(gmenu2x->s, "cancel", "",
|
|
gmenu2x->drawButton(gmenu2x->s, "right", gmenu2x->tr["Change page"],
|
|
gmenu2x->drawButton(gmenu2x->s, "left", "", 5)-10))-10);
|
|
|
|
ss.clear();
|
|
ss << page+1;
|
|
ss >> pageStatus;
|
|
pageStatus = gmenu2x->tr["Page"]+": "+pageStatus+"/"+spagecount;
|
|
gmenu2x->s->write(gmenu2x->font, pageStatus, 310, 230, Font::HAlignRight, Font::VAlignMiddle);
|
|
|
|
gmenu2x->s->flip();
|
|
repaint = false;
|
|
}
|
|
|
|
switch(inputMgr.waitForPressedButton()) {
|
|
case InputManager::SETTINGS:
|
|
case InputManager::CANCEL:
|
|
close = true;
|
|
break;
|
|
case InputManager::LEFT:
|
|
if (page > 0) {
|
|
page--;
|
|
repaint = true;
|
|
}
|
|
break;
|
|
case InputManager::RIGHT:
|
|
if (page < pagecount-1) {
|
|
page++;
|
|
repaint=true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
delete bg;
|
|
return;
|
|
}
|
|
|
|
// Txt manuals
|
|
if (manual.substr(manual.size()-8,8)==".man.txt") {
|
|
vector<string> txtman;
|
|
|
|
string line;
|
|
ifstream infile(manual.c_str(), ios_base::in);
|
|
if (infile.is_open()) {
|
|
while (getline(infile, line, '\n')) txtman.push_back(line);
|
|
infile.close();
|
|
|
|
TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), &txtman);
|
|
tmd.exec();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//Readmes
|
|
vector<string> readme;
|
|
|
|
string line;
|
|
ifstream infile(manual.c_str(), ios_base::in);
|
|
if (infile.is_open()) {
|
|
while (getline(infile, line, '\n')) readme.push_back(line);
|
|
infile.close();
|
|
|
|
TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), &readme);
|
|
td.exec();
|
|
}
|
|
}
|
|
|
|
void LinkApp::selector(int startSelection, const string &selectorDir) {
|
|
//Run selector interface
|
|
Selector sel(gmenu2x, this, selectorDir);
|
|
int selection = sel.exec(startSelection);
|
|
if (selection!=-1) {
|
|
const string &selectedDir = sel.getDir();
|
|
if (!selectedDir.empty()) {
|
|
selectordir = selectedDir;
|
|
}
|
|
gmenu2x->writeTmp(selection, selectedDir);
|
|
gmenu2x->queueLaunch(this, selectedDir + sel.getFile());
|
|
}
|
|
}
|
|
|
|
void LinkApp::launch(const string &selectedFile) {
|
|
save();
|
|
|
|
if (isOpk()) {
|
|
#ifdef HAVE_LIBOPK
|
|
int err;
|
|
|
|
/* To be sure... */
|
|
string cmd = "umount " + opkMount;
|
|
system(cmd.c_str());
|
|
|
|
mkdir(opkMount.c_str(), 0700);
|
|
cmd = "mount -o loop,nosuid,ro " + opkFile + ' ' + opkMount;
|
|
err = system(cmd.c_str());
|
|
|
|
if (err) {
|
|
ERROR("Unable to mount OPK\n");
|
|
return;
|
|
}
|
|
|
|
chdir(opkMount.c_str());
|
|
if (exec[0] != '/') {
|
|
string tmp = opkMount + exec.substr(0, exec.find(" "));
|
|
if (fileExists(tmp))
|
|
exec = opkMount + exec;
|
|
}
|
|
#endif
|
|
} else {
|
|
//Set correct working directory
|
|
string::size_type pos = exec.rfind("/");
|
|
if (pos != string::npos) {
|
|
string wd = exec.substr(0, pos + 1);
|
|
chdir(wd.c_str());
|
|
exec = wd + exec.substr(pos + 1);
|
|
DEBUG("Changed working directory to %s\n", wd.c_str());
|
|
}
|
|
}
|
|
|
|
if (!selectedFile.empty()) {
|
|
string path = cmdclean(selectedFile);
|
|
|
|
if (params.empty()) {
|
|
params = path;
|
|
} else {
|
|
unsigned int i;
|
|
string::size_type pos;
|
|
|
|
for (i = 0; i < sizeof(tokens) / sizeof(tokens[0]); i++)
|
|
while((pos = params.find(tokens[i])) != params.npos)
|
|
params.replace(pos, 2, path);
|
|
}
|
|
}
|
|
|
|
INFO("Executing '%s' (%s %s)\n", title.c_str(), exec.c_str(), params.c_str());
|
|
|
|
//check if we have to quit
|
|
string command = cmdclean(exec);
|
|
|
|
// Check to see if permissions are desirable
|
|
struct stat fstat;
|
|
if( stat( command.c_str(), &fstat ) == 0 ) {
|
|
struct stat newstat = fstat;
|
|
if( S_IRUSR != ( fstat.st_mode & S_IRUSR ) )
|
|
newstat.st_mode |= S_IRUSR;
|
|
if( S_IXUSR != ( fstat.st_mode & S_IXUSR ) )
|
|
newstat.st_mode |= S_IXUSR;
|
|
if( fstat.st_mode != newstat.st_mode )
|
|
chmod( command.c_str(), newstat.st_mode );
|
|
} // else, well.. we are no worse off :)
|
|
|
|
if (!params.empty()) command += " " + params;
|
|
#if defined(PLATFORM_A320) || defined(PLATFORM_GCW0)
|
|
if (gmenu2x->confInt["outputLogs"] && !consoleApp)
|
|
command += " &> " + cmdclean(gmenu2x->getHome()) + "/log.txt";
|
|
#else
|
|
if (gmenu2x->confInt["outputLogs"])
|
|
command += " &> " + cmdclean(gmenu2x->getHome()) + "/log.txt";
|
|
#endif
|
|
#ifdef HAVE_LIBOPK
|
|
if (isOPK)
|
|
command += " ; umount -l " + opkMount;
|
|
#endif
|
|
|
|
gmenu2x->saveSelection();
|
|
|
|
if (selectedFile.empty()) {
|
|
gmenu2x->writeTmp();
|
|
}
|
|
#ifdef ENABLE_CPUFREQ
|
|
if (clock() != gmenu2x->confInt["menuClock"]) {
|
|
gmenu2x->setClock(clock());
|
|
}
|
|
#endif
|
|
gmenu2x->quit();
|
|
|
|
/* Make the terminal we're connected to (via stdin/stdout) our
|
|
controlling terminal again. Else many console programs are
|
|
not going to work correctly. Actually this would not be
|
|
necessary, if SDL correctly restored terminal state after
|
|
SDL_Quit(). */
|
|
(void) setsid();
|
|
|
|
ioctl(1, TIOCSCTTY, STDOUT_FILENO);
|
|
(void) dup2(STDOUT_FILENO, 0);
|
|
(void) dup2(STDOUT_FILENO, 1);
|
|
(void) dup2(STDOUT_FILENO, 2);
|
|
|
|
if (STDOUT_FILENO > 2)
|
|
close(STDOUT_FILENO);
|
|
|
|
int pgid = tcgetpgrp(STDOUT_FILENO);
|
|
signal(SIGTTOU, SIG_IGN);
|
|
tcsetpgrp(STDOUT_FILENO, pgid);
|
|
|
|
#if defined(PLATFORM_A320) || defined(PLATFORM_GCW0)
|
|
if (consoleApp) {
|
|
/* Enable the framebuffer console */
|
|
char c = '1';
|
|
int fd = open("/sys/devices/virtual/vtconsole/vtcon1/bind", O_WRONLY);
|
|
if (fd < 0) {
|
|
WARNING("Unable to open fbcon handle\n");
|
|
} else {
|
|
write(fd, &c, 1);
|
|
close(fd);
|
|
}
|
|
|
|
fd = open("/dev/tty1", O_RDWR);
|
|
if (fd < 0) {
|
|
WARNING("Unable to open tty1 handle\n");
|
|
} else {
|
|
if (ioctl(fd, VT_ACTIVATE, 1) < 0)
|
|
WARNING("Unable to activate tty1\n");
|
|
close(fd);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
execlp("/bin/sh","/bin/sh", "-c", command.c_str(), NULL);
|
|
//if execution continues then something went wrong and as we already called SDL_Quit we cannot continue
|
|
//try relaunching gmenu2x
|
|
gmenu2x->main();
|
|
}
|
|
|
|
const string &LinkApp::getExec() {
|
|
return exec;
|
|
}
|
|
|
|
void LinkApp::setExec(const string &exec) {
|
|
this->exec = exec;
|
|
edited = true;
|
|
}
|
|
|
|
const string &LinkApp::getParams() {
|
|
return params;
|
|
}
|
|
|
|
void LinkApp::setParams(const string ¶ms) {
|
|
this->params = params;
|
|
edited = true;
|
|
}
|
|
|
|
const string &LinkApp::getManual() {
|
|
return manual;
|
|
}
|
|
|
|
void LinkApp::setManual(const string &manual) {
|
|
this->manual = manual;
|
|
edited = true;
|
|
}
|
|
|
|
const string &LinkApp::getSelectorDir() {
|
|
return selectordir;
|
|
}
|
|
|
|
void LinkApp::setSelectorDir(const string &selectordir) {
|
|
this->selectordir = selectordir;
|
|
if (!selectordir.empty() && selectordir[selectordir.length() - 1] != '/') {
|
|
this->selectordir += "/";
|
|
}
|
|
edited = true;
|
|
}
|
|
|
|
bool LinkApp::getSelectorBrowser() {
|
|
return selectorbrowser;
|
|
}
|
|
|
|
void LinkApp::setSelectorBrowser(bool value) {
|
|
selectorbrowser = value;
|
|
edited = true;
|
|
}
|
|
|
|
const string &LinkApp::getSelectorFilter() {
|
|
return selectorfilter;
|
|
}
|
|
|
|
void LinkApp::setSelectorFilter(const string &selectorfilter) {
|
|
this->selectorfilter = selectorfilter;
|
|
edited = true;
|
|
}
|
|
|
|
const string &LinkApp::getSelectorScreens() {
|
|
return selectorscreens;
|
|
}
|
|
|
|
void LinkApp::setSelectorScreens(const string &selectorscreens) {
|
|
this->selectorscreens = selectorscreens;
|
|
edited = true;
|
|
}
|
|
|
|
const string &LinkApp::getAliasFile() {
|
|
return aliasfile;
|
|
}
|
|
|
|
void LinkApp::setAliasFile(const string &aliasfile) {
|
|
if (fileExists(aliasfile)) {
|
|
this->aliasfile = aliasfile;
|
|
edited = true;
|
|
}
|
|
}
|
|
|
|
void LinkApp::renameFile(const string &name) {
|
|
file = name;
|
|
}
|