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 <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
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
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();
00099 #ifdef Q_WS_WIN
00100 s_nofork = true;
00101 #else
00102 KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kuniqueapp");
00103 s_nofork = !args->isSet("fork");
00104 delete args;
00105 #endif
00106
00107 QCString appName = KCmdLineArgs::about->appName();
00108
00109 if (s_nofork)
00110 {
00111 if (s_multipleInstances)
00112 {
00113 QCString 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(QCString().setNum(getpid()));
00153 dc = dcopClient();
00154 {
00155 QCString regName = dc->registerAs(appName, false);
00156 if (regName.isEmpty())
00157 {
00158
00159 if (QCString(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 KStartupInfoId id;
00190 if( kapp != NULL )
00191 id.initId( kapp->startupId());
00192 else
00193 id = KStartupInfo::currentStartupIdEnv();
00194 if( !id.none())
00195 {
00196 Display* disp = XOpenDisplay( NULL );
00197 if( disp != NULL )
00198 {
00199 KStartupInfo::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 KStartupInfoId id;
00214 if( kapp != NULL )
00215 id.initId( kapp->startupId());
00216 else
00217 id = KStartupInfo::currentStartupIdEnv();
00218 if( !id.none())
00219 {
00220 Display* disp = XOpenDisplay( NULL );
00221 if( disp != NULL )
00222 {
00223 KStartupInfoData data;
00224 data.addPid( getpid());
00225 KStartupInfo::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(QCString().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 QCString new_asn_id;
00275 #if defined Q_WS_X11
00276 KStartupInfoId id;
00277 if( kapp != NULL )
00278 id.initId( kapp->startupId());
00279 else
00280 id = KStartupInfo::currentStartupIdEnv();
00281 if( !id.none())
00282 new_asn_id = id.id();
00283 #endif
00284
00285 QByteArray data, reply;
00286 QDataStream ds(data, IO_WriteOnly);
00287
00288 KCmdLineArgs::saveAppArgs(ds);
00289 ds << new_asn_id;
00290
00291 dc->setPriorityCall(true);
00292 QCString replyType;
00293 if (!dc->call(appName, KCmdLineArgs::about->appName(), "newInstance()", data, replyType, reply))
00294 {
00295 kdError() << "Communication problem with " << KCmdLineArgs::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 QDataStream 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 : KApplication( allowStyles, GUIenabled, initHack( configUnique )),
00319 DCOPObject(KCmdLineArgs::about->appName())
00320 {
00321 d = new KUniqueApplicationPrivate;
00322 d->processingRequest = false;
00323 d->firstInstance = true;
00324
00325 if (s_nofork)
00326
00327 QTimer::singleShot( 0, this, SLOT(newInstanceNoFork()) );
00328 }
00329
00330
00331 #ifdef Q_WS_X11
00332 KUniqueApplication::KUniqueApplication(Display *display, Qt::HANDLE visual,
00333 Qt::HANDLE colormap, bool allowStyles, bool configUnique)
00334 : KApplication( display, visual, colormap, allowStyles, initHack( configUnique )),
00335 DCOPObject(KCmdLineArgs::about->appName())
00336 {
00337 d = new KUniqueApplicationPrivate;
00338 d->processingRequest = false;
00339 d->firstInstance = true;
00340
00341 if (s_nofork)
00342
00343 QTimer::singleShot( 0, this, SLOT(newInstanceNoFork()) );
00344 }
00345 #endif
00346
00347
00348 KUniqueApplication::~KUniqueApplication()
00349 {
00350 delete d;
00351 }
00352
00353
00354 KInstance* KUniqueApplication::initHack( bool configUnique )
00355 {
00356 KInstance* inst = new KInstance( KCmdLineArgs::about );
00357 if (configUnique)
00358 {
00359 KConfigGroupSaver saver( inst->config(), "KDE" );
00360 s_multipleInstances = inst->config()->readBoolEntry("MultipleInstances", false);
00361 }
00362 if( !start())
00363
00364 ::exit( 0 );
00365 return inst;
00366 }
00367
00368 void KUniqueApplication::newInstanceNoFork()
00369 {
00370 if (dcopClient()->isSuspended())
00371 {
00372
00373 QTimer::singleShot( 200, this, SLOT(newInstanceNoFork()) );
00374 return;
00375 }
00376
00377 s_handleAutoStarted = false;
00378 newInstance();
00379 d->firstInstance = false;
00380 #if defined Q_WS_X11
00381
00382
00383
00384
00385
00386
00387 if( s_handleAutoStarted )
00388 KStartupInfo::handleAutoAppStartedSending();
00389 #endif
00390
00391 }
00392
00393 bool KUniqueApplication::process(const QCString &fun, const QByteArray &data,
00394 QCString &replyType, QByteArray &replyData)
00395 {
00396 if (fun == "newInstance()")
00397 {
00398 delayRequest(fun, data);
00399 return true;
00400 } else
00401 return DCOPObject::process(fun, data, replyType, replyData);
00402 }
00403
00404 void
00405 KUniqueApplication::delayRequest(const QCString &fun, const QByteArray &data)
00406 {
00407 DCOPRequest *request = new DCOPRequest;
00408 request->fun = fun;
00409 request->data = data;
00410 request->transaction = dcopClient()->beginTransaction();
00411 d->requestList.append(request);
00412 if (!d->processingRequest)
00413 {
00414 QTimer::singleShot(0, this, SLOT(processDelayed()));
00415 }
00416 }
00417
00418 void
00419 KUniqueApplication::processDelayed()
00420 {
00421 if (dcopClient()->isSuspended())
00422 {
00423
00424 QTimer::singleShot( 200, this, SLOT(processDelayed()));
00425 return;
00426 }
00427 d->processingRequest = true;
00428 while( !d->requestList.isEmpty() )
00429 {
00430 DCOPRequest *request = d->requestList.take(0);
00431 QByteArray replyData;
00432 QCString replyType;
00433 if (request->fun == "newInstance()") {
00434 dcopClient()->setPriorityCall(false);
00435 QDataStream ds(request->data, IO_ReadOnly);
00436 KCmdLineArgs::loadAppArgs(ds);
00437 if( !ds.atEnd())
00438 {
00439 QCString asn_id;
00440 ds >> asn_id;
00441 setStartupId( asn_id );
00442 }
00443 s_handleAutoStarted = false;
00444 int exitCode = newInstance();
00445 d->firstInstance = false;
00446 #if defined Q_WS_X11
00447 if( s_handleAutoStarted )
00448 KStartupInfo::handleAutoAppStartedSending();
00449 #endif
00450 QDataStream rs(replyData, IO_WriteOnly);
00451 rs << exitCode;
00452 replyType = "int";
00453 }
00454 dcopClient()->endTransaction( request->transaction, replyType, replyData);
00455 delete request;
00456 }
00457
00458 d->processingRequest = false;
00459 }
00460
00461 bool KUniqueApplication::restoringSession()
00462 {
00463 return d->firstInstance && isRestored();
00464 }
00465
00466 int KUniqueApplication::newInstance()
00467 {
00468 if (!d->firstInstance)
00469 {
00470
00471 if ( mainWidget() )
00472 {
00473 mainWidget()->show();
00474 #if defined Q_WS_X11
00475
00476
00477
00478
00479 KStartupInfo::setNewStartupId( mainWidget(), kapp->startupId());
00480 #endif
00481 }
00482 }
00483 return 0;
00484 }
00485
00486 void KUniqueApplication::setHandleAutoStarted()
00487 {
00488 s_handleAutoStarted = false;
00489 }
00490
00491 void KUniqueApplication::virtual_hook( int id, void* data )
00492 { KApplication::virtual_hook( id, data );
00493 DCOPObject::virtual_hook( id, data ); }
00494
00495 #include "kuniqueapplication.moc"