00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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();
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
00119
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
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
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
00159 if (TQCString(getenv(DISPLAY)).isEmpty())
00160 {
00161 kdError() << "KUniqueApplication: Can't determine DISPLAY. Aborting." << endl;
00162 result = -1;
00163 ::write(fd[1], &result, 1);
00164 ::exit(255);
00165 }
00166
00167
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;
00175 ::write(fd[1], &result, 1);
00176 ::exit(255);
00177 }
00178 }
00179 if (regName != appName)
00180 {
00181
00182 result = 0;
00183 delete dc;
00184 ::write(fd[1], &result, 1);
00185 ::close(fd[1]);
00186 #if 0
00187 #ifdef Q_WS_X11
00188
00189 TDEStartupInfoId id;
00190 if( kapp != NULL )
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 )
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 )
00215 id.initId( kapp->startupId());
00216 else
00217 id = TDEStartupInfo::currentStartupIdEnv();
00218 if( !id.none())
00219 {
00220 Display* disp = XOpenDisplay( NULL );
00221 if( disp != NULL )
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;
00236 default:
00237
00238
00239
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);
00262
00263 dc = new DCOPClient();
00264 if (!dc->attach())
00265 {
00266 kdError() << "KUniqueApplication: Parent process can't attach to DCOP." << endl;
00267 delete dc;
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 )
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;
00297 ::exit(255);
00298 }
00299 dc->setPriorityCall(false);
00300 if (replyType != "int")
00301 {
00302 kdError() << "KUniqueApplication: DCOP communication error!" << endl;
00303 delete dc;
00304 ::exit(255);
00305 }
00306 TQDataStream rs(reply, IO_ReadOnly);
00307 int exitCode;
00308 rs >> exitCode;
00309 delete dc;
00310 ::exit(exitCode);
00311 break;
00312 }
00313 return false;
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
00327 TQTimer::singleShot( 0, this, TQT_SLOT(newInstanceNoFork()) );
00328 }
00329 else {
00330
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
00348 TQTimer::singleShot( 0, this, TQT_SLOT(newInstanceNoFork()) );
00349 }
00350 else {
00351
00352 TQTimer::singleShot( 0, this, TQT_SLOT(processDelayed()));
00353 }
00354 }
00355 #endif
00356
00357
00358 KUniqueApplication::~KUniqueApplication()
00359 {
00360 delete d;
00361 }
00362
00363
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
00374 ::exit( 0 );
00375 return inst;
00376 }
00377
00378 void KUniqueApplication::newInstanceNoFork()
00379 {
00380 if (dcopClient()->isSuspended())
00381 {
00382
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
00392
00393
00394
00395
00396
00397 if( s_handleAutoStarted )
00398 TDEStartupInfo::handleAutoAppStartedSending();
00399 #endif
00400
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
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())
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();
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
00486
00487
00488
00489 TDEStartupInfo::setNewStartupId( mainWidget(), kapp->startupId());
00490 #endif
00491 }
00492 }
00493 return 0;
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"