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 #include <kwindowsystem.h>
00044
00045 #if defined Q_WS_X11
00046 #include <kstartupinfo.h>
00047 #endif
00048
00049
00050
00051
00052 #include <QWidget>
00053
00054 #include <kconfig.h>
00055 #include "kdebug.h"
00056
00057 #if defined Q_WS_X11
00058 #include <netwm.h>
00059 #include <X11/Xlib.h>
00060 #define DISPLAY "DISPLAY"
00061 #else
00062 # ifdef Q_WS_QWS
00063 # define DISPLAY "QWS_DISPLAY"
00064 # else
00065 # define DISPLAY "DISPLAY"
00066 # endif
00067 #endif
00068
00069 #ifdef Q_WS_MAC
00070 #include <kkernel_mac.h>
00071 #endif
00072
00073 bool KUniqueApplication::Private::s_nofork = false;
00074 bool KUniqueApplication::Private::s_multipleInstances = false;
00075 bool s_kuniqueapplication_startCalled = false;
00076 bool KUniqueApplication::Private::s_handleAutoStarted = false;
00077 #ifdef Q_WS_WIN
00078 QString KUniqueApplication::Private::s_dbusServiceName;
00079
00080 void KApplication_activateWindowForProcess( const QString& executableName );
00081 bool KApplication_dbusIsPatched();
00082 #endif
00083
00084 void
00085 KUniqueApplication::addCmdLineOptions()
00086 {
00087 KCmdLineOptions kunique_options;
00088 kunique_options.add("nofork", ki18n("Do not run in the background."));
00089 #ifdef Q_WS_MACX
00090 kunique_options.add("psn", ki18n("Internally added if launched from Finder"));
00091 #endif
00092 KCmdLineArgs::addCmdLineOptions(kunique_options, KLocalizedString(), "kuniqueapp", "kde");
00093 }
00094
00095 static QDBusConnectionInterface *tryToInitDBusConnection()
00096 {
00097
00098 QDBusConnectionInterface* dbusService = 0;
00099 if (!QDBusConnection::sessionBus().isConnected() || !(dbusService = QDBusConnection::sessionBus().interface()))
00100 {
00101 kError() << "KUniqueApplication: Cannot find the D-Bus session server: " << QDBusConnection::sessionBus().lastError().message() << endl;
00102 ::exit(255);
00103 }
00104 return dbusService;
00105 }
00106
00107 bool KUniqueApplication::start()
00108 {
00109 return start(0);
00110 }
00111
00112 bool
00113 KUniqueApplication::start(StartFlags flags)
00114 {
00115 if( s_kuniqueapplication_startCalled )
00116 return true;
00117 s_kuniqueapplication_startCalled = true;
00118
00119 addCmdLineOptions();
00120 #ifdef Q_WS_WIN
00121 Private::s_nofork = true;
00122 #else
00123 KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kuniqueapp");
00124 #ifdef Q_WS_MACX
00125
00126 if(args->isSet("psn"))
00127 Private::s_nofork = true;
00128 else
00129 #endif
00130 Private::s_nofork = !args->isSet("fork");
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 #ifdef Q_WS_X11
00423
00424
00425
00426
00427 KStartupInfo::setNewStartupId(mainWindow, startupId());
00428 #endif
00429 #ifdef Q_WS_WIN
00430 KWindowSystem::forceActiveWindow( mainWindow->winId() );
00431 #endif
00432
00433 }
00434 }
00435 }
00436 return 0;
00437 }
00438
00439 void KUniqueApplication::setHandleAutoStarted()
00440 {
00441 Private::s_handleAutoStarted = false;
00442 }
00443
00445
00446 int KUniqueApplicationAdaptor::newInstance(const QByteArray &asn_id, const QByteArray &args)
00447 {
00448 if (!asn_id.isEmpty())
00449 parent()->setStartupId(asn_id);
00450
00451
00452
00453
00454 QMetaObject::invokeMethod(parent(), "loadCommandLineOptionsForNewInstance");
00455
00456 QDataStream ds(args);
00457 KCmdLineArgs::loadAppArgs(ds);
00458
00459 int ret = parent()->newInstance();
00460
00461 parent()->d->firstInstance = false;
00462 return ret;
00463 }
00464
00465 #include "kuniqueapplication.moc"
00466 #include "kuniqueapplication_p.moc"