• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • 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 
00044 #if defined Q_WS_X11
00045 #include <kstartupinfo.h>
00046 #endif
00047 
00048 /* I don't know why, but I end up with complaints about
00049    a forward-declaration of QWidget in the activeWidow()->show
00050    call below on Qt/Mac if I don't include this here... */
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 /* private helpers from kapplication_win.cpp */
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     // Check the D-Bus connection health
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(); // Make sure to add cmd line options
00119 #ifdef Q_WS_WIN
00120   Private::s_nofork = true;
00121 #else
00122   KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kuniqueapp");
00123 #ifdef Q_WS_MACX
00124   // avoid focus loss caused by extra fork when launched from Finder
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      // Check to make sure that we're actually able to register with the D-Bus session
00159      // server.
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      // We'll call newInstance in the constructor. Do nothing here.
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         // Child
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            // Already running. Ok.
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 ) // KApplication constructor unsets the env. variable
00236              id.initId( kapp->startupId());
00237          else
00238              id = KStartupInfo::currentStartupIdEnv();
00239          if( !id.none())
00240          { // notice about pid change
00241             Display* disp = XOpenDisplay( NULL );
00242             if( disp != NULL ) // use extra X connection
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; // Finished.
00257   default:
00258      // Parent
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); // Error occurred in child.
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 ) // KApplication constructor unsets the env. variable
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; // make insure++ happy
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   // the sanity checking happened in initHack
00332   new KUniqueApplicationAdaptor(this);
00333 
00334   if (Private::s_nofork)
00335     // Can't call newInstance directly from the constructor since it's virtual...
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   // the sanity checking happened in initHack
00350   new KUniqueApplicationAdaptor(this);
00351 
00352   if (Private::s_nofork)
00353     // Can't call newInstance directly from the constructor since it's virtual...
00354     QTimer::singleShot( 0, this, SLOT(_k_newInstanceNoFork()) );
00355 }
00356 #endif
00357 
00358 
00359 KUniqueApplication::~KUniqueApplication()
00360 {
00361 #ifdef Q_WS_WIN
00362   // work around for KUniqueApplication being not completely implemented on windows
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 // this gets called before even entering QApplication::QApplication()
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      // Already running
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   // KDE4 remove
00396   // A hack to make startup notification stop for apps which override newInstance()
00397   // and reuse an already existing window there, but use KWindowSystem::activateWindow()
00398   // instead of KStartupInfo::setNewStartupId(). Therefore KWindowSystem::activateWindow()
00399   // for now sets this flag. Automatically ending startup notification always
00400   // would cause problem if the new window would show up with a small delay.
00401   if( s_handleAutoStarted )
00402       KStartupInfo::handleAutoAppStartedSending();
00403 #endif
00404   // What to do with the return value ?
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             // This method is documented to only work for applications
00418             // with only one mainwindow.
00419             KMainWindow* mainWindow = allWindows.first();
00420             if (mainWindow) {
00421                 mainWindow->show();
00422 #if defined Q_WS_X11
00423                 // This is the line that handles window activation if necessary,
00424                 // and what's important, it does it properly. If you reimplement newInstance(),
00425                 // and don't call the inherited one, use this (but NOT when newInstance()
00426                 // is called for the first time, like here).
00427                 KStartupInfo::setNewStartupId(mainWindow, startupId());
00428 #endif
00429             }
00430         }
00431     }
00432     return 0; // do nothing in default implementation
00433 }
00434 
00435 void KUniqueApplication::setHandleAutoStarted()
00436 {
00437     Private::s_handleAutoStarted = false;
00438 }
00439 
00440 #include "kuniqueapplication.moc"
00441 #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
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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