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