KDEUI
kuniqueapplication.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "kuniqueapplication.h"
00021 #include "kuniqueapplication_p.h"
00022 #include <kmainwindow.h>
00023
00024 #include <config.h>
00025
00026 #include <sys/types.h>
00027 #include <sys/wait.h>
00028
00029 #include <assert.h>
00030 #include <errno.h>
00031 #include <stdlib.h>
00032 #include <unistd.h>
00033
00034 #include <QtCore/QFile>
00035 #include <QtCore/QList>
00036 #include <QtCore/QTimer>
00037 #include <QtDBus/QtDBus>
00038
00039 #include <kcmdlineargs.h>
00040 #include <kstandarddirs.h>
00041 #include <kaboutdata.h>
00042 #include <kconfiggroup.h>
00043
00044 #if defined Q_WS_X11
00045 #include <kstartupinfo.h>
00046 #endif
00047
00048
00049
00050
00051 #include <QWidget>
00052
00053 #include <kconfig.h>
00054 #include "kdebug.h"
00055
00056 #if defined Q_WS_X11
00057 #include <netwm.h>
00058 #include <X11/Xlib.h>
00059 #define DISPLAY "DISPLAY"
00060 #else
00061 # ifdef Q_WS_QWS
00062 # define DISPLAY "QWS_DISPLAY"
00063 # else
00064 # define DISPLAY "DISPLAY"
00065 # endif
00066 #endif
00067
00068 #ifdef Q_WS_MAC
00069 #include <kkernel_mac.h>
00070 #endif
00071
00072 bool KUniqueApplication::Private::s_nofork = false;
00073 bool KUniqueApplication::Private::s_multipleInstances = false;
00074 bool s_kuniqueapplication_startCalled = false;
00075 bool KUniqueApplication::Private::s_handleAutoStarted = false;
00076 #ifdef Q_WS_WIN
00077 QString KUniqueApplication::Private::s_dbusServiceName;
00078
00079 void KApplication_activateWindowForProcess( const QString& executableName );
00080 bool KApplication_dbusIsPatched();
00081 #endif
00082
00083 void
00084 KUniqueApplication::addCmdLineOptions()
00085 {
00086 KCmdLineOptions kunique_options;
00087 kunique_options.add("nofork", ki18n("Do not run in the background."));
00088 #ifdef Q_WS_MACX
00089 kunique_options.add("psn", ki18n("Internally added if launched from Finder"));
00090 #endif
00091 KCmdLineArgs::addCmdLineOptions(kunique_options, KLocalizedString(), "kuniqueapp", "kde");
00092 }
00093
00094 static QDBusConnectionInterface *tryToInitDBusConnection()
00095 {
00096
00097 QDBusConnectionInterface* dbusService = 0;
00098 if (!QDBusConnection::sessionBus().isConnected() || !(dbusService = QDBusConnection::sessionBus().interface()))
00099 {
00100 kError() << "KUniqueApplication: Cannot find the D-Bus session server" << endl;
00101 ::exit(255);
00102 }
00103 return dbusService;
00104 }
00105
00106 bool KUniqueApplication::start()
00107 {
00108 return start(0);
00109 }
00110
00111 bool
00112 KUniqueApplication::start(StartFlags flags)
00113 {
00114 if( s_kuniqueapplication_startCalled )
00115 return true;
00116 s_kuniqueapplication_startCalled = true;
00117
00118 addCmdLineOptions();
00119 #ifdef Q_WS_WIN
00120 Private::s_nofork = true;
00121 #else
00122 KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kuniqueapp");
00123 #ifdef Q_WS_MACX
00124
00125 if(args->isSet("psn"))
00126 Private::s_nofork = true;
00127 else
00128 #endif
00129 Private::s_nofork = !args->isSet("fork");
00130 delete args;
00131 #endif
00132
00133 QString appName = KCmdLineArgs::aboutData()->appName();
00134 const QStringList parts = KCmdLineArgs::aboutData()->organizationDomain().split(QLatin1Char('.'), QString::SkipEmptyParts);
00135 if (parts.isEmpty())
00136 appName.prepend(QLatin1String("local."));
00137 else
00138 foreach (const QString& s, parts)
00139 {
00140 appName.prepend(QLatin1Char('.'));
00141 appName.prepend(s);
00142 }
00143
00144 #ifdef Q_WS_MAC
00145 mac_initialize_dbus();
00146 #endif
00147
00148 bool forceNewProcess = Private::s_multipleInstances || flags & NonUniqueInstance;
00149
00150 if (Private::s_nofork)
00151 {
00152 QDBusConnectionInterface* dbusService = tryToInitDBusConnection();
00153
00154 QString pid = QString::number(getpid());
00155 if (forceNewProcess)
00156 appName = appName + '-' + pid;
00157
00158
00159
00160 bool registered;
00161 #ifdef Q_WS_WIN
00162 if (KApplication_dbusIsPatched())
00163 registered = dbusService->registerService(appName + ".unique-" + pid) == QDBusConnectionInterface::ServiceRegistered;
00164 else
00165 registered = dbusService->registerService(appName) == QDBusConnectionInterface::ServiceRegistered;
00166 #else
00167 registered = dbusService->registerService(appName) == QDBusConnectionInterface::ServiceRegistered;
00168 #endif
00169 if (!registered)
00170 {
00171 kError() << "KUniqueApplication: Can't setup D-Bus service. Probably already running."
00172 << endl;
00173 #ifdef Q_WS_WIN
00174 KApplication_activateWindowForProcess(KCmdLineArgs::aboutData()->appName());
00175 #endif
00176 ::exit(255);
00177 }
00178 #ifdef Q_WS_WIN
00179 Private::s_dbusServiceName = appName;
00180 #endif
00181
00182
00183 return true;
00184
00185 #ifdef Q_WS_MACX
00186 } else {
00187 mac_fork_and_reexec_self();
00188 #endif
00189
00190 }
00191
00192 #ifndef Q_WS_WIN
00193 int fd[2];
00194 signed char result;
00195 if (0 > pipe(fd))
00196 {
00197 kError() << "KUniqueApplication: pipe() failed!" << endl;
00198 ::exit(255);
00199 }
00200 int fork_result = fork();
00201 switch(fork_result) {
00202 case -1:
00203 kError() << "KUniqueApplication: fork() failed!" << endl;
00204 ::exit(255);
00205 break;
00206 case 0:
00207 {
00208
00209
00210 QDBusConnectionInterface* dbusService = tryToInitDBusConnection();
00211 ::close(fd[0]);
00212 if (forceNewProcess)
00213 appName.append("-").append(QString::number(getpid()));
00214
00215 QDBusReply<QDBusConnectionInterface::RegisterServiceReply> reply =
00216 dbusService->registerService(appName);
00217 if (!reply.isValid())
00218 {
00219 kError() << "KUniqueApplication: Can't setup D-Bus service." << endl;
00220 result = -1;
00221 ::write(fd[1], &result, 1);
00222 ::exit(255);
00223 }
00224 if (reply == QDBusConnectionInterface::ServiceNotRegistered)
00225 {
00226
00227 result = 0;
00228 ::write(fd[1], &result, 1);
00229 ::close(fd[1]);
00230 return false;
00231 }
00232
00233 #ifdef Q_WS_X11
00234 KStartupInfoId id;
00235 if( kapp != NULL )
00236 id.initId( kapp->startupId());
00237 else
00238 id = KStartupInfo::currentStartupIdEnv();
00239 if( !id.none())
00240 {
00241 Display* disp = XOpenDisplay( NULL );
00242 if( disp != NULL )
00243 {
00244 KStartupInfoData data;
00245 data.addPid( getpid());
00246 KStartupInfo::sendChangeX( disp, id, data );
00247 XCloseDisplay( disp );
00248 }
00249 }
00250 #else //FIXME(E): Implement
00251 #endif
00252 }
00253 result = 0;
00254 ::write(fd[1], &result, 1);
00255 ::close(fd[1]);
00256 return true;
00257 default:
00258
00259
00260 if (forceNewProcess)
00261 appName.append("-").append(QString::number(fork_result));
00262 ::close(fd[1]);
00263
00264 Q_FOREVER
00265 {
00266 int n = ::read(fd[0], &result, 1);
00267 if (n == 1) break;
00268 if (n == 0)
00269 {
00270 kError() << "KUniqueApplication: Pipe closed unexpectedly." << endl;
00271 ::exit(255);
00272 }
00273 if (errno != EINTR)
00274 {
00275 kError() << "KUniqueApplication: Error reading from pipe." << endl;
00276 ::exit(255);
00277 }
00278 }
00279 ::close(fd[0]);
00280
00281 if (result != 0)
00282 ::exit(result);
00283
00284 #endif
00285 QDBusConnectionInterface* dbusService = tryToInitDBusConnection();
00286 if (!dbusService->isServiceRegistered(appName))
00287 {
00288 kError() << "KUniqueApplication: Registering failed!" << endl;
00289 }
00290
00291 QByteArray saved_args;
00292 QDataStream ds(&saved_args, QIODevice::WriteOnly);
00293 KCmdLineArgs::saveAppArgs(ds);
00294
00295 QByteArray new_asn_id;
00296 #if defined Q_WS_X11
00297 KStartupInfoId id;
00298 if( kapp != NULL )
00299 id.initId( kapp->startupId());
00300 else
00301 id = KStartupInfo::currentStartupIdEnv();
00302 if( !id.none())
00303 new_asn_id = id.id();
00304 #endif
00305
00306 QDBusInterface iface(appName, "/MainApplication", "org.kde.KUniqueApplication", QDBusConnection::sessionBus());
00307 QDBusReply<int> reply;
00308 if (!iface.isValid() || !(reply = iface.call("newInstance", new_asn_id, saved_args)).isValid())
00309 {
00310 QDBusError err = iface.lastError();
00311 kError() << "Communication problem with " << KCmdLineArgs::aboutData()->appName() << ", it probably crashed." << endl
00312 << "Error message was: " << err.name() << ": \"" << err.message() << "\"" << endl;
00313 ::exit(255);
00314 }
00315 #ifndef Q_WS_WIN
00316 ::exit(reply);
00317 break;
00318 }
00319 #endif
00320 return false;
00321 }
00322
00323
00324 KUniqueApplication::KUniqueApplication(bool GUIenabled, bool configUnique)
00325 : KApplication( GUIenabled, Private::initHack( configUnique )),
00326 d(new Private(this))
00327 {
00328 d->processingRequest = false;
00329 d->firstInstance = true;
00330
00331
00332 new KUniqueApplicationAdaptor(this);
00333
00334 if (Private::s_nofork)
00335
00336 QTimer::singleShot( 0, this, SLOT(_k_newInstanceNoFork()) );
00337 }
00338
00339
00340 #ifdef Q_WS_X11
00341 KUniqueApplication::KUniqueApplication(Display *display, Qt::HANDLE visual,
00342 Qt::HANDLE colormap, bool configUnique)
00343 : KApplication( display, visual, colormap, Private::initHack( configUnique )),
00344 d(new Private(this))
00345 {
00346 d->processingRequest = false;
00347 d->firstInstance = true;
00348
00349
00350 new KUniqueApplicationAdaptor(this);
00351
00352 if (Private::s_nofork)
00353
00354 QTimer::singleShot( 0, this, SLOT(_k_newInstanceNoFork()) );
00355 }
00356 #endif
00357
00358
00359 KUniqueApplication::~KUniqueApplication()
00360 {
00361 #ifdef Q_WS_WIN
00362
00363 QDBusConnectionInterface* dbusService;
00364 if (QDBusConnection::sessionBus().isConnected()
00365 && (dbusService = QDBusConnection::sessionBus().interface()))
00366 {
00367 dbusService->unregisterService(Private::s_dbusServiceName);
00368 }
00369 #endif
00370
00371 delete d;
00372 }
00373
00374
00375 KComponentData KUniqueApplication::Private::initHack(bool configUnique)
00376 {
00377 KComponentData cData(KCmdLineArgs::aboutData());
00378 if (configUnique)
00379 {
00380 KConfigGroup cg(cData.config(), "KDE");
00381 s_multipleInstances = cg.readEntry("MultipleInstances", false);
00382 }
00383 if( !KUniqueApplication::start())
00384
00385 ::exit( 0 );
00386 return cData;
00387 }
00388
00389 void KUniqueApplication::Private::_k_newInstanceNoFork()
00390 {
00391 s_handleAutoStarted = false;
00392 q->newInstance();
00393 firstInstance = false;
00394 #if defined Q_WS_X11
00395
00396
00397
00398
00399
00400
00401 if( s_handleAutoStarted )
00402 KStartupInfo::handleAutoAppStartedSending();
00403 #endif
00404
00405 }
00406
00407 bool KUniqueApplication::restoringSession()
00408 {
00409 return d->firstInstance && isSessionRestored();
00410 }
00411
00412 int KUniqueApplication::newInstance()
00413 {
00414 if (!d->firstInstance) {
00415 QList<KMainWindow*> allWindows = KMainWindow::memberList();
00416 if (!allWindows.isEmpty()) {
00417
00418
00419 KMainWindow* mainWindow = allWindows.first();
00420 if (mainWindow) {
00421 mainWindow->show();
00422 #if defined Q_WS_X11
00423
00424
00425
00426
00427 KStartupInfo::setNewStartupId(mainWindow, startupId());
00428 #endif
00429 }
00430 }
00431 }
00432 return 0;
00433 }
00434
00435 void KUniqueApplication::setHandleAutoStarted()
00436 {
00437 Private::s_handleAutoStarted = false;
00438 }
00439
00440 #include "kuniqueapplication.moc"
00441 #include "kuniqueapplication_p.moc"