• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdecore
 

tdecore

  • tdecore
tdeuniqueapplication.cpp
1/* This file is part of the KDE libraries
2 Copyright (c) 1999 Preston Brown <pbrown@kde.org>
3
4 $Id$
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20*/
21
22#include <config.h>
23
24#include <sys/types.h>
25#include <sys/wait.h>
26
27#include <assert.h>
28#include <errno.h>
29#include <stdlib.h>
30#include <unistd.h>
31
32#include <tqfile.h>
33#include <tqptrlist.h>
34#include <tqtimer.h>
35
36#include <dcopclient.h>
37#include <tdecmdlineargs.h>
38#include <tdestandarddirs.h>
39#include <tdeaboutdata.h>
40
41#if defined TQ_WS_X11
42#include <twin.h>
43#include <tdestartupinfo.h>
44#endif
45
46#include <tdeconfig.h>
47#include "kdebug.h"
48#include "tdeuniqueapplication.h"
49
50#if defined TQ_WS_X11
51#include <netwm.h>
52#include <X11/Xlib.h>
53#define DISPLAY "DISPLAY"
54#else
55# ifdef TQ_WS_QWS
56# define DISPLAY "QWS_DISPLAY"
57# else
58# define DISPLAY "DISPLAY"
59# endif
60#endif
61
62bool TDEUniqueApplication::s_nofork = false;
63bool TDEUniqueApplication::s_multipleInstances = false;
64bool TDEUniqueApplication::s_uniqueTestDone = false;
65bool TDEUniqueApplication::s_handleAutoStarted = false;
66
67static TDECmdLineOptions tdeunique_options[] =
68{
69 { "nofork", "Don't run in the background.", 0 },
70 TDECmdLineLastOption
71};
72
73struct DCOPRequest {
74 TQCString fun;
75 TQByteArray data;
76 DCOPClientTransaction *transaction;
77};
78
79class TDEUniqueApplicationPrivate {
80public:
81 TQPtrList <DCOPRequest> requestList;
82 bool processingRequest;
83 bool firstInstance;
84};
85
86void
87TDEUniqueApplication::addCmdLineOptions()
88{
89 TDECmdLineArgs::addCmdLineOptions(tdeunique_options, 0, "tdeuniqueapp", "tde" );
90}
91
92bool
93TDEUniqueApplication::start()
94{
95 if( s_uniqueTestDone )
96 return true;
97 s_uniqueTestDone = true;
98 addCmdLineOptions(); // Make sure to add cmd line options
99#ifdef TQ_WS_WIN
100 s_nofork = true;
101#else
102 TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs("tdeuniqueapp");
103 s_nofork = !args->isSet("fork");
104 delete args;
105#endif
106
107 TQCString appName = TDECmdLineArgs::about->appName();
108
109 if (s_nofork)
110 {
111 if (s_multipleInstances)
112 {
113 TQCString pid;
114 pid.setNum(getpid());
115 appName = appName + "-" + pid;
116 }
117
118 // Check to make sure that we're actually able to register with the DCOP
119 // server.
120
121#ifndef TQ_WS_WIN //TODO
122 if(dcopClient()->registerAs(appName, false).isEmpty()) {
123 startKdeinit();
124 if(dcopClient()->registerAs(appName, false).isEmpty()) {
125 kdError() << "TDEUniqueApplication: Can't setup DCOP communication." << endl;
126 ::exit(255);
127 }
128 }
129#endif
130
131 // We'll call newInstance in the constructor. Do nothing here.
132 return true;
133 }
134 DCOPClient *dc;
135 int fd[2];
136 signed char result;
137 if (0 > pipe(fd))
138 {
139 kdError() << "TDEUniqueApplication: pipe() failed!" << endl;
140 ::exit(255);
141 }
142 int fork_result = fork();
143 switch(fork_result) {
144 case -1:
145 kdError() << "TDEUniqueApplication: fork() failed!" << endl;
146 ::exit(255);
147 break;
148 case 0:
149 // Child
150 ::close(fd[0]);
151 if (s_multipleInstances)
152 appName.append("-").append(TQCString().setNum(getpid()));
153 dc = dcopClient();
154 {
155 TQCString regName = dc->registerAs(appName, false);
156 if (regName.isEmpty())
157 {
158 // Check DISPLAY
159 if (TQCString(getenv(DISPLAY)).isEmpty())
160 {
161 kdError() << "TDEUniqueApplication: Can't determine DISPLAY. Aborting." << endl;
162 result = -1; // Error
163 ::write(fd[1], &result, 1);
164 ::exit(255);
165 }
166
167 // Try to launch tdeinit.
168 startKdeinit();
169 regName = dc->registerAs(appName, false);
170 if (regName.isEmpty())
171 {
172 kdError() << "TDEUniqueApplication: Can't setup DCOP communication." << endl;
173 result = -1;
174 delete dc; // Clean up DCOP commmunication
175 ::write(fd[1], &result, 1);
176 ::exit(255);
177 }
178 }
179 if (regName != appName)
180 {
181 // Already running. Ok.
182 result = 0;
183 delete dc; // Clean up DCOP commmunication
184 ::write(fd[1], &result, 1);
185 ::close(fd[1]);
186#if 0
187#ifdef TQ_WS_X11
188 // say we're up and running ( probably no new window will appear )
189 TDEStartupInfoId id;
190 if( tdeApp != NULL ) // TDEApplication constructor unsets the env. variable
191 id.initId( tdeApp->startupId());
192 else
193 id = TDEStartupInfo::currentStartupIdEnv();
194 if( !id.none())
195 {
196 Display* disp = XOpenDisplay( NULL );
197 if( disp != NULL ) // use extra X connection
198 {
199 TDEStartupInfo::sendFinishX( disp, id );
200 XCloseDisplay( disp );
201 }
202 }
203#else //FIXME(E): implement
204#endif
205#endif
206 return false;
207 }
208 dc->setPriorityCall(true);
209 }
210
211 {
212#ifdef TQ_WS_X11
213 TDEStartupInfoId id;
214 if( tdeApp != NULL ) // TDEApplication constructor unsets the env. variable
215 id.initId( tdeApp->startupId());
216 else
217 id = TDEStartupInfo::currentStartupIdEnv();
218 if( !id.none())
219 { // notice about pid change
220 Display* disp = XOpenDisplay( NULL );
221 if( disp != NULL ) // use extra X connection
222 {
223 TDEStartupInfoData data;
224 data.addPid( getpid());
225 TDEStartupInfo::sendChangeX( disp, id, data );
226 XCloseDisplay( disp );
227 }
228 }
229#else //FIXME(E): Implement
230#endif
231 }
232 result = 0;
233 ::write(fd[1], &result, 1);
234 ::close(fd[1]);
235 return true; // Finished.
236 default:
237 // Parent
238// DCOPClient::emergencyClose();
239// dcopClient()->detach();
240 if (s_multipleInstances)
241 appName.append("-").append(TQCString().setNum(fork_result));
242 ::close(fd[1]);
243 for(;;)
244 {
245 int n = ::read(fd[0], &result, 1);
246 if (n == 1) break;
247 if (n == 0)
248 {
249 kdError() << "TDEUniqueApplication: Pipe closed unexpectedly." << endl;
250 ::exit(255);
251 }
252 if (errno != EINTR)
253 {
254 kdError() << "TDEUniqueApplication: Error reading from pipe." << endl;
255 ::exit(255);
256 }
257 }
258 ::close(fd[0]);
259
260 if (result != 0)
261 ::exit(result); // Error occurred in child.
262
263 dc = new DCOPClient();
264 if (!dc->attach())
265 {
266 kdError() << "TDEUniqueApplication: Parent process can't attach to DCOP." << endl;
267 delete dc; // Clean up DCOP commmunication
268 ::exit(255);
269 }
270 if (!dc->isApplicationRegistered(appName)) {
271 kdError() << "TDEUniqueApplication: Registering failed!" << endl;
272 }
273
274 TQCString new_asn_id;
275#if defined TQ_WS_X11
276 TDEStartupInfoId id;
277 if( tdeApp != NULL ) // TDEApplication constructor unsets the env. variable
278 id.initId( tdeApp->startupId());
279 else
280 id = TDEStartupInfo::currentStartupIdEnv();
281 if( !id.none())
282 new_asn_id = id.id();
283#endif
284
285 TQByteArray data, reply;
286 TQDataStream ds(data, IO_WriteOnly);
287
288 TDECmdLineArgs::saveAppArgs(ds);
289 ds << new_asn_id;
290
291 dc->setPriorityCall(true);
292 TQCString replyType;
293 if (!dc->call(appName, TDECmdLineArgs::about->appName(), "newInstance()", data, replyType, reply))
294 {
295 kdError() << "Communication problem with " << TDECmdLineArgs::about->appName() << ", it probably crashed." << endl;
296 delete dc; // Clean up DCOP commmunication
297 ::exit(255);
298 }
299 dc->setPriorityCall(false);
300 if (replyType != "int")
301 {
302 kdError() << "TDEUniqueApplication: DCOP communication error!" << endl;
303 delete dc; // Clean up DCOP commmunication
304 ::exit(255);
305 }
306 TQDataStream rs(reply, IO_ReadOnly);
307 int exitCode;
308 rs >> exitCode;
309 delete dc; // Clean up DCOP commmunication
310 ::exit(exitCode);
311 break;
312 }
313 return false; // make insure++ happy
314}
315
316
317TDEUniqueApplication::TDEUniqueApplication(bool allowStyles, bool GUIenabled, bool configUnique)
318 : TDEApplication( allowStyles, GUIenabled, initHack( configUnique )),
319 DCOPObject(TDECmdLineArgs::about->appName())
320{
321 d = new TDEUniqueApplicationPrivate;
322 d->processingRequest = false;
323 d->firstInstance = true;
324
325 if (s_nofork) {
326 // Can't call newInstance directly from the constructor since it's virtual...
327 TQTimer::singleShot( 0, this, TQ_SLOT(newInstanceNoFork()) );
328 }
329 else {
330 // Force to handle DCOP requests (newInstance call)
331 TQTimer::singleShot( 0, this, TQ_SLOT(processDelayed()));
332 }
333}
334
335
336#ifdef TQ_WS_X11
337TDEUniqueApplication::TDEUniqueApplication(Display *display, TQt::HANDLE visual,
338 TQt::HANDLE colormap, bool allowStyles, bool configUnique)
339 : TDEApplication( display, visual, colormap, allowStyles, initHack( configUnique )),
340 DCOPObject(TDECmdLineArgs::about->appName())
341{
342 d = new TDEUniqueApplicationPrivate;
343 d->processingRequest = false;
344 d->firstInstance = true;
345
346 if (s_nofork) {
347 // Can't call newInstance directly from the constructor since it's virtual...
348 TQTimer::singleShot( 0, this, TQ_SLOT(newInstanceNoFork()) );
349 }
350 else {
351 // Force to handle DCOP requests (newInstance call)
352 TQTimer::singleShot( 0, this, TQ_SLOT(processDelayed()));
353 }
354}
355#endif
356
357
358TDEUniqueApplication::~TDEUniqueApplication()
359{
360 delete d;
361}
362
363// this gets called before even entering TQApplication::TQApplication()
364TDEInstance* TDEUniqueApplication::initHack( bool configUnique )
365{
366 TDEInstance* inst = new TDEInstance( TDECmdLineArgs::about );
367 if (configUnique)
368 {
369 TDEConfigGroupSaver saver( inst->config(), "KDE" );
370 s_multipleInstances = inst->config()->readBoolEntry("MultipleInstances", false);
371 }
372 if( !start())
373 // Already running
374 ::exit( 0 );
375 return inst;
376}
377
378void TDEUniqueApplication::newInstanceNoFork()
379{
380 if (dcopClient()->isSuspended())
381 {
382 // Try again later.
383 TQTimer::singleShot( 200, this, TQ_SLOT(newInstanceNoFork()) );
384 return;
385 }
386
387 s_handleAutoStarted = false;
388 newInstance();
389 d->firstInstance = false;
390#if defined TQ_WS_X11
391 // KDE4 remove
392 // A hack to make startup notification stop for apps which override newInstance()
393 // and reuse an already existing window there, but use KWin::activateWindow()
394 // instead of TDEStartupInfo::setNewStartupId(). Therefore KWin::activateWindow()
395 // for now sets this flag. Automatically ending startup notification always
396 // would cause problem if the new window would show up with a small delay.
397 if( s_handleAutoStarted )
398 TDEStartupInfo::handleAutoAppStartedSending();
399#endif
400 // What to do with the return value ?
401}
402
403bool TDEUniqueApplication::process(const TQCString &fun, const TQByteArray &data,
404 TQCString &replyType, TQByteArray &replyData)
405{
406 if (fun == "newInstance()")
407 {
408 delayRequest(fun, data);
409 return true;
410 } else
411 return DCOPObject::process(fun, data, replyType, replyData);
412}
413
414void
415TDEUniqueApplication::delayRequest(const TQCString &fun, const TQByteArray &data)
416{
417 DCOPRequest *request = new DCOPRequest;
418 request->fun = fun;
419 request->data = data;
420 request->transaction = dcopClient()->beginTransaction();
421 d->requestList.append(request);
422 if (!d->processingRequest)
423 {
424 TQTimer::singleShot(0, this, TQ_SLOT(processDelayed()));
425 }
426}
427
428void
429TDEUniqueApplication::processDelayed()
430{
431 if (dcopClient()->isSuspended())
432 {
433 // Try again later.
434 TQTimer::singleShot( 200, this, TQ_SLOT(processDelayed()));
435 return;
436 }
437 d->processingRequest = true;
438 while( !d->requestList.isEmpty() )
439 {
440 DCOPRequest *request = d->requestList.take(0);
441 TQByteArray replyData;
442 TQCString replyType;
443 if (request->fun == "newInstance()") {
444 dcopClient()->setPriorityCall(false);
445 TQDataStream ds(request->data, IO_ReadOnly);
446 TDECmdLineArgs::loadAppArgs(ds);
447 if( !ds.atEnd()) // backwards compatibility
448 {
449 TQCString asn_id;
450 ds >> asn_id;
451 setStartupId( asn_id );
452 }
453 s_handleAutoStarted = false;
454 int exitCode = newInstance();
455 d->firstInstance = false;
456#if defined TQ_WS_X11
457 if( s_handleAutoStarted )
458 TDEStartupInfo::handleAutoAppStartedSending(); // KDE4 remove?
459#endif
460 TQDataStream rs(replyData, IO_WriteOnly);
461 rs << exitCode;
462 replyType = "int";
463 }
464 dcopClient()->endTransaction( request->transaction, replyType, replyData);
465 delete request;
466 }
467
468 d->processingRequest = false;
469}
470
471bool TDEUniqueApplication::restoringSession()
472{
473 return d->firstInstance && isRestored();
474}
475
476int TDEUniqueApplication::newInstance()
477{
478 if (!d->firstInstance)
479 {
480
481 if ( mainWidget() )
482 {
483 mainWidget()->show();
484#if defined TQ_WS_X11
485 // This is the line that handles window activation if necessary,
486 // and what's important, it does it properly. If you reimplement newInstance(),
487 // and don't call the inherited one, use this (but NOT when newInstance()
488 // is called for the first time, like here).
489 TDEStartupInfo::setNewStartupId( mainWidget(), tdeApp->startupId());
490#endif
491 }
492 }
493 return 0; // do nothing in default implementation
494}
495
496void TDEUniqueApplication::setHandleAutoStarted()
497{
498 s_handleAutoStarted = false;
499}
500
501void TDEUniqueApplication::virtual_hook( int id, void* data )
502{ TDEApplication::virtual_hook( id, data );
503 DCOPObject::virtual_hook( id, data ); }
504
505#include "tdeuniqueapplication.moc"
DCOPClient
DCOPClient::endTransaction
void endTransaction(DCOPClientTransaction *t, TQCString &replyType, TQByteArray &replyData)
DCOPClient::beginTransaction
DCOPClientTransaction * beginTransaction()
DCOPClient::isApplicationRegistered
bool isApplicationRegistered(const TQCString &remApp)
DCOPClient::attach
bool attach()
DCOPClient::call
bool call(const TQCString &remApp, const TQCString &remObj, const TQCString &remFun, const TQByteArray &data, TQCString &replyType, TQByteArray &replyData, bool useEventLoop, int timeout, bool forceRemote)
DCOPClient::registerAs
TQCString registerAs(const TQCString &appId, bool addPID=true)
DCOPObject
DCOPObject::process
virtual bool process(const TQCString &fun, const TQByteArray &data, TQCString &replyType, TQByteArray &replyData)
TDEAboutData::appName
const char * appName() const
Returns the application's internal name.
Definition: tdeaboutdata.cpp:237
TDEApplication
Controls and provides information to all KDE applications.
Definition: tdeapplication.h:95
TDEApplication::isRestored
bool isRestored() const
Is the application restored from the session manager?
Definition: tdeapplication.h:318
TDEApplication::dcopClient
static DCOPClient * dcopClient()
Returns a pointer to a DCOPClient for the application.
Definition: tdeapplication.cpp:1187
TDECmdLineArgs
A class for command-line argument handling.
Definition: tdecmdlineargs.h:223
TDECmdLineArgs::isSet
bool isSet(const char *option) const
Read out a boolean option or check for the presence of string option.
Definition: tdecmdlineargs.cpp:1181
TDECmdLineArgs::parsedArgs
static TDECmdLineArgs * parsedArgs(const char *id=0)
Access parsed arguments.
Definition: tdecmdlineargs.cpp:310
TDECmdLineArgs::addCmdLineOptions
static void addCmdLineOptions(const TDECmdLineOptions *options, const char *name=0, const char *id=0, const char *afterId=0)
Add options to your application.
Definition: tdecmdlineargs.cpp:206
TDECmdLineArgs::loadAppArgs
static void loadAppArgs(TQDataStream &)
Load arguments from a stream.
Definition: tdecmdlineargs.cpp:264
TDEConfigBase::readBoolEntry
bool readBoolEntry(const TQString &pKey, bool bDefault=false) const
Reads a boolean entry.
Definition: tdeconfigbase.cpp:748
TDEConfigGroupSaver
Helper class to facilitate working with TDEConfig / KSimpleConfig groups.
Definition: tdeconfigbase.h:2083
TDEInstance
Access to KDE global objects for use in shared libraries.
Definition: kinstance.h:48
TDEInstance::config
TDEConfig * config() const
Returns the general config object ("appnamerc").
Definition: kinstance.cpp:212
TDEInstance::TDEInstance
TDEInstance(const TQCString &instanceName)
Constructor.
Definition: kinstance.cpp:75
TDEUniqueApplication::start
static bool start()
Forks and registers with dcop.
Definition: tdeuniqueapplication.cpp:93
TDEUniqueApplication::process
bool process(const TQCString &fun, const TQByteArray &data, TQCString &replyType, TQByteArray &replyData)
Dispatches any incoming DCOP message for a new instance.
Definition: tdeuniqueapplication.cpp:403
TDEUniqueApplication::~TDEUniqueApplication
virtual ~TDEUniqueApplication()
Destructor.
Definition: tdeuniqueapplication.cpp:358
TDEUniqueApplication::restoringSession
bool restoringSession()
Returns whether newInstance() is being called while session restoration is in progress.
Definition: tdeuniqueapplication.cpp:471
TDEUniqueApplication::newInstance
virtual int newInstance()
Creates a new "instance" of the application.
Definition: tdeuniqueapplication.cpp:476
TDEUniqueApplication::addCmdLineOptions
static void addCmdLineOptions()
Adds command line options specific for TDEUniqueApplication.
Definition: tdeuniqueapplication.cpp:87
TDEUniqueApplication::TDEUniqueApplication
TDEUniqueApplication(bool allowStyles=true, bool GUIenabled=true, bool configUnique=false)
Constructor.
Definition: tdeuniqueapplication.cpp:317
endl
kndbgstream & endl(kndbgstream &s)
Does nothing.
Definition: kdebug.h:583
TDECmdLineOptions
Structure that holds command line options.
Definition: tdecmdlineargs.h:41

tdecore

Skip menu "tdecore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

tdecore

Skip menu "tdecore"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdecore by doxygen 1.9.4
This website is maintained by Timothy Pearson.