KDEUI
kuniqueapplication.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (c) 1999 Preston Brown <pbrown@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 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 /* I don't know why, but I end up with complaints about 00050 a forward-declaration of QWidget in the activeWidow()->show 00051 call below on Qt/Mac if I don't include this here... */ 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 #if defined(Q_OS_DARWIN) || defined (Q_OS_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 /* private helpers from kapplication_win.cpp */ 00079 #ifndef _WIN32_WCE 00080 void KApplication_activateWindowForProcess( const QString& executableName ); 00081 #endif 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 // Check the D-Bus connection health 00098 QDBusConnectionInterface* dbusService = 0; 00099 QDBusConnection sessionBus = QDBusConnection::sessionBus(); 00100 if (!sessionBus.isConnected() || !(dbusService = sessionBus.interface())) 00101 { 00102 kError() << "KUniqueApplication: Cannot find the D-Bus session server: " << sessionBus.lastError().message() << endl; 00103 ::exit(255); 00104 } 00105 return dbusService; 00106 } 00107 00108 bool KUniqueApplication::start() 00109 { 00110 return start(0); 00111 } 00112 00113 bool 00114 KUniqueApplication::start(StartFlags flags) 00115 { 00116 if( s_kuniqueapplication_startCalled ) 00117 return true; 00118 s_kuniqueapplication_startCalled = true; 00119 00120 addCmdLineOptions(); // Make sure to add cmd line options 00121 #if defined(Q_WS_WIN) || defined(Q_WS_MACX) 00122 Private::s_nofork = true; 00123 #else 00124 KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kuniqueapp"); 00125 Private::s_nofork = !args->isSet("fork"); 00126 #endif 00127 00128 QString appName = KCmdLineArgs::aboutData()->appName(); 00129 const QStringList parts = KCmdLineArgs::aboutData()->organizationDomain().split(QLatin1Char('.'), QString::SkipEmptyParts); 00130 if (parts.isEmpty()) 00131 appName.prepend(QLatin1String("local.")); 00132 else 00133 foreach (const QString& s, parts) 00134 { 00135 appName.prepend(QLatin1Char('.')); 00136 appName.prepend(s); 00137 } 00138 00139 bool forceNewProcess = Private::s_multipleInstances || flags & NonUniqueInstance; 00140 00141 if (Private::s_nofork) 00142 { 00143 00144 #if defined(Q_OS_DARWIN) || defined (Q_OS_MAC) 00145 mac_initialize_dbus(); 00146 #endif 00147 00148 QDBusConnectionInterface* dbusService = tryToInitDBusConnection(); 00149 00150 QString pid = QString::number(getpid()); 00151 if (forceNewProcess) 00152 appName = appName + '-' + pid; 00153 00154 // Check to make sure that we're actually able to register with the D-Bus session 00155 // server. 00156 bool registered = dbusService->registerService(appName) == QDBusConnectionInterface::ServiceRegistered; 00157 if (!registered) 00158 { 00159 kError() << "KUniqueApplication: Can't setup D-Bus service. Probably already running." 00160 << endl; 00161 #if defined(Q_WS_WIN) && !defined(_WIN32_WCE) 00162 KApplication_activateWindowForProcess(KCmdLineArgs::aboutData()->appName()); 00163 #endif 00164 ::exit(255); 00165 } 00166 00167 // We'll call newInstance in the constructor. Do nothing here. 00168 return true; 00169 00170 #if defined(Q_OS_DARWIN) || defined (Q_OS_MAC) 00171 } else { 00172 mac_fork_and_reexec_self(); 00173 #endif 00174 00175 } 00176 00177 #ifndef Q_WS_WIN 00178 int fd[2]; 00179 signed char result; 00180 if (0 > pipe(fd)) 00181 { 00182 kError() << "KUniqueApplication: pipe() failed!" << endl; 00183 ::exit(255); 00184 } 00185 int fork_result = fork(); 00186 switch(fork_result) { 00187 case -1: 00188 kError() << "KUniqueApplication: fork() failed!" << endl; 00189 ::exit(255); 00190 break; 00191 case 0: 00192 { 00193 // Child 00194 00195 QDBusConnectionInterface* dbusService = tryToInitDBusConnection(); 00196 ::close(fd[0]); 00197 if (forceNewProcess) 00198 appName.append("-").append(QString::number(getpid())); 00199 00200 QDBusReply<QDBusConnectionInterface::RegisterServiceReply> reply = 00201 dbusService->registerService(appName); 00202 if (!reply.isValid()) 00203 { 00204 kError() << "KUniqueApplication: Can't setup D-Bus service." << endl; 00205 result = -1; 00206 ::write(fd[1], &result, 1); 00207 ::exit(255); 00208 } 00209 if (reply == QDBusConnectionInterface::ServiceNotRegistered) 00210 { 00211 // Already running. Ok. 00212 result = 0; 00213 ::write(fd[1], &result, 1); 00214 ::close(fd[1]); 00215 return false; 00216 } 00217 00218 #ifdef Q_WS_X11 00219 KStartupInfoId id; 00220 if( kapp != NULL ) // KApplication constructor unsets the env. variable 00221 id.initId( kapp->startupId()); 00222 else 00223 id = KStartupInfo::currentStartupIdEnv(); 00224 if( !id.none()) 00225 { // notice about pid change 00226 Display* disp = XOpenDisplay( NULL ); 00227 if( disp != NULL ) // use extra X connection 00228 { 00229 KStartupInfoData data; 00230 data.addPid( getpid()); 00231 KStartupInfo::sendChangeX( disp, id, data ); 00232 XCloseDisplay( disp ); 00233 } 00234 } 00235 #else //FIXME(E): Implement 00236 #endif 00237 } 00238 result = 0; 00239 ::write(fd[1], &result, 1); 00240 ::close(fd[1]); 00241 return true; // Finished. 00242 default: 00243 // Parent 00244 00245 if (forceNewProcess) 00246 appName.append("-").append(QString::number(fork_result)); 00247 ::close(fd[1]); 00248 00249 Q_FOREVER 00250 { 00251 int n = ::read(fd[0], &result, 1); 00252 if (n == 1) break; 00253 if (n == 0) 00254 { 00255 kError() << "KUniqueApplication: Pipe closed unexpectedly." << endl; 00256 ::exit(255); 00257 } 00258 if (errno != EINTR) 00259 { 00260 kError() << "KUniqueApplication: Error reading from pipe." << endl; 00261 ::exit(255); 00262 } 00263 } 00264 ::close(fd[0]); 00265 00266 if (result != 0) 00267 ::exit(result); // Error occurred in child. 00268 00269 #endif 00270 QDBusConnectionInterface* dbusService = tryToInitDBusConnection(); 00271 if (!dbusService->isServiceRegistered(appName)) 00272 { 00273 kError() << "KUniqueApplication: Registering failed!" << endl; 00274 } 00275 00276 QByteArray saved_args; 00277 QDataStream ds(&saved_args, QIODevice::WriteOnly); 00278 KCmdLineArgs::saveAppArgs(ds); 00279 00280 QByteArray new_asn_id; 00281 #if defined Q_WS_X11 00282 KStartupInfoId id; 00283 if( kapp != NULL ) // KApplication constructor unsets the env. variable 00284 id.initId( kapp->startupId()); 00285 else 00286 id = KStartupInfo::currentStartupIdEnv(); 00287 if( !id.none()) 00288 new_asn_id = id.id(); 00289 #endif 00290 00291 QDBusMessage msg = QDBusMessage::createMethodCall(appName, "/MainApplication", "org.kde.KUniqueApplication", 00292 "newInstance"); 00293 msg << new_asn_id << saved_args; 00294 QDBusReply<int> reply = QDBusConnection::sessionBus().call(msg, QDBus::Block, INT_MAX); 00295 00296 if (!reply.isValid()) 00297 { 00298 QDBusError err = reply.error(); 00299 kError() << "Communication problem with " << KCmdLineArgs::aboutData()->appName() << ", it probably crashed." << endl 00300 << "Error message was: " << err.name() << ": \"" << err.message() << "\"" << endl; 00301 ::exit(255); 00302 } 00303 #ifndef Q_WS_WIN 00304 ::exit(reply); 00305 break; 00306 } 00307 #endif 00308 return false; // make insure++ happy 00309 } 00310 00311 00312 KUniqueApplication::KUniqueApplication(bool GUIenabled, bool configUnique) 00313 : KApplication( GUIenabled, Private::initHack( configUnique )), 00314 d(new Private(this)) 00315 { 00316 d->processingRequest = false; 00317 d->firstInstance = true; 00318 00319 // the sanity checking happened in initHack 00320 new KUniqueApplicationAdaptor(this); 00321 00322 if (Private::s_nofork) 00323 // Can't call newInstance directly from the constructor since it's virtual... 00324 QTimer::singleShot( 0, this, SLOT(_k_newInstanceNoFork()) ); 00325 } 00326 00327 00328 #ifdef Q_WS_X11 00329 KUniqueApplication::KUniqueApplication(Display *display, Qt::HANDLE visual, 00330 Qt::HANDLE colormap, bool configUnique) 00331 : KApplication( display, visual, colormap, Private::initHack( configUnique )), 00332 d(new Private(this)) 00333 { 00334 d->processingRequest = false; 00335 d->firstInstance = true; 00336 00337 // the sanity checking happened in initHack 00338 new KUniqueApplicationAdaptor(this); 00339 00340 if (Private::s_nofork) 00341 // Can't call newInstance directly from the constructor since it's virtual... 00342 QTimer::singleShot( 0, this, SLOT(_k_newInstanceNoFork()) ); 00343 } 00344 #endif 00345 00346 00347 KUniqueApplication::~KUniqueApplication() 00348 { 00349 delete d; 00350 } 00351 00352 // this gets called before even entering QApplication::QApplication() 00353 KComponentData KUniqueApplication::Private::initHack(bool configUnique) 00354 { 00355 KComponentData cData(KCmdLineArgs::aboutData()); 00356 if (configUnique) 00357 { 00358 KConfigGroup cg(cData.config(), "KDE"); 00359 s_multipleInstances = cg.readEntry("MultipleInstances", false); 00360 } 00361 if( !KUniqueApplication::start()) 00362 // Already running 00363 ::exit( 0 ); 00364 return cData; 00365 } 00366 00367 void KUniqueApplication::Private::_k_newInstanceNoFork() 00368 { 00369 s_handleAutoStarted = false; 00370 q->newInstance(); 00371 firstInstance = false; 00372 #if defined Q_WS_X11 00373 // KDE4 remove 00374 // A hack to make startup notification stop for apps which override newInstance() 00375 // and reuse an already existing window there, but use KWindowSystem::activateWindow() 00376 // instead of KStartupInfo::setNewStartupId(). Therefore KWindowSystem::activateWindow() 00377 // for now sets this flag. Automatically ending startup notification always 00378 // would cause problem if the new window would show up with a small delay. 00379 if( s_handleAutoStarted ) 00380 KStartupInfo::handleAutoAppStartedSending(); 00381 #endif 00382 // What to do with the return value ? 00383 } 00384 00385 bool KUniqueApplication::restoringSession() 00386 { 00387 return d->firstInstance && isSessionRestored(); 00388 } 00389 00390 int KUniqueApplication::newInstance() 00391 { 00392 if (!d->firstInstance) { 00393 QList<KMainWindow*> allWindows = KMainWindow::memberList(); 00394 if (!allWindows.isEmpty()) { 00395 // This method is documented to only work for applications 00396 // with only one mainwindow. 00397 KMainWindow* mainWindow = allWindows.first(); 00398 if (mainWindow) { 00399 mainWindow->show(); 00400 #ifdef Q_WS_X11 00401 // This is the line that handles window activation if necessary, 00402 // and what's important, it does it properly. If you reimplement newInstance(), 00403 // and don't call the inherited one, use this (but NOT when newInstance() 00404 // is called for the first time, like here). 00405 KStartupInfo::setNewStartupId(mainWindow, startupId()); 00406 #endif 00407 #ifdef Q_WS_WIN 00408 KWindowSystem::forceActiveWindow( mainWindow->winId() ); 00409 #endif 00410 00411 } 00412 } 00413 } 00414 return 0; // do nothing in default implementation 00415 } 00416 00417 #ifndef KDE_NO_DEPRECATED 00418 void KUniqueApplication::setHandleAutoStarted() 00419 { 00420 Private::s_handleAutoStarted = false; 00421 } 00422 #endif 00423 00425 00426 int KUniqueApplicationAdaptor::newInstance(const QByteArray &asn_id, const QByteArray &args) 00427 { 00428 if (!asn_id.isEmpty()) 00429 parent()->setStartupId(asn_id); 00430 00431 const int index = parent()->metaObject()->indexOfMethod("loadCommandLineOptionsForNewInstance"); 00432 if (index != -1) { 00433 // This hook allows the application to set up KCmdLineArgs using addCmdLineOptions 00434 // before we load the app args. Normally not necessary, but needed by kontact 00435 // since it switches to other sets of options when called as e.g. kmail or korganizer 00436 QMetaObject::invokeMethod(parent(), "loadCommandLineOptionsForNewInstance"); 00437 } 00438 00439 QDataStream ds(args); 00440 KCmdLineArgs::loadAppArgs(ds); 00441 00442 int ret = parent()->newInstance(); 00443 // Must be done out of the newInstance code, in case it is overloaded 00444 parent()->d->firstInstance = false; 00445 return ret; 00446 } 00447 00448 #include "kuniqueapplication.moc" 00449 #include "kuniqueapplication_p.moc"
KDE 4.7 API Reference