kuniqueapplication.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (c) 1999 Preston Brown <pbrown@kde.org> 00003 00004 $Id$ 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., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, 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 <tqfile.h> 00033 #include <tqptrlist.h> 00034 #include <tqtimer.h> 00035 00036 #include <dcopclient.h> 00037 #include <tdecmdlineargs.h> 00038 #include <kstandarddirs.h> 00039 #include <tdeaboutdata.h> 00040 00041 #if defined Q_WS_X11 00042 #include <twin.h> 00043 #include <tdestartupinfo.h> 00044 #endif 00045 00046 #include <tdeconfig.h> 00047 #include "kdebug.h" 00048 #include "kuniqueapplication.h" 00049 00050 #if defined Q_WS_X11 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 TDECmdLineOptions kunique_options[] = 00068 { 00069 { "nofork", "Don't run in the background.", 0 }, 00070 TDECmdLineLastOption 00071 }; 00072 00073 struct DCOPRequest { 00074 TQCString fun; 00075 TQByteArray data; 00076 DCOPClientTransaction *transaction; 00077 }; 00078 00079 class KUniqueApplicationPrivate { 00080 public: 00081 TQPtrList <DCOPRequest> requestList; 00082 bool processingRequest; 00083 bool firstInstance; 00084 }; 00085 00086 void 00087 KUniqueApplication::addCmdLineOptions() 00088 { 00089 TDECmdLineArgs::addCmdLineOptions(kunique_options, 0, "kuniqueapp", "tde" ); 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 #ifdef Q_WS_WIN 00100 s_nofork = true; 00101 #else 00102 TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs("kuniqueapp"); 00103 s_nofork = !args->isSet("fork"); 00104 delete args; 00105 #endif 00106 00107 TQCString appName = TDECmdLineArgs::about->appName(); 00108 00109 if (s_nofork) 00110 { 00111 if (s_multipleInstances) 00112 { 00113 TQCString pid; 00114 pid.setNum(getpid()); 00115 appName = appName + "-" + pid; 00116 } 00117 00118 // Check to make sure that we're actually able to register with the DCOP 00119 // server. 00120 00121 #ifndef Q_WS_WIN //TODO 00122 if(dcopClient()->registerAs(appName, false).isEmpty()) { 00123 startKdeinit(); 00124 if(dcopClient()->registerAs(appName, false).isEmpty()) { 00125 kdError() << "KUniqueApplication: Can't setup DCOP communication." << endl; 00126 ::exit(255); 00127 } 00128 } 00129 #endif 00130 00131 // We'll call newInstance in the constructor. Do nothing here. 00132 return true; 00133 } 00134 DCOPClient *dc; 00135 int fd[2]; 00136 signed char result; 00137 if (0 > pipe(fd)) 00138 { 00139 kdError() << "KUniqueApplication: pipe() failed!" << endl; 00140 ::exit(255); 00141 } 00142 int fork_result = fork(); 00143 switch(fork_result) { 00144 case -1: 00145 kdError() << "KUniqueApplication: fork() failed!" << endl; 00146 ::exit(255); 00147 break; 00148 case 0: 00149 // Child 00150 ::close(fd[0]); 00151 if (s_multipleInstances) 00152 appName.append("-").append(TQCString().setNum(getpid())); 00153 dc = dcopClient(); 00154 { 00155 TQCString regName = dc->registerAs(appName, false); 00156 if (regName.isEmpty()) 00157 { 00158 // Check DISPLAY 00159 if (TQCString(getenv(DISPLAY)).isEmpty()) 00160 { 00161 kdError() << "KUniqueApplication: Can't determine DISPLAY. Aborting." << endl; 00162 result = -1; // Error 00163 ::write(fd[1], &result, 1); 00164 ::exit(255); 00165 } 00166 00167 // Try to launch tdeinit. 00168 startKdeinit(); 00169 regName = dc->registerAs(appName, false); 00170 if (regName.isEmpty()) 00171 { 00172 kdError() << "KUniqueApplication: Can't setup DCOP communication." << endl; 00173 result = -1; 00174 delete dc; // Clean up DCOP commmunication 00175 ::write(fd[1], &result, 1); 00176 ::exit(255); 00177 } 00178 } 00179 if (regName != appName) 00180 { 00181 // Already running. Ok. 00182 result = 0; 00183 delete dc; // Clean up DCOP commmunication 00184 ::write(fd[1], &result, 1); 00185 ::close(fd[1]); 00186 #if 0 00187 #ifdef Q_WS_X11 00188 // say we're up and running ( probably no new window will appear ) 00189 TDEStartupInfoId id; 00190 if( kapp != NULL ) // TDEApplication constructor unsets the env. variable 00191 id.initId( kapp->startupId()); 00192 else 00193 id = TDEStartupInfo::currentStartupIdEnv(); 00194 if( !id.none()) 00195 { 00196 Display* disp = XOpenDisplay( NULL ); 00197 if( disp != NULL ) // use extra X connection 00198 { 00199 TDEStartupInfo::sendFinishX( disp, id ); 00200 XCloseDisplay( disp ); 00201 } 00202 } 00203 #else //FIXME(E): implement 00204 #endif 00205 #endif 00206 return false; 00207 } 00208 dc->setPriorityCall(true); 00209 } 00210 00211 { 00212 #ifdef Q_WS_X11 00213 TDEStartupInfoId id; 00214 if( kapp != NULL ) // TDEApplication constructor unsets the env. variable 00215 id.initId( kapp->startupId()); 00216 else 00217 id = TDEStartupInfo::currentStartupIdEnv(); 00218 if( !id.none()) 00219 { // notice about pid change 00220 Display* disp = XOpenDisplay( NULL ); 00221 if( disp != NULL ) // use extra X connection 00222 { 00223 TDEStartupInfoData data; 00224 data.addPid( getpid()); 00225 TDEStartupInfo::sendChangeX( disp, id, data ); 00226 XCloseDisplay( disp ); 00227 } 00228 } 00229 #else //FIXME(E): Implement 00230 #endif 00231 } 00232 result = 0; 00233 ::write(fd[1], &result, 1); 00234 ::close(fd[1]); 00235 return true; // Finished. 00236 default: 00237 // Parent 00238 // DCOPClient::emergencyClose(); 00239 // dcopClient()->detach(); 00240 if (s_multipleInstances) 00241 appName.append("-").append(TQCString().setNum(fork_result)); 00242 ::close(fd[1]); 00243 for(;;) 00244 { 00245 int n = ::read(fd[0], &result, 1); 00246 if (n == 1) break; 00247 if (n == 0) 00248 { 00249 kdError() << "KUniqueApplication: Pipe closed unexpectedly." << endl; 00250 ::exit(255); 00251 } 00252 if (errno != EINTR) 00253 { 00254 kdError() << "KUniqueApplication: Error reading from pipe." << endl; 00255 ::exit(255); 00256 } 00257 } 00258 ::close(fd[0]); 00259 00260 if (result != 0) 00261 ::exit(result); // Error occurred in child. 00262 00263 dc = new DCOPClient(); 00264 if (!dc->attach()) 00265 { 00266 kdError() << "KUniqueApplication: Parent process can't attach to DCOP." << endl; 00267 delete dc; // Clean up DCOP commmunication 00268 ::exit(255); 00269 } 00270 if (!dc->isApplicationRegistered(appName)) { 00271 kdError() << "KUniqueApplication: Registering failed!" << endl; 00272 } 00273 00274 TQCString new_asn_id; 00275 #if defined Q_WS_X11 00276 TDEStartupInfoId id; 00277 if( kapp != NULL ) // TDEApplication constructor unsets the env. variable 00278 id.initId( kapp->startupId()); 00279 else 00280 id = TDEStartupInfo::currentStartupIdEnv(); 00281 if( !id.none()) 00282 new_asn_id = id.id(); 00283 #endif 00284 00285 TQByteArray data, reply; 00286 TQDataStream ds(data, IO_WriteOnly); 00287 00288 TDECmdLineArgs::saveAppArgs(ds); 00289 ds << new_asn_id; 00290 00291 dc->setPriorityCall(true); 00292 TQCString replyType; 00293 if (!dc->call(appName, TDECmdLineArgs::about->appName(), "newInstance()", data, replyType, reply)) 00294 { 00295 kdError() << "Communication problem with " << TDECmdLineArgs::about->appName() << ", it probably crashed." << endl; 00296 delete dc; // Clean up DCOP commmunication 00297 ::exit(255); 00298 } 00299 dc->setPriorityCall(false); 00300 if (replyType != "int") 00301 { 00302 kdError() << "KUniqueApplication: DCOP communication error!" << endl; 00303 delete dc; // Clean up DCOP commmunication 00304 ::exit(255); 00305 } 00306 TQDataStream rs(reply, IO_ReadOnly); 00307 int exitCode; 00308 rs >> exitCode; 00309 delete dc; // Clean up DCOP commmunication 00310 ::exit(exitCode); 00311 break; 00312 } 00313 return false; // make insure++ happy 00314 } 00315 00316 00317 KUniqueApplication::KUniqueApplication(bool allowStyles, bool GUIenabled, bool configUnique) 00318 : TDEApplication( allowStyles, GUIenabled, initHack( configUnique )), 00319 DCOPObject(TDECmdLineArgs::about->appName()) 00320 { 00321 d = new KUniqueApplicationPrivate; 00322 d->processingRequest = false; 00323 d->firstInstance = true; 00324 00325 if (s_nofork) { 00326 // Can't call newInstance directly from the constructor since it's virtual... 00327 TQTimer::singleShot( 0, this, TQT_SLOT(newInstanceNoFork()) ); 00328 } 00329 else { 00330 // Force to handle DCOP requests (newInstance call) 00331 TQTimer::singleShot( 0, this, TQT_SLOT(processDelayed())); 00332 } 00333 } 00334 00335 00336 #ifdef Q_WS_X11 00337 KUniqueApplication::KUniqueApplication(Display *display, Qt::HANDLE visual, 00338 Qt::HANDLE colormap, bool allowStyles, bool configUnique) 00339 : TDEApplication( display, visual, colormap, allowStyles, initHack( configUnique )), 00340 DCOPObject(TDECmdLineArgs::about->appName()) 00341 { 00342 d = new KUniqueApplicationPrivate; 00343 d->processingRequest = false; 00344 d->firstInstance = true; 00345 00346 if (s_nofork) { 00347 // Can't call newInstance directly from the constructor since it's virtual... 00348 TQTimer::singleShot( 0, this, TQT_SLOT(newInstanceNoFork()) ); 00349 } 00350 else { 00351 // Force to handle DCOP requests (newInstance call) 00352 TQTimer::singleShot( 0, this, TQT_SLOT(processDelayed())); 00353 } 00354 } 00355 #endif 00356 00357 00358 KUniqueApplication::~KUniqueApplication() 00359 { 00360 delete d; 00361 } 00362 00363 // this gets called before even entering TQApplication::TQApplication() 00364 TDEInstance* KUniqueApplication::initHack( bool configUnique ) 00365 { 00366 TDEInstance* inst = new TDEInstance( TDECmdLineArgs::about ); 00367 if (configUnique) 00368 { 00369 TDEConfigGroupSaver saver( inst->config(), "KDE" ); 00370 s_multipleInstances = inst->config()->readBoolEntry("MultipleInstances", false); 00371 } 00372 if( !start()) 00373 // Already running 00374 ::exit( 0 ); 00375 return inst; 00376 } 00377 00378 void KUniqueApplication::newInstanceNoFork() 00379 { 00380 if (dcopClient()->isSuspended()) 00381 { 00382 // Try again later. 00383 TQTimer::singleShot( 200, this, TQT_SLOT(newInstanceNoFork()) ); 00384 return; 00385 } 00386 00387 s_handleAutoStarted = false; 00388 newInstance(); 00389 d->firstInstance = false; 00390 #if defined Q_WS_X11 00391 // KDE4 remove 00392 // A hack to make startup notification stop for apps which override newInstance() 00393 // and reuse an already existing window there, but use KWin::activateWindow() 00394 // instead of TDEStartupInfo::setNewStartupId(). Therefore KWin::activateWindow() 00395 // for now sets this flag. Automatically ending startup notification always 00396 // would cause problem if the new window would show up with a small delay. 00397 if( s_handleAutoStarted ) 00398 TDEStartupInfo::handleAutoAppStartedSending(); 00399 #endif 00400 // What to do with the return value ? 00401 } 00402 00403 bool KUniqueApplication::process(const TQCString &fun, const TQByteArray &data, 00404 TQCString &replyType, TQByteArray &replyData) 00405 { 00406 if (fun == "newInstance()") 00407 { 00408 delayRequest(fun, data); 00409 return true; 00410 } else 00411 return DCOPObject::process(fun, data, replyType, replyData); 00412 } 00413 00414 void 00415 KUniqueApplication::delayRequest(const TQCString &fun, const TQByteArray &data) 00416 { 00417 DCOPRequest *request = new DCOPRequest; 00418 request->fun = fun; 00419 request->data = data; 00420 request->transaction = dcopClient()->beginTransaction(); 00421 d->requestList.append(request); 00422 if (!d->processingRequest) 00423 { 00424 TQTimer::singleShot(0, this, TQT_SLOT(processDelayed())); 00425 } 00426 } 00427 00428 void 00429 KUniqueApplication::processDelayed() 00430 { 00431 if (dcopClient()->isSuspended()) 00432 { 00433 // Try again later. 00434 TQTimer::singleShot( 200, this, TQT_SLOT(processDelayed())); 00435 return; 00436 } 00437 d->processingRequest = true; 00438 while( !d->requestList.isEmpty() ) 00439 { 00440 DCOPRequest *request = d->requestList.take(0); 00441 TQByteArray replyData; 00442 TQCString replyType; 00443 if (request->fun == "newInstance()") { 00444 dcopClient()->setPriorityCall(false); 00445 TQDataStream ds(request->data, IO_ReadOnly); 00446 TDECmdLineArgs::loadAppArgs(ds); 00447 if( !ds.atEnd()) // backwards compatibility 00448 { 00449 TQCString asn_id; 00450 ds >> asn_id; 00451 setStartupId( asn_id ); 00452 } 00453 s_handleAutoStarted = false; 00454 int exitCode = newInstance(); 00455 d->firstInstance = false; 00456 #if defined Q_WS_X11 00457 if( s_handleAutoStarted ) 00458 TDEStartupInfo::handleAutoAppStartedSending(); // KDE4 remove? 00459 #endif 00460 TQDataStream rs(replyData, IO_WriteOnly); 00461 rs << exitCode; 00462 replyType = "int"; 00463 } 00464 dcopClient()->endTransaction( request->transaction, replyType, replyData); 00465 delete request; 00466 } 00467 00468 d->processingRequest = false; 00469 } 00470 00471 bool KUniqueApplication::restoringSession() 00472 { 00473 return d->firstInstance && isRestored(); 00474 } 00475 00476 int KUniqueApplication::newInstance() 00477 { 00478 if (!d->firstInstance) 00479 { 00480 00481 if ( mainWidget() ) 00482 { 00483 mainWidget()->show(); 00484 #if defined Q_WS_X11 00485 // This is the line that handles window activation if necessary, 00486 // and what's important, it does it properly. If you reimplement newInstance(), 00487 // and don't call the inherited one, use this (but NOT when newInstance() 00488 // is called for the first time, like here). 00489 TDEStartupInfo::setNewStartupId( mainWidget(), kapp->startupId()); 00490 #endif 00491 } 00492 } 00493 return 0; // do nothing in default implementation 00494 } 00495 00496 void KUniqueApplication::setHandleAutoStarted() 00497 { 00498 s_handleAutoStarted = false; 00499 } 00500 00501 void KUniqueApplication::virtual_hook( int id, void* data ) 00502 { TDEApplication::virtual_hook( id, data ); 00503 DCOPObject::virtual_hook( id, data ); } 00504 00505 #include "kuniqueapplication.moc"