| 1 | /**************************************************************************** | 
|---|
| 2 | ** | 
|---|
| 3 | ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). | 
|---|
| 4 | ** Contact: Qt Software Information (qt-info@nokia.com) | 
|---|
| 5 | ** | 
|---|
| 6 | ** This file is part of the QtDBus module of the Qt Toolkit. | 
|---|
| 7 | ** | 
|---|
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ | 
|---|
| 9 | ** Commercial Usage | 
|---|
| 10 | ** Licensees holding valid Qt Commercial licenses may use this file in | 
|---|
| 11 | ** accordance with the Qt Commercial License Agreement provided with the | 
|---|
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
|---|
| 13 | ** a written agreement between you and Nokia. | 
|---|
| 14 | ** | 
|---|
| 15 | ** GNU Lesser General Public License Usage | 
|---|
| 16 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
|---|
| 17 | ** General Public License version 2.1 as published by the Free Software | 
|---|
| 18 | ** Foundation and appearing in the file LICENSE.LGPL included in the | 
|---|
| 19 | ** packaging of this file.  Please review the following information to | 
|---|
| 20 | ** ensure the GNU Lesser General Public License version 2.1 requirements | 
|---|
| 21 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | 
|---|
| 22 | ** | 
|---|
| 23 | ** In addition, as a special exception, Nokia gives you certain | 
|---|
| 24 | ** additional rights. These rights are described in the Nokia Qt LGPL | 
|---|
| 25 | ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this | 
|---|
| 26 | ** package. | 
|---|
| 27 | ** | 
|---|
| 28 | ** GNU General Public License Usage | 
|---|
| 29 | ** Alternatively, this file may be used under the terms of the GNU | 
|---|
| 30 | ** General Public License version 3.0 as published by the Free Software | 
|---|
| 31 | ** Foundation and appearing in the file LICENSE.GPL included in the | 
|---|
| 32 | ** packaging of this file.  Please review the following information to | 
|---|
| 33 | ** ensure the GNU General Public License version 3.0 requirements will be | 
|---|
| 34 | ** met: http://www.gnu.org/copyleft/gpl.html. | 
|---|
| 35 | ** | 
|---|
| 36 | ** If you are unsure which license is appropriate for your use, please | 
|---|
| 37 | ** contact the sales department at qt-sales@nokia.com. | 
|---|
| 38 | ** $QT_END_LICENSE$ | 
|---|
| 39 | ** | 
|---|
| 40 | ****************************************************************************/ | 
|---|
| 41 |  | 
|---|
| 42 | #include "qdbusabstractadaptor.h" | 
|---|
| 43 |  | 
|---|
| 44 | #include <QtCore/qcoreapplication.h> | 
|---|
| 45 | #include <QtCore/qmetaobject.h> | 
|---|
| 46 | #include <QtCore/qset.h> | 
|---|
| 47 | #include <QtCore/qtimer.h> | 
|---|
| 48 | #include <QtCore/qthread.h> | 
|---|
| 49 |  | 
|---|
| 50 | #include "qdbusconnection.h" | 
|---|
| 51 |  | 
|---|
| 52 | #include "qdbusconnection_p.h"  // for qDBusParametersForMethod | 
|---|
| 53 | #include "qdbusabstractadaptor_p.h" | 
|---|
| 54 | #include "qdbusmetatype_p.h" | 
|---|
| 55 |  | 
|---|
| 56 | QT_BEGIN_NAMESPACE | 
|---|
| 57 |  | 
|---|
| 58 | QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *obj) | 
|---|
| 59 | { | 
|---|
| 60 | if (!obj) | 
|---|
| 61 | return 0; | 
|---|
| 62 | const QObjectList &children = obj->children(); | 
|---|
| 63 | QObjectList::ConstIterator it = children.constBegin(); | 
|---|
| 64 | QObjectList::ConstIterator end = children.constEnd(); | 
|---|
| 65 | for ( ; it != end; ++it) { | 
|---|
| 66 | QDBusAdaptorConnector *connector = qobject_cast<QDBusAdaptorConnector *>(*it); | 
|---|
| 67 | if (connector) { | 
|---|
| 68 | connector->polish(); | 
|---|
| 69 | return connector; | 
|---|
| 70 | } | 
|---|
| 71 | } | 
|---|
| 72 | return 0; | 
|---|
| 73 | } | 
|---|
| 74 |  | 
|---|
| 75 | QDBusAdaptorConnector *qDBusFindAdaptorConnector(QDBusAbstractAdaptor *adaptor) | 
|---|
| 76 | { | 
|---|
| 77 | return qDBusFindAdaptorConnector(adaptor->parent()); | 
|---|
| 78 | } | 
|---|
| 79 |  | 
|---|
| 80 | QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *obj) | 
|---|
| 81 | { | 
|---|
| 82 | QDBusAdaptorConnector *connector = qDBusFindAdaptorConnector(obj); | 
|---|
| 83 | if (connector) | 
|---|
| 84 | return connector; | 
|---|
| 85 | return new QDBusAdaptorConnector(obj); | 
|---|
| 86 | } | 
|---|
| 87 |  | 
|---|
| 88 | QString QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor) | 
|---|
| 89 | { | 
|---|
| 90 | return adaptor->d_func()->xml; | 
|---|
| 91 | } | 
|---|
| 92 |  | 
|---|
| 93 | void QDBusAbstractAdaptorPrivate::saveIntrospectionXml(QDBusAbstractAdaptor *adaptor, | 
|---|
| 94 | const QString &xml) | 
|---|
| 95 | { | 
|---|
| 96 | adaptor->d_func()->xml = xml; | 
|---|
| 97 | } | 
|---|
| 98 |  | 
|---|
| 99 | /*! | 
|---|
| 100 | \class QDBusAbstractAdaptor | 
|---|
| 101 | \inmodule QtDBus | 
|---|
| 102 | \since 4.2 | 
|---|
| 103 |  | 
|---|
| 104 | \brief The QDBusAbstractAdaptor class is the base class of D-Bus adaptor classes. | 
|---|
| 105 |  | 
|---|
| 106 | The QDBusAbstractAdaptor class is the starting point for all objects intending to provide | 
|---|
| 107 | interfaces to the external world using D-Bus. This is accomplished by attaching a one or more | 
|---|
| 108 | classes derived from QDBusAbstractAdaptor to a normal QObject and then registering that QObject | 
|---|
| 109 | with QDBusConnection::registerObject. QDBusAbstractAdaptor objects are intended to be | 
|---|
| 110 | light-weight wrappers, mostly just relaying calls into the real object (its parent) and the | 
|---|
| 111 | signals from it. | 
|---|
| 112 |  | 
|---|
| 113 | Each QDBusAbstractAdaptor-derived class should define the D-Bus interface it is implementing | 
|---|
| 114 | using the Q_CLASSINFO macro in the class definition. Note that only one interface can be | 
|---|
| 115 | exposed in this way. | 
|---|
| 116 |  | 
|---|
| 117 | QDBusAbstractAdaptor uses the standard QObject mechanism of signals, slots and properties to | 
|---|
| 118 | determine what signals, methods and properties to export to the bus. Any signal emitted by | 
|---|
| 119 | QDBusAbstractAdaptor-derived classes will be automatically be relayed through any D-Bus | 
|---|
| 120 | connections the object is registered on. | 
|---|
| 121 |  | 
|---|
| 122 | Classes derived from QDBusAbstractAdaptor must be created on the heap using the \a new operator | 
|---|
| 123 | and must not be deleted by the user (they will be deleted automatically when the object they are | 
|---|
| 124 | connected to is also deleted). | 
|---|
| 125 |  | 
|---|
| 126 | \sa {usingadaptors.html}{Using adaptors}, QDBusConnection | 
|---|
| 127 | */ | 
|---|
| 128 |  | 
|---|
| 129 | /*! | 
|---|
| 130 | Constructs a QDBusAbstractAdaptor with \a obj as the parent object. | 
|---|
| 131 | */ | 
|---|
| 132 | QDBusAbstractAdaptor::QDBusAbstractAdaptor(QObject* obj) | 
|---|
| 133 | : QObject(*new QDBusAbstractAdaptorPrivate, obj) | 
|---|
| 134 | { | 
|---|
| 135 | QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(obj); | 
|---|
| 136 |  | 
|---|
| 137 | connector->waitingForPolish = true; | 
|---|
| 138 | QMetaObject::invokeMethod(connector, "polish", Qt::QueuedConnection); | 
|---|
| 139 | } | 
|---|
| 140 |  | 
|---|
| 141 | /*! | 
|---|
| 142 | Destroys the adaptor. | 
|---|
| 143 |  | 
|---|
| 144 | \warning Adaptors are destroyed automatically when the real object they refer to is | 
|---|
| 145 | destroyed. Do not delete the adaptors yourself. | 
|---|
| 146 | */ | 
|---|
| 147 | QDBusAbstractAdaptor::~QDBusAbstractAdaptor() | 
|---|
| 148 | { | 
|---|
| 149 | } | 
|---|
| 150 |  | 
|---|
| 151 | /*! | 
|---|
| 152 | Toggles automatic signal relaying from the real object (see object()). | 
|---|
| 153 |  | 
|---|
| 154 | Automatic signal relaying consists of signal-to-signal connection of the signals on the parent | 
|---|
| 155 | that have the exact same method signatue in both classes. | 
|---|
| 156 |  | 
|---|
| 157 | If \a enable is set to true, connect the signals; if set to false, disconnect all signals. | 
|---|
| 158 | */ | 
|---|
| 159 | void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable) | 
|---|
| 160 | { | 
|---|
| 161 | const QMetaObject *us = metaObject(); | 
|---|
| 162 | const QMetaObject *them = parent()->metaObject(); | 
|---|
| 163 | bool connected = false; | 
|---|
| 164 | for (int idx = staticMetaObject.methodCount(); idx < us->methodCount(); ++idx) { | 
|---|
| 165 | QMetaMethod mm = us->method(idx); | 
|---|
| 166 |  | 
|---|
| 167 | if (mm.methodType() != QMetaMethod::Signal) | 
|---|
| 168 | continue; | 
|---|
| 169 |  | 
|---|
| 170 | // try to connect/disconnect to a signal on the parent that has the same method signature | 
|---|
| 171 | QByteArray sig = QMetaObject::normalizedSignature(mm.signature()); | 
|---|
| 172 | if (them->indexOfSignal(sig) == -1) | 
|---|
| 173 | continue; | 
|---|
| 174 | sig.prepend(QSIGNAL_CODE + '0'); | 
|---|
| 175 | parent()->disconnect(sig, this, sig); | 
|---|
| 176 | if (enable) | 
|---|
| 177 | connected = connect(parent(), sig, sig) || connected; | 
|---|
| 178 | } | 
|---|
| 179 | d_func()->autoRelaySignals = connected; | 
|---|
| 180 | } | 
|---|
| 181 |  | 
|---|
| 182 | /*! | 
|---|
| 183 | Returns true if automatic signal relaying from the real object (see object()) is enabled, | 
|---|
| 184 | otherwiser returns false. | 
|---|
| 185 |  | 
|---|
| 186 | \sa setAutoRelaySignals() | 
|---|
| 187 | */ | 
|---|
| 188 | bool QDBusAbstractAdaptor::autoRelaySignals() const | 
|---|
| 189 | { | 
|---|
| 190 | return d_func()->autoRelaySignals; | 
|---|
| 191 | } | 
|---|
| 192 |  | 
|---|
| 193 | QDBusAdaptorConnector::QDBusAdaptorConnector(QObject *obj) | 
|---|
| 194 | : QObject(obj), waitingForPolish(false) | 
|---|
| 195 | { | 
|---|
| 196 | } | 
|---|
| 197 |  | 
|---|
| 198 | QDBusAdaptorConnector::~QDBusAdaptorConnector() | 
|---|
| 199 | { | 
|---|
| 200 | } | 
|---|
| 201 |  | 
|---|
| 202 | void QDBusAdaptorConnector::addAdaptor(QDBusAbstractAdaptor *adaptor) | 
|---|
| 203 | { | 
|---|
| 204 | // find the interface name | 
|---|
| 205 | const QMetaObject *mo = adaptor->metaObject(); | 
|---|
| 206 | int ciid = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE); | 
|---|
| 207 | if (ciid != -1) { | 
|---|
| 208 | QMetaClassInfo mci = mo->classInfo(ciid); | 
|---|
| 209 | if (*mci.value()) { | 
|---|
| 210 | // find out if this interface exists first | 
|---|
| 211 | const char *interface = mci.value(); | 
|---|
| 212 | AdaptorMap::Iterator it = qLowerBound(adaptors.begin(), adaptors.end(), | 
|---|
| 213 | QByteArray(interface)); | 
|---|
| 214 | if (it != adaptors.end() && qstrcmp(interface, it->interface) == 0) { | 
|---|
| 215 | // exists. Replace it (though it's probably the same) | 
|---|
| 216 | if (it->adaptor != adaptor) { | 
|---|
| 217 | // reconnect the signals | 
|---|
| 218 | disconnectAllSignals(it->adaptor); | 
|---|
| 219 | connectAllSignals(adaptor); | 
|---|
| 220 | } | 
|---|
| 221 | it->adaptor = adaptor; | 
|---|
| 222 | } else { | 
|---|
| 223 | // create a new one | 
|---|
| 224 | AdaptorData entry; | 
|---|
| 225 | entry.interface = interface; | 
|---|
| 226 | entry.adaptor = adaptor; | 
|---|
| 227 | adaptors << entry; | 
|---|
| 228 |  | 
|---|
| 229 | // connect the adaptor's signals to our relaySlot slot | 
|---|
| 230 | connectAllSignals(adaptor); | 
|---|
| 231 | } | 
|---|
| 232 | } | 
|---|
| 233 | } | 
|---|
| 234 | } | 
|---|
| 235 |  | 
|---|
| 236 | void QDBusAdaptorConnector::disconnectAllSignals(QObject *obj) | 
|---|
| 237 | { | 
|---|
| 238 | QMetaObject::disconnect(obj, -1, this, metaObject()->methodOffset()); | 
|---|
| 239 | } | 
|---|
| 240 |  | 
|---|
| 241 | void QDBusAdaptorConnector::connectAllSignals(QObject *obj) | 
|---|
| 242 | { | 
|---|
| 243 | QMetaObject::connect(obj, -1, this, metaObject()->methodOffset(), Qt::DirectConnection); | 
|---|
| 244 | } | 
|---|
| 245 |  | 
|---|
| 246 | void QDBusAdaptorConnector::polish() | 
|---|
| 247 | { | 
|---|
| 248 | if (!waitingForPolish) | 
|---|
| 249 | return;                 // avoid working multiple times if multiple adaptors were added | 
|---|
| 250 |  | 
|---|
| 251 | waitingForPolish = false; | 
|---|
| 252 | const QObjectList &objs = parent()->children(); | 
|---|
| 253 | QObjectList::ConstIterator it = objs.constBegin(); | 
|---|
| 254 | QObjectList::ConstIterator end = objs.constEnd(); | 
|---|
| 255 | for ( ; it != end; ++it) { | 
|---|
| 256 | QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(*it); | 
|---|
| 257 | if (adaptor) | 
|---|
| 258 | addAdaptor(adaptor); | 
|---|
| 259 | } | 
|---|
| 260 |  | 
|---|
| 261 | // sort the adaptor list | 
|---|
| 262 | qSort(adaptors); | 
|---|
| 263 | } | 
|---|
| 264 |  | 
|---|
| 265 | void QDBusAdaptorConnector::relaySlot(void **argv) | 
|---|
| 266 | { | 
|---|
| 267 | QObjectPrivate *d = static_cast<QObjectPrivate *>(d_ptr); | 
|---|
| 268 | relay(d->currentSender->sender, d->currentSender->signal, argv); | 
|---|
| 269 | } | 
|---|
| 270 |  | 
|---|
| 271 | void QDBusAdaptorConnector::relay(QObject *senderObj, int lastSignalIdx, void **argv) | 
|---|
| 272 | { | 
|---|
| 273 | if (lastSignalIdx < QObject::staticMetaObject.methodCount()) | 
|---|
| 274 | // QObject signal (destroyed(QObject *)) -- ignore | 
|---|
| 275 | return; | 
|---|
| 276 |  | 
|---|
| 277 | const QMetaObject *senderMetaObject = senderObj->metaObject(); | 
|---|
| 278 | QMetaMethod mm = senderMetaObject->method(lastSignalIdx); | 
|---|
| 279 |  | 
|---|
| 280 | QObject *realObject = senderObj; | 
|---|
| 281 | if (qobject_cast<QDBusAbstractAdaptor *>(senderObj)) | 
|---|
| 282 | // it's an adaptor, so the real object is in fact its parent | 
|---|
| 283 | realObject = realObject->parent(); | 
|---|
| 284 |  | 
|---|
| 285 | // break down the parameter list | 
|---|
| 286 | QList<int> types; | 
|---|
| 287 | int inputCount = qDBusParametersForMethod(mm, types); | 
|---|
| 288 | if (inputCount == -1) | 
|---|
| 289 | // invalid signal signature | 
|---|
| 290 | // qDBusParametersForMethod has already complained | 
|---|
| 291 | return; | 
|---|
| 292 | if (inputCount + 1 != types.count() || | 
|---|
| 293 | types.at(inputCount) == QDBusMetaTypeId::message) { | 
|---|
| 294 | // invalid signal signature | 
|---|
| 295 | // qDBusParametersForMethod has not yet complained about this one | 
|---|
| 296 | qWarning("QDBusAbstractAdaptor: Cannot relay signal %s::%s", | 
|---|
| 297 | senderMetaObject->className(), mm.signature()); | 
|---|
| 298 | return; | 
|---|
| 299 | } | 
|---|
| 300 |  | 
|---|
| 301 | QVariantList args; | 
|---|
| 302 | for (int i = 1; i < types.count(); ++i) | 
|---|
| 303 | args << QVariant(types.at(i), argv[i]); | 
|---|
| 304 |  | 
|---|
| 305 | // now emit the signal with all the information | 
|---|
| 306 | emit relaySignal(realObject, senderMetaObject, lastSignalIdx, args); | 
|---|
| 307 | } | 
|---|
| 308 |  | 
|---|
| 309 | // our Meta Object | 
|---|
| 310 | // modify carefully: this has been hand-edited! | 
|---|
| 311 | // the relaySlot slot has local ID 0 (we use this when calling QMetaObject::connect) | 
|---|
| 312 | // it also gets called with the void** array | 
|---|
| 313 |  | 
|---|
| 314 | static const uint qt_meta_data_QDBusAdaptorConnector[] = { | 
|---|
| 315 | // content: | 
|---|
| 316 | 1,       // revision | 
|---|
| 317 | 0,       // classname | 
|---|
| 318 | 0,    0, // classinfo | 
|---|
| 319 | 3,   10, // methods | 
|---|
| 320 | 0,    0, // properties | 
|---|
| 321 | 0,    0, // enums/sets | 
|---|
| 322 |  | 
|---|
| 323 | // slots: signature, parameters, type, tag, flags | 
|---|
| 324 | 106,   22,   22,   22, 0x0a, | 
|---|
| 325 | 118,   22,   22,   22, 0x0a, | 
|---|
| 326 |  | 
|---|
| 327 | // signals: signature, parameters, type, tag, flags | 
|---|
| 328 | 47,   23,   22,   22, 0x05, | 
|---|
| 329 |  | 
|---|
| 330 | 0        // eod | 
|---|
| 331 | }; | 
|---|
| 332 |  | 
|---|
| 333 | static const char qt_meta_stringdata_QDBusAdaptorConnector[] = { | 
|---|
| 334 | "QDBusAdaptorConnector\0\0obj,metaobject,sid,args\0" | 
|---|
| 335 | "relaySignal(QObject*,const QMetaObject*,int,QVariantList)\0\0relaySlot()\0" | 
|---|
| 336 | "polish()\0" | 
|---|
| 337 | }; | 
|---|
| 338 |  | 
|---|
| 339 | const QMetaObject QDBusAdaptorConnector::staticMetaObject = { | 
|---|
| 340 | { &QObject::staticMetaObject, qt_meta_stringdata_QDBusAdaptorConnector, | 
|---|
| 341 | qt_meta_data_QDBusAdaptorConnector, 0 } | 
|---|
| 342 | }; | 
|---|
| 343 |  | 
|---|
| 344 | const QMetaObject *QDBusAdaptorConnector::metaObject() const | 
|---|
| 345 | { | 
|---|
| 346 | return &staticMetaObject; | 
|---|
| 347 | } | 
|---|
| 348 |  | 
|---|
| 349 | void *QDBusAdaptorConnector::qt_metacast(const char *_clname) | 
|---|
| 350 | { | 
|---|
| 351 | if (!_clname) return 0; | 
|---|
| 352 | if (!strcmp(_clname, qt_meta_stringdata_QDBusAdaptorConnector)) | 
|---|
| 353 | return static_cast<void*>(const_cast<QDBusAdaptorConnector*>(this)); | 
|---|
| 354 | return QObject::qt_metacast(_clname); | 
|---|
| 355 | } | 
|---|
| 356 |  | 
|---|
| 357 | int QDBusAdaptorConnector::qt_metacall(QMetaObject::Call _c, int _id, void **_a) | 
|---|
| 358 | { | 
|---|
| 359 | _id = QObject::qt_metacall(_c, _id, _a); | 
|---|
| 360 | if (_id < 0) | 
|---|
| 361 | return _id; | 
|---|
| 362 | if (_c == QMetaObject::InvokeMetaMethod) { | 
|---|
| 363 | switch (_id) { | 
|---|
| 364 | case 0: relaySlot(_a); break; // HAND EDIT: add the _a parameter | 
|---|
| 365 | case 1: polish(); break; | 
|---|
| 366 | case 2: relaySignal((*reinterpret_cast< QObject*(*)>(_a[1])),(*reinterpret_cast< const QMetaObject*(*)>(_a[2])),(*reinterpret_cast< int(*)>(_a[3])),(*reinterpret_cast< const QVariantList(*)>(_a[4]))); break; | 
|---|
| 367 | } | 
|---|
| 368 | _id -= 3; | 
|---|
| 369 | } | 
|---|
| 370 | return _id; | 
|---|
| 371 | } | 
|---|
| 372 |  | 
|---|
| 373 | // SIGNAL 0 | 
|---|
| 374 | void QDBusAdaptorConnector::relaySignal(QObject * _t1, const QMetaObject * _t2, int _t3, const QVariantList & _t4) | 
|---|
| 375 | { | 
|---|
| 376 | void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)), const_cast<void*>(reinterpret_cast<const void*>(&_t3)), const_cast<void*>(reinterpret_cast<const void*>(&_t4)) }; | 
|---|
| 377 | QMetaObject::activate(this, &staticMetaObject, 2, _a); | 
|---|
| 378 | } | 
|---|
| 379 |  | 
|---|
| 380 | QT_END_NAMESPACE | 
|---|