1/*
2 This file is part of libkdbusaddons
3
4 SPDX-FileCopyrightText: 2011 David Faure <faure@kde.org>
5 SPDX-FileCopyrightText: 2011 Kevin Ottens <ervin@kde.org>
6
7 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
8*/
9
10#ifndef KDBUSSERVICE_H
11#define KDBUSSERVICE_H
12
13#include <QObject>
14#include <QUrl>
15#include <memory>
16
17#include <kdbusaddons_export.h>
18
19class KDBusServicePrivate;
20
21/*!
22 * \class KDBusService
23 * \inmodule KDBusAddons
24 * \brief KDBusService takes care of registering the current process with D-Bus.
25 *
26 * This registers the application at a predictable location on D-Bus, registers
27 * the QCoreApplication (or subclass) object at /MainApplication, and
28 * assists in implementing the application side of D-Bus activation from
29 * the \l {http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html}
30 * {Desktop Entry Specification}.
31 *
32 * An application can either work in Multiple mode or Unique mode.
33 *
34 * In Multiple mode, the application can be launched many times. The service
35 * name in the D-Bus registration will contain the PID to distinguish the
36 * various instances; for example: \e org.kde.konqueror-12345.
37 *
38 * In Unique mode, only one instance of this application can ever run.
39 * The first instance of the application registers with D-Bus without the PID,
40 * and any attempt to run the application again will cause the
41 * activateRequested() signal to be emitted in the already-running instance; the
42 * duplicate instance will then quit. The exit value can be set by the already
43 * running instance with setExitValue(), the default value is \c 0.
44 *
45 * Unique-mode applications should usually delay parsing command-line arguments
46 * until after creating a KDBusService object; that way they know they are the
47 * original instance of the application.
48 *
49 * Applications that set the D-Bus activation entry (\c DBusActivatable=true) in
50 * their desktop files will use Unique mode and connect to the signals emitted
51 * by this class.
52 * Note that the D-Bus interface is exported for Multiple-mode applications as
53 * well, so it also makes sense for such applications to connect to the signals
54 * emitted by this class.
55 *
56 * \note In order to avoid a race, the application should export its objects to
57 * D-Bus before allowing the event loop to run (for example, by calling
58 * QCoreApplication::exec()). Otherwise, the application will appear on the bus
59 * before its objects are accessible via D-Bus, which could be a problem for
60 * other applications or scripts which start the application in order to talk
61 * D-Bus to it immediately.
62 *
63 * Example usage:
64 *
65 * \code
66 * QApplication app(argc, argv);
67 * app.setApplicationName("kuiserver");
68 * app.setOrganizationDomain("kde.org");
69 * // Create your D-Bus objects here
70 * // ...
71 * KDBusService service(KDBusService::Unique);
72 * // If this point is reached, this is the only running instance
73 * // i.e. org.kde.kuiserver has been registered
74 * return app.exec();
75 * \endcode
76 *
77 * \since 5.0
78 */
79class KDBUSADDONS_EXPORT KDBusService : public QObject
80{
81 Q_OBJECT
82
83public:
84 /*!
85 * \enum KDBusService::StartupOption
86 * Options to control the behaviour of KDBusService
87 * \value Unique
88 * Indicates that only one instance of this application should ever exist.
89 * Cannot be combined with \c Multiple.
90 * \value Multiple
91 * Indicates that multiple instances of the application may exist.
92 * Cannot be combined with \c Unique. This is the default.
93 * \value NoExitOnFailure
94 * Indicates that the application should not exit if it failed to
95 * register with D-Bus.
96 * If not set, KDBusService will quit the application if it failed to
97 * register the service with D-Bus or a \c Unique instance can not be
98 * activated. A \c Multiple instance will exit with error code \c 1.
99 * The exit value of a \c Unique instance can be set from the running
100 * instance with setExitValue(), the default value is \c 0.
101 * \value [since 5.65] Replace
102 * Indicates that if there's already a unique service running,
103 * to be quit and replaced with our own.
104 * If exported, it will try first quitting the service calling
105 * \c org.qtproject.Qt.QCoreApplication.quit,
106 * which is exported by KDBusService by default.
107 */
108 enum StartupOption {
109 Unique = 1,
110 Multiple = 2,
111 NoExitOnFailure = 4,
112 Replace = 8
113 };
114 Q_ENUM(StartupOption)
115 Q_DECLARE_FLAGS(StartupOptions, StartupOption)
116 Q_FLAG(StartupOptions)
117
118 /*!
119 * Tries to register the current process to D-Bus at an address based on the
120 * application name and organization domain under the given \a parent.
121 *
122 * The D-Bus service name is the reversed organization domain, followed by
123 * the application name. If \a options includes the \c Multiple flag, the
124 * application PID will be appended. For example:
125 * \code
126 * app.setApplicationName("kuiserver");
127 * app.setOrganizationDomain("kde.org");
128 * \endcode
129 * The above will make KDBusService register as \c org.kde.kuiserver in \c Unique
130 * mode, and \c org.kde.kuiserver-1234 (if the process has PID \c 1234) in
131 * \c Multiple mode.
132 */
133 explicit KDBusService(StartupOptions options = Multiple, QObject *parent = nullptr);
134
135 /*!
136 * Destroys this object (but does not unregister the application).
137 *
138 * Deleting this object before unregister() could confuse
139 * clients, who will see the service on the bus but will be unable to use
140 * the activation methods.
141 */
142 ~KDBusService() override;
143
144 /*!
145 * Returns true if the D-Bus registration succeeded.
146 *
147 * Note that this is only useful when specifying the option NoExitOnFailure.
148 * Otherwise, the simple fact that this process is still running indicates
149 * that the registration succeeded.
150 */
151 bool isRegistered() const;
152
153 /*!
154 * Returns the name of the D-Bus service registered by this class.
155 * Mostly useful when using the option Multiple.
156 * \since 5.33
157 */
158 QString serviceName() const;
159
160 /*!
161 * Returns the error message from the D-Bus registration if it failed.
162 *
163 * Note that this is only useful when specifying the option NoExitOnFailure.
164 * Otherwise the process has quit by the time you can get a chance to call this.
165 */
166 QString errorMessage() const;
167
168 /*!
169 * Sets the exit value to be used for a duplicate instance.
170 *
171 * If this is a \c Unique application, a slot connected to
172 * activateRequested() can use this to specify a non-zero exit value for the
173 * duplicate instance. This would typically be done if invalid command-line
174 * arguments are passed.
175 *
176 * Note that this will only work if the signal-slot connection type is
177 * Qt::DirectConnection.
178 *
179 * \a value The exit value for the duplicate instance.
180 */
181 void setExitValue(int value);
182
183Q_SIGNALS:
184 /*!
185 * Signals that the application is to be activated.
186 *
187 * If this is a \c Unique application, when KDBusService is constructed in
188 * subsequent instances of the application (ie: when the executable is run
189 * when an instance is already running), it will cause this signal to be
190 * emitted in the already-running instance (with the arguments passed to the
191 * duplicate instance), and the duplicate instance will then exit.
192 *
193 * If this application's desktop file indicates that it supports D-Bus
194 * activation (DBusActivatable=true), a command launcher may also call the Activate()
195 * D-Bus method to trigger this signal. In this case, \a arguments will be empty.
196 *
197 * In single-window applications, the connected signal should typically
198 * raise the window.
199 *
200 * \a arguments The arguments the executable was called with,
201 * starting with the executable file name.
202 * See QCoreApplication::arguments(). This can be empty.
203 *
204 * \a workingDirectory The directory from which the executable is called.
205 * This can be empty.
206 *
207 * A typical implementation of the signal handler would be:
208 * \code
209 * if (!arguments.isEmpty()) {
210 * commandLineParser->parse(arguments); // same QCommandLineParser instance as the one used in main()
211 * handleCmdLine(workingDirectory); // shared method with main(), which uses commandLineParser to handle options and positional arguments
212 * }
213 * \endcode
214 *
215 * For GUI applications, the handler also needs to deal with any platform-specific startup ids
216 * and make sure the mainwindow is shown as well as request its activation from the window manager.
217 * For X11, KDBusService makes the id for the Startup Notification protocol available
218 * from QX11Info::nextStartupId(), if there is one.
219 * For Wayland, KDBusService provides the token for the XDG Activation protocol in the
220 * "XDG_ACTIVATION_TOKEN" environment variable and unsets it again after the signal, if there is one.
221 * The util method \c KWindowSystem::updateStartupId(QWindow *window) (since KF 5.91) takes care of that.
222 * A typical implementation in the signal handler would be:
223 * \code
224 * mainWindow->show();
225 * KWindowSystem::updateStartupId(mainWindow->windowHandle());
226 * mainWindow->raise();
227 * KWindowSystem::activateWindow(mainWindow->windowHandle());
228 * \endcode
229 *
230 * If you're using the builtin handling of \c --help and \c --version in QCommandLineParser,
231 * you should call parser.process(arguments) before creating the KDBusService instance,
232 * since parse() doesn't handle those (and exiting the already-running instance would be wrong anyway).
233 *
234 * \sa setExitValue()
235 */
236 void activateRequested(const QStringList &arguments, const QString &workingDirectory);
237
238 /*!
239 * Signals that one or more files should be opened in the application.
240 *
241 * This signal is emitted to request handling of the respective method of the D-Bus activation.
242 * For GUI applications, the signal handler also needs to deal with any platform-specific startup ids
243 * and make sure the mainwindow is shown as well as request its activation from the window manager.
244 * See documentation of activateRequested(const QStringList &arguments, const QString &)
245 * for details.
246 *
247 * \a uris The URLs of the files to open.
248 */
249 void openRequested(const QList<QUrl> &uris);
250
251 /*!
252 * Signals that an application action \a actionName should be triggered
253 * with the given \a parameter.
254 *
255 * This signal is emitted to request handling of the respective method of the D-Bus activation.
256 * For GUI applications, the signal handler also needs to deal with any platform-specific startup ids
257 * and make sure the mainwindow is shown as well as request its activation from the window manager.
258 * See documentation of activateRequested(const QStringList &arguments, const QString &)
259 * for details.
260 *
261 * See the desktop entry specification for more information about action activation.
262 */
263 void activateActionRequested(const QString &actionName, const QVariant &parameter);
264
265public Q_SLOTS:
266 /*!
267 * Manually unregister the given serviceName from D-Bus.
268 */
269 void unregister();
270
271private:
272 // fdo.Application spec
273 KDBUSADDONS_NO_EXPORT void Activate(const QVariantMap &platform_data);
274 KDBUSADDONS_NO_EXPORT void Open(const QStringList &uris, const QVariantMap &platform_data);
275 KDBUSADDONS_NO_EXPORT void ActivateAction(const QString &action_name, const QVariantList &maybeParameter, const QVariantMap &platform_data);
276 friend class KDBusServiceAdaptor;
277
278 // org.kde.KDBusService
279 KDBUSADDONS_NO_EXPORT int CommandLine(const QStringList &arguments, const QString &workingDirectory, const QVariantMap &platform_data);
280 friend class KDBusServiceExtensionsAdaptor;
281
282private:
283 std::unique_ptr<KDBusServicePrivate> const d;
284};
285
286Q_DECLARE_OPERATORS_FOR_FLAGS(KDBusService::StartupOptions)
287
288#endif /* KDBUSSERVICE_H */
289

source code of kdbusaddons/src/kdbusservice.h