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"