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 |
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 | */ |
79 | class KDBUSADDONS_EXPORT KDBusService : public QObject |
80 | { |
81 | Q_OBJECT |
82 | |
83 | public: |
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 | |
183 | Q_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 ¶meter); |
264 | |
265 | public Q_SLOTS: |
266 | /*! |
267 | * Manually unregister the given serviceName from D-Bus. |
268 | */ |
269 | void unregister(); |
270 | |
271 | private: |
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 | |
282 | private: |
283 | std::unique_ptr<KDBusServicePrivate> const d; |
284 | }; |
285 | |
286 | Q_DECLARE_OPERATORS_FOR_FLAGS(KDBusService::StartupOptions) |
287 | |
288 | #endif /* KDBUSSERVICE_H */ |
289 | |