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"