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