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
00175 return false;
00176 #else
00177 ::exit(255);
00178 #endif
00179 }
00180 #ifdef Q_WS_WIN
00181 Private::s_dbusServiceName = appName;
00182 #endif
00183
00184
00185 return true;
00186
00187 #ifdef Q_WS_MACX
00188 } else {
00189 mac_fork_and_reexec_self();
00190 #endif
00191
00192 }
00193
00194 #ifndef Q_WS_WIN
00195 int fd[2];
00196 signed char result;
00197 if (0 > pipe(fd))
00198 {
00199 kError() << "KUniqueApplication: pipe() failed!" << endl;
00200 ::exit(255);
00201 }
00202 int fork_result = fork();
00203 switch(fork_result) {
00204 case -1:
00205 kError() << "KUniqueApplication: fork() failed!" << endl;
00206 ::exit(255);
00207 break;
00208 case 0:
00209 {
00210
00211
00212 QDBusConnectionInterface* dbusService = tryToInitDBusConnection();
00213 ::close(fd[0]);
00214 if (forceNewProcess)
00215 appName.append("-").append(QString::number(getpid()));
00216
00217 QDBusReply<QDBusConnectionInterface::RegisterServiceReply> reply =
00218 dbusService->registerService(appName);
00219 if (!reply.isValid())
00220 {
00221 kError() << "KUniqueApplication: Can't setup D-Bus service." << endl;
00222 result = -1;
00223 ::write(fd[1], &result, 1);
00224 ::exit(255);
00225 }
00226 if (reply == QDBusConnectionInterface::ServiceNotRegistered)
00227 {
00228
00229 result = 0;
00230 ::write(fd[1], &result, 1);
00231 ::close(fd[1]);
00232 return false;
00233 }
00234
00235 #ifdef Q_WS_X11
00236 KStartupInfoId id;
00237 if( kapp != NULL )
00238 id.initId( kapp->startupId());
00239 else
00240 id = KStartupInfo::currentStartupIdEnv();
00241 if( !id.none())
00242 {
00243 Display* disp = XOpenDisplay( NULL );
00244 if( disp != NULL )
00245 {
00246 KStartupInfoData data;
00247 data.addPid( getpid());
00248 KStartupInfo::sendChangeX( disp, id, data );
00249 XCloseDisplay( disp );
00250 }
00251 }
00252 #else //FIXME(E): Implement
00253 #endif
00254 }
00255 result = 0;
00256 ::write(fd[1], &result, 1);
00257 ::close(fd[1]);
00258 return true;
00259 default:
00260
00261
00262 if (forceNewProcess)
00263 appName.append("-").append(QString::number(fork_result));
00264 ::close(fd[1]);
00265
00266 Q_FOREVER
00267 {
00268 int n = ::read(fd[0], &result, 1);
00269 if (n == 1) break;
00270 if (n == 0)
00271 {
00272 kError() << "KUniqueApplication: Pipe closed unexpectedly." << endl;
00273 ::exit(255);
00274 }
00275 if (errno != EINTR)
00276 {
00277 kError() << "KUniqueApplication: Error reading from pipe." << endl;
00278 ::exit(255);
00279 }
00280 }
00281 ::close(fd[0]);
00282
00283 if (result != 0)
00284 ::exit(result);
00285
00286 #endif
00287 QDBusConnectionInterface* dbusService = tryToInitDBusConnection();
00288 if (!dbusService->isServiceRegistered(appName))
00289 {
00290 kError() << "KUniqueApplication: Registering failed!" << endl;
00291 }
00292
00293 QByteArray saved_args;
00294 QDataStream ds(&saved_args, QIODevice::WriteOnly);
00295 KCmdLineArgs::saveAppArgs(ds);
00296
00297 QByteArray new_asn_id;
00298 #if defined Q_WS_X11
00299 KStartupInfoId id;
00300 if( kapp != NULL )
00301 id.initId( kapp->startupId());
00302 else
00303 id = KStartupInfo::currentStartupIdEnv();
00304 if( !id.none())
00305 new_asn_id = id.id();
00306 #endif
00307
00308 QDBusInterface iface(appName, "/MainApplication", "org.kde.KUniqueApplication", QDBusConnection::sessionBus());
00309 QDBusReply<int> reply;
00310 if (!iface.isValid() || !(reply = iface.call("newInstance", new_asn_id, saved_args)).isValid())
00311 {
00312 QDBusError err = iface.lastError();
00313 kError() << "Communication problem with " << KCmdLineArgs::aboutData()->appName() << ", it probably crashed." << endl
00314 << "Error message was: " << err.name() << ": \"" << err.message() << "\"" << endl;
00315 ::exit(255);
00316 }
00317 #ifndef Q_WS_WIN
00318 ::exit(reply);
00319 break;
00320 }
00321 #endif
00322 return false;
00323 }
00324
00325
00326 KUniqueApplication::KUniqueApplication(bool GUIenabled, bool configUnique)
00327 : KApplication( GUIenabled, Private::initHack( configUnique )),
00328 d(new Private(this))
00329 {
00330 d->processingRequest = false;
00331 d->firstInstance = true;
00332
00333
00334 new KUniqueApplicationAdaptor(this);
00335
00336 if (Private::s_nofork)
00337
00338 QTimer::singleShot( 0, this, SLOT(_k_newInstanceNoFork()) );
00339 }
00340
00341
00342 #ifdef Q_WS_X11
00343 KUniqueApplication::KUniqueApplication(Display *display, Qt::HANDLE visual,
00344 Qt::HANDLE colormap, bool configUnique)
00345 : KApplication( display, visual, colormap, Private::initHack( configUnique )),
00346 d(new Private(this))
00347 {
00348 d->processingRequest = false;
00349 d->firstInstance = true;
00350
00351
00352 new KUniqueApplicationAdaptor(this);
00353
00354 if (Private::s_nofork)
00355
00356 QTimer::singleShot( 0, this, SLOT(_k_newInstanceNoFork()) );
00357 }
00358 #endif
00359
00360
00361 KUniqueApplication::~KUniqueApplication()
00362 {
00363 #ifdef Q_WS_WIN
00364
00365 QDBusConnectionInterface* dbusService;
00366 if (QDBusConnection::sessionBus().isConnected()
00367 && (dbusService = QDBusConnection::sessionBus().interface()))
00368 {
00369 dbusService->unregisterService(Private::s_dbusServiceName);
00370 }
00371 #endif
00372
00373 delete d;
00374 }
00375
00376
00377 KComponentData KUniqueApplication::Private::initHack(bool configUnique)
00378 {
00379 KComponentData cData(KCmdLineArgs::aboutData());
00380 if (configUnique)
00381 {
00382 KConfigGroup cg(cData.config(), "KDE");
00383 s_multipleInstances = cg.readEntry("MultipleInstances", false);
00384 }
00385 if( !KUniqueApplication::start())
00386
00387 ::exit( 0 );
00388 return cData;
00389 }
00390
00391 void KUniqueApplication::Private::_k_newInstanceNoFork()
00392 {
00393 s_handleAutoStarted = false;
00394 q->newInstance();
00395 firstInstance = false;
00396 #if defined Q_WS_X11
00397
00398
00399
00400
00401
00402
00403 if( s_handleAutoStarted )
00404 KStartupInfo::handleAutoAppStartedSending();
00405 #endif
00406
00407 }
00408
00409 bool KUniqueApplication::restoringSession()
00410 {
00411 return d->firstInstance && isSessionRestored();
00412 }
00413
00414 int KUniqueApplication::newInstance()
00415 {
00416 if (!d->firstInstance) {
00417 QList<KMainWindow*> allWindows = KMainWindow::memberList();
00418 if (!allWindows.isEmpty()) {
00419
00420
00421 KMainWindow* mainWindow = allWindows.first();
00422 if (mainWindow) {
00423 mainWindow->show();
00424 #if defined Q_WS_X11
00425
00426
00427
00428
00429 KStartupInfo::setNewStartupId(mainWindow, startupId());
00430 #endif
00431 }
00432 }
00433 }
00434 return 0;
00435 }
00436
00437 void KUniqueApplication::setHandleAutoStarted()
00438 {
00439 Private::s_handleAutoStarted = false;
00440 }
00441
00442 #include "kuniqueapplication.moc"
00443 #include "kuniqueapplication_p.moc"