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 <qdebug.h> |
43 | #include <qcoreapplication.h> |
44 | #include <qstringlist.h> |
45 | #include <qthread.h> |
46 | |
47 | #include "qdbusconnection.h" |
48 | #include "qdbusconnectioninterface.h" |
49 | #include "qdbuserror.h" |
50 | #include "qdbusmessage.h" |
51 | #include "qdbusmessage_p.h" |
52 | #include "qdbusconnection_p.h" |
53 | #include "qdbusinterface_p.h" |
54 | #include "qdbusutil_p.h" |
55 | #include "qdbusconnectionmanager_p.h" |
56 | #include "qdbuspendingcall_p.h" |
57 | |
58 | #include "qdbusthreaddebug_p.h" |
59 | |
60 | #ifndef QT_NO_DBUS |
61 | |
62 | QT_BEGIN_NAMESPACE |
63 | |
64 | Q_GLOBAL_STATIC(QDBusConnectionManager, _q_manager) |
65 | |
66 | QDBusConnectionPrivate *QDBusConnectionManager::sender() const |
67 | { |
68 | QMutexLocker locker(&senderMutex); |
69 | return connection(senderName); |
70 | } |
71 | |
72 | void QDBusConnectionManager::setSender(const QDBusConnectionPrivate *s) |
73 | { |
74 | QMutexLocker locker(&senderMutex); |
75 | senderName = (s ? s->name : QString()); |
76 | } |
77 | |
78 | QDBusConnectionPrivate *QDBusConnectionManager::connection(const QString &name) const |
79 | { |
80 | return connectionHash.value(name, 0); |
81 | } |
82 | |
83 | void QDBusConnectionManager::removeConnection(const QString &name) |
84 | { |
85 | QDBusConnectionPrivate *d = 0; |
86 | d = connectionHash.take(name); |
87 | if (d && !d->ref.deref()) |
88 | d->deleteYourself(); |
89 | |
90 | // Static objects may be keeping the connection open. |
91 | // However, it is harmless to have outstanding references to a connection that is |
92 | // closing as long as those references will be soon dropped without being used. |
93 | |
94 | // ### Output a warning if connections are being used after they have been removed. |
95 | } |
96 | |
97 | QDBusConnectionManager::~QDBusConnectionManager() |
98 | { |
99 | for (QHash<QString, QDBusConnectionPrivate *>::const_iterator it = connectionHash.constBegin(); |
100 | it != connectionHash.constEnd(); ++it) { |
101 | QDBusConnectionPrivate *d = it.value(); |
102 | if (!d->ref.deref()) |
103 | d->deleteYourself(); |
104 | else |
105 | d->closeConnection(); |
106 | } |
107 | connectionHash.clear(); |
108 | } |
109 | |
110 | QDBusConnectionManager* QDBusConnectionManager::instance() |
111 | { |
112 | return _q_manager(); |
113 | } |
114 | |
115 | Q_DBUS_EXPORT void qDBusBindToApplication(); |
116 | void qDBusBindToApplication() |
117 | { |
118 | } |
119 | |
120 | void QDBusConnectionManager::setConnection(const QString &name, QDBusConnectionPrivate *c) |
121 | { |
122 | connectionHash[name] = c; |
123 | c->name = name; |
124 | } |
125 | |
126 | /*! |
127 | \fn QDBusConnection &QDBusConnection::sessionBus() |
128 | \relates QDBusConnection |
129 | |
130 | Returns a QDBusConnection object opened with the session bus. The object |
131 | reference returned by this function is valid until the application terminates, |
132 | at which point the connection will be closed and the object deleted. |
133 | */ |
134 | /*! |
135 | \fn QDBusConnection &QDBusConnection::systemBus() |
136 | \relates QDBusConnection |
137 | |
138 | Returns a QDBusConnection object opened with the system bus. The object reference returned |
139 | by this function is valid until the QCoreApplication's destructor is run, when the |
140 | connection will be closed and the object, deleted. |
141 | */ |
142 | |
143 | /*! |
144 | \class QDBusConnection |
145 | \inmodule QtDBus |
146 | \since 4.2 |
147 | |
148 | \brief The QDBusConnection class represents a connection to the D-Bus bus daemon. |
149 | |
150 | This class is the initial point in a D-Bus session. Using it, you |
151 | can get access to remote objects, interfaces; connect remote |
152 | signals to your object's slots; register objects, etc. |
153 | |
154 | D-Bus connections are created using the connectToBus() function, |
155 | which opens a connection to the server daemon and does the initial |
156 | handshaking, associating that connection with a name. Further |
157 | attempts to connect using the same name will return the same |
158 | connection. |
159 | |
160 | The connection is then torn down using the disconnectFromBus() |
161 | function. |
162 | |
163 | Once disconnected, calling connectToBus() will not reestablish a |
164 | connection, you must create a new QDBusConnection instance. |
165 | |
166 | As a convenience for the two most common connection types, the |
167 | sessionBus() and systemBus() functions return open connections to |
168 | the session server daemon and the system server daemon, |
169 | respectively. Those connections are opened when first used and are |
170 | closed when the QCoreApplication destructor is run. |
171 | |
172 | D-Bus also supports peer-to-peer connections, without the need for |
173 | a bus server daemon. Using this facility, two applications can |
174 | talk to each other and exchange messages. This can be achieved by |
175 | passing an address to connectToBus() function, which was opened by |
176 | another D-Bus application using QDBusServer. |
177 | */ |
178 | |
179 | /*! |
180 | \enum QDBusConnection::BusType |
181 | Specifies the type of the bus connection. The valid bus types are: |
182 | |
183 | \value SessionBus the session bus, associated with the running desktop session |
184 | \value SystemBus the system bus, used to communicate with system-wide processes |
185 | \value ActivationBus the activation bus, the "alias" for the bus that started the |
186 | service |
187 | |
188 | On the Session Bus, one can find other applications by the same user that are sharing the same |
189 | desktop session (hence the name). On the System Bus, however, processes shared for the whole |
190 | system are usually found. |
191 | */ |
192 | |
193 | /*! |
194 | \enum QDBusConnection::RegisterOption |
195 | Specifies the options for registering objects with the connection. The possible values are: |
196 | |
197 | \value ExportAdaptors export the contents of adaptors found in this object |
198 | |
199 | \value ExportScriptableSlots export this object's scriptable slots |
200 | \value ExportScriptableSignals export this object's scriptable signals |
201 | \value ExportScriptableProperties export this object's scriptable properties |
202 | \value ExportScriptableInvokables export this object's scriptable invokables |
203 | \value ExportScriptableContents shorthand form for ExportScriptableSlots | |
204 | ExportScriptableSignals | |
205 | ExportScriptableProperties |
206 | |
207 | \value ExportNonScriptableSlots export this object's non-scriptable slots |
208 | \value ExportNonScriptableSignals export this object's non-scriptable signals |
209 | \value ExportNonScriptableProperties export this object's non-scriptable properties |
210 | \value ExportNonScriptableInvokables export this object's non-scriptable invokables |
211 | \value ExportNonScriptableContents shorthand form for ExportNonScriptableSlots | |
212 | ExportNonScriptableSignals | |
213 | ExportNonScriptableProperties |
214 | |
215 | \value ExportAllSlots export all of this object's slots |
216 | \value ExportAllSignals export all of this object's signals |
217 | \value ExportAllProperties export all of this object's properties |
218 | \value ExportAllInvokables export all of this object's invokables |
219 | \value ExportAllContents export all of this object's contents |
220 | \value ExportChildObjects export this object's child objects |
221 | |
222 | \sa registerObject(), QDBusAbstractAdaptor, {usingadaptors.html}{Using adaptors} |
223 | */ |
224 | |
225 | /*! |
226 | \internal |
227 | \since 4.8 |
228 | \enum QDBusConnection::VirtualObjectRegisterOption |
229 | Specifies the options for registering virtual objects with the connection. The possible values are: |
230 | |
231 | \value SingleNode register a virtual object to handle one path only |
232 | \value SubPath register a virtual object so that it handles all sub paths |
233 | |
234 | \sa registerVirtualObject(), QDBusVirtualObject |
235 | */ |
236 | |
237 | /*! |
238 | \enum QDBusConnection::UnregisterMode |
239 | The mode for unregistering an object path: |
240 | |
241 | \value UnregisterNode unregister this node only: do not unregister child objects |
242 | \value UnregisterTree unregister this node and all its sub-tree |
243 | |
244 | Note, however, if this object was registered with the ExportChildObjects option, UnregisterNode |
245 | will unregister the child objects too. |
246 | */ |
247 | |
248 | /*! |
249 | \since 4.8 |
250 | \enum QDBusConnection::ConnectionCapability |
251 | |
252 | This enum describes the available capabilities for a D-Bus connection. |
253 | |
254 | \value UnixFileDescriptorPassing enables passing of Unix file descriptors to other processes |
255 | (see QDBusUnixFileDescriptor) |
256 | |
257 | \sa connectionCapabilities() |
258 | */ |
259 | |
260 | /*! |
261 | Creates a QDBusConnection object attached to the connection with name \a name. |
262 | |
263 | This does not open the connection. You have to call connectToBus() to open it. |
264 | */ |
265 | QDBusConnection::QDBusConnection(const QString &name) |
266 | { |
267 | if (name.isEmpty()) { |
268 | d = 0; |
269 | } else { |
270 | QMutexLocker locker(&_q_manager()->mutex); |
271 | d = _q_manager()->connection(name); |
272 | if (d) |
273 | d->ref.ref(); |
274 | } |
275 | } |
276 | |
277 | /*! |
278 | Creates a copy of the \a other connection. |
279 | */ |
280 | QDBusConnection::QDBusConnection(const QDBusConnection &other) |
281 | { |
282 | d = other.d; |
283 | if (d) |
284 | d->ref.ref(); |
285 | } |
286 | |
287 | /*! |
288 | \internal |
289 | Creates a connection object with the given \a dd as private object. |
290 | */ |
291 | QDBusConnection::QDBusConnection(QDBusConnectionPrivate *dd) |
292 | { |
293 | d = dd; |
294 | if (d) |
295 | d->ref.ref(); |
296 | } |
297 | |
298 | /*! |
299 | Disposes of this object. This does not close the connection: you |
300 | have to call disconnectFromBus() to do that. |
301 | */ |
302 | QDBusConnection::~QDBusConnection() |
303 | { |
304 | if (d && !d->ref.deref()) |
305 | d->deleteYourself(); |
306 | } |
307 | |
308 | /*! |
309 | Creates a copy of the connection \a other in this object. Note |
310 | that the connection this object referenced before the copy, is not |
311 | spontaneously disconnected. |
312 | |
313 | \sa disconnectFromBus() |
314 | */ |
315 | QDBusConnection &QDBusConnection::operator=(const QDBusConnection &other) |
316 | { |
317 | if (other.d) |
318 | other.d->ref.ref(); |
319 | if (d && !d->ref.deref()) |
320 | d->deleteYourself(); |
321 | d = other.d; |
322 | return *this; |
323 | } |
324 | |
325 | /*! |
326 | Opens a connection of type \a type to one of the known busses and |
327 | associate with it the connection name \a name. Returns a |
328 | QDBusConnection object associated with that connection. |
329 | */ |
330 | QDBusConnection QDBusConnection::connectToBus(BusType type, const QString &name) |
331 | { |
332 | // Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection::addConnection", |
333 | // "Cannot create connection without a Q[Core]Application instance"); |
334 | if (!qdbus_loadLibDBus()) { |
335 | QDBusConnectionPrivate *d = 0; |
336 | return QDBusConnection(d); |
337 | } |
338 | |
339 | QMutexLocker locker(&_q_manager()->mutex); |
340 | |
341 | QDBusConnectionPrivate *d = _q_manager()->connection(name); |
342 | if (d || name.isEmpty()) |
343 | return QDBusConnection(d); |
344 | |
345 | d = new QDBusConnectionPrivate; |
346 | DBusConnection *c = 0; |
347 | QDBusErrorInternal error; |
348 | switch (type) { |
349 | case SystemBus: |
350 | c = q_dbus_bus_get_private(DBUS_BUS_SYSTEM, error); |
351 | break; |
352 | case SessionBus: |
353 | c = q_dbus_bus_get_private(DBUS_BUS_SESSION, error); |
354 | break; |
355 | case ActivationBus: |
356 | c = q_dbus_bus_get_private(DBUS_BUS_STARTER, error); |
357 | break; |
358 | } |
359 | d->setConnection(c, error); //setConnection does the error handling for us |
360 | |
361 | _q_manager()->setConnection(name, d); |
362 | |
363 | QDBusConnection retval(d); |
364 | |
365 | // create the bus service |
366 | // will lock in QDBusConnectionPrivate::connectRelay() |
367 | d->setBusService(retval); |
368 | |
369 | return retval; |
370 | } |
371 | |
372 | /*! |
373 | Opens a connection to a private bus on address \a address and associate with it the |
374 | connection name \a name. Returns a QDBusConnection object associated with that connection. |
375 | */ |
376 | QDBusConnection QDBusConnection::connectToBus(const QString &address, |
377 | const QString &name) |
378 | { |
379 | // Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection::addConnection", |
380 | // "Cannot create connection without a Q[Core]Application instance"); |
381 | if (!qdbus_loadLibDBus()) { |
382 | QDBusConnectionPrivate *d = 0; |
383 | return QDBusConnection(d); |
384 | } |
385 | |
386 | QMutexLocker locker(&_q_manager()->mutex); |
387 | |
388 | QDBusConnectionPrivate *d = _q_manager()->connection(name); |
389 | if (d || name.isEmpty()) |
390 | return QDBusConnection(d); |
391 | |
392 | d = new QDBusConnectionPrivate; |
393 | // setConnection does the error handling for us |
394 | QDBusErrorInternal error; |
395 | DBusConnection *c = q_dbus_connection_open_private(address.toUtf8().constData(), error); |
396 | if (c) { |
397 | if (!q_dbus_bus_register(c, error)) { |
398 | q_dbus_connection_unref(c); |
399 | c = 0; |
400 | } |
401 | } |
402 | d->setConnection(c, error); |
403 | _q_manager()->setConnection(name, d); |
404 | |
405 | QDBusConnection retval(d); |
406 | |
407 | // create the bus service |
408 | // will lock in QDBusConnectionPrivate::connectRelay() |
409 | d->setBusService(retval); |
410 | |
411 | return retval; |
412 | } |
413 | /*! |
414 | \since 4.8 |
415 | |
416 | Opens a peer-to-peer connection on address \a address and associate with it the |
417 | connection name \a name. Returns a QDBusConnection object associated with that connection. |
418 | */ |
419 | QDBusConnection QDBusConnection::connectToPeer(const QString &address, |
420 | const QString &name) |
421 | { |
422 | // Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection::addConnection", |
423 | // "Cannot create connection without a Q[Core]Application instance"); |
424 | if (!qdbus_loadLibDBus()) { |
425 | QDBusConnectionPrivate *d = 0; |
426 | return QDBusConnection(d); |
427 | } |
428 | |
429 | QMutexLocker locker(&_q_manager()->mutex); |
430 | |
431 | QDBusConnectionPrivate *d = _q_manager()->connection(name); |
432 | if (d || name.isEmpty()) |
433 | return QDBusConnection(d); |
434 | |
435 | d = new QDBusConnectionPrivate; |
436 | // setPeer does the error handling for us |
437 | QDBusErrorInternal error; |
438 | DBusConnection *c = q_dbus_connection_open_private(address.toUtf8().constData(), error); |
439 | |
440 | d->setPeer(c, error); |
441 | _q_manager()->setConnection(name, d); |
442 | |
443 | QDBusConnection retval(d); |
444 | |
445 | return retval; |
446 | } |
447 | |
448 | /*! |
449 | Closes the bus connection of name \a name. |
450 | |
451 | Note that if there are still QDBusConnection objects associated |
452 | with the same connection, the connection will not be closed until |
453 | all references are dropped. However, no further references can be |
454 | created using the QDBusConnection constructor. |
455 | */ |
456 | void QDBusConnection::disconnectFromBus(const QString &name) |
457 | { |
458 | if (_q_manager()) { |
459 | QMutexLocker locker(&_q_manager()->mutex); |
460 | QDBusConnectionPrivate *d = _q_manager()->connection(name); |
461 | if (d && d->mode != QDBusConnectionPrivate::ClientMode) |
462 | return; |
463 | _q_manager()->removeConnection(name); |
464 | } |
465 | } |
466 | |
467 | /*! |
468 | \since 4.8 |
469 | |
470 | Closes the peer connection of name \a name. |
471 | |
472 | Note that if there are still QDBusConnection objects associated |
473 | with the same connection, the connection will not be closed until |
474 | all references are dropped. However, no further references can be |
475 | created using the QDBusConnection constructor. |
476 | */ |
477 | void QDBusConnection::disconnectFromPeer(const QString &name) |
478 | { |
479 | if (_q_manager()) { |
480 | QMutexLocker locker(&_q_manager()->mutex); |
481 | QDBusConnectionPrivate *d = _q_manager()->connection(name); |
482 | if (d && d->mode != QDBusConnectionPrivate::PeerMode) |
483 | return; |
484 | _q_manager()->removeConnection(name); |
485 | } |
486 | } |
487 | |
488 | /*! |
489 | Sends the \a message over this connection, without waiting for a |
490 | reply. This is suitable for errors, signals, and return values as |
491 | well as calls whose return values are not necessary. |
492 | |
493 | Returns true if the message was queued successfully, false otherwise. |
494 | */ |
495 | bool QDBusConnection::send(const QDBusMessage &message) const |
496 | { |
497 | if (!d || !d->connection) { |
498 | QDBusError err = QDBusError(QDBusError::Disconnected, |
499 | QLatin1String("Not connected to D-BUS server" )); |
500 | if (d) |
501 | d->lastError = err; |
502 | return false; |
503 | } |
504 | return d->send(message) != 0; |
505 | } |
506 | |
507 | /*! |
508 | Sends the \a message over this connection and returns immediately. |
509 | When the reply is received, the method \a returnMethod is called in |
510 | the \a receiver object. If an error occurs, the method \a errorMethod |
511 | will be called instead. |
512 | |
513 | If no reply is received within \a timeout milliseconds, an automatic |
514 | error will be delivered indicating the expiration of the call. |
515 | The default \a timeout is -1, which will be replaced with an |
516 | implementation-defined value that is suitable for inter-process |
517 | communications (generally, 25 seconds). |
518 | |
519 | This function is suitable for method calls only. It is guaranteed |
520 | that the slot will be called exactly once with the reply, as long |
521 | as the parameter types match and no error occurs. |
522 | |
523 | Returns true if the message was sent, or false if the message could |
524 | not be sent. |
525 | */ |
526 | bool QDBusConnection::callWithCallback(const QDBusMessage &message, QObject *receiver, |
527 | const char *returnMethod, const char *errorMethod, |
528 | int timeout) const |
529 | { |
530 | if (!d || !d->connection) { |
531 | QDBusError err = QDBusError(QDBusError::Disconnected, |
532 | QLatin1String("Not connected to D-BUS server" )); |
533 | if (d) |
534 | d->lastError = err; |
535 | return false; |
536 | } |
537 | return d->sendWithReplyAsync(message, receiver, returnMethod, errorMethod, timeout) != 0; |
538 | } |
539 | |
540 | /*! |
541 | \overload |
542 | \deprecated |
543 | Sends the \a message over this connection and returns immediately. |
544 | When the reply is received, the method \a returnMethod is called in |
545 | the \a receiver object. |
546 | |
547 | This function is suitable for method calls only. It is guaranteed |
548 | that the slot will be called exactly once with the reply, as long |
549 | as the parameter types match and no error occurs. |
550 | |
551 | This function is dangerous because it cannot report errors, including |
552 | the expiration of the timeout. |
553 | |
554 | Returns true if the message was sent, or false if the message could |
555 | not be sent. |
556 | */ |
557 | bool QDBusConnection::callWithCallback(const QDBusMessage &message, QObject *receiver, |
558 | const char *returnMethod, int timeout) const |
559 | { |
560 | return callWithCallback(message, receiver, returnMethod, 0, timeout); |
561 | } |
562 | |
563 | /*! |
564 | Sends the \a message over this connection and blocks, waiting for |
565 | a reply, for at most \a timeout milliseconds. This function is |
566 | suitable for method calls only. It returns the reply message as |
567 | its return value, which will be either of type |
568 | QDBusMessage::ReplyMessage or QDBusMessage::ErrorMessage. |
569 | |
570 | If no reply is received within \a timeout milliseconds, an automatic |
571 | error will be delivered indicating the expiration of the call. |
572 | The default \a timeout is -1, which will be replaced with an |
573 | implementation-defined value that is suitable for inter-process |
574 | communications (generally, 25 seconds). |
575 | |
576 | See the QDBusInterface::call() function for a more friendly way |
577 | of placing calls. |
578 | |
579 | \warning If \a mode is QDBus::BlockWithGui, this function will |
580 | reenter the Qt event loop in order to wait for the |
581 | reply. During the wait, it may deliver signals and other |
582 | method calls to your application. Therefore, it must be |
583 | prepared to handle a reentrancy whenever a call is |
584 | placed with call(). |
585 | */ |
586 | QDBusMessage QDBusConnection::call(const QDBusMessage &message, QDBus::CallMode mode, int timeout) const |
587 | { |
588 | if (!d || !d->connection) { |
589 | QDBusError err = QDBusError(QDBusError::Disconnected, |
590 | QLatin1String("Not connected to D-Bus server" )); |
591 | if (d) |
592 | d->lastError = err; |
593 | |
594 | return QDBusMessage::createError(err); |
595 | } |
596 | |
597 | if (mode != QDBus::NoBlock) |
598 | return d->sendWithReply(message, mode, timeout); |
599 | |
600 | d->send(message); |
601 | QDBusMessage retval; |
602 | retval << QVariant(); // add one argument (to avoid .at(0) problems) |
603 | return retval; |
604 | } |
605 | |
606 | /*! |
607 | \since 4.5 |
608 | Sends the \a message over this connection and returns |
609 | immediately. This function is suitable for method calls only. It |
610 | returns an object of type QDBusPendingCall which can be used to |
611 | track the status of the reply. |
612 | |
613 | If no reply is received within \a timeout milliseconds, an automatic |
614 | error will be delivered indicating the expiration of the call. The |
615 | default \a timeout is -1, which will be replaced with an |
616 | implementation-defined value that is suitable for inter-process |
617 | communications (generally, 25 seconds). This timeout is also the |
618 | upper limit for waiting in QDBusPendingCall::waitForFinished(). |
619 | |
620 | See the QDBusInterface::asyncCall() function for a more friendly way |
621 | of placing calls. |
622 | */ |
623 | QDBusPendingCall QDBusConnection::asyncCall(const QDBusMessage &message, int timeout) const |
624 | { |
625 | if (!d || !d->connection) { |
626 | return QDBusPendingCall(0); // null pointer -> disconnected |
627 | } |
628 | |
629 | QDBusPendingCallPrivate *priv = d->sendWithReplyAsync(message, 0, 0, 0, timeout); |
630 | return QDBusPendingCall(priv); |
631 | } |
632 | |
633 | /*! |
634 | Connects the signal specified by the \a service, \a path, \a interface and \a name parameters to |
635 | the slot \a slot in object \a receiver. The arguments \a service and \a path can be empty, |
636 | denoting a connection to any signal of the (\a interface, \a name) pair, from any remote |
637 | application. |
638 | |
639 | Returns true if the connection was successful. |
640 | |
641 | \warning The signal will only be delivered to the slot if the parameters match. This verification |
642 | can be done only when the signal is received, not at connection time. |
643 | */ |
644 | bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface, |
645 | const QString &name, QObject *receiver, const char *slot) |
646 | { |
647 | return connect(service, path, interface, name, QStringList(), QString(), receiver, slot); |
648 | } |
649 | |
650 | /*! |
651 | \overload |
652 | |
653 | Connects the signal to the slot \a slot in object \a |
654 | receiver. Unlike the previous connect() overload, this function |
655 | allows one to specify the parameter signature to be connected |
656 | using the \a signature variable. The function will then verify |
657 | that this signature can be delivered to the slot specified by \a |
658 | slot and return false otherwise. |
659 | |
660 | Returns true if the connection was successful. |
661 | |
662 | \note This function verifies that the signal signature matches the |
663 | slot's parameters, but it does not verify that the actual |
664 | signal exists with the given signature in the remote |
665 | service. |
666 | */ |
667 | bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface, |
668 | const QString &name, const QString &signature, |
669 | QObject *receiver, const char *slot) |
670 | { |
671 | return connect(service, path, interface, name, QStringList(), signature, receiver, slot); |
672 | } |
673 | |
674 | /*! |
675 | \overload |
676 | \since 4.6 |
677 | |
678 | Connects the signal to the slot \a slot in object \a |
679 | receiver. Unlike the previous connect() overload, this function |
680 | allows one to specify the parameter signature to be connected |
681 | using the \a signature variable. The function will then verify |
682 | that this signature can be delivered to the slot specified by \a |
683 | slot and return false otherwise. |
684 | |
685 | The \a argumentMatch parameter lists the string parameters to be matched, |
686 | in sequential order. Note that, to match an empty string, you need to |
687 | pass a QString that is empty but not null (i.e., QString("")). A null |
688 | QString skips matching at that position. |
689 | |
690 | Returns true if the connection was successful. |
691 | |
692 | \note This function verifies that the signal signature matches the |
693 | slot's parameters, but it does not verify that the actual |
694 | signal exists with the given signature in the remote |
695 | service. |
696 | */ |
697 | bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface, |
698 | const QString &name, const QStringList &argumentMatch, const QString &signature, |
699 | QObject *receiver, const char *slot) |
700 | { |
701 | |
702 | if (!receiver || !slot || !d || !d->connection) |
703 | return false; |
704 | if (interface.isEmpty() && name.isEmpty()) |
705 | return false; |
706 | if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) { |
707 | #ifndef QT_NO_DEBUG |
708 | qWarning("QDBusConnection::connect: interface name '%s' is not valid" , interface.toLatin1().constData()); |
709 | #endif |
710 | return false; |
711 | } |
712 | if (!service.isEmpty() && !QDBusUtil::isValidBusName(service)) { |
713 | #ifndef QT_NO_DEBUG |
714 | qWarning("QDBusConnection::connect: service name '%s' is not valid" , service.toLatin1().constData()); |
715 | #endif |
716 | return false; |
717 | } |
718 | if (!path.isEmpty() && !QDBusUtil::isValidObjectPath(path)) { |
719 | #ifndef QT_NO_DEBUG |
720 | qWarning("QDBusConnection::connect: object path '%s' is not valid" , path.toLatin1().constData()); |
721 | #endif |
722 | return false; |
723 | } |
724 | |
725 | QDBusWriteLocker locker(ConnectAction, d); |
726 | return d->connectSignal(service, path, interface, name, argumentMatch, signature, receiver, slot); |
727 | } |
728 | |
729 | /*! |
730 | Disconnects the signal specified by the \a service, \a path, \a interface |
731 | and \a name parameters from the slot \a slot in object \a receiver. The |
732 | arguments must be the same as passed to the connect() function. |
733 | |
734 | Returns true if the disconnection was successful. |
735 | */ |
736 | bool QDBusConnection::disconnect(const QString &service, const QString &path, const QString &interface, |
737 | const QString &name, QObject *receiver, const char *slot) |
738 | { |
739 | return disconnect(service, path, interface, name, QStringList(), QString(), receiver, slot); |
740 | } |
741 | |
742 | /*! |
743 | \overload |
744 | |
745 | Disconnects the signal specified by the \a service, \a path, \a |
746 | interface, \a name, and \a signature parameters from the slot \a slot in |
747 | object \a receiver. The arguments must be the same as passed to the |
748 | connect() function. |
749 | |
750 | Returns true if the disconnection was successful. |
751 | */ |
752 | bool QDBusConnection::disconnect(const QString &service, const QString &path, const QString& interface, |
753 | const QString &name, const QString &signature, |
754 | QObject *receiver, const char *slot) |
755 | { |
756 | return disconnect(service, path, interface, name, QStringList(), signature, receiver, slot); |
757 | } |
758 | |
759 | /*! |
760 | \overload |
761 | \since 4.6 |
762 | |
763 | Disconnects the signal specified by the \a service, \a path, \a |
764 | interface, \a name, \a argumentMatch, and \a signature parameters from |
765 | the slot \a slot in object \a receiver. The arguments must be the same as |
766 | passed to the connect() function. |
767 | |
768 | Returns true if the disconnection was successful. |
769 | */ |
770 | bool QDBusConnection::disconnect(const QString &service, const QString &path, const QString& interface, |
771 | const QString &name, const QStringList &argumentMatch, const QString &signature, |
772 | QObject *receiver, const char *slot) |
773 | { |
774 | if (!receiver || !slot || !d || !d->connection) |
775 | return false; |
776 | if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) |
777 | return false; |
778 | if (interface.isEmpty() && name.isEmpty()) |
779 | return false; |
780 | |
781 | QDBusWriteLocker locker(DisconnectAction, d); |
782 | return d->disconnectSignal(service, path, interface, name, argumentMatch, signature, receiver, slot); |
783 | } |
784 | |
785 | /*! |
786 | Registers the object \a object at path \a path and returns true if |
787 | the registration was successful. The \a options parameter |
788 | specifies how much of the object \a object will be exposed through |
789 | D-Bus. |
790 | |
791 | This function does not replace existing objects: if there is already an object registered at |
792 | path \a path, this function will return false. Use unregisterObject() to unregister it first. |
793 | |
794 | You cannot register an object as a child object of an object that |
795 | was registered with QDBusConnection::ExportChildObjects. |
796 | */ |
797 | bool QDBusConnection::registerObject(const QString &path, QObject *object, RegisterOptions options) |
798 | { |
799 | Q_ASSERT_X(QDBusUtil::isValidObjectPath(path), "QDBusConnection::registerObject" , |
800 | "Invalid object path given" ); |
801 | if (!d || !d->connection || !object || !options || !QDBusUtil::isValidObjectPath(path)) |
802 | return false; |
803 | |
804 | QStringList pathComponents = path.split(QLatin1Char('/')); |
805 | if (pathComponents.last().isEmpty()) |
806 | pathComponents.removeLast(); |
807 | QDBusWriteLocker locker(RegisterObjectAction, d); |
808 | |
809 | // lower-bound search for where this object should enter in the tree |
810 | QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode; |
811 | int i = 1; |
812 | while (node) { |
813 | if (pathComponents.count() == i) { |
814 | // this node exists |
815 | // consider it free if there's no object here and the user is not trying to |
816 | // replace the object sub-tree |
817 | if (node->obj) |
818 | return false; |
819 | |
820 | if (options & QDBusConnectionPrivate::VirtualObject) { |
821 | // technically the check for children needs to go even deeper |
822 | if (options & SubPath) { |
823 | foreach (const QDBusConnectionPrivate::ObjectTreeNode &child, node->children) { |
824 | if (child.obj) |
825 | return false; |
826 | } |
827 | } |
828 | } else { |
829 | if ((options & ExportChildObjects && !node->children.isEmpty())) |
830 | return false; |
831 | } |
832 | // we can add the object here |
833 | node->obj = object; |
834 | node->flags = options; |
835 | |
836 | d->registerObject(node); |
837 | //qDebug("REGISTERED FOR %s", path.toLocal8Bit().constData()); |
838 | return true; |
839 | } |
840 | |
841 | // if a virtual object occupies this path, return false |
842 | if (node->obj && (node->flags & QDBusConnectionPrivate::VirtualObject) && (node->flags & QDBusConnection::SubPath)) { |
843 | qDebug("Cannot register object at %s because QDBusVirtualObject handles all sub-paths." , |
844 | qPrintable(path)); |
845 | return false; |
846 | } |
847 | |
848 | // find the position where we'd insert the node |
849 | QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = |
850 | qLowerBound(node->children.begin(), node->children.end(), pathComponents.at(i)); |
851 | if (it != node->children.end() && it->name == pathComponents.at(i)) { |
852 | // match: this node exists |
853 | node = it; |
854 | |
855 | // are we allowed to go deeper? |
856 | if (node->flags & ExportChildObjects) { |
857 | // we're not |
858 | qDebug("Cannot register object at %s because %s exports its own child objects" , |
859 | qPrintable(path), qPrintable(pathComponents.at(i))); |
860 | return false; |
861 | } |
862 | } else { |
863 | // add entry |
864 | node = node->children.insert(it, pathComponents.at(i)); |
865 | } |
866 | |
867 | // iterate |
868 | ++i; |
869 | } |
870 | |
871 | Q_ASSERT_X(false, "QDBusConnection::registerObject" , "The impossible happened" ); |
872 | return false; |
873 | } |
874 | |
875 | /*! |
876 | \internal |
877 | \since 4.8 |
878 | Registers a QDBusTreeNode for a path. It can handle a path including all child paths, thus |
879 | handling multiple DBus nodes. |
880 | |
881 | To unregister a QDBusTreeNode use the unregisterObject() function with its path. |
882 | */ |
883 | bool QDBusConnection::registerVirtualObject(const QString &path, QDBusVirtualObject *treeNode, |
884 | VirtualObjectRegisterOption options) |
885 | { |
886 | int opts = options | QDBusConnectionPrivate::VirtualObject; |
887 | return registerObject(path, (QObject*) treeNode, (RegisterOptions) opts); |
888 | } |
889 | |
890 | /*! |
891 | Unregisters an object that was registered with the registerObject() at the object path given by |
892 | \a path and, if \a mode is QDBusConnection::UnregisterTree, all of its sub-objects too. |
893 | |
894 | Note that you cannot unregister objects that were not registered with registerObject(). |
895 | */ |
896 | void QDBusConnection::unregisterObject(const QString &path, UnregisterMode mode) |
897 | { |
898 | if (!d || !d->connection || !QDBusUtil::isValidObjectPath(path)) |
899 | return; |
900 | |
901 | QStringList pathComponents = path.split(QLatin1Char('/')); |
902 | QDBusWriteLocker locker(UnregisterObjectAction, d); |
903 | QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode; |
904 | int i = 1; |
905 | |
906 | // find the object |
907 | while (node) { |
908 | if (pathComponents.count() == i || !path.compare(QLatin1String("/" ))) { |
909 | // found it |
910 | node->obj = 0; |
911 | node->flags = 0; |
912 | |
913 | if (mode == UnregisterTree) { |
914 | // clear the sub-tree as well |
915 | node->children.clear(); // can't disconnect the objects because we really don't know if they can |
916 | // be found somewhere else in the path too |
917 | } |
918 | |
919 | return; |
920 | } |
921 | |
922 | QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = |
923 | qLowerBound(node->children.begin(), node->children.end(), pathComponents.at(i)); |
924 | if (it == node->children.end() || it->name != pathComponents.at(i)) |
925 | break; // node not found |
926 | |
927 | node = it; |
928 | ++i; |
929 | } |
930 | } |
931 | |
932 | /*! |
933 | Return the object that was registered with the registerObject() at the object path given by |
934 | \a path. |
935 | */ |
936 | QObject *QDBusConnection::objectRegisteredAt(const QString &path) const |
937 | { |
938 | Q_ASSERT_X(QDBusUtil::isValidObjectPath(path), "QDBusConnection::registeredObject" , |
939 | "Invalid object path given" ); |
940 | if (!d || !d->connection || !QDBusUtil::isValidObjectPath(path)) |
941 | return 0; |
942 | |
943 | QStringList pathComponents = path.split(QLatin1Char('/')); |
944 | if (pathComponents.last().isEmpty()) |
945 | pathComponents.removeLast(); |
946 | |
947 | // lower-bound search for where this object should enter in the tree |
948 | QDBusReadLocker lock(ObjectRegisteredAtAction, d); |
949 | const QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode; |
950 | |
951 | int i = 1; |
952 | while (node) { |
953 | if (pathComponents.count() == i) |
954 | return node->obj; |
955 | if ((node->flags & QDBusConnectionPrivate::VirtualObject) && (node->flags & QDBusConnection::SubPath)) |
956 | return node->obj; |
957 | |
958 | QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = |
959 | qLowerBound(node->children.constBegin(), node->children.constEnd(), pathComponents.at(i)); |
960 | if (it == node->children.constEnd() || it->name != pathComponents.at(i)) |
961 | break; // node not found |
962 | |
963 | node = it; |
964 | ++i; |
965 | } |
966 | return 0; |
967 | } |
968 | |
969 | |
970 | |
971 | /*! |
972 | Returns a QDBusConnectionInterface object that represents the |
973 | D-Bus server interface on this connection. |
974 | */ |
975 | QDBusConnectionInterface *QDBusConnection::interface() const |
976 | { |
977 | if (!d) |
978 | return 0; |
979 | return d->busService; |
980 | } |
981 | |
982 | /*! |
983 | \internal |
984 | \since 4.8 |
985 | |
986 | Returns the internal, implementation-defined pointer for this |
987 | connection. Currently, this returns a DBusConnection* pointer, |
988 | without changing the reference count. It is the responsibility of |
989 | the caller to call dbus_connection_ref if it wants to store the |
990 | pointer. |
991 | */ |
992 | void *QDBusConnection::internalPointer() const |
993 | { |
994 | return d ? d->connection : 0; |
995 | } |
996 | |
997 | /*! |
998 | Returns true if this QDBusConnection object is connected. |
999 | */ |
1000 | bool QDBusConnection::isConnected() const |
1001 | { |
1002 | return d && d->connection && q_dbus_connection_get_is_connected(d->connection); |
1003 | } |
1004 | |
1005 | /*! |
1006 | Returns the last error that happened in this connection. |
1007 | |
1008 | This function is provided for low-level code. If you're using |
1009 | QDBusInterface::call(), error codes are reported by its return |
1010 | value. |
1011 | |
1012 | \sa QDBusInterface, QDBusMessage |
1013 | */ |
1014 | QDBusError QDBusConnection::lastError() const |
1015 | { |
1016 | return d ? d->lastError : QDBusError(); |
1017 | } |
1018 | |
1019 | /*! |
1020 | Returns the unique connection name for this connection, if this QDBusConnection object is |
1021 | connected, or an empty QString otherwise. |
1022 | |
1023 | A Unique Connection Name is a string in the form ":x.xxx" (where x |
1024 | are decimal digits) that is assigned by the D-Bus server daemon |
1025 | upon connection. It uniquely identifies this client in the bus. |
1026 | |
1027 | This function returns an empty QString for peer-to-peer connections. |
1028 | */ |
1029 | QString QDBusConnection::baseService() const |
1030 | { |
1031 | return d ? d->baseService : QString(); |
1032 | } |
1033 | |
1034 | /*! |
1035 | \since 4.5 |
1036 | |
1037 | Returns the connection name for this connection, as given as the |
1038 | name parameter to connectToBus(). |
1039 | |
1040 | The connection name can be used to uniquely identify actual |
1041 | underlying connections to buses. Copies made from a single |
1042 | connection will always implicitly share the underlying connection, |
1043 | and hence will have the same connection name. |
1044 | |
1045 | Inversely, two connections having different connection names will |
1046 | always either be connected to different buses, or have a different |
1047 | unique name (as returned by baseService()) on that bus. |
1048 | |
1049 | \sa connectToBus(), disconnectFromBus() |
1050 | */ |
1051 | QString QDBusConnection::name() const |
1052 | { |
1053 | return d ? d->name : QString(); |
1054 | } |
1055 | |
1056 | /*! |
1057 | \since 4.8 |
1058 | |
1059 | Returns the capabilities of this connection as negotiated with the bus |
1060 | server or peer. If this QDBusConnection is not connected, this function |
1061 | returns no capabilities. |
1062 | */ |
1063 | QDBusConnection::ConnectionCapabilities QDBusConnection::connectionCapabilities() const |
1064 | { |
1065 | return d ? d->capabilities : ConnectionCapabilities(0); |
1066 | } |
1067 | |
1068 | /*! |
1069 | Attempts to register the \a serviceName on the D-Bus server and |
1070 | returns true if the registration succeeded. The registration will |
1071 | fail if the name is already registered by another application. |
1072 | |
1073 | \sa unregisterService(), QDBusConnectionInterface::registerService() |
1074 | */ |
1075 | bool QDBusConnection::registerService(const QString &serviceName) |
1076 | { |
1077 | if (interface() && interface()->registerService(serviceName)) { |
1078 | if (d) d->registerService(serviceName); |
1079 | return true; |
1080 | } |
1081 | return false; |
1082 | } |
1083 | |
1084 | /*! |
1085 | Unregisters the service \a serviceName that was previously |
1086 | registered with registerService() and returns true if it |
1087 | succeeded. |
1088 | |
1089 | \sa registerService(), QDBusConnectionInterface::unregisterService() |
1090 | */ |
1091 | bool QDBusConnection::unregisterService(const QString &serviceName) |
1092 | { |
1093 | if (interface()->unregisterService(serviceName)) { |
1094 | if (d) d->unregisterService(serviceName); |
1095 | return true; |
1096 | } |
1097 | return false; |
1098 | } |
1099 | |
1100 | static const char _q_sessionBusName[] = "qt_default_session_bus" ; |
1101 | static const char _q_systemBusName[] = "qt_default_system_bus" ; |
1102 | |
1103 | class QDBusDefaultConnection: public QDBusConnection |
1104 | { |
1105 | const char *ownName; |
1106 | public: |
1107 | inline QDBusDefaultConnection(BusType type, const char *name) |
1108 | : QDBusConnection(connectToBus(type, QString::fromLatin1(name))), ownName(name) |
1109 | { |
1110 | // make sure this connection is running on the main thread |
1111 | QCoreApplication *instance = QCoreApplication::instance(); |
1112 | if (!instance) { |
1113 | qWarning("QDBusConnection: %s D-Bus connection created before QCoreApplication. Application may misbehave." , |
1114 | type == SessionBus ? "session" : type == SystemBus ? "system" : "generic" ); |
1115 | } else if (QDBusConnectionPrivate::d(*this)) { |
1116 | QDBusConnectionPrivate::d(*this)->moveToThread(instance->thread()); |
1117 | } |
1118 | } |
1119 | |
1120 | inline ~QDBusDefaultConnection() |
1121 | { disconnectFromBus(QString::fromLatin1(ownName)); } |
1122 | }; |
1123 | |
1124 | Q_GLOBAL_STATIC_WITH_ARGS(QDBusDefaultConnection, _q_sessionBus, |
1125 | (QDBusConnection::SessionBus, _q_sessionBusName)) |
1126 | Q_GLOBAL_STATIC_WITH_ARGS(QDBusDefaultConnection, _q_systemBus, |
1127 | (QDBusConnection::SystemBus, _q_systemBusName)) |
1128 | |
1129 | QDBusConnection QDBusConnection::sessionBus() |
1130 | { |
1131 | return *_q_sessionBus(); |
1132 | } |
1133 | |
1134 | QDBusConnection QDBusConnection::systemBus() |
1135 | { |
1136 | return *_q_systemBus(); |
1137 | } |
1138 | |
1139 | /*! |
1140 | \nonreentrant |
1141 | |
1142 | Returns the connection that sent the signal, if called in a slot activated |
1143 | by QDBus; otherwise it returns 0. |
1144 | |
1145 | \note Please avoid this function. This function is not thread-safe, so if |
1146 | there's any other thread delivering a D-Bus call, this function may return |
1147 | the wrong connection. In new code, please use QDBusContext::connection() |
1148 | (see that class for a description on how to use it). |
1149 | */ |
1150 | QDBusConnection QDBusConnection::sender() |
1151 | { |
1152 | return QDBusConnection(_q_manager()->sender()); |
1153 | } |
1154 | |
1155 | /*! |
1156 | \internal |
1157 | */ |
1158 | void QDBusConnectionPrivate::setSender(const QDBusConnectionPrivate *s) |
1159 | { |
1160 | _q_manager()->setSender(s); |
1161 | } |
1162 | |
1163 | /*! |
1164 | \internal |
1165 | */ |
1166 | void QDBusConnectionPrivate::setBusService(const QDBusConnection &connection) |
1167 | { |
1168 | busService = new QDBusConnectionInterface(connection, this); |
1169 | ref.deref(); // busService has increased the refcounting to us |
1170 | // avoid cyclic refcounting |
1171 | |
1172 | QObject::connect(this, SIGNAL(callWithCallbackFailed(QDBusError,QDBusMessage)), |
1173 | busService, SIGNAL(callWithCallbackFailed(QDBusError,QDBusMessage)), |
1174 | Qt::QueuedConnection); |
1175 | } |
1176 | |
1177 | /*! |
1178 | \since 4.8 |
1179 | Returns the local machine ID as known to the D-Bus system. Each |
1180 | node or host that runs D-Bus has a unique identifier that can be |
1181 | used to distinguish it from other hosts if they are sharing |
1182 | resources like the filesystem. |
1183 | |
1184 | Note that the local machine ID is not guaranteed to be persistent |
1185 | across boots of the system, so this identifier should not be |
1186 | stored in persistent storage (like the filesystem). It is |
1187 | guaranteed to remain constant only during the lifetime of this |
1188 | boot session. |
1189 | */ |
1190 | QByteArray QDBusConnection::localMachineId() |
1191 | { |
1192 | char *dbus_machine_id = q_dbus_get_local_machine_id(); |
1193 | QByteArray result = dbus_machine_id; |
1194 | q_dbus_free(dbus_machine_id); |
1195 | return result; |
1196 | } |
1197 | |
1198 | /*! |
1199 | \namespace QDBus |
1200 | \inmodule QtDBus |
1201 | |
1202 | \brief The QDBus namespace contains miscellaneous identifiers used |
1203 | throughout the QtDBus library. |
1204 | */ |
1205 | |
1206 | /*! |
1207 | \enum QDBus::CallMode |
1208 | |
1209 | This enum describes the various ways of placing a function call. The valid modes are: |
1210 | |
1211 | \value NoBlock Place the call but don't wait for the reply (the reply's contents |
1212 | will be discarded). |
1213 | \value Block Don't use an event loop to wait for a reply, but instead block on |
1214 | network operations while waiting. This means the |
1215 | user-interface may not be updated until the function returns. |
1216 | \value BlockWithGui Use the Qt event loop to wait for a reply. This means that the |
1217 | user-interface will stay responsive (processing input events), |
1218 | but it also means other events may happen, like signal delivery |
1219 | and other D-Bus method calls. |
1220 | \value AutoDetect Automatically detect if the called function has a reply. |
1221 | |
1222 | When using BlockWithGui, applications must be prepared for reentrancy in any function. |
1223 | */ |
1224 | |
1225 | QT_END_NAMESPACE |
1226 | |
1227 | #endif // QT_NO_DBUS |
1228 | |