1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtDBus module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qdbusservicewatcher.h"
41#include "qdbusconnection.h"
42#include "qdbusutil_p.h"
43
44#include <QStringList>
45
46#include <private/qobject_p.h>
47#include <private/qdbusconnection_p.h>
48
49#ifndef QT_NO_DBUS
50
51QT_BEGIN_NAMESPACE
52
53class QDBusServiceWatcherPrivate: public QObjectPrivate
54{
55 Q_DECLARE_PUBLIC(QDBusServiceWatcher)
56public:
57 QDBusServiceWatcherPrivate(const QDBusConnection &c, QDBusServiceWatcher::WatchMode wm)
58 : connection(c), watchMode(wm)
59 {
60 }
61
62 QStringList servicesWatched;
63 QDBusConnection connection;
64 QDBusServiceWatcher::WatchMode watchMode;
65
66 void _q_serviceOwnerChanged(const QString &, const QString &, const QString &);
67 void setConnection(const QStringList &services, const QDBusConnection &c, QDBusServiceWatcher::WatchMode watchMode);
68
69 void addService(const QString &service);
70 void removeService(const QString &service);
71};
72
73void QDBusServiceWatcherPrivate::_q_serviceOwnerChanged(const QString &service, const QString &oldOwner, const QString &newOwner)
74{
75 Q_Q(QDBusServiceWatcher);
76 emit q->serviceOwnerChanged(service, oldOwner, newOwner);
77 if (oldOwner.isEmpty())
78 emit q->serviceRegistered(service);
79 else if (newOwner.isEmpty())
80 emit q->serviceUnregistered(service);
81}
82
83void QDBusServiceWatcherPrivate::setConnection(const QStringList &s, const QDBusConnection &c, QDBusServiceWatcher::WatchMode wm)
84{
85 if (connection.isConnected()) {
86 // remove older rules
87 for (const QString &s : qAsConst(servicesWatched))
88 removeService(s);
89 }
90
91 connection = c;
92 watchMode = wm;
93 servicesWatched = s;
94
95 if (connection.isConnected()) {
96 // add new rules
97 for (const QString &s : qAsConst(servicesWatched))
98 addService(s);
99 }
100}
101
102void QDBusServiceWatcherPrivate::addService(const QString &service)
103{
104 QDBusConnectionPrivate *d = QDBusConnectionPrivate::d(connection);
105 if (d && d->shouldWatchService(service))
106 d->watchService(service, watchMode, q_func(), SLOT(_q_serviceOwnerChanged(QString,QString,QString)));
107}
108
109void QDBusServiceWatcherPrivate::removeService(const QString &service)
110{
111 QDBusConnectionPrivate *d = QDBusConnectionPrivate::d(connection);
112 if (d && d->shouldWatchService(service))
113 d->unwatchService(service, watchMode, q_func(), SLOT(_q_serviceOwnerChanged(QString,QString,QString)));
114}
115
116/*!
117 \class QDBusServiceWatcher
118 \since 4.6
119 \inmodule QtDBus
120
121 \brief The QDBusServiceWatcher class allows the user to watch for a bus service change.
122
123 A QDBusServiceWatcher object can be used to notify the application about
124 an ownership change of a service name on the bus. It has three watch
125 modes:
126
127 \list
128 \li Watching for service registration only.
129 \li Watching for service unregistration only.
130 \li Watching for any kind of service ownership change (the default mode).
131 \endlist
132
133 Besides being created or deleted, services may change owners without a
134 unregister/register operation happening. So the serviceRegistered()
135 and serviceUnregistered() signals may not be emitted if that
136 happens.
137
138 This class is more efficient than using the
139 QDBusConnectionInterface::serviceOwnerChanged() signal because it allows
140 one to receive only the signals for which the class is interested in.
141
142 Ending a service name with the character '*' will match all service names
143 within the specified namespace.
144
145 For example "com.example.backend1*" will match
146 \list
147 \li com.example.backend1
148 \li com.example.backend1.foo
149 \li com.example.backend1.foo.bar
150 \endlist
151 Substrings in the same domain will not be matched, i.e "com.example.backend12".
152
153 \sa QDBusConnection
154*/
155
156/*!
157 \enum QDBusServiceWatcher::WatchModeFlag
158
159 QDBusServiceWatcher supports three different watch modes, which are configured by this flag:
160
161 \value WatchForRegistration watch for service registration only, ignoring
162 any signals related to other service ownership change.
163
164 \value WatchForUnregistration watch for service unregistration only,
165 ignoring any signals related to other service ownership change.
166
167 \value WatchForOwnerChange watch for any kind of service ownership
168 change.
169*/
170
171/*!
172 \property QDBusServiceWatcher::watchMode
173
174 The \c watchMode property holds the current watch mode for this
175 QDBusServiceWatcher object. The default value for this property is
176 QDBusServiceWatcher::WatchForOwnershipChange.
177*/
178
179/*!
180 \property QDBusServiceWatcher::watchedServices
181
182 The \c servicesWatched property holds the list of services watched.
183
184 Note that modifying this list with setServicesWatched() is an expensive
185 operation. If you can, prefer to change it by way of addWatchedService()
186 and removeWatchedService().
187*/
188
189/*!
190 \fn void QDBusServiceWatcher::serviceRegistered(const QString &serviceName)
191
192 This signal is emitted whenever this object detects that the service \a
193 serviceName became available on the bus.
194
195 \sa serviceUnregistered(), serviceOwnerChanged()
196*/
197
198/*!
199 \fn void QDBusServiceWatcher::serviceUnregistered(const QString &serviceName)
200
201 This signal is emitted whenever this object detects that the service \a
202 serviceName was unregistered from the bus and is no longer available.
203
204 \sa serviceRegistered(), serviceOwnerChanged()
205*/
206
207/*!
208 \fn void QDBusServiceWatcher::serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner)
209
210 This signal is emitted whenever this object detects that there was a
211 service ownership change relating to the \a serviceName service. The \a
212 oldOwner parameter contains the old owner name and \a newOwner is the new
213 owner. Both \a oldOwner and \a newOwner are unique connection names.
214
215 Note that this signal is also emitted whenever the \a serviceName service
216 was registered or unregistered. If it was registered, \a oldOwner will
217 contain an empty string, whereas if it was unregistered, \a newOwner will
218 contain an empty string.
219
220 If you need only to find out if the service is registered or unregistered
221 only, without being notified that the ownership changed, consider using
222 the specific modes for those operations. This class is more efficient if
223 you use the more specific modes.
224
225 \sa serviceRegistered(), serviceUnregistered()
226*/
227
228/*!
229 Creates a QDBusServiceWatcher object. Note that until you set a
230 connection with setConnection(), this object will not emit any signals.
231
232 The \a parent parameter is passed to QObject to set the parent of this
233 object.
234*/
235QDBusServiceWatcher::QDBusServiceWatcher(QObject *parent)
236 : QObject(*new QDBusServiceWatcherPrivate(QDBusConnection(QString()), WatchForOwnerChange), parent)
237{
238}
239
240/*!
241 Creates a QDBusServiceWatcher object and attaches it to the \a connection
242 connection. Also, this function immediately starts watching for \a
243 watchMode changes to service \a service.
244
245 The \a parent parameter is passed to QObject to set the parent of this
246 object.
247*/
248QDBusServiceWatcher::QDBusServiceWatcher(const QString &service, const QDBusConnection &connection, WatchMode watchMode, QObject *parent)
249 : QObject(*new QDBusServiceWatcherPrivate(connection, watchMode), parent)
250{
251 d_func()->setConnection(QStringList() << service, connection, watchMode);
252}
253
254/*!
255 Destroys the QDBusServiceWatcher object and releases any resources
256 associated with it.
257*/
258QDBusServiceWatcher::~QDBusServiceWatcher()
259{
260}
261
262/*!
263 Returns the list of D-Bus services that are being watched.
264
265 \sa setWatchedServices()
266*/
267QStringList QDBusServiceWatcher::watchedServices() const
268{
269 return d_func()->servicesWatched;
270}
271
272/*!
273 Sets the list of D-Bus services being watched to be \a services.
274
275 Note that setting the entire list means removing all previous rules for
276 watching services and adding new ones. This is an expensive operation and
277 should be avoided, if possible. Instead, use addWatchedService() and
278 removeWatchedService() if you can to manipulate entries in the list.
279*/
280void QDBusServiceWatcher::setWatchedServices(const QStringList &services)
281{
282 Q_D(QDBusServiceWatcher);
283 if (services == d->servicesWatched)
284 return;
285 d->setConnection(services, d->connection, d->watchMode);
286}
287
288/*!
289 Adds \a newService to the list of services to be watched by this object.
290 This function is more efficient than setWatchedServices() and should be
291 used whenever possible to add services.
292*/
293void QDBusServiceWatcher::addWatchedService(const QString &newService)
294{
295 Q_D(QDBusServiceWatcher);
296 if (d->servicesWatched.contains(newService))
297 return;
298 d->addService(newService);
299 d->servicesWatched << newService;
300}
301
302/*!
303 Removes the \a service from the list of services being watched by this
304 object. Note that D-Bus notifications are asynchronous, so there may
305 still be signals pending delivery about \a service. Those signals will
306 still be emitted whenever the D-Bus messages are processed.
307
308 This function returns \c true if any services were removed.
309*/
310bool QDBusServiceWatcher::removeWatchedService(const QString &service)
311{
312 Q_D(QDBusServiceWatcher);
313 d->removeService(service);
314 return d->servicesWatched.removeOne(service);
315}
316
317QDBusServiceWatcher::WatchMode QDBusServiceWatcher::watchMode() const
318{
319 return d_func()->watchMode;
320}
321
322void QDBusServiceWatcher::setWatchMode(WatchMode mode)
323{
324 Q_D(QDBusServiceWatcher);
325 if (mode == d->watchMode)
326 return;
327 d->setConnection(d->servicesWatched, d->connection, mode);
328}
329
330/*!
331 Returns the QDBusConnection that this object is attached to.
332
333 \sa setConnection()
334*/
335QDBusConnection QDBusServiceWatcher::connection() const
336{
337 return d_func()->connection;
338}
339
340/*!
341 Sets the D-Bus connection that this object is attached to be \a
342 connection. All services watched will be transferred to this connection.
343
344 Note that QDBusConnection objects are reference counted:
345 QDBusServiceWatcher will keep a reference for this connection while it
346 exists. The connection is not closed until the reference count drops to
347 zero, so this will ensure that any notifications are received while this
348 QDBusServiceWatcher object exists.
349
350 \sa connection()
351*/
352void QDBusServiceWatcher::setConnection(const QDBusConnection &connection)
353{
354 Q_D(QDBusServiceWatcher);
355 if (connection.name() == d->connection.name())
356 return;
357 d->setConnection(d->servicesWatched, connection, d->watchMode);
358}
359
360QT_END_NAMESPACE
361
362#endif // QT_NO_DBUS
363
364#include "moc_qdbusservicewatcher.cpp"
365