• Skip to content
  • Skip to link menu
KDE 4.7 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     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"

KDEUI

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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.5
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