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