• Skip to content
  • Skip to link menu
KDE 4.0 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 
00023 #include <config.h>
00024 
00025 #include <sys/types.h>
00026 #include <sys/wait.h>
00027 
00028 #include <assert.h>
00029 #include <errno.h>
00030 #include <stdlib.h>
00031 #include <unistd.h>
00032 
00033 #include <QtCore/QFile>
00034 #include <QtCore/QList>
00035 #include <QtCore/QTimer>
00036 #include <QtDBus/QtDBus>
00037 
00038 #include <kcmdlineargs.h>
00039 #include <kstandarddirs.h>
00040 #include <kaboutdata.h>
00041 #include <kconfiggroup.h>
00042 
00043 #if defined Q_WS_X11
00044 #include <kstartupinfo.h>
00045 #endif
00046 
00047 /* I don't know why, but I end up with complaints about
00048    a forward-declaration of QWidget in the activeWidow()->show
00049    call below on Qt/Mac if I don't include this here... */
00050 #include <QWidget>
00051 
00052 #include <kconfig.h>
00053 #include "kdebug.h"
00054 
00055 #if defined Q_WS_X11
00056 #include <netwm.h>
00057 #include <X11/Xlib.h>
00058 #define DISPLAY "DISPLAY"
00059 #else
00060 #  ifdef Q_WS_QWS
00061 #    define DISPLAY "QWS_DISPLAY"
00062 #  else
00063 #    define DISPLAY "DISPLAY"
00064 #  endif
00065 #endif
00066 
00067 bool KUniqueApplication::Private::s_nofork = false;
00068 bool KUniqueApplication::Private::s_multipleInstances = false;
00069 bool s_kuniqueapplication_startCalled = false;
00070 bool KUniqueApplication::Private::s_handleAutoStarted = false;
00071 
00072 #ifdef Q_WS_MAC
00073 void KApplication_early_init_mac();
00074 #endif
00075 
00076 void
00077 KUniqueApplication::addCmdLineOptions()
00078 {
00079   KCmdLineOptions kunique_options;
00080   kunique_options.add("nofork", ki18n("Do not run in the background."));
00081 #ifdef Q_WS_MACX
00082   kunique_options.add("psn", ki18n("Internally added if launched from Finder"));
00083 #endif
00084   KCmdLineArgs::addCmdLineOptions(kunique_options, KLocalizedString(), "kuniqueapp", "kde");
00085 }
00086 
00087 static QDBusConnectionInterface *tryToInitDBusConnection()
00088 {
00089     // Check the D-Bus connection health
00090     QDBusConnectionInterface* dbusService = 0;
00091     if (!QDBusConnection::sessionBus().isConnected() || !(dbusService = QDBusConnection::sessionBus().interface()))
00092     {
00093         kError() << "KUniqueApplication: Cannot find the D-Bus session server" << endl;
00094         ::exit(255);
00095     }
00096     return dbusService;
00097 }
00098 
00099 bool
00100 KUniqueApplication::start()
00101 {
00102   if( s_kuniqueapplication_startCalled )
00103     return true;
00104   s_kuniqueapplication_startCalled = true;
00105 
00106   addCmdLineOptions(); // Make sure to add cmd line options
00107 #ifdef Q_WS_WIN
00108   Private::s_nofork = true;
00109 #else
00110   KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kuniqueapp");
00111 #ifdef Q_WS_MACX
00112   // avoid focus loss caused by extra fork when launched from Finder
00113   if(args->isSet("psn"))
00114      Private::s_nofork = true;
00115   else
00116 #endif
00117   Private::s_nofork = !args->isSet("fork");
00118   delete args;
00119 #endif
00120 
00121   QString appName = KCmdLineArgs::aboutData()->appName();
00122   const QStringList parts = KCmdLineArgs::aboutData()->organizationDomain().split(QLatin1Char('.'), QString::SkipEmptyParts);
00123   if (parts.isEmpty())
00124      appName.prepend(QLatin1String("local."));
00125   else
00126      foreach (const QString& s, parts)
00127      {
00128         appName.prepend(QLatin1Char('.'));
00129         appName.prepend(s);
00130      }
00131 
00132 #ifdef Q_WS_MAC
00133   KApplication_early_init_mac();
00134 #endif
00135 
00136   if (Private::s_nofork)
00137   {
00138      QDBusConnectionInterface* dbusService = tryToInitDBusConnection();
00139 
00140      if (Private::s_multipleInstances)
00141      {
00142         QString pid = QString::number(getpid());
00143         appName = appName + '-' + pid;
00144      }
00145 
00146      // Check to make sure that we're actually able to register with the D-Bus session
00147      // server.
00148      if (dbusService->registerService(appName) != QDBusConnectionInterface::ServiceRegistered)
00149      {
00150         kError() << "KUniqueApplication: Can't setup D-Bus service. Probably already running."
00151                  << endl;
00152         ::exit(255);
00153      }
00154 
00155      // We'll call newInstance in the constructor. Do nothing here.
00156      return true;
00157   }
00158 #ifndef Q_WS_WIN
00159   int fd[2];
00160   signed char result;
00161   if (0 > pipe(fd))
00162   {
00163      kError() << "KUniqueApplication: pipe() failed!" << endl;
00164      ::exit(255);
00165   }
00166   int fork_result = fork();
00167   switch(fork_result) {
00168   case -1:
00169      kError() << "KUniqueApplication: fork() failed!" << endl;
00170      ::exit(255);
00171      break;
00172   case 0:
00173      {
00174         // Child
00175 
00176         QDBusConnectionInterface* dbusService = tryToInitDBusConnection();
00177         ::close(fd[0]);
00178         if (Private::s_multipleInstances)
00179            appName.append("-").append(QString::number(getpid()));
00180 
00181         QDBusReply<QDBusConnectionInterface::RegisterServiceReply> reply =
00182             dbusService->registerService(appName);
00183         if (!reply.isValid())
00184         {
00185            kError() << "KUniqueApplication: Can't setup D-Bus service." << endl;
00186            result = -1;
00187            ::write(fd[1], &result, 1);
00188            ::exit(255);
00189         }
00190         if (reply == QDBusConnectionInterface::ServiceNotRegistered)
00191         {
00192            // Already running. Ok.
00193            result = 0;
00194            ::write(fd[1], &result, 1);
00195            ::close(fd[1]);
00196            return false;
00197         }
00198 
00199 #ifdef Q_WS_X11
00200          KStartupInfoId id;
00201          if( kapp != NULL ) // KApplication constructor unsets the env. variable
00202              id.initId( kapp->startupId());
00203          else
00204              id = KStartupInfo::currentStartupIdEnv();
00205          if( !id.none())
00206          { // notice about pid change
00207             Display* disp = XOpenDisplay( NULL );
00208             if( disp != NULL ) // use extra X connection
00209             {
00210                KStartupInfoData data;
00211                data.addPid( getpid());
00212                KStartupInfo::sendChangeX( disp, id, data );
00213                XCloseDisplay( disp );
00214             }
00215          }
00216 #else //FIXME(E): Implement
00217 #endif
00218      }
00219      result = 0;
00220      ::write(fd[1], &result, 1);
00221      ::close(fd[1]);
00222      return true; // Finished.
00223   default:
00224      // Parent
00225 
00226      if (Private::s_multipleInstances)
00227         appName.append("-").append(QString::number(fork_result));
00228      ::close(fd[1]);
00229 
00230      Q_FOREVER
00231      {
00232        int n = ::read(fd[0], &result, 1);
00233        if (n == 1) break;
00234        if (n == 0)
00235        {
00236           kError() << "KUniqueApplication: Pipe closed unexpectedly." << endl;
00237           ::exit(255);
00238        }
00239        if (errno != EINTR)
00240        {
00241           kError() << "KUniqueApplication: Error reading from pipe." << endl;
00242           ::exit(255);
00243        }
00244      }
00245      ::close(fd[0]);
00246 
00247      if (result != 0)
00248         ::exit(result); // Error occurred in child.
00249 
00250 #endif
00251      QDBusConnectionInterface* dbusService = tryToInitDBusConnection();
00252      if (!dbusService->isServiceRegistered(appName))
00253      {
00254         kError() << "KUniqueApplication: Registering failed!" << endl;
00255      }
00256 
00257      QByteArray saved_args;
00258      QDataStream ds(&saved_args, QIODevice::WriteOnly);
00259      KCmdLineArgs::saveAppArgs(ds);
00260 
00261      QByteArray new_asn_id;
00262 #if defined Q_WS_X11
00263      KStartupInfoId id;
00264      if( kapp != NULL ) // KApplication constructor unsets the env. variable
00265          id.initId( kapp->startupId());
00266      else
00267          id = KStartupInfo::currentStartupIdEnv();
00268      if( !id.none())
00269          new_asn_id = id.id();
00270 #endif
00271 
00272      QDBusInterface iface(appName, "/MainApplication", "org.kde.KUniqueApplication", QDBusConnection::sessionBus());
00273      QDBusReply<int> reply;
00274      if (!iface.isValid() || !(reply = iface.call("newInstance", new_asn_id, saved_args)).isValid())
00275      {
00276        QDBusError err = iface.lastError();
00277         kError() << "Communication problem with " << KCmdLineArgs::aboutData()->appName() << ", it probably crashed." << endl
00278                  << "Error message was: " << err.name() << ": \"" << err.message() << "\"" << endl;
00279         ::exit(255);
00280      }
00281 #ifndef Q_WS_WIN
00282      ::exit(reply);
00283      break;
00284   }
00285 #endif
00286   return false; // make insure++ happy
00287 }
00288 
00289 
00290 KUniqueApplication::KUniqueApplication(bool GUIenabled, bool configUnique)
00291   : KApplication( GUIenabled, Private::initHack( configUnique )),
00292     d(new Private(this))
00293 {
00294   d->processingRequest = false;
00295   d->firstInstance = true;
00296 
00297   // the sanity checking happened in initHack
00298   new KUniqueApplicationAdaptor(this);
00299 
00300   if (Private::s_nofork)
00301     // Can't call newInstance directly from the constructor since it's virtual...
00302     QTimer::singleShot( 0, this, SLOT(_k_newInstanceNoFork()) );
00303 }
00304 
00305 
00306 #ifdef Q_WS_X11
00307 KUniqueApplication::KUniqueApplication(Display *display, Qt::HANDLE visual,
00308         Qt::HANDLE colormap, bool configUnique)
00309   : KApplication( display, visual, colormap, 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 #endif
00323 
00324 
00325 KUniqueApplication::~KUniqueApplication()
00326 {
00327   delete d;
00328 }
00329 
00330 // this gets called before even entering QApplication::QApplication()
00331 KComponentData KUniqueApplication::Private::initHack(bool configUnique)
00332 {
00333   KComponentData cData(KCmdLineArgs::aboutData());
00334   if (configUnique)
00335   {
00336      KConfigGroup cg(cData.config(), "KDE");
00337      s_multipleInstances = cg.readEntry("MultipleInstances", false);
00338   }
00339   if( !KUniqueApplication::start())
00340      // Already running
00341      ::exit( 0 );
00342   return cData;
00343 }
00344 
00345 void KUniqueApplication::Private::_k_newInstanceNoFork()
00346 {
00347   s_handleAutoStarted = false;
00348   q->newInstance();
00349   firstInstance = false;
00350 #if defined Q_WS_X11
00351   // KDE4 remove
00352   // A hack to make startup notification stop for apps which override newInstance()
00353   // and reuse an already existing window there, but use KWindowSystem::activateWindow()
00354   // instead of KStartupInfo::setNewStartupId(). Therefore KWindowSystem::activateWindow()
00355   // for now sets this flag. Automatically ending startup notification always
00356   // would cause problem if the new window would show up with a small delay.
00357   if( s_handleAutoStarted )
00358       KStartupInfo::handleAutoAppStartedSending();
00359 #endif
00360   // What to do with the return value ?
00361 }
00362 
00363 bool KUniqueApplication::restoringSession()
00364 {
00365   return d->firstInstance && isSessionRestored();
00366 }
00367 
00368 int KUniqueApplication::newInstance()
00369 {
00370   if (!d->firstInstance)
00371   {
00372 
00373     if ( activeWindow() )
00374     {
00375       activeWindow()->show();
00376 #if defined Q_WS_X11
00377     // This is the line that handles window activation if necessary,
00378     // and what's important, it does it properly. If you reimplement newInstance(),
00379     // and don't call the inherited one, use this (but NOT when newInstance()
00380     // is called for the first time, like here).
00381       KStartupInfo::setNewStartupId( activeWindow(), kapp->startupId());
00382 #endif
00383     }
00384   }
00385   return 0; // do nothing in default implementation
00386 }
00387 
00388 void KUniqueApplication::setHandleAutoStarted()
00389 {
00390     Private::s_handleAutoStarted = false;
00391 }
00392 
00393 #include "kuniqueapplication.moc"
00394 #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
  •   WTF
  • KJSEmbed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  •   core
  • Phonon
  •   Backend
  • 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