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