• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

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

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal