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.74 2004/06/03 12:54:00 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> 
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 && ! defined K_WS_QTONLY
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   KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kuniqueapp");
00100   s_nofork = !args->isSet("fork");
00101   delete args;
00102 
00103   QCString appName = KCmdLineArgs::about->appName();
00104 
00105   if (s_nofork)
00106   {
00107      if (s_multipleInstances)
00108      {
00109         QCString pid;
00110         pid.setNum(getpid());
00111         appName = appName + "-" + pid;
00112      }
00113 
00114      // Check to make sure that we're actually able to register with the DCOP
00115      // server.
00116 
00117      if(dcopClient()->registerAs(appName, false).isEmpty()) {
00118         startKdeinit();
00119         if(dcopClient()->registerAs(appName, false).isEmpty()) {
00120            kdError() << "KUniqueApplication: Can't setup DCOP communication." << endl;
00121            ::exit(255);
00122         }           
00123      }
00124 
00125      // We'll call newInstance in the constructor. Do nothing here.
00126      return true;
00127   }
00128   DCOPClient *dc;
00129   int fd[2];
00130   signed char result;
00131   if (0 > pipe(fd))
00132   {
00133      kdError() << "KUniqueApplication: pipe() failed!" << endl;
00134      ::exit(255);
00135   }
00136   int fork_result = fork();
00137   switch(fork_result) {
00138   case -1:
00139      kdError() << "KUniqueApplication: fork() failed!" << endl;
00140      ::exit(255);
00141      break;
00142   case 0:
00143      // Child
00144      ::close(fd[0]);
00145      if (s_multipleInstances)
00146         appName.append("-").append(QCString().setNum(getpid()));
00147      dc = dcopClient();
00148      {
00149         QCString regName = dc->registerAs(appName, false);
00150         if (regName.isEmpty())
00151         {
00152            // Check DISPLAY
00153            if (QCString(getenv(DISPLAY)).isEmpty())
00154            {
00155               kdError() << "KUniqueApplication: Can't determine DISPLAY. Aborting." << endl;
00156               result = -1; // Error
00157               ::write(fd[1], &result, 1);
00158               ::exit(255);
00159            }
00160 
00161            // Try to launch kdeinit.
00162            startKdeinit();
00163            regName = dc->registerAs(appName, false);
00164            if (regName.isEmpty())
00165            {
00166               kdError() << "KUniqueApplication: Can't setup DCOP communication." << endl;
00167               result = -1;
00168               delete dc;    // Clean up DCOP commmunication
00169               ::write(fd[1], &result, 1);
00170               ::exit(255);
00171            }
00172         }
00173         if (regName != appName)
00174         {
00175            // Already running. Ok.
00176            result = 0;
00177            delete dc;   // Clean up DCOP commmunication
00178            ::write(fd[1], &result, 1);
00179            ::close(fd[1]);
00180 #if 0
00181 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00182 //#ifdef Q_WS_X11
00183            // say we're up and running ( probably no new window will appear )
00184            KStartupInfoId id;
00185            if( kapp != NULL ) // KApplication constructor unsets the env. variable
00186                id.initId( kapp->startupId());
00187            else
00188                id = KStartupInfo::currentStartupIdEnv();
00189            if( !id.none())
00190            {
00191                Display* disp = XOpenDisplay( NULL );
00192                if( disp != NULL ) // use extra X connection
00193                {
00194                    KStartupInfo::sendFinishX( disp, id );
00195                    XCloseDisplay( disp );
00196                }
00197            }
00198 #else //FIXME(E): implement
00199 #endif
00200 #endif
00201            return false;
00202         }
00203         dc->setPriorityCall(true);
00204      }
00205 
00206      {
00207 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00208 //#ifdef Q_WS_X11
00209          KStartupInfoId id;
00210          if( kapp != NULL ) // KApplication constructor unsets the env. variable
00211              id.initId( kapp->startupId());
00212          else
00213              id = KStartupInfo::currentStartupIdEnv();
00214          if( !id.none())
00215          { // notice about pid change
00216             Display* disp = XOpenDisplay( NULL );
00217             if( disp != NULL ) // use extra X connection
00218                {
00219                KStartupInfoData data;
00220                data.addPid( getpid());
00221                KStartupInfo::sendChangeX( disp, id, data );
00222                XCloseDisplay( disp );
00223                }
00224          }
00225 #else //FIXME(E): Implement
00226 #endif
00227      }
00228      result = 0;
00229      ::write(fd[1], &result, 1);
00230      ::close(fd[1]);
00231      return true; // Finished.
00232   default:
00233      // Parent
00234 //     DCOPClient::emergencyClose();
00235 //     dcopClient()->detach();
00236      if (s_multipleInstances)
00237         appName.append("-").append(QCString().setNum(fork_result));
00238      ::close(fd[1]);
00239      for(;;)
00240      {
00241        int n = ::read(fd[0], &result, 1);
00242        if (n == 1) break;
00243        if (n == 0)
00244        {
00245           kdError() << "KUniqueApplication: Pipe closed unexpectedly." << endl;
00246           ::exit(255);
00247        }
00248        if (errno != EINTR)
00249        {
00250           kdError() << "KUniqueApplication: Error reading from pipe." << endl;
00251           ::exit(255);
00252        }
00253      }
00254      ::close(fd[0]);
00255 
00256      if (result != 0)
00257         ::exit(result); // Error occurred in child.
00258 
00259      dc = new DCOPClient();
00260      if (!dc->attach())
00261      {
00262         kdError() << "KUniqueApplication: Parent process can't attach to DCOP." << endl;
00263         delete dc;  // Clean up DCOP commmunication
00264         ::exit(255);
00265      }
00266      if (!dc->isApplicationRegistered(appName)) {
00267         kdError() << "KUniqueApplication: Registering failed!" << endl;
00268      }
00269 
00270      QCString new_asn_id;
00271 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00272      KStartupInfoId id;
00273      if( kapp != NULL ) // KApplication constructor unsets the env. variable
00274          id.initId( kapp->startupId());
00275      else
00276          id = KStartupInfo::currentStartupIdEnv();
00277      if( !id.none())
00278          new_asn_id = id.id();
00279 #endif
00280      
00281      QByteArray data, reply;
00282      QDataStream ds(data, IO_WriteOnly);
00283 
00284      KCmdLineArgs::saveAppArgs(ds);
00285      ds << new_asn_id;
00286 
00287      dc->setPriorityCall(true);
00288      QCString replyType;
00289      if (!dc->call(appName, KCmdLineArgs::about->appName(), "newInstance()", data, replyType, reply))
00290      {
00291         kdError() << "Communication problem with " << KCmdLineArgs::about->appName() << ", it probably crashed." << endl;
00292         delete dc;  // Clean up DCOP commmunication
00293         ::exit(255);
00294      }
00295      dc->setPriorityCall(false);
00296      if (replyType != "int")
00297      {
00298         kdError() << "KUniqueApplication: DCOP communication error!" << endl;
00299         delete dc;  // Clean up DCOP commmunication
00300         ::exit(255);
00301      }
00302      QDataStream rs(reply, IO_ReadOnly);
00303      int exitCode;
00304      rs >> exitCode;
00305      delete dc; // Clean up DCOP commmunication
00306      ::exit(exitCode);
00307      break;
00308   }
00309   return false; // make insure++ happy
00310 }
00311 
00312 
00313 KUniqueApplication::KUniqueApplication(bool allowStyles, bool GUIenabled, bool configUnique)
00314   : KApplication( allowStyles, GUIenabled, initHack( configUnique )),
00315     DCOPObject(KCmdLineArgs::about->appName())
00316 {
00317   d = new KUniqueApplicationPrivate;
00318   d->processingRequest = false;
00319   d->firstInstance = true;
00320 
00321   if (s_nofork)
00322     // Can't call newInstance directly from the constructor since it's virtual...
00323     QTimer::singleShot( 0, this, SLOT(newInstanceNoFork()) );
00324 }
00325 
00326 
00327 #ifdef Q_WS_X11
00328 KUniqueApplication::KUniqueApplication(Display *display, Qt::HANDLE visual,
00329         Qt::HANDLE colormap, bool allowStyles, bool configUnique)
00330   : KApplication( display, visual, colormap, allowStyles, initHack( configUnique )),
00331     DCOPObject(KCmdLineArgs::about->appName())
00332 {
00333   d = new KUniqueApplicationPrivate;
00334   d->processingRequest = false;
00335   d->firstInstance = true;
00336 
00337   if (s_nofork)
00338     // Can't call newInstance directly from the constructor since it's virtual...
00339     QTimer::singleShot( 0, this, SLOT(newInstanceNoFork()) );
00340 }
00341 #endif
00342 
00343 
00344 KUniqueApplication::~KUniqueApplication()
00345 {
00346   delete d;
00347 }
00348 
00349 // this gets called before even entering QApplication::QApplication()
00350 KInstance* KUniqueApplication::initHack( bool configUnique )
00351 {
00352   KInstance* inst = new KInstance( KCmdLineArgs::about );
00353   if (configUnique)
00354   {
00355     KConfigGroupSaver saver( inst->config(), "KDE" );
00356     s_multipleInstances = inst->config()->readBoolEntry("MultipleInstances", false);
00357   }
00358   if( !start())
00359          // Already running
00360       ::exit( 0 );
00361   return inst;
00362 }
00363 
00364 void KUniqueApplication::newInstanceNoFork()
00365 {
00366   if (dcopClient()->isSuspended())
00367   {
00368     // Try again later.
00369     QTimer::singleShot( 200, this, SLOT(newInstanceNoFork()) );
00370     return;
00371   }
00372   
00373   s_handleAutoStarted = false;
00374   newInstance();
00375   d->firstInstance = false;
00376   // KDE4 remove
00377   // A hack to make startup notification stop for apps which override newInstance()
00378   // and reuse an already existing window there, but use KWin::activateWindow()
00379   // instead of KStartupInfo::setNewStartupId(). Therefore KWin::activateWindow()
00380   // for now sets this flag. Automatically ending startup notification always
00381   // would cause problem if the new window would show up with a small delay.
00382   if( s_handleAutoStarted )
00383       KStartupInfo::handleAutoAppStartedSending();
00384   // What to do with the return value ?
00385 }
00386 
00387 bool KUniqueApplication::process(const QCString &fun, const QByteArray &data,
00388                  QCString &replyType, QByteArray &replyData)
00389 {
00390   if (fun == "newInstance()")
00391   {
00392     delayRequest(fun, data);
00393     return true;
00394   } else
00395     return DCOPObject::process(fun, data, replyType, replyData);
00396 }
00397 
00398 void
00399 KUniqueApplication::delayRequest(const QCString &fun, const QByteArray &data)
00400 {
00401   DCOPRequest *request = new DCOPRequest;
00402   request->fun = fun;
00403   request->data = data;
00404   request->transaction = dcopClient()->beginTransaction();
00405   d->requestList.append(request);
00406   if (!d->processingRequest)
00407   {
00408      QTimer::singleShot(0, this, SLOT(processDelayed()));
00409   }
00410 }
00411 
00412 void
00413 KUniqueApplication::processDelayed()
00414 {
00415   if (dcopClient()->isSuspended())
00416   {
00417     // Try again later.
00418     QTimer::singleShot( 200, this, SLOT(processDelayed()));
00419     return;
00420   }
00421   d->processingRequest = true;
00422   while( !d->requestList.isEmpty() )
00423   {
00424      DCOPRequest *request = d->requestList.take(0);
00425      QByteArray replyData;
00426      QCString replyType;
00427      if (request->fun == "newInstance()") {
00428        dcopClient()->setPriorityCall(false);
00429        QDataStream ds(request->data, IO_ReadOnly);
00430        KCmdLineArgs::loadAppArgs(ds);
00431        if( !ds.atEnd()) // backwards compatibility
00432        {
00433            QCString asn_id;
00434            ds >> asn_id;
00435            setStartupId( asn_id );
00436        }
00437        s_handleAutoStarted = false;
00438        int exitCode = newInstance();
00439        d->firstInstance = false;
00440        if( s_handleAutoStarted )
00441            KStartupInfo::handleAutoAppStartedSending(); // KDE4 remove?
00442        QDataStream rs(replyData, IO_WriteOnly);
00443        rs << exitCode;
00444        replyType = "int";
00445      }
00446      dcopClient()->endTransaction( request->transaction, replyType, replyData);
00447      delete request;
00448   }
00449 
00450   d->processingRequest = false;
00451 }
00452 
00453 bool KUniqueApplication::restoringSession()
00454 {
00455   return d->firstInstance && isRestored();
00456 }
00457 
00458 int KUniqueApplication::newInstance()
00459 {
00460   if (!d->firstInstance)
00461   {
00462     
00463 //#ifndef Q_WS_QWS // FIXME(E): Implement for Qt/Embedded
00464     if ( mainWidget() )
00465     {
00466       mainWidget()->show();
00467 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00468     // This is the line that handles window activation if necessary,
00469     // and what's important, it does it properly. If you reimplement newInstance(),
00470     // and don't call the inherited one, use this.
00471       KStartupInfo::setNewStartupId( mainWidget(), kapp->startupId());
00472 #endif
00473     }
00474   }
00475   return 0; // do nothing in default implementation
00476 }
00477 
00478 void KUniqueApplication::setHandleAutoStarted()
00479 {
00480     s_handleAutoStarted = false;
00481 }
00482 
00483 void KUniqueApplication::virtual_hook( int id, void* data )
00484 { KApplication::virtual_hook( id, data );
00485   DCOPObject::virtual_hook( id, data ); }
00486 
00487 #include "kuniqueapplication.moc"
KDE Logo
This file is part of the documentation for kdecore Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Nov 27 13:41:24 2004 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003