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