kuniqueapplication.cpp Source File (kdecore Library)

| # kdecore Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | 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,v 1.52.2.2 2003/06/05 09:12:50 waba Exp $
    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 #include <kwin.h>
    00041 #include <kstartupinfo.h>
    00042 #include <kconfig.h>
    00043 #include "kdebug.h"
    00044 #include "kuniqueapplication.h"
    00045 #ifdef Q_WS_X11
    00046 #include <X11/Xlib.h>
    00047 #define DISPLAY "DISPLAY"
    00048 #else
    00049 #define DISPLAY "QWS_DISPLAY"
    00050 #endif
    00051
    00052 bool KUniqueApplication::s_nofork = false;
    00053 bool KUniqueApplication::s_multipleInstances = false;
    00054 bool KUniqueApplication::s_uniqueTestDone = false;
    00055
    00056 static KCmdLineOptions kunique_options[] =
    00057 {
    00058   { "nofork", "Don't run in the background.", 0 },
    00059   { 0, 0, 0 }
    00060 };
    00061
    00062 struct DCOPRequest {
    00063    QCString fun;
    00064    QByteArray data;
    00065    DCOPClientTransaction *transaction;
    00066 };
    00067
    00068 class KUniqueApplicationPrivate {
    00069 public:
    00070    QPtrList <DCOPRequest> requestList;
    00071    bool processingRequest;
    00072    bool firstInstance;
    00073 };
    00074
    00075 void
    00076 KUniqueApplication::addCmdLineOptions()
    00077 {
    00078   KCmdLineArgs::addCmdLineOptions(kunique_options, 0, "kuniqueapp", "kde" );
    00079 }
    00080
    00081 bool
    00082 KUniqueApplication::start()
    00083 {
    00084   if( s_uniqueTestDone )
    00085     return true;
    00086   s_uniqueTestDone = true;
    00087   addCmdLineOptions(); // Make sure to add cmd line options
    00088   KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kuniqueapp");
    00089   s_nofork = !args->isSet("fork");
    00090   delete args;
    00091
    00092   QCString appName = KCmdLineArgs::about->appName();
    00093
    00094   if (s_nofork)
    00095   {
    00096      if (s_multipleInstances)
    00097      {
    00098         QCString pid;
    00099         pid.setNum(getpid());
    00100         appName = appName + "-" + pid;
    00101      }
    00102      ( void ) dcopClient();
    00103      dcopClient()->registerAs(appName, false );
    00104      // We'll call newInstance in the constructor. Do nothing here.
    00105      return true;
    00106   }
    00107   DCOPClient *dc;
    00108   int fd[2];
    00109   signed char result;
    00110   if (0 > pipe(fd))
    00111   {
    00112      kdError() << "KUniqueApplication: pipe() failed!" << endl;
    00113      ::exit(255);
    00114   }
    00115   int fork_result = fork();
    00116   switch(fork_result) {
    00117   case -1:
    00118      kdError() << "KUniqueApplication: fork() failed!" << endl;
    00119      ::exit(255);
    00120      break;
    00121   case 0:
    00122      // Child
    00123      ::close(fd[0]);
    00124      if (s_multipleInstances)
    00125      {
    00126         QCString pid;
    00127         pid.setNum(getpid());
    00128         appName = appName + "-" + pid;
    00129      }
    00130      dc = dcopClient();
    00131      {
    00132         QCString regName = dc->registerAs(appName, false);
    00133         if (regName.isEmpty())
    00134         {
    00135            // Check DISPLAY
    00136            if (QCString(getenv(DISPLAY)).isEmpty())
    00137            {
    00138               kdError() << "KUniqueApplication: Can't determine DISPLAY. Aborting." << endl;
    00139               result = -1; // Error
    00140               ::write(fd[1], &result, 1);
    00141               ::exit(255);
    00142            }
    00143
    00144            // Try to launch kdeinit.
    00145            startKdeinit();
    00146            regName = dc->registerAs(appName, false);
    00147            if (regName.isEmpty())
    00148            {
    00149               kdError() << "KUniqueApplication: Can't setup DCOP communication." << endl;
    00150               result = -1;
    00151               delete dc;        // Clean up DCOP commmunication
    00152               ::write(fd[1], &result, 1);
    00153               ::exit(255);
    00154            }
    00155         }
    00156         if (regName != appName)
    00157         {
    00158            // Already running. Ok.
    00159            result = 0;
    00160            delete dc;   // Clean up DCOP commmunication
    00161            ::write(fd[1], &result, 1);
    00162            ::close(fd[1]);
    00163 #ifdef Q_WS_X11
    00164            // say we're up and running ( probably no new window will appear )
    00165            KStartupInfoId id;
    00166            if( kapp != NULL ) // KApplication constructor unsets the env. variable
    00167                id.initId( kapp->startupId());
    00168            else
    00169                id = KStartupInfo::currentStartupIdEnv();
    00170            if( !id.none())
    00171            {
    00172                Display* disp = XOpenDisplay( NULL );
    00173                if( disp != NULL ) // use extra X connection
    00174                {
    00175                    KStartupInfo::sendFinishX( disp, id );
    00176                    XCloseDisplay( disp );
    00177                }
    00178            }
    00179 #else //FIXME(E): implement
    00180 #endif
    00181            return false;
    00182         }
    00183      }
    00184
    00185      {
    00186 #ifdef Q_WS_X11
    00187          KStartupInfoId id;
    00188          if( kapp != NULL ) // KApplication constructor unsets the env. variable
    00189              id.initId( kapp->startupId());
    00190          else
    00191              id = KStartupInfo::currentStartupIdEnv();
    00192          if( !id.none())
    00193          { // notice about pid change
    00194             Display* disp = XOpenDisplay( NULL );
    00195             if( disp != NULL ) // use extra X connection
    00196                {
    00197                KStartupInfoData data;
    00198                data.addPid( getpid());
    00199                KStartupInfo::sendChangeX( disp, id, data );
    00200                XCloseDisplay( disp );
    00201                }
    00202          }
    00203 #else //FIXME(E): Implement
    00204 #endif
    00205      }
    00206      result = 0;
    00207      ::write(fd[1], &result, 1);
    00208      ::close(fd[1]);
    00209      return true; // Finished.
    00210   default:
    00211      // Parent
    00212 //     DCOPClient::emergencyClose();
    00213 //     dcopClient()->detach();
    00214      if (s_multipleInstances)
    00215      {
    00216         QCString pid;
    00217         pid.setNum(fork_result);
    00218         appName = appName + "-" + pid;
    00219      }
    00220      ::close(fd[1]);
    00221      for(;;)
    00222      {
    00223        int n = ::read(fd[0], &result, 1);
    00224        if (n == 1) break;
    00225        if (n == 0)
    00226        {
    00227           kdError() << "KUniqueApplication: Pipe closed unexpected." << endl;
    00228           ::exit(255);
    00229        }
    00230        if (errno != EINTR)
    00231        {
    00232           kdError() << "KUniqueApplication: Error reading from pipe." << endl;
    00233           ::exit(255);
    00234        }
    00235      }
    00236      ::close(fd[0]);
    00237
    00238      if (result != 0)
    00239         ::exit(result); // Error occured in child.
    00240
    00241      dc = new DCOPClient();
    00242      if (!dc->attach())
    00243      {
    00244         kdError() << "KUniqueApplication: Parent process can't attach to DCOP." << endl;
    00245         delete dc;      // Clean up DCOP commmunication
    00246         ::exit(255);
    00247      }
    00248      if (!dc->isApplicationRegistered(appName)) {
    00249         kdError() << "KUniqueApplication: Registering failed!" << endl;
    00250      }
    00251      QByteArray data, reply;
    00252      QDataStream ds(data, IO_WriteOnly);
    00253
    00254      KCmdLineArgs::saveAppArgs(ds);
    00255
    00256      QCString replyType;
    00257      if (!dc->call(appName, KCmdLineArgs::about->appName(), "newInstance()", data, replyType, reply))
    00258      {
    00259         kdError() << "Communication problem with " << KCmdLineArgs::about->appName() << ", it probably crashed." << endl;
    00260         delete dc;      // Clean up DCOP commmunication
    00261         ::exit(255);
    00262      }
    00263      if (replyType != "int")
    00264      {
    00265         kdError() << "KUniqueApplication: DCOP communication error!" << endl;
    00266         delete dc;      // Clean up DCOP commmunication
    00267         ::exit(255);
    00268      }
    00269      QDataStream rs(reply, IO_ReadOnly);
    00270      int exitCode;
    00271      rs >> exitCode;
    00272      delete dc; // Clean up DCOP commmunication
    00273      ::exit(exitCode);
    00274      break;
    00275   }
    00276   return false; // make insure++ happy
    00277 }
    00278
    00279
    00280 KUniqueApplication::KUniqueApplication(bool allowStyles, bool GUIenabled, bool configUnique)
    00281   : KApplication( allowStyles, GUIenabled, initHack( configUnique )),
    00282     DCOPObject(KCmdLineArgs::about->appName())
    00283 {
    00284   d = new KUniqueApplicationPrivate;
    00285   d->processingRequest = false;
    00286   d->firstInstance = true;
    00287
    00288   if (s_nofork)
    00289     // Can't call newInstance directly from the constructor since it's virtual...
    00290     QTimer::singleShot( 0, this, SLOT(newInstanceNoFork()) );
    00291 }
    00292
    00293 KUniqueApplication::~KUniqueApplication()
    00294 {
    00295   delete d;
    00296 }
    00297
    00298 // this gets called before even entering QApplication::QApplication()
    00299 KInstance* KUniqueApplication::initHack( bool configUnique )
    00300 {
    00301   KInstance* inst = new KInstance( KCmdLineArgs::about );
    00302   if (configUnique)
    00303   {
    00304     KConfigGroupSaver saver( inst->config(), "KDE" );
    00305     s_multipleInstances = inst->config()->readBoolEntry("MultipleInstances", false);
    00306   }
    00307   if( !start())
    00308          // Already running
    00309       ::exit( 0 );
    00310   return inst;
    00311 }
    00312
    00313 void KUniqueApplication::newInstanceNoFork()
    00314 {
    00315   if (dcopClient()->isSuspended())
    00316   {
    00317     // Try again later.
    00318     QTimer::singleShot( 200, this, SLOT(newInstanceNoFork()) );
    00319     return;
    00320   }
    00321
    00322   newInstance();
    00323   // What to do with the return value ?
    00324 }
    00325
    00326 bool KUniqueApplication::process(const QCString &fun, const QByteArray &data,
    00327                                  QCString &replyType, QByteArray &replyData)
    00328 {
    00329   if (fun == "newInstance()")
    00330   {
    00331     delayRequest(fun, data);
    00332     return true;
    00333   } else
    00334     return DCOPObject::process(fun, data, replyType, replyData);
    00335 }
    00336
    00337 void
    00338 KUniqueApplication::delayRequest(const QCString &fun, const QByteArray &data)
    00339 {
    00340   DCOPRequest *request = new DCOPRequest;
    00341   request->fun = fun;
    00342   request->data = data;
    00343   request->transaction = dcopClient()->beginTransaction();
    00344   d->requestList.append(request);
    00345   if (!d->processingRequest)
    00346   {
    00347      QTimer::singleShot(0, this, SLOT(processDelayed()));
    00348   }
    00349 }
    00350
    00351 void
    00352 KUniqueApplication::processDelayed()
    00353 {
    00354   if (dcopClient()->isSuspended())
    00355   {
    00356     // Try again later.
    00357     QTimer::singleShot( 200, this, SLOT(processDelayed()));
    00358     return;
    00359   }
    00360   d->processingRequest = true;
    00361   while( !d->requestList.isEmpty() )
    00362   {
    00363      DCOPRequest *request = d->requestList.take(0);
    00364      QByteArray replyData;
    00365      QCString replyType;
    00366      if (request->fun == "newInstance()") {
    00367        QDataStream ds(request->data, IO_ReadOnly);
    00368        KCmdLineArgs::loadAppArgs(ds);
    00369        int exitCode = newInstance();
    00370        QDataStream rs(replyData, IO_WriteOnly);
    00371        rs << exitCode;
    00372        replyType = "int";
    00373      }
    00374      dcopClient()->endTransaction( request->transaction, replyType, replyData);
    00375      delete request;
    00376   }
    00377
    00378   d->processingRequest = false;
    00379 }
    00380
    00381 int KUniqueApplication::newInstance()
    00382 {
    00383   if (!d->firstInstance)
    00384   {
    00385 #ifndef Q_WS_QWS // FIXME(E): Implement for Qt/Embedded
    00386      if ( mainWidget() )
    00387         KWin::setActiveWindow(mainWidget()->winId());
    00388 #endif
    00389   }
    00390   d->firstInstance = false;
    00391   return 0; // do nothing in default implementation
    00392 }
    00393
    00394 void KUniqueApplication::virtual_hook( int id, void* data )
    00395 { KApplication::virtual_hook( id, data );
    00396   DCOPObject::virtual_hook( id, data ); }
    00397
    00398 #include "kuniqueapplication.moc"

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 12:46:58 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001