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