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 && ! 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();
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
00115
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
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
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
00153 if (QCString(getenv(DISPLAY)).isEmpty())
00154 {
00155 kdError() << "KUniqueApplication: Can't determine DISPLAY. Aborting." << endl;
00156 result = -1;
00157 ::write(fd[1], &result, 1);
00158 ::exit(255);
00159 }
00160
00161
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;
00169 ::write(fd[1], &result, 1);
00170 ::exit(255);
00171 }
00172 }
00173 if (regName != appName)
00174 {
00175
00176 result = 0;
00177 delete dc;
00178 ::write(fd[1], &result, 1);
00179 ::close(fd[1]);
00180 #if 0
00181 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00182
00183
00184 KStartupInfoId id;
00185 if( kapp != NULL )
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 )
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
00209 KStartupInfoId id;
00210 if( kapp != NULL )
00211 id.initId( kapp->startupId());
00212 else
00213 id = KStartupInfo::currentStartupIdEnv();
00214 if( !id.none())
00215 {
00216 Display* disp = XOpenDisplay( NULL );
00217 if( disp != NULL )
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;
00232 default:
00233
00234
00235
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);
00258
00259 dc = new DCOPClient();
00260 if (!dc->attach())
00261 {
00262 kdError() << "KUniqueApplication: Parent process can't attach to DCOP." << endl;
00263 delete dc;
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 )
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;
00293 ::exit(255);
00294 }
00295 dc->setPriorityCall(false);
00296 if (replyType != "int")
00297 {
00298 kdError() << "KUniqueApplication: DCOP communication error!" << endl;
00299 delete dc;
00300 ::exit(255);
00301 }
00302 QDataStream rs(reply, IO_ReadOnly);
00303 int exitCode;
00304 rs >> exitCode;
00305 delete dc;
00306 ::exit(exitCode);
00307 break;
00308 }
00309 return false;
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
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
00339 QTimer::singleShot( 0, this, SLOT(newInstanceNoFork()) );
00340 }
00341 #endif
00342
00343
00344 KUniqueApplication::~KUniqueApplication()
00345 {
00346 delete d;
00347 }
00348
00349
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
00360 ::exit( 0 );
00361 return inst;
00362 }
00363
00364 void KUniqueApplication::newInstanceNoFork()
00365 {
00366 if (dcopClient()->isSuspended())
00367 {
00368
00369 QTimer::singleShot( 200, this, SLOT(newInstanceNoFork()) );
00370 return;
00371 }
00372
00373 s_handleAutoStarted = false;
00374 newInstance();
00375 d->firstInstance = false;
00376
00377
00378
00379
00380
00381
00382 if( s_handleAutoStarted )
00383 KStartupInfo::handleAutoAppStartedSending();
00384
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
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())
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();
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
00464 if ( mainWidget() )
00465 {
00466 mainWidget()->show();
00467 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00468
00469
00470
00471 KStartupInfo::setNewStartupId( mainWidget(), kapp->startupId());
00472 #endif
00473 }
00474 }
00475 return 0;
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"