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

tdecore

  • tdecore
kuniqueapplication.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 <kstandarddirs.h>
39 #include <tdeaboutdata.h>
40 
41 #if defined Q_WS_X11
42 #include <twin.h>
43 #include <tdestartupinfo.h>
44 #endif
45 
46 #include <tdeconfig.h>
47 #include "kdebug.h"
48 #include "kuniqueapplication.h"
49 
50 #if defined Q_WS_X11
51 #include <netwm.h>
52 #include <X11/Xlib.h>
53 #define DISPLAY "DISPLAY"
54 #else
55 # ifdef Q_WS_QWS
56 # define DISPLAY "QWS_DISPLAY"
57 # else
58 # define DISPLAY "DISPLAY"
59 # endif
60 #endif
61 
62 bool KUniqueApplication::s_nofork = false;
63 bool KUniqueApplication::s_multipleInstances = false;
64 bool KUniqueApplication::s_uniqueTestDone = false;
65 bool KUniqueApplication::s_handleAutoStarted = false;
66 
67 static TDECmdLineOptions kunique_options[] =
68 {
69  { "nofork", "Don't run in the background.", 0 },
70  TDECmdLineLastOption
71 };
72 
73 struct DCOPRequest {
74  TQCString fun;
75  TQByteArray data;
76  DCOPClientTransaction *transaction;
77 };
78 
79 class KUniqueApplicationPrivate {
80 public:
81  TQPtrList <DCOPRequest> requestList;
82  bool processingRequest;
83  bool firstInstance;
84 };
85 
86 void
87 KUniqueApplication::addCmdLineOptions()
88 {
89  TDECmdLineArgs::addCmdLineOptions(kunique_options, 0, "kuniqueapp", "tde" );
90 }
91 
92 bool
93 KUniqueApplication::start()
94 {
95  if( s_uniqueTestDone )
96  return true;
97  s_uniqueTestDone = true;
98  addCmdLineOptions(); // Make sure to add cmd line options
99 #ifdef Q_WS_WIN
100  s_nofork = true;
101 #else
102  TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs("kuniqueapp");
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 Q_WS_WIN //TODO
122  if(dcopClient()->registerAs(appName, false).isEmpty()) {
123  startKdeinit();
124  if(dcopClient()->registerAs(appName, false).isEmpty()) {
125  kdError() << "KUniqueApplication: 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() << "KUniqueApplication: pipe() failed!" << endl;
140  ::exit(255);
141  }
142  int fork_result = fork();
143  switch(fork_result) {
144  case -1:
145  kdError() << "KUniqueApplication: 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() << "KUniqueApplication: 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() << "KUniqueApplication: 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 Q_WS_X11
188  // say we're up and running ( probably no new window will appear )
189  TDEStartupInfoId id;
190  if( kapp != NULL ) // TDEApplication constructor unsets the env. variable
191  id.initId( kapp->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 Q_WS_X11
213  TDEStartupInfoId id;
214  if( kapp != NULL ) // TDEApplication constructor unsets the env. variable
215  id.initId( kapp->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() << "KUniqueApplication: Pipe closed unexpectedly." << endl;
250  ::exit(255);
251  }
252  if (errno != EINTR)
253  {
254  kdError() << "KUniqueApplication: 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() << "KUniqueApplication: 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() << "KUniqueApplication: Registering failed!" << endl;
272  }
273 
274  TQCString new_asn_id;
275 #if defined Q_WS_X11
276  TDEStartupInfoId id;
277  if( kapp != NULL ) // TDEApplication constructor unsets the env. variable
278  id.initId( kapp->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() << "KUniqueApplication: 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 
317 KUniqueApplication::KUniqueApplication(bool allowStyles, bool GUIenabled, bool configUnique)
318  : TDEApplication( allowStyles, GUIenabled, initHack( configUnique )),
319  DCOPObject(TDECmdLineArgs::about->appName())
320 {
321  d = new KUniqueApplicationPrivate;
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, TQT_SLOT(newInstanceNoFork()) );
328  }
329  else {
330  // Force to handle DCOP requests (newInstance call)
331  TQTimer::singleShot( 0, this, TQT_SLOT(processDelayed()));
332  }
333 }
334 
335 
336 #ifdef Q_WS_X11
337 KUniqueApplication::KUniqueApplication(Display *display, Qt::HANDLE visual,
338  Qt::HANDLE colormap, bool allowStyles, bool configUnique)
339  : TDEApplication( display, visual, colormap, allowStyles, initHack( configUnique )),
340  DCOPObject(TDECmdLineArgs::about->appName())
341 {
342  d = new KUniqueApplicationPrivate;
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, TQT_SLOT(newInstanceNoFork()) );
349  }
350  else {
351  // Force to handle DCOP requests (newInstance call)
352  TQTimer::singleShot( 0, this, TQT_SLOT(processDelayed()));
353  }
354 }
355 #endif
356 
357 
358 KUniqueApplication::~KUniqueApplication()
359 {
360  delete d;
361 }
362 
363 // this gets called before even entering TQApplication::TQApplication()
364 TDEInstance* KUniqueApplication::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 
378 void KUniqueApplication::newInstanceNoFork()
379 {
380  if (dcopClient()->isSuspended())
381  {
382  // Try again later.
383  TQTimer::singleShot( 200, this, TQT_SLOT(newInstanceNoFork()) );
384  return;
385  }
386 
387  s_handleAutoStarted = false;
388  newInstance();
389  d->firstInstance = false;
390 #if defined Q_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 
403 bool KUniqueApplication::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 
414 void
415 KUniqueApplication::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, TQT_SLOT(processDelayed()));
425  }
426 }
427 
428 void
429 KUniqueApplication::processDelayed()
430 {
431  if (dcopClient()->isSuspended())
432  {
433  // Try again later.
434  TQTimer::singleShot( 200, this, TQT_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 Q_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 
471 bool KUniqueApplication::restoringSession()
472 {
473  return d->firstInstance && isRestored();
474 }
475 
476 int KUniqueApplication::newInstance()
477 {
478  if (!d->firstInstance)
479  {
480 
481  if ( mainWidget() )
482  {
483  mainWidget()->show();
484 #if defined Q_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(), kapp->startupId());
490 #endif
491  }
492  }
493  return 0; // do nothing in default implementation
494 }
495 
496 void KUniqueApplication::setHandleAutoStarted()
497 {
498  s_handleAutoStarted = false;
499 }
500 
501 void KUniqueApplication::virtual_hook( int id, void* data )
502 { TDEApplication::virtual_hook( id, data );
503  DCOPObject::virtual_hook( id, data ); }
504 
505 #include "kuniqueapplication.moc"
TDECmdLineArgs
A class for command-line argument handling.
Definition: tdecmdlineargs.h:222
KUniqueApplication::start
static bool start()
Forks and registers with dcop.
Definition: kuniqueapplication.cpp:93
DCOPClient::isApplicationRegistered
bool isApplicationRegistered(const TQCString &remApp)
KUniqueApplication::KUniqueApplication
KUniqueApplication(bool allowStyles=true, bool GUIenabled=true, bool configUnique=false)
Constructor.
Definition: kuniqueapplication.cpp:317
TDEApplication::dcopClient
static DCOPClient * dcopClient()
Returns a pointer to a DCOPClient for the application.
Definition: tdeapplication.cpp:1189
TDEConfigGroupSaver
Helper class to facilitate working with TDEConfig / KSimpleConfig groups.
Definition: tdeconfigbase.h:2082
DCOPClient::attach
bool attach()
DCOPClient::endTransaction
void endTransaction(DCOPClientTransaction *t, TQCString &replyType, TQByteArray &replyData)
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
KUniqueApplication::~KUniqueApplication
virtual ~KUniqueApplication()
Destructor.
Definition: kuniqueapplication.cpp:358
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
DCOPClient
DCOPClient::beginTransaction
DCOPClientTransaction * beginTransaction()
TDEApplication
Controls and provides information to all KDE applications.
Definition: tdeapplication.h:96
TDECmdLineArgs::loadAppArgs
static void loadAppArgs(TQDataStream &)
Load arguments from a stream.
Definition: tdecmdlineargs.cpp:264
KUniqueApplication::restoringSession
bool restoringSession()
Returns whether newInstance() is being called while session restoration is in progress.
Definition: kuniqueapplication.cpp:471
TDECmdLineArgs::parsedArgs
static TDECmdLineArgs * parsedArgs(const char *id=0)
Access parsed arguments.
Definition: tdecmdlineargs.cpp:310
TDEConfigBase::readBoolEntry
bool readBoolEntry(const TQString &pKey, bool bDefault=false) const
Reads a boolean entry.
Definition: tdeconfigbase.cpp:772
DCOPClient::registerAs
TQCString registerAs(const TQCString &appId, bool addPID=true)
KUniqueApplication::newInstance
virtual int newInstance()
Creates a new "instance" of the application.
Definition: kuniqueapplication.cpp:476
TDEApplication::isRestored
bool isRestored() const
Is the application restored from the session manager?
Definition: tdeapplication.h:320
KUniqueApplication::addCmdLineOptions
static void addCmdLineOptions()
Adds command line options specific for KUniqueApplication.
Definition: kuniqueapplication.cpp:87
TDEInstance::config
TDEConfig * config() const
Returns the general config object ("appnamerc").
Definition: kinstance.cpp:210
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
endl
kndbgstream & endl(kndbgstream &s)
Does nothing.
Definition: kdebug.h:583
DCOPObject
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)
KUniqueApplication::process
bool process(const TQCString &fun, const TQByteArray &data, TQCString &replyType, TQByteArray &replyData)
Dispatches any incoming DCOP message for a new instance.
Definition: kuniqueapplication.cpp:403
TDEInstance
Access to KDE global objects for use in shared libraries.
Definition: kinstance.h:47
TDECmdLineOptions
Structure that holds command line options.
Definition: tdecmdlineargs.h:40
TDEInstance::TDEInstance
TDEInstance(const TQCString &instanceName)
Constructor.
Definition: kinstance.cpp:73

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
  •     tdecore
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  • tdeioslave
  •   http
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdecore by doxygen 1.8.8
This website is maintained by Timothy Pearson.