• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KDECore

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     $Id: kuniqueapplication.cpp 474468 2005-10-26 13:51:39Z lunakl $
00005 
00006     This library is free software; you can redistribute it and/or
00007     modify it under the terms of the GNU Library General Public
00008     License as published by the Free Software Foundation; either
00009     version 2 of the License, or (at your option) any later version.
00010 
00011     This library is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     Library General Public License for more details.
00015 
00016     You should have received a copy of the GNU Library General Public License
00017     along with this library; see the file COPYING.LIB.  If not, write to
00018     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019     Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include <config.h>
00023 
00024 #include <sys/types.h>
00025 #include <sys/wait.h>
00026 
00027 #include <assert.h>
00028 #include <errno.h>
00029 #include <stdlib.h>
00030 #include <unistd.h>
00031 
00032 #include <qfile.h>
00033 #include <qptrlist.h>
00034 #include <qtimer.h>
00035 
00036 #include <dcopclient.h>
00037 #include <kcmdlineargs.h>
00038 #include <kstandarddirs.h>
00039 #include <kaboutdata.h>
00040 
00041 #if defined Q_WS_X11
00042 #include <kwin.h> 
00043 #include <kstartupinfo.h> 
00044 #endif
00045 
00046 #include <kconfig.h>
00047 #include "kdebug.h"
00048 #include "kuniqueapplication.h"
00049 
00050 #if defined Q_WS_X11
00051 #include <netwm.h> 
00052 #include <X11/Xlib.h> 
00053 #define DISPLAY "DISPLAY"
00054 #else
00055 #  ifdef Q_WS_QWS
00056 #    define DISPLAY "QWS_DISPLAY"
00057 #  else
00058 #    define DISPLAY "DISPLAY"
00059 #  endif
00060 #endif
00061 
00062 bool KUniqueApplication::s_nofork = false;
00063 bool KUniqueApplication::s_multipleInstances = false;
00064 bool KUniqueApplication::s_uniqueTestDone = false;
00065 bool KUniqueApplication::s_handleAutoStarted = false;
00066 
00067 static KCmdLineOptions kunique_options[] =
00068 {
00069   { "nofork", "Don't run in the background.", 0 },
00070   KCmdLineLastOption
00071 };
00072 
00073 struct DCOPRequest {
00074    QCString fun;
00075    QByteArray data;
00076    DCOPClientTransaction *transaction;
00077 };
00078 
00079 class KUniqueApplicationPrivate {
00080 public:
00081    QPtrList <DCOPRequest> requestList;
00082    bool processingRequest;
00083    bool firstInstance;
00084 };
00085 
00086 void
00087 KUniqueApplication::addCmdLineOptions()
00088 {
00089   KCmdLineArgs::addCmdLineOptions(kunique_options, 0, "kuniqueapp", "kde" );
00090 }
00091 
00092 bool
00093 KUniqueApplication::start()
00094 {
00095   if( s_uniqueTestDone )
00096     return true;
00097   s_uniqueTestDone = true;
00098   addCmdLineOptions(); // Make sure to add cmd line options
00099 #ifdef Q_WS_WIN
00100   s_nofork = true;
00101 #else
00102   KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kuniqueapp");
00103   s_nofork = !args->isSet("fork");
00104   delete args;
00105 #endif
00106 
00107   QCString appName = KCmdLineArgs::about->appName();
00108 
00109   if (s_nofork)
00110   {
00111      if (s_multipleInstances)
00112      {
00113         QCString pid;
00114         pid.setNum(getpid());
00115         appName = appName + "-" + pid;
00116      }
00117 
00118      // Check to make sure that we're actually able to register with the DCOP
00119      // server.
00120 
00121 #ifndef Q_WS_WIN //TODO
00122      if(dcopClient()->registerAs(appName, false).isEmpty()) {
00123         startKdeinit();
00124         if(dcopClient()->registerAs(appName, false).isEmpty()) {
00125            kdError() << "KUniqueApplication: Can't setup DCOP communication." << endl;
00126            ::exit(255);
00127         }           
00128      }
00129 #endif
00130 
00131      // We'll call newInstance in the constructor. Do nothing here.
00132      return true;
00133   }
00134   DCOPClient *dc;
00135   int fd[2];
00136   signed char result;
00137   if (0 > pipe(fd))
00138   {
00139      kdError() << "KUniqueApplication: pipe() failed!" << endl;
00140      ::exit(255);
00141   }
00142   int fork_result = fork();
00143   switch(fork_result) {
00144   case -1:
00145      kdError() << "KUniqueApplication: fork() failed!" << endl;
00146      ::exit(255);
00147      break;
00148   case 0:
00149      // Child
00150      ::close(fd[0]);
00151      if (s_multipleInstances)
00152         appName.append("-").append(QCString().setNum(getpid()));
00153      dc = dcopClient();
00154      {
00155         QCString regName = dc->registerAs(appName, false);
00156         if (regName.isEmpty())
00157         {
00158            // Check DISPLAY
00159            if (QCString(getenv(DISPLAY)).isEmpty())
00160            {
00161               kdError() << "KUniqueApplication: Can't determine DISPLAY. Aborting." << endl;
00162               result = -1; // Error
00163               ::write(fd[1], &result, 1);
00164               ::exit(255);
00165            }
00166 
00167            // Try to launch kdeinit.
00168            startKdeinit();
00169            regName = dc->registerAs(appName, false);
00170            if (regName.isEmpty())
00171            {
00172               kdError() << "KUniqueApplication: Can't setup DCOP communication." << endl;
00173               result = -1;
00174               delete dc;    // Clean up DCOP commmunication
00175               ::write(fd[1], &result, 1);
00176               ::exit(255);
00177            }
00178         }
00179         if (regName != appName)
00180         {
00181            // Already running. Ok.
00182            result = 0;
00183            delete dc;   // Clean up DCOP commmunication
00184            ::write(fd[1], &result, 1);
00185            ::close(fd[1]);
00186 #if 0
00187 #ifdef Q_WS_X11
00188            // say we're up and running ( probably no new window will appear )
00189            KStartupInfoId id;
00190            if( kapp != NULL ) // KApplication constructor unsets the env. variable
00191                id.initId( kapp->startupId());
00192            else
00193                id = KStartupInfo::currentStartupIdEnv();
00194            if( !id.none())
00195            {
00196                Display* disp = XOpenDisplay( NULL );
00197                if( disp != NULL ) // use extra X connection
00198                {
00199                    KStartupInfo::sendFinishX( disp, id );
00200                    XCloseDisplay( disp );
00201                }
00202            }
00203 #else //FIXME(E): implement
00204 #endif
00205 #endif
00206            return false;
00207         }
00208         dc->setPriorityCall(true);
00209      }
00210 
00211      {
00212 #ifdef Q_WS_X11
00213          KStartupInfoId id;
00214          if( kapp != NULL ) // KApplication constructor unsets the env. variable
00215              id.initId( kapp->startupId());
00216          else
00217              id = KStartupInfo::currentStartupIdEnv();
00218          if( !id.none())
00219          { // notice about pid change
00220             Display* disp = XOpenDisplay( NULL );
00221             if( disp != NULL ) // use extra X connection
00222                {
00223                KStartupInfoData data;
00224                data.addPid( getpid());
00225                KStartupInfo::sendChangeX( disp, id, data );
00226                XCloseDisplay( disp );
00227                }
00228          }
00229 #else //FIXME(E): Implement
00230 #endif
00231      }
00232      result = 0;
00233      ::write(fd[1], &result, 1);
00234      ::close(fd[1]);
00235      return true; // Finished.
00236   default:
00237      // Parent
00238 //     DCOPClient::emergencyClose();
00239 //     dcopClient()->detach();
00240      if (s_multipleInstances)
00241         appName.append("-").append(QCString().setNum(fork_result));
00242      ::close(fd[1]);
00243      for(;;)
00244      {
00245        int n = ::read(fd[0], &result, 1);
00246        if (n == 1) break;
00247        if (n == 0)
00248        {
00249           kdError() << "KUniqueApplication: Pipe closed unexpectedly." << endl;
00250           ::exit(255);
00251        }
00252        if (errno != EINTR)
00253        {
00254           kdError() << "KUniqueApplication: Error reading from pipe." << endl;
00255           ::exit(255);
00256        }
00257      }
00258      ::close(fd[0]);
00259 
00260      if (result != 0)
00261         ::exit(result); // Error occurred in child.
00262 
00263      dc = new DCOPClient();
00264      if (!dc->attach())
00265      {
00266         kdError() << "KUniqueApplication: Parent process can't attach to DCOP." << endl;
00267         delete dc;  // Clean up DCOP commmunication
00268         ::exit(255);
00269      }
00270      if (!dc->isApplicationRegistered(appName)) {
00271         kdError() << "KUniqueApplication: Registering failed!" << endl;
00272      }
00273 
00274      QCString new_asn_id;
00275 #if defined Q_WS_X11
00276      KStartupInfoId id;
00277      if( kapp != NULL ) // KApplication constructor unsets the env. variable
00278          id.initId( kapp->startupId());
00279      else
00280          id = KStartupInfo::currentStartupIdEnv();
00281      if( !id.none())
00282          new_asn_id = id.id();
00283 #endif
00284      
00285      QByteArray data, reply;
00286      QDataStream ds(data, IO_WriteOnly);
00287 
00288      KCmdLineArgs::saveAppArgs(ds);
00289      ds << new_asn_id;
00290 
00291      dc->setPriorityCall(true);
00292      QCString replyType;
00293      if (!dc->call(appName, KCmdLineArgs::about->appName(), "newInstance()", data, replyType, reply))
00294      {
00295         kdError() << "Communication problem with " << KCmdLineArgs::about->appName() << ", it probably crashed." << endl;
00296         delete dc;  // Clean up DCOP commmunication
00297         ::exit(255);
00298      }
00299      dc->setPriorityCall(false);
00300      if (replyType != "int")
00301      {
00302         kdError() << "KUniqueApplication: DCOP communication error!" << endl;
00303         delete dc;  // Clean up DCOP commmunication
00304         ::exit(255);
00305      }
00306      QDataStream rs(reply, IO_ReadOnly);
00307      int exitCode;
00308      rs >> exitCode;
00309      delete dc; // Clean up DCOP commmunication
00310      ::exit(exitCode);
00311      break;
00312   }
00313   return false; // make insure++ happy
00314 }
00315 
00316 
00317 KUniqueApplication::KUniqueApplication(bool allowStyles, bool GUIenabled, bool configUnique)
00318   : KApplication( allowStyles, GUIenabled, initHack( configUnique )),
00319     DCOPObject(KCmdLineArgs::about->appName())
00320 {
00321   d = new KUniqueApplicationPrivate;
00322   d->processingRequest = false;
00323   d->firstInstance = true;
00324 
00325   if (s_nofork)
00326     // Can't call newInstance directly from the constructor since it's virtual...
00327     QTimer::singleShot( 0, this, SLOT(newInstanceNoFork()) );
00328 }
00329 
00330 
00331 #ifdef Q_WS_X11
00332 KUniqueApplication::KUniqueApplication(Display *display, Qt::HANDLE visual,
00333         Qt::HANDLE colormap, bool allowStyles, bool configUnique)
00334   : KApplication( display, visual, colormap, allowStyles, initHack( configUnique )),
00335     DCOPObject(KCmdLineArgs::about->appName())
00336 {
00337   d = new KUniqueApplicationPrivate;
00338   d->processingRequest = false;
00339   d->firstInstance = true;
00340 
00341   if (s_nofork)
00342     // Can't call newInstance directly from the constructor since it's virtual...
00343     QTimer::singleShot( 0, this, SLOT(newInstanceNoFork()) );
00344 }
00345 #endif
00346 
00347 
00348 KUniqueApplication::~KUniqueApplication()
00349 {
00350   delete d;
00351 }
00352 
00353 // this gets called before even entering QApplication::QApplication()
00354 KInstance* KUniqueApplication::initHack( bool configUnique )
00355 {
00356   KInstance* inst = new KInstance( KCmdLineArgs::about );
00357   if (configUnique)
00358   {
00359     KConfigGroupSaver saver( inst->config(), "KDE" );
00360     s_multipleInstances = inst->config()->readBoolEntry("MultipleInstances", false);
00361   }
00362   if( !start())
00363          // Already running
00364       ::exit( 0 );
00365   return inst;
00366 }
00367 
00368 void KUniqueApplication::newInstanceNoFork()
00369 {
00370   if (dcopClient()->isSuspended())
00371   {
00372     // Try again later.
00373     QTimer::singleShot( 200, this, SLOT(newInstanceNoFork()) );
00374     return;
00375   }
00376   
00377   s_handleAutoStarted = false;
00378   newInstance();
00379   d->firstInstance = false;
00380 #if defined Q_WS_X11
00381   // KDE4 remove
00382   // A hack to make startup notification stop for apps which override newInstance()
00383   // and reuse an already existing window there, but use KWin::activateWindow()
00384   // instead of KStartupInfo::setNewStartupId(). Therefore KWin::activateWindow()
00385   // for now sets this flag. Automatically ending startup notification always
00386   // would cause problem if the new window would show up with a small delay.
00387   if( s_handleAutoStarted )
00388       KStartupInfo::handleAutoAppStartedSending();
00389 #endif
00390   // What to do with the return value ?
00391 }
00392 
00393 bool KUniqueApplication::process(const QCString &fun, const QByteArray &data,
00394                  QCString &replyType, QByteArray &replyData)
00395 {
00396   if (fun == "newInstance()")
00397   {
00398     delayRequest(fun, data);
00399     return true;
00400   } else
00401     return DCOPObject::process(fun, data, replyType, replyData);
00402 }
00403 
00404 void
00405 KUniqueApplication::delayRequest(const QCString &fun, const QByteArray &data)
00406 {
00407   DCOPRequest *request = new DCOPRequest;
00408   request->fun = fun;
00409   request->data = data;
00410   request->transaction = dcopClient()->beginTransaction();
00411   d->requestList.append(request);
00412   if (!d->processingRequest)
00413   {
00414      QTimer::singleShot(0, this, SLOT(processDelayed()));
00415   }
00416 }
00417 
00418 void
00419 KUniqueApplication::processDelayed()
00420 {
00421   if (dcopClient()->isSuspended())
00422   {
00423     // Try again later.
00424     QTimer::singleShot( 200, this, SLOT(processDelayed()));
00425     return;
00426   }
00427   d->processingRequest = true;
00428   while( !d->requestList.isEmpty() )
00429   {
00430      DCOPRequest *request = d->requestList.take(0);
00431      QByteArray replyData;
00432      QCString replyType;
00433      if (request->fun == "newInstance()") {
00434        dcopClient()->setPriorityCall(false);
00435        QDataStream ds(request->data, IO_ReadOnly);
00436        KCmdLineArgs::loadAppArgs(ds);
00437        if( !ds.atEnd()) // backwards compatibility
00438        {
00439            QCString asn_id;
00440            ds >> asn_id;
00441            setStartupId( asn_id );
00442        }
00443        s_handleAutoStarted = false;
00444        int exitCode = newInstance();
00445        d->firstInstance = false;
00446 #if defined Q_WS_X11
00447        if( s_handleAutoStarted )
00448            KStartupInfo::handleAutoAppStartedSending(); // KDE4 remove?
00449 #endif
00450        QDataStream rs(replyData, IO_WriteOnly);
00451        rs << exitCode;
00452        replyType = "int";
00453      }
00454      dcopClient()->endTransaction( request->transaction, replyType, replyData);
00455      delete request;
00456   }
00457 
00458   d->processingRequest = false;
00459 }
00460 
00461 bool KUniqueApplication::restoringSession()
00462 {
00463   return d->firstInstance && isRestored();
00464 }
00465 
00466 int KUniqueApplication::newInstance()
00467 {
00468   if (!d->firstInstance)
00469   {
00470     
00471     if ( mainWidget() )
00472     {
00473       mainWidget()->show();
00474 #if defined Q_WS_X11
00475     // This is the line that handles window activation if necessary,
00476     // and what's important, it does it properly. If you reimplement newInstance(),
00477     // and don't call the inherited one, use this (but NOT when newInstance()
00478     // is called for the first time, like here).
00479       KStartupInfo::setNewStartupId( mainWidget(), kapp->startupId());
00480 #endif
00481     }
00482   }
00483   return 0; // do nothing in default implementation
00484 }
00485 
00486 void KUniqueApplication::setHandleAutoStarted()
00487 {
00488     s_handleAutoStarted = false;
00489 }
00490 
00491 void KUniqueApplication::virtual_hook( int id, void* data )
00492 { KApplication::virtual_hook( id, data );
00493   DCOPObject::virtual_hook( id, data ); }
00494 
00495 #include "kuniqueapplication.moc"

KDECore

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

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
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