3 * Copyright (C) 2006 Trolltech AS. All rights reserved.
4 * Author: Thiago Macieira <thiago.macieira@trolltech.com>
6 * Licensed under the Academic Free License version 2.1
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software Foundation
20 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include "qdbusabstractadaptor.h"
26 #include <QtCore/qmetaobject.h>
27 #include <QtCore/qtimer.h>
29 #include "qdbusconnection.h"
31 #include "qdbusconnection_p.h" // for qDBusParametersForMethod
32 #include "qdbusabstractadaptor_p.h"
34 struct QDBusAdaptorInit
36 QSignalSpyCallbackSet callbacks;
39 extern void qt_register_signal_spy_callbacks(const QSignalSpyCallbackSet &callback_set);
40 callbacks.signal_begin_callback = QDBusAdaptorConnector::signalBeginCallback;
41 callbacks.signal_end_callback = QDBusAdaptorConnector::signalEndCallback;
42 callbacks.slot_begin_callback = 0;
43 callbacks.slot_end_callback = 0;
44 qt_register_signal_spy_callbacks(callbacks);
46 //QDBusAdaptorConnector::id = QObject::registerUserData();
50 Q_GLOBAL_STATIC(QDBusAdaptorInit, qAdaptorInit)
52 QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *obj)
58 QDBusAdaptorConnector *connector = qFindChild<QDBusAdaptorConnector *>(obj);
64 QDBusAdaptorConnector *qDBusFindAdaptorConnector(QDBusAbstractAdaptor *adaptor)
66 return qDBusFindAdaptorConnector(adaptor->parent());
69 QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *obj)
73 QDBusAdaptorConnector *connector = qDBusFindAdaptorConnector(obj);
76 return new QDBusAdaptorConnector(obj);
79 QString QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor)
81 return adaptor->d->xml;
84 void QDBusAbstractAdaptorPrivate::saveIntrospectionXml(QDBusAbstractAdaptor *adaptor,
87 adaptor->d->xml = xml;
91 \page usingannotations.html
92 \title Using annotations in adaptors
94 It is currently not possible to specify arbitrary annotations in adaptors.
98 \class QDBusAbstractAdaptor
99 \brief Abstract adaptor for D-Bus adaptor classes.
101 The QDBusAbstractAdaptor class is the starting point for all objects intending to provide
102 interfaces to the external world using D-Bus. This is accomplished by attaching a one or more
103 classes derived from QDBusAbstractAdaptor to a normal QObject and then registering that QObject
104 with QDBusConnection::registerObject. QDBusAbstractAdaptor objects are intended to be
105 light-weight wrappers, mostly just relaying calls into the real object (see object()) and the
108 Each QDBusAbstractAdaptor-derived class should define the D-Bus interface it is implementing
109 using the Q_CLASSINFO macro in the class definition.
111 QDBusAbstractAdaptor uses the standard QObject mechanism of signals, slots and properties to
112 determine what signals, methods and properties to export to the bus. Any signal emitted by
113 QDBusAbstractAdaptor-derived classes will be automatically be relayed through any D-Bus
114 connections the object is registered on.
116 Classes derived from QDBusAbstractAdaptor must be created on the heap using the \a new operator
117 and must not be deleted by the user (they will be deleted automatically when the object they are
118 connected to is also deleted).
120 \sa {usingadaptors.html}{Using adaptors}, QDBusConnection
124 Constructs a QDBusAbstractAdaptor with \a parent as the object we refer to.
126 QDBusAbstractAdaptor::QDBusAbstractAdaptor(QObject* parent)
127 : QObject(parent), d(new QDBusAbstractAdaptorPrivate)
129 QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(parent);
131 connector->waitingForPolish = true;
132 QTimer::singleShot(0, connector, SLOT(polish()));
136 Destroys the adaptor.
138 \warning Adaptors are destroyed automatically when the real object they refer to is
139 destroyed. Do not delete the adaptors yourself.
141 QDBusAbstractAdaptor::~QDBusAbstractAdaptor()
147 Returns the QObject that we're the adaptor for. This is the same object that was passed as an
148 argument to the QDBusAbstractAdaptor constructor.
150 QObject* QDBusAbstractAdaptor::object() const
156 Toggles automatic signal relaying from the real object (see object()).
158 Automatic signal relaying consists of signal-to-signal connection of the signals on the parent
159 that have the exact same method signatue in both classes.
161 If \a enable is set to true, connect the signals; if set to false, disconnect all signals.
163 void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable)
165 const QMetaObject *us = metaObject();
166 const QMetaObject *them = parent()->metaObject();
167 for (int idx = staticMetaObject.methodCount(); idx < us->methodCount(); ++idx) {
168 QMetaMethod mm = us->method(idx);
170 if (mm.methodType() != QMetaMethod::Signal)
173 // try to connect/disconnect to a signal on the parent that has the same method signature
174 QByteArray sig = QMetaObject::normalizedSignature(mm.signature());
175 if (them->indexOfSignal(sig) == -1)
177 sig.prepend(QSIGNAL_CODE + '0');
178 parent()->disconnect(sig, this, sig);
180 connect(parent(), sig, sig);
184 QDBusAdaptorConnector::QDBusAdaptorConnector(QObject *parent)
185 : QObject(parent), waitingForPolish(false), lastSignalIdx(0), argv(0)
189 QDBusAdaptorConnector::~QDBusAdaptorConnector()
193 void QDBusAdaptorConnector::addAdaptor(QDBusAbstractAdaptor *adaptor)
195 // find the interface name
196 const QMetaObject *mo = adaptor->metaObject();
197 while (mo != &QDBusAbstractAdaptor::staticMetaObject) {
198 int ciend = mo->classInfoCount();
199 for (int i = mo->classInfoOffset(); i < ciend; ++i) {
200 QMetaClassInfo mci = mo->classInfo(i);
201 if (strcmp(mci.name(), QCLASSINFO_DBUS_INTERFACE) == 0 && *mci.value()) {
202 // find out if this interface exists first
203 QString interface = QString::fromUtf8(mci.value());
204 AdaptorMap::Iterator it = qLowerBound(adaptors.begin(), adaptors.end(), interface);
205 if (it != adaptors.end() && it->interface == interface) {
206 // exists. Replace it (though it's probably the same)
207 it->adaptor = adaptor;
212 entry.interface = interface;
213 entry.adaptor = adaptor;
214 entry.metaObject = mo;
220 mo = mo->superClass();
223 // connect the adaptor's signals to our relaySlot slot
224 mo = adaptor->metaObject();
225 for (int i = QDBusAbstractAdaptor::staticMetaObject.methodCount();
226 i < mo->methodCount(); ++i) {
227 QMetaMethod mm = mo->method(i);
229 if (mm.methodType() != QMetaMethod::Signal)
232 QByteArray sig = mm.signature();
233 sig.prepend(QSIGNAL_CODE + '0');
234 disconnect(adaptor, sig, this, SLOT(relaySlot()));
235 connect(adaptor, sig, this, SLOT(relaySlot()));
239 void QDBusAdaptorConnector::polish()
241 if (!waitingForPolish)
242 return; // avoid working multiple times if multiple adaptors were added
244 waitingForPolish = false;
245 const QObjectList &objs = parent()->children();
246 foreach (QObject *obj, objs) {
247 QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(obj);
252 // sort the adaptor list
256 void QDBusAdaptorConnector::relaySlot()
261 void QDBusAdaptorConnector::relay(QObject *sender)
263 // we're being called because there is a signal being emitted that we must relay
264 Q_ASSERT(lastSignalIdx);
266 Q_ASSERT(senderMetaObject);
268 if (senderMetaObject != sender->metaObject()) {
269 qWarning("Inconsistency detected: QDBusAdaptorConnector::relay got called with unexpected sender object!");
271 QMetaMethod mm = senderMetaObject->method(lastSignalIdx);
272 QObject *object = static_cast<QDBusAbstractAdaptor *>(sender)->parent();
274 // break down the parameter list
276 int inputCount = qDBusParametersForMethod(mm, types);
277 if (inputCount == -1)
278 // invalid signal signature
279 // qDBusParametersForMethod has already complained
281 if (inputCount + 1 != types.count() ||
282 types.at(inputCount) == QDBusConnectionPrivate::messageMetaType) {
283 // invalid signal signature
284 // qDBusParametersForMethod has not yet complained about this one
285 qWarning("Cannot relay signal %s::%s", senderMetaObject->className(), mm.signature());
289 QByteArray signature = QMetaObject::normalizedSignature(mm.signature());
290 signature.truncate(signature.indexOf('(')); // remove parameter decoration
293 for (int i = 1; i < types.count(); ++i)
294 args << QVariant(types.at(i), argv[i]);
296 // find all the interfaces this signal belongs to
297 for (const QMetaObject *mo = senderMetaObject; mo != &QDBusAbstractAdaptor::staticMetaObject;
298 mo = mo->superClass()) {
299 if (lastSignalIdx < mo->methodOffset())
302 for (int i = mo->classInfoOffset(); i < mo->classInfoCount(); ++i) {
303 QMetaClassInfo mci = mo->classInfo(i);
304 if (qstrcmp(mci.name(), QCLASSINFO_DBUS_INTERFACE) == 0 && *mci.value())
305 // now emit the signal with all the information
306 emit relaySignal(object, mci.value(), signature.constData(), args);
312 void QDBusAdaptorConnector::signalBeginCallback(QObject *caller, int method_index, void **argv)
314 QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(caller);
316 QDBusAdaptorConnector *data = qDBusFindAdaptorConnector(adaptor);
317 data->lastSignalIdx = method_index;
319 data->senderMetaObject = caller->metaObject();
320 data->polish(); // make sure it's polished
324 void QDBusAdaptorConnector::signalEndCallback(QObject *caller, int)
326 QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(caller);
328 QDBusAdaptorConnector *data = qDBusFindAdaptorConnector(adaptor);
329 data->lastSignalIdx = 0;
331 data->senderMetaObject = 0;
335 #include "qdbusabstractadaptor.moc"
336 #include "qdbusabstractadaptor_p.moc"