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
00066 static KCmdLineOptions kunique_options[] =
00067 {
00068 { "nofork", "Don't run in the background.", 0 },
00069 KCmdLineLastOption
00070 };
00071
00072 struct DCOPRequest {
00073 QCString fun;
00074 QByteArray data;
00075 DCOPClientTransaction *transaction;
00076 };
00077
00078 class KUniqueApplicationPrivate {
00079 public:
00080 QPtrList <DCOPRequest> requestList;
00081 bool processingRequest;
00082 bool firstInstance;
00083 };
00084
00085 void
00086 KUniqueApplication::addCmdLineOptions()
00087 {
00088 KCmdLineArgs::addCmdLineOptions(kunique_options, 0, "kuniqueapp", "kde" );
00089 }
00090
00091 bool
00092 KUniqueApplication::start()
00093 {
00094 if( s_uniqueTestDone )
00095 return true;
00096 s_uniqueTestDone = true;
00097 addCmdLineOptions();
00098 KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kuniqueapp");
00099 s_nofork = !args->isSet("fork");
00100 delete args;
00101
00102 QCString appName = KCmdLineArgs::about->appName();
00103
00104 if (s_nofork)
00105 {
00106 if (s_multipleInstances)
00107 {
00108 QCString pid;
00109 pid.setNum(getpid());
00110 appName = appName + "-" + pid;
00111 }
00112 dcopClient()->registerAs(appName, false );
00113
00114 return true;
00115 }
00116 DCOPClient *dc;
00117 int fd[2];
00118 signed char result;
00119 if (0 > pipe(fd))
00120 {
00121 kdError() << "KUniqueApplication: pipe() failed!" << endl;
00122 ::exit(255);
00123 }
00124 int fork_result = fork();
00125 switch(fork_result) {
00126 case -1:
00127 kdError() << "KUniqueApplication: fork() failed!" << endl;
00128 ::exit(255);
00129 break;
00130 case 0:
00131
00132 ::close(fd[0]);
00133 if (s_multipleInstances)
00134 appName.append("-").append(QCString().setNum(getpid()));
00135 dc = dcopClient();
00136 {
00137 QCString regName = dc->registerAs(appName, false);
00138 if (regName.isEmpty())
00139 {
00140
00141 if (QCString(getenv(DISPLAY)).isEmpty())
00142 {
00143 kdError() << "KUniqueApplication: Can't determine DISPLAY. Aborting." << endl;
00144 result = -1;
00145 ::write(fd[1], &result, 1);
00146 ::exit(255);
00147 }
00148
00149
00150 startKdeinit();
00151 regName = dc->registerAs(appName, false);
00152 if (regName.isEmpty())
00153 {
00154 kdError() << "KUniqueApplication: Can't setup DCOP communication." << endl;
00155 result = -1;
00156 delete dc;
00157 ::write(fd[1], &result, 1);
00158 ::exit(255);
00159 }
00160 }
00161 if (regName != appName)
00162 {
00163
00164 result = 0;
00165 delete dc;
00166 ::write(fd[1], &result, 1);
00167 ::close(fd[1]);
00168 #if 0
00169 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00170
00171
00172 KStartupInfoId id;
00173 if( kapp != NULL )
00174 id.initId( kapp->startupId());
00175 else
00176 id = KStartupInfo::currentStartupIdEnv();
00177 if( !id.none())
00178 {
00179 Display* disp = XOpenDisplay( NULL );
00180 if( disp != NULL )
00181 {
00182 KStartupInfo::sendFinishX( disp, id );
00183 XCloseDisplay( disp );
00184 }
00185 }
00186 #else //FIXME(E): implement
00187 #endif
00188 #endif
00189 return false;
00190 }
00191 dc->setPriorityCall(true);
00192 }
00193
00194 {
00195 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00196
00197 KStartupInfoId id;
00198 if( kapp != NULL )
00199 id.initId( kapp->startupId());
00200 else
00201 id = KStartupInfo::currentStartupIdEnv();
00202 if( !id.none())
00203 {
00204 Display* disp = XOpenDisplay( NULL );
00205 if( disp != NULL )
00206 {
00207 KStartupInfoData data;
00208 data.addPid( getpid());
00209 KStartupInfo::sendChangeX( disp, id, data );
00210 XCloseDisplay( disp );
00211 }
00212 }
00213 #else //FIXME(E): Implement
00214 #endif
00215 }
00216 result = 0;
00217 ::write(fd[1], &result, 1);
00218 ::close(fd[1]);
00219 return true;
00220 default:
00221
00222
00223
00224 if (s_multipleInstances)
00225 appName.append("-").append(QCString().setNum(fork_result));
00226 ::close(fd[1]);
00227 for(;;)
00228 {
00229 int n = ::read(fd[0], &result, 1);
00230 if (n == 1) break;
00231 if (n == 0)
00232 {
00233 kdError() << "KUniqueApplication: Pipe closed unexpectedly." << endl;
00234 ::exit(255);
00235 }
00236 if (errno != EINTR)
00237 {
00238 kdError() << "KUniqueApplication: Error reading from pipe." << endl;
00239 ::exit(255);
00240 }
00241 }
00242 ::close(fd[0]);
00243
00244 if (result != 0)
00245 ::exit(result);
00246
00247 dc = new DCOPClient();
00248 if (!dc->attach())
00249 {
00250 kdError() << "KUniqueApplication: Parent process can't attach to DCOP." << endl;
00251 delete dc;
00252 ::exit(255);
00253 }
00254 if (!dc->isApplicationRegistered(appName)) {
00255 kdError() << "KUniqueApplication: Registering failed!" << endl;
00256 }
00257
00258 QCString new_asn_id;
00259 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00260 KStartupInfoId id;
00261 if( kapp != NULL )
00262 id.initId( kapp->startupId());
00263 else
00264 id = KStartupInfo::currentStartupIdEnv();
00265 if( !id.none())
00266 new_asn_id = id.id();
00267 #endif
00268
00269 QByteArray data, reply;
00270 QDataStream ds(data, IO_WriteOnly);
00271
00272 KCmdLineArgs::saveAppArgs(ds);
00273 ds << new_asn_id;
00274
00275 dc->setPriorityCall(true);
00276 QCString replyType;
00277 if (!dc->call(appName, KCmdLineArgs::about->appName(), "newInstance()", data, replyType, reply))
00278 {
00279 kdError() << "Communication problem with " << KCmdLineArgs::about->appName() << ", it probably crashed." << endl;
00280 delete dc;
00281 ::exit(255);
00282 }
00283 dc->setPriorityCall(false);
00284 if (replyType != "int")
00285 {
00286 kdError() << "KUniqueApplication: DCOP communication error!" << endl;
00287 delete dc;
00288 ::exit(255);
00289 }
00290 QDataStream rs(reply, IO_ReadOnly);
00291 int exitCode;
00292 rs >> exitCode;
00293 delete dc;
00294 ::exit(exitCode);
00295 break;
00296 }
00297 return false;
00298 }
00299
00300
00301 KUniqueApplication::KUniqueApplication(bool allowStyles, bool GUIenabled, bool configUnique)
00302 : KApplication( allowStyles, GUIenabled, initHack( configUnique )),
00303 DCOPObject(KCmdLineArgs::about->appName())
00304 {
00305 d = new KUniqueApplicationPrivate;
00306 d->processingRequest = false;
00307 d->firstInstance = true;
00308
00309 if (s_nofork)
00310
00311 QTimer::singleShot( 0, this, SLOT(newInstanceNoFork()) );
00312 }
00313
00314 KUniqueApplication::~KUniqueApplication()
00315 {
00316 delete d;
00317 }
00318
00319
00320 KInstance* KUniqueApplication::initHack( bool configUnique )
00321 {
00322 KInstance* inst = new KInstance( KCmdLineArgs::about );
00323 if (configUnique)
00324 {
00325 KConfigGroupSaver saver( inst->config(), "KDE" );
00326 s_multipleInstances = inst->config()->readBoolEntry("MultipleInstances", false);
00327 }
00328 if( !start())
00329
00330 ::exit( 0 );
00331 return inst;
00332 }
00333
00334 void KUniqueApplication::newInstanceNoFork()
00335 {
00336 if (dcopClient()->isSuspended())
00337 {
00338
00339 QTimer::singleShot( 200, this, SLOT(newInstanceNoFork()) );
00340 return;
00341 }
00342
00343 newInstance();
00344 KStartupInfo::handleAutoAppStartedSending();
00345
00346 }
00347
00348 bool KUniqueApplication::process(const QCString &fun, const QByteArray &data,
00349 QCString &replyType, QByteArray &replyData)
00350 {
00351 if (fun == "newInstance()")
00352 {
00353 delayRequest(fun, data);
00354 return true;
00355 } else
00356 return DCOPObject::process(fun, data, replyType, replyData);
00357 }
00358
00359 void
00360 KUniqueApplication::delayRequest(const QCString &fun, const QByteArray &data)
00361 {
00362 DCOPRequest *request = new DCOPRequest;
00363 request->fun = fun;
00364 request->data = data;
00365 request->transaction = dcopClient()->beginTransaction();
00366 d->requestList.append(request);
00367 if (!d->processingRequest)
00368 {
00369 QTimer::singleShot(0, this, SLOT(processDelayed()));
00370 }
00371 }
00372
00373 void
00374 KUniqueApplication::processDelayed()
00375 {
00376 if (dcopClient()->isSuspended())
00377 {
00378
00379 QTimer::singleShot( 200, this, SLOT(processDelayed()));
00380 return;
00381 }
00382 d->processingRequest = true;
00383 while( !d->requestList.isEmpty() )
00384 {
00385 DCOPRequest *request = d->requestList.take(0);
00386 QByteArray replyData;
00387 QCString replyType;
00388 if (request->fun == "newInstance()") {
00389 dcopClient()->setPriorityCall(false);
00390 QDataStream ds(request->data, IO_ReadOnly);
00391 KCmdLineArgs::loadAppArgs(ds);
00392 if( !ds.atEnd())
00393 {
00394 QCString asn_id;
00395 ds >> asn_id;
00396 setStartupId( asn_id );
00397 }
00398 int exitCode = newInstance();
00399 KStartupInfo::handleAutoAppStartedSending();
00400 QDataStream rs(replyData, IO_WriteOnly);
00401 rs << exitCode;
00402 replyType = "int";
00403 }
00404 dcopClient()->endTransaction( request->transaction, replyType, replyData);
00405 delete request;
00406 }
00407
00408 d->processingRequest = false;
00409 }
00410
00411 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00412
00413 extern Time qt_x_time;
00414 #endif
00415
00416 int KUniqueApplication::newInstance()
00417 {
00418 if (!d->firstInstance)
00419 {
00420
00421 if ( mainWidget() )
00422 {
00423 mainWidget()->show();
00424 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00425
00426
00427
00428 KStartupInfo::setNewStartupId( mainWidget(), kapp->startupId());
00429 #endif
00430 }
00431 }
00432 d->firstInstance = false;
00433 return 0;
00434 }
00435
00436 void KUniqueApplication::virtual_hook( int id, void* data )
00437 { KApplication::virtual_hook( id, data );
00438 DCOPObject::virtual_hook( id, data ); }
00439
00440 #include "kuniqueapplication.moc"