• Skip to content
  • Skip to link menu
KDE 4.1 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         // on Windows we'll restart the app (at least in KMail, KOrganizer, Konact...)
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      // We'll call newInstance in the constructor. Do nothing here.
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         // Child
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            // Already running. Ok.
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 ) // KApplication constructor unsets the env. variable
00238              id.initId( kapp->startupId());
00239          else
00240              id = KStartupInfo::currentStartupIdEnv();
00241          if( !id.none())
00242          { // notice about pid change
00243             Display* disp = XOpenDisplay( NULL );
00244             if( disp != NULL ) // use extra X connection
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; // Finished.
00259   default:
00260      // Parent
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); // Error occurred in child.
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 ) // KApplication constructor unsets the env. variable
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; // make insure++ happy
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   // 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 
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   // the sanity checking happened in initHack
00352   new KUniqueApplicationAdaptor(this);
00353 
00354   if (Private::s_nofork)
00355     // Can't call newInstance directly from the constructor since it's virtual...
00356     QTimer::singleShot( 0, this, SLOT(_k_newInstanceNoFork()) );
00357 }
00358 #endif
00359 
00360 
00361 KUniqueApplication::~KUniqueApplication()
00362 {
00363 #ifdef Q_WS_WIN
00364   // work around for KUniqueApplication being not completely implemented on windows
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 // this gets called before even entering QApplication::QApplication()
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      // Already running
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   // KDE4 remove
00398   // A hack to make startup notification stop for apps which override newInstance()
00399   // and reuse an already existing window there, but use KWindowSystem::activateWindow()
00400   // instead of KStartupInfo::setNewStartupId(). Therefore KWindowSystem::activateWindow()
00401   // for now sets this flag. Automatically ending startup notification always
00402   // would cause problem if the new window would show up with a small delay.
00403   if( s_handleAutoStarted )
00404       KStartupInfo::handleAutoAppStartedSending();
00405 #endif
00406   // What to do with the return value ?
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             // This method is documented to only work for applications
00420             // with only one mainwindow.
00421             KMainWindow* mainWindow = allWindows.first();
00422             if (mainWindow) {
00423                 mainWindow->show();
00424 #if defined Q_WS_X11
00425                 // This is the line that handles window activation if necessary,
00426                 // and what's important, it does it properly. If you reimplement newInstance(),
00427                 // and don't call the inherited one, use this (but NOT when newInstance()
00428                 // is called for the first time, like here).
00429                 KStartupInfo::setNewStartupId(mainWindow, startupId());
00430 #endif
00431             }
00432         }
00433     }
00434     return 0; // do nothing in default implementation
00435 }
00436 
00437 void KUniqueApplication::setHandleAutoStarted()
00438 {
00439     Private::s_handleAutoStarted = false;
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
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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