• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

KDEUI

  • kdeui
  • kernel
kuniqueapplication.cpp
Go to the documentation of this file.
1/* This file is part of the KDE libraries
2 Copyright (c) 1999 Preston Brown <pbrown@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*/
19
20#include "kuniqueapplication.h"
21#include "kuniqueapplication_p.h"
22#include <kmainwindow.h>
23
24#include <config.h>
25
26#include <sys/types.h>
27#include <sys/wait.h>
28
29#include <assert.h>
30#include <errno.h>
31#include <stdlib.h>
32#include <unistd.h>
33
34#include <QtCore/QFile>
35#include <QtCore/QList>
36#include <QtCore/QTimer>
37#include <QtDBus/QtDBus>
38
39#include <kcmdlineargs.h>
40#include <kstandarddirs.h>
41#include <kaboutdata.h>
42#include <kconfiggroup.h>
43#include <kwindowsystem.h>
44
45#if defined Q_WS_X11
46#include <kstartupinfo.h>
47#endif
48
49/* I don't know why, but I end up with complaints about
50 a forward-declaration of QWidget in the activeWidow()->show
51 call below on Qt/Mac if I don't include this here... */
52#include <QWidget>
53
54#include <kconfig.h>
55#include "kdebug.h"
56
57#if defined Q_WS_X11
58#include <netwm.h>
59#include <X11/Xlib.h>
60#define DISPLAY "DISPLAY"
61#else
62# ifdef Q_WS_QWS
63# define DISPLAY "QWS_DISPLAY"
64# else
65# define DISPLAY "DISPLAY"
66# endif
67#endif
68
69#if defined(Q_OS_DARWIN) || defined (Q_OS_MAC)
70#include <kkernel_mac.h>
71#endif
72
73bool KUniqueApplication::Private::s_nofork = false;
74bool KUniqueApplication::Private::s_multipleInstances = false;
75bool s_kuniqueapplication_startCalled = false;
76bool KUniqueApplication::Private::s_handleAutoStarted = false;
77#ifdef Q_WS_WIN
78/* private helpers from kapplication_win.cpp */
79#ifndef _WIN32_WCE
80void KApplication_activateWindowForProcess( const QString& executableName );
81#endif
82#endif
83
84void
85KUniqueApplication::addCmdLineOptions()
86{
87 KCmdLineOptions kunique_options;
88 kunique_options.add("nofork", ki18n("Do not run in the background."));
89#ifdef Q_WS_MACX
90 kunique_options.add("psn", ki18n("Internally added if launched from Finder"));
91#endif
92 KCmdLineArgs::addCmdLineOptions(kunique_options, KLocalizedString(), "kuniqueapp", "kde");
93}
94
95static QDBusConnectionInterface *tryToInitDBusConnection()
96{
97 // Check the D-Bus connection health
98 QDBusConnectionInterface* dbusService = 0;
99 QDBusConnection sessionBus = QDBusConnection::sessionBus();
100 if (!sessionBus.isConnected() || !(dbusService = sessionBus.interface()))
101 {
102 kError() << "KUniqueApplication: Cannot find the D-Bus session server: " << sessionBus.lastError().message() << endl;
103 ::exit(255);
104 }
105 return dbusService;
106}
107
108bool KUniqueApplication::start()
109{
110 return start(0);
111}
112
113bool
114KUniqueApplication::start(StartFlags flags)
115{
116 if( s_kuniqueapplication_startCalled )
117 return true;
118 s_kuniqueapplication_startCalled = true;
119
120 addCmdLineOptions(); // Make sure to add cmd line options
121#if defined(Q_WS_WIN) || defined(Q_WS_MACX)
122 Private::s_nofork = true;
123#else
124 KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kuniqueapp");
125 Private::s_nofork = !args->isSet("fork");
126#endif
127
128 QString appName = KCmdLineArgs::aboutData()->appName();
129 const QStringList parts = KCmdLineArgs::aboutData()->organizationDomain().split(QLatin1Char('.'), QString::SkipEmptyParts);
130 if (parts.isEmpty())
131 appName.prepend(QLatin1String("local."));
132 else
133 foreach (const QString& s, parts)
134 {
135 appName.prepend(QLatin1Char('.'));
136 appName.prepend(s);
137 }
138
139 bool forceNewProcess = Private::s_multipleInstances || flags & NonUniqueInstance;
140
141 if (Private::s_nofork)
142 {
143
144#if defined(Q_OS_DARWIN) || defined (Q_OS_MAC)
145 mac_initialize_dbus();
146#endif
147
148 QDBusConnectionInterface* dbusService = tryToInitDBusConnection();
149
150 QString pid = QString::number(getpid());
151 if (forceNewProcess)
152 appName = appName + '-' + pid;
153
154 // Check to make sure that we're actually able to register with the D-Bus session
155 // server.
156 bool registered = dbusService->registerService(appName) == QDBusConnectionInterface::ServiceRegistered;
157 if (!registered)
158 {
159 kError() << "KUniqueApplication: Can't setup D-Bus service. Probably already running."
160 << endl;
161#if defined(Q_WS_WIN) && !defined(_WIN32_WCE)
162 KApplication_activateWindowForProcess(KCmdLineArgs::aboutData()->appName());
163#endif
164 ::exit(255);
165 }
166
167 // We'll call newInstance in the constructor. Do nothing here.
168 return true;
169
170#if defined(Q_OS_DARWIN) || defined (Q_OS_MAC)
171 } else {
172 mac_fork_and_reexec_self();
173#endif
174
175 }
176
177#ifndef Q_WS_WIN
178 int fd[2];
179 signed char result;
180 if (0 > pipe(fd))
181 {
182 kError() << "KUniqueApplication: pipe() failed!" << endl;
183 ::exit(255);
184 }
185 int fork_result = fork();
186 switch(fork_result) {
187 case -1:
188 kError() << "KUniqueApplication: fork() failed!" << endl;
189 ::exit(255);
190 break;
191 case 0:
192 {
193 // Child
194
195 QDBusConnectionInterface* dbusService = tryToInitDBusConnection();
196 ::close(fd[0]);
197 if (forceNewProcess)
198 appName.append("-").append(QString::number(getpid()));
199
200 QDBusReply<QDBusConnectionInterface::RegisterServiceReply> reply =
201 dbusService->registerService(appName);
202 if (!reply.isValid())
203 {
204 kError() << "KUniqueApplication: Can't setup D-Bus service." << endl;
205 result = -1;
206 ::write(fd[1], &result, 1);
207 ::exit(255);
208 }
209 if (reply == QDBusConnectionInterface::ServiceNotRegistered)
210 {
211 // Already running. Ok.
212 result = 0;
213 ::write(fd[1], &result, 1);
214 ::close(fd[1]);
215 return false;
216 }
217
218#ifdef Q_WS_X11
219 KStartupInfoId id;
220 if( kapp != NULL ) // KApplication constructor unsets the env. variable
221 id.initId( kapp->startupId());
222 else
223 id = KStartupInfo::currentStartupIdEnv();
224 if( !id.none())
225 { // notice about pid change
226 Display* disp = XOpenDisplay( NULL );
227 if( disp != NULL ) // use extra X connection
228 {
229 KStartupInfoData data;
230 data.addPid( getpid());
231 KStartupInfo::sendChangeX( disp, id, data );
232 XCloseDisplay( disp );
233 }
234 }
235#else //FIXME(E): Implement
236#endif
237 }
238 result = 0;
239 ::write(fd[1], &result, 1);
240 ::close(fd[1]);
241 return true; // Finished.
242 default:
243 // Parent
244
245 if (forceNewProcess)
246 appName.append("-").append(QString::number(fork_result));
247 ::close(fd[1]);
248
249 Q_FOREVER
250 {
251 int n = ::read(fd[0], &result, 1);
252 if (n == 1) break;
253 if (n == 0)
254 {
255 kError() << "KUniqueApplication: Pipe closed unexpectedly." << endl;
256 ::exit(255);
257 }
258 if (errno != EINTR)
259 {
260 kError() << "KUniqueApplication: Error reading from pipe." << endl;
261 ::exit(255);
262 }
263 }
264 ::close(fd[0]);
265
266 if (result != 0)
267 ::exit(result); // Error occurred in child.
268
269#endif
270 QDBusConnectionInterface* dbusService = tryToInitDBusConnection();
271 if (!dbusService->isServiceRegistered(appName))
272 {
273 kError() << "KUniqueApplication: Registering failed!" << endl;
274 }
275
276 QByteArray saved_args;
277 QDataStream ds(&saved_args, QIODevice::WriteOnly);
278 KCmdLineArgs::saveAppArgs(ds);
279
280 QByteArray new_asn_id;
281#if defined Q_WS_X11
282 KStartupInfoId id;
283 if( kapp != NULL ) // KApplication constructor unsets the env. variable
284 id.initId( kapp->startupId());
285 else
286 id = KStartupInfo::currentStartupIdEnv();
287 if( !id.none())
288 new_asn_id = id.id();
289#endif
290
291 QDBusMessage msg = QDBusMessage::createMethodCall(appName, "/MainApplication", "org.kde.KUniqueApplication",
292 "newInstance");
293 msg << new_asn_id << saved_args;
294 QDBusReply<int> reply = QDBusConnection::sessionBus().call(msg, QDBus::Block, INT_MAX);
295
296 if (!reply.isValid())
297 {
298 QDBusError err = reply.error();
299 kError() << "Communication problem with " << KCmdLineArgs::aboutData()->appName() << ", it probably crashed." << endl
300 << "Error message was: " << err.name() << ": \"" << err.message() << "\"" << endl;
301 ::exit(255);
302 }
303#ifndef Q_WS_WIN
304 ::exit(reply);
305 break;
306 }
307#endif
308 return false; // make insure++ happy
309}
310
311
312KUniqueApplication::KUniqueApplication(bool GUIenabled, bool configUnique)
313 : KApplication( GUIenabled, Private::initHack( configUnique )),
314 d(new Private(this))
315{
316 d->processingRequest = false;
317 d->firstInstance = true;
318
319 // the sanity checking happened in initHack
320 new KUniqueApplicationAdaptor(this);
321
322 if (Private::s_nofork)
323 // Can't call newInstance directly from the constructor since it's virtual...
324 QTimer::singleShot( 0, this, SLOT(_k_newInstanceNoFork()) );
325}
326
327
328#ifdef Q_WS_X11
329KUniqueApplication::KUniqueApplication(Display *display, Qt::HANDLE visual,
330 Qt::HANDLE colormap, bool configUnique)
331 : KApplication( display, visual, colormap, Private::initHack( configUnique )),
332 d(new Private(this))
333{
334 d->processingRequest = false;
335 d->firstInstance = true;
336
337 // the sanity checking happened in initHack
338 new KUniqueApplicationAdaptor(this);
339
340 if (Private::s_nofork)
341 // Can't call newInstance directly from the constructor since it's virtual...
342 QTimer::singleShot( 0, this, SLOT(_k_newInstanceNoFork()) );
343}
344#endif
345
346
347KUniqueApplication::~KUniqueApplication()
348{
349 delete d;
350}
351
352// this gets called before even entering QApplication::QApplication()
353KComponentData KUniqueApplication::Private::initHack(bool configUnique)
354{
355 KComponentData cData(KCmdLineArgs::aboutData());
356 if (configUnique)
357 {
358 KConfigGroup cg(cData.config(), "KDE");
359 s_multipleInstances = cg.readEntry("MultipleInstances", false);
360 }
361 if( !KUniqueApplication::start())
362 // Already running
363 ::exit( 0 );
364 return cData;
365}
366
367void KUniqueApplication::Private::_k_newInstanceNoFork()
368{
369 s_handleAutoStarted = false;
370 q->newInstance();
371 firstInstance = false;
372#if defined Q_WS_X11
373 // KDE4 remove
374 // A hack to make startup notification stop for apps which override newInstance()
375 // and reuse an already existing window there, but use KWindowSystem::activateWindow()
376 // instead of KStartupInfo::setNewStartupId(). Therefore KWindowSystem::activateWindow()
377 // for now sets this flag. Automatically ending startup notification always
378 // would cause problem if the new window would show up with a small delay.
379 if( s_handleAutoStarted )
380 KStartupInfo::handleAutoAppStartedSending();
381#endif
382 // What to do with the return value ?
383}
384
385bool KUniqueApplication::restoringSession()
386{
387 return d->firstInstance && isSessionRestored();
388}
389
390int KUniqueApplication::newInstance()
391{
392 if (!d->firstInstance) {
393 QList<KMainWindow*> allWindows = KMainWindow::memberList();
394 if (!allWindows.isEmpty()) {
395 // This method is documented to only work for applications
396 // with only one mainwindow.
397 KMainWindow* mainWindow = allWindows.first();
398 if (mainWindow) {
399 mainWindow->show();
400#ifdef Q_WS_X11
401 // This is the line that handles window activation if necessary,
402 // and what's important, it does it properly. If you reimplement newInstance(),
403 // and don't call the inherited one, use this (but NOT when newInstance()
404 // is called for the first time, like here).
405 KStartupInfo::setNewStartupId(mainWindow, startupId());
406#endif
407#ifdef Q_WS_WIN
408 KWindowSystem::forceActiveWindow( mainWindow->winId() );
409#endif
410
411 }
412 }
413 }
414 return 0; // do nothing in default implementation
415}
416
417#ifndef KDE_NO_DEPRECATED
418void KUniqueApplication::setHandleAutoStarted()
419{
420 Private::s_handleAutoStarted = false;
421}
422#endif
423
425
426int KUniqueApplicationAdaptor::newInstance(const QByteArray &asn_id, const QByteArray &args)
427{
428 if (!asn_id.isEmpty())
429 parent()->setStartupId(asn_id);
430
431 const int index = parent()->metaObject()->indexOfMethod("loadCommandLineOptionsForNewInstance");
432 if (index != -1) {
433 // This hook allows the application to set up KCmdLineArgs using addCmdLineOptions
434 // before we load the app args. Normally not necessary, but needed by kontact
435 // since it switches to other sets of options when called as e.g. kmail or korganizer
436 QMetaObject::invokeMethod(parent(), "loadCommandLineOptionsForNewInstance");
437 }
438
439 QDataStream ds(args);
440 KCmdLineArgs::loadAppArgs(ds);
441
442 int ret = parent()->newInstance();
443 // Must be done out of the newInstance code, in case it is overloaded
444 parent()->d->firstInstance = false;
445 return ret;
446}
447
448#include "kuniqueapplication.moc"
449#include "kuniqueapplication_p.moc"
KAboutData::organizationDomain
QString organizationDomain() const
KAboutData::appName
QString appName() const
KApplication
Controls and provides information to all KDE applications.
Definition: kapplication.h:83
KApplication::startupId
QByteArray startupId() const
Returns the app startup notification identifier for this running application.
Definition: kapplication.cpp:1075
KCmdLineArgs
KCmdLineArgs::isSet
bool isSet(const QByteArray &option) const
KCmdLineArgs::saveAppArgs
static void saveAppArgs(QDataStream &)
KCmdLineArgs::aboutData
static const KAboutData * aboutData()
KCmdLineArgs::parsedArgs
static KCmdLineArgs * parsedArgs(const QByteArray &id=QByteArray())
KCmdLineArgs::addCmdLineOptions
static void addCmdLineOptions(const KCmdLineOptions &options, const KLocalizedString &name=KLocalizedString(), const QByteArray &id=QByteArray(), const QByteArray &afterId=QByteArray())
KCmdLineArgs::loadAppArgs
static void loadAppArgs(QDataStream &)
KCmdLineOptions
KCmdLineOptions::add
KCmdLineOptions & add(const KCmdLineOptions &options)
KComponentData
KConfigGroup
KLocalizedString
KMainWindow
KDE top level main window
Definition: kmainwindow.h:107
KMainWindow::memberList
static QList< KMainWindow * > memberList()
List of members of KMainWindow class.
Definition: kmainwindow.cpp:1218
KStartupInfoData
Class representing data about an application startup notification.
Definition: kstartupinfo.h:440
KStartupInfoData::addPid
void addPid(pid_t pid)
Adds a PID to the list of processes that belong to the startup notification.
Definition: kstartupinfo.cpp:1446
KStartupInfoId
Class representing an identification of application startup notification.
Definition: kstartupinfo.h:369
KStartupInfoId::initId
void initId(const QByteArray &id="")
Initializes this object with the given identification ( which may be also "0" for no notification ),...
Definition: kstartupinfo.cpp:1058
KStartupInfo::setNewStartupId
static void setNewStartupId(QWidget *window, const QByteArray &startup_id)
Use this function if the application got a request with startup notification from outside (for exampl...
Definition: kstartupinfo.cpp:643
KStartupInfo::handleAutoAppStartedSending
static void handleAutoAppStartedSending()
Definition: kstartupinfo.cpp:637
KStartupInfo::sendChangeX
static bool sendChangeX(Display *dpy, const KStartupInfoId &id, const KStartupInfoData &data)
Like sendChange , uses dpy instead of qt_x11display() for sending the info.
Definition: kstartupinfo.cpp:510
KStartupInfo::currentStartupIdEnv
static KStartupInfoId currentStartupIdEnv()
Returns the current startup notification identification for the current startup notification environm...
Definition: kstartupinfo.cpp:1090
KUniqueApplication::start
static bool start()
Definition: kuniqueapplication.cpp:108
KUniqueApplication::start
static bool start(StartFlags flags)
Forks and registers with D-Bus.
Definition: kuniqueapplication.cpp:114
KUniqueApplication::restoringSession
bool restoringSession()
Returns whether newInstance() is being called while session restoration is in progress.
Definition: kuniqueapplication.cpp:385
KUniqueApplication::NonUniqueInstance
@ NonUniqueInstance
Create a new instance of the application in a new process and do not attempt to re-use an existing pr...
Definition: kuniqueapplication.h:119
KUniqueApplication::~KUniqueApplication
virtual ~KUniqueApplication()
Destructor.
Definition: kuniqueapplication.cpp:347
KUniqueApplication::newInstance
virtual int newInstance()
Creates a new "instance" of the application.
Definition: kuniqueapplication.cpp:390
KUniqueApplication::KUniqueApplication
KUniqueApplication(bool GUIenabled=true, bool configUnique=false)
Constructor.
Definition: kuniqueapplication.cpp:312
KUniqueApplication::setHandleAutoStarted
static void setHandleAutoStarted()
Definition: kuniqueapplication.cpp:418
KUniqueApplication::addCmdLineOptions
static void addCmdLineOptions()
Adds command line options specific for KUniqueApplication.
Definition: kuniqueapplication.cpp:85
KWindowSystem::forceActiveWindow
static void forceActiveWindow(WId win, long time=0)
Sets window win to be the active window.
Definition: kwindowsystem_mac.cpp:366
QList
kaboutdata.h
kapp
#define kapp
Definition: kapplication.h:56
KApplication_activateWindowForProcess
void KApplication_activateWindowForProcess(const QString &executableName)
Definition: kapplication_win.cpp:242
kcmdlineargs.h
kconfig.h
kconfiggroup.h
kdebug.h
mac_initialize_dbus
void mac_initialize_dbus()
mac_fork_and_reexec_self
void mac_fork_and_reexec_self()
kkernel_mac.h
ki18n
KLocalizedString ki18n(const char *msg)
kmainwindow.h
kstandarddirs.h
kstartupinfo.h
tryToInitDBusConnection
static QDBusConnectionInterface * tryToInitDBusConnection()
Definition: kuniqueapplication.cpp:95
s_kuniqueapplication_startCalled
bool s_kuniqueapplication_startCalled
Definition: kuniqueapplication.cpp:75
KApplication_activateWindowForProcess
void KApplication_activateWindowForProcess(const QString &executableName)
Definition: kapplication_win.cpp:242
kuniqueapplication.h
kwindowsystem.h
netwm.h
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDEUI

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

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal