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 Wed Sep 29 09:43:11 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003