diff options
Diffstat (limited to 'src/dbus/qdbusabstractadaptor.cpp')
-rw-r--r-- | src/dbus/qdbusabstractadaptor.cpp | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/src/dbus/qdbusabstractadaptor.cpp b/src/dbus/qdbusabstractadaptor.cpp new file mode 100644 index 0000000..883aabf --- /dev/null +++ b/src/dbus/qdbusabstractadaptor.cpp @@ -0,0 +1,380 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDBus module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusabstractadaptor.h" + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qset.h> +#include <QtCore/qtimer.h> +#include <QtCore/qthread.h> + +#include "qdbusconnection.h" + +#include "qdbusconnection_p.h" // for qDBusParametersForMethod +#include "qdbusabstractadaptor_p.h" +#include "qdbusmetatype_p.h" + +QT_BEGIN_NAMESPACE + +QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *obj) +{ + if (!obj) + return 0; + const QObjectList &children = obj->children(); + QObjectList::ConstIterator it = children.constBegin(); + QObjectList::ConstIterator end = children.constEnd(); + for ( ; it != end; ++it) { + QDBusAdaptorConnector *connector = qobject_cast<QDBusAdaptorConnector *>(*it); + if (connector) { + connector->polish(); + return connector; + } + } + return 0; +} + +QDBusAdaptorConnector *qDBusFindAdaptorConnector(QDBusAbstractAdaptor *adaptor) +{ + return qDBusFindAdaptorConnector(adaptor->parent()); +} + +QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *obj) +{ + QDBusAdaptorConnector *connector = qDBusFindAdaptorConnector(obj); + if (connector) + return connector; + return new QDBusAdaptorConnector(obj); +} + +QString QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor) +{ + return adaptor->d_func()->xml; +} + +void QDBusAbstractAdaptorPrivate::saveIntrospectionXml(QDBusAbstractAdaptor *adaptor, + const QString &xml) +{ + adaptor->d_func()->xml = xml; +} + +/*! + \class QDBusAbstractAdaptor + \inmodule QtDBus + \since 4.2 + + \brief The QDBusAbstractAdaptor class is the base class of D-Bus adaptor classes. + + The QDBusAbstractAdaptor class is the starting point for all objects intending to provide + interfaces to the external world using D-Bus. This is accomplished by attaching a one or more + classes derived from QDBusAbstractAdaptor to a normal QObject and then registering that QObject + with QDBusConnection::registerObject. QDBusAbstractAdaptor objects are intended to be + light-weight wrappers, mostly just relaying calls into the real object (its parent) and the + signals from it. + + Each QDBusAbstractAdaptor-derived class should define the D-Bus interface it is implementing + using the Q_CLASSINFO macro in the class definition. Note that only one interface can be + exposed in this way. + + QDBusAbstractAdaptor uses the standard QObject mechanism of signals, slots and properties to + determine what signals, methods and properties to export to the bus. Any signal emitted by + QDBusAbstractAdaptor-derived classes will be automatically be relayed through any D-Bus + connections the object is registered on. + + Classes derived from QDBusAbstractAdaptor must be created on the heap using the \a new operator + and must not be deleted by the user (they will be deleted automatically when the object they are + connected to is also deleted). + + \sa {usingadaptors.html}{Using adaptors}, QDBusConnection +*/ + +/*! + Constructs a QDBusAbstractAdaptor with \a obj as the parent object. +*/ +QDBusAbstractAdaptor::QDBusAbstractAdaptor(QObject* obj) + : QObject(*new QDBusAbstractAdaptorPrivate, obj) +{ + QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(obj); + + connector->waitingForPolish = true; + QMetaObject::invokeMethod(connector, "polish", Qt::QueuedConnection); +} + +/*! + Destroys the adaptor. + + \warning Adaptors are destroyed automatically when the real object they refer to is + destroyed. Do not delete the adaptors yourself. +*/ +QDBusAbstractAdaptor::~QDBusAbstractAdaptor() +{ +} + +/*! + Toggles automatic signal relaying from the real object (see object()). + + Automatic signal relaying consists of signal-to-signal connection of the signals on the parent + that have the exact same method signatue in both classes. + + If \a enable is set to true, connect the signals; if set to false, disconnect all signals. +*/ +void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable) +{ + const QMetaObject *us = metaObject(); + const QMetaObject *them = parent()->metaObject(); + bool connected = false; + for (int idx = staticMetaObject.methodCount(); idx < us->methodCount(); ++idx) { + QMetaMethod mm = us->method(idx); + + if (mm.methodType() != QMetaMethod::Signal) + continue; + + // try to connect/disconnect to a signal on the parent that has the same method signature + QByteArray sig = QMetaObject::normalizedSignature(mm.signature()); + if (them->indexOfSignal(sig) == -1) + continue; + sig.prepend(QSIGNAL_CODE + '0'); + parent()->disconnect(sig, this, sig); + if (enable) + connected = connect(parent(), sig, sig) || connected; + } + d_func()->autoRelaySignals = connected; +} + +/*! + Returns true if automatic signal relaying from the real object (see object()) is enabled, + otherwiser returns false. + + \sa setAutoRelaySignals() +*/ +bool QDBusAbstractAdaptor::autoRelaySignals() const +{ + return d_func()->autoRelaySignals; +} + +QDBusAdaptorConnector::QDBusAdaptorConnector(QObject *obj) + : QObject(obj), waitingForPolish(false) +{ +} + +QDBusAdaptorConnector::~QDBusAdaptorConnector() +{ +} + +void QDBusAdaptorConnector::addAdaptor(QDBusAbstractAdaptor *adaptor) +{ + // find the interface name + const QMetaObject *mo = adaptor->metaObject(); + int ciid = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE); + if (ciid != -1) { + QMetaClassInfo mci = mo->classInfo(ciid); + if (*mci.value()) { + // find out if this interface exists first + const char *interface = mci.value(); + AdaptorMap::Iterator it = qLowerBound(adaptors.begin(), adaptors.end(), + QByteArray(interface)); + if (it != adaptors.end() && qstrcmp(interface, it->interface) == 0) { + // exists. Replace it (though it's probably the same) + if (it->adaptor != adaptor) { + // reconnect the signals + disconnectAllSignals(it->adaptor); + connectAllSignals(adaptor); + } + it->adaptor = adaptor; + } else { + // create a new one + AdaptorData entry; + entry.interface = interface; + entry.adaptor = adaptor; + adaptors << entry; + + // connect the adaptor's signals to our relaySlot slot + connectAllSignals(adaptor); + } + } + } +} + +void QDBusAdaptorConnector::disconnectAllSignals(QObject *obj) +{ + QMetaObject::disconnect(obj, -1, this, metaObject()->methodOffset()); +} + +void QDBusAdaptorConnector::connectAllSignals(QObject *obj) +{ + QMetaObject::connect(obj, -1, this, metaObject()->methodOffset(), Qt::DirectConnection); +} + +void QDBusAdaptorConnector::polish() +{ + if (!waitingForPolish) + return; // avoid working multiple times if multiple adaptors were added + + waitingForPolish = false; + const QObjectList &objs = parent()->children(); + QObjectList::ConstIterator it = objs.constBegin(); + QObjectList::ConstIterator end = objs.constEnd(); + for ( ; it != end; ++it) { + QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(*it); + if (adaptor) + addAdaptor(adaptor); + } + + // sort the adaptor list + qSort(adaptors); +} + +void QDBusAdaptorConnector::relaySlot(void **argv) +{ + QObjectPrivate *d = static_cast<QObjectPrivate *>(d_ptr); + relay(d->currentSender->sender, d->currentSender->signal, argv); +} + +void QDBusAdaptorConnector::relay(QObject *senderObj, int lastSignalIdx, void **argv) +{ + if (lastSignalIdx < QObject::staticMetaObject.methodCount()) + // QObject signal (destroyed(QObject *)) -- ignore + return; + + const QMetaObject *senderMetaObject = senderObj->metaObject(); + QMetaMethod mm = senderMetaObject->method(lastSignalIdx); + + QObject *realObject = senderObj; + if (qobject_cast<QDBusAbstractAdaptor *>(senderObj)) + // it's an adaptor, so the real object is in fact its parent + realObject = realObject->parent(); + + // break down the parameter list + QList<int> types; + int inputCount = qDBusParametersForMethod(mm, types); + if (inputCount == -1) + // invalid signal signature + // qDBusParametersForMethod has already complained + return; + if (inputCount + 1 != types.count() || + types.at(inputCount) == QDBusMetaTypeId::message) { + // invalid signal signature + // qDBusParametersForMethod has not yet complained about this one + qWarning("QDBusAbstractAdaptor: Cannot relay signal %s::%s", + senderMetaObject->className(), mm.signature()); + return; + } + + QVariantList args; + for (int i = 1; i < types.count(); ++i) + args << QVariant(types.at(i), argv[i]); + + // now emit the signal with all the information + emit relaySignal(realObject, senderMetaObject, lastSignalIdx, args); +} + +// our Meta Object +// modify carefully: this has been hand-edited! +// the relaySlot slot has local ID 0 (we use this when calling QMetaObject::connect) +// it also gets called with the void** array + +static const uint qt_meta_data_QDBusAdaptorConnector[] = { + // content: + 1, // revision + 0, // classname + 0, 0, // classinfo + 3, 10, // methods + 0, 0, // properties + 0, 0, // enums/sets + + // slots: signature, parameters, type, tag, flags + 106, 22, 22, 22, 0x0a, + 118, 22, 22, 22, 0x0a, + + // signals: signature, parameters, type, tag, flags + 47, 23, 22, 22, 0x05, + + 0 // eod +}; + +static const char qt_meta_stringdata_QDBusAdaptorConnector[] = { + "QDBusAdaptorConnector\0\0obj,metaobject,sid,args\0" + "relaySignal(QObject*,const QMetaObject*,int,QVariantList)\0\0relaySlot()\0" + "polish()\0" +}; + +const QMetaObject QDBusAdaptorConnector::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_QDBusAdaptorConnector, + qt_meta_data_QDBusAdaptorConnector, 0 } +}; + +const QMetaObject *QDBusAdaptorConnector::metaObject() const +{ + return &staticMetaObject; +} + +void *QDBusAdaptorConnector::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_QDBusAdaptorConnector)) + return static_cast<void*>(const_cast<QDBusAdaptorConnector*>(this)); + return QObject::qt_metacast(_clname); +} + +int QDBusAdaptorConnector::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: relaySlot(_a); break; // HAND EDIT: add the _a parameter + case 1: polish(); break; + 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; + } + _id -= 3; + } + return _id; +} + +// SIGNAL 0 +void QDBusAdaptorConnector::relaySignal(QObject * _t1, const QMetaObject * _t2, int _t3, const QVariantList & _t4) +{ + 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)) }; + QMetaObject::activate(this, &staticMetaObject, 2, _a); +} + +QT_END_NAMESPACE |