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 KStartupInfo::setNewStartupId( mainWidget(), kapp->startupId());
00479
#endif
00480
}
00481 }
00482
return 0;
00483 }
00484
00485
void KUniqueApplication::setHandleAutoStarted()
00486 {
00487 s_handleAutoStarted =
false;
00488 }
00489
00490
void KUniqueApplication::virtual_hook(
int id,
void* data )
00491 { KApplication::virtual_hook(
id, data );
00492
DCOPObject::virtual_hook(
id, data ); }
00493
00494
#include "kuniqueapplication.moc"