summaryrefslogtreecommitdiff
path: root/core/launcher/applauncher.cpp
authorzecke <zecke>2003-08-25 15:00:50 (UTC)
committer zecke <zecke>2003-08-25 15:00:50 (UTC)
commitbf3c4abb9dff716e098f05852d9a3d8ac8cbcb44 (patch) (side-by-side diff)
treef74d4765868000d27c45771573032515ac8db8be /core/launcher/applauncher.cpp
parent292b097e7db25dd231381c5b09307a1fbe81a492 (diff)
downloadopie-bf3c4abb9dff716e098f05852d9a3d8ac8cbcb44.zip
opie-bf3c4abb9dff716e098f05852d9a3d8ac8cbcb44.tar.gz
opie-bf3c4abb9dff716e098f05852d9a3d8ac8cbcb44.tar.bz2
Initial revision
Diffstat (limited to 'core/launcher/applauncher.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--core/launcher/applauncher.cpp705
1 files changed, 705 insertions, 0 deletions
diff --git a/core/launcher/applauncher.cpp b/core/launcher/applauncher.cpp
new file mode 100644
index 0000000..50c1b71
--- a/dev/null
+++ b/core/launcher/applauncher.cpp
@@ -0,0 +1,705 @@
+/**********************************************************************
+** Copyright (C) 2000-2002 Trolltech AS. All rights reserved.
+**
+** This file is part of the Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QTOPIA_INTERNAL_PRELOADACCESS
+#define QTOPIA_INTERNAL_PRELOADACCESS
+#endif
+#ifndef QTOPIA_INTERNAL_FILEOPERATIONS
+#define QTOPIA_INTERNAL_FILEOPERATIONS
+#endif
+#ifndef QTOPIA_PROGRAM_MONITOR
+#define QTOPIA_PROGRAM_MONITOR
+#endif
+#include <qtopia/qpeglobal.h>
+
+#ifndef Q_OS_WIN32
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <errno.h>
+#else
+#include <process.h>
+#include <windows.h>
+#include <winbase.h>
+#endif
+
+#include <signal.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include <qtimer.h>
+#include <qwindowsystem_qws.h>
+#include <qmessagebox.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+
+#include <qtopia/qcopenvelope_qws.h>
+#include <qtopia/applnk.h>
+#include <qtopia/qpeapplication.h>
+#include <qtopia/config.h>
+#include <qtopia/global.h>
+
+#include "applauncher.h"
+#include "documentlist.h"
+
+const int AppLauncher::RAISE_TIMEOUT_MS = 5000;
+
+//---------------------------------------------------------------------------
+
+static AppLauncher* appLauncherPtr;
+
+const int appStopEventID = 1290;
+
+class AppStoppedEvent : public QCustomEvent
+{
+public:
+ AppStoppedEvent(int pid, int status)
+ : QCustomEvent( appStopEventID ), mPid(pid), mStatus(status) { }
+
+ int pid() { return mPid; }
+ int status() { return mStatus; }
+
+private:
+ int mPid, mStatus;
+};
+
+AppLauncher::AppLauncher(QObject *parent, const char *name)
+ : QObject(parent, name), qlPid(0), qlReady(FALSE),
+ appKillerBox(0)
+{
+ connect(qwsServer, SIGNAL(newChannel(const QString&)), this, SLOT(newQcopChannel(const QString&)));
+ connect(qwsServer, SIGNAL(removedChannel(const QString&)), this, SLOT(removedQcopChannel(const QString&)));
+ QCopChannel* channel = new QCopChannel( "QPE/System", this );
+ connect( channel, SIGNAL(received(const QCString&, const QByteArray&)),
+ this, SLOT(received(const QCString&, const QByteArray&)) );
+
+ channel = new QCopChannel( "QPE/Server", this );
+ connect( channel, SIGNAL(received(const QCString&, const QByteArray&)),
+ this, SLOT(received(const QCString&, const QByteArray&)) );
+
+#ifndef Q_OS_WIN32
+ signal(SIGCHLD, signalHandler);
+#else
+ runningAppsProc.setAutoDelete( TRUE );
+#endif
+ QString tmp = qApp->argv()[0];
+ int pos = tmp.findRev('/');
+ if ( pos > -1 )
+ tmp = tmp.mid(++pos);
+ runningApps[::getpid()] = tmp;
+
+ appLauncherPtr = this;
+
+ QTimer::singleShot( 1000, this, SLOT(createQuickLauncher()) );
+}
+
+AppLauncher::~AppLauncher()
+{
+ appLauncherPtr = 0;
+#ifndef Q_OS_WIN32
+ signal(SIGCHLD, SIG_DFL);
+#endif
+ if ( qlPid ) {
+ int status;
+ ::kill( qlPid, SIGTERM );
+ waitpid( qlPid, &status, 0 );
+ }
+}
+
+/* We use the QCopChannel of the app as an indicator of when it has been launched
+ so that we can disable the busy indicators */
+void AppLauncher::newQcopChannel(const QString& channelName)
+{
+// qDebug("channel %s added", channelName.data() );
+ QString prefix("QPE/Application/");
+ if (channelName.startsWith(prefix)) {
+ {
+ QCopEnvelope e("QPE/System", "newChannel(QString)");
+ e << channelName;
+ }
+ QString appName = channelName.mid(prefix.length());
+ if ( appName != "quicklauncher" ) {
+ emit connected( appName );
+ QCopEnvelope e("QPE/System", "notBusy(QString)");
+ e << appName;
+ }
+ } else if (channelName.startsWith("QPE/QuickLauncher-")) {
+ qDebug("Registered %s", channelName.latin1());
+ int pid = channelName.mid(18).toInt();
+ if (pid == qlPid)
+ qlReady = TRUE;
+ }
+}
+
+void AppLauncher::removedQcopChannel(const QString& channelName)
+{
+ if (channelName.startsWith("QPE/Application/")) {
+ QCopEnvelope e("QPE/System", "removedChannel(QString)");
+ e << channelName;
+ }
+}
+
+void AppLauncher::received(const QCString& msg, const QByteArray& data)
+{
+ QDataStream stream( data, IO_ReadOnly );
+ if ( msg == "execute(QString)" ) {
+ QString t;
+ stream >> t;
+ if ( !executeBuiltin( t, QString::null ) )
+ execute(t, QString::null);
+ } else if ( msg == "execute(QString,QString)" ) {
+ QString t,d;
+ stream >> t >> d;
+ if ( !executeBuiltin( t, d ) )
+ execute( t, d );
+ } else if ( msg == "processQCop(QString)" ) { // from QPE/Server
+ QString t;
+ stream >> t;
+ if ( !executeBuiltin( t, QString::null ) )
+ execute( t, QString::null, TRUE);
+ } else if ( msg == "raise(QString)" ) {
+ QString appName;
+ stream >> appName;
+
+ if ( !executeBuiltin( appName, QString::null ) ) {
+ if ( !waitingHeartbeat.contains( appName ) && appKillerName != appName ) {
+ //qDebug( "Raising: %s", appName.latin1() );
+ QCString channel = "QPE/Application/";
+ channel += appName.latin1();
+
+ // Need to lock it to avoid race conditions with QPEApplication::processQCopFile
+ QFile f("/tmp/qcop-msg-" + appName);
+ if ( f.open(IO_WriteOnly | IO_Append) ) {
+#ifndef Q_OS_WIN32
+ flock(f.handle(), LOCK_EX);
+#endif
+ QDataStream ds(&f);
+ QByteArray b;
+ QDataStream bstream(b, IO_WriteOnly);
+ ds << channel << QCString("raise()") << b;
+ f.flush();
+#ifndef Q_OS_WIN32
+ flock(f.handle(), LOCK_UN);
+#endif
+ f.close();
+ }
+ bool alreadyRunning = isRunning( appName );
+ if ( execute(appName, QString::null) ) {
+ int id = startTimer(RAISE_TIMEOUT_MS + alreadyRunning?2000:0);
+ waitingHeartbeat.insert( appName, id );
+ }
+ }
+ }
+ } else if ( msg == "sendRunningApps()" ) {
+ QStringList apps;
+ QMap<int,QString>::Iterator it;
+ for( it = runningApps.begin(); it != runningApps.end(); ++it )
+ apps.append( *it );
+ QCopEnvelope e( "QPE/Desktop", "runningApps(QStringList)" );
+ e << apps;
+ } else if ( msg == "appRaised(QString)" ) {
+ QString appName;
+ stream >> appName;
+ qDebug("Got a heartbeat from %s", appName.latin1());
+ QMap<QString,int>::Iterator it = waitingHeartbeat.find(appName);
+ if ( it != waitingHeartbeat.end() ) {
+ killTimer( *it );
+ waitingHeartbeat.remove(it);
+ }
+ // Check to make sure we're not waiting on user input...
+ if ( appKillerBox && appName == appKillerName ) {
+ // If we are, we kill the dialog box, and the code waiting on the result
+ // will clean us up (basically the user said "no").
+ delete appKillerBox;
+ appKillerBox = 0;
+ appKillerName = QString::null;
+ }
+ }
+}
+
+void AppLauncher::signalHandler(int)
+{
+#ifndef Q_OS_WIN32
+ int status;
+ pid_t pid = waitpid(-1, &status, WNOHANG);
+/* if (pid == 0 || &status == 0 ) {
+ qDebug("hmm, could not get return value from signal");
+ }
+*/
+ QApplication::postEvent(appLauncherPtr, new AppStoppedEvent(pid, status) );
+#else
+ qDebug("Unhandled signal see by AppLauncher::signalHandler(int)");
+#endif
+}
+
+bool AppLauncher::event(QEvent *e)
+{
+ if ( e->type() == appStopEventID ) {
+ AppStoppedEvent *ae = (AppStoppedEvent *) e;
+ sigStopped(ae->pid(), ae->status() );
+ return TRUE;
+ }
+
+ return QObject::event(e);
+}
+
+void AppLauncher::timerEvent( QTimerEvent *e )
+{
+ int id = e->timerId();
+ QMap<QString,int>::Iterator it;
+ for ( it = waitingHeartbeat.begin(); it != waitingHeartbeat.end(); ++it ) {
+ if ( *it == id ) {
+ if ( appKillerBox ) // we're already dealing with one
+ return;
+
+ appKillerName = it.key();
+ killTimer( id );
+ waitingHeartbeat.remove( it );
+
+ // qDebug("Checking in on %s", appKillerName.latin1());
+
+ // We store this incase the application responds while we're
+ // waiting for user input so we know not to delete ourselves.
+ appKillerBox = new QMessageBox(tr("Application Problem"),
+ tr("<p>%1 is not responding.</p>").arg(appKillerName) +
+ tr("<p>Would you like to force the application to exit?</p>"),
+ QMessageBox::Warning, QMessageBox::Yes,
+ QMessageBox::No | QMessageBox::Default,
+ QMessageBox::NoButton);
+ if (appKillerBox->exec() == QMessageBox::Yes) {
+ // qDebug("Killing the app!!! Bwuhahahaha!");
+ int pid = pidForName(appKillerName);
+ if ( pid > 0 )
+ kill( pid );
+ }
+ appKillerName = QString::null;
+ delete appKillerBox;
+ appKillerBox = 0;
+ return;
+ }
+ }
+
+ QObject::timerEvent( e );
+}
+
+#ifndef Q_OS_WIN32
+void AppLauncher::sigStopped(int sigPid, int sigStatus)
+{
+ int exitStatus = 0;
+
+ bool crashed = WIFSIGNALED(sigStatus);
+ if ( !crashed ) {
+ if ( WIFEXITED(sigStatus) )
+ exitStatus = WEXITSTATUS(sigStatus);
+ } else {
+ exitStatus = WTERMSIG(sigStatus);
+ }
+
+ QMap<int,QString>::Iterator it = runningApps.find( sigPid );
+ if ( it == runningApps.end() ) {
+ if ( sigPid == qlPid ) {
+ qDebug( "quicklauncher stopped" );
+ qlPid = 0;
+ qlReady = FALSE;
+ QFile::remove("/tmp/qcop-msg-quicklauncher" );
+ QTimer::singleShot( 2000, this, SLOT(createQuickLauncher()) );
+ }
+/*
+ if ( sigPid == -1 )
+ qDebug("non-qtopia application exited (disregarded)");
+ else
+ qDebug("==== no pid matching %d in list, definite bug", sigPid);
+*/
+ return;
+ }
+ QString appName = *it;
+ runningApps.remove(it);
+
+ QMap<QString,int>::Iterator hbit = waitingHeartbeat.find(appName);
+ if ( hbit != waitingHeartbeat.end() ) {
+ killTimer( *hbit );
+ waitingHeartbeat.remove( hbit );
+ }
+ if ( appName == appKillerName ) {
+ appKillerName = QString::null;
+ delete appKillerBox;
+ appKillerBox = 0;
+ }
+
+ /* we must disable preload for an app that crashes as the system logic relies on preloaded apps
+ actually being loaded. If eg. the crash happened in the constructor, we can't automatically reload
+ the app (withouth some timeout value for eg. 3 tries (which I think is a bad solution)
+ */
+ bool preloadDisabled = FALSE;
+ if ( !DocumentList::appLnkSet ) return;
+ const AppLnk* app = DocumentList::appLnkSet->findExec( appName );
+ if ( !app ) return; // QCop messages processed to slow?
+ if ( crashed && app->isPreloaded() ) {
+ Config cfg("Launcher");
+ cfg.setGroup("Preload");
+ QStringList apps = cfg.readListEntry("Apps",',');
+ QString exe = app->exec();
+ apps.remove(exe);
+ cfg.writeEntry("Apps",apps,',');
+ preloadDisabled = TRUE;
+ }
+
+ // clean up
+ if ( exitStatus ) {
+ QCopEnvelope e("QPE/System", "notBusy(QString)");
+ e << app->exec();
+ }
+/*
+ // debug info
+ for (it = runningApps.begin(); it != runningApps.end(); ++it) {
+ qDebug("running according to internal list: %s, with pid %d", (*it).data(), it.key() );
+ }
+*/
+
+#ifdef QTOPIA_PROGRAM_MONITOR
+ if ( crashed ) {
+ QString sig;
+ switch( exitStatus ) {
+ case SIGABRT: sig = "SIGABRT"; break;
+ case SIGALRM: sig = "SIGALRM"; break;
+ case SIGBUS: sig = "SIGBUS"; break;
+ case SIGFPE: sig = "SIGFPE"; break;
+ case SIGHUP: sig = "SIGHUP"; break;
+ case SIGILL: sig = "SIGILL"; break;
+ case SIGKILL: sig = "SIGKILL"; break;
+ case SIGPIPE: sig = "SIGPIPE"; break;
+ case SIGQUIT: sig = "SIGQUIT"; break;
+ case SIGSEGV: sig = "SIGSEGV"; break;
+ case SIGTERM: sig = "SIGTERM"; break;
+ case SIGTRAP: sig = "SIGTRAP"; break;
+ default: sig = QString("Unkown %1").arg(exitStatus);
+ }
+ if ( preloadDisabled )
+ sig += tr("<qt><p>Fast loading has been disabled for this application. Tap and hold the application icon to reenable it.</qt>");
+
+ QString str = tr("<qt><b>%1</b> was terminated due to signal code %2</qt>").arg( app->name() ).arg( sig );
+ QMessageBox::information(0, tr("Application terminated"), str );
+ } else {
+ if ( exitStatus == 255 ) { //could not find app (because global returns -1)
+ QMessageBox::information(0, tr("Application not found"), tr("<qt>Could not locate application <b>%1</b></qt>").arg( app->exec() ) );
+ } else {
+ QFileInfo fi(Global::tempDir() + "qcop-msg-" + appName);
+ if ( fi.exists() && fi.size() ) {
+ emit terminated(sigPid, appName);
+ execute( appName, QString::null );
+ return;
+ }
+ }
+ }
+
+#endif
+
+ emit terminated(sigPid, appName);
+}
+#else
+void AppLauncher::sigStopped(int sigPid, int sigStatus)
+{
+ qDebug("Unhandled signal : AppLauncher::sigStopped(int sigPid, int sigStatus)");
+}
+#endif // Q_OS_WIN32
+
+bool AppLauncher::isRunning(const QString &app)
+{
+ for (QMap<int,QString>::ConstIterator it = runningApps.begin(); it != runningApps.end(); ++it) {
+ if ( *it == app ) {
+#ifdef Q_OS_UNIX
+ pid_t t = ::__getpgid( it.key() );
+ if ( t == -1 ) {
+ qDebug("appLauncher bug, %s believed running, but pid %d is not existing", app.data(), it.key() );
+ runningApps.remove( it.key() );
+ return FALSE;
+ }
+#endif
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+bool AppLauncher::executeBuiltin(const QString &c, const QString &document)
+{
+ Global::Command* builtin = Global::builtinCommands();
+ QGuardedPtr<QWidget> *running = Global::builtinRunning();
+
+ // Attempt to execute the app using a builtin class for the app
+ if (builtin) {
+ for (int i = 0; builtin[i].file; i++) {
+ if ( builtin[i].file == c ) {
+ if ( running[i] ) {
+ if ( !document.isNull() && builtin[i].documentary )
+ Global::setDocument(running[i], document);
+ running[i]->raise();
+ running[i]->show();
+ running[i]->setActiveWindow();
+ } else {
+ running[i] = builtin[i].func( builtin[i].maximized );
+ }
+#ifndef QT_NO_COP
+ QCopEnvelope e("QPE/System", "notBusy(QString)" );
+ e << c; // that was quick ;-)
+#endif
+ return TRUE;
+ }
+ }
+ }
+
+ // Convert the command line in to a list of arguments
+ QStringList list = QStringList::split(QRegExp(" *"),c);
+ QString ap=list[0];
+
+ if ( ap == "suspend" ) { // No tr
+ QWSServer::processKeyEvent( 0xffff, Qt::Key_F34, FALSE, TRUE, FALSE );
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+bool AppLauncher::execute(const QString &c, const QString &docParam, bool noRaise)
+{
+ // Convert the command line in to a list of arguments
+ QStringList list = QStringList::split(QRegExp(" *"),c);
+ if ( !docParam.isEmpty() )
+ list.append( docParam );
+
+ QString appName = list[0];
+ if ( isRunning(appName) ) {
+ QCString channel = "QPE/Application/";
+ channel += appName.latin1();
+
+ // Need to lock it to avoid race conditions with QPEApplication::processQCopFile
+ QFile f(Global::tempDir() + "qcop-msg-" + appName);
+ if ( !noRaise && f.open(IO_WriteOnly | IO_Append) ) {
+#ifndef Q_OS_WIN32
+ flock(f.handle(), LOCK_EX);
+#endif
+
+ QDataStream ds(&f);
+ QByteArray b;
+ QDataStream bstream(b, IO_WriteOnly);
+ if ( !f.size() ) {
+ ds << channel << QCString("raise()") << b;
+ if ( !waitingHeartbeat.contains( appName ) && appKillerName != appName ) {
+ int id = startTimer(RAISE_TIMEOUT_MS);
+ waitingHeartbeat.insert( appName, id );
+ }
+ }
+ if ( !docParam.isEmpty() ) {
+ bstream << docParam;
+ ds << channel << QCString("setDocument(QString)") << b;
+ }
+
+ f.flush();
+#ifndef Q_OS_WIN32
+ flock(f.handle(), LOCK_UN);
+#endif
+ f.close();
+ }
+ if ( QCopChannel::isRegistered(channel) ) // avoid unnecessary warnings
+ QCopChannel::send(channel,"QPEProcessQCop()");
+
+ return TRUE;
+ }
+
+#ifdef QT_NO_QWS_MULTIPROCESS
+ QMessageBox::warning( 0, tr("Error"), tr("Could not find the application %1").arg(c),
+ tr("OK"), 0, 0, 0, 1 );
+#else
+
+ QStrList slist;
+ unsigned j;
+ for ( j = 0; j < list.count(); j++ )
+ slist.append( list[j].utf8() );
+
+ const char **args = new const char *[slist.count() + 1];
+ for ( j = 0; j < slist.count(); j++ )
+ args[j] = slist.at(j);
+ args[j] = NULL;
+
+#ifndef Q_OS_WIN32
+ if ( qlPid && qlReady && QFile::exists( QPEApplication::qpeDir()+"plugins/application/lib"+args[0] + ".so" ) ) {
+ qDebug( "Quick launching: %s", args[0] );
+ if ( getuid() == 0 )
+ setpriority( PRIO_PROCESS, qlPid, 0 );
+ QCString qlch("QPE/QuickLauncher-");
+ qlch += QString::number(qlPid);
+ QCopEnvelope env( qlch, "execute(QStrList)" );
+ env << slist;
+ runningApps[qlPid] = QString(args[0]);
+ emit launched(qlPid, QString(args[0]));
+ QCopEnvelope e("QPE/System", "busy()");
+ qlPid = 0;
+ qlReady = FALSE;
+ QTimer::singleShot( getuid() == 0 ? 800 : 1500, this, SLOT(createQuickLauncher()) );
+ } else {
+ int pid = ::vfork();
+ if ( !pid ) {
+ for ( int fd = 3; fd < 100; fd++ )
+ ::close( fd );
+ ::setpgid( ::getpid(), ::getppid() );
+ // Try bindir first, so that foo/bar works too
+ ::execv( QPEApplication::qpeDir()+"bin/"+args[0], (char * const *)args );
+ ::execvp( args[0], (char * const *)args );
+ _exit( -1 );
+ }
+
+ runningApps[pid] = QString(args[0]);
+ emit launched(pid, QString(args[0]));
+ QCopEnvelope e("QPE/System", "busy()");
+ }
+#else
+ QProcess *proc = new QProcess(this);
+ if (proc){
+ for (int i=0; i < slist.count(); i++)
+ proc->addArgument(args[i]);
+ connect(proc, SIGNAL(processExited()), this, SLOT(processExited()));
+ if (!proc->start()){
+ qDebug("Unable to start application %s", args[0]);
+ }else{
+ PROCESS_INFORMATION *procInfo = (PROCESS_INFORMATION *)proc->processIdentifier();
+ if (procInfo){
+ DWORD pid = procInfo->dwProcessId;
+ runningApps[pid] = QString(args[0]);
+ runningAppsProc.append(proc);
+ emit launched(pid, QString(args[0]));
+ QCopEnvelope e("QPE/System", "busy()");
+ }else{
+ qDebug("Unable to read process inforation #1 for %s", args[0]);
+ }
+ }
+ }else{
+ qDebug("Unable to create process for application %s", args[0]);
+ return FALSE;
+ }
+#endif
+#endif //QT_NO_QWS_MULTIPROCESS
+
+ delete [] args;
+ return TRUE;
+}
+
+void AppLauncher::kill( int pid )
+{
+#ifndef Q_OS_WIN32
+ ::kill( pid, SIGTERM );
+#else
+ for ( QProcess *proc = runningAppsProc.first(); proc; proc = runningAppsProc.next() ) {
+ if ( proc->processIdentifier() == pid ) {
+ proc->kill();
+ break;
+ }
+ }
+#endif
+}
+
+int AppLauncher::pidForName( const QString &appName )
+{
+ int pid = -1;
+
+ QMap<int, QString>::Iterator it;
+ for (it = runningApps.begin(); it!= runningApps.end(); ++it) {
+ if (*it == appName) {
+ pid = it.key();
+ break;
+ }
+ }
+
+ return pid;
+}
+
+void AppLauncher::createQuickLauncher()
+{
+ qlReady = FALSE;
+ qlPid = ::vfork();
+ if ( !qlPid ) {
+ char **args = new char *[2];
+ args[0] = "quicklauncher";
+ args[1] = 0;
+ for ( int fd = 3; fd < 100; fd++ )
+ ::close( fd );
+ ::setpgid( ::getpid(), ::getppid() );
+ // Try bindir first, so that foo/bar works too
+ setenv( "LD_BIND_NOW", "1", 1 );
+ ::execv( QPEApplication::qpeDir()+"bin/quicklauncher", args );
+ ::execvp( "quicklauncher", args );
+ _exit( -1 );
+ } else if ( qlPid == -1 ) {
+ qlPid = 0;
+ } else {
+ if ( getuid() == 0 )
+ setpriority( PRIO_PROCESS, qlPid, 19 );
+ }
+}
+
+// Used only by Win32
+void AppLauncher::processExited()
+{
+#ifdef Q_OS_WIN32
+ qDebug("AppLauncher::processExited()");
+ bool found = FALSE;
+ QProcess *proc = (QProcess *) sender();
+ if (!proc){
+ qDebug("Interanl error NULL proc");
+ return;
+ }
+
+ QString appName = proc->arguments()[0];
+ qDebug("Removing application %s", appName.latin1());
+ runningAppsProc.remove(proc);
+
+ QMap<QString,int>::Iterator hbit = waitingHeartbeat.find(appName);
+ if ( hbit != waitingHeartbeat.end() ) {
+ killTimer( *hbit );
+ waitingHeartbeat.remove( hbit );
+ }
+ if ( appName == appKillerName ) {
+ appKillerName = QString::null;
+ delete appKillerBox;
+ appKillerBox = 0;
+ }
+
+ // Search for the app to find its PID
+ QMap<int, QString>::Iterator it;
+ for (it = runningApps.begin(); it!= runningApps.end(); ++it){
+ if (it.data() == appName){
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (found){
+ emit terminated(it.key(), it.data());
+ runningApps.remove(it.key());
+ }else{
+ qDebug("Internal error application %s not listed as running", appName.latin1());
+ }
+
+#endif
+}
+