summaryrefslogtreecommitdiffstats
path: root/qt/qdbusabstractadaptor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qt/qdbusabstractadaptor.cpp')
-rw-r--r--qt/qdbusabstractadaptor.cpp767
1 files changed, 758 insertions, 9 deletions
diff --git a/qt/qdbusabstractadaptor.cpp b/qt/qdbusabstractadaptor.cpp
index 335469e7..4d6217a0 100644
--- a/qt/qdbusabstractadaptor.cpp
+++ b/qt/qdbusabstractadaptor.cpp
@@ -26,19 +26,622 @@
#include <QtCore/qmetaobject.h>
#include <QtCore/qtimer.h>
+#include "qdbusconnection.h"
+
+#include "qdbusconnection_p.h" // for qDBusParametersForMethod
+#include "qdbusabstractadaptor_p.h"
+
+/*!
+ \internal
+*/
+struct QDBusAdaptorInit
+{
+ QSignalSpyCallbackSet callbacks;
+ QDBusAdaptorInit()
+ {
+ extern void qt_register_signal_spy_callbacks(const QSignalSpyCallbackSet &callback_set);
+ callbacks.signal_begin_callback = QDBusAdaptorConnector::signalBeginCallback;
+ callbacks.signal_end_callback = QDBusAdaptorConnector::signalEndCallback;
+ callbacks.slot_begin_callback = 0;
+ callbacks.slot_end_callback = 0;
+ qt_register_signal_spy_callbacks(callbacks);
+
+ //QDBusAdaptorConnector::id = QObject::registerUserData();
+ }
+};
+
+Q_GLOBAL_STATIC(QDBusAdaptorInit, qAdaptorInit)
+
+QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *obj)
+{
+ qAdaptorInit();
+
+#if 0
+ if (caller->metaObject() == QDBusAdaptorConnector::staticMetaObject)
+ return 0; // it's a QDBusAdaptorConnector
+#endif
+
+ if (!obj)
+ return 0;
+ QDBusAdaptorConnector *connector = qFindChild<QDBusAdaptorConnector *>(obj);
+ if (connector)
+ connector->polish();
+ return connector;
+}
+
+QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *obj)
+{
+ qAdaptorInit();
+
+ QDBusAdaptorConnector *connector = qDBusFindAdaptorConnector(obj);
+ if (connector)
+ return connector;
+ return new QDBusAdaptorConnector(obj);
+}
+
+QString QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor)
+{
+ return adaptor->d->xml;
+}
+
+void QDBusAbstractAdaptorPrivate::saveIntrospectionXml(QDBusAbstractAdaptor *adaptor,
+ const QString &xml)
+{
+ adaptor->d->xml = xml;
+}
+
+/*!
+ \page UsingAdaptors Using Adaptors
+
+ Adaptors are special classes that are attached to any QObject-derived class and provide the
+ interface to the external world using D-Bus. Adaptors are intended to be light-weight classes
+ whose main purpose is to relay calls to and from the real object, possibly validating or
+ converting the input from the external world and, thus, protecting the real object.
+
+ Unlike multiple inheritance, adaptors can be added at any time to any object (but not removed),
+ which allows for greater flexibility when exporting existing classes. Another advantage of
+ adaptors is to provide similar but not identical functionality in methods of the same name in
+ different interfaces, a case which can be quite common when adding a new version of a standard
+ interface to an object.
+
+ In order to use an adaptor, one must create a class which inherits QDBusAbstractAdaptor. Since
+ that is a standard QObject-derived class, the Q_OBJECT macro must appear in the declaration and
+ the source file must be processed with the \link moc \endlink tool. The class must also contain
+ one or more Q_CLASSINFO entries with the "D-Bus Interface" name, declaring which interfaces it
+ is exporting.
+
+ Any public slot in the class will be accessible through the bus over messages of the MethodCall
+ type. (See \link DeclaringSlots \endlink for more information). Signals in the class will be
+ automatically relayed over D-Bus. However, not all types are allowed signals or slots' parameter
+ lists: see \link AllowedParameters \endlink for more information.
+
+ Also, any property declared with Q_PROPERTY will be automatically exposed over the Properties
+ interface on D-Bus. Since the QObject property system does not allow for non-readable
+ properties, it is not possible to declare write-only properties using adaptors.
+
+ More information:
+ - \subpage DeclaringSlots
+ - \subpage DeclaringSignals
+ - \subpage AllowedParameters
+ - \subpage UsingAnnotations
+ - \subpage AdaptorExample
+
+ \sa QDBusAbstractAdaptor
+*/
+
+/*!
+ \page AdaptorExample Example of an interface implemented with an adaptor
+
+ A sample usage of QDBusAbstractAdaptor is as follows:
+ \code
+ class MainApplicationAdaptor: public QDBusAbstractAdaptor
+ {
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "com.example.DBus.MainApplication")
+ Q_CLASSINFO("D-Bus Interface", "org.kde.DBus.MainApplication")
+ Q_PROPERTY(QString caption READ caption WRITE setCaption)
+ Q_PROPERTY(QString organizationName READ organizationName)
+ Q_PROPERTY(QString organizationDomain READ organizationDomain)
+
+ private:
+ MyApplication *app;
+
+ public:
+ MyInterfaceAdaptor(MyApplication *application)
+ : QDBusAbstractAdaptor(application), app(application)
+ {
+ connect(application, SIGNAL(aboutToQuit()), SIGNAL(aboutToQuit());
+ connect(application, SIGNAL(focusChanged(QWidget*, QWidget*)),
+ SLOT(focusChangedSlot(QWidget*, QWidget*)));
+ }
+
+ QString caption()
+ {
+ if (app->hasMainWindow())
+ return app->mainWindow()->caption();
+ return QString();
+ }
+
+ void setCaption(const QString &newCaption)
+ {
+ if (app->hasMainWindow())
+ app->mainWindow()->setCaption(newCaption);
+ }
+
+ QString organizationName()
+ {
+ return app->organizationName();
+ }
+
+ QString organizationDomain()
+ {
+ return app->organizationDomain();
+ }
+
+ public slots:
+ async void quit()
+ { app->quit(); }
+
+ void reparseConfiguration()
+ { app->reparseConfiguration(); }
+
+ QString mainWindowObject()
+ {
+ if (app->hasMainWindow())
+ return QString("/%1/mainwindow").arg(app->applicationName());
+ return QString();
+ }
+
+ void setSessionManagement(bool enable)
+ {
+ if (enable)
+ app->enableSessionManagement();
+ else
+ app->disableSessionManagement();
+ }
+
+ private slots:
+ void focusChangedSlot(QWidget *, QWidget *now)
+ {
+ if (now == app->mainWindow())
+ emit mainWindowHasFocus();
+ }
+
+ signals:
+ void aboutToQuit();
+ void mainWindowHasFocus();
+ };
+ \endcode
+
+ The code above would create an interface that could be represented more or less in the following
+ canonical representation:
+ \code
+ interface com.example.DBus.MainApplication
+ {
+ property readwrite STRING caption
+ property read STRING organizationName
+ property read STRING organizationDomain
+
+ method quit() annotation("org.freedesktop.DBus.Method.NoReply", "true")
+ method reparseConfiguration()
+ method mainWindowObject(out STRING)
+ method disableSessionManagement(in BOOLEAN enable)
+
+ signal aboutToQuit()
+ signal mainWindowHasFocus()
+ }
+
+ interface org.kde.DBus.MainApplication
+ {
+ ....
+ }
+ \endcode
+
+ This adaptor could be used in the application's constructor as follows:
+ \code
+ MyApplication::MyApplication()
+ {
+ [...]
+
+ // create the MainApplication adaptor:
+ new MainApplicationAdaptor(this);
+
+ // connect to D-Bus:
+ QDBusConnection connection = QDBusConnection::addConnection(QDBusConnection::SessionBus);
+
+ // register us as an object:
+ connection.registerObject("/MainApplication", this);
+
+ [...]
+ }
+ \endcode
+
+ Break-down analysis:
+ - \subpage AdaptorExampleHeader
+ - \subpage AdaptorExampleProperties
+ - \subpage AdaptorExampleConstructor
+ - \subpage AdaptorExampleSlots
+ - \subpage AdaptorExampleSignals
+*/
+
+/*!
+ \page AdaptorExampleHeader The header
+
+ The header of the example is:
+ \code
+ class MainApplicationAdaptor: public QDBusAbstractAdaptor
+ {
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "com.example.DBus.MainApplication")
+ Q_CLASSINFO("D-Bus Interface", "org.kde.DBus.MainApplication")
+ \endcode
+
+ The code does the following:
+ - it declares the adaptor MainApplicationAdaptor, which descends from QDBusAbstractAdaptor
+ - it declares the Qt Meta Object data using the #Q_OBJECT macro
+ - it declares the names of two D-Bus interfaces it implements. Those interfaces are equal in all
+ aspects.
+*/
+
+/*!
+ \page AdaptorExampleProperties The properties
+
+ The properties are declared as follows:
+ \code
+ Q_PROPERTY(QString caption READ caption WRITE setCaption)
+ Q_PROPERTY(QString organizationName READ organizationName)
+ Q_PROPERTY(QString organizationDomain READ organizationDomain)
+ \endcode
+
+ And are implemented as follows:
+ \code
+ QString caption()
+ {
+ if (app->hasMainWindow())
+ return app->mainWindow()->caption();
+ return QString();
+ }
+
+ void setCaption(const QString &newCaption)
+ {
+ if (app->hasMainWindow())
+ app->mainWindow()->setCaption(newCaption);
+ }
+
+ QString organizationName()
+ {
+ return app->organizationName();
+ }
+
+ QString organizationDomain()
+ {
+ return app->organizationDomain();
+ }
+ \endcode
+
+ The code declares three properties: one of them is a read-write property called "caption" of
+ string type. The other two are read-only, also of the string type.
+
+ The properties organizationName and organizationDomain are simple relays of the app object's
+ organizationName and organizationDomain properties. However, the caption property requires
+ verifying if the application has a main window associated with it: if there isn't any, the
+ caption property is empty. Note how it is possible to access data defined in other objects
+ through the getter/setter functions.
+ */
+
+/*!
+ \page AdaptorExampleConstructor The constructor
+
+ The constructor:
+ \code
+ MyInterfaceAdaptor(MyApplication *application)
+ : QDBusAbstractAdaptor(application), app(application)
+ {
+ connect(application, SIGNAL(aboutToQuit()), SIGNAL(aboutToQuit());
+ connect(application, SIGNAL(focusChanged(QWidget*, QWidget*)),
+ SLOT(focusChangedSlot(QWidget*, QWidget*)));
+ }
+ \endcode
+
+ The constructor does the following:
+ - it initialises its base class (QDBusAbstractAdaptor) with the parent object it is related to.
+ - it stores the app pointer in a member variable. Note that it would be possible to access the
+ same object using the QDBusAbstractAdaptor::object() function, but it would be necessary to
+ use \a static_cast<> to properly access the methods in MyApplication that are not part of
+ QObject.
+ - it connects the application's signal \a aboutToQuit to its own signal \a aboutToQuit.
+ - it connects the application's signal \a focusChanged to a private slot to do some further
+ processing before emitting a D-Bus signal.
+
+ Note that there is no destructor in the example. An eventual destructor could be used to emit
+ one last signal before the object is destroyed, for instance.
+*/
+
+/*!
+ \page AdaptorExampleSlots Slots/methods
+
+ The public slots in the example (which will be exported as D-Bus methods) are the following:
+ \code
+ public slots:
+ async void quit()
+ { app->quit(); }
+
+ void reparseConfiguration()
+ { app->reparseConfiguration(); }
+
+ QString mainWindowObject()
+ {
+ if (app->hasMainWindow())
+ return QString("/%1/mainwindow").arg(app->applicationName());
+ return QString();
+ }
+
+ void setSessionManagement(bool enable)
+ {
+ if (enable)
+ app->enableSessionManagement();
+ else
+ app->disableSessionManagement();
+ }
+ \endcode
+
+ This snippet of code defines 4 methods with different properties each:
+ - \p quit: this method takes no parameters and is defined to be asynchronous. That is, callers
+ are expected to use "fire-and-forget" mechanism when calling this method, since it provides no
+ useful reply. This is represented in D-Bus by the use of the
+ org.freedesktop.DBus.Method.NoReply annotation. See #Q_ASYNC for more information on
+ asynchronous methods
+
+ - \p reparseConfiguration: this simple method, with no input or output arguments simply relays
+ the call to the application's reparseConfiguration member function.
+
+ - \p mainWindowObject: this method takes no input parameter, but returns one string output
+ argument, containing the path to the main window object (if the application has a main
+ window), or an empty string if it has no main window. Note that this method could have also
+ been written: void mainWindowObject(QString &path).
+
+ - \p setSessionManagement: this method takes one input argument (a boolean) and, depending on
+ its value, it calls one function or another in the application.
+
+ \sa #Q_ASYNC
+*/
+
+/*!
+ \page AdaptorExampleSignals Signals
+
+ The signals in this example are defined as follows:
+ \code
+ signals:
+ void aboutToQuit();
+ void mainWindowHasFocus();
+ \endcode
+
+ However, signal definition isn't enough: signals have to be emitted. One simple way of emitting
+ signals is to connect another signal to them, so that Qt's signal handling system chains them
+ automatically. This is what is done for the \a aboutToQuit signal (see \ref
+ AdaptorExampleConstructor).
+
+ When this is the case, one can use the QDBusAbstractAdaptor::setAutoRelaySignals to
+ automatically connect every signal from the real object to the adaptor.
+
+ When simple signal-to-signal connection isn't enough, one can use a private slot do do some
+ work. This is what was done for the mainWindowHasFocus signal:
+ \code
+ private slots:
+ void focusChangedSlot(QWidget *, QWidget *now)
+ {
+ if (now == app->mainWindow())
+ emit mainWindowHasFocus();
+ }
+ \endcode
+
+ This private slot (which will not be exported as a method via D-Bus) was connected to the
+ \a focusChanged signal in the adaptor's constructor. It is therefore able to shape the
+ application's signal into what the interface expects it to be.
+*/
+
+/*!
+ \page DeclaringSlots Declaring slots
+
+ Slots in D-Bus adaptors are declared just like normal, public slots, but their parameters must
+ follow certain rules (see \ref AllowedParameters for more information). Slots whose parameters
+ do not follow those rules or that are not public will not be accessible via D-Bus.
+
+ Slots can be of three kinds:
+ -# Asynchronous
+ -# Input-only
+ -# Input-and-output
+
+ \par Asynchronous slots
+ Asynchronous slots are those that do not normally return any reply to the caller. For that
+ reason, they cannot take any output parameters. In most cases, by the time the first line
+ of the slot is run, the caller function has already resumed working.
+
+ \par
+ However, slots must rely on that behavior. Scheduling and message-dispatching issues could
+ change the order in which the slot is run. Code intending to synchronize with the caller
+ should provide its own method of synchronization.
+
+ \par
+ Asynchronous slots are marked by the keyword \p #async or \p #Q_ASYNC in the method
+ signature, before the \p void return type and the slot name. (See the \p quit slot in the
+ \ref AdaptorExample "adaptor example").
+
+ \par Input-only slots
+ Input-only slots are normal slots that take parameters passed by value or by constant
+ reference. However, unlike asynchronous slots, the caller is usually waiting for completion
+ of the callee before resuming operation. Therefore, non-asynchronous slots should not block
+ or should state it its documentation that they may do so.
+
+ \par
+ Input-only slots have no special marking in their signature, except that they take only
+ parameters passed by value or by constant reference. Optionally, slots can take a
+ QDBusMessage parameter as a last parameter, which can be used to perform additional
+ analysis of the method call message.
+
+ \par Input and output slots
+ Like input-only slots, input-and-output slots are those that the caller is waiting for a
+ reply. Unlike input-only ones, though, this reply will contain data. Slots that output data
+ may contain non-constant references and may return a value as well. However, the output
+ parameters must all appear at the end of the argument list and may not have input arguments
+ interleaved. Optionally, a QDBusMessage argument may appear between the input and the
+ output arguments.
+
+ \note When a caller places a method call and waits for a reply, it will only wait for so long.
+ Slots intending to take a long time to complete should make that fact clear in
+ documentation so that callers properly set higher timeouts.
+
+ Method replies are generated automatically with the contents of the output parameters (if there
+ were any) by the QtDBus implementation. Slots need not worry about constructing proper
+ QDBusMessage objects and sending them over the connection.
+
+ However, the possibility of doing so remains there. Should the slot find out it needs to send a
+ special reply or even an error, it can do so by using QDBusMessage::methodReply or
+ QDBusMessage::error on the QDBusMessage parameter and send it with QDBusConnection::send. The
+ QtDBus implementation will not generate any reply if the slot did so.
+
+ \sa \ref UsingAdaptors, \ref DeclaringSignals, \ref AllowedParameters, QDBusConnection,
+ QDBusMessage
+*/
+
+/*!
+ \page DeclaringSignals Declaring signals
+
+ Any signal in a class derived from QDBusAbstractAdaptor will be automatically relayed into
+ D-Bus, provided that the signal's parameters conform to certain rules (see \ref
+ AllowedParameters for more information). No special code is necessary to make this relay.
+
+ However, signals must still be emitted. The easiest way to emit an adaptor signal is to connect
+ another signal to it, so that the Qt signal/slot mechanism automatically emits the adaptor
+ signal too. This can be done in the adaptor's constructor, as has been done in the \ref
+ AdaptorExample "adaptor example".
+
+ The convenience function QDBusAbstractAdaptor::setAutoRelaySignals can also be used to connect
+ or disconnect every signal in the real object to the same signal in the adaptor. It will inspect
+ the list of signals in both classes and connect those that have exact parameter match.
+
+ \sa \ref UsingAdaptors, \ref DeclaringSlots, \ref AllowedParameters, QDBusAbstractAdaptor
+*/
+
+/*!
+ \page AllowedParameters Allowed parameter types
+
+ D-Bus has a very limited set of types that can be sent and received over the bus. They are
+ listed below, along with the D-Bus type they relate to:
+ - unsigned char / uchar (BYTE)
+ - short (INT16)
+ - unsigned short / ushort (UINT16)
+ - int (INT32)
+ - unsigned int / uint (UINT32)
+ - qlonglong (INT64)
+ - qulonglong (UINT64)
+ - bool (BOOLEAN)
+ - double (DOUBLE)
+ - QString (STRING)
+ - QByteArray (ARRAY of BYTE)
+ - QStringList (ARRAY of STRING)
+ - QVariant / QDBusVariant (VARIANT)
+ - QVariantList (ARRAY of VARIANT)
+ - QVariantMap (ARRAY of DICT_ENTRY of (STRING, VARIANT))
+
+ The last two types may be used to receive any array (except string and byte arrays), any structs
+ and any maps. However, it is currently not possible to generate external function definitions
+ containing specific types of lists, structs and maps.
+
+ All of the types above may be passed by value or by constant reference for input arguments to
+ slots as well as the output arguments to signals. When used as output arguments for slots, they
+ can all be used as non-constant references or the return type.
+
+ Additionally, slots can have one parameter of type \p const \p QDBusMessage \p \&, which must
+ appear at the end of the input parameter list, before any output parameters. Signals cannot have
+ this parameter.
+
+ \warning You may not use any type that is not on the list above, including \a typedefs to the
+ types listed. This also includes QList<QVariant> and QMap<QString,QVariant>.
+*/
+
+/*!
+ \page UsingAnnotations Using annotations in adaptors
+
+ It is currently not possible to specify arbitrary annotations in adaptors.
+*/
+
+/*!
+ \class QDBusAbstractAdaptor
+ \brief Abstract adaptor for 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 (see object()) 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.
+
+ 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 \ref UsingAdaptors, QDBusConnection
+*/
+
+/*!
+ Constructs a QDBusAbstractAdaptor with \a parent as the object we refer to.
+
+ \param parent the real object we're the adaptor for
+
+ \warning Use object() to retrieve the object passed as \a parent to this constructor. The real
+ parent object (as retrieved by QObject::parent()) may be something else.
+*/
QDBusAbstractAdaptor::QDBusAbstractAdaptor(QObject* parent)
- : QObject(parent)
+ : d(new QDBusAbstractAdaptorPrivate)
{
- QTimer::singleShot(0, this, SLOT(polish()));
+ QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(parent);
+ setParent(connector);
+
+ connector->waitingForPolish = true;
+ QTimer::singleShot(0, connector, SLOT(polish()));
}
+/*!
+ 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()
{
+ delete d;
+}
+
+/*!
+ Returns the QObject that we're the adaptor for. This is the same object that was passed as an
+ argument to the QDBusAbstractAdaptor constructor.
+*/
+QObject* QDBusAbstractAdaptor::object() const
+{
+ return parent()->parent();
}
+/*!
+ 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.
+
+ \param enable if 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();
for (int idx = staticMetaObject.methodCount(); idx < us->methodCount(); ++idx) {
QMetaMethod mm = us->method(idx);
@@ -46,19 +649,165 @@ void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable)
continue;
// try to connect/disconnect to a signal on the parent that has the same method signature
- QByteArray sig = mm.signature();
+ QByteArray sig = QMetaObject::normalizedSignature(mm.signature());
+ if (them->indexOfSignal(sig) == -1)
+ continue;
sig.prepend(QSIGNAL_CODE + '0');
+ object()->disconnect(sig, this, sig);
if (enable)
- connect(parent(), sig, sig);
- else
- parent()->disconnect(sig, this, sig);
+ connect(object(), sig, sig, Qt::QueuedConnection);
+ }
+}
+
+QDBusAdaptorConnector::QDBusAdaptorConnector(QObject *parent)
+ : QObject(parent), waitingForPolish(false), lastSignalIdx(0), argv(0)
+{
+}
+
+QDBusAdaptorConnector::~QDBusAdaptorConnector()
+{
+}
+
+void QDBusAdaptorConnector::addAdaptor(QDBusAbstractAdaptor *adaptor)
+{
+ // find the interface name
+ const QMetaObject *mo = adaptor->metaObject();
+ while (mo != &QDBusAbstractAdaptor::staticMetaObject) {
+ int ciend = mo->classInfoCount();
+ for (int i = mo->classInfoOffset(); i < ciend; ++i) {
+ QMetaClassInfo mci = mo->classInfo(i);
+ if (strcmp(mci.name(), QCLASSINFO_DBUS_INTERFACE) == 0 && *mci.value()) {
+ // find out if this interface exists first
+ QString interface = QString::fromUtf8(mci.value());
+ AdaptorMap::Iterator it = qLowerBound(adaptors.begin(), adaptors.end(), interface);
+ if (it != adaptors.end() && it->interface == interface) {
+ // exists. Replace it (though it's probably the same)
+ it->adaptor = adaptor;
+ it->metaObject = mo;
+ } else {
+ // create a new one
+ AdaptorData entry;
+ entry.interface = interface;
+ entry.adaptor = adaptor;
+ entry.metaObject = mo;
+ adaptors << entry;
+ }
+ }
+ }
+
+ mo = mo->superClass();
+ }
+
+ // connect the adaptor's signals to our relaySlot slot
+ mo = adaptor->metaObject();
+ for (int i = QDBusAbstractAdaptor::staticMetaObject.methodCount();
+ i < mo->methodCount(); ++i) {
+ QMetaMethod mm = mo->method(i);
+
+ if (mm.methodType() != QMetaMethod::Signal)
+ continue;
+
+ QByteArray sig = mm.signature();
+ sig.prepend(QSIGNAL_CODE + '0');
+ disconnect(adaptor, sig, this, SLOT(relaySlot()));
+ connect(adaptor, sig, this, SLOT(relaySlot()));
}
}
-void QDBusAbstractAdaptor::polish()
+void QDBusAdaptorConnector::polish()
{
- // future work:
- // connect every signal in this adaptor to a slot that will relay them into D-Bus
+ if (!waitingForPolish)
+ return; // avoid working multiple times if multiple adaptors were added
+
+ waitingForPolish = false;
+ const QObjectList &objs = children();
+ foreach (QObject *obj, objs) {
+ Q_ASSERT(qobject_cast<QDBusAbstractAdaptor *>(obj));
+
+ QDBusAbstractAdaptor *adaptor = static_cast<QDBusAbstractAdaptor *>(obj);
+ addAdaptor(adaptor);
+ }
+
+ // sort the adaptor list
+ qSort(adaptors);
+}
+
+void QDBusAdaptorConnector::relaySlot()
+{
+ relay(sender());
+}
+
+void QDBusAdaptorConnector::relay(QObject *sender)
+{
+ // we're being called because there is a signal being emitted that we must relay
+ Q_ASSERT(lastSignalIdx);
+ Q_ASSERT(argv);
+ Q_ASSERT(senderMetaObject);
+
+ if (senderMetaObject != sender->metaObject()) {
+ qWarning("Inconsistency detected: QDBusAdaptorConnector::relay got called with unexpected sender object!");
+ } else {
+ QMetaMethod mm = senderMetaObject->method(lastSignalIdx);
+ QObject *object = static_cast<QDBusAbstractAdaptor *>(sender)->object();
+
+ // break down the parameter list
+ QList<int> types;
+ QByteArray signature = QMetaObject::normalizedSignature(mm.signature());
+ int inputCount = qDBusParametersForMethod(signature, types);
+ if (inputCount == -1)
+ // invalid signal signature
+ // qDBusParametersForMethod has already complained
+ return;
+ if (inputCount + 1 != types.count() ||
+ types.at(inputCount) == QDBusConnectionPrivate::messageMetaType) {
+ // invalid signal signature
+ // qDBusParametersForMethod has not yet complained about this one
+ qWarning("Cannot relay signal %s::%s", senderMetaObject->className(), mm.signature());
+ return;
+ }
+
+ signature.truncate(signature.indexOf('(')); // remove parameter decoration
+
+ QVariantList args;
+ for (int i = 1; i < types.count(); ++i)
+ args << QVariant(types.at(i), argv[i]);
+
+ // find all the interfaces this signal belongs to
+ for (const QMetaObject *mo = senderMetaObject; mo != &QDBusAbstractAdaptor::staticMetaObject;
+ mo = mo->superClass()) {
+ if (lastSignalIdx < mo->methodOffset())
+ break;
+
+ for (int i = mo->classInfoOffset(); i < mo->classInfoCount(); ++i) {
+ QMetaClassInfo mci = mo->classInfo(i);
+ if (qstrcmp(mci.name(), QCLASSINFO_DBUS_INTERFACE) == 0 && *mci.value())
+ // now emit the signal with all the information
+ emit relaySignal(object, mci.value(), signature.constData(), args);
+ }
+ }
+ }
+}
+
+void QDBusAdaptorConnector::signalBeginCallback(QObject *caller, int method_index, void **argv)
+{
+ QDBusAdaptorConnector *data = qobject_cast<QDBusAdaptorConnector *>(caller->parent());
+ if (data) {
+ data->lastSignalIdx = method_index;
+ data->argv = argv;
+ data->senderMetaObject = caller->metaObject();
+ data->polish(); // make sure it's polished
+ }
+}
+
+void QDBusAdaptorConnector::signalEndCallback(QObject *caller, int)
+{
+ QDBusAdaptorConnector *data = qobject_cast<QDBusAdaptorConnector *>(caller->parent());
+ if (data) {
+ data->lastSignalIdx = 0;
+ data->argv = 0;
+ data->senderMetaObject = 0;
+ }
}
#include "qdbusabstractadaptor.moc"
+#include "qdbusabstractadaptor_p.moc"