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.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Oct 8 11:14:03 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003