summaryrefslogtreecommitdiff
path: root/qt
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@trolltech.com>2006-03-06 14:29:39 +0000
committerJohn Palmieri <johnp@remedyz.boston.redhat.com>2006-06-28 08:15:13 -0400
commit71e4814d89d31873ee39fc8a7c4b645011653774 (patch)
tree50ac0fd9f6b9dc6e65fb0f009357947f2b5ff3e2 /qt
parent5085f431dba8ad1b4443083a37ad7375d4cdf612 (diff)
2006-03-06 Thiago Macieira <thiago.macieira@trolltech.com>
* qt/*: * dbus/qdbus.h: Sync with KDE Subversion revision 516237. This represents the first feature-complete version of the Qt4 bindings since I took ove maintainership.
Diffstat (limited to 'qt')
-rw-r--r--qt/Makefile.am14
-rw-r--r--qt/qdbusabstractadaptor.cpp767
-rw-r--r--qt/qdbusabstractadaptor.h12
-rw-r--r--qt/qdbusabstractadaptor_p.h127
-rw-r--r--qt/qdbusconnection.cpp547
-rw-r--r--qt/qdbusconnection.h88
-rw-r--r--qt/qdbusconnection_p.h79
-rw-r--r--qt/qdbuserror.cpp53
-rw-r--r--qt/qdbuserror.h3
-rw-r--r--qt/qdbusintegrator.cpp616
-rw-r--r--qt/qdbusinterface.cpp289
-rw-r--r--qt/qdbusinterface.h161
-rw-r--r--qt/qdbusinterface_p.h9
-rw-r--r--qt/qdbusinternalfilters.cpp369
-rw-r--r--qt/qdbusintrospection.cpp44
-rw-r--r--qt/qdbusintrospection.h2
-rw-r--r--qt/qdbusmacros.h48
-rw-r--r--qt/qdbusmarshall.cpp6
-rw-r--r--qt/qdbusmarshall_p.h (renamed from qt/qdbusmarshall.h)21
-rw-r--r--qt/qdbusmessage.cpp220
-rw-r--r--qt/qdbusmessage.h10
-rw-r--r--qt/qdbusmessage_p.h2
-rw-r--r--qt/qdbusobject.cpp97
-rw-r--r--qt/qdbusobject.h63
-rw-r--r--qt/qdbusreply.h110
-rw-r--r--qt/qdbusstandardinterfaces.cpp188
-rw-r--r--qt/qdbusstandardinterfaces.h148
-rw-r--r--qt/qdbustype.cpp214
-rw-r--r--qt/qdbustype.h5
-rw-r--r--qt/qdbusutil.cpp90
-rw-r--r--qt/qdbusvariant.h11
-rw-r--r--qt/qdbusxmlparser.cpp94
32 files changed, 3584 insertions, 923 deletions
diff --git a/qt/Makefile.am b/qt/Makefile.am
index f1719fd..b133ece 100644
--- a/qt/Makefile.am
+++ b/qt/Makefile.am
@@ -18,12 +18,14 @@ dbusinclude_HEADERS= \
qdbusstandardinterfaces.h \
qdbusutil.h \
qdbusintrospection.h \
- qdbusabstractadaptor.h
+ qdbusabstractadaptor.h \
+ qdbusreply.h
libdbus_qt4_1_la_SOURCES = \
qdbusconnection.cpp \
qdbuserror.cpp \
qdbusintegrator.cpp \
+ qdbusinternalfilters.cpp \
qdbusmarshall.cpp \
qdbusmessage.cpp \
qdbusserver.cpp \
@@ -38,6 +40,7 @@ libdbus_qt4_1_la_SOURCES = \
qdbusthread.cpp \
\
qdbusabstractadaptor.h \
+ qdbusabstractadaptor_p.h \
qdbusconnection.h \
qdbusconnection_p.h \
qdbuserror.h \
@@ -45,11 +48,12 @@ libdbus_qt4_1_la_SOURCES = \
qdbusinterface_p.h \
qdbusintrospection.h \
qdbusmacros.h \
- qdbusmarshall.h \
+ qdbusmarshall_p.h \
qdbusmessage.h \
qdbusmessage_p.h \
qdbusobject.h \
qdbusobject_p.h \
+ qdbusreply.h \
qdbusserver.h \
qdbusstandardinterfaces.h \
qdbustype.h \
@@ -57,15 +61,17 @@ libdbus_qt4_1_la_SOURCES = \
qdbusxmlparser_p.h
-qdbusabstractadaptor.lo: qdbusabstractadaptor.moc
+qdbusabstractadaptor.lo: qdbusabstractadaptor.moc qdbusabstractadaptor_p.moc
qdbusserver.lo: qdbusserver.moc
qdbusconnection.lo: qdbusconnection_p.moc
-CLEANFILES=qdbusabstractadaptor.moc qdbusserver.moc qdbusconnection.moc
+CLEANFILES=qdbusabstractadaptor.moc qdbusserver.moc qdbusconnection.moc qdbusabstractadaptor_p.moc
libdbus_qt4_1_la_LIBADD= $(DBUS_QT_LIBS) $(top_builddir)/dbus/libdbus-1.la
libdbus_qt4_1_la_LDFLAGS= -version-info 1:0 -no-undefined
+EXTRA_DIST = Doxyfile
+
%.moc: %.h
$(QT_MOC) $< > $@
endif
diff --git a/qt/qdbusabstractadaptor.cpp b/qt/qdbusabstractadaptor.cpp
index 335469e..4d6217a 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"
diff --git a/qt/qdbusabstractadaptor.h b/qt/qdbusabstractadaptor.h
index 49535f5..cc85a89 100644
--- a/qt/qdbusabstractadaptor.h
+++ b/qt/qdbusabstractadaptor.h
@@ -27,18 +27,24 @@
#include <QtCore/qobject.h>
#include "qdbusmacros.h"
+class QDBusAbstractAdaptorPrivate;
class QDBUS_EXPORT QDBusAbstractAdaptor: public QObject
{
Q_OBJECT
+protected:
+ QDBusAbstractAdaptor(QObject *parent);
+
public:
- QDBusAbstractAdaptor(QObject* parent);
~QDBusAbstractAdaptor();
+ QObject *object() const;
+
protected:
void setAutoRelaySignals(bool enable);
-private slots:
- void polish();
+private:
+ friend class QDBusAbstractAdaptorPrivate;
+ QDBusAbstractAdaptorPrivate *d;
};
#endif
diff --git a/qt/qdbusabstractadaptor_p.h b/qt/qdbusabstractadaptor_p.h
new file mode 100644
index 0000000..71bfb58
--- /dev/null
+++ b/qt/qdbusabstractadaptor_p.h
@@ -0,0 +1,127 @@
+/* -*- mode: C++; set-fill-width: 100 -*-
+ *
+ * Copyright (C) 2006 Trolltech AS. All rights reserved.
+ * Author: Thiago Macieira <thiago.macieira@trolltech.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the public API. This header file may
+// change from version to version without notice, or even be
+// removed.
+//
+// We mean it.
+//
+//
+
+#ifndef QDBUSABSTRACTADAPTORPRIVATE_H
+#define QDBUSABSTRACTADAPTORPRIVATE_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qreadwritelock.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qvector.h>
+
+#define QCLASSINFO_DBUS_INTERFACE "D-Bus Interface"
+#define QCLASSINFO_DBUS_INTROSPECTION "D-Bus Introspection"
+
+class QDBusAbstractAdaptor;
+class QDBusAdaptorConnector;
+class QDBusAdaptorManager;
+class QDBusConnectionPrivate;
+
+#if QT_VERSION < 0x040200
+/* mirrored in qobject_p.h, DON'T CHANGE without prior warning */
+struct QSignalSpyCallbackSet
+{
+ typedef void (*BeginCallback)(QObject *caller, int method_index, void **argv);
+ typedef void (*EndCallback)(QObject *caller, int method_index);
+ BeginCallback signal_begin_callback,
+ slot_begin_callback;
+ EndCallback signal_end_callback,
+ slot_end_callback;
+};
+#else
+# error Qt 4.2.0 is supposed to have a better solution!
+ CHOKE!
+#endif // Qt 4.2.0
+
+class QDBusAbstractAdaptorPrivate
+{
+public:
+ QString xml;
+
+ static QString retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor);
+ static void saveIntrospectionXml(QDBusAbstractAdaptor *adaptor, const QString &xml);
+};
+
+class QDBusAdaptorConnector: public QObject
+{
+ Q_OBJECT
+public: // typedefs
+ struct AdaptorData
+ {
+ QString interface;
+ QDBusAbstractAdaptor *adaptor;
+ const QMetaObject *metaObject;
+
+ inline bool operator<(const AdaptorData &other) const
+ { return interface < other.interface; }
+ inline bool operator<(const QString &other) const
+ { return interface < other; }
+ };
+ typedef QVector<AdaptorData> AdaptorMap;
+
+public: // methods
+ explicit QDBusAdaptorConnector(QObject *parent);
+ ~QDBusAdaptorConnector();
+
+ void addAdaptor(QDBusAbstractAdaptor *adaptor);
+ void relay(QObject *sender);
+
+public slots:
+ void relaySlot();
+ void polish();
+
+signals:
+ void relaySignal(QObject *obj, const char *interface, const char *name, const QVariantList &args);
+
+public: // member variables
+ AdaptorMap adaptors;
+ bool waitingForPolish : 1;
+
+ int lastSignalIdx;
+ void **argv;
+ const QMetaObject *senderMetaObject;
+
+public: // static members
+ static void signalBeginCallback(QObject *caller, int method_index, void **argv);
+ static void signalEndCallback(QObject *caller, int method_index);
+ //static int id;
+};
+
+extern QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *object);
+extern QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *object);
+
+#endif // QDBUSABSTRACTADAPTORPRIVATE_H
diff --git a/qt/qdbusconnection.cpp b/qt/qdbusconnection.cpp
index 8ac13e4..422b087 100644
--- a/qt/qdbusconnection.cpp
+++ b/qt/qdbusconnection.cpp
@@ -33,12 +33,10 @@
#include "qdbusobject_p.h"
#include "qdbusutil.h"
-QT_STATIC_CONST_IMPL char *QDBusConnection::default_connection_name = "qt_dbus_default_connection";
-
class QDBusConnectionManager
{
public:
- QDBusConnectionManager(): default_connection(0) {}
+ QDBusConnectionManager() {}
~QDBusConnectionManager();
void bindToApplication();
QDBusConnectionPrivate *connection(const QString &name) const;
@@ -46,7 +44,7 @@ public:
void setConnection(const QString &name, QDBusConnectionPrivate *c);
private:
- QDBusConnectionPrivate *default_connection;
+ mutable QMutex mutex;
QHash<QString, QDBusConnectionPrivate *> connectionHash;
};
@@ -54,29 +52,22 @@ Q_GLOBAL_STATIC(QDBusConnectionManager, manager);
QDBusConnectionPrivate *QDBusConnectionManager::connection(const QString &name) const
{
- return name == QLatin1String(QDBusConnection::default_connection_name) ?
- default_connection : connectionHash.value(name, 0);
+ QMutexLocker locker(&mutex);
+ return connectionHash.value(name, 0);
}
void QDBusConnectionManager::removeConnection(const QString &name)
{
+ QMutexLocker locker(&mutex);
+
QDBusConnectionPrivate *d = 0;
- if (name == QLatin1String(QDBusConnection::default_connection_name)) {
- d = default_connection;
- default_connection = 0;
- } else {
- d = connectionHash.take(name);
- }
- if (!d->ref.deref())
+ d = connectionHash.take(name);
+ if (d && !d->ref.deref())
delete d;
}
QDBusConnectionManager::~QDBusConnectionManager()
{
- if (default_connection) {
- delete default_connection;
- default_connection = 0;
- }
for (QHash<QString, QDBusConnectionPrivate *>::const_iterator it = connectionHash.constBegin();
it != connectionHash.constEnd(); ++it) {
delete it.value();
@@ -86,9 +77,7 @@ QDBusConnectionManager::~QDBusConnectionManager()
void QDBusConnectionManager::bindToApplication()
{
- if (default_connection) {
- default_connection->bindToApplication();
- }
+ QMutexLocker locker(&mutex);
for (QHash<QString, QDBusConnectionPrivate *>::const_iterator it = connectionHash.constBegin();
it != connectionHash.constEnd(); ++it) {
(*it)->bindToApplication();
@@ -102,13 +91,117 @@ void qDBusBindToApplication()
void QDBusConnectionManager::setConnection(const QString &name, QDBusConnectionPrivate *c)
{
- if (name == QLatin1String(QDBusConnection::default_connection_name))
- default_connection = c;
- else
- connectionHash[name] = c;
+ connectionHash[name] = c;
+ c->name = name;
}
+/*!
+ \fn QDBusConnection QDBus::sessionBus()
+
+ Returns a QDBusConnection object opened with the session bus. The object reference returned
+ by this function is valid until the QCoreApplication's destructor is run, when the
+ connection will be closed and the object, deleted.
+*/
+/*!
+ \fn QDBusConnection QDBus::systemBus()
+
+ Returns a QDBusConnection object opened with the system bus. The object reference returned
+ by this function is valid until the QCoreApplication's destructor is run, when the
+ connection will be closed and the object, deleted.
+*/
+
+/*!
+ \class QDBusConnection
+ \brief A connection to the D-Bus bus daemon.
+
+ This class is the initial point in a D-Bus session. Using it, you can get access to remote
+ objects, interfaces; connect remote signals to your object's slots; register objects, etc.
+
+ D-Bus connections are created using the QDBusConnection::addConnection function, which opens a
+ connection to the server daemon and does the initial handshaking, associating that connection
+ with a name. Further attempts to connect using the same name will return the same
+ connection.
+
+ The connection is then torn down using the QDBusConnection::closeConnection function.
+
+ As a convenience for the two most common connection types, the QDBus::sessionBus and
+ QDBus::systemBus functions return open connections to the session server daemon and the system
+ server daemon, respectively. Those connections are opened when first used and are closed when
+ the QCoreApplication destructor is run.
+
+ D-Bus also supports peer-to-peer connections, without the need for a bus server daemon. Using
+ this facility, two applications can talk to each other and exchange messages. This can be
+ achieved by passing an address to QDBusConnection::addConnection(const QString &, const QString
+ &) function, which was opened by another D-Bus application using QDBusServer.
+*/
+
+/*!
+ \enum QDBusConnection::BusType
+ Specifies the type of the bus connection. The valid bus types are:
+
+ \value SessionBus the session bus, associated with the running desktop session
+ \value SystemBus the system bus, used to communicate with system-wide processes
+ \value ActivationBus the activation bus, whose purpose I have no idea...
+
+ On the Session Bus, one can find other applications by the same user that are sharing the same
+ desktop session (hence the name). On the System Bus, however, processes shared for the whole
+ system are usually found.
+
+ \todo Find out what the ActivationBus is for
+*/
+
+/*!
+ \enum QDBusConnection::NameRequestMode
+ Specifies the flags for when requesting a name in the bus.
+
+ \bug Change the enum into flags and update with the new flags from the spec.
+*/
+
+/*!
+ \enum QDBusConnection::RegisterOption
+ Specifies the options for registering objects with the connection. The possible values are:
+
+ \value ExportAdaptors export the contents of adaptors found in this object
+
+ \value ExportSlots export this object's scriptable slots
+ \value ExportSignals export this object's scriptable signals
+ \value ExportProperties export this object's scriptable properties
+ \value ExportContents shorthand form for ExportSlots | ExportSignals |
+ ExportProperties
+
+ \value ExportNonScriptableSlots export all of this object's slots, including
+ non-scriptable ones
+ \value ExportNonScriptableSignals export all of this object's signals, including
+ non-scriptable ones
+ \value ExportNonScriptableProperties export all of this object's properties, including
+ non-scriptable ones
+ \value ExportNonScriptableContents export all of this object's slots, signals and
+ properties, including non-scriptable ones
+
+ \value ExportChildObjects export this object's child objects
+ \note It is currently not possible to export signals from objects. If you pass the flag
+ ExportSignals or ExportNonScriptableSignals, the registerObject() function will print a warning.
+
+ \sa QDBusConnection::registerObject, QDBusAbstractAdaptor, \ref UsingAdaptors
+*/
+
+/*!
+ \enum QDBusConnection::UnregisterMode
+ The mode for unregistering an object path:
+
+ \value UnregisterNode unregister this node only: do not unregister child objects
+ \value UnregisterTree unregister this node and all its sub-tree
+
+ Note, however, if this object was registered with the ExportChildObjects option, UnregisterNode
+ will unregister the child objects too.
+*/
+
+/*!
+ Creates a QDBusConnection object attached to the connection with name \p name.
+
+ This does not open the connection. You have to call QDBusConnection::addConnection to open it.
+*/
QDBusConnection::QDBusConnection(const QString &name)
{
d = manager()->connection(name);
@@ -116,6 +209,9 @@ QDBusConnection::QDBusConnection(const QString &name)
d->ref.ref();
}
+/*!
+ Creates a copy of the \p other connection.
+*/
QDBusConnection::QDBusConnection(const QDBusConnection &other)
{
d = other.d;
@@ -123,12 +219,21 @@ QDBusConnection::QDBusConnection(const QDBusConnection &other)
d->ref.ref();
}
+/*!
+ Disposes of this object. This does not close the connection: you have to call
+ QDBusConnection::closeConnection to do that.
+*/
QDBusConnection::~QDBusConnection()
{
if (d && !d->ref.deref())
delete d;
}
+/*!
+ Creates a copy of the connection \p other in this object. The connection this object referenced
+ before the copy is not spontaneously disconnected. See QDBusConnection::closeConnection for more
+ information.
+*/
QDBusConnection &QDBusConnection::operator=(const QDBusConnection &other)
{
if (other.d)
@@ -141,13 +246,17 @@ QDBusConnection &QDBusConnection::operator=(const QDBusConnection &other)
return *this;
}
+/*!
+ Opens a connection of type \p type to one of the known busses and associate with it the
+ connection name \p name. Returns a QDBusConnection object associated with that connection.
+*/
QDBusConnection QDBusConnection::addConnection(BusType type, const QString &name)
{
// Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection::addConnection",
// "Cannot create connection without a Q[Core]Application instance");
QDBusConnectionPrivate *d = manager()->connection(name);
- if (d)
+ if (d || name.isEmpty())
return QDBusConnection(name);
d = new QDBusConnectionPrivate;
@@ -170,6 +279,10 @@ QDBusConnection QDBusConnection::addConnection(BusType type, const QString &name
return QDBusConnection(name);
}
+/*!
+ Opens a peer-to-peer connection on address \p address and associate with it the
+ connection name \p name. Returns a QDBusConnection object associated with that connection.
+*/
QDBusConnection QDBusConnection::addConnection(const QString &address,
const QString &name)
{
@@ -177,7 +290,7 @@ QDBusConnection QDBusConnection::addConnection(const QString &address,
// "Cannot create connection without a Q[Core]Application instance");
QDBusConnectionPrivate *d = manager()->connection(name);
- if (d)
+ if (d || name.isEmpty())
return QDBusConnection(name);
d = new QDBusConnectionPrivate;
@@ -189,6 +302,13 @@ QDBusConnection QDBusConnection::addConnection(const QString &address,
return QDBusConnection(name);
}
+/*!
+ Closes the connection of name \p name.
+
+ Note that if there are still QDBusConnection objects associated with the same connection, the
+ connection will not be closed until all references are dropped. However, no further references
+ can be created using the QDBusConnection::QDBusConnection constructor.
+*/
void QDBusConnection::closeConnection(const QString &name)
{
manager()->removeConnection(name);
@@ -200,6 +320,12 @@ void QDBusConnectionPrivate::timerEvent(QTimerEvent *e)
dbus_timeout_handle(timeout);
}
+/*!
+ Sends the message over this connection, without waiting for a reply. This is suitable for errors,
+ signals, and return values as well as calls whose return values are not necessary.
+
+ \returns true if the message was queued successfully, false otherwise
+*/
bool QDBusConnection::send(const QDBusMessage &message) const
{
if (!d || !d->connection)
@@ -207,6 +333,16 @@ bool QDBusConnection::send(const QDBusMessage &message) const
return d->send(message);
}
+/*!
+ Sends the message over this connection and returns immediately after queueing it. When the reply
+ is received, the slot \p method is called in the object \p receiver. This function is suitable
+ for method calls only.
+
+ This function guarantees that the slot will be called exactly once with the reply, as long as
+ the parameter types match. If they don't, the reply cannot be delivered.
+
+ \returns true if the message was queued successfully, false otherwise.
+*/
int QDBusConnection::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
const char *method) const
{
@@ -216,15 +352,27 @@ int QDBusConnection::sendWithReplyAsync(const QDBusMessage &message, QObject *re
return d->sendWithReplyAsync(message, receiver, method);
}
+/*!
+ Sends the message over this connection and blocks, waiting for a reply. This function is
+ suitable for method calls only. It returns the reply message as its return value, which will be
+ either of type QDBusMessage::ReplyMessage or QDBusMessage::ErrorMessage.
+
+ See the QDBusInterface::call function for a more friendly way of placing calls.
+
+ \warning This function reenters the Qt event loop in order to wait for the reply, excluding user
+ input. During the wait, it may deliver signals and other method calls to your
+ application. Therefore, it must be prepared to handle a reentrancy whenever a call is
+ placed with sendWithReply().
+*/
QDBusMessage QDBusConnection::sendWithReply(const QDBusMessage &message) const
{
if (!d || !d->connection)
- return QDBusMessage::fromDBusMessage(0);
+ return QDBusMessage::fromDBusMessage(0, *this);
if (!QCoreApplication::instance()) {
DBusMessage *msg = message.toDBusMessage();
if (!msg)
- return QDBusMessage::fromDBusMessage(0);
+ return QDBusMessage::fromDBusMessage(0, *this);
DBusMessage *reply = dbus_connection_send_with_reply_and_block(d->connection, msg,
-1, &d->error);
@@ -234,108 +382,229 @@ QDBusMessage QDBusConnection::sendWithReply(const QDBusMessage &message) const
if (lastError().isValid())
return QDBusMessage::fromError(lastError());
- return QDBusMessage::fromDBusMessage(reply);
+ return QDBusMessage::fromDBusMessage(reply, *this);
} else {
QDBusReplyWaiter waiter;
if (d->sendWithReplyAsync(message, &waiter, SLOT(reply(const QDBusMessage&))) > 0) {
// enter the event loop and wait for a reply
waiter.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);
-
+
d->lastError = waiter.replyMsg; // set or clear error
return waiter.replyMsg;
}
- return QDBusMessage::fromDBusMessage(0);
+ return QDBusMessage::fromDBusMessage(0, *this);
}
}
+/*!
+ Connects the signal to the slot \p slot in object \p receiver.
+
+ \param service the service that will emit the signal, or QString() to wait for the signal
+ coming from any remote application
+ \param path the path that will emit the signal, or QString() to wait for the signal
+ coming from any object path (usually associated with an empty \p service)
+ \param interface the name of the interface to for this signal
+ \param name the name of the signal
+ \param receiver the object to connect to
+ \param slot the slot that will be invoked when the signal is emitted
+ \returns true if the connection was successful
+
+ \note The signal will only be delivered to the slot if the parameters match. This verification
+ can be done only when the signal is received, not at connection time.
+
+ \bug does not allow an empty service
+*/
bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface,
const QString &name, QObject *receiver, const char *slot)
{
return connect(service, path, interface, name, QString(), receiver, slot);
}
+/*!
+ \overload
+ Connects the signal to the slot \p slot in object \p receiver. Unlike the other
+ QDBusConnection::connect overload, this function allows one to specify the parameter signature
+ to be connected. The function will then verify that this signature can be delivered to the slot
+ specified by \p slot and return false otherwise.
+
+ \bug does not validate signature vs slot yet
+*/
bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface,
const QString &name, const QString &signature,
QObject *receiver, const char *slot)
{
- if (!receiver || !slot || !d || !d->connection)
+ if (!receiver || !slot || !d || !d->connection || !QDBusUtil::isValidInterfaceName(interface))
return false;
- QString source = getNameOwner(service);
- if (source.isEmpty())
- return false;
+ QString source;
+ if (!service.isEmpty()) {
+ source = getNameOwner(service);
+ if (source.isEmpty())
+ return false;
+ }
source += path;
// check the slot
QDBusConnectionPrivate::SignalHook hook;
if ((hook.midx = QDBusConnectionPrivate::findSlot(receiver, slot + 1, hook.params)) == -1)
return false;
-
+
hook.interface = interface;
hook.name = name;
hook.signature = signature;
- hook.obj = QPointer<QObject>(receiver);
+ hook.obj = receiver;
+
+ // avoid duplicating:
+ QDBusConnectionPrivate::SignalHookHash::ConstIterator it = d->signalHooks.find(source);
+ for ( ; it != d->signalHooks.end() && it.key() == source; ++it) {
+ const QDBusConnectionPrivate::SignalHook &entry = it.value();
+ if (entry.interface == hook.interface &&
+ entry.name == hook.name &&
+ entry.signature == hook.signature &&
+ entry.obj == hook.obj &&
+ entry.midx == hook.midx) {
+ // no need to compare the parameters if it's the same slot
+ return true; // already there
+ }
+ }
- d->signalHooks.insertMulti(source, hook);
- d->connect(receiver, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));
+ d->connectSignal(source, hook);
return true;
}
-bool QDBusConnection::registerObject(const QString &path, QObject *object, RegisterOptions options)
-{
- return registerObject(path, QString(), object, options);
-}
+/*!
+ Registers the object \p object at path \p path and returns true if the registration was
+ successful.
+
+ This function does not replace existing objects: if there is already an object registered at
+ path \p path, this function will return false. Use unregisterObject() to unregister it first.
-bool QDBusConnection::registerObject(const QString &path, const QString &interface,
- QObject *object, RegisterOptions options)
+ You cannot register an object as a child object of an object that was registered with
+ QDBusConnection::ExportChildObjects.
+*/
+bool QDBusConnection::registerObject(const QString &path, QObject *object, RegisterOptions options)
{
if (!d || !d->connection || !object || !options || !QDBusUtil::isValidObjectPath(path))
return false;
- QString iface = interface;
- if (options & ExportForAnyInterface)
- iface.clear();
-
- QDBusConnectionPrivate::ObjectDataHash& hook = d->objectHooks[path];
-
- // if we're replacing and matching any interface, then we're replacing every interface
- // this catches ExportAdaptors | Reexport too
- if (( options & ( ExportForAnyInterface | Reexport )) == ( ExportForAnyInterface | Reexport ))
- hook.clear();
-
- // we're not matching any interface, but if we're not replacing, make sure it doesn't exist yet
- else if (( options & Reexport ) == 0 && hook.find(iface) != hook.end())
+ if (options & ExportSignals) {
+ qWarning("Cannot export signals from objects. Use an adaptor for that purpose.");
return false;
+ }
- QDBusConnectionPrivate::ObjectData& data = hook[iface];
+ QStringList pathComponents = path.split(QLatin1Char('/'));
+ if (pathComponents.last().isEmpty())
+ pathComponents.removeLast();
+ QWriteLocker locker(&d->lock);
+
+ // lower-bound search for where this object should enter in the tree
+ QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode;
+ int i = 1;
+ while (node) {
+ if (pathComponents.count() == i) {
+ // this node exists
+ // consider it free if there's no object here and the user is not trying to
+ // replace the object sub-tree
+ if ((options & ExportChildObjects && !node->children.isEmpty()) || node->obj)
+ return false;
+
+ // we can add the object here
+ node->obj = object;
+ node->flags = options;
+
+ d->registerObject(node);
+ qDebug("REGISTERED FOR %s", path.toLocal8Bit().constData());
+ return true;
+ }
- data.flags = options;
- data.obj = object;
+ // find the position where we'd insert the node
+ QVector<QDBusConnectionPrivate::ObjectTreeNode::Data>::Iterator it =
+ qLowerBound(node->children.begin(), node->children.end(), pathComponents.at(i));
+ if (it != node->children.constEnd() && it->name == pathComponents.at(i)) {
+ // match: this node exists
+ node = it->node;
+
+ // are we allowed to go deeper?
+ if (node->flags & ExportChildObjects) {
+ // we're not
+ qDebug("Cannot register object at %s because %s exports its own child objects",
+ qPrintable(path), qPrintable(pathComponents.at(i)));
+ return false;
+ }
+ } else {
+ // add entry
+ QDBusConnectionPrivate::ObjectTreeNode::Data entry;
+ entry.name = pathComponents.at(i);
+ entry.node = new QDBusConnectionPrivate::ObjectTreeNode;
+ node->children.insert(it, entry);
+
+ node = entry.node;
+ }
- d->connect(object, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));
- qDebug("REGISTERED FOR %s", path.toLocal8Bit().constData());
+ // iterate
+ ++i;
+ }
- return true; // todo - check for slots etc.
+ Q_ASSERT_X(false, "QDBusConnection::registerObject", "The impossible happened");
+ return false;
}
-void QDBusConnection::unregisterObject(const QString &path)
+/*!
+ Unregisters an object that was registered with the registerObject() function and, if \p mode is
+ QDBusConnection::UnregisterTree, all of its sub-objects too.
+
+ Note that you cannot unregister objects that were not registered with registerObject().
+*/
+void QDBusConnection::unregisterObject(const QString &path, UnregisterMode mode)
{
- if (!d || !d->connection)
+ if (!d || !d->connection || !QDBusUtil::isValidObjectPath(path))
return;
- d->objectHooks.remove(path);
+ QStringList pathComponents = path.split(QLatin1Char('/'));
+ QWriteLocker locker(&d->lock);
+ QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode;
+ int i = 1;
+
+ // find the object
+ while (node) {
+ if (pathComponents.count() == i) {
+ // found it
+ node->obj = 0;
+ node->flags = 0;
+
+ if (mode == UnregisterTree) {
+ // clear the sub-tree as well
+ node->clear(); // can't disconnect the objects because we really don't know if they can
+ // be found somewhere else in the path too
+ }
+
+ return;
+ }
+
+ QVector<QDBusConnectionPrivate::ObjectTreeNode::Data>::ConstIterator it =
+ qLowerBound(node->children.constBegin(), node->children.constEnd(), pathComponents.at(i));
+ if (it == node->children.constEnd() || it->name != pathComponents.at(i))
+ break; // node not found
+
+ node = it->node;
+ ++i;
+ }
}
+/*!
+ Returns a QDBusInterface associated with the interface \p interface on object at path \p path on
+ service \p service.
+*/
QDBusInterface QDBusConnection::findInterface(const QString& service, const QString& path,
const QString& interface)
{
// create one
- QDBusInterfacePrivate *priv = new QDBusInterfacePrivate;
- priv->conn = *this;
+ QDBusInterfacePrivate *priv = new QDBusInterfacePrivate(*this);
- if (!QDBusUtil::isValidObjectPath(path) || !QDBusUtil::isValidInterfaceName(interface))
+ if (!(interface.isEmpty() || QDBusUtil::isValidInterfaceName(interface)) ||
+ !QDBusUtil::isValidObjectPath(path))
return QDBusInterface(priv);
// check if it's there first
@@ -352,28 +621,64 @@ QDBusInterface QDBusConnection::findInterface(const QString& service, const QStr
return QDBusInterface(priv); // will increment priv's refcount
}
+/*!
+ \fn QDBusConnection::findInterface(const QString &service, const QString &path)
+ Returns an interface of type \p Interface associated with the object on path \p path at service
+ \p service.
+
+ \p Interface must be a class derived from QDBusInterface.
+*/
+
+/*!
+ Returns a QDBusObject associated with the object on path \p path at service \p service.
+*/
QDBusObject QDBusConnection::findObject(const QString& service, const QString& path)
{
QDBusObjectPrivate* priv = 0;
if (d && QDBusUtil::isValidObjectPath(path)) {
QString owner = getNameOwner(service);
-
+
if (!owner.isEmpty())
priv = new QDBusObjectPrivate(d, owner, path);
}
return QDBusObject(priv, *this);
-}
+}
+
+/*!
+ Returns true if this QDBusConnection object is connected.
+
+ \note If it isn't connected, calling QDBusConnection::addConnection on the same connection name
+ will not make be connected. You need to call the QDBusConnection constructor again.
+*/
bool QDBusConnection::isConnected( ) const
{
return d && d->connection && dbus_connection_get_is_connected(d->connection);
}
+/*!
+ Returns the last error that happened in this connection.
+
+ This function is provided for low-level code. If you're using QDBusInterface::call, error codes are
+ reported by its return value.
+
+ \sa QDBusInterface, QDBusMessage
+*/
QDBusError QDBusConnection::lastError() const
{
return d ? d->lastError : QDBusError();
}
+/*!
+ Returns the unique connection name for this connection, if this QDBusConnection object is
+ connected, or an empty QString otherwise.
+
+ A Unique Connection Name is a string in the form ":x.xxx" (where x are decimal digits) that is
+ assigned by the D-Bus server daemon upon connection. It uniquely identifies this client in the
+ bus.
+
+ This function returns an empty QString for peer-to-peer connections.
+*/
QString QDBusConnection::baseService() const
{
return d && d->connection ?
@@ -381,6 +686,25 @@ QString QDBusConnection::baseService() const
: QString();
}
+/*!
+ Sends a request to the D-Bus server daemon to request the service name \p name. The flags \p
+ mode indicate how to proceed if the name is already taken or when another D-Bus client requests
+ the same name.
+
+ Service names are used to publish well-known services on the D-Bus bus, by associating a
+ friendly name to this connection. Other D-Bus clients will then be able to contact this
+ connection and the objects registered on it by using this name instead of the unique connection
+ name (see baseService()). This also allows one application to always have the same name, while
+ its unique connection name changes.
+
+ This function has no meaning in peer-to-peer connections.
+
+ This function returns true if the name is assigned to this connection now (including the case
+ when it was already assigned).
+
+ \todo probably move to the QObject representing the bus
+ \todo update the NameRequestMode flags
+*/
bool QDBusConnection::requestName(const QString &name, NameRequestMode mode)
{
static const int DBusModes[] = { DBUS_NAME_FLAG_ALLOW_REPLACEMENT, 0,
@@ -392,6 +716,17 @@ bool QDBusConnection::requestName(const QString &name, NameRequestMode mode)
retval == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER;
}
+/*!
+ Releases a name that had been requested using requestName(). This function returns true if the
+ name has been released, false otherwise.
+
+ This function has no meaning in peer-to-peer connections.
+
+ You cannot cause a name owned by another application to be released using releaseName(). Use
+ requestName() instead to assign it to your application.
+
+ \todo probably move to the QObject representing the bus
+*/
bool QDBusConnection::releaseName(const QString &name)
{
int retval = dbus_bus_release_name(d->connection, name.toUtf8(), &d->error);
@@ -401,15 +736,23 @@ bool QDBusConnection::releaseName(const QString &name)
return retval == DBUS_RELEASE_NAME_REPLY_RELEASED;
}
+/*!
+ Returns the unique connection name of the client that currently has the \p name
+ requested. Returns an empty QString in case there is no such name on the bus or if \p name is
+ not a well-formed bus name.
+
+ \todo probably move to the QObject representing the bus
+*/
QString QDBusConnection::getNameOwner(const QString& name)
{
if (QDBusUtil::isValidUniqueConnectionName(name))
return name;
if (!d || !QDBusUtil::isValidBusName(name))
return QString();
-
- QDBusMessage msg = QDBusMessage::methodCall(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
- DBUS_INTERFACE_DBUS, "GetNameOwner");
+
+ QDBusMessage msg = QDBusMessage::methodCall(QLatin1String(DBUS_SERVICE_DBUS),
+ QLatin1String(DBUS_PATH_DBUS), QLatin1String(DBUS_INTERFACE_DBUS),
+ QLatin1String("GetNameOwner"));
msg << name;
QDBusMessage reply = sendWithReply(msg);
if (!lastError().isValid() && reply.type() == QDBusMessage::ReplyMessage)
@@ -417,4 +760,56 @@ QString QDBusConnection::getNameOwner(const QString& name)
return QString();
}
-#include "qdbusconnection_p.moc"
+/*!
+ \internal
+*/
+template<int type>
+struct DefaultBus
+{
+ DefaultBus()
+ {
+ QDBusConnection con = QDBusConnection::addConnection(QDBusConnection::BusType(type),
+ QLatin1String(busName));
+ bus = new QDBusConnection(con);
+ qAddPostRoutine(clear);
+ }
+
+ ~DefaultBus()
+ {
+ delete bus;
+ }
+
+ static void clear()
+ {
+ delete bus;
+ bus = 0;
+ QDBusConnection::closeConnection(QLatin1String(busName));
+ }
+
+ static QDBusConnection *bus;
+ static const char busName[];
+};
+
+Q_GLOBAL_STATIC(DefaultBus<QDBusConnection::SessionBus>, sessionBusPtr);
+Q_GLOBAL_STATIC(DefaultBus<QDBusConnection::SystemBus>, systemBusPtr);
+
+template<>
+QT_STATIC_CONST_IMPL char DefaultBus<QDBusConnection::SessionBus>::busName[] = "qt_default_session_bus";
+template<>
+QT_STATIC_CONST_IMPL char DefaultBus<QDBusConnection::SystemBus>::busName[] = "qt_default_system_bus";
+
+template<> QDBusConnection *DefaultBus<QDBusConnection::SessionBus>::bus = 0;
+template<> QDBusConnection *DefaultBus<QDBusConnection::SystemBus>::bus = 0;
+
+namespace QDBus {
+ QDBusConnection &sessionBus()
+ {
+ return *sessionBusPtr()->bus;
+ }
+
+ QDBusConnection &systemBus()
+ {
+ return *systemBusPtr()->bus;
+ }
+}
+
diff --git a/qt/qdbusconnection.h b/qt/qdbusconnection.h
index c6913b2..dc3eb47 100644
--- a/qt/qdbusconnection.h
+++ b/qt/qdbusconnection.h
@@ -41,23 +41,38 @@ class QDBUS_EXPORT QDBusConnection
{
public:
enum BusType { SessionBus, SystemBus, ActivationBus };
+ enum NameRequestMode { NoReplace = 0, ProhibitReplace = 1, ReplaceExisting = 2 };
+ enum RegisterOption {
+ ExportAdaptors = 0x01,
+
+ ExportSlots = 0x10,
+ ExportSignals = 0x20,
+ ExportProperties = 0x40,
+ ExportContents = 0xf0,
+
+ ExportNonScriptableSlots = 0x110,
+ ExportNonScriptableSignals = 0x220,
+ ExportNonScriptableProperties = 0x440,
+ ExportNonScriptableContents = 0xff0,
+
+ ExportChildObjects = 0x1000
+ };
+ enum UnregisterMode {
+ UnregisterNode,
+ UnregisterTree
+ };
+
+ Q_DECLARE_FLAGS(RegisterOptions, RegisterOption);
- QDBusConnection(const QString &name = QLatin1String(default_connection_name));
+ QDBusConnection(const QString &name);
QDBusConnection(const QDBusConnection &other);
~QDBusConnection();
QDBusConnection &operator=(const QDBusConnection &other);
bool isConnected() const;
- QDBusError lastError() const;
-
- enum NameRequestMode { NoReplace = 0, ProhibitReplace = 1, ReplaceExisting = 2 };
- bool requestName(const QString &name, NameRequestMode mode = NoReplace);
- bool releaseName(const QString& name);
- QString getNameOwner(const QString& name);
-
-
QString baseService() const;
+ QDBusError lastError() const;
bool send(const QDBusMessage &message) const;
QDBusMessage sendWithReply(const QDBusMessage &message) const;
@@ -70,48 +85,45 @@ public:
const QString &name, const QString& signature,
QObject *receiver, const char *slot);
- enum RegisterOption {
- ExportForAnyInterface = 0x01,
- ExportAdaptors = 0x03,
-
- ExportOwnSlots = 0x10,
- ExportOwnSignals = 0x20,
- ExportOwnProperties = 0x40,
- ExportOwnContents = 0xf0,
-
- ExportNonScriptableSlots = 0x100,
- ExportNonScriptableSignals = 0x200,
- ExportNonScriptableProperties = 0x400,
- ExportNonScriptables = 0xf00,
-
- ExportChildObjects = 0x1000,
-
- Reexport = 0x100000,
- };
- Q_DECLARE_FLAGS(RegisterOptions, RegisterOption);
-
- bool registerObject(const QString &path, const QString &interface, QObject *object,
- RegisterOptions options = ExportOwnContents);
bool registerObject(const QString &path, QObject *object,
RegisterOptions options = ExportAdaptors);
- void unregisterObject(const QString &path);
+ void unregisterObject(const QString &path, UnregisterMode = UnregisterNode);
QDBusObject findObject(const QString& service, const QString& path);
QDBusInterface findInterface(const QString& service, const QString& path, const QString& interface);
-
+
+#ifndef QT_NO_MEMBER_TEMPLATES
+ template<class Interface>
+ inline Interface findInterface(const QString &service, const QString &path)
+ { return Interface(findObject(service, path)); }
+#endif
+
+ bool requestName(const QString &name, NameRequestMode mode = NoReplace);
+ bool releaseName(const QString& name);
+ QString getNameOwner(const QString& name);
static QDBusConnection addConnection(BusType type,
- const QString &name = QLatin1String(default_connection_name));
+ const QString &name);
static QDBusConnection addConnection(const QString &address,
- const QString &name = QLatin1String(default_connection_name));
- static void closeConnection(const QString &name = QLatin1String(default_connection_name));
-
- QT_STATIC_CONST char *default_connection_name;
+ const QString &name);
+ static void closeConnection(const QString &name);
private:
friend class QDBusObject;
QDBusConnectionPrivate *d;
};
+namespace QDBus {
+ QDBusConnection &sessionBus();
+ QDBusConnection &systemBus();
+}
+
+template<class Interface>
+inline Interface qDBusConnectionFindInterface(QDBusConnection &connection, const QString &service,
+ const QString &path)
+{
+ return Interface(connection.findObject(service, path));
+}
+
Q_DECLARE_OPERATORS_FOR_FLAGS(QDBusConnection::RegisterOptions)
#endif
diff --git a/qt/qdbusconnection_p.h b/qt/qdbusconnection_p.h
index cd2e48e..7904a84 100644
--- a/qt/qdbusconnection_p.h
+++ b/qt/qdbusconnection_p.h
@@ -40,13 +40,14 @@
#include "qdbuserror.h"
#include <QtCore/qatomic.h>
-#include <QtCore/qmutex.h>
+#include <QtCore/qeventloop.h>
#include <QtCore/qhash.h>
+#include <QtCore/qmutex.h>
#include <QtCore/qobject.h>
#include <QtCore/qpointer.h>
+#include <QtCore/qreadwritelock.h>
#include <QtCore/qvarlengtharray.h>
-#include <QtCore/qeventloop.h>
-#include <QtCore/qmutex.h>
+#include <QtCore/qvector.h>
#include <dbus/dbus.h>
@@ -79,16 +80,38 @@ public:
struct SignalHook
{
+ inline SignalHook() : obj(0), midx(-1) { }
QString interface, name, signature;
- QPointer<QObject> obj;
+ QObject* obj;
int midx;
QList<int> params;
};
- struct ObjectData
+ struct ObjectTreeNode
{
- QPointer<QObject> obj;
+ struct Data
+ {
+ QString name;
+ ObjectTreeNode *node;
+
+ inline bool operator<(const QString &other) const
+ { return name < other; }
+ };
+
+ inline ObjectTreeNode() : obj(0), flags(0) { }
+ inline ~ObjectTreeNode() { clear(); }
+ inline void clear()
+ {
+ foreach (const Data &entry, children) {
+ entry.node->clear();
+ delete entry.node;
+ }
+ children.clear();
+ }
+
+ QObject* obj;
int flags;
+ QVector<Data> children;
};
public:
@@ -96,11 +119,9 @@ public:
typedef QMultiHash<int, Watcher> WatcherHash;
typedef QHash<int, DBusTimeout *> TimeoutHash;
typedef QMultiHash<QString, SignalHook> SignalHookHash;
- typedef QHash<QString, ObjectData> ObjectDataHash;
- typedef QHash<QString, ObjectDataHash> ObjectHookHash;
typedef QHash<QString, QSharedDataPointer<QDBusIntrospection::Interface> > KnownInterfacesHash;
typedef QHash<QString, QDBusIntrospection::Object* > KnownObjectsHash;
-
+
public:
// public methods
QDBusConnectionPrivate(QObject *parent = 0);
@@ -113,11 +134,13 @@ public:
void closeConnection();
void timerEvent(QTimerEvent *e);
- bool handleSignal(const QString &path, const QDBusMessage &msg);
bool send(const QDBusMessage &message) const;
int sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
const char *method) const;
+ void connectSignal(const QString &key, const SignalHook &hook);
+ void registerObject(const ObjectTreeNode *node);
+ bool handleSignal(const QString &path, const QDBusMessage &msg);
bool handleSignal(const QDBusMessage &msg);
bool handleObjectCall(const QDBusMessage &message);
bool handleError();
@@ -127,13 +150,14 @@ public:
QSharedDataPointer<QDBusIntrospection::Interface> findInterface(const QString& name);
QDBusIntrospection::Object* findObject(const QString& service,
const QString& path);
-
- bool activateReply(QObject *object, int idx, const QList<int>& metaTypes,
- const QDBusMessage &msg);
+
bool activateSignal(const SignalHook& hook, const QDBusMessage &msg);
bool activateCall(QObject* object, int flags, const QDBusMessage &msg);
- bool activateAdaptor(QObject *object, int flags, const QDBusMessage &msg);
- bool activateObject(const ObjectData& data, const QDBusMessage &msg);
+ bool activateObject(const ObjectTreeNode *node, const QDBusMessage &msg);
+ bool activateInternalFilters(const ObjectTreeNode *node, const QDBusMessage &msg);
+
+ void postCallDeliveryEvent(CallDeliveryEvent *data);
+ CallDeliveryEvent *postedCallDeliveryEvent();
void deliverCall(const CallDeliveryEvent &data) const;
protected:
@@ -144,14 +168,17 @@ public slots:
void socketRead(int);
void socketWrite(int);
void objectDestroyed(QObject *o);
+ void relaySignal(QObject *obj, const char *interface, const char *name, const QVariantList &args);
public:
// public member variables
+ QString name; // this connection's name
+
DBusError error;
QDBusError lastError;
QAtomic ref;
- QMutex mutex;
+ QReadWriteLock lock;
ConnectionMode mode;
DBusConnection *connection;
DBusServer *server;
@@ -159,9 +186,13 @@ public:
WatcherHash watchers;
TimeoutHash timeouts;
SignalHookHash signalHooks;
- ObjectHookHash objectHooks;
QList<DBusTimeout *> pendingTimeouts;
+ ObjectTreeNode rootNode;
+
+ QMutex callDeliveryMutex;
+ CallDeliveryEvent *callDeliveryState; // protected by the callDeliveryMutex mutex
+
public:
// public mutable member variables
mutable KnownInterfacesHash knownInterfaces;
@@ -182,6 +213,18 @@ public:
public slots:
void reply(const QDBusMessage &msg);
-};
+};
+
+extern int qDBusParametersForMethod(const QByteArray &sig, QList<int>& metaTypes);
+extern int qDBusNameToTypeId(const char *name);
+extern bool qDBusCheckAsyncTag(const char *tag);
+
+// in qdbusinternalfilters.cpp
+extern void qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode *node,
+ const QDBusMessage &msg);
+extern void qDBusPropertyGet(const QDBusConnectionPrivate::ObjectTreeNode *node,
+ const QDBusMessage &msg);
+extern void qDBusPropertySet(const QDBusConnectionPrivate::ObjectTreeNode *node,
+ const QDBusMessage &msg);
#endif
diff --git a/qt/qdbuserror.cpp b/qt/qdbuserror.cpp
index 64b68b3..ec066e1 100644
--- a/qt/qdbuserror.cpp
+++ b/qt/qdbuserror.cpp
@@ -29,6 +29,30 @@
#include <dbus/dbus.h>
#include "qdbusmessage.h"
+/*!
+ \class QDBusError
+ \brief Represents an error received from the D-Bus bus or from remote applications found in the bus.
+
+ When dealing with the D-Bus bus service or with remote applications over D-Bus, a number of
+ error conditions can happen. This error conditions are sometimes signalled by a returned error
+ value or by a QDBusError.
+
+ C++ and Java exceptions are a valid analogy for D-Bus errors: instead of returning normally with
+ a return value, remote applications and the bus may decide to throw an error condition. However,
+ the QtDBus implementation does not use the C++ exception-throwing mechanism, so you will receive
+ QDBusErrors in the return reply (see QDBusReply::error()).
+
+ QDBusError objects are used to inspect the error name and message as received from the bus and
+ remote applications. You should not create such objects yourself to signal error conditions when
+ called from D-Bus: instead, use QDBusMessage::error and QDBusConnection::send.
+
+ \sa QDBusConnection::send, QDBusMessage, QDBusReply
+*/
+
+/*!
+ \internal
+ Constructs a QDBusError from a DBusError structure.
+*/
QDBusError::QDBusError(const DBusError *error)
{
if (!error || !dbus_error_is_set(error))
@@ -38,6 +62,10 @@ QDBusError::QDBusError(const DBusError *error)
msg = QString::fromUtf8(error->message);
}
+/*!
+ \internal
+ Constructs a QDBusError from a QDBusMessage.
+*/
QDBusError::QDBusError(const QDBusMessage &qdmsg)
{
if (qdmsg.type() != QDBusMessage::ErrorMessage)
@@ -48,6 +76,31 @@ QDBusError::QDBusError(const QDBusMessage &qdmsg)
msg = qdmsg[0].toString();
}
+/*!
+ \fn QDBusError::QDBusError(const QString &name, const QString &message)
+ \internal
+
+ Constructs an error by passing the name and message.
+*/
+
+/*!
+ \fn QDBusError::name() const
+ Returns this error's name. Error names are similar to D-Bus Interface names, like
+ "org.freedesktop.DBus.InvalidArgs".
+*/
+
+/*!
+ \fn QDBusError::message() const
+ Returns the message that the callee associated with this error. Error messages are
+ implementation defined and usually contain a human-readable error code, though this does not
+ mean it is suitable for your end-users.
+*/
+
+/*!
+ \fn QDBusError::isValid() const
+ Returns true if this is a valid error condition (i.e., if there was an error), false otherwise.
+*/
+
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const QDBusError &msg)
{
diff --git a/qt/qdbuserror.h b/qt/qdbuserror.h
index 9450088..da3d026 100644
--- a/qt/qdbuserror.h
+++ b/qt/qdbuserror.h
@@ -36,6 +36,9 @@ class QDBUS_EXPORT QDBusError
public:
QDBusError(const DBusError *error = 0);
QDBusError(const QDBusMessage& msg);
+ inline QDBusError(const QString &name, const QString &message)
+ : nm(name), msg(message)
+ { }
inline QString name() const { return nm; }
inline QString message() const { return msg; }
diff --git a/qt/qdbusintegrator.cpp b/qt/qdbusintegrator.cpp
index 4c123d1..f108fc4 100644
--- a/qt/qdbusintegrator.cpp
+++ b/qt/qdbusintegrator.cpp
@@ -26,8 +26,8 @@
#include <qcoreevent.h>
#include <qdebug.h>
#include <qmetaobject.h>
+#include <qobject.h>
#include <qsocketnotifier.h>
-#include <qcoreevent.h>
#include <qtimer.h>
#include "qdbusvariant.h"
@@ -36,9 +36,17 @@
#include "qdbusobject_p.h"
#include "qdbusmessage.h"
#include "qdbusabstractadaptor.h"
+#include "qdbusabstractadaptor_p.h"
+
+#ifndef USE_OUTSIDE_DISPATCH
+# define USE_OUTSIDE_DISPATCH 0
+#endif
int QDBusConnectionPrivate::messageMetaType = 0;
+/*!
+ \internal
+*/
struct QDBusPendingCall
{
QPointer<QObject> receiver;
@@ -48,6 +56,9 @@ struct QDBusPendingCall
const QDBusConnectionPrivate *connection;
};
+/*!
+ \internal
+*/
class CallDeliveryEvent: public QEvent
{
public:
@@ -59,11 +70,23 @@ public:
QPointer<QObject> object;
QDBusMessage message;
QList<int> metaTypes;
-
+
int flags;
int slotIdx;
};
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+/*!
+ \internal
+*/
+union integer
+{
+ short s;
+ unsigned short us;
+ unsigned char uc;
+};
+#endif
+
static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
{
Q_ASSERT(timeout);
@@ -209,6 +232,30 @@ static void qDBusNewConnection(DBusServer *server, DBusConnection *c, void *data
qDebug("SERVER: GOT A NEW CONNECTION"); // TODO
}
+#if USE_OUTSIDE_DISPATCH
+# define HANDLED DBUS_HANDLER_RESULT_HANDLED_OUTSIDE_DISPATCH
+static DBusHandlerResult qDBusSignalFilterOutside(DBusConnection *connection,
+ DBusMessage *message, void *data)
+{
+ Q_ASSERT(data);
+ Q_UNUSED(connection);
+ Q_UNUSED(message);
+
+ QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
+ if (d->mode == QDBusConnectionPrivate::InvalidMode)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; // internal error, actually
+
+ CallDeliveryEvent *e = d->postedCallDeliveryEvent();
+
+ d->deliverCall(*e);
+ delete e;
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+#else
+# define HANDLED DBUS_HANDLER_RESULT_HANDLED
+#endif
+
static DBusHandlerResult qDBusSignalFilter(DBusConnection *connection,
DBusMessage *message, void *data)
{
@@ -219,23 +266,52 @@ static DBusHandlerResult qDBusSignalFilter(DBusConnection *connection,
if (d->mode == QDBusConnectionPrivate::InvalidMode)
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- int msgType = dbus_message_get_type(message);
- bool handled = false;
-
- QDBusMessage amsg = QDBusMessage::fromDBusMessage(message);
+ QDBusMessage amsg = QDBusMessage::fromDBusMessage(message, QDBusConnection(d->name));
qDebug() << "got message:" << amsg;
+ bool handled = false;
+ int msgType = dbus_message_get_type(message);
if (msgType == DBUS_MESSAGE_TYPE_SIGNAL) {
handled = d->handleSignal(amsg);
} else if (msgType == DBUS_MESSAGE_TYPE_METHOD_CALL) {
handled = d->handleObjectCall(amsg);
}
- return handled ? DBUS_HANDLER_RESULT_HANDLED :
- DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ return handled ? HANDLED :
+ DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode *haystack)
+{
+ foreach (const QDBusConnectionPrivate::ObjectTreeNode::Data &entry, haystack->children)
+ huntAndDestroy(needle, entry.node);
+
+ if (needle == haystack->obj) {
+ haystack->obj = 0;
+ haystack->flags = 0;
+ }
+}
+
+static void huntAndEmit(DBusConnection *connection, DBusMessage *msg,
+ QObject *needle, QDBusConnectionPrivate::ObjectTreeNode *haystack,
+ const QString &path = QString())
+{
+ foreach (const QDBusConnectionPrivate::ObjectTreeNode::Data &entry, haystack->children)
+ huntAndEmit(connection, msg, needle, entry.node, path + QLatin1String("/") + entry.name);
+
+ if (needle == haystack->obj && haystack->flags & QDBusConnection::ExportAdaptors) {
+ QByteArray p = path.toLatin1();
+ if (p.isEmpty())
+ p = "/";
+ qDebug() << p;
+ DBusMessage *msg2 = dbus_message_copy(msg);
+ dbus_message_set_path(msg2, p);
+ dbus_connection_send(connection, msg2, 0);
+ dbus_message_unref(msg2);
+ }
}
-static bool checkAsyncTag(const char *tag)
+bool qDBusCheckAsyncTag(const char *tag)
{
if (!tag || !*tag)
return false;
@@ -288,27 +364,11 @@ static bool typesMatch(int metaId, QVariant::Type variantType)
return false; // no match
}
-static int returnTypeId(const char *name)
+int qDBusNameToTypeId(const char *name)
{
- if (!name || !*name)
- return QMetaType::Void;
-
- // force normalizedSignature to work for us
- QVarLengthArray<char, 32> buf(strlen(name) + 3);
- buf.append("_(", 2);
- buf.append(name, strlen(name));
- buf.append(')');
-
- QByteArray normalized = QMetaObject::normalizedSignature( buf.data() );
- normalized.truncate(normalized.length() - 1);
- return QMetaType::type(normalized.constData() + 2);
-}
-
-static int typeId(const char *type)
-{
- int id = static_cast<int>( QVariant::nameToType(type) );
+ int id = static_cast<int>( QVariant::nameToType(name) );
if (id == QVariant::UserType)
- id = QMetaType::type(type);
+ id = QMetaType::type(name);
switch (id) {
case QVariant::Bool:
@@ -334,11 +394,10 @@ static int typeId(const char *type)
default:
if (id == qMetaTypeId<QDBusVariant>() || id == QDBusConnectionPrivate::messageMetaType)
return id;
-
+
return 0; // invalid
}
}
-
// calculates the metatypes for the method
// the slot must have the parameters in the following form:
@@ -352,7 +411,7 @@ static int typeId(const char *type)
// metaTypes.count() >= retval + 1 in all cases
//
// sig must be the normalised signature for the method
-static int parametersForMethod(const QByteArray &sig, QList<int>& metaTypes)
+int qDBusParametersForMethod(const QByteArray &sig, QList<int>& metaTypes)
{
if (sig.indexOf('<') != -1) {
qWarning("Could not parse the method '%s'", sig.constData());
@@ -376,21 +435,15 @@ static int parametersForMethod(const QByteArray &sig, QList<int>& metaTypes)
if (type.endsWith('&')) {
type.truncate(type.length() - 1);
- int id = typeId(type);
+ int id = qDBusNameToTypeId(type);
if (id == 0) {
qWarning("Could not parse the method '%s'", sig.constData());
// invalid type in method parameter list
return -1;
}
-
- metaTypes.append( id );
-
- if (metaTypes.last() == 0) {
- qWarning("Could not parse the method '%s'", sig.constData());
- // void?
- return -1;
- }
+ metaTypes.append( id );
+ seenMessage = true; // it cannot appear anymore anyways
continue;
}
@@ -400,13 +453,13 @@ static int parametersForMethod(const QByteArray &sig, QList<int>& metaTypes)
return -1; // not allowed
}
- int id = typeId(type);
+ int id = qDBusNameToTypeId(type);
if (id == 0) {
qWarning("Could not parse the method '%s'", sig.constData());
// invalid type in method parameter list
return -1;
}
- metaTypes.append(id);
+ metaTypes.append(id);
++inputCount;
if (id == QDBusConnectionPrivate::messageMetaType)
@@ -417,14 +470,14 @@ static int parametersForMethod(const QByteArray &sig, QList<int>& metaTypes)
}
static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags,
- const QDBusTypeList &types, QList<int>& metaTypes, int &msgPos)
+ const QDBusTypeList &types, QList<int>& metaTypes)
{
// find the first slot
const QMetaObject *super = mo;
- while (qstrcmp(super->className(), "QObject") != 0 &&
- qstrcmp(super->className(), "QDBusAbstractAdaptor") != 0)
+ while (super != &QObject::staticMetaObject &&
+ super != &QDBusAbstractAdaptor::staticMetaObject)
super = super->superClass();
-
+
int attributeMask = (flags & QDBusConnection::ExportNonScriptableSlots) ?
0 : QMetaMethod::Scriptable;
@@ -446,48 +499,46 @@ static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags,
if (paren != name.length() || !sig.startsWith( name ))
continue;
- int returnType = returnTypeId(mm.typeName());
- bool isAsync = checkAsyncTag(mm.tag());
+ int returnType = qDBusNameToTypeId(mm.typeName());
+ bool isAsync = qDBusCheckAsyncTag(mm.tag());
// consistency check:
if (isAsync && returnType != QMetaType::Void)
continue;
- int inputCount = parametersForMethod(sig, metaTypes);
+ int inputCount = qDBusParametersForMethod(sig, metaTypes);
if (inputCount == -1)
continue; // problem parsing
metaTypes[0] = returnType;
- msgPos = 0;
+ bool hasMessage = false;
if (inputCount > 0 &&
metaTypes.at(inputCount) == QDBusConnectionPrivate::messageMetaType) {
- // no input parameters is allowed as long as the message meta type is there
- msgPos = inputCount;
+ // "no input parameters" is allowed as long as the message meta type is there
+ hasMessage = true;
--inputCount;
}
- if (inputCount) {
- // try to match the parameters
- if (inputCount < types.count())
- continue; // not enough parameters
+ // try to match the parameters
+ if (inputCount != types.count())
+ continue; // not enough parameters
- bool matches = true;
- int i;
- for (i = 0; i < types.count(); ++i)
- if ( !typesMatch(metaTypes.at(i + 1), types.at(i).qvariantType()) ) {
- matches = false;
- break;
- }
+ bool matches = true;
+ int i;
+ for (i = 0; i < types.count(); ++i)
+ if ( !typesMatch(metaTypes.at(i + 1), types.at(i).qvariantType()) ) {
+ matches = false;
+ break;
+ }
- if (!matches)
- continue; // we didn't match them all
+ if (!matches)
+ continue; // we didn't match them all
- // consistency check:
- if (isAsync && metaTypes.count() > i + 1)
- continue;
- }
+ // consistency check:
+ if (isAsync && metaTypes.count() > i + 1)
+ continue;
- if (!msgPos && (mm.attributes() & attributeMask) != attributeMask)
+ if (hasMessage && (mm.attributes() & attributeMask) != attributeMask)
continue; // not exported
// if we got here, this slot matched
@@ -496,32 +547,11 @@ static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags,
// no slot matched
return -1;
-}
-
-bool QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook,
- const QDBusMessage &msg)
-{
- // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal
- // that was received from D-Bus
- //
- // Signals are delivered to slots if the parameters match
- // Slots can have less parameters than there are on the message
- // Slots can optionally have one final parameter that is a QDBusMessage
- // Slots receive read-only copies of the message (i.e., pass by value or by const-ref)
- return activateReply(hook.obj, hook.midx, hook.params, msg);
}
-bool QDBusConnectionPrivate::activateReply(QObject *object, int idx, const QList<int> &metaTypes,
- const QDBusMessage &msg)
+static CallDeliveryEvent* prepareReply(QObject *object, int idx, const QList<int> &metaTypes,
+ const QDBusMessage &msg)
{
- // This is called by qDBusResultReceived and is used to deliver the return value
- // of a remote function call.
- //
- // There is only one connection and it is specified by idx
- // The slot must have the same parameter types that the message does
- // The slot may have less parameters than the message
- // The slot may optionally have one final parameter that is QDBusMessage
- // The slot receives read-only copies of the message (i.e., pass by value or by const-ref)
Q_ASSERT(object);
int n = metaTypes.count() - 1;
@@ -531,21 +561,37 @@ bool QDBusConnectionPrivate::activateReply(QObject *object, int idx, const QList
// check that types match
for (int i = 0; i < n; ++i)
if (!typesMatch(metaTypes.at(i + 1), msg.at(i).type()))
- return false; // no match
+ return 0; // no match
// we can deliver
// prepare for the call
CallDeliveryEvent *data = new CallDeliveryEvent;
- data->conn = this;
data->object = object;
data->flags = 0;
data->message = msg;
data->metaTypes = metaTypes;
data->slotIdx = idx;
- QCoreApplication::postEvent( this, data );
-
- return true;
+ return data;
+}
+
+bool QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook,
+ const QDBusMessage &msg)
+{
+ // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal
+ // that was received from D-Bus
+ //
+ // Signals are delivered to slots if the parameters match
+ // Slots can have less parameters than there are on the message
+ // Slots can optionally have one final parameter that is a QDBusMessage
+ // Slots receive read-only copies of the message (i.e., pass by value or by const-ref)
+ CallDeliveryEvent *call = prepareReply(hook.obj, hook.midx, hook.params, msg);
+ if (call) {
+ call->conn = this;
+ postCallDeliveryEvent(call);
+ return true;
+ }
+ return false;
}
bool QDBusConnectionPrivate::activateCall(QObject* object, int flags,
@@ -574,22 +620,25 @@ bool QDBusConnectionPrivate::activateCall(QObject* object, int flags,
// in the message's first position. If there are non-const reference parameters to the
// slot, they must appear at the end and will be placed in the subsequent message
// positions.
-
- Q_ASSERT(object);
+
+ if (!object)
+ return false;
QList<int> metaTypes;
int idx;
- int msgPos;
-
+
{
const QMetaObject *mo = object->metaObject();
QDBusTypeList typeList(msg.signature().toUtf8());
// find a slot that matches according to the rules above
- idx = ::findSlot(mo, msg.name().toUtf8(), flags, typeList, metaTypes, msgPos);
- if (idx == -1)
- // no match
- return false;
+ idx = ::findSlot(mo, msg.name().toUtf8(), flags, typeList, metaTypes);
+ if (idx == -1) {
+ // try with no parameters, but with a QDBusMessage
+ idx = ::findSlot(mo, msg.name().toUtf8(), flags, QDBusTypeList(), metaTypes);
+ if (metaTypes.count() != 2 || metaTypes.at(1) != messageMetaType)
+ return false;
+ }
}
// found the slot to be called
@@ -606,12 +655,34 @@ bool QDBusConnectionPrivate::activateCall(QObject* object, int flags,
call->metaTypes = metaTypes;
call->slotIdx = idx;
- QCoreApplication::postEvent( this, call );
+ postCallDeliveryEvent(call);
// ready
return true;
}
+void QDBusConnectionPrivate::postCallDeliveryEvent(CallDeliveryEvent *data)
+{
+#if USE_OUTSIDE_DISPATCH
+ callDeliveryMutex.lock();
+ callDeliveryState = data;
+#else
+ QCoreApplication::postEvent( this, data );
+#endif
+}
+
+CallDeliveryEvent *QDBusConnectionPrivate::postedCallDeliveryEvent()
+{
+ CallDeliveryEvent *e = callDeliveryState;
+ Q_ASSERT(e && e->conn == this);
+
+ // release it:
+ callDeliveryState = 0;
+ callDeliveryMutex.unlock();
+
+ return e;
+}
+
void QDBusConnectionPrivate::deliverCall(const CallDeliveryEvent& data) const
{
// resume state:
@@ -622,12 +693,6 @@ void QDBusConnectionPrivate::deliverCall(const CallDeliveryEvent& data) const
params.reserve(metaTypes.count());
#if __BYTE_ORDER != __LITTLE_ENDIAN
- union integer
- {
- short s;
- unsigned short us;
- unsigned char uc;
- }
QVarLengthArray<integer, 4> auxParameters;
#endif
// let's create the parameter list
@@ -637,20 +702,20 @@ void QDBusConnectionPrivate::deliverCall(const CallDeliveryEvent& data) const
// add the input parameters
int i;
- for (i = 0; i < msg.count(); ++i) {
- int id = metaTypes[i + 1];
+ for (i = 1; i <= msg.count(); ++i) {
+ int id = metaTypes[i];
if (id == QDBusConnectionPrivate::messageMetaType)
break;
#if __BYTE_ORDER == __LITTLE_ENDIAN
- params.append(const_cast<void *>( msg.at(i).constData() ));
+ params.append(const_cast<void *>( msg.at(i - 1).constData() ));
#else
- if (id == msg.at(i).type())
- params.append(const_cast<void *>( msg.at(i).constData() ));
+ if (id == int(msg.at(i).type()))
+ params.append(const_cast<void *>( msg.at(i - 1).constData() ));
else {
// need some help
integer aux;
- const QVariant &var = msg.at(i);
+ const QVariant &var = msg.at(i - 1);
if (id == QMetaType::Short)
aux.s = var.toInt();
else if (id == QMetaType::UShort)
@@ -664,7 +729,7 @@ void QDBusConnectionPrivate::deliverCall(const CallDeliveryEvent& data) const
}
bool takesMessage = false;
- if (metaTypes.count() > i + 1 && metaTypes[i + 1] == QDBusConnectionPrivate::messageMetaType) {
+ if (metaTypes.count() > i && metaTypes[i] == QDBusConnectionPrivate::messageMetaType) {
params.append(const_cast<void*>(static_cast<const void*>(&msg)));
takesMessage = true;
++i;
@@ -699,14 +764,15 @@ void QDBusConnectionPrivate::deliverCall(const CallDeliveryEvent& data) const
// normal reply
QDBusMessage reply = QDBusMessage::methodReply(msg);
reply += outputArgs;
-
+
qDebug() << "Automatically sending reply:" << reply;
send(reply);
}
else {
// generate internal error
- QDBusMessage reply = QDBusMessage::error(msg, "com.trolltech.QtDBus.InternalError",
- "Failed to deliver message");
+ QDBusMessage reply = QDBusMessage::error(msg,
+ QLatin1String("com.trolltech.QtDBus.InternalError"),
+ QLatin1String("Failed to deliver message"));
qDebug("Internal error: Failed to deliver message");
send(reply);
}
@@ -737,12 +803,14 @@ QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *parent)
Q_UNUSED(threads);
dbus_error_init(&error);
+
+ rootNode.flags = 0;
}
QDBusConnectionPrivate::~QDBusConnectionPrivate()
{
Q_ASSERT(knownObjects.isEmpty());
-
+
if (dbus_error_is_set(&error))
dbus_error_free(&error);
@@ -751,15 +819,18 @@ QDBusConnectionPrivate::~QDBusConnectionPrivate()
KnownInterfacesHash::iterator it = knownInterfaces.begin();
while (it != knownInterfaces.end()) {
const QSharedDataPointer<QDBusIntrospection::Interface>& item = *it;
-
+
const_cast<QDBusIntrospection::Interface*>(item.constData())->ref.deref();
it = knownInterfaces.erase(it);
}
+
+ rootNode.clear(); // free resources
}
void QDBusConnectionPrivate::closeConnection()
{
+ QWriteLocker locker(&lock);
ConnectionMode oldMode = mode;
mode = InvalidMode; // prevent reentrancy
if (oldMode == ServerMode) {
@@ -835,21 +906,8 @@ void QDBusConnectionPrivate::socketWrite(int fd)
void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
{
- ObjectHookHash::iterator it = objectHooks.begin();
- while (it != objectHooks.end()) {
- ObjectDataHash::iterator dit = it->begin();
- while (dit != it->end()) {
- if (static_cast<QObject *>(dit.value().obj) == obj)
- dit = it->erase(dit);
- else
- ++dit;
- }
-
- if (it->isEmpty())
- it = objectHooks.erase(it);
- else
- ++it;
- }
+ QWriteLocker locker(&lock);
+ huntAndDestroy(obj, &rootNode);
SignalHookHash::iterator sit = signalHooks.begin();
while (sit != signalHooks.end()) {
@@ -858,41 +916,28 @@ void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
else
++sit;
}
+
obj->disconnect(this);
}
-bool QDBusConnectionPrivate::activateAdaptor(QObject* object, int flags,
- const QDBusMessage &msg)
+void QDBusConnectionPrivate::relaySignal(QObject *obj, const char *interface, const char *name,
+ const QVariantList &args)
{
- // This is called by QDBusConnectionPrivate::handleObjectCall to place a call to a slot
- // on the object.
- //
- // The call is routed through the adaptor sub-objects
-
- Q_ASSERT(object);
- flags |= QDBusConnection::ExportNonScriptableSlots;
-
- const QObjectList& children = object->children();
- QObjectList::const_iterator child = children.begin(),
- end = children.end();
- for ( ; child != end; ++child) {
- // check if this is an adaptor
- if (!qobject_cast<QDBusAbstractAdaptor *>(*child))
- continue; // not adaptor
-
- const QMetaObject *mo = (*child)->metaObject();
- int ciend = mo->classInfoCount();
- for (int i = 0; i < ciend; ++i) {
- QMetaClassInfo mci = mo->classInfo(i);
- if (strcmp(mci.name(), "DBus Interface") == 0 && *mci.value()) {
- // one interface.
- // is this it?
- if (msg.interface().isEmpty() || msg.interface() == mci.value())
- return activateCall(*child, flags, msg);
- }
- }
+ QReadLocker locker(&lock);
+ QDBusMessage message = QDBusMessage::signal(QLatin1String("/"), QLatin1String(interface),
+ QLatin1String(name));
+ message += args;
+ DBusMessage *msg = message.toDBusMessage();
+ if (!msg) {
+ qWarning("Could not emit signal %s.%s", interface, name);
+ return;
}
- return false;
+
+ qDebug() << "Emitting signal" << message;
+ qDebug() << "for paths:";
+ dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
+ huntAndEmit(connection, msg, obj, &rootNode);
+ dbus_message_unref(msg);
}
int QDBusConnectionPrivate::registerMessageMetaType()
@@ -905,85 +950,174 @@ int QDBusConnectionPrivate::findSlot(QObject* obj, const char *slotName, QList<i
{
Q_ASSERT(slotName);
QByteArray normalizedName = QMetaObject::normalizedSignature(slotName);
- int midx = obj->metaObject()->indexOfMethod(normalizedName);
+ int midx = obj->metaObject()->indexOfSlot(normalizedName);
if (midx == -1) {
qWarning("No such slot '%s' while connecting D-Bus", slotName);
return -1;
}
- int inputCount = parametersForMethod(normalizedName, params);
+ int inputCount = qDBusParametersForMethod(normalizedName, params);
if ( inputCount == -1 || inputCount + 1 != params.count() )
return -1; // failed to parse or invalid arguments or output arguments
-
+
return midx;
}
-bool QDBusConnectionPrivate::activateObject(const QDBusConnectionPrivate::ObjectData &hook,
- const QDBusMessage &msg)
+bool QDBusConnectionPrivate::activateInternalFilters(const ObjectTreeNode *node, const QDBusMessage &msg)
{
- if (!hook.obj)
- return false; // object is gone
+ // object may be null
- if (hook.flags & QDBusConnection::ExportAdaptors)
- return activateAdaptor(hook.obj, hook.flags, msg);
- else
- return activateCall(hook.obj, hook.flags, msg);
+ if (msg.interface() == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE)) {
+ if (msg.method() == QLatin1String("Introspect") && msg.signature().isEmpty())
+ qDBusIntrospectObject(node, msg);
+ return true;
+ }
+
+ if (node->obj && msg.interface() == QLatin1String(DBUS_INTERFACE_PROPERTIES)) {
+ if (msg.method() == QLatin1String("Get") && msg.signature() == QLatin1String("ss"))
+ qDBusPropertyGet(node, msg);
+ else if (msg.method() == QLatin1String("Set") && msg.signature() == QLatin1String("ssv"))
+ qDBusPropertySet(node, msg);
+ return true;
+ }
+
+ return false;
+}
+
+bool QDBusConnectionPrivate::activateObject(const ObjectTreeNode *node, const QDBusMessage &msg)
+{
+ // This is called by QDBusConnectionPrivate::handleObjectCall to place a call to a slot
+ // on the object.
+ //
+ // The call is routed through the adaptor sub-objects if we have any
+
+ // object may be null
+
+ QDBusAdaptorConnector *connector;
+ if (node->flags & QDBusConnection::ExportAdaptors &&
+ (connector = qDBusFindAdaptorConnector(node->obj))) {
+ int newflags = node->flags | QDBusConnection::ExportNonScriptableSlots;
+
+ if (msg.interface().isEmpty()) {
+ // place the call in all interfaces
+ // let the first one that handles it to work
+ foreach (const QDBusAdaptorConnector::AdaptorData &entry, connector->adaptors)
+ if (activateCall(entry.adaptor, newflags, msg))
+ return true;
+ } else {
+ // check if we have an interface matching the name that was asked:
+ QDBusAdaptorConnector::AdaptorMap::ConstIterator it;
+ it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(),
+ msg.interface());
+ if (it != connector->adaptors.end() && it->interface == msg.interface())
+ if (activateCall(it->adaptor, newflags, msg))
+ return true;
+ }
+ }
+
+ // no adaptors matched
+ // try our standard filters
+ if (activateInternalFilters(node, msg))
+ return true;
+
+ // try the object itself:
+ if (node->flags & QDBusConnection::ExportSlots && activateCall(node->obj, node->flags, msg))
+ return true;
+
+ // nothing matched
+ qDebug("Call failed: no match for %s%s%s at %s",
+ qPrintable(msg.interface()), msg.interface().isEmpty() ? "" : ".",
+ qPrintable(msg.name()),
+ qPrintable(msg.path()));
+ return false;
}
bool QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg)
{
- ObjectHookHash::ConstIterator it = objectHooks.find(msg.path());
- if (it == objectHooks.constEnd())
- return false;
-
- bool ok = false;
- const ObjectDataHash& hook = it.value();
- ObjectDataHash::ConstIterator hit;
- if (msg.interface().isEmpty()) {
- // we must go through all the objects and interfaces
-
- for (hit = hook.begin(); hit != hook.end(); ++hit) {
- ok = activateObject(hit.value(), msg);
- if (ok)
- break; // processed
+ QReadLocker locker(&lock);
+
+ // walk the object tree
+ QStringList path = msg.path().split(QLatin1Char('/'));
+ if (path.last().isEmpty())
+ path.removeLast(); // happens if path is "/"
+ int i = 1;
+ ObjectTreeNode *node = &rootNode;
+
+ // try our own tree first
+ while (node && !(node->flags & QDBusConnection::ExportChildObjects) ) {
+ if (i == path.count()) {
+ // found our object
+ return activateObject(node, msg);
}
- } else {
- // find the interface:
- hit = hook.find(msg.interface());
- if (hit != hook.end())
- ok = activateObject(hit.value(), msg);
-
- if (!ok) {
- // try adaptors (or any interface)
- hit = hook.find(QString());
- if (hit != hook.end())
- ok = activateObject(hit.value(), msg);
+
+ QVector<ObjectTreeNode::Data>::ConstIterator it =
+ qLowerBound(node->children.constBegin(), node->children.constEnd(), path.at(i));
+ if (it != node->children.constEnd() && it->name == path.at(i))
+ // match
+ node = it->node;
+ else
+ node = 0;
+
+ ++i;
+ }
+
+ // any object in the tree can tell us to switch to its own object tree:
+ if (node && node->flags & QDBusConnection::ExportChildObjects) {
+ QObject *obj = node->obj;
+
+ while (obj) {
+ if (i == path.count()) {
+ // we're at the correct level
+ ObjectTreeNode fakenode(*node);
+ fakenode.obj = obj;
+ return activateObject(&fakenode, msg);
+ }
+
+ const QObjectList &children = obj->children();
+
+ // find a child with the proper name
+ QObject *next = 0;
+ foreach (QObject *child, children)
+ if (child->objectName() == path.at(i)) {
+ next = child;
+ break;
+ }
+
+ if (!next)
+ break;
+
+ ++i;
+ obj = next;
}
}
- qDebug(ok ? "Call scheduled" : "Call failed");
- return ok;
+ qDebug("Call failed: no object found at %s", qPrintable(msg.path()));
+ return false;
}
bool QDBusConnectionPrivate::handleSignal(const QString &path, const QDBusMessage &msg)
{
+ QReadLocker locker(&lock);
+
+ bool result = false;
SignalHookHash::const_iterator it = signalHooks.find(path);
qDebug("looking for: %s", path.toLocal8Bit().constData());
qDebug() << signalHooks.keys();
for ( ; it != signalHooks.constEnd() && it.key() == path; ++ it) {
const SignalHook &hook = it.value();
- if ( hook.obj.isNull() )
- continue;
if ( !hook.name.isEmpty() && hook.name != msg.name() )
continue;
if ( !hook.interface.isEmpty() && hook.interface != msg.interface() )
continue;
if ( !hook.signature.isEmpty() && hook.signature != msg.signature() )
continue;
+ if ( hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty())
+ continue;
- activateSignal(hook, msg);
+ // yes, |=
+ result |= activateSignal(hook, msg);
}
- return true;
+ return result;
}
bool QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg)
@@ -1057,7 +1191,11 @@ void QDBusConnectionPrivate::setConnection(DBusConnection *dbc)
qWarning("QDBusConnectionPrivate::SetConnection: Unable to get base service");
}
+#if USE_OUTSIDE_DISPATCH
+ dbus_connection_add_filter_outside(connection, qDBusSignalFilter, qDBusSignalFilterOutside, this, 0);
+#else
dbus_connection_add_filter(connection, qDBusSignalFilter, this, 0);
+#endif
qDebug("base service: %s", service);
}
@@ -1070,8 +1208,20 @@ static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
if (!call->receiver.isNull() && call->methodIdx != -1) {
DBusMessage *reply = dbus_pending_call_steal_reply(pending);
- connection->activateReply(call->receiver, call->methodIdx, call->metaTypes,
- QDBusMessage::fromDBusMessage(reply));
+
+ // Deliver the return values of a remote function call.
+ //
+ // There is only one connection and it is specified by idx
+ // The slot must have the same parameter types that the message does
+ // The slot may have less parameters than the message
+ // The slot may optionally have one final parameter that is QDBusMessage
+ // The slot receives read-only copies of the message (i.e., pass by value or by const-ref)
+ CallDeliveryEvent *e = prepareReply(call->receiver, call->methodIdx, call->metaTypes,
+ QDBusMessage::fromDBusMessage(reply,
+ QDBusConnection(connection->name)));
+ if (e)
+ connection->deliverCall(*e);
+ delete e;
}
dbus_pending_call_unref(pending);
delete call;
@@ -1122,10 +1272,31 @@ int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObj
return 0;
}
+void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook &hook)
+{
+ signalHooks.insertMulti(key, hook);
+ connect(hook.obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));
+}
+
+void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node)
+{
+ connect(node->obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));
+
+ if (node->flags & QDBusConnection::ExportAdaptors) {
+ QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(node->obj);
+
+ // disconnect and reconnect to avoid duplicates
+ connector->disconnect(SIGNAL(relaySignal(QObject *,const char*, const char*,QVariantList)),
+ this, SLOT(relaySignal(QObject*, const char*, const char*,QVariantList)));
+ connect(connector, SIGNAL(relaySignal(QObject *,const char*, const char*,QVariantList)),
+ SLOT(relaySignal(QObject*, const char*, const char*,QVariantList)));
+ }
+}
+
QSharedDataPointer<QDBusIntrospection::Interface>
QDBusConnectionPrivate::findInterface(const QString& name)
{
- QMutexLocker locker(&mutex);
+ QWriteLocker locker(&lock);
QSharedDataPointer<QDBusIntrospection::Interface> data = knownInterfaces.value(name);
if (!data) {
data = new QDBusIntrospection::Interface;
@@ -1140,7 +1311,7 @@ QDBusConnectionPrivate::findInterface(const QString& name)
QDBusIntrospection::Object*
QDBusConnectionPrivate::findObject(const QString& service, const QString& path)
{
- QMutexLocker locker(&mutex);
+ QWriteLocker locker(&lock);
QDBusIntrospection::Object* data = knownObjects.value(service + path);
if (!data) {
data = new QDBusIntrospection::Object;
@@ -1149,7 +1320,7 @@ QDBusConnectionPrivate::findObject(const QString& service, const QString& path)
knownObjects.insert(service + path, data);
}
-
+
return data;
}
@@ -1159,7 +1330,7 @@ void QDBusConnectionPrivate::disposeOfLocked(QDBusIntrospection::Object* p)
// no one else is using it
// get rid of the reference
QString objName = p->service + p->path;
-
+
#ifndef QT_NO_DEBUG
// debug code
Q_ASSERT(p == knownObjects.take(objName));
@@ -1169,11 +1340,11 @@ void QDBusConnectionPrivate::disposeOfLocked(QDBusIntrospection::Object* p)
#endif
// remove sub-objects too
- if (!objName.endsWith('/'))
- objName.append('/');
+ if (!objName.endsWith(QLatin1Char('/')))
+ objName.append(QLatin1Char('/'));
foreach (QString subObjName, p->childObjects)
disposeOfLocked(knownObjects.value(objName + subObjName));
-
+
delete p;
}
}
@@ -1183,7 +1354,7 @@ void QDBusConnectionPrivate::disposeOf(QDBusObjectPrivate* p)
// We're called from QDBusConnectionPrivate's destructor
// that means the object it represents is going out of scope
- QMutexLocker locker(&mutex);
+ QWriteLocker locker(&lock);
disposeOfLocked( const_cast<QDBusIntrospection::Object*>(p->data) );
}
@@ -1192,3 +1363,4 @@ void QDBusReplyWaiter::reply(const QDBusMessage &msg)
replyMsg = msg;
QTimer::singleShot(0, this, SLOT(quit()));
}
+#include "qdbusconnection_p.moc"
diff --git a/qt/qdbusinterface.cpp b/qt/qdbusinterface.cpp
index d1b73d5..1572b2a 100644
--- a/qt/qdbusinterface.cpp
+++ b/qt/qdbusinterface.cpp
@@ -28,31 +28,115 @@
#include "qdbusinterface_p.h"
+/*!
+ \internal
+*/
+struct EmptyInterfaceInitializer
+{
+ QDBusIntrospection::Interface *data;
+ EmptyInterfaceInitializer()
+ {
+ data = new QDBusIntrospection::Interface;
+ data->ref = 1;
+ data->introspection = QLatin1String("");
+ }
+
+ ~EmptyInterfaceInitializer()
+ {
+ Q_ASSERT(data->ref == 1);
+ delete data;
+ data = 0;
+ }
+};
+
+Q_GLOBAL_STATIC(EmptyInterfaceInitializer, emptyDataInit);
+
+const QDBusIntrospection::Interface*
+QDBusInterfacePrivate::emptyData()
+{
+ return emptyDataInit()->data;
+}
+
+/*!
+ \class QDBusInterface
+ \brief Base class for all D-Bus interfaces in the QtDBus binding, allowing access to remote interfaces.
+
+ QDBusInterface is a generic accessor class that is used to place calls to remote objects,
+ connect to signals exported by remote objects and get/set the value of remote properties. This
+ class is useful for dynamic access to remote objects: that is, when you do not have a generated
+ code that represents the remote interface.
+
+ Generated-code classes also derive from QDBusInterface, all methods described here are also
+ valid for generated-code classes. In addition to those described here, generated-code classes
+ provide member functions for the remote methods, which allow for compile-time checking of the
+ correct parameters and return values, as well as property type-matching and signal
+ parameter-matching.
+
+ Calls are usually placed by using the call() function, which constructs the message, sends it
+ over the bus, waits for the reply and decodes the reply. Signals are connected to by using the
+ connect() family of functions, whose behavior is similar to QObject::connect(). Finally,
+ properties are accessed using the property() and setProperty() functions, whose behaviour is
+ also similar to QObject::property() and QObject::setProperty().
+
+ \sa \ref StandardInterfaces, \ref dbusidl2cpp
+*/
+
+/*!
+ \enum QDBusInterface::CallMode
+ \todo turn this into flags and add UseEventLoop/NoUseEventLoop
+
+ Specifies how a call should be placed. The valid options are:
+ \value NoWaitForReply place the call but don't wait for the reply (the reply's contents
+ will be discarded)
+ \value WaitForReply place the call and wait for the method to finish before returning
+ (the reply's contents will be returned)
+ \value NoUseEventLoop don't use an event loop to wait for a reply, but instead block on
+ network operations while waiting. This option means the
+ user-interface may not be updated for the duration of the call.
+ \value UseEventLoop use the Qt event loop to wait for a reply. This option means the
+ user-interface will update, but it also means other events may
+ happen, like signal delivery and other D-Bus method calls.
+
+ When using UseEventLoop, applications must be prepared for reentrancy in any function.
+*/
+
QDBusInterface::QDBusInterface(QDBusInterfacePrivate* p)
: d(p)
{
d->ref.ref();
}
+/*!
+ Constructs a QDBusInterface object by associating it with the interface \p name in the remote
+ object \p obj.
+*/
QDBusInterface::QDBusInterface(const QDBusObject& obj, const QString& name)
: d(0)
{
*this = obj.connection().findInterface(obj.service(), obj.path(), name);
}
-QDBusInterface::QDBusInterface(QDBusConnection& conn, const QString& service, const QString& path,
- const QString& name)
+/*!
+ Constructs a copy QDBusInterface object.
+*/
+QDBusInterface::QDBusInterface(const QDBusInterface &other)
: d(0)
{
- *this = conn.findInterface(service, path, name);
+ *this = other;
}
+/*!
+ Releases this object's resources.
+*/
QDBusInterface::~QDBusInterface()
{
if (!d->ref.deref())
delete d;
}
+/*!
+ Constructs a copy QDBusInterface object.
+*/
QDBusInterface& QDBusInterface::operator=(const QDBusInterface& other)
{
other.d->ref.ref();
@@ -63,67 +147,132 @@ QDBusInterface& QDBusInterface::operator=(const QDBusInterface& other)
return *this;
}
+/*!
+ \fn QDBusInterface::object()
+ Returns the object associated with this interface.
+*/
+
+/*!
+ \fn QDBusInterface::object() const
+ \overload
+ Returns the object associated with this interface.
+*/
+
+/*!
+ \fn "QDBusInterface::operator QDBusObject"
+ \overload
+ Returns the object associated with this interface.
+*/
+
+/*!
+ \fn "QDBusInterface::operator const QDBusObject"
+ \overload
+ Returns the object associated with this interface.
+*/
+
+/*!
+ Returns the connection this interface is assocated with.
+*/
QDBusConnection QDBusInterface::connection() const
{
return d->conn;
}
+/*!
+ Returns the name of the service this interface is associated with.
+*/
QString QDBusInterface::service() const
{
return d->service;
}
+/*!
+ Returns the object path that this interface is associated with.
+*/
QString QDBusInterface::path() const
{
return d->path;
}
+/*!
+ Returns the name of this interface.
+*/
QString QDBusInterface::interface() const
{
return d->data->name;
}
+/*!
+ Returns the XML document fragment that describes the introspection of this interface. This is
+ the raw XML form of the structures returned by interfaceData().
+ */
QString QDBusInterface::introspectionData() const
{
d->introspect();
return d->data->introspection;
}
+/*!
+ Returns the interface data for this interface. This is the parsed form of the XML introspection
+ data, as returned by introspectionData().
+ */
const QDBusIntrospection::Interface& QDBusInterface::interfaceData() const
{
d->introspect();
return *d->data;
}
+/*!
+ Returns the annotations present in this interface, if any.
+ This information can also be found in the data returned by interfaceData().
+*/
const QDBusIntrospection::Annotations& QDBusInterface::annotationData() const
{
d->introspect();
return d->data->annotations;
}
+/*!
+ Returns a map of all the methods found in this interface.
+ This information can also be found in the data returned by interfaceData().
+*/
const QDBusIntrospection::Methods& QDBusInterface::methodData() const
{
d->introspect();
return d->data->methods;
}
+/*!
+ Returns a map of all the signals found in this interface.
+ This information can also be found in the data returned by interfaceData().
+*/
const QDBusIntrospection::Signals& QDBusInterface::signalData() const
{
d->introspect();
return d->data->signals_;
}
+/*!
+ Returns a map of all the properties found in this interface.
+ This information can also be found in the data returned by interfaceData().
+*/
const QDBusIntrospection::Properties& QDBusInterface::propertyData() const
{
d->introspect();
return d->data->properties;
}
+/*!
+ Places a call to the remote method specified by \p method on this interface, using \p a_args as
+ arguments.
+
+ Normally, you should place calls using call().
+*/
QDBusMessage QDBusInterface::callWithArgs(const QDBusIntrospection::Method& method,
const QList<QVariant>& a_args,
CallMode mode)
{
- QString signature(""); // empty, not null
+ QString signature = QLatin1String(""); // empty, not null
QVariantList args = a_args;
if (!method.inputArgs.isEmpty())
@@ -142,29 +291,45 @@ QDBusMessage QDBusInterface::callWithArgs(const QDBusIntrospection::Method& meth
else
args.clear();
- if (method.annotations.value(ANNOTATION_NO_WAIT, "false") == "true")
+ if (method.annotations.value(QLatin1String(ANNOTATION_NO_WAIT)) == QLatin1String("true"))
mode = NoWaitForReply;
return callWithArgs(method.name, signature, args, mode);
}
+/*!
+ \overload
+ Places a call to the remote method specified by \p method on this interface, using \p args as
+ arguments.
+
+ Normally, you should place calls using call().
+*/
QDBusMessage QDBusInterface::callWithArgs(const QString& method, const QList<QVariant>& args,
CallMode mode)
{
QString m = method, sig;
// split out the signature from the method
- int pos = method.indexOf('.');
+ int pos = method.indexOf(QLatin1Char('.'));
if (pos != -1) {
m.truncate(pos);
sig = method.mid(pos + 1);
- }
+ }
return callWithArgs(m, sig, args, mode);
}
+/*!
+ \overload
+ Places a call to the remote method specified by \p method on this interface, using \p args as
+ arguments. The \p signature parameter specifies how the arguments should be marshalled over the
+ connection. (It also serves to distinguish between overloading of remote methods by name)
+
+ Normally, you should place calls using call().
+*/
QDBusMessage QDBusInterface::callWithArgs(const QString& method, const QString& signature,
const QList<QVariant>& args, CallMode mode)
{
- QDBusMessage msg = QDBusMessage::methodCall(service(), path(), interface(), method, signature);
+ QDBusMessage msg = QDBusMessage::methodCall(service(), path(), interface(), method);
+ msg.setSignature(signature);
msg.QList<QVariant>::operator=(args);
QDBusMessage reply;
@@ -182,9 +347,14 @@ QDBusMessage QDBusInterface::callWithArgs(const QString& method, const QString&
return reply;
}
+/*!
+ Connects the D-Bus signal specified by \p sig to the given slot \p slot in the object \p obj.
+
+ This function is similar to QObject::connect.
+*/
bool QDBusInterface::connect(const QDBusIntrospection::Signal& sig, QObject* obj, const char *slot)
{
- QString signature(""); // empty, not null
+ QString signature = QLatin1String(""); // empty, not null
if (!sig.outputArgs.isEmpty())
{
@@ -203,54 +373,135 @@ bool QDBusInterface::connect(const QDBusIntrospection::Signal& sig, QObject* obj
return connect(sig.name, signature, obj, slot);
}
+/*!
+ \overload
+ Connects the D-Bus signal specified by \p signalName to the given slot \p slot in the object \p
+ obj.
+
+ This function is similar to QObject::connect.
+*/
bool QDBusInterface::connect(const QString& signalName, QObject* obj, const char *slot)
{
QString s = signalName, sig;
// split out the signature from the name
- int pos = signalName.indexOf('.');
+ int pos = signalName.indexOf(QLatin1Char('.'));
if (pos != -1) {
s.truncate(pos);
- sig = signalName.mid(pos + 1);
+ sig = QLatin1String("") + signalName.mid(pos + 1);
}
return connect(s, sig, obj, slot);
}
+/*!
+ \overload
+ Connects the D-Bus signal specified by \p signalName to the given slot \p slot in the object \p
+ obj. The \p signature parameter allows one to connect to the signal only if it is emitted with
+ the parameters matching the given type signature.
+
+ This function is similar to QObject::connect.
+*/
bool QDBusInterface::connect(const QString& signalName, const QString& signature,
QObject* obj, const char *slot)
{
return d->conn.connect(service(), path(), interface(), signalName, signature, obj, slot);
}
-QVariant QDBusInterface::propertyGet(const QDBusIntrospection::Property& prop)
+/*!
+ Retrieves the value of the property \p prop in the remote object. This function returns an error
+ if you try to read the value of a write-only property.
+*/
+QDBusReply<QDBusVariant> QDBusInterface::property(const QDBusIntrospection::Property& prop)
{
// sanity checking
if (prop.access == QDBusIntrospection::Property::Write)
- return QVariant(); // write-only prop
+ // write-only prop
+ return QDBusError(QLatin1String(DBUS_ERROR_ACCESS_DENIED),
+ QString::fromLatin1("Property %1 in interface %2 in object %3 is write-only")
+ .arg(prop.name, interface(), path()));
QDBusPropertiesInterface pi(object());
return pi.get(interface(), prop.name);
}
-QVariant QDBusInterface::propertyGet(const QString& propName)
+/*!
+ \overload
+ Retrieves the value of the property \p propname in the remote object. This function returns an
+ error if you try to read the value of a write-only property.
+*/
+QDBusReply<QDBusVariant> QDBusInterface::property(const QString& propName)
{
// can't do sanity checking
QDBusPropertiesInterface pi(object());
return pi.get(interface(), propName);
}
-void QDBusInterface::propertySet(const QDBusIntrospection::Property& prop, QVariant newValue)
+/*!
+ Sets the value of the property \p prop to \p newValue in the remote object. This function
+ automatically changes the type of \p newValue to the property's type, but the call will fail if
+ the types don't match.
+
+ This function returns an error if the property is read-only.
+*/
+QDBusReply<void> QDBusInterface::setProperty(const QDBusIntrospection::Property& prop,
+ const QDBusVariant &newValue)
{
// sanity checking
if (prop.access == QDBusIntrospection::Property::Read)
- return;
+ // read-only prop
+ return QDBusError(QLatin1String(DBUS_ERROR_ACCESS_DENIED),
+ QString::fromLatin1("Property %1 in interface %2 in object %3 is read-only")
+ .arg(prop.name, interface(), path()));
+
+ // set the property type
+ QDBusVariant value = newValue;
+ value.type = prop.type;
QDBusPropertiesInterface pi(object());
- pi.set(interface(), prop.name, newValue);
+ return pi.set(interface(), prop.name, value);
}
-void QDBusInterface::propertySet(const QString& propName, QVariant newValue)
+/*!
+ \overload
+ Sets the value of the property \p propName to \p newValue in the remote object. This function
+ will not change \p newValue's type to match the property, so it is your responsibility to make
+ sure it is of the correct type.
+
+ This function returns an error if the property is read-only.
+*/
+QDBusReply<void> QDBusInterface::setProperty(const QString& propName, const QDBusVariant &newValue)
{
// can't do sanity checking
QDBusPropertiesInterface pi(object());
- pi.set(interface(), propName, newValue);
+ return pi.set(interface(), propName, newValue);
}
+
+/*!
+ \fn QDBusMessage QDBusInterface::call(const QDBusIntrospection::Method &method, ...)
+
+ Calls the method \p method on this interface and passes the parameters to this function to the
+ method.
+
+ The parameters to \a call are passed on to the remote function via D-Bus as input
+ arguments. Output arguments are returned in the QDBusMessage reply.
+
+ \warning This function reenters the Qt event loop in order to wait for the reply, excluding user
+ input. During the wait, it may deliver signals and other method calls to your
+ application. Therefore, it must be prepared to handle a reentrancy whenever a call is
+ placed with call().
+*/
+
+/*!
+ \overload
+ \fn QDBusMessage QDBusInterface::call(const QString &method, ...)
+
+ Calls the method \p method on this interface and passes the parameters to this function to the
+ method.
+
+ The parameters to \a call are passed on to the remote function via D-Bus as input
+ arguments. Output arguments are returned in the QDBusMessage reply.
+
+ \warning This function reenters the Qt event loop in order to wait for the reply, excluding user
+ input. During the wait, it may deliver signals and other method calls to your
+ application. Therefore, it must be prepared to handle a reentrancy whenever a call is
+ placed with call().
+*/
diff --git a/qt/qdbusinterface.h b/qt/qdbusinterface.h
index 2b96cae..966a470 100644
--- a/qt/qdbusinterface.h
+++ b/qt/qdbusinterface.h
@@ -28,6 +28,8 @@
#include "qdbusmessage.h"
#include "qdbusobject.h"
#include "qdbusintrospection.h"
+#include "qdbusreply.h"
+#include "qdbusvariant.h"
#include <QtCore/qstring.h>
#include <QtCore/qvariant.h>
#include <QtCore/qlist.h>
@@ -35,9 +37,6 @@
class QDBusConnection;
class QDBusInterfacePrivate;
-/**
- * Base class for all DBUS interfaces in the QtDBUS binding.
- */
class QDBUS_EXPORT QDBusInterface
{
friend class QDBusConnection;
@@ -49,171 +48,65 @@ public:
};
public:
- /**
- * Construct an interface of the given name
- */
QDBusInterface(const QDBusObject& obj, const QString& name);
-
- /**
- * @overload.
- * Construct an interface of the given name
- */
- QDBusInterface(QDBusConnection& conn, const QString& service, const QString& path, const QString& name);
-
- /**
- * Construct a copy of the interface.
- */
QDBusInterface(const QDBusInterface&);
-
- /**
- * Destructs this interface.
- */
virtual ~QDBusInterface();
- /**
- * Copy the interface.
- */
QDBusInterface& operator=(const QDBusInterface&);
- /**
- * Returns the object associated with this interface.
- */
inline QDBusObject object()
{ return QDBusObject(*this); }
inline const QDBusObject object() const
{ return QDBusObject(*this); }
- /**
- * Returns the connection this interface is on.
- */
+ inline operator QDBusObject()
+ { return QDBusObject(*this); }
+
+ inline operator const QDBusObject() const
+ { return QDBusObject(*this); }
+
+
QDBusConnection connection() const;
- /**
- * Returns the name of the service this interface is associated with.
- */
QString service() const;
-
- /**
- * Returns the object path that this interface is associated with.
- */
QString path() const;
-
- /**
- * Returns the name of this interface.
- */
QString interface() const;
- /**
- * Returns the introspection XML fragment data of this interface.
- */
virtual QString introspectionData() const;
-
- /**
- * Returns the interface data for this interface.
- */
const QDBusIntrospection::Interface& interfaceData() const;
-
- /**
- * Returns the annotations present in this interface, if any.
- */
const QDBusIntrospection::Annotations& annotationData() const;
-
- /**
- * List all methods in this interface.
- */
const QDBusIntrospection::Methods& methodData() const;
-
- /**
- * List all signals in this interface.
- */
const QDBusIntrospection::Signals& signalData() const;
-
- /**
- * List all properties in this interface.
- */
const QDBusIntrospection::Properties& propertyData() const;
- /**
- * Call the given method.
- */
QDBusMessage callWithArgs(const QDBusIntrospection::Method& method,
const QList<QVariant>& args = QList<QVariant>(),
CallMode mode = WaitForReply);
-
- /**
- * Call the given method.
- */
QDBusMessage callWithArgs(const QString& method, const QList<QVariant>& args = QList<QVariant>(),
CallMode mode = WaitForReply);
-
- /**
- * Call the given method.
- */
QDBusMessage callWithArgs(const QString& method, const QString& signature,
const QList<QVariant>& args = QList<QVariant>(),
CallMode mode = WaitForReply);
- /**
- * Connects the DBUS signal to the given slot.
- */
bool connect(const QDBusIntrospection::Signal&, QObject* obj, const char *slot);
-
- /**
- * Connects the DBUS signal to the given slot.
- */
bool connect(const QString& signalName, QObject* obj, const char *slot);
-
- /**
- * Connects the DBUS signal to the given slot.
- */
bool connect(const QString& signalName, const QString& signature,
QObject* obj, const char *slot);
- /**
- * Gets the value of the given property.
- */
- QVariant propertyGet(const QDBusIntrospection::Property&);
-
- /**
- * Gets the value of the given property.
- */
- QVariant propertyGet(const QString& property);
-
- /**
- * Sets the value of the given property.
- */
- void propertySet(const QDBusIntrospection::Property&, QVariant newValue);
+ QDBusReply<QDBusVariant> property(const QDBusIntrospection::Property&);
+ QDBusReply<QDBusVariant> property(const QString& property);
- /**
- * Sets the value of the given property.
- */
- void propertySet(const QString& property, QVariant newValue);
+ QDBusReply<void> setProperty(const QDBusIntrospection::Property&, const QDBusVariant& newValue);
+ QDBusReply<void> setProperty(const QString& property, const QDBusVariant& newValue);
- /**
- * Casts to QDBusObject.
- */
- inline operator QDBusObject()
- { return QDBusObject(*this); }
-
- /**
- * Casts to const QDBusObject.
- */
- inline operator const QDBusObject() const
- { return QDBusObject(*this); }
-
- /**
- * Call the given method.
- */
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
template<typename MethodType>
inline QDBusMessage call(MethodType m)
{
return callWithArgs(m);
}
- /**
- * Call the given method.
- */
template<typename MethodType, typename T1>
inline QDBusMessage call(MethodType m, T1 t1)
{
@@ -222,9 +115,6 @@ public:
return callWithArgs(m, args);
}
- /**
- * Call the given method.
- */
template<typename MethodType, typename T1, typename T2>
inline QDBusMessage call(MethodType m, T1 t1, T2 t2)
{
@@ -233,9 +123,6 @@ public:
return callWithArgs(m, args);
}
- /**
- * Call the given method.
- */
template<typename MethodType, typename T1, typename T2, typename T3>
inline QDBusMessage call(MethodType m, T1 t1, T2 t2, T3 t3)
{
@@ -244,9 +131,6 @@ public:
return callWithArgs(m, args);
}
- /**
- * Call the given method.
- */
template<typename MethodType, typename T1, typename T2, typename T3, typename T4>
inline QDBusMessage call(MethodType m, T1 t1, T2 t2, T3 t3, T4 t4)
{
@@ -255,9 +139,6 @@ public:
return callWithArgs(m, args);
}
- /**
- * Call the given method.
- */
template<typename MethodType, typename T1, typename T2, typename T3, typename T4, typename T5>
inline QDBusMessage call(MethodType m, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{
@@ -266,9 +147,6 @@ public:
return callWithArgs(m, args);
}
- /**
- * Call the given method.
- */
template<typename MethodType, typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6>
inline QDBusMessage call(MethodType m, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
@@ -278,9 +156,6 @@ public:
return callWithArgs(m, args);
}
- /**
- * Call the given method.
- */
template<typename MethodType, typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7>
inline QDBusMessage call(MethodType m, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
@@ -290,9 +165,6 @@ public:
return callWithArgs(m, args);
}
- /**
- * Call the given method.
- */
template<typename MethodType, typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8>
inline QDBusMessage call(MethodType m, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
@@ -301,6 +173,11 @@ public:
args << t1 << t2 << t3 << t4 << t5 << t6 << t7 << t8;
return callWithArgs(m, args);
}
+#else
+ // fool Doxygen
+ inline QDBusMessage call(const QDBusIntrospection::Method &method, ...);
+ inline QDBusMessage call(const QString &method, ...);
+#endif
private:
QDBusInterface(QDBusInterfacePrivate*);
diff --git a/qt/qdbusinterface_p.h b/qt/qdbusinterface_p.h
index 3e0bd01..04aa243 100644
--- a/qt/qdbusinterface_p.h
+++ b/qt/qdbusinterface_p.h
@@ -56,11 +56,16 @@ public:
//QConstSharedDataPointer<QDBusIntrospection::Interface> data;
const QDBusIntrospection::Interface* data;
+ inline QDBusInterfacePrivate(const QDBusConnection &other) : conn(other), data(emptyData())
+ { }
+
inline bool needsIntrospection() const
- { return data->introspection.isNull(); }
+ { return data && data->introspection.isNull(); }
inline void introspect()
- { if (needsIntrospection()) QDBusObject(conn, service, path).introspect(); }
+ { if (needsIntrospection()) conn.findObject(service, path).introspect(); }
+
+ static const QDBusIntrospection::Interface *emptyData();
};
diff --git a/qt/qdbusinternalfilters.cpp b/qt/qdbusinternalfilters.cpp
new file mode 100644
index 0000000..984a878
--- /dev/null
+++ b/qt/qdbusinternalfilters.cpp
@@ -0,0 +1,369 @@
+/* -*- mode: C++ -*-
+ *
+ * Copyright (C) 2006 Trolltech AS. All rights reserved.
+ * Author: Thiago Macieira <thiago.macieira@trolltech.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "qdbusconnection_p.h"
+
+#include <dbus/dbus.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qmetaobject.h>
+
+#include "qdbusstandardinterfaces.h"
+#include "qdbusabstractadaptor.h"
+#include "qdbusabstractadaptor_p.h"
+#include "qdbusinterface_p.h" // for ANNOTATION_NO_WAIT
+#include "qdbusmessage.h"
+#include "qdbustype.h"
+#include "qdbusvariant.h"
+
+// implement the D-Bus org.freedesktop.DBus.Introspectable interface
+// we do that by analysing the metaObject of all the adaptor interfaces
+
+static inline QString dbusMemberName(const char *qtMemberName)
+{
+ QString retval = QLatin1String(qtMemberName);
+ if (!retval.isEmpty())
+ retval[0] = retval[0].toUpper();
+ return retval;
+}
+
+static QString generateInterfaceXml(const QMetaObject *mo, int flags, int methodOffset, int propOffset)
+{
+ QString retval;
+
+ // start with properties:
+ if (flags & QDBusConnection::ExportProperties) {
+ for (int i = propOffset; i < mo->propertyCount(); ++i) {
+ static const char *accessvalues[] = {0, "read", "write", "readwrite"};
+
+ QMetaProperty mp = mo->property(i);
+
+ if (!mp.isScriptable() && (flags & QDBusConnection::ExportNonScriptableProperties) !=
+ QDBusConnection::ExportNonScriptableProperties)
+ continue;
+
+ int access = 0;
+ if (mp.isReadable())
+ access |= 1;
+ if (mp.isWritable())
+ access |= 2;
+
+ int typeId = qDBusNameToTypeId(mp.typeName());
+ if (!typeId)
+ continue;
+
+ retval += QString(QLatin1String(" <property name=\"%1\" type=\"%2\" access=\"%3\" />\n"))
+ .arg(dbusMemberName(mp.name()))
+ .arg(QLatin1String(QDBusType::dbusSignature( QVariant::Type(typeId) )))
+ .arg(QLatin1String( accessvalues[access] ));
+ }
+ }
+
+ // now add methods:
+ for (int i = methodOffset; i < mo->methodCount(); ++i) {
+ QMetaMethod mm = mo->method(i);
+ QByteArray signature = mm.signature();
+ int paren = signature.indexOf('(');
+
+ bool isSignal;
+ if (mm.methodType() == QMetaMethod::Signal)
+ // adding a signal
+ isSignal = true;
+ else if (mm.methodType() == QMetaMethod::Slot && mm.access() == QMetaMethod::Public)
+ isSignal = false;
+ else
+ continue; // neither signal nor public slot
+
+ if ((isSignal && !(flags & QDBusConnection::ExportSignals)) ||
+ (!isSignal && !(flags & QDBusConnection::ExportSlots)))
+ continue;
+
+ QString xml = QString(QLatin1String(" <%1 name=\"%2\">\n"))
+ .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"))
+ .arg(dbusMemberName(signature.left(paren)));
+
+ // check the return type first
+ int typeId = qDBusNameToTypeId(mm.typeName());
+ if (typeId)
+ xml += QString(QLatin1String(" <arg type=\"%1\" direction=\"out\"/>\n"))
+ .arg(QLatin1String(QDBusType::dbusSignature( QVariant::Type(typeId) )));
+ else if (*mm.typeName())
+ continue; // wasn't a valid type
+
+ QList<QByteArray> names = mm.parameterNames();
+ QList<int> types;
+ int inputCount = qDBusParametersForMethod(QMetaObject::normalizedSignature(signature),
+ types);
+ if (inputCount == -1)
+ continue; // invalid form
+ if (isSignal && inputCount + 1 != types.count())
+ continue; // signal with output arguments?
+ if (isSignal && types.at(inputCount) == QDBusConnectionPrivate::messageMetaType)
+ continue; // signal with QDBusMessage argument?
+
+ int j;
+ bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
+ for (j = 1; j < types.count(); ++j) {
+ // input parameter for a slot or output for a signal
+ if (types.at(j) == QDBusConnectionPrivate::messageMetaType) {
+ isScriptable = true;
+ continue;
+ }
+
+ QString name;
+ if (!names.at(j - 1).isEmpty())
+ name = QString(QLatin1String("name=\"%1\" ")).arg(QLatin1String(names.at(j - 1)));
+
+ bool isOutput = isSignal || j > inputCount;
+
+ xml += QString(QLatin1String(" <arg %1type=\"%2\" direction=\"%3\"/>\n"))
+ .arg(name)
+ .arg(QLatin1String(QDBusType::dbusSignature( QVariant::Type(types.at(j)) )))
+ .arg(isOutput ? QLatin1String("out") : QLatin1String("in"));
+ }
+
+ if (!isScriptable &&
+ !(flags & (QDBusConnection::ExportNonScriptableSlots | QDBusConnection::ExportNonScriptableSignals)))
+ continue;
+
+ if (qDBusCheckAsyncTag(mm.tag()))
+ // add the no-reply annotation
+ xml += QLatin1String(" <annotation name=\"" ANNOTATION_NO_WAIT "\""
+ " value=\"true\"/>\n");
+
+ retval += xml;
+ retval += QString(QLatin1String(" </%1>\n"))
+ .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"));
+ }
+
+ return retval;
+}
+
+static QString generateMetaObjectXml(QString interface, const QMetaObject *mo, const QMetaObject *base,
+ int flags)
+{
+ if (interface.isEmpty()) {
+ // generate the interface name from the meta object
+ int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE);
+ if (idx >= mo->classInfoOffset()) {
+ interface = QLatin1String(mo->classInfo(idx).value());
+ } else {
+ interface = QLatin1String(mo->className());
+ interface.replace(QLatin1String("::"), QLatin1String("."));
+
+ if (interface.startsWith( QLatin1String("QDBus") )) {
+ interface.prepend( QLatin1String("com.trolltech.QtDBus.") );
+ } else if (interface.startsWith( QLatin1Char('Q') )) {
+ // assume it's Qt
+ interface.prepend( QLatin1String("com.trolltech.Qt.") );
+ } else if (!QCoreApplication::instance() ||
+ QCoreApplication::instance()->applicationName().isEmpty()) {
+ interface.prepend( QLatin1String("local.") );
+ } else {
+ interface.prepend(QLatin1Char('.')).prepend( QCoreApplication::instance()->applicationName() );
+ QStringList domainName = QCoreApplication::instance()->organizationDomain().split(QLatin1Char('.'));
+ foreach (const QString &part, domainName)
+ interface.prepend(QLatin1Char('.')).prepend(part);
+ }
+ }
+ }
+
+ QString xml;
+ int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTROSPECTION);
+ if (idx >= mo->classInfoOffset())
+ xml = QString::fromUtf8(mo->classInfo(idx).value());
+ else
+ xml = generateInterfaceXml(mo, flags, base->methodCount(), base->propertyCount());
+
+ return QString(QLatin1String(" <interface name=\"%1\">\n%2 </interface>"))
+ .arg(interface, xml);
+}
+
+static QString generateSubObjectXml(QObject *object)
+{
+ QString retval;
+ foreach (QObject *child, object->children()) {
+ QString name = child->objectName();
+ if (!name.isEmpty())
+ retval += QString(QLatin1String(" <node name=\"%1\"/>\n"))
+ .arg(name);
+ }
+ return retval;
+}
+
+void qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode *node,
+ const QDBusMessage &msg)
+{
+ // object may be null
+
+ QString xml_data(QLatin1String(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE));
+ xml_data += QLatin1String("<node>\n");
+
+ if (node->obj) {
+ xml_data += QLatin1String( QDBusIntrospectableInterface::staticIntrospectionData() );
+ xml_data += QLatin1String( QDBusPropertiesInterface::staticIntrospectionData() );
+
+ if (node->flags & QDBusConnection::ExportContents) {
+ const QMetaObject *mo = node->obj->metaObject();
+ for ( ; mo != &QObject::staticMetaObject; mo = mo->superClass())
+ xml_data += generateMetaObjectXml(QString(), mo, &QObject::staticMetaObject,
+ node->flags);
+ }
+
+ // does this object have adaptors?
+ QDBusAdaptorConnector *connector;
+ if (node->flags & QDBusConnection::ExportAdaptors &&
+ (connector = qDBusFindAdaptorConnector(node->obj))) {
+
+ // trasverse every adaptor in this object
+ QDBusAdaptorConnector::AdaptorMap::ConstIterator it = connector->adaptors.constBegin();
+ QDBusAdaptorConnector::AdaptorMap::ConstIterator end = connector->adaptors.constEnd();
+ for ( ; it != end; ++it) {
+ // add the interface:
+ QString ifaceXml = QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(it->adaptor);
+ if (ifaceXml.isEmpty()) {
+ // add the interface's contents:
+ ifaceXml += generateMetaObjectXml(it->interface, it->metaObject,
+ &QDBusAbstractAdaptor::staticMetaObject,
+ QDBusConnection::ExportNonScriptableContents);
+
+ QDBusAbstractAdaptorPrivate::saveIntrospectionXml(it->adaptor, ifaceXml);
+ }
+
+ xml_data += ifaceXml;
+ }
+ }
+ }
+
+ if (node->flags & QDBusConnection::ExportChildObjects) {
+ xml_data += generateSubObjectXml(node->obj);
+ } else {
+ // generate from the object tree
+ foreach (const QDBusConnectionPrivate::ObjectTreeNode::Data &entry, node->children) {
+ if (entry.node && (entry.node->obj || !entry.node->children.isEmpty()))
+ xml_data += QString(QLatin1String(" <node name=\"%1\"/>\n"))
+ .arg(entry.name);
+ }
+ }
+
+ xml_data += QLatin1String("</node>\n");
+
+ // now send it
+ QDBusMessage reply = QDBusMessage::methodReply(msg);
+ reply << xml_data;
+ msg.connection().send(reply);
+}
+
+// implement the D-Bus interface org.freedesktop.DBus.Properties
+
+static void sendPropertyError(const QDBusMessage &msg, const QString &interface_name)
+{
+ QDBusMessage error = QDBusMessage::error(msg, QLatin1String(DBUS_ERROR_INVALID_ARGS),
+ QString::fromLatin1("Interface %1 was not found in object %2")
+ .arg(interface_name)
+ .arg(msg.path()));
+ msg.connection().send(error);
+}
+
+void qDBusPropertyGet(const QDBusConnectionPrivate::ObjectTreeNode *node, const QDBusMessage &msg)
+{
+ Q_ASSERT(msg.count() == 2);
+ QString interface_name = msg.at(0).toString();
+ QByteArray property_name = msg.at(1).toString().toUtf8();
+
+ QDBusAdaptorConnector *connector;
+ QVariant value;
+ if (node->flags & QDBusConnection::ExportAdaptors &&
+ (connector = qDBusFindAdaptorConnector(node->obj))) {
+
+ // find the class that implements interface_name
+ QDBusAdaptorConnector::AdaptorMap::ConstIterator it;
+ it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(),
+ interface_name);
+ if (it != connector->adaptors.end() && it->interface == interface_name)
+ value = it->adaptor->property(property_name);
+ }
+
+ if (!value.isValid() && node->flags & QDBusConnection::ExportProperties) {
+ // try the object itself
+ int pidx = node->obj->metaObject()->indexOfProperty(property_name);
+ if (pidx != -1) {
+ QMetaProperty mp = node->obj->metaObject()->property(pidx);
+ if (mp.isScriptable() || (node->flags & QDBusConnection::ExportNonScriptableProperties) ==
+ QDBusConnection::ExportNonScriptableProperties)
+ value = mp.read(node->obj);
+ }
+ }
+
+ if (!value.isValid()) {
+ // the property was not found
+ sendPropertyError(msg, interface_name);
+ return;
+ }
+
+ QDBusMessage reply = QDBusMessage::methodReply(msg);
+ reply.setSignature(QLatin1String("v"));
+ reply << value;
+ msg.connection().send(reply);
+}
+
+void qDBusPropertySet(const QDBusConnectionPrivate::ObjectTreeNode *node, const QDBusMessage &msg)
+{
+ Q_ASSERT(msg.count() == 3);
+ QString interface_name = msg.at(0).toString();
+ QByteArray property_name = msg.at(1).toString().toUtf8();
+ QVariant value = msg.at(2).value<QDBusVariant>();
+
+ QDBusAdaptorConnector *connector;
+ if (node->flags & QDBusConnection::ExportAdaptors &&
+ (connector = qDBusFindAdaptorConnector(node->obj))) {
+
+ // find the class that implements interface_name
+ QDBusAdaptorConnector::AdaptorMap::ConstIterator it;
+ it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(),
+ interface_name);
+ if (it != connector->adaptors.end() && it->interface == interface_name)
+ if (it->adaptor->setProperty(property_name, value)) {
+ msg.connection().send(QDBusMessage::methodReply(msg));
+ return;
+ }
+ }
+
+ if (!value.isValid() && node->flags & QDBusConnection::ExportProperties) {
+ // try the object itself
+ int pidx = node->obj->metaObject()->indexOfProperty(property_name);
+ if (pidx != -1) {
+ QMetaProperty mp = node->obj->metaObject()->property(pidx);
+ if (mp.isScriptable() || (node->flags & QDBusConnection::ExportNonScriptableProperties) ==
+ QDBusConnection::ExportNonScriptableProperties) {
+
+ if (mp.write(node->obj, value)) {
+ msg.connection().send(QDBusMessage::methodReply(msg));
+ return;
+ }
+ }
+ }
+ }
+
+ // the property was not found or not written to
+ sendPropertyError(msg, interface_name);
+}
diff --git a/qt/qdbusintrospection.cpp b/qt/qdbusintrospection.cpp
index e1183b7..634ee9d 100644
--- a/qt/qdbusintrospection.cpp
+++ b/qt/qdbusintrospection.cpp
@@ -54,6 +54,11 @@
*/
/*!
+ \fn QDBusIntrospection::Argument::operator==
+ Compares this object against \p other and return true if they are the same.
+*/
+
+/*!
\struct QDBusIntrospection::Method
\brief Information about one method.
@@ -84,6 +89,11 @@
*/
/*!
+ \fn QDBusIntrospection::Method::operator==
+ Compares this object against \p other and return true if they are the same.
+*/
+
+/*!
\struct QDBusIntrospection::Signal
\brief Information about one signal.
@@ -92,6 +102,11 @@
*/
/*!
+ \var QDBusIntrospection::Signal::name
+ The signal's name.
+*/
+
+/*!
\var QDBusIntrospection::Signal::outputArgs
A list of the signal's arguments.
*/
@@ -103,6 +118,11 @@
*/
/*!
+ \fn QDBusIntrospection::Signal::operator==
+ Compares this object against \p other and return true if they are the same.
+*/
+
+/*!
\struct QDBusIntrospection::Property
\brief Information about one property.
@@ -140,6 +160,11 @@
*/
/*!
+ \fn QDBusIntrospection::Property::operator==
+ Compares this object against \p other and return true if they are the same.
+*/
+
+/*!
\struct QDBusIntrospection::Interface
\brief Information about one interface on the bus.
@@ -186,6 +211,14 @@
*/
/*!
+ \fn QDBusIntrospection::Interface::operator==
+ Compares this object against \p other and return true if they are the same.
+
+ Note that two interfaces are considered to be the same if they have the same name. The internal
+ structures in the objects are not compared.
+*/
+
+/*!
\struct QDBusIntrospection::Object
\brief Information about one object on the bus.
@@ -233,6 +266,7 @@
/*!
\struct QDBusIntrospection::ObjectTree
\brief Complete information about one object node and its descendency.
+
This struct contains the same data as QDBusIntrospection::Object, plus the actual data for the
interfaces and child (sub) objects that was available in the XML document.
*/
@@ -296,8 +330,8 @@
/*!
Parses the XML document fragment containing one interface.
- The first element tag in this XML data must be either <node> or <interface>. If it is
- <node>, then the <interface> tag must be a child tag of the <node> one.
+ The first element tag in this XML data must be either \<node\> or \<interface\>. If it is
+ \<node\>, then the \<interface\> tag must be a child tag of the \<node\> one.
If there are multiple interfaces in this XML data, it is undefined which one will be
returned.
@@ -320,8 +354,8 @@ QDBusIntrospection::parseInterface(const QString &xml)
/*!
Parses the XML document fragment containing several interfaces.
- If the first element tag in this document fragment is <node>, the interfaces parsed will
- be those found as child elements of the <node> tag.
+ If the first element tag in this document fragment is \<node\>, the interfaces parsed will
+ be those found as child elements of the \<node\> tag.
\param xml the XML data to be parsed
\returns the parsed interfaces
@@ -336,7 +370,7 @@ QDBusIntrospection::parseInterfaces(const QString &xml)
/*!
Parses the XML document fragment containing one object.
- The first element tag in this document must be <node>. If that tag does not contain
+ The first element tag in this document must be \<node\>. If that tag does not contain
a name attribute, the \a path argument will be used to determine the path of this
object node.
diff --git a/qt/qdbusintrospection.h b/qt/qdbusintrospection.h
index 4e1a874..86f8524 100644
--- a/qt/qdbusintrospection.h
+++ b/qt/qdbusintrospection.h
@@ -113,7 +113,7 @@ public:
Properties properties;
inline bool operator==(const Interface &other) const
- { return name == other.name; }
+ { return !name.isEmpty() && name == other.name; }
};
struct Object: public QSharedData
diff --git a/qt/qdbusmacros.h b/qt/qdbusmacros.h
index e36bfb7..72db643 100644
--- a/qt/qdbusmacros.h
+++ b/qt/qdbusmacros.h
@@ -1,6 +1,8 @@
/* qdbusmessage.h QDBusMessage object
*
* Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org>
+ * Copyright (C) 2006 Trolltech AS. All rights reserved.
+ * Author: Thiago Macieira <thiago.macieira@trolltech.com>
*
* Licensed under the Academic Free License version 2.1
*
@@ -15,22 +17,60 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, write to the Free Software Foundation
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
+/*!
+ \file qdbusmacros.h
+*/
+
#ifndef QDBUSMACROS_H
#define QDBUSMACROS_H
#include <QtCore/qglobal.h>
-#ifndef QDBUS_EXPORT
-#ifdef QDBUS_MAKEDLL
+#ifdef DBUS_COMPILATION
+/// \internal
# define QDBUS_EXPORT Q_DECL_EXPORT
#else
+/// \internal
# define QDBUS_EXPORT Q_DECL_IMPORT
#endif
+
+#ifndef Q_MOC_RUN
+/*!
+ \relates QDBusAbstractAdaptor
+ \brief Marks a method as "asynchronous"
+
+ The Q_ASYNC macro can be used to mark a method to be called and not wait for it to finish
+ processing before returning from QDBusInterface::call. The called method cannot return any
+ output arguments and, if it does, any such arguments will be discarded.
+
+ You can use this macro in your own adaptors by placing it before your method's return value
+ (which must be "void") in the class declaration, as shown in the example:
+ \code
+ Q_ASYNC void myMethod();
+ \endcode
+
+ Its presence in the method implementation (outside the class declaration) is optional.
+
+ \sa #async, \ref UsingAdaptors
+*/
+# define Q_ASYNC
+#endif
+#ifndef QT_NO_KEYWORDS
+
+/*!
+ \relates QDBusAbstractAdaptor
+ \brief Marks a method as "asynchronous"
+
+ This macro is the same as #Q_ASYNC and is provided as a shorthand. However, it is not defined if
+ QT_NO_KEYWORDS is defined, which makes Qt not use its extensions to the C++ language (keywords
+ emit, signals, slots).
+*/
+# define async Q_ASYNC
#endif
#endif
diff --git a/qt/qdbusmarshall.cpp b/qt/qdbusmarshall.cpp
index 19de0d9..eb9493b 100644
--- a/qt/qdbusmarshall.cpp
+++ b/qt/qdbusmarshall.cpp
@@ -22,7 +22,7 @@
*
*/
-#include "qdbusmarshall.h"
+#include "qdbusmarshall_p.h"
#include "qdbustype.h"
#include "qdbusvariant.h"
@@ -349,8 +349,10 @@ static void qAppendVariantToMessage(DBusMessageIter *it, const QDBusType & /* ty
}
else {
v = var;
- t = QDBusType::guessFromVariant(v);
}
+
+ if (!t.isValid())
+ t = QDBusType::guessFromVariant(v);
// now add this variant
DBusMessageIter sub;
diff --git a/qt/qdbusmarshall.h b/qt/qdbusmarshall_p.h
index 6ec8cae..7a2d46f 100644
--- a/qt/qdbusmarshall.h
+++ b/qt/qdbusmarshall_p.h
@@ -1,4 +1,4 @@
-/* qdbusmarshall.h QDBusMarshall object
+/* qdbusmarshall_p.h QDBusMarshall object
*
* Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org>
* Copyright (C) 2006 Trolltech AS. All rights reserved.
@@ -22,8 +22,20 @@
*
*/
-#ifndef QDBUSMARSHALL_H
-#define QDBUSMARSHALL_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the public API. This header file may
+// change from version to version without notice, or even be
+// removed.
+//
+// We mean it.
+//
+//
+
+#ifndef QDBUSMARSHALLPRIVATE_H
+#define QDBUSMARSHALLPRIVATE_H
struct DBusMessage;
@@ -31,6 +43,9 @@ template <typename T> class QList;
class QVariant;
class QString;
+/*!
+ \internal
+*/
class QDBusMarshall
{
public:
diff --git a/qt/qdbusmessage.cpp b/qt/qdbusmessage.cpp
index b88d7e3..15364a7 100644
--- a/qt/qdbusmessage.cpp
+++ b/qt/qdbusmessage.cpp
@@ -29,13 +29,13 @@
#include <dbus/dbus.h>
-#include "qdbusmarshall.h"
#include "qdbuserror.h"
+#include "qdbusmarshall_p.h"
#include "qdbusmessage_p.h"
QDBusMessagePrivate::QDBusMessagePrivate(QDBusMessage *qq)
- : msg(0), reply(0), q(qq), type(DBUS_MESSAGE_TYPE_INVALID), timeout(-1), ref(1),
- repliedTo(false)
+ : connection(QString()), msg(0), reply(0), q(qq), type(DBUS_MESSAGE_TYPE_INVALID),
+ timeout(-1), ref(1), repliedTo(false)
{
}
@@ -64,6 +64,17 @@ QDBusMessagePrivate::~QDBusMessagePrivate()
*/
/*!
+ \enum QDBusMessage::MessageType
+ The possible message types:
+
+ \value MethodCallMessage a message representing an outgoing or incoming method call
+ \value SignalMessage a message representing an outgoing or incoming signal emission
+ \value ReplyMessage a message representing the return values of a method call
+ \value ErrorMessage a message representing an error condition in response to a method call
+ \value InvalidMessage an invalid message: this is never set on messages received from D-Bus
+*/
+
+/*!
Constructs a new DBus message representing a signal emission. A DBus signal is emitted
from one application and is received by all applications that are listening for that signal
from that interface.
@@ -111,14 +122,11 @@ QDBusMessage QDBusMessage::signal(const QString &path, const QString &interface,
\param path the path of the object on the remote service to be called
\param interface the remote interface that is wanted (can be null)
\param method the remote method to be called (a.k.a., name)
- \param sig the DBus signature (set to null to discard processing and guess the
- method signature from the arguments; empty means no arguments)
\returns a QDBusMessage object that can be sent with QDBusConnection::send,
QDBusConnection::sendWithReply, or QDBusConnection::sendWithReplyAsync
*/
QDBusMessage QDBusMessage::methodCall(const QString &service, const QString &path,
- const QString &interface, const QString &method,
- const QString &sig)
+ const QString &interface, const QString &method)
{
QDBusMessage message;
message.d->type = DBUS_MESSAGE_TYPE_METHOD_CALL;
@@ -126,7 +134,6 @@ QDBusMessage QDBusMessage::methodCall(const QString &service, const QString &pat
message.d->path = path;
message.d->interface = interface;
message.d->name = method;
- message.d->signature = sig;
return message;
}
@@ -142,6 +149,7 @@ QDBusMessage QDBusMessage::methodReply(const QDBusMessage &other)
Q_ASSERT(other.d->msg);
QDBusMessage message;
+ message.d->connection = other.d->connection;
message.d->type = DBUS_MESSAGE_TYPE_METHOD_RETURN;
message.d->reply = dbus_message_ref(other.d->msg);
other.d->repliedTo = true;
@@ -164,6 +172,7 @@ QDBusMessage QDBusMessage::error(const QDBusMessage &other, const QString &name,
Q_ASSERT(other.d->msg);
QDBusMessage message;
+ message.d->connection = other.d->connection;
message.d->type = DBUS_MESSAGE_TYPE_ERROR;
message.d->name = name;
message.d->message = msg;
@@ -186,6 +195,7 @@ QDBusMessage QDBusMessage::error(const QDBusMessage &other, const QDBusError &er
Q_ASSERT(other.d->msg);
QDBusMessage message;
+ message.d->connection = other.d->connection;
message.d->type = DBUS_MESSAGE_TYPE_ERROR;
message.d->name = error.name();
message.d->message = error.message();
@@ -234,6 +244,11 @@ QDBusMessage &QDBusMessage::operator=(const QDBusMessage &other)
return *this;
}
+static inline const char *data(const QByteArray &arr)
+{
+ return arr.isEmpty() ? 0 : arr.constData();
+}
+
/*!
\internal
Constructs a DBusMessage object from this object. The returned value must be de-referenced
@@ -245,20 +260,18 @@ DBusMessage *QDBusMessage::toDBusMessage() const
switch (d->type) {
case DBUS_MESSAGE_TYPE_METHOD_CALL:
- msg = dbus_message_new_method_call(d->service.toUtf8().constData(),
- d->path.toUtf8().constData(), d->interface.toUtf8().constData(),
- d->name.toUtf8().constData());
+ msg = dbus_message_new_method_call(data(d->service.toUtf8()), data(d->path.toUtf8()),
+ data(d->interface.toUtf8()), data(d->name.toUtf8()));
break;
case DBUS_MESSAGE_TYPE_SIGNAL:
- msg = dbus_message_new_signal(d->path.toUtf8().constData(),
- d->interface.toUtf8().constData(), d->name.toUtf8().constData());
+ msg = dbus_message_new_signal(data(d->path.toUtf8()), data(d->interface.toUtf8()),
+ data(d->name.toUtf8()));
break;
case DBUS_MESSAGE_TYPE_METHOD_RETURN:
msg = dbus_message_new_method_return(d->reply);
break;
case DBUS_MESSAGE_TYPE_ERROR:
- msg = dbus_message_new_error(d->reply, d->name.toUtf8().constData(),
- d->message.toUtf8().constData());
+ msg = dbus_message_new_error(d->reply, data(d->name.toUtf8()), data(d->message.toUtf8()));
break;
}
if (!msg)
@@ -272,12 +285,13 @@ DBusMessage *QDBusMessage::toDBusMessage() const
\internal
Constructs a QDBusMessage by parsing the given DBusMessage object.
*/
-QDBusMessage QDBusMessage::fromDBusMessage(DBusMessage *dmsg)
+QDBusMessage QDBusMessage::fromDBusMessage(DBusMessage *dmsg, const QDBusConnection &connection)
{
QDBusMessage message;
if (!dmsg)
return message;
+ message.d->connection = connection;
message.d->type = dbus_message_get_type(dmsg);
message.d->path = QString::fromUtf8(dbus_message_get_path(dmsg));
message.d->interface = QString::fromUtf8(dbus_message_get_interface(dmsg));
@@ -333,12 +347,12 @@ QString QDBusMessage::name() const
}
/*!
- \fn QDBusMessage::member
+ \fn QDBusMessage::member() const
Returns the name of the method being called.
*/
/*!
- \fn QDBusMessage::method
+ \fn QDBusMessage::method() const
\overload
Returns the name of the method being called.
*/
@@ -352,7 +366,7 @@ QString QDBusMessage::service() const
}
/*!
- \fn QDBusMessage::sender
+ \fn QDBusMessage::sender() const
Returns the unique name of the remote sender.
*/
@@ -387,18 +401,6 @@ bool QDBusMessage::noReply() const
}
/*!
- Sets the flag that indicates whether we're expecting a reply from the callee. This flag only
- makes sense for MethodCall messages.
-
- \param enable whether to enable the flag (i.e., we are not expecting a reply)
-*/
-void QDBusMessage::setNoReply(bool enable)
-{
- if (d->msg)
- dbus_message_set_no_reply(d->msg, enable);
-}
-
-/*!
Returns the unique serial number assigned to this message
or 0 if the message was not sent yet.
*/
@@ -443,6 +445,24 @@ QString QDBusMessage::signature() const
}
/*!
+ Sets the signature for the output arguments of this method call. This function has no meaning
+ in other types of messages or when dealing with received method calls.
+*/
+void QDBusMessage::setSignature(const QString &signature)
+{
+ d->signature = signature;
+}
+
+/*!
+ Returns the connection this message was received on or an unconnected QDBusConnection object if
+ this isn't a message that has been received.
+*/
+QDBusConnection QDBusMessage::connection() const
+{
+ return d->connection;
+}
+
+/*!
Returns the message type.
*/
QDBusMessage::MessageType QDBusMessage::type() const
@@ -461,6 +481,144 @@ QDBusMessage::MessageType QDBusMessage::type() const
}
}
+// Document QDBusReply here
+/*!
+ \class QDBusReply
+ \brief The reply for a method call to a remote object.
+
+ A QDBusReply object is a subset of the QDBusMessage object that represents a method call's
+ reply. It contains only the first output argument or the error code and is used by
+ QDBusInterface-derived classes to allow returning the error code as the function's return
+ argument.
+
+ It can be used in the following manner:
+ \code
+ QDBusReply<QString> reply = interface.call("RemoteMethod");
+ if (reply.isSuccess())
+ // use the returned value
+ useValue(reply.value());
+ else
+ // call failed. Show an error condition.
+ showError(reply.error());
+ \endcode
+
+ If the remote method call cannot fail, you can skip the error checking:
+ \code
+ QString reply = interface.call("RemoteMethod");
+ \endcode
+
+ However, if it does fail under those conditions, the value returned by QDBusReply::value() is
+ undefined. It may be undistinguishable from a valid return value.
+
+ QDBusReply objects are used for remote calls that have no output arguments or return values
+ (i.e., they have a "void" return type). In this case, you can only test if the reply succeeded
+ or not, by calling isError() and isSuccess(), and inspecting the error condition by calling
+ error(). You cannot call value().
+
+ \sa QDBusMessage, QDBusInterface, \ref StandardInterfaces
+*/
+
+/*!
+ \fn QDBusReply::QDBusReply(const QDBusMessage &reply)
+ Automatically construct a QDBusReply object from the reply message \p reply, extracting the
+ first return value from it if it is a success reply.
+*/
+
+/*!
+ \fn QDBusReply::QDBusReply(const QDBusError &error)
+ Construct an error reply from the D-Bus error.
+*/
+
+/*!
+ \fn QDBusReply::isError() const
+ Returns true if this reply is an error reply. You can extract the error contents using the
+ error() function.
+*/
+
+/*!
+ \fn QDBusReply::isSuccess() const
+ Returns true if this reply is a normal error reply (not an error). You can extract the returned
+ value with value()
+*/
+
+/*!
+ \fn QDBusReply::error()
+ Returns the error code that was returned from the remote function call. If the remote call did
+ not return an error (i.e., if it succeeded), then the QDBusError object that is returned will
+ not be a valid error code (QDBusError::isValid() will return false).
+*/
+
+/*!
+ \fn QDBusReply::value()
+ Returns the remote function's calls return value. If the remote call returned with an error,
+ the return value of this function is undefined and may be undistinguishable from a valid return
+ value.
+
+ This function is not available if the remote call returns "void".
+*/
+
+/*!
+ \fn QDBusReply::operator Type()
+ Returns the same as value().
+
+ This function is not available if the remote call returns "void".
+*/
+
+/*!
+ \fn QDBusReply::fromVariant(const QDBusReply<QDBusVariant> &variantReply)>
+ Converts the QDBusReply<QDBusVariant> object to this type by converting the variant contained in
+ \p variantReply to the template's type and copying the error condition.
+
+ If the QDBusVariant in variantReply is not convertible to this type, it will assume an undefined
+ value.
+*/
+
+// document QDBusVariant here too
+/*!
+ \class QDBusVariant
+ \brief Represents the D-Bus type VARIANT.
+
+ This class represents a D-Bus argument of type VARIANT, which is composed of a type description
+ and its value.
+*/
+
+/*!
+ \var QDBusVariant::type
+ Contains the VARIANT's type. It will contain an invalid type if this QDBusVariant argument was
+ constructed, as opposed to being received over the D-Bus connection.
+*/
+
+/*!
+ \var QDBusVariant::value
+ Contain's the VARIANT's value.
+*/
+
+/*!
+ \fn QDBusVariant::QDBusVariant()
+ Constructs an empty variant. An empty variant cannot be sent over D-Bus without being
+ initialized first.
+*/
+
+/*!
+ \fn QDBusVariant::QDBusVariant(const QVariant &variant)
+ Constructs a D-Bus Variant from the QVariant value \p variant. The D-Bus type, if not set, will
+ be guessed from the QVariant value when actually sending the argument over D-Bus by calling
+ QDBusType::guessFromVariant. You should explicitly set the type if are unsure the automatic
+ guessing will produce the correct type.
+*/
+
+/*!
+ \fn QDBusVariant::QDBusVariant(const QVariant &variant, const QDBusType &forcetype)
+ Constructs a D-Bus Variant from the QVariant of value \p variant and sets the type to \p
+ forcetype. The actual transformation of the QVariant to the proper D-Bus type will happen only
+ when sending this argument over D-Bus.
+*/
+
+/*!
+ \fn QDBusVariant::operator const QVariant &() const
+ Returns the value #value.
+*/
+
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, QDBusMessage::MessageType t)
{
diff --git a/qt/qdbusmessage.h b/qt/qdbusmessage.h
index 7c190c6..6af5785 100644
--- a/qt/qdbusmessage.h
+++ b/qt/qdbusmessage.h
@@ -33,6 +33,7 @@
class QDBusMessagePrivate;
class QDBusError;
+class QDBusConnection;
struct DBusMessage;
class QDBUS_EXPORT QDBusMessage: public QList<QVariant>
@@ -52,8 +53,7 @@ public:
static QDBusMessage signal(const QString &path, const QString &interface,
const QString &name);
static QDBusMessage methodCall(const QString &destination, const QString &path,
- const QString &interface, const QString &method,
- const QString &signature = QString());
+ const QString &interface, const QString &method);
static QDBusMessage methodReply(const QDBusMessage &other);
static QDBusMessage error(const QDBusMessage &other, const QString &name,
const QString &message = QString());
@@ -72,13 +72,15 @@ public:
void setTimeout(int ms);
bool noReply() const;
- void setNoReply(bool enable);
QString signature() const;
+ void setSignature(const QString &signature);
+
+ QDBusConnection connection() const;
//protected:
DBusMessage *toDBusMessage() const;
- static QDBusMessage fromDBusMessage(DBusMessage *dmsg);
+ static QDBusMessage fromDBusMessage(DBusMessage *dmsg, const QDBusConnection &connection);
static QDBusMessage fromError(const QDBusError& error);
int serialNumber() const;
int replySerialNumber() const;
diff --git a/qt/qdbusmessage_p.h b/qt/qdbusmessage_p.h
index ef63324..22180db 100644
--- a/qt/qdbusmessage_p.h
+++ b/qt/qdbusmessage_p.h
@@ -27,6 +27,7 @@
#include <qatomic.h>
#include <qstring.h>
+#include "qdbusconnection.h"
struct DBusMessage;
class QDBusMessagePrivate
@@ -36,6 +37,7 @@ public:
~QDBusMessagePrivate();
QString service, path, interface, name, message, signature;
+ QDBusConnection connection;
DBusMessage *msg;
DBusMessage *reply;
QDBusMessage *q;
diff --git a/qt/qdbusobject.cpp b/qt/qdbusobject.cpp
index 139154e..3fd9a5b 100644
--- a/qt/qdbusobject.cpp
+++ b/qt/qdbusobject.cpp
@@ -33,32 +33,50 @@
#include "qdbusobject_p.h"
#include "qdbusutil.h"
+/*!
+ \class QDBusObject
+ \brief Base object for referencing remote D-Bus Objects
+
+ This class provides the basic functionality for referencing remote objects. It does not,
+ however, allow you to place calls to the remote object: you have to use the QDBusInterface class
+ for that.
+*/
+
+/*!
+ \internal
+*/
QDBusObject::QDBusObject(QDBusObjectPrivate* p, const QDBusConnection& conn)
:d(p), m_conn(conn)
{
}
-QDBusObject::QDBusObject(const QDBusConnection& conn, const QString& service, const QString& path)
- : m_conn(conn)
-{
- *this = m_conn.findObject(service, path);
-}
-
+/*!
+ Creates a QDBusObject that references the same object that the QDBusInterface class does.
+*/
QDBusObject::QDBusObject(const QDBusInterface& iface)
: m_conn(iface.connection())
{
*this = m_conn.findObject(iface.service(), iface.path());
}
+/*!
+ Copy constructor: creates a copy of the \p other object.
+*/
QDBusObject::QDBusObject(const QDBusObject& other)
: d(other.d), m_conn(other.m_conn)
{
}
+/*!
+ Destroys this object and frees any resource it held.
+*/
QDBusObject::~QDBusObject()
{
}
+/*!
+ Assignment operator: copy the contents of the \p other QDBusObject.
+*/
QDBusObject& QDBusObject::operator=(const QDBusObject& other)
{
#if 0
@@ -74,21 +92,38 @@ QDBusObject& QDBusObject::operator=(const QDBusObject& other)
return *this;
}
+/*!
+ Returns the connection this object is bound to.
+*/
QDBusConnection QDBusObject::connection() const
{
return m_conn;
}
+/*!
+ Returns the service this object is associated to.
+ \sa connection
+*/
QString QDBusObject::service() const
{
return d ? d->data->service : QString();
}
+/*!
+ Returns the path on the remote service this object is on.
+ \sa connection, service
+*/
QString QDBusObject::path() const
{
return d ? d->data->path : QString();
}
+/*!
+ Places an Introspect call to the remote object and return the XML data that describes its
+ contents. This is the raw XML data of the structures introspectionData() returns.
+
+ \bug We should not cache here. The remote object can change.
+*/
QString QDBusObject::introspect() const
{
if (!d)
@@ -97,17 +132,22 @@ QString QDBusObject::introspect() const
if (d->data->introspection.isNull()) {
// Try to introspect
- QDBusIntrospectableInterface iface = *this;
- QString xml = iface.introspect();
+ QDBusIntrospectableInterface iface(*this);
+ QDBusReply<QString> reply = iface.introspect();
- if (!m_conn.lastError().isValid()) {
+ if (reply.isSuccess()) {
// this will change the contents of d->data
- QDBusXmlParser::parse(d, xml);
+ QDBusXmlParser::parse(d, reply);
}
}
return d->data->introspection;
}
+/*!
+ Places an Introspect call to the remote object and return the parsed structures representing the
+ object's interfaces and child objects. The raw XML data corresponding to this function's
+ structures can be obtained using introspect().
+*/
QSharedDataPointer<QDBusIntrospection::Object> QDBusObject::introspectionData() const
{
QSharedDataPointer<QDBusIntrospection::Object> retval;
@@ -116,12 +156,23 @@ QSharedDataPointer<QDBusIntrospection::Object> QDBusObject::introspectionData()
return retval;
}
+/*!
+ Returns a list of all the interfaces in this object. This is the same value as the found in the
+ \ref QDBusIntrospection::Object::interfaces "interfaces" member of the value returned by
+ introspectionData().
+*/
QStringList QDBusObject::interfaces() const
{
introspect();
return d ? d->data->interfaces : QStringList();
}
+/*!
+ Returns a map of all the children object in this object along with pre-created QDBusObjects for
+ referencing them.
+
+ \todo Write this function!
+*/
QMap<QString, QDBusObject> QDBusObject::children() const
{
QMap<QString, QDBusObject> retval;
@@ -141,6 +192,10 @@ QMap<QString, QDBusObject> QDBusObject::children() const
return retval;
}
+/*!
+ Returns true if we're referencing a valid object service and path. This does not mean the object
+ actually exists in the remote application or that the remote application exists.
+*/
bool QDBusObject::isValid() const
{
return d && m_conn.isConnected() && QDBusUtil::isValidBusName(d->data->service) &&
@@ -148,6 +203,9 @@ bool QDBusObject::isValid() const
}
#if 0 // we don't have a way of determining if an object exists or not
+/*!
+ Returns true if the object being referenced exists.
+*/
bool QDBusObject::exists() const
{
if (!isValid())
@@ -167,7 +225,24 @@ bool QDBusObject::exists() const
}
if (err.name == DBUS_ERROR_SERVICE_UNKNOWN ||
- err.name == DBUS_ERROR_BAD_ADDRESS
+ err.name == DBUS_ERROR_BAD_ADDRESS)
return !m_conn.lastError().isValid();
}
#endif
+
+/*!
+ \fn QDBusObject::operator Interface()
+ Cast this object to an interface, if possible.
+*/
+
+/*!
+ \fn QDBusObject::operator const Interface()
+ Cast this object to an interface, if possible.
+*/
+
+/*!
+ \fn qdbus_cast
+ \relates QDBusObject
+
+ Casts a QDBusObject to the QDBusInterface-derived class of type Interface.
+*/
diff --git a/qt/qdbusobject.h b/qt/qdbusobject.h
index ffb41ad..584f3b2 100644
--- a/qt/qdbusobject.h
+++ b/qt/qdbusobject.h
@@ -43,102 +43,43 @@ template<class Interface>
inline const Interface qdbus_cast(const QDBusObject& obj, Interface * = 0);
class QDBusObjectPrivate;
-/**
- * QDBusObject
- * Base object for DBUS objects imported and exported.
- */
class QDBUS_EXPORT QDBusObject
{
friend class QDBusConnection;
public:
// public constructors
- /**
- * Construct a QDBusObject referencing the remote object given.
- */
- QDBusObject(const QDBusConnection& conn, const QString& service, const QString& path);
-
- /**
- * Copy constructor.
- */
QDBusObject(const QDBusObject& other);
-
- /**
- * Construct from an interface.
- */
QDBusObject(const QDBusInterface& iface);
// public destructors
- /**
- * Destructor.
- */
~QDBusObject();
public:
// public functions
-
- /**
- * Assignment operator
- */
QDBusObject& operator=(const QDBusObject&);
- /**
- * Returns the connection this object is bound to.
- */
QDBusConnection connection() const;
-
- /**
- * Returns the service this object is associated to.
- */
QString service() const;
-
- /**
- * Returns the path on the service this object is on.
- */
QString path() const;
- /**
- * Returns the introspection XML data of this object node.
- */
QString introspect() const;
-
- /**
- * Returns the introspection data for this object node.
- */
QSharedDataPointer<QDBusIntrospection::Object> introspectionData() const;
- /**
- * Returns all the interfaces in this object.
- */
QStringList interfaces() const;
-
- /**
- * Returns all the children object in this object.
- */
QMap<QString, QDBusObject> children() const;
- /**
- * Returns true if the object being referenced exists.
- */
//bool exists() const;
-
- /**
- * Returns true if we're referencing a valid object.
- */
bool isValid() const;
- /**
- * Cast this object to an interface, if possible.
- */
+#ifndef QT_NO_MEMBER_TEMPLATES
template<typename Interface>
inline operator Interface()
{ return qdbus_cast<Interface>(*this); }
- /**
- * Cast this object to an interface, if possible.
- */
template<typename Interface>
inline operator const Interface() const
{ return qdbus_cast<Interface>(*this); }
+#endif
private:
QDBusObject(QDBusObjectPrivate*, const QDBusConnection& conn);
diff --git a/qt/qdbusreply.h b/qt/qdbusreply.h
new file mode 100644
index 0000000..3d7a008
--- /dev/null
+++ b/qt/qdbusreply.h
@@ -0,0 +1,110 @@
+/* qdbusreply.h QDBusReply object - a reply from D-Bus
+ *
+ * Copyright (C) 2006 Trolltech AS. All rights reserved.
+ * Author: Thiago Macieira <thiago.macieira@trolltech.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef QDBUSREPLY_H
+#define QDBUSREPLY_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qvariant.h>
+
+#include "qdbusmacros.h"
+#include "qdbusmessage.h"
+#include "qdbuserror.h"
+#include "qdbusvariant.h"
+
+template<typename T>
+class QDBUS_EXPORT QDBusReply
+{
+ typedef T Type;
+public:
+
+ inline QDBusReply(const QDBusMessage &reply)
+ : m_error(reply)
+ {
+ if (isSuccess())
+ m_data = qvariant_cast<Type>(reply.at(0));
+ }
+ inline QDBusReply(const QDBusError &error)
+ : m_error(error)
+ {
+ }
+
+ inline bool isError() const { return m_error.isValid(); }
+ inline bool isSuccess() const { return !m_error.isValid(); }
+
+ inline const QDBusError& error() { return m_error; }
+
+ inline Type value()
+ {
+ return m_data;
+ }
+
+ inline operator Type ()
+ {
+ return m_data;
+ }
+
+ static QDBusReply<T> fromVariant(const QDBusReply<QDBusVariant> &variantReply)
+ {
+ QDBusReply<T> retval;
+ retval.m_error = variantReply.m_error;
+ if (retval.isSuccess()) {
+ retval.m_data = qvariant_cast<Type>(variantReply.value);
+ if (!qVariantCanConvert<Type>(variantReply.value))
+ retval.m_error = QDBusError(QLatin1String(DBUS_ERROR_INVALID_SIGNATURE),
+ QLatin1String("Unexpected reply signature"));
+ }
+ return retval;
+ }
+
+private:
+ QDBusError m_error;
+ Type m_data;
+};
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+// specialize for void:
+template<>
+class QDBUS_EXPORT QDBusReply<void>
+{
+public:
+ inline QDBusReply(const QDBusMessage &reply)
+ : m_error(reply)
+ {
+ }
+ inline QDBusReply(const QDBusError &error)
+ : m_error(error)
+ {
+ }
+
+ inline bool isError() const { return m_error.isValid(); }
+ inline bool isSuccess() const { return !m_error.isValid(); }
+
+ inline const QDBusError& error() { return m_error; }
+
+private:
+ QDBusError m_error;
+};
+#endif
+
+#endif
diff --git a/qt/qdbusstandardinterfaces.cpp b/qt/qdbusstandardinterfaces.cpp
index f8ea7e1..e444d97 100644
--- a/qt/qdbusstandardinterfaces.cpp
+++ b/qt/qdbusstandardinterfaces.cpp
@@ -24,18 +24,205 @@
#include "qdbusstandardinterfaces.h"
+/*!
+ \page StandardInterfaces Standard D-Bus Interfaces
+
+ The standard, well-known interfaces provided by D-Bus are as follows:
+ \value org.freedesktop.DBus.Peer Peer detection
+ \value org.freedesktop.DBus.Introspectable Introspection of remote object's contents
+ \value org.freedesktop.DBus.Properties Access to remote object's properties
+
+ The QtDBus implementation provides easy access to those three interfaces with the
+ QDBusPeerInterface, QDBusIntrospectableInterface and QDBusPropertiesInterface classes. As a
+ convenience form, they can also be accessed by the classes org::freedesktop::DBus::Peer,
+ org::freedesktop::DBus::Introspectable and org::freedesktop::DBus::Properties.
+
+ Those three classes also illustrate code-generation by the \ref dbusidl2cpp tool: the methods
+ defined in those three interfaces are provided as member functions in the QtDBus classes, which
+ are capable of type-checking the parameters at compile-time, in order to guarantee that they
+ conform to the types expected by the remote objects.
+*/
+
+/*!
+ \typedef org::freedesktop::DBus::Peer
+*/
+
+/*!
+ \typedef org::freedesktop::DBus::Introspectable
+*/
+
+/*!
+ \typedef org::freedesktop::DBus::Properties
+*/
+
+/*!
+ \class QDBusPeerInterface
+ \brief Provides access to the \a org.freedesktop.DBus.Peer interface.
+
+ This interface has only one method: ping(). Calling this method will generate a success reply if
+ the target service exists or a failure if it doesn't. The target object path is irrelevant in
+ this case.
+*/
+
+/*!
+ \fn QDBusPeerInterface::staticInterfaceName
+ Returns the interface name: "org.freedesktop.DBus.Peer"
+*/
+
+/*!
+ \fn QDBusPeerInterface::staticIntrospectionData
+ Returns the XML fragment corresponding to this interface's definition.
+*/
+
+/*!
+ \fn QDBusPeerInterface::QDBusPeerInterface(const QDBusObject &)
+ Creates a QDBusPeerInterface object accessing interface the \a org.freedesktop.DBus.Peer
+ interface on object \p obj.
+*/
+
+/*!
+ \fn QDBusPeerInterface::introspectionData() const
+ Returns the XML fragment corresponding to this interface's definition.
+*/
+
+/*!
+ \fn QDBusPeerInterface::ping
+ Emits an \a org.freedesktop.DBus.Peer.Ping call to the remote object.
+*/
+
+/*!
+ Destroys this object.
+*/
QDBusPeerInterface::~QDBusPeerInterface()
{
}
+/*!
+ \class QDBusIntrospectableInterface
+ \brief Provides access to the \a org.freedesktop.DBus.Introspectable interface.
+
+ The \a Introspectable interface is used to obtain information about the remote object's
+ internals. Its one method, \a introspect(), returns a XML document describing the interfaces and
+ child objects of a remote object in the D-Bus bus.
+
+ The QtDBus implementation automatically introspects remote objects in order to construct the
+ introspection structures found in QDBusIntrospection and QDBusInterface.
+
+ \sa QDBusInterface, QDBusIntrospection, QDBusObject::interfaces, QDBusObject::childObjects,
+ QDBusObject::introspect
+*/
+
+/*!
+ \fn QDBusIntrospectableInterface::staticInterfaceName
+ Returns the interface name: "org.freedesktop.DBus.Introspection"
+*/
+
+/*!
+ \fn QDBusIntrospectableInterface::staticIntrospectionData
+ Returns the XML fragment corresponding to this interface's definition.
+*/
+
+/*!
+ \fn QDBusIntrospectableInterface::QDBusIntrospectableInterface(const QDBusObject &)
+ Creates a QDBusIntrospectableInterface object accessing interface the \a
+ org.freedesktop.DBus.Introspectable interface on object \p obj.
+*/
+
+/*!
+ \fn QDBusIntrospectableInterface::introspectionData() const
+ Returns the XML fragment corresponding to this interface's definition.
+*/
+
+/*!
+ \fn QDBusIntrospectableInterface::introspect
+ Places an \a org.freedesktop.DBus.Introspectable.Introspect call to the remote object and
+ return the XML result.
+*/
+
+/*!
+ Destroys the object.
+*/
QDBusIntrospectableInterface::~QDBusIntrospectableInterface()
{
}
+/*!
+ \class QDBusPropertiesInterface
+ \brief Provides access to the \a org.freedesktop.DBus.Properties interface
+
+ D-Bus interfaces can export properties, much like the ones used in QObject. In order to access
+ those properties, two methods are defined in the \a org.freedesktop.DBus.Properties interface:
+ get() and set(), which are similar in functionality to QObject::property and
+ QObject::setProperty.
+*/
+
+/*!
+ \fn QDBusPropertiesInterface::staticInterfaceName
+ Returns the interface name: "org.freedesktop.DBus.Properties"
+*/
+
+/*!
+ \fn QDBusPropertiesInterface::staticIntrospectionData
+ Returns the XML fragment corresponding to this interface's definition.
+*/
+
+/*!
+ \fn QDBusPropertiesInterface::QDBusPropertiesInterface(const QDBusObject &)
+ Creates a QDBusPropertiesInterface object accessing interface the \a
+ org.freedesktop.DBus.Properties interface on object \p obj.
+*/
+
+/*!
+ \fn QDBusPropertiesInterface::introspectionData() const
+ Returns the XML fragment corresponding to this interface's definition.
+*/
+
+/*!
+ \fn QDBusPropertiesInterface::set(const QString &interfaceName, const QString &propertyName,
+ const QDBusVariant &value)
+ Sets the property named \a propertyName in interface \a interfaceName in the remote object this
+ QDBusPropertiesInterface object points to to the value specified by \a value. This function is
+ analogous to QObject::setProperty.
+
+ If the type of the \a value parameter is not what the remote interface declared, the result is
+ undefined. See QDBusInterface::properties for information on remote properties.
+
+ \sa QDBusInterface::setProperty
+*/
+
+/*!
+ \fn QDBusPropertiesInterface::get(const QString &interfaceName, const QString &propertyName)
+ Retrieves the value of property named \a propertyName in interface \a interfaceName in the
+ remote object this QDBusPropertiesInterface object points to. This function is analogous to
+ QObject::property.
+
+ \sa QDBusInterface::property
+*/
+
+/*!
+ Destroys the object.
+*/
QDBusPropertiesInterface::~QDBusPropertiesInterface()
{
}
+#if 0
+/*!
+ \class QDBusBusInterface
+ \internal
+ \brief Provides access to the \a org.freedesktop.DBus interface found in the D-Bus server
+ daemon.
+
+ The org.freedesktop.DBus interface is found only in the D-Bus daemon server. It is used to
+ communicate with it and to request information about the bus itself and other applications in
+ it.
+
+ Normally, you don't need to use this interface in your application. Instead, use the methods in
+ QDBusConnection.
+
+ \sa QDBusConnection
+*/
+
QDBusBusInterface::~QDBusBusInterface()
{
}
@@ -112,3 +299,4 @@ const char* QDBusBusInterface::staticIntrospectionData()
"</signal>"
"</interface>";
}
+#endif
diff --git a/qt/qdbusstandardinterfaces.h b/qt/qdbusstandardinterfaces.h
index af2c8dd..f2c88c3 100644
--- a/qt/qdbusstandardinterfaces.h
+++ b/qt/qdbusstandardinterfaces.h
@@ -26,6 +26,7 @@
#define QDBUS_STANDARD_INTERFACES_H
#include "qdbusinterface.h"
+#include "qdbusreply.h"
#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
#include <dbus/dbus.h>
@@ -41,27 +42,23 @@ public:
static inline const char* staticIntrospectionData()
{
return
- "<interface name=\"org.freedesktop.DBus.Peer\">"
- "<method name=\"Ping\" />"
- "</interface>";
+ " <interface name=\"org.freedesktop.DBus.Peer\">\n"
+ " <method name=\"Ping\" />\n"
+ " </interface>\n";
}
public:
explicit QDBusPeerInterface(const QDBusObject& obj)
- : QDBusInterface(obj, staticInterfaceName())
- { }
-
- QDBusPeerInterface(QDBusConnection& conn, const QString& service, const QString& path)
- : QDBusInterface(conn, service, path, staticInterfaceName())
+ : QDBusInterface(obj, QLatin1String(staticInterfaceName()))
{ }
~QDBusPeerInterface();
inline virtual QString introspectionData() const
- { return staticIntrospectionData(); }
+ { return QString::fromLatin1(staticIntrospectionData()); }
- inline void ping()
- { call(QLatin1String("Ping")); }
+ inline QDBusReply<void> ping()
+ { return call(QLatin1String("Ping")); }
};
class QDBUS_EXPORT QDBusIntrospectableInterface: public QDBusInterface
@@ -73,28 +70,24 @@ public:
static inline const char* staticIntrospectionData()
{
return
- "<interface name=\"org.freedesktop.DBus.Introspectable\">"
- "<method name=\"Introspect\">"
- "<arg name=\"xml_data\" type=\"s\" direction=\"out\" />"
- "</method>"
- "</interface>";
+ " <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
+ " <method name=\"Introspect\">\n"
+ " <arg name=\"xml_data\" type=\"s\" direction=\"out\"/>\n"
+ " </method>\n"
+ " </interface>\n";
}
public:
explicit QDBusIntrospectableInterface(const QDBusObject& obj)
- : QDBusInterface(obj, staticInterfaceName())
- { }
-
- QDBusIntrospectableInterface(QDBusConnection& conn, const QString& service, const QString& path)
- : QDBusInterface(conn, service, path, staticInterfaceName())
+ : QDBusInterface(obj, QLatin1String(staticInterfaceName()))
{ }
~QDBusIntrospectableInterface();
inline virtual QString introspectionData() const
- { return staticIntrospectionData(); }
-
- inline QString introspect()
- { return call(QLatin1String("Introspect")).at(0).toString(); }
+ { return QLatin1String(staticIntrospectionData()); }
+
+ inline QDBusReply<QString> introspect()
+ { return call(QLatin1String("Introspect")); }
};
class QDBUS_EXPORT QDBusPropertiesInterface: public QDBusInterface
@@ -106,39 +99,38 @@ public:
static inline const char* staticIntrospectionData()
{
return
- "<interface name=\"org.freedesktop.DBus.Properties\">"
- "<method name=\"Get\">"
- "<arg name=\"interface_name\" type=\"s\" direction=\"in\"/>"
- "<arg name=\"property_name\" type=\"s\" direction=\"in\"/>"
- "<arg name=\"value\" type=\"v\" direction=\"out\"/>"
- "</method>"
- "<method name=\"Set\">"
- "<arg name=\"interface_name\" type=\"s\" direction=\"in\"/>"
- "<arg name=\"property_name\" type=\"s\" direction=\"in\"/>"
- "<arg name=\"value\" type=\"v\" direction=\"in\"/>"
- "</method>";
+ " <interface name=\"org.freedesktop.DBus.Properties\">\n"
+ " <method name=\"Get\">\n"
+ " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+ " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+ " <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
+ " </method>\n"
+ " <method name=\"Set\">\n"
+ " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+ " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+ " <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
+ " </method>\n"
+ " </interface>\n";
}
public:
explicit QDBusPropertiesInterface(const QDBusObject& obj)
- : QDBusInterface(obj, staticInterfaceName())
- { }
-
- QDBusPropertiesInterface(QDBusConnection& conn, const QString& service, const QString& path)
- : QDBusInterface(conn, service, path, staticInterfaceName())
+ : QDBusInterface(obj, QLatin1String(staticInterfaceName()))
{ }
~QDBusPropertiesInterface();
-
+
inline virtual QString introspectionData() const
- { return staticIntrospectionData(); }
+ { return QString::fromLatin1(staticIntrospectionData()); }
- inline void set(const QString& interfaceName, const QString& propertyName, QVariant value)
- { call(QLatin1String("Set.ssv"), interfaceName, propertyName, value); }
+ inline QDBusReply<void> set(const QString& interfaceName, const QString& propertyName,
+ const QDBusVariant &value)
+ { return call(QLatin1String("Set.ssv"), interfaceName, propertyName, value); }
- inline QVariant get(const QString& interfaceName, const QString& propertyName)
- { return call(QLatin1String("Get.ss"), interfaceName, propertyName).at(0); }
+ inline QDBusReply<QDBusVariant> get(const QString& interfaceName, const QString& propertyName)
+ { return call(QLatin1String("Get.ss"), interfaceName, propertyName); }
};
+#if 0
class QDBUS_EXPORT QDBusBusInterface: public QDBusInterface
{
public:
@@ -152,58 +144,54 @@ public:
: QDBusInterface(obj, staticInterfaceName())
{ }
- QDBusBusInterface(QDBusConnection& conn, const QString& service, const QString& path)
- : QDBusInterface(conn, service, path, staticInterfaceName())
- { }
-
~QDBusBusInterface();
inline virtual QString introspectionData() const
{ return staticIntrospectionData(); }
- inline unsigned requestName(const QString& name, unsigned flags)
- { return call(QLatin1String("RequestName.su"), name, flags).at(0).toUInt(); }
+ inline QDBusReply<unsigned> requestName(const QString& name, unsigned flags)
+ { return call(QLatin1String("RequestName.su"), name, flags); }
- inline unsigned releaseName(const QString& name)
- { return call(QLatin1String("ReleaseName.s"), name).at(0).toUInt(); }
+ inline QDBusReply<unsigned> releaseName(const QString& name)
+ { return call(QLatin1String("ReleaseName.s"), name); }
- inline unsigned startServiceByName(const QString& name, unsigned flags)
- { return call(QLatin1String("StartServiceByName.su"), name, flags).at(0).toUInt(); }
+ inline QDBusReply<unsigned> startServiceByName(const QString& name, unsigned flags)
+ { return call(QLatin1String("StartServiceByName.su"), name, flags); }
- inline QString Hello()
- { return call(QLatin1String("Hello")).at(0).toString(); }
+ inline QDBusReply<QString> Hello()
+ { return call(QLatin1String("Hello")); }
- inline bool nameHasOwner(const QString& name)
- { return call(QLatin1String("NameHasOwner.s"), name).at(0).toBool(); }
+ inline QDBusReply<bool> nameHasOwner(const QString& name)
+ { return call(QLatin1String("NameHasOwner.s"), name); }
- inline QStringList listNames()
- { return call(QLatin1String("ListNames")).at(0).toStringList(); }
+ inline QDBusReply<QStringList> listNames()
+ { return call(QLatin1String("ListNames")); }
- inline void addMatch(const QString& rule)
- { call(QLatin1String("AddMatch"), rule); }
+ inline QDBusReply<void> addMatch(const QString& rule)
+ { return call(QLatin1String("AddMatch"), rule); }
- inline void removeMatch(const QString& rule)
- { call(QLatin1String("RemoveMatch"), rule); }
+ inline QDBusReply<void> removeMatch(const QString& rule)
+ { return call(QLatin1String("RemoveMatch"), rule); }
- inline QString getNameOwner(const QString& name)
- { return call(QLatin1String("GetNameOwner.s"), name).at(0).toString(); }
+ inline QDBusReply<QString> getNameOwner(const QString& name)
+ { return call(QLatin1String("GetNameOwner.s"), name); }
- inline QStringList listQueuedOwners(const QString& name)
- { return call(QLatin1String("ListQueuedOwners.s"), name).at(0).toStringList(); }
+ inline QDBusReply<QStringList> listQueuedOwners(const QString& name)
+ { return call(QLatin1String("ListQueuedOwners.s"), name); }
- inline quint32 getConnectionUnixUser(const QString& connectionName)
- { return call(QLatin1String("GetConnectionUnixUser.s"), connectionName).at(0).toUInt(); }
+ inline QDBusReply<quint32> getConnectionUnixUser(const QString& connectionName)
+ { return call(QLatin1String("GetConnectionUnixUser.s"), connectionName); }
- inline quint32 getConnectionUnixProcessID(const QString& connectionName)
- { return call(QLatin1String("GetConnectionUnixProcessID.s"), connectionName).at(0).toUInt(); }
+ inline QDBusReply<quint32> getConnectionUnixProcessID(const QString& connectionName)
+ { return call(QLatin1String("GetConnectionUnixProcessID.s"), connectionName); }
- inline QByteArray getConnectionSELinuxSecurityContext(const QString& connectionName)
- { return call(QLatin1String("GetConnectionSELinuxSecurityContext.s"), connectionName).at(0).toByteArray(); }
+ inline QDBusReply<QByteArray> getConnectionSELinuxSecurityContext(const QString& connectionName)
+ { return call(QLatin1String("GetConnectionSELinuxSecurityContext.s"), connectionName); }
- inline void reloadConfig()
- { call(QLatin1String("ReloadConfig")); }
+ inline QDBusReply<void> reloadConfig()
+ { return call(QLatin1String("ReloadConfig")); }
};
-
+#endif
namespace org {
namespace freedesktop {
diff --git a/qt/qdbustype.cpp b/qt/qdbustype.cpp
index 036bbe1..97c9e15 100644
--- a/qt/qdbustype.cpp
+++ b/qt/qdbustype.cpp
@@ -92,79 +92,6 @@ public:
virtual QString addElementsToStruct(const QStringList& subTypes) ;
};
-#if 0
-/*
- * Parse the signature and return the max length that is valid
- */
-static int parse(const char* signature)
-{
- if (!signature || !*signature)
- return 0; // not valid
-
- switch (signature[0]) {
- case DBUS_TYPE_BOOLEAN:
- case DBUS_TYPE_BYTE:
- case DBUS_TYPE_INT16:
- case DBUS_TYPE_INT32:
- case DBUS_TYPE_UINT16:
- case DBUS_TYPE_UINT32:
- case DBUS_TYPE_INT64:
- case DBUS_TYPE_UINT64:
- case DBUS_TYPE_DOUBLE:
- case DBUS_TYPE_STRING:
- case DBUS_TYPE_OBJECT_PATH:
- case DBUS_TYPE_SIGNATURE:
- case DBUS_TYPE_VARIANT:
- return 1;
-
- case DBUS_TYPE_ARRAY: {
- // check if it's a dict-entry array
- if (signature[1] == DBUS_DICT_ENTRY_BEGIN_CHAR) {
- // the first type must be ok and primitive (length 1)
- char c[2] = { signature[2], 0 };
- if (parse(c) != 1)
- return 0; // not valid
-
- // the rest must be a valid type too
- int len = parse(signature + 3);
- if (len == 0)
- return 0; // not valid
-
- // check the closing brace
- if (signature[len + 3] != DBUS_DICT_ENTRY_END_CHAR)
- return 0; // not valid
-
- // it's valid
- return len + 4;
- }
-
- // it's not a dict-entry, so it's ok as long as the internal type is ok too
- int len = parse(signature + 1);
- return len ? len + 1 : 0;
- }
-
- case DBUS_STRUCT_BEGIN_CHAR: {
- // check that each entry is valid
- int i = 1;
- while (i) {
- if (i > 1 && signature[i] == DBUS_STRUCT_END_CHAR)
- break; // this is valid
-
- int len = parse(signature + i);
- if (len)
- i += len;
- else
- i = 0;
- }
- return i;
- }
-
- default:
- return 0; // not valid
- }
-}
-#endif
-
static QString findInMap(char type, const QDBusPrettyTypeBase::Entry* map)
{
for ( ; map->signature; ++map)
@@ -197,10 +124,10 @@ inline QString QDBusPrettyTypeBase::toString(const QDBusType& type)
case DBUS_TYPE_DICT_ENTRY: {
Q_ASSERT_X(subTypes.size() == 2, "QDBusType::toString",
"maps must have exactly two elements");
-
+
QString key = findInMap( subTypes.at(0).dbusType(), map );
QString value = toString( subTypes.at(1) );
-
+
Q_ASSERT(!key.isNull());
return addElementsToMap( key, value );
@@ -264,14 +191,14 @@ QString QDBusConventionalNames::addElementsToArray(const QString& subType)
const QDBusPrettyTypeBase::Entry* QDBusQtNames::entryMap()
{
static QDBusPrettyTypeBase::Entry translation[] = {
- { "quint8", DBUS_TYPE_BYTE },
+ { "uchar", DBUS_TYPE_BYTE },
{ "bool", DBUS_TYPE_BOOLEAN },
- { "qint16", DBUS_TYPE_INT16 },
- { "quint16", DBUS_TYPE_UINT16 },
- { "qint32", DBUS_TYPE_INT32 },
- { "quint32", DBUS_TYPE_UINT32 },
- { "qint64", DBUS_TYPE_INT64 },
- { "quint64", DBUS_TYPE_UINT64 },
+ { "short", DBUS_TYPE_INT16 },
+ { "ushort", DBUS_TYPE_UINT16 },
+ { "int", DBUS_TYPE_INT32 },
+ { "uint", DBUS_TYPE_UINT32 },
+ { "qlonglong", DBUS_TYPE_INT64 },
+ { "qulonglong", DBUS_TYPE_UINT64 },
{ "double", DBUS_TYPE_DOUBLE },
{ "QString", DBUS_TYPE_STRING },
{ "QString", DBUS_TYPE_OBJECT_PATH },
@@ -283,8 +210,8 @@ const QDBusPrettyTypeBase::Entry* QDBusQtNames::entryMap()
static inline QString templateArg(const QString& input)
{
- if (input.endsWith('>'))
- return input + ' ';
+ if (input.endsWith(QLatin1Char('>')))
+ return input + QLatin1Char(' ');
return input;
}
@@ -292,11 +219,14 @@ QString QDBusQtNames::addElementsToStruct(const QStringList& subTypes)
{
Q_UNUSED(subTypes);
- return QLatin1String("QList"); // CHANGEME in the future
+ return QLatin1String("QVariantList"); // CHANGEME in the future
}
QString QDBusQtNames::addElementsToMap(const QString& key, const QString& value)
{
+ if (key == QLatin1String("QString") && value == QLatin1String("QDBusVariant"))
+ return QLatin1String("QVariantMap");
+
return QString( QLatin1String("QMap<%1, %2>") )
.arg(key)
.arg( templateArg(value) );
@@ -304,10 +234,13 @@ QString QDBusQtNames::addElementsToMap(const QString& key, const QString& value)
QString QDBusQtNames::addElementsToArray(const QString& subType)
{
- if (subType == QLatin1String("quint8"))
+ if (subType == QLatin1String("uchar"))
// special case
return QLatin1String("QByteArray");
-
+ else if (subType == QLatin1String("QString"))
+ // special case
+ return QLatin1String("QStringList");
+
return QString( QLatin1String("QList<%1>") )
.arg( templateArg(subType) );
}
@@ -323,16 +256,19 @@ QString QDBusQVariantNames::addElementsToMap(const QString& key, const QString&
{
Q_UNUSED(key);
Q_UNUSED(value);
-
+
return QLatin1String("QVariantMap");
}
QString QDBusQVariantNames::addElementsToArray(const QString& subType)
{
- if (subType == QLatin1String("quint8"))
+ if (subType == QLatin1String("uchar"))
// special case
return QLatin1String("QByteArray");
-
+ else if (subType == QLatin1String("QString"))
+ // special case
+ return QLatin1String("QStringList");
+
return QLatin1String("QVariantList");
}
@@ -354,15 +290,42 @@ public:
/*!
\class QDBusType
-
- Represents one single DBus type.
+ \brief Represents one single D-Bus type.
+
+ D-Bus provides a set of primitive types that map to normal, C++ types and to QString, as well as
+ the possibility to extend the set with the so-called "container" types. The available types are
+ as follows:
+
+ - Primitive (or basic): integers of 16, 32 and 64 bits, both signed and unsigned; byte (8 bits);
+ double-precision floating point and Unicode strings
+ - Arrays: a homogeneous, ordered list of zero or more entries
+ - Maps: an unordered list of (key, value) pairs, where key must be a primitive type and value
+ can be any D-Bus type
+ - Structs: an ordered list of a fixed number of entries of any type
+ - Variants: a "wildcard" container that can assume the value of any other type, including
+ structs and arrays
+
+ Any type can be placed inside an array (including other arrays), but only entries of the same
+ type can be placed inside the same array. The analogous type for D-Bus arrays are the Qt
+ #QList template classes.
+
+ Structs have a fixed number of entries and each entry has a fixed type. They are analogous to C
+ and C++ structs (hence the name).
+
+ Maps or dictionaries are analogous to the Qt #QMap template class, with the additional
+ restriction that the key type must be a primitive one. D-Bus implements maps by using arrays of
+ a special type (a "dictionary entry"), so inspecting a QDBusType of a Map will reveal that it is
+ an array (see isArray()).
+
+ Variants contain exactly one entry, but the type can vary freely. It is analogous to the Qt
+ class #QVariant, but the QtDBus implementation uses #QDBusVariant to represent D-Bus Variants.
*/
/*!
\enum QDBusType::StringFormat
This enum is used in QDBusType::toString to determine which type of formatting
- to apply to the DBus types:
+ to apply to the D-Bus types:
\value ConventionalNames Use the DBus conventional names, such as STRING, BOOLEAN or
ARRAY of BYTE.
@@ -380,8 +343,6 @@ QDBusType::QDBusType()
/*!
Constructs the type based on the given DBus type.
-
- \param type the type
*/
QDBusType::QDBusType(int type)
{
@@ -392,7 +353,6 @@ QDBusType::QDBusType(int type)
/*!
Constructs the type based on the given QVariant type.
- \param type the type
\sa QVariant::Type
*/
QDBusType::QDBusType(QVariant::Type type)
@@ -430,7 +390,7 @@ QDBusType::QDBusType(QVariant::Type type)
/*!
Parses the given DBus signature and constructs the type it represents.
- \param signature the signature to parse. It must represent one single type, but can
+ \param signature the signature to parse. It must represent one single type, but can be
a container type.
*/
QDBusType::QDBusType(const char* signature)
@@ -447,8 +407,8 @@ QDBusType::QDBusType(const char* signature)
/*!
Parses the given DBus signature and constructs the type it represents.
-
- \param signature the signature to parse. It must represent one single type, but can
+
+ \param str the signature to parse. It must represent one single type, but can
a container type.
*/
QDBusType::QDBusType(const QString& str)
@@ -458,8 +418,8 @@ QDBusType::QDBusType(const QString& str)
/*!
Parses the given DBus signature and constructs the type it represents.
-
- \param signature the signature to parse. It must represent one single type, but can
+
+ \param str the signature to parse. It must represent one single type, but can
a container type.
*/
QDBusType::QDBusType(const QByteArray& str)
@@ -468,6 +428,7 @@ QDBusType::QDBusType(const QByteArray& str)
}
/*!
+ \internal
Creates a QDBusType object based on the current element pointed to by \a iter.
\param iter the iterator. Can be pointing to container types.
@@ -479,7 +440,7 @@ QDBusType::QDBusType(DBusSignatureIter* iter)
// we have to recurse
if ( d->code == DBUS_TYPE_VARIANT )
return; // no we don't. dbus_type_is_container lies to us
-
+
// we have to recurse
DBusSignatureIter subiter;
dbus_signature_iter_recurse(iter, &subiter);
@@ -545,11 +506,11 @@ QByteArray QDBusType::dbusSignature() const
QByteArray retval;
switch (d->code) {
// can only be array, map or struct
-
+
case DBUS_TYPE_ARRAY:
Q_ASSERT_X(d->subTypes.size() == 1, "QDBusType::dbusSignature",
"more than one element in array");
-
+
retval += DBUS_TYPE_ARRAY;
retval += d->subTypes.at(0).dbusSignature();
break;
@@ -563,7 +524,7 @@ QByteArray QDBusType::dbusSignature() const
Q_ASSERT(key != DBUS_TYPE_INVALID);
Q_ASSERT(!value.isEmpty());
-
+
retval.reserve(value.length() + 3);
retval = DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING;
retval += key;
@@ -593,7 +554,7 @@ QVariant::Type QDBusType::qvariantType() const
{
if (d && d->qvariantType != QVariant::Invalid)
return d->qvariantType;
-
+
// check the special array cases:
if (isArray()) {
QDBusType t = arrayElement();
@@ -619,7 +580,7 @@ bool QDBusType::isValid() const
/*!
Returns true if this type is a basic one.
-
+
\sa dbus_type_is_basic
*/
bool QDBusType::isBasic() const
@@ -629,7 +590,7 @@ bool QDBusType::isBasic() const
/*!
Returns true if this type is a container.
-
+
\sa dbus_type_is_container
*/
bool QDBusType::isContainer() const
@@ -639,7 +600,7 @@ bool QDBusType::isContainer() const
/*!
Returns the subtypes of this type, if this is a container.
-
+
\sa isContainer
*/
QDBusTypeList QDBusType::subTypes() const
@@ -728,7 +689,7 @@ QString QDBusType::toString(StringFormat sf) const
switch (sf) {
case ConventionalNames:
return QDBusConventionalNames().toString(*this);
-
+
case QtNames:
return QDBusQtNames().toString(*this);
@@ -778,7 +739,7 @@ QVariant::Type QDBusType::qvariantType(const char* signature)
case DBUS_TYPE_INT16:
case DBUS_TYPE_INT32:
return QVariant::Int;
-
+
case DBUS_TYPE_BYTE:
case DBUS_TYPE_UINT16:
case DBUS_TYPE_UINT32:
@@ -803,7 +764,7 @@ QVariant::Type QDBusType::qvariantType(const char* signature)
case DBUS_TYPE_VARIANT:
return QVariant::UserType; // must set user-type too
-
+
case DBUS_TYPE_ARRAY: // special case
// check if it's a string list
if (qvariantType(signature + 1) == QVariant::String)
@@ -872,7 +833,7 @@ int QDBusType::dbusType(QVariant::Type t)
case QVariant::Map:
// internal type information has been lost
return DBUS_TYPE_DICT_ENTRY;
-
+
case QVariant::List:
case QVariant::StringList:
case QVariant::ByteArray:
@@ -896,7 +857,7 @@ int QDBusType::dbusType(QVariant::Type t)
Converts the QVariant::Type to a DBus type signature.
\param t the type to convert
-*/
+*/
const char* QDBusType::dbusSignature(QVariant::Type t)
{
switch (t)
@@ -946,11 +907,11 @@ const char* QDBusType::dbusSignature(QVariant::Type t)
case QVariant::StringList:
return DBUS_TYPE_ARRAY_AS_STRING
DBUS_TYPE_STRING_AS_STRING; // as
-
+
case QVariant::ByteArray:
return DBUS_TYPE_ARRAY_AS_STRING
DBUS_TYPE_BYTE_AS_STRING; // ay
-
+
case QVariant::List:
// not a string list
// internal list data has been lost
@@ -967,6 +928,14 @@ const char* QDBusType::dbusSignature(QVariant::Type t)
}
/*!
+ \enum QDBusType::VariantListMode
+ Defines how the guessFromVariant() function will behave when the QVariant is of type
+ QVariant::List.
+
+ \todo Improve the algorithm
+*/
+
+/*!
Guesses the DBus type from the given variant.
*/
QDBusType QDBusType::guessFromVariant(const QVariant& variant, VariantListMode mode)
@@ -1014,7 +983,7 @@ QDBusType QDBusType::guessFromVariant(const QVariant& variant, VariantListMode m
t.d->subTypes << guessFromVariant(v, mode);
return t;
- }
+ }
}
else if (variant.type() == QVariant::Map) {
// investigate deeper
@@ -1061,25 +1030,25 @@ QDBusType QDBusType::guessFromVariant(const QVariant& variant, VariantListMode m
}
else
return QDBusType(variant.type());
-}
+}
/*!
\class QDBusTypeList
\brief A list of DBus types.
-
+
Represents zero or more DBus types in sequence, such as those used in argument lists
or in subtypes of structs and maps.
*/
/*!
\fn QDBusTypeList::QDBusTypeList()
-
+
Default constructor.
*/
/*!
\fn QDBusTypeList::QDBusTypeList(const QDBusTypeList& other)
-
+
Copy constructor.
\param other the list to copy
*/
@@ -1099,7 +1068,7 @@ QDBusTypeList::QDBusTypeList(const char* signature)
{
if (!signature || !*signature)
return; // empty
-
+
// validate it first
if ( !dbus_signature_validate(signature, 0) )
return;
@@ -1114,6 +1083,7 @@ QDBusTypeList::QDBusTypeList(const char* signature)
}
/*!
+ \internal
Constructs a type list by parsing the elements on this iterator level.
\param iter the iterator containing the elements on this level
diff --git a/qt/qdbustype.h b/qt/qdbustype.h
index c26d7c8..094f390 100644
--- a/qt/qdbustype.h
+++ b/qt/qdbustype.h
@@ -45,7 +45,7 @@ public:
QtNames,
QVariantNames
};
-
+
QDBusType();
explicit QDBusType(int type);
explicit QDBusType(QVariant::Type type);
@@ -110,9 +110,6 @@ public:
bool canBeMap() const;
- inline QDBusTypeList& operator<<(const QDBusType& item)
- { QList<QDBusType>::operator<<(item); return *this; }
-
QByteArray dbusSignature() const;
};
diff --git a/qt/qdbusutil.cpp b/qt/qdbusutil.cpp
index e10b302..d4a8659 100644
--- a/qt/qdbusutil.cpp
+++ b/qt/qdbusutil.cpp
@@ -30,13 +30,22 @@
namespace QDBusUtil
{
-
+ /*!
+ Returns true if this is \p ifaceName is a valid interface name.
+
+ Valid interface names must:
+ - not be empty
+ - not exceed 255 characters in length
+ - be composed of dot-separated string components that contain only ASCII letters, digits and the
+ underscore ("_") character
+ - contain at least two such components
+ */
bool isValidInterfaceName(const QString& ifaceName)
{
if (ifaceName.isEmpty() || ifaceName.length() > DBUS_MAXIMUM_NAME_LENGTH)
return false;
- QStringList parts = ifaceName.split('.');
+ QStringList parts = ifaceName.split(QLatin1Char('.'));
if (parts.count() < 2)
return false; // at least two parts
@@ -47,37 +56,55 @@ namespace QDBusUtil
return true;
}
+ /*!
+ Returns true if \p connName is a valid unique connection name.
+
+ Unique connection names start with a colon (":") and are followed by a list of dot-separated
+ components composed of ASCII letters, digits, the hypen or the underscore ("_") character.
+ */
bool isValidUniqueConnectionName(const QString &connName)
{
if (connName.isEmpty() || connName.length() > DBUS_MAXIMUM_NAME_LENGTH ||
- !connName.startsWith(':'))
+ !connName.startsWith(QLatin1Char(':')))
return false;
- QStringList parts = connName.mid(1).split('.');
+ QStringList parts = connName.mid(1).split(QLatin1Char('.'));
if (parts.count() < 1)
return false;
- QRegExp regex("[a-zA-Z0-9_-]+");
+ QRegExp regex(QLatin1String("[a-zA-Z0-9_-]+"));
foreach (QString part, parts)
if (!regex.exactMatch(part))
return false;
return true;
}
-
+
+ /*!
+ Returns true if \p busName is a valid bus name.
+
+ A valid bus name is either a valid unique connection name or follows the rules:
+ - is not empty
+ - does not exceed 255 characters in length
+ - be composed of dot-separated string components that contain only ASCII letters, digits,
+ hyphens or underscores ("_"), but don't start with a digit
+ - contains at least two such elements
+
+ \see isValidUniqueConnectionName
+ */
bool isValidBusName(const QString &busName)
{
if (busName.isEmpty() || busName.length() > DBUS_MAXIMUM_NAME_LENGTH)
return false;
- if (busName.startsWith(':'))
+ if (busName.startsWith(QLatin1Char(':')))
return isValidUniqueConnectionName(busName);
- QStringList parts = busName.split('.');
+ QStringList parts = busName.split(QLatin1Char('.'));
if (parts.count() < 1)
return false;
- QRegExp regex("[a-zA-Z_-][a-zA-Z0-9_-]*");
+ QRegExp regex(QLatin1String("[a-zA-Z_-][a-zA-Z0-9_-]*"));
foreach (QString part, parts)
if (!regex.exactMatch(part))
return false;
@@ -85,34 +112,53 @@ namespace QDBusUtil
return true;
}
+ /*!
+ Returns true if \p memberName is a valid member name. A valid member name does not exceed
+ 255 characters in length, is not empty, is composed only of ASCII letters, digits and
+ underscores, but does not start with a digit.
+ */
bool isValidMemberName(const QString &memberName)
{
if (memberName.isEmpty() || memberName.length() > DBUS_MAXIMUM_NAME_LENGTH)
return false;
- QRegExp regex("[a-zA-Z0-9_]+");
+ QRegExp regex(QLatin1String("[a-zA-Z_][a-zA-Z0-9_]+"));
return regex.exactMatch(memberName);
}
+ /*!
+ Returns true if \p errorName is a valid error name. Valid error names are valid interface
+ names and vice-versa, so this function is actually an alias for isValidInterfaceName.
+ */
bool isValidErrorName(const QString &errorName)
{
return isValidInterfaceName(errorName);
}
+ /*!
+ Returns true if \p path is valid object path.
+
+ Valid object paths follow the rules:
+ - start with the slash character ("/")
+ - do not end in a slash, unless the path is just the initial slash
+ - do not contain any two slashes in sequence
+ - contain slash-separated parts, each of which is composed of ASCII letters, digits and
+ underscores ("_")
+ */
bool isValidObjectPath(const QString &path)
{
if (path == QLatin1String("/"))
return true;
- if (!path.startsWith('/') || path.indexOf(QLatin1String("//")) != -1 ||
- path.endsWith('/'))
+ if (!path.startsWith(QLatin1Char('/')) || path.indexOf(QLatin1String("//")) != -1 ||
+ path.endsWith(QLatin1Char('/')))
return false;
- QStringList parts = path.split('/');
+ QStringList parts = path.split(QLatin1Char('/'));
Q_ASSERT(parts.count() >= 1);
parts.removeFirst(); // it starts with /, so we get an empty first part
-
- QRegExp regex("[a-zA-Z0-9_]+");
+
+ QRegExp regex(QLatin1String("[a-zA-Z0-9_]+"));
foreach (QString part, parts)
if (!regex.exactMatch(part))
return false;
@@ -120,14 +166,26 @@ namespace QDBusUtil
return true;
}
+ /*!
+ Returns true if \p signature is a valid D-Bus type signature for one or more types.
+ This function returns true if it can all of \p signature into valid, individual types and no
+ characters remain in \p signature.
+
+ \see isValidSingleSignature
+ */
bool isValidSignature(const QString &signature)
{
return dbus_signature_validate(signature.toUtf8(), 0);
}
+ /*!
+ Returns true if \p signature is a valid D-Bus type signature for exactly one full type. This
+ function tries to convert the type signature into a D-Bus type and, if it succeeds and no
+ characters remain in the signature, it returns true.
+ */
bool isValidSingleSignature(const QString &signature)
{
return dbus_signature_validate_single(signature.toUtf8(), 0);
}
-
+
} // namespace QDBusUtil
diff --git a/qt/qdbusvariant.h b/qt/qdbusvariant.h
index eb7e3aa..0a6d41e 100644
--- a/qt/qdbusvariant.h
+++ b/qt/qdbusvariant.h
@@ -33,6 +33,17 @@ struct QDBUS_EXPORT QDBusVariant
{
QDBusType type;
QVariant value;
+
+ inline QDBusVariant()
+ { }
+ inline QDBusVariant(const QVariant &variant) : value(variant)
+ { }
+ inline QDBusVariant(const QVariant &variant, const QDBusType &forcetype)
+ : type(forcetype), value(variant)
+ { }
+
+ inline operator const QVariant &() const
+ { return value; }
};
Q_DECLARE_METATYPE(QDBusVariant)
diff --git a/qt/qdbusxmlparser.cpp b/qt/qdbusxmlparser.cpp
index 9d80fe2..5586217 100644
--- a/qt/qdbusxmlparser.cpp
+++ b/qt/qdbusxmlparser.cpp
@@ -37,22 +37,22 @@ static QDBusIntrospection::Annotations
parseAnnotations(const QDomElement& elem)
{
QDBusIntrospection::Annotations retval;
- QDomNodeList list = elem.elementsByTagName("annotation");
+ QDomNodeList list = elem.elementsByTagName(QLatin1String("annotation"));
for (int i = 0; i < list.count(); ++i)
{
QDomElement ann = list.item(i).toElement();
if (ann.isNull())
continue;
-
- QString name = ann.attribute("name"),
- value = ann.attribute("value");
+
+ QString name = ann.attribute(QLatin1String("name")),
+ value = ann.attribute(QLatin1String("value"));
if (name.isEmpty())
continue;
retval.insert(name, value);
}
-
+
return retval;
}
@@ -68,24 +68,24 @@ static QDBusIntrospection::Arguments
parseArgs(const QDomElement& elem, const QLatin1String& direction, bool acceptEmpty = false)
{
QDBusIntrospection::Arguments retval;
- QDomNodeList list = elem.elementsByTagName("arg");
+ QDomNodeList list = elem.elementsByTagName(QLatin1String("arg"));
for (int i = 0; i < list.count(); ++i)
{
QDomElement arg = list.item(i).toElement();
if (arg.isNull())
continue;
- if ((acceptEmpty && !arg.hasAttribute("direction")) ||
- arg.attribute("direction") == direction) {
-
+ if ((acceptEmpty && !arg.hasAttribute(QLatin1String("direction"))) ||
+ arg.attribute(QLatin1String("direction")) == direction) {
+
QDBusIntrospection::Argument argData;
- if (arg.hasAttribute("name"))
- argData.name = arg.attribute("name"); // can be empty
- argData.type = parseType(arg.attribute("type"));
-
+ if (arg.hasAttribute(QLatin1String("name")))
+ argData.name = arg.attribute(QLatin1String("name")); // can be empty
+ argData.type = parseType(arg.attribute(QLatin1String("type")));
+
if (!argData.type.isValid())
continue;
-
+
retval << argData;
}
}
@@ -98,7 +98,7 @@ QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
{
QDomDocument doc;
doc.setContent(xmlData);
- m_node = doc.firstChildElement("node");
+ m_node = doc.firstChildElement(QLatin1String("node"));
}
QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
@@ -121,16 +121,16 @@ QDBusXmlParser::interfaces() const
if (m_node.isNull())
return retval;
-
- QDomNodeList interfaces = m_node.elementsByTagName("interface");
+
+ QDomNodeList interfaces = m_node.elementsByTagName(QLatin1String("interface"));
for (int i = 0; i < interfaces.count(); ++i)
{
QDomElement iface = interfaces.item(i).toElement();
- QString ifaceName = iface.attribute("name");
+ QString ifaceName = iface.attribute(QLatin1String("name"));
if (iface.isNull() || ifaceName.isEmpty())
continue; // for whatever reason
- QDBusIntrospection::Interface *ifaceData;
+ QDBusIntrospection::Interface *ifaceData = 0; // make gcc shut up
if (m_store) {
QSharedDataPointer<QDBusIntrospection::Interface> knownData =
m_store->findInterface(ifaceName);
@@ -161,11 +161,11 @@ QDBusXmlParser::interfaces() const
ifaceData->annotations = parseAnnotations(iface);
// parse methods
- QDomNodeList list = iface.elementsByTagName("method");
+ QDomNodeList list = iface.elementsByTagName(QLatin1String("method"));
for (int j = 0; j < list.count(); ++j)
{
QDomElement method = list.item(j).toElement();
- QString methodName = method.attribute("name");
+ QString methodName = method.attribute(QLatin1String("name"));
if (method.isNull() || methodName.isEmpty())
continue;
@@ -178,15 +178,15 @@ QDBusXmlParser::interfaces() const
methodData.annotations = parseAnnotations(method);
// add it
- ifaceData->methods.insert(methodName, methodData);
+ ifaceData->methods.insert(methodName, methodData);
}
// parse signals
- list = iface.elementsByTagName("signal");
+ list = iface.elementsByTagName(QLatin1String("signal"));
for (int j = 0; j < list.count(); ++j)
{
QDomElement signal = list.item(j).toElement();
- QString signalName = signal.attribute("name");
+ QString signalName = signal.attribute(QLatin1String("name"));
if (signal.isNull() || signalName.isEmpty())
continue;
@@ -202,11 +202,11 @@ QDBusXmlParser::interfaces() const
}
// parse properties
- list = iface.elementsByTagName("property");
+ list = iface.elementsByTagName(QLatin1String("property"));
for (int j = 0; j < list.count(); ++j)
{
QDomElement property = list.item(j).toElement();
- QString propertyName = property.attribute("name");
+ QString propertyName = property.attribute(QLatin1String("name"));
if (property.isNull() || propertyName.isEmpty())
continue;
@@ -214,14 +214,14 @@ QDBusXmlParser::interfaces() const
// parse data
propertyData.name = propertyName;
- propertyData.type = parseType(property.attribute("type"));
+ propertyData.type = parseType(property.attribute(QLatin1String("type")));
propertyData.annotations = parseAnnotations(property);
if (!propertyData.type.isValid())
// cannot be!
continue;
- QString access = property.attribute("access");
+ QString access = property.attribute(QLatin1String("access"));
if (access.isEmpty())
// can't be empty either!
continue;
@@ -248,49 +248,51 @@ QDBusXmlParser::interfaces() const
QSharedDataPointer<QDBusIntrospection::Object>
QDBusXmlParser::object() const
{
- QSharedDataPointer<QDBusIntrospection::Object> retval;
-
if (m_node.isNull())
- return retval;
+ return QSharedDataPointer<QDBusIntrospection::Object>();
// check if the store knows about this one
QDBusIntrospection::Object* objData;
if (m_store) {
- retval = objData = m_store->findObject(m_service, m_path);
+ objData = m_store->findObject(m_service, m_path);
}
else {
objData = new QDBusIntrospection::Object;
objData->service = m_service;
objData->path = m_path;
}
-
+
// check if we have anything to process
if (objData->introspection.isNull() && !m_node.firstChild().isNull()) {
// yes, introspect this object
QTextStream ts(&objData->introspection);
m_node.save(ts,2);
-
- QDomNodeList objects = m_node.elementsByTagName("node");
+
+ QDomNodeList objects = m_node.elementsByTagName(QLatin1String("node"));
for (int i = 0; i < objects.count(); ++i) {
QDomElement obj = objects.item(i).toElement();
- QString objName = obj.attribute("name");
+ QString objName = obj.attribute(QLatin1String("name"));
if (obj.isNull() || objName.isEmpty())
continue; // for whatever reason
objData->childObjects.append(objName);
}
- QDomNodeList interfaces = m_node.elementsByTagName("interface");
+ QDomNodeList interfaces = m_node.elementsByTagName(QLatin1String("interface"));
for (int i = 0; i < interfaces.count(); ++i) {
QDomElement iface = interfaces.item(i).toElement();
- QString ifaceName = iface.attribute("name");
+ QString ifaceName = iface.attribute(QLatin1String("name"));
if (iface.isNull() || ifaceName.isEmpty())
continue;
objData->interfaces.append(ifaceName);
}
+ } else {
+ objData->introspection = QLatin1String("<node/>\n");
}
+ QSharedDataPointer<QDBusIntrospection::Object> retval;
+ retval = objData;
return retval;
}
@@ -308,17 +310,17 @@ QDBusXmlParser::objectTree() const
retval->path = m_path;
QTextStream ts(&retval->introspection);
- m_node.save(ts,2);
-
+ m_node.save(ts,2);
+
// interfaces are easy:
retval->interfaceData = interfaces();
retval->interfaces = retval->interfaceData.keys();
// sub-objects are slightly more difficult:
- QDomNodeList objects = m_node.elementsByTagName("node");
+ QDomNodeList objects = m_node.elementsByTagName(QLatin1String("node"));
for (int i = 0; i < objects.count(); ++i) {
QDomElement obj = objects.item(i).toElement();
- QString objName = obj.attribute("name");
+ QString objName = obj.attribute(QLatin1String("name"));
if (obj.isNull() || objName.isEmpty())
continue; // for whatever reason
@@ -331,10 +333,10 @@ QDBusXmlParser::objectTree() const
// parse it
QString objAbsName = m_path;
- if (!objAbsName.endsWith('/'))
- objAbsName.append('/');
+ if (!objAbsName.endsWith(QLatin1Char('/')))
+ objAbsName.append(QLatin1Char('/'));
objAbsName += objName;
-
+
QDBusXmlParser parser(m_service, objAbsName, obj, m_store);
retval->childObjectData.insert(objName, parser.objectTree());
}
@@ -344,4 +346,4 @@ QDBusXmlParser::objectTree() const
return QSharedDataPointer<QDBusIntrospection::ObjectTree>( retval );
}
-
+