kdecore Library API Documentation

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"
KDE Logo
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