kdecore Library: kuniqueapplication.cpp Source File (kdecore Library)

| # kdecore Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# kuniqueapplication.cpp

    00001 /* This file is part of the KDE libraries
    00002     Copyright (c) 1999 Preston Brown <pbrown@kde.org>
    00003
    00004     $Id: kuniqueapplication.cpp 382228 2005-01-25 10:23:34Z dfaure $
    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., 59 Temple Place - Suite 330,
    00019     Boston, MA 02111-1307, 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.
    00478       KStartupInfo::setNewStartupId( mainWidget(), kapp->startupId());
    00479 #endif
    00480     }
    00481   }
    00482   return 0; // do nothing in default implementation
    00483 }
    00484
    00485 void KUniqueApplication::setHandleAutoStarted()
    00486 {
    00487     s_handleAutoStarted = false;
    00488 }
    00489
    00490 void KUniqueApplication::virtual_hook( int id, void* data )
    00491 { KApplication::virtual_hook( id, data );
    00492   DCOPObject::virtual_hook( id, data ); }
    00493
    00494 #include "kuniqueapplication.moc"

This file is part of the documentation for kdecore Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:18:12 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003