summaryrefslogtreecommitdiffstats
path: root/qt
diff options
context:
space:
mode:
authorThiago Macieira <thiago@kde.org>2006-02-15 16:25:12 +0000
committerThiago Macieira <thiago@kde.org>2006-02-15 16:25:12 +0000
commitc2432800b1d387c5512e605ba92060d4827b7606 (patch)
tree859ad5945d12efbfecdbd0fb1244642de6ae51c6 /qt
parent120d4ae484acde0d8684a2ba72874bd1c3017b97 (diff)
Merge the changes to the bindings from the KDE Subversion server.
This is a major change: library is source- and binary-incompatible to what it used to be. All testcases are green, functionality is preserved. It is not feature-complete. Development will continue in the branch in the Subversion server for a while.
Diffstat (limited to 'qt')
-rw-r--r--qt/Makefile.am57
-rw-r--r--qt/README10
-rw-r--r--qt/qdbusabstractadaptor.cpp64
-rw-r--r--qt/qdbusabstractadaptor.h44
-rw-r--r--qt/qdbusconnection.cpp203
-rw-r--r--qt/qdbusconnection.h55
-rw-r--r--qt/qdbusconnection_p.h144
-rw-r--r--qt/qdbuserror.cpp19
-rw-r--r--qt/qdbuserror.h10
-rw-r--r--qt/qdbusintegrator.cpp916
-rw-r--r--qt/qdbusinterface.cpp256
-rw-r--r--qt/qdbusinterface.h310
-rw-r--r--qt/qdbusinterface_p.h67
-rw-r--r--qt/qdbusintrospection.cpp380
-rw-r--r--qt/qdbusintrospection.h148
-rw-r--r--qt/qdbusmacros.h36
-rw-r--r--qt/qdbusmarshall.cpp444
-rw-r--r--qt/qdbusmarshall.h13
-rw-r--r--qt/qdbusmessage.cpp254
-rw-r--r--qt/qdbusmessage.h27
-rw-r--r--qt/qdbusmessage_p.h13
-rw-r--r--qt/qdbusobject.cpp173
-rw-r--r--qt/qdbusobject.h161
-rw-r--r--qt/qdbusobject_p.h69
-rw-r--r--qt/qdbusserver.cpp6
-rw-r--r--qt/qdbusserver.h2
-rw-r--r--qt/qdbusstandardinterfaces.cpp114
-rw-r--r--qt/qdbusstandardinterfaces.h218
-rw-r--r--qt/qdbusthread.cpp116
-rw-r--r--qt/qdbustype.cpp1151
-rw-r--r--qt/qdbustype.h119
-rw-r--r--qt/qdbusutil.cpp133
-rw-r--r--qt/qdbusutil.h50
-rw-r--r--qt/qdbusvariant.h16
-rw-r--r--qt/qdbusxmlparser.cpp347
-rw-r--r--qt/qdbusxmlparser_p.h59
36 files changed, 5741 insertions, 463 deletions
diff --git a/qt/Makefile.am b/qt/Makefile.am
index 9247ba1e..9117d27e 100644
--- a/qt/Makefile.am
+++ b/qt/Makefile.am
@@ -6,11 +6,19 @@ dbusincludedir=$(includedir)/dbus-1.0/dbus
lib_LTLIBRARIES=libdbus-qt4-1.la
dbusinclude_HEADERS= \
- qdbuserror.h \
- qdbusmessage.h \
- qdbusserver.h \
+ qdbusmacros.h \
+ qdbuserror.h \
+ qdbusmessage.h \
+ qdbusserver.h \
qdbusconnection.h \
- qdbusvariant.h
+ qdbusvariant.h \
+ qdbusobject.h \
+ qdbusinterface.h \
+ qdbustype.h \
+ qdbusstandardinterfaces.h \
+ qdbusutil.h \
+ qdbusintrospection.h \
+ qdbusabstractadaptor.h
libdbus_qt4_1_la_SOURCES = \
$(top_srcdir)/qt/qdbusconnection.cpp \
@@ -19,29 +27,46 @@ libdbus_qt4_1_la_SOURCES = \
$(top_srcdir)/qt/qdbusmarshall.cpp \
$(top_srcdir)/qt/qdbusmessage.cpp \
$(top_srcdir)/qt/qdbusserver.cpp \
+ $(top_srcdir)/qt/qdbustype.cpp \
+ $(top_srcdir)/qt/qdbusobject.cpp \
+ $(top_srcdir)/qt/qdbusinterface.cpp \
+ $(top_srcdir)/qt/qdbusstandardinterfaces.cpp \
+ $(top_srcdir)/qt/qdbusxmlparser.cpp \
+ $(top_srcdir)/qt/qdbusutil.cpp \
+ $(top_srcdir)/qt/qdbusintrospection.cpp \
+ $(top_srcdir)/qt/qdbusabstractadaptor.cpp \
+ $(top_srcdir)/qt/qdbusthread.cpp \
+ \
+ $(top_srcdir)/dbus/qdbus.h \
+ $(top_srcdir)/qt/qdbusabstractadaptor.h \
$(top_srcdir)/qt/qdbusconnection.h \
- $(top_srcdir)/qt/qdbuserror.h \
- $(top_srcdir)/qt/qdbusmessage.h \
- $(top_srcdir)/qt/qdbusserver.h \
$(top_srcdir)/qt/qdbusconnection_p.h \
- $(top_srcdir)/dbus/qdbus.h \
+ $(top_srcdir)/qt/qdbuserror.h \
+ $(top_srcdir)/qt/qdbusinterface.h \
+ $(top_srcdir)/qt/qdbusinterface_p.h \
+ $(top_srcdir)/qt/qdbusintrospection.h \
+ $(top_srcdir)/qt/qdbusmacros.h \
$(top_srcdir)/qt/qdbusmarshall.h \
+ $(top_srcdir)/qt/qdbusmessage.h \
$(top_srcdir)/qt/qdbusmessage_p.h \
- $(top_srcdir)/qt/qdbusvariant.h
+ $(top_srcdir)/qt/qdbusobject.h \
+ $(top_srcdir)/qt/qdbusobject_p.h \
+ $(top_srcdir)/qt/qdbusserver.h \
+ $(top_srcdir)/qt/qdbusstandardinterfaces.
+ $(top_srcdir)/qt/qdbustype.h \
+ $(top_srcdir)/qt/qdbusvariant.h \
+ $(top_srcdir)/qt/qdbusxmlparser_p.h
-$(top_srcdir)/qt/qdbusserver.cpp: qdbusserver.moc
-$(top_srcdir)/qt/qdbusconnection.cpp: qdbusconnection.moc
+$(top_srcdir)/qt/qdbusabstractadaptor.lo: qdbusabstractadaptor.moc
+$(top_srcdir)/qt/qdbusserver.lo: qdbusserver.moc
+$(top_srcdir)/qt/qdbusconnection.lo: qdbusconnection_p.moc
-CLEANFILES=qdbusserver.moc qdbusconnection.moc
+CLEANFILES=qdbusabstractadaptor.moc qdbusserver.moc qdbusconnection.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
-# _p.h files are a exception
-qdbusconnection.moc: qdbusconnection_p.h
- $(QT_MOC) -o qdbusconnection.moc $(top_srcdir)/qt/qdbusconnection_p.h
-
%.moc: %.h
$(QT_MOC) $< > $@
endif
diff --git a/qt/README b/qt/README
new file mode 100644
index 00000000..efa2799d
--- /dev/null
+++ b/qt/README
@@ -0,0 +1,10 @@
+These are the Qt4 D-Bus bindings.
+
+They are being maintained by Trolltech AS. As we are currently
+considering placing this code in a future version of Qt, we would like
+to ask any contributors to contact us before submitting code to this
+repository.
+
+
+For more information, please contact
+ Thiago Macieira <thiago.macieira@trolltech.com>
diff --git a/qt/qdbusabstractadaptor.cpp b/qt/qdbusabstractadaptor.cpp
new file mode 100644
index 00000000..335469e7
--- /dev/null
+++ b/qt/qdbusabstractadaptor.cpp
@@ -0,0 +1,64 @@
+/* -*- 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 "qdbusabstractadaptor.h"
+
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qtimer.h>
+
+QDBusAbstractAdaptor::QDBusAbstractAdaptor(QObject* parent)
+ : QObject(parent)
+{
+ QTimer::singleShot(0, this, SLOT(polish()));
+}
+
+QDBusAbstractAdaptor::~QDBusAbstractAdaptor()
+{
+}
+
+void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable)
+{
+ const QMetaObject *us = metaObject();
+ for (int idx = staticMetaObject.methodCount(); idx < us->methodCount(); ++idx) {
+ QMetaMethod mm = us->method(idx);
+
+ if (mm.methodType() != QMetaMethod::Signal)
+ continue;
+
+ // try to connect/disconnect to a signal on the parent that has the same method signature
+ QByteArray sig = mm.signature();
+ sig.prepend(QSIGNAL_CODE + '0');
+ if (enable)
+ connect(parent(), sig, sig);
+ else
+ parent()->disconnect(sig, this, sig);
+ }
+}
+
+void QDBusAbstractAdaptor::polish()
+{
+ // future work:
+ // connect every signal in this adaptor to a slot that will relay them into D-Bus
+}
+
+#include "qdbusabstractadaptor.moc"
diff --git a/qt/qdbusabstractadaptor.h b/qt/qdbusabstractadaptor.h
new file mode 100644
index 00000000..49535f51
--- /dev/null
+++ b/qt/qdbusabstractadaptor.h
@@ -0,0 +1,44 @@
+/* -*- 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.
+ *
+ */
+
+#ifndef QDBUSABSTRACTADAPTOR_H
+#define QDBUSABSTRACTADAPTOR_H
+
+#include <QtCore/qobject.h>
+#include "qdbusmacros.h"
+
+class QDBUS_EXPORT QDBusAbstractAdaptor: public QObject
+{
+ Q_OBJECT
+public:
+ QDBusAbstractAdaptor(QObject* parent);
+ ~QDBusAbstractAdaptor();
+
+protected:
+ void setAutoRelaySignals(bool enable);
+
+private slots:
+ void polish();
+};
+
+#endif
diff --git a/qt/qdbusconnection.cpp b/qt/qdbusconnection.cpp
index f5e1bf37..8ac13e4d 100644
--- a/qt/qdbusconnection.cpp
+++ b/qt/qdbusconnection.cpp
@@ -1,6 +1,8 @@
/* qdbusconnection.cpp
*
* 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,16 +17,21 @@
* 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.
*
*/
-#include <QtCore/qdebug.h>
-#include <QtCore/qcoreapplication.h>
+#include <qdebug.h>
+#include <qcoreapplication.h>
#include "qdbusconnection.h"
+#include "qdbuserror.h"
+#include "qdbusmessage.h"
#include "qdbusconnection_p.h"
+#include "qdbusinterface_p.h"
+#include "qdbusobject_p.h"
+#include "qdbusutil.h"
QT_STATIC_CONST_IMPL char *QDBusConnection::default_connection_name = "qt_dbus_default_connection";
@@ -197,14 +204,7 @@ bool QDBusConnection::send(const QDBusMessage &message) const
{
if (!d || !d->connection)
return false;
-
- DBusMessage *msg = message.toDBusMessage();
- if (!msg)
- return false;
-
- bool isOk = dbus_connection_send(d->connection, msg, 0);
- dbus_message_unref(msg);
- return isOk;
+ return d->send(message);
}
int QDBusConnection::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
@@ -221,57 +221,98 @@ QDBusMessage QDBusConnection::sendWithReply(const QDBusMessage &message) const
if (!d || !d->connection)
return QDBusMessage::fromDBusMessage(0);
- DBusMessage *msg = message.toDBusMessage();
- if (!msg)
- return QDBusMessage::fromDBusMessage(0);
- DBusMessage *reply = dbus_connection_send_with_reply_and_block(d->connection, msg,
- -1, &d->error);
- d->handleError();
- dbus_message_unref(msg);
+ if (!QCoreApplication::instance()) {
+ DBusMessage *msg = message.toDBusMessage();
+ if (!msg)
+ return QDBusMessage::fromDBusMessage(0);
+
+ DBusMessage *reply = dbus_connection_send_with_reply_and_block(d->connection, msg,
+ -1, &d->error);
+ d->handleError();
+ dbus_message_unref(msg);
+
+ if (lastError().isValid())
+ return QDBusMessage::fromError(lastError());
- return QDBusMessage::fromDBusMessage(reply);
+ return QDBusMessage::fromDBusMessage(reply);
+ } 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);
+ }
}
-bool QDBusConnection::connect(const QString &path, const QString &interface,
+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);
+}
+
+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)
return false;
- QDBusConnectionPrivate::SignalHook hook;
+ QString 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);
- if (!hook.setSlot(slot + 1))
- return false;
- d->signalHooks.insertMulti(path, hook);
+ d->signalHooks.insertMulti(source, hook);
d->connect(receiver, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));
return true;
}
+bool QDBusConnection::registerObject(const QString &path, QObject *object, RegisterOptions options)
+{
+ return registerObject(path, QString(), object, options);
+}
+
bool QDBusConnection::registerObject(const QString &path, const QString &interface,
- QObject *object)
+ QObject *object, RegisterOptions options)
{
- if (!d || !d->connection || !object || path.isEmpty() || interface.isEmpty())
+ if (!d || !d->connection || !object || !options || !QDBusUtil::isValidObjectPath(path))
return false;
- QDBusConnectionPrivate::ObjectHook hook;
- hook.interface = interface;
- hook.obj = object;
+ QString iface = interface;
+ if (options & ExportForAnyInterface)
+ iface.clear();
- QDBusConnectionPrivate::ObjectHookHash::iterator it = d->objectHooks.find(path);
- while (it != d->objectHooks.end() && it.key() == path) {
- if (it.value().interface == interface) {
- d->objectHooks.erase(it);
- break;
- }
- ++it;
- }
+ 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())
+ return false;
- d->objectHooks.insert(path, hook);
+ QDBusConnectionPrivate::ObjectData& data = hook[iface];
+
+ data.flags = options;
+ data.obj = object;
d->connect(object, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));
qDebug("REGISTERED FOR %s", path.toLocal8Bit().constData());
@@ -284,10 +325,45 @@ void QDBusConnection::unregisterObject(const QString &path)
if (!d || !d->connection)
return;
- // TODO - check interfaces
d->objectHooks.remove(path);
}
+QDBusInterface QDBusConnection::findInterface(const QString& service, const QString& path,
+ const QString& interface)
+{
+ // create one
+ QDBusInterfacePrivate *priv = new QDBusInterfacePrivate;
+ priv->conn = *this;
+
+ if (!QDBusUtil::isValidObjectPath(path) || !QDBusUtil::isValidInterfaceName(interface))
+ return QDBusInterface(priv);
+
+ // check if it's there first
+ QString owner = getNameOwner(service);
+ if (owner.isEmpty())
+ return QDBusInterface(priv);
+
+ // getNameOwner returns empty if d is 0
+ Q_ASSERT(d);
+ priv->service = owner;
+ priv->path = path;
+ priv->data = d->findInterface(interface).constData();
+
+ return QDBusInterface(priv); // will increment priv's refcount
+}
+
+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);
+}
+
bool QDBusConnection::isConnected( ) const
{
return d && d->connection && dbus_connection_get_is_connected(d->connection);
@@ -307,21 +383,38 @@ QString QDBusConnection::baseService() const
bool QDBusConnection::requestName(const QString &name, NameRequestMode mode)
{
- //FIXME: DBUS_NAME_FLAGS_* are bit fields not enumeration
- static const int DBusModes[] = { 0, DBUS_NAME_FLAG_ALLOW_REPLACEMENT,
- DBUS_NAME_FLAG_REPLACE_EXISTING };
- Q_ASSERT(mode == 0 || mode == AllowReplace ||
- mode == ReplaceExisting );
-
- DBusError error;
- dbus_error_init (&error);
- dbus_bus_request_name(d->connection, name.toUtf8(), DBusModes[mode], &error);
- if (dbus_error_is_set (&error)) {
- qDebug("Error %s\n", error.message);
- dbus_error_free (&error);
+ static const int DBusModes[] = { DBUS_NAME_FLAG_ALLOW_REPLACEMENT, 0,
+ DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_ALLOW_REPLACEMENT};
+
+ int retval = dbus_bus_request_name(d->connection, name.toUtf8(), DBusModes[mode], &d->error);
+ d->handleError();
+ return retval == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER ||
+ retval == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER;
+}
+
+bool QDBusConnection::releaseName(const QString &name)
+{
+ int retval = dbus_bus_release_name(d->connection, name.toUtf8(), &d->error);
+ d->handleError();
+ if (lastError().isValid())
return false;
- }
- return true;
+ return retval == DBUS_RELEASE_NAME_REPLY_RELEASED;
+}
+
+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");
+ msg << name;
+ QDBusMessage reply = sendWithReply(msg);
+ if (!lastError().isValid() && reply.type() == QDBusMessage::ReplyMessage)
+ return reply.first().toString();
+ return QString();
}
-#include "qdbusconnection.moc"
+#include "qdbusconnection_p.moc"
diff --git a/qt/qdbusconnection.h b/qt/qdbusconnection.h
index bbab0ec6..c6913b29 100644
--- a/qt/qdbusconnection.h
+++ b/qt/qdbusconnection.h
@@ -1,6 +1,8 @@
/* qdbusconnection.h QDBusConnection 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,18 +17,21 @@
* 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.
*
*/
#ifndef QDBUSCONNECTION_H
#define QDBUSCONNECTION_H
-#include "dbus/qdbus.h"
+#include "qdbusmacros.h"
#include <QtCore/qstring.h>
class QDBusConnectionPrivate;
+class QDBusXmlParser;
+class QDBusObject;
+class QDBusInterface;
class QDBusError;
class QDBusMessage;
class QByteArray;
@@ -46,8 +51,11 @@ public:
bool isConnected() const;
QDBusError lastError() const;
- enum NameRequestMode { NoReplace = 0, AllowReplace = 1, ReplaceExisting = 2 };
+ 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;
@@ -56,13 +64,42 @@ public:
int sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
const char *slot) const;
- bool connect(const QString &path, const QString &interface,
+ bool connect(const QString &service, const QString &path, const QString &interface,
const QString &name, QObject *receiver, const char *slot);
-
- bool registerObject(const QString &path, const QString &interface,
- QObject *object);
+ bool connect(const QString &service, const QString &path, const QString &interface,
+ 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);
+ QDBusObject findObject(const QString& service, const QString& path);
+ QDBusInterface findInterface(const QString& service, const QString& path, const QString& interface);
+
+
static QDBusConnection addConnection(BusType type,
const QString &name = QLatin1String(default_connection_name));
static QDBusConnection addConnection(const QString &address,
@@ -72,7 +109,9 @@ public:
QT_STATIC_CONST char *default_connection_name;
private:
+ friend class QDBusObject;
QDBusConnectionPrivate *d;
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QDBusConnection::RegisterOptions)
#endif
diff --git a/qt/qdbusconnection_p.h b/qt/qdbusconnection_p.h
index 3f78dad2..fa0fdd82 100644
--- a/qt/qdbusconnection_p.h
+++ b/qt/qdbusconnection_p.h
@@ -1,6 +1,8 @@
/* qdbusconnection_p.h QDBusConnection private 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,8 +17,8 @@
* 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.
*
*/
@@ -38,16 +40,24 @@
#include "qdbuserror.h"
#include <QtCore/qatomic.h>
+#include <QtCore/qmutex.h>
#include <QtCore/qhash.h>
#include <QtCore/qobject.h>
#include <QtCore/qpointer.h>
#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qeventloop.h>
+#include <QtCore/qmutex.h>
#include <dbus/dbus.h>
+#include "qdbusmessage.h"
+#include "qdbusintrospection.h"
+
class QDBusMessage;
class QSocketNotifier;
class QTimerEvent;
+class QDBusObjectPrivate;
+class CallDeliveryEvent;
typedef struct DBusConnection;
typedef struct DBusServer;
@@ -56,6 +66,43 @@ class QDBusConnectionPrivate: public QObject
{
Q_OBJECT
public:
+ // structs and enums
+ enum ConnectionMode { InvalidMode, ServerMode, ClientMode };
+
+ struct Watcher
+ {
+ Watcher(): watch(0), read(0), write(0) {}
+ DBusWatch *watch;
+ QSocketNotifier *read;
+ QSocketNotifier *write;
+ };
+
+ struct SignalHook
+ {
+ QString interface, name, signature;
+ QPointer<QObject> obj;
+ int midx;
+ QList<int> params;
+ };
+
+ struct ObjectData
+ {
+ QPointer<QObject> obj;
+ int flags;
+ };
+
+public:
+ // typedefs
+ 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);
~QDBusConnectionPrivate();
@@ -66,66 +113,81 @@ public:
void closeConnection();
void timerEvent(QTimerEvent *e);
- bool handleSignal(DBusMessage *msg) const;
- bool handleObjectCall(DBusMessage *message) const;
+ 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;
+
+ bool handleSignal(const QDBusMessage &msg);
+ bool handleObjectCall(const QDBusMessage &message);
bool handleError();
+ void disposeOfLocked(QDBusIntrospection::Object* obj);
+ void disposeOf(QDBusObjectPrivate* obj);
+ 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);
+ void deliverCall(const CallDeliveryEvent &data) const;
+
+protected:
+ virtual void customEvent(QEvent *event);
+
public slots:
+ // public slots
void socketRead(int);
void socketWrite(int);
void objectDestroyed(QObject *o);
public:
+ // public member variables
DBusError error;
QDBusError lastError;
- enum ConnectionMode { InvalidMode, ServerMode, ClientMode };
-
QAtomic ref;
+ QMutex mutex;
ConnectionMode mode;
DBusConnection *connection;
DBusServer *server;
- static int messageMetaType;
- static int registerMessageMetaType();
- bool handleSignal(const QString &path, const QDBusMessage &msg) const;
- int sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
- const char *method) const;
-
- struct Watcher
- {
- Watcher(): watch(0), read(0), write(0) {}
- DBusWatch *watch;
- QSocketNotifier *read;
- QSocketNotifier *write;
- };
- typedef QMultiHash<int, Watcher> WatcherHash;
WatcherHash watchers;
-
- typedef QHash<int, DBusTimeout *> TimeoutHash;
TimeoutHash timeouts;
-
- struct SignalHook
- {
- QString interface, name;
- QPointer<QObject> obj;
- int midx;
- QVarLengthArray<int, 10> params;
-
- bool setSlot(const char *slotName);
- };
-
- typedef QMultiHash<QString, SignalHook> SignalHookHash;
SignalHookHash signalHooks;
-
- struct ObjectHook
- {
- QString interface;
- QPointer<QObject> obj;
- };
- typedef QMultiHash<QString, ObjectHook> ObjectHookHash;
ObjectHookHash objectHooks;
QList<DBusTimeout *> pendingTimeouts;
+
+public:
+ // public mutable member variables
+ mutable KnownInterfacesHash knownInterfaces;
+ mutable KnownObjectsHash knownObjects;
+
+public:
+ // static methods
+ static int messageMetaType;
+ static int registerMessageMetaType();
+ static int findSlot(QObject *obj, const char *slotName, QList<int>& params);
};
+class QDBusReplyWaiter: public QEventLoop
+{
+ Q_OBJECT
+public:
+ QDBusMessage replyMsg;
+
+#ifndef QT_NO_DEBUG
+ int level;
+ int exec(ProcessEventsFlags flags);
+ void exit(int = 0);
+#endif
+
+public slots:
+ void reply(const QDBusMessage &msg);
+};
+
#endif
diff --git a/qt/qdbuserror.cpp b/qt/qdbuserror.cpp
index 0f7dc92c..64b68b37 100644
--- a/qt/qdbuserror.cpp
+++ b/qt/qdbuserror.cpp
@@ -1,6 +1,8 @@
/* qdbuserror.h QDBusError 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,16 +17,17 @@
* 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.
*
*/
#include "qdbuserror.h"
-#include <QtCore/qdebug.h>
+#include <qdebug.h>
#include <dbus/dbus.h>
+#include "qdbusmessage.h"
QDBusError::QDBusError(const DBusError *error)
{
@@ -35,6 +38,16 @@ QDBusError::QDBusError(const DBusError *error)
msg = QString::fromUtf8(error->message);
}
+QDBusError::QDBusError(const QDBusMessage &qdmsg)
+{
+ if (qdmsg.type() != QDBusMessage::ErrorMessage)
+ return;
+
+ nm = qdmsg.name();
+ if (qdmsg.count())
+ msg = qdmsg[0].toString();
+}
+
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const QDBusError &msg)
{
diff --git a/qt/qdbuserror.h b/qt/qdbuserror.h
index 07d2c566..94500887 100644
--- a/qt/qdbuserror.h
+++ b/qt/qdbuserror.h
@@ -1,6 +1,8 @@
/* qdbuserror.h QDBusError 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,23 +17,25 @@
* 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.
*
*/
#ifndef QDBUSERROR_H
#define QDBUSERROR_H
-#include "dbus/qdbus.h"
+#include "qdbusmacros.h"
#include <QtCore/qstring.h>
struct DBusError;
+class QDBusMessage;
class QDBUS_EXPORT QDBusError
{
public:
QDBusError(const DBusError *error = 0);
+ QDBusError(const QDBusMessage& msg);
inline QString name() const { return nm; }
inline QString message() const { return msg; }
diff --git a/qt/qdbusintegrator.cpp b/qt/qdbusintegrator.cpp
index 4a19d33e..79baa101 100644
--- a/qt/qdbusintegrator.cpp
+++ b/qt/qdbusintegrator.cpp
@@ -1,6 +1,8 @@
/* qdbusintegrator.cpp QDBusConnection private implementation
*
* 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,54 @@
* 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.
*
*/
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qcoreevent.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qmetaobject.h>
-#include <QtCore/qsocketnotifier.h>
+#include <qcoreapplication.h>
+#include <qcoreevent.h>
+#include <qdebug.h>
+#include <qmetaobject.h>
+#include <qsocketnotifier.h>
+#include <qcoreevent.h>
+#include <qtimer.h>
+#include "qdbusvariant.h"
#include "qdbusconnection_p.h"
+#include "qdbusinterface_p.h"
+#include "qdbusobject_p.h"
#include "qdbusmessage.h"
+#include "qdbusabstractadaptor.h"
int QDBusConnectionPrivate::messageMetaType = 0;
+struct QDBusPendingCall
+{
+ QPointer<QObject> receiver;
+ QList<int> metaTypes;
+ int methodIdx;
+ DBusPendingCall *pending;
+ const QDBusConnectionPrivate *connection;
+};
+
+class CallDeliveryEvent: public QEvent
+{
+public:
+ CallDeliveryEvent()
+ : QEvent(QEvent::User), object(0), flags(0), slotIdx(-1)
+ { }
+
+ const QDBusConnectionPrivate *conn;
+ QPointer<QObject> object;
+ QDBusMessage message;
+ QList<int> metaTypes;
+
+ int flags;
+ int slotIdx;
+ bool generateReply : 1;
+};
+
static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
{
Q_ASSERT(timeout);
@@ -103,6 +137,7 @@ static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
watcher.watch = watch;
if (QCoreApplication::instance()) {
watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d);
+ watcher.read->setEnabled(dbus_watch_get_enabled(watch));
d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int)));
}
}
@@ -111,6 +146,7 @@ static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
watcher.watch = watch;
if (QCoreApplication::instance()) {
watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d);
+ watcher.write->setEnabled(dbus_watch_get_enabled(watch));
d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int)));
}
}
@@ -188,168 +224,560 @@ static DBusHandlerResult qDBusSignalFilter(DBusConnection *connection,
bool handled = false;
QDBusMessage amsg = QDBusMessage::fromDBusMessage(message);
- qDebug() << "got message: " << dbus_message_get_type(message) << amsg;
+ qDebug() << "got message:" << amsg;
if (msgType == DBUS_MESSAGE_TYPE_SIGNAL) {
- handled = d->handleSignal(message);
+ handled = d->handleSignal(amsg);
} else if (msgType == DBUS_MESSAGE_TYPE_METHOD_CALL) {
- handled = d->handleObjectCall(message);
+ handled = d->handleObjectCall(amsg);
}
return handled ? DBUS_HANDLER_RESULT_HANDLED :
DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
-static bool qInvokeDBusSlot(const QDBusConnectionPrivate::SignalHook& hook, const QDBusMessage &msg)
+static bool checkAsyncTag(const char *tag)
{
- int count = msg.count();
- if (!(count == hook.params.count()
- || (count + 1 == hook.params.count()
- && hook.params[count] == QDBusConnectionPrivate::messageMetaType)))
+ if (!tag || !*tag)
return false;
- QVarLengthArray<void *, 16> params;
- params.append(0); // return value
- for (int i = 0; i < msg.count(); ++i) {
- const QVariant &v = msg.at(i);
- if (int(v.type()) != hook.params[i]) {
- return false;
- }
- params.append(const_cast<void *>(v.constData()));
+ const char *p = strstr(tag, "async");
+ if (p != NULL &&
+ (p == tag || *(p-1) == ' ') &&
+ (p[6] == '\0' || p[6] == ' '))
+ return true;
+
+ p = strstr(tag, "Q_ASYNC");
+ if (p != NULL &&
+ (p == tag || *(p-1) == ' ') &&
+ (p[8] == '\0' || p[8] == ' '))
+ return true;
+
+ return false;
+}
+
+static QList<QByteArray> splitParameters(const char *p)
+{
+ QList<QByteArray> retval;
+ ++p;
+ const char *e = p;
+ while (*e != ')') {
+ while (*e != ')' && *e != ',')
+ ++e;
+
+ // found the end of this parameter
+ retval += QByteArray(p, e - p);
+
+ if (*e != ')')
+ p = ++e;
}
- if (count + 1 == hook.params.count())
- params.append(const_cast<QDBusMessage *>(&msg));
- return hook.obj->qt_metacall(QMetaObject::InvokeMetaMethod, hook.midx, params.data()) < 0;
+ return retval;
}
-static bool qInvokeDBusSlot(QObject *object, int idx, const QDBusMessage &msg)
+static bool typesMatch(int metaId, QVariant::Type variantType)
{
- Q_ASSERT(object);
+ if (metaId == (int)variantType)
+ return true;
- const QMetaMethod method = object->metaObject()->method(idx);
- if (!method.signature())
- return false;
+ if (variantType == QVariant::Int && metaId == QMetaType::Short)
+ return true;
- QVarLengthArray<void *> params;
- params.append(0); // ### return type
+ if (variantType == QVariant::UInt && (metaId == QMetaType::UShort ||
+ metaId == QMetaType::UChar))
+ return true;
- QList<QByteArray> parameterTypes = method.parameterTypes();
+ return false; // no match
+}
- // check parameters, the slot should have <= parameters than the message
- // also allow the QDBusMessage itself as last parameter slot
- if ((parameterTypes.count() > msg.count())
- || (parameterTypes.count() + 1 != msg.count())
- && parameterTypes.last() != "QDBusMessage") {
- qWarning("Cannot deliver asynchronous reply to object named '%s' because of parameter "
- "mismatch. Please check your sendWithReplyAsync() statements.",
- object->objectName().toLocal8Bit().constData());
- return false;
+static int returnTypeId(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) );
+ if (id == QVariant::UserType)
+ id = QMetaType::type(type);
+
+ switch (id) {
+ case QVariant::Bool:
+ case QVariant::Int:
+ case QVariant::UInt:
+ case QVariant::Char:
+ case QMetaType::Short:
+ case QMetaType::UShort:
+ case QMetaType::UChar:
+ case QVariant::LongLong:
+ case QVariant::ULongLong:
+ case QVariant::Double:
+ case QVariant::String:
+ case QVariant::Date:
+ case QVariant::Time:
+ case QVariant::DateTime:
+ case QVariant::Map:
+ case QVariant::StringList:
+ case QVariant::ByteArray:
+ case QVariant::List:
+ return id;
+
+ 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:
+// - zero or more value or const-ref parameters of any kind
+// - zero or one const ref of QDBusMessage
+// - zero or more non-const ref parameters
+// No parameter may be a template.
+// this function returns -1 if the parameters don't match the above form
+// this function returns the number of *input* parameters, including the QDBusMessage one if any
+// this function does not check the return type, so metaTypes[0] is always 0 and always present
+// 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)
+{
+ if (sig.indexOf('<') != -1) {
+ qWarning("Could not parse the method '%s'", sig.constData());
+ // there's no type with templates that we can handle
+ return -1;
}
- int i;
- for (i = 0; i < parameterTypes.count(); ++i) {
- const QByteArray param = parameterTypes.at(i);
- if (param == msg.at(i).typeName()) {
- params.append(const_cast<void *>(msg.at(i).constData()));
- } else if (i == parameterTypes.count() - 1 && param == "QDBusMessage") {
- params.append(const_cast<void *>(static_cast<const void *>(&msg)));
- } else {
- qWarning("Parameter mismatch while delivering message, expected '%s', got '%s'",
- msg.at(i).typeName(), param.constData());
- return false;
+ int paren = sig.indexOf('(');
+ QList<QByteArray> parameterTypes = splitParameters(sig.data() + paren);
+ metaTypes.clear();
+
+ metaTypes.append(0); // return type
+ int inputCount = 0;
+ bool seenMessage = false;
+ foreach (QByteArray type, parameterTypes) {
+ if (type.endsWith('*')) {
+ qWarning("Could not parse the method '%s'", sig.constData());
+ // pointer?
+ return -1;
+ }
+
+ if (type.endsWith('&')) {
+ type.truncate(type.length() - 1);
+ int id = typeId(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;
+ }
+
+ continue;
+ }
+
+ if (seenMessage) { // && !type.endsWith('&')
+ qWarning("Could not parse the method '%s'", sig.constData());
+ // non-output parameters after message or after output params
+ return -1; // not allowed
+ }
+
+ int id = typeId(type);
+ if (id == 0) {
+ qWarning("Could not parse the method '%s'", sig.constData());
+ // invalid type in method parameter list
+ return -1;
}
+ metaTypes.append(id);
+ ++inputCount;
+
+ if (id == QDBusConnectionPrivate::messageMetaType)
+ seenMessage = true;
}
- return object->qt_metacall(QMetaObject::InvokeMetaMethod, idx, params.data()) < 0;
+
+ return inputCount;
}
-static bool qInvokeDBusSlot(QObject *object, QDBusMessage *msg)
+static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags,
+ const QDBusTypeList &types, QList<int>& metaTypes, bool &isAsync, int &msgPos)
+{
+ // find the first slot
+ const QMetaObject *super = mo;
+ while (qstrcmp(super->className(), "QObject") != 0 &&
+ qstrcmp(super->className(), "QDBusAbstractAdaptor") != 0)
+ super = super->superClass();
+
+ int attributeMask = (flags & QDBusConnection::ExportNonScriptableSlots) ?
+ 0 : QMetaMethod::Scriptable;
+
+ for (int idx = super->methodCount() ; idx <= mo->methodCount(); ++idx) {
+ QMetaMethod mm = mo->method(idx);
+
+ // check access:
+ if (mm.access() != QMetaMethod::Public)
+ continue;
+
+ // check type:
+ // unnecessary, since slots are never public:
+ //if (mm.methodType() != QMetaMethod::Slot)
+ // continue;
+
+ // check name:
+ QByteArray sig = QMetaObject::normalizedSignature(mm.signature());
+ int paren = sig.indexOf('(');
+ if (paren != name.length() || !sig.startsWith( name ))
+ continue;
+
+ int returnType = returnTypeId(mm.typeName());
+ isAsync = checkAsyncTag(mm.tag());
+
+ // consistency check:
+ if (isAsync && returnType != QMetaType::Void)
+ continue;
+
+ int inputCount = parametersForMethod(sig, metaTypes);
+ if (inputCount == -1)
+ continue; // problem parsing
+
+ metaTypes[0] = returnType;
+ msgPos = 0;
+ if (inputCount > 0 &&
+ metaTypes.at(inputCount) == QDBusConnectionPrivate::messageMetaType) {
+ // no input parameters is allowed as long as the message meta type is there
+ msgPos = inputCount;
+ --inputCount;
+ }
+
+ if (inputCount) {
+ // 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;
+ }
+
+ if (!matches)
+ continue; // we didn't match them all
+
+ // consistency check:
+ if (isAsync && metaTypes.count() > i + 1)
+ continue;
+ }
+
+ if (!msgPos && (mm.attributes() & attributeMask) != attributeMask)
+ continue; // not exported
+
+ // if we got here, this slot matched
+ return idx;
+ }
+
+ // 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)
+{
+ // 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);
- Q_ASSERT(msg);
- const QMetaObject *mo = object->metaObject();
- QVarLengthArray<void *> params;
- params.append(0); // ### return type
+ int n = metaTypes.count() - 1;
+ if (metaTypes[n] == QDBusConnectionPrivate::messageMetaType)
+ --n;
+
+ // 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
+
+ // 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;
+ data->generateReply = false;
+
+ QCoreApplication::postEvent( this, data );
+
+ return true;
+}
+
+bool QDBusConnectionPrivate::activateCall(QObject* object, int flags,
+ const QDBusMessage &msg)
+{
+ // This is called by QDBusConnectionPrivate::handleObjectCall to place a call
+ // to a slot on the object.
+ //
+ // The call is delivered to the first slot that matches the following conditions:
+ // - has the same name as the message's target name
+ // - ALL of the message's types are found in slot's parameter list
+ // - optionally has one more parameter of type QDBusMessage
+ // If none match, then the slot of the same name as the message target and with
+ // the first type of QDBusMessage is delivered.
+ //
+ // Because the marshalling of D-Bus data into QVariant loses the information on
+ // the original types, the message signature is used to determine the original type.
+ // Aside from that, the "int" and "unsigned" types will be tried as well.
+ //
+ // Return message handling depends on whether the asynchronous tag ("async" or "Q_ASYNC")
+ // tag is found, whether the slot takes a QDBusMessage parameter and whether there are
+ // return values (non-const reference parameters or a return type).
+ // The table indicates the possibilities:
+ // async QDBusMessage parameter return values return message generated
+ // yes irrelevant irrelevant no
+ // no irrelevant yes yes
+ // no yes no no
+ // no no no yes
+ //
+ // When a return message is generated, the slot's return type, if any, will be placed
+ // 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);
- /* Try to find a slot with all args and the QDBusMessage */
- QByteArray slotName = msg->name().toUtf8(); // QVarLengthArray?
- slotName.append("(");
- for (int i = 0; i < msg->count(); ++i) {
- slotName.append(msg->at(i).typeName()).append(",");
- params.append(const_cast<void *>(msg->at(i).constData()));
+ QList<int> metaTypes;
+ int idx;
+ bool isAsync;
+ 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, isAsync, msgPos);
+ if (idx == -1)
+ // no match
+ return false;
}
- slotName.append("QDBusMessage)");
- int idx = mo->indexOfSlot(slotName.constData());
- if (idx >= 0) {
- params.append(msg);
- return object->qt_metacall(QMetaObject::InvokeMetaMethod, idx, params.data()) < 0;
+ bool generateReply;
+ if (isAsync)
+ generateReply = false;
+ else if (metaTypes[0] != QMetaType::Void)
+ generateReply = true;
+ else {
+ if (msgPos != 0)
+ // generate a reply if there are more parameters past QDBusMessage
+ generateReply = metaTypes.count() > msgPos + 1;
+ else
+ // generate a reply if there are more parameters than input parameters
+ generateReply = metaTypes.count() > msg.count() + 1;
}
- /* Try to find only args, without the QDBusMessage */
- slotName.chop(13);
- slotName[slotName.count() - 1] = ')';
+ // found the slot to be called
+ // prepare for the call:
+ CallDeliveryEvent *call = new CallDeliveryEvent;
+ call->conn = this;
- idx = mo->indexOfSlot(slotName.constData());
- if (idx >= 0 && (mo->method(idx).attributes() & QMetaMethod::Scriptable))
- return object->qt_metacall(QMetaObject::InvokeMetaMethod, idx, params.data()) < 0;
+ // parameters:
+ call->object = object;
+ call->flags = flags;
+ call->message = msg;
- /* Try to find a slot with only QDBusMessage */
- slotName = msg->name().toUtf8();
- slotName.append("(QDBusMessage)");
+ // save our state:
+ call->metaTypes = metaTypes;
+ call->slotIdx = idx;
+ call->generateReply = generateReply;
- idx = mo->indexOfSlot(slotName.constData());
- if (idx >= 0)
- return QMetaObject::invokeMethod(object, msg->name().toUtf8().constData(),
- Q_ARG(QDBusMessage, *msg));
+ QCoreApplication::postEvent( this, call );
- return false;
+ // ready
+ return true;
}
-int QDBusConnectionPrivate::registerMessageMetaType()
+void QDBusConnectionPrivate::deliverCall(const CallDeliveryEvent& data) const
{
- int tp = messageMetaType = qRegisterMetaType<QDBusMessage>("QDBusMessage");
- return tp;
-}
+ // resume state:
+ const QList<int>& metaTypes = data.metaTypes;
+ const QDBusMessage& msg = data.message;
+
+ QVarLengthArray<void *, 10> params;
+ 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
-bool QDBusConnectionPrivate::SignalHook::setSlot(const char *slotName)
-{
- Q_ASSERT(static_cast<QObject *>(obj)); Q_ASSERT(slotName);
+ // first one is the return type -- add it below
+ params.append(0);
- QByteArray normalizedName = QMetaObject::normalizedSignature(slotName);
- const QMetaObject *mo = obj->metaObject();
- midx = mo->indexOfMethod(normalizedName.constData());
- if (midx < 0)
- return false;
+ // add the input parameters
+ int i;
+ for (i = 0; i < msg.count(); ++i) {
+ int id = metaTypes[i + 1];
+ if (id == QDBusConnectionPrivate::messageMetaType)
+ break;
- const QList<QByteArray> ptypes = mo->method(midx).parameterTypes();
- for (int i = 0; i < ptypes.count(); ++i) {
- int t = QVariant::nameToType(ptypes.at(i).constData());
- if (t == QVariant::UserType)
- t = QMetaType::type(ptypes.at(i).constData());
- if (t == QVariant::Invalid)
- return false;
- params.append(t);
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ params.append(const_cast<void *>( msg.at(i).constData() ));
+#else
+ if (id == msg.at(i).type())
+ params.append(const_cast<void *>( msg.at(i).constData() ));
+ else {
+ // need some help
+ integer aux;
+ const QVariant &var = msg.at(i);
+ if (id == QMetaType::Short)
+ aux.s = var.toInt();
+ else if (id == QMetaType::UShort)
+ aux.us = var.toUInt();
+ else
+ aux.uc = var.toUInt();
+ auxParameters.append(aux);
+ params.append( &auxParameters[auxParameters.count()] );
+ }
+#endif
}
- return true;
+ bool takesMessage = false;
+ if (metaTypes.count() > i + 1 && metaTypes[i + 1] == QDBusConnectionPrivate::messageMetaType) {
+ params.append(const_cast<void*>(static_cast<const void*>(&msg)));
+ takesMessage = true;
+ ++i;
+ }
+
+ // output arguments
+ QVariantList outputArgs;
+ void *null = 0;
+ if (metaTypes[0] != QMetaType::Void) {
+ QVariant arg(metaTypes[0], null);
+ params.append( arg.data() );
+ outputArgs.append( arg );
+ }
+ for ( ; i < metaTypes.count(); ++i) {
+ QVariant arg(metaTypes[i], null);
+ params.append( arg.data() );
+ outputArgs.append( arg );
+ }
+
+ // make call:
+ bool fail;
+ if (data.object.isNull())
+ fail = true;
+ else
+ fail = data.object->qt_metacall(QMetaObject::InvokeMetaMethod,
+ data.slotIdx, params.data()) >= 0;
+
+ // do we create a reply?
+ if (data.generateReply) {
+ if (!fail) {
+ // yes
+ 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");
+ qDebug("Internal error: Failed to deliver message");
+ send(reply);
+ }
+ }
+
+ return;
+}
+
+void QDBusConnectionPrivate::customEvent(QEvent *event)
+{
+ // nothing else should be sending custom events at us
+ CallDeliveryEvent* call = static_cast<CallDeliveryEvent *>(event);
+
+ // self check:
+ Q_ASSERT(call->conn == this);
+
+ deliverCall(*call);
}
QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *parent)
: QObject(parent), ref(1), mode(InvalidMode), connection(0), server(0)
{
+ extern bool qDBusInitThreads();
static const int msgType = registerMessageMetaType();
+ static const bool threads = qDBusInitThreads();
+
Q_UNUSED(msgType);
+ Q_UNUSED(threads);
dbus_error_init(&error);
}
QDBusConnectionPrivate::~QDBusConnectionPrivate()
{
+ Q_ASSERT(knownObjects.isEmpty());
+
if (dbus_error_is_set(&error))
dbus_error_free(&error);
closeConnection();
+
+ 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);
+ }
}
void QDBusConnectionPrivate::closeConnection()
@@ -413,7 +841,6 @@ void QDBusConnectionPrivate::socketRead(int fd)
}
if (mode == ClientMode)
while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS);
- // ### break out of loop?
}
void QDBusConnectionPrivate::socketWrite(int fd)
@@ -432,11 +859,20 @@ void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
{
ObjectHookHash::iterator it = objectHooks.begin();
while (it != objectHooks.end()) {
- if (static_cast<QObject *>(it.value().obj) == obj)
+ 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;
}
+
SignalHookHash::iterator sit = signalHooks.begin();
while (sit != signalHooks.end()) {
if (static_cast<QObject *>(sit.value().obj) == obj)
@@ -447,56 +883,135 @@ void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
obj->disconnect(this);
}
-bool QDBusConnectionPrivate::handleObjectCall(DBusMessage *message) const
+bool QDBusConnectionPrivate::activateAdaptor(QObject* object, int flags,
+ const QDBusMessage &msg)
{
- QDBusMessage msg = QDBusMessage::fromDBusMessage(message);
+ // 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
- ObjectHook hook;
- ObjectHookHash::ConstIterator it = objectHooks.find(msg.path());
- while (it != objectHooks.constEnd() && it.key() == msg.path()) {
- if (it.value().interface == msg.interface()) {
- hook = it.value();
- break;
- } else if (it.value().interface.isEmpty()) {
- hook = it.value();
+ 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);
+ }
}
- ++it;
}
+ return false;
+}
- if (!hook.obj) {
- qDebug("NO OBJECT for %s", msg.path().toLocal8Bit().constData());
- return false;
+int QDBusConnectionPrivate::registerMessageMetaType()
+{
+ int tp = messageMetaType = qRegisterMetaType<QDBusMessage>("QDBusMessage");
+ return tp;
+}
+
+int QDBusConnectionPrivate::findSlot(QObject* obj, const char *slotName, QList<int>& params)
+{
+ Q_ASSERT(slotName);
+ QByteArray normalizedName = QMetaObject::normalizedSignature(slotName);
+ int midx = obj->metaObject()->indexOfMethod(normalizedName);
+ if (midx == -1) {
+ qWarning("No such slot '%s' while connecting D-Bus", slotName);
+ return -1;
}
- if (!qInvokeDBusSlot(hook.obj, &msg)) {
- qDebug("NO SUCH SLOT: %s(QDBusMessage)", msg.name().toLocal8Bit().constData());
+ int inputCount = parametersForMethod(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)
+{
+ if (!hook.obj)
+ return false; // object is gone
+
+ if (hook.flags & QDBusConnection::ExportAdaptors)
+ return activateAdaptor(hook.obj, hook.flags, msg);
+ else
+ return activateCall(hook.obj, hook.flags, msg);
+}
+
+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
+ }
+ } 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);
+ }
}
- return true;
+ qDebug(ok ? "Call scheduled" : "Call failed");
+ return ok;
}
-bool QDBusConnectionPrivate::handleSignal(const QString &path, const QDBusMessage &msg) const
+bool QDBusConnectionPrivate::handleSignal(const QString &path, const QDBusMessage &msg)
{
SignalHookHash::const_iterator it = signalHooks.find(path);
qDebug("looking for: %s", path.toLocal8Bit().constData());
qDebug() << signalHooks.keys();
- while (it != signalHooks.constEnd() && it.key() == path) {
+ for ( ; it != signalHooks.constEnd() && it.key() == path; ++ it) {
const SignalHook &hook = it.value();
- if ((hook.name.isEmpty() || hook.name == msg.name())
- && (hook.interface.isEmpty() || hook.interface == msg.interface()))
- qInvokeDBusSlot(hook, msg);
- ++it;
+ 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;
+
+ activateSignal(hook, msg);
}
return true;
}
-bool QDBusConnectionPrivate::handleSignal(DBusMessage *message) const
+bool QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg)
{
- QDBusMessage msg = QDBusMessage::fromDBusMessage(message);
-
// yes, it is a single "|" below...
- return handleSignal(QString(), msg) | handleSignal(msg.path(), msg);
+ return handleSignal(QString(), msg) | handleSignal(msg.sender() + msg.path(), msg);
}
static dbus_int32_t server_slot = -1;
@@ -569,53 +1084,152 @@ void QDBusConnectionPrivate::setConnection(DBusConnection *dbc)
qDebug("base service: %s", service);
}
-struct QDBusPendingCall
-{
- QPointer<QObject> receiver;
- int methodIdx;
- DBusPendingCall *pending;
-};
-
static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
{
QDBusPendingCall *call = reinterpret_cast<QDBusPendingCall *>(user_data);
+ QDBusConnectionPrivate *connection = const_cast<QDBusConnectionPrivate *>(call->connection);
Q_ASSERT(call->pending == pending);
if (!call->receiver.isNull() && call->methodIdx != -1) {
DBusMessage *reply = dbus_pending_call_steal_reply(pending);
- qInvokeDBusSlot(call->receiver, call->methodIdx, QDBusMessage::fromDBusMessage(reply));
+ connection->activateReply(call->receiver, call->methodIdx, call->metaTypes,
+ QDBusMessage::fromDBusMessage(reply));
}
dbus_pending_call_unref(pending);
delete call;
}
+bool QDBusConnectionPrivate::send(const QDBusMessage& message) const
+{
+ DBusMessage *msg = message.toDBusMessage();
+ if (!msg)
+ return false;
+
+ dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
+
+ qDebug() << "sending message:" << message;
+ bool isOk = dbus_connection_send(connection, msg, 0);
+ dbus_message_unref(msg);
+ return isOk;
+}
+
int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
- const char *method) const
+ const char *method) const
{
DBusMessage *msg = message.toDBusMessage();
if (!msg)
return 0;
int slotIdx = -1;
- if (receiver && method && *method) {
- QByteArray normalized = QMetaObject::normalizedSignature(method + 1);
- slotIdx = receiver->metaObject()->indexOfMethod(normalized.constData());
- if (slotIdx == -1)
- qWarning("QDBusConnection::sendWithReplyAsync: no such method: '%s'",
- normalized.constData());
- }
+ QList<int> metaTypes;
+ if (receiver && method && *method)
+ slotIdx = findSlot(receiver, method + 1, metaTypes);
+ qDebug() << "sending message:" << message;
DBusPendingCall *pending = 0;
if (dbus_connection_send_with_reply(connection, msg, &pending, message.timeout())) {
if (slotIdx != -1) {
QDBusPendingCall *pcall = new QDBusPendingCall;
pcall->receiver = receiver;
+ pcall->metaTypes = metaTypes;
pcall->methodIdx = slotIdx;
+ pcall->connection = this;
pcall->pending = dbus_pending_call_ref(pending);
dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, 0);
}
+ dbus_pending_call_unref(pending);
return dbus_message_get_serial(msg);
}
return 0;
}
+
+QSharedDataPointer<QDBusIntrospection::Interface>
+QDBusConnectionPrivate::findInterface(const QString& name)
+{
+ QMutexLocker locker(&mutex);
+ QSharedDataPointer<QDBusIntrospection::Interface> data = knownInterfaces.value(name);
+ if (!data) {
+ data = new QDBusIntrospection::Interface;
+ data->name = name;
+ data->ref.ref(); // don't delete
+
+ knownInterfaces.insert(name, data);
+ }
+ return data;
+}
+
+QDBusIntrospection::Object*
+QDBusConnectionPrivate::findObject(const QString& service, const QString& path)
+{
+ QMutexLocker locker(&mutex);
+ QDBusIntrospection::Object* data = knownObjects.value(service + path);
+ if (!data) {
+ data = new QDBusIntrospection::Object;
+ data->service = service;
+ data->path = path;
+
+ knownObjects.insert(service + path, data);
+ }
+
+ return data;
+}
+
+void QDBusConnectionPrivate::disposeOfLocked(QDBusIntrospection::Object* p)
+{
+ if (p && !p->ref.deref()) { // ref--
+ // 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));
+#else
+ // non-debug
+ knownObjects.remove(objName);
+#endif
+
+ // remove sub-objects too
+ if (!objName.endsWith('/'))
+ objName.append('/');
+ foreach (QString subObjName, p->childObjects)
+ disposeOfLocked(knownObjects.value(objName + subObjName));
+
+ delete p;
+ }
+}
+
+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);
+ disposeOfLocked( const_cast<QDBusIntrospection::Object*>(p->data) );
+}
+
+#ifndef QT_NO_DEBUG
+int QDBusReplyWaiter::exec(QEventLoop::ProcessEventsFlags flags)
+{
+ static int eventlevel;
+ level = ++eventlevel;
+ qDebug("QDBusReplyWaiter::exec %p level %d starting", this, level);
+ int retcode = QEventLoop::exec(flags);
+ qDebug("QDBusReplyWaiter::exec %p level %d exiting", this, level);
+ --eventlevel;
+ return retcode;
+}
+
+void QDBusReplyWaiter::exit(int retcode)
+{
+ qDebug("QDBusReplyWaiter::exit %p level %d called", this, level);
+ QEventLoop::exit(retcode);
+}
+#endif
+
+void QDBusReplyWaiter::reply(const QDBusMessage &msg)
+{
+ replyMsg = msg;
+ QTimer::singleShot(0, this, SLOT(quit()));
+}
diff --git a/qt/qdbusinterface.cpp b/qt/qdbusinterface.cpp
new file mode 100644
index 00000000..36354fc1
--- /dev/null
+++ b/qt/qdbusinterface.cpp
@@ -0,0 +1,256 @@
+/* -*- C++ -*-
+ *
+ * Copyright (C) 2005 Thiago Macieira <thiago@kde.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
+ *
+ * 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 "qdbusinterface.h"
+#include "qdbusobject.h"
+#include "qdbusstandardinterfaces.h"
+
+#include "qdbusinterface_p.h"
+
+QDBusInterface::QDBusInterface(QDBusInterfacePrivate* p)
+ : d(p)
+{
+ d->ref.ref();
+}
+
+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)
+ : d(0)
+{
+ *this = conn.findInterface(service, path, name);
+}
+
+QDBusInterface::~QDBusInterface()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+QDBusInterface& QDBusInterface::operator=(const QDBusInterface& other)
+{
+ other.d->ref.ref();
+ QDBusInterfacePrivate* old = qAtomicSetPtr(&d, other.d);
+ if (old && !old->ref.deref())
+ delete old;
+
+ return *this;
+}
+
+QDBusConnection QDBusInterface::connection() const
+{
+ return d->conn;
+}
+
+QString QDBusInterface::service() const
+{
+ return d->service;
+}
+
+QString QDBusInterface::path() const
+{
+ return d->path;
+}
+
+QString QDBusInterface::interface() const
+{
+ return d->data->name;
+}
+
+QString QDBusInterface::introspectionData() const
+{
+ d->introspect();
+ return d->data->introspection;
+}
+
+const QDBusIntrospection::Interface& QDBusInterface::interfaceData() const
+{
+ d->introspect();
+ return *d->data;
+}
+
+const QDBusIntrospection::Annotations& QDBusInterface::annotationData() const
+{
+ d->introspect();
+ return d->data->annotations;
+}
+
+const QDBusIntrospection::Methods& QDBusInterface::methodData() const
+{
+ d->introspect();
+ return d->data->methods;
+}
+
+const QDBusIntrospection::Signals& QDBusInterface::signalData() const
+{
+ d->introspect();
+ return d->data->signals_;
+}
+
+const QDBusIntrospection::Properties& QDBusInterface::propertyData() const
+{
+ d->introspect();
+ return d->data->properties;
+}
+
+QDBusMessage QDBusInterface::callWithArgs(const QDBusIntrospection::Method& method,
+ const QList<QVariant>& a_args,
+ CallMode mode)
+{
+ QString signature(""); // empty, not null
+ QVariantList args = a_args;
+
+ if (!method.inputArgs.isEmpty())
+ {
+ // go over the list of parameters for the method
+ QDBusIntrospection::Arguments::const_iterator it = method.inputArgs.begin(),
+ end = method.inputArgs.end();
+ int arg;
+ for (arg = 0; it != end; ++it, ++arg)
+ {
+ // find the marshalled name for this type
+ QString typeSig = QLatin1String(it->type.dbusSignature());
+ signature += typeSig;
+ }
+ }
+ else
+ args.clear();
+
+ if (method.annotations.contains(ANNOTATION_NO_WAIT))
+ mode = NoWaitForReply;
+
+ return callWithArgs(method.name, signature, args, mode);
+}
+
+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('.');
+ if (pos != -1) {
+ m.truncate(pos);
+ sig = method.mid(pos + 1);
+ }
+ return callWithArgs(m, sig, args, mode);
+}
+
+QDBusMessage QDBusInterface::callWithArgs(const QString& method, const QString& signature,
+ const QList<QVariant>& args, CallMode mode)
+{
+ QDBusMessage msg = QDBusMessage::methodCall(service(), path(), interface(), method, signature);
+ msg.QList<QVariant>::operator=(args);
+
+ QDBusMessage reply;
+ if (mode == WaitForReply)
+ reply = d->conn.sendWithReply(msg);
+ else
+ d->conn.send(msg);
+
+ d->lastError = reply; // will clear if reply isn't an error
+
+ // ensure that there is at least one element
+ if (reply.isEmpty())
+ reply << QVariant();
+
+ return reply;
+}
+
+bool QDBusInterface::connect(const QDBusIntrospection::Signal& sig, QObject* obj, const char *slot)
+{
+ QString signature(""); // empty, not null
+
+ if (!sig.outputArgs.isEmpty())
+ {
+ // go over the list of parameters for the method
+ QDBusIntrospection::Arguments::const_iterator it = sig.outputArgs.begin(),
+ end = sig.outputArgs.end();
+ int arg;
+ for (arg = 0; it != end; ++it, ++arg)
+ {
+ // find the marshalled name for this type
+ QString typeSig = QLatin1String(it->type.dbusSignature());
+ signature += typeSig;
+ }
+ }
+
+ return connect(sig.name, signature, obj, slot);
+}
+
+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('.');
+ if (pos != -1) {
+ s.truncate(pos);
+ sig = signalName.mid(pos + 1);
+ }
+ return connect(s, sig, obj, slot);
+}
+
+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)
+{
+ // sanity checking
+ if (prop.access == QDBusIntrospection::Property::Write)
+ return QVariant(); // write-only prop
+
+ QDBusPropertiesInterface pi(object());
+ return pi.get(interface(), prop.name);
+}
+
+QVariant QDBusInterface::propertyGet(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)
+{
+ // sanity checking
+ if (prop.access == QDBusIntrospection::Property::Read)
+ return;
+
+ QDBusPropertiesInterface pi(object());
+ pi.set(interface(), prop.name, newValue);
+}
+
+void QDBusInterface::propertySet(const QString& propName, QVariant newValue)
+{
+ // can't do sanity checking
+ QDBusPropertiesInterface pi(object());
+ pi.set(interface(), propName, newValue);
+}
diff --git a/qt/qdbusinterface.h b/qt/qdbusinterface.h
new file mode 100644
index 00000000..2b96cae3
--- /dev/null
+++ b/qt/qdbusinterface.h
@@ -0,0 +1,310 @@
+/* -*- C++ -*-
+ *
+ * Copyright (C) 2005 Thiago Macieira <thiago@kde.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
+ *
+ * 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 QDBUSINTERFACE_H
+#define QDBUSINTERFACE_H
+
+#include "qdbusmessage.h"
+#include "qdbusobject.h"
+#include "qdbusintrospection.h"
+#include <QtCore/qstring.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qlist.h>
+
+class QDBusConnection;
+
+class QDBusInterfacePrivate;
+/**
+ * Base class for all DBUS interfaces in the QtDBUS binding.
+ */
+class QDBUS_EXPORT QDBusInterface
+{
+ friend class QDBusConnection;
+
+public:
+ enum CallMode {
+ WaitForReply,
+ NoWaitForReply
+ };
+
+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.
+ */
+ 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);
+
+ /**
+ * Sets the value of the given property.
+ */
+ void propertySet(const QString& property, QVariant 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.
+ */
+ 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)
+ {
+ QList<QVariant> args;
+ args << t1;
+ return callWithArgs(m, args);
+ }
+
+ /**
+ * Call the given method.
+ */
+ template<typename MethodType, typename T1, typename T2>
+ inline QDBusMessage call(MethodType m, T1 t1, T2 t2)
+ {
+ QList<QVariant> args;
+ args << t1 << t2;
+ 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)
+ {
+ QList<QVariant> args;
+ args << t1 << t2 << t3;
+ 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)
+ {
+ QList<QVariant> args;
+ args << t1 << t2 << t3 << t4;
+ 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)
+ {
+ QList<QVariant> args;
+ args << t1 << t2 << t3 << t4 << t5;
+ 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)
+ {
+ QList<QVariant> args;
+ args << t1 << t2 << t3 << t4 << t5 << t6;
+ 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)
+ {
+ QList<QVariant> args;
+ args << t1 << t2 << t3 << t4 << t5 << t6 << t7;
+ 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)
+ {
+ QList<QVariant> args;
+ args << t1 << t2 << t3 << t4 << t5 << t6 << t7 << t8;
+ return callWithArgs(m, args);
+ }
+
+private:
+ QDBusInterface(QDBusInterfacePrivate*);
+ QDBusInterfacePrivate *d;
+};
+
+#endif
diff --git a/qt/qdbusinterface_p.h b/qt/qdbusinterface_p.h
new file mode 100644
index 00000000..52fa8008
--- /dev/null
+++ b/qt/qdbusinterface_p.h
@@ -0,0 +1,67 @@
+/*
+ *
+ * Copyright (C) 2006 Thiago José Macieira <thiago@kde.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
+ *
+ * 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 QDBUSINTERFACEPRIVATE_H
+#define QDBUSINTERFACEPRIVATE_H
+
+#include "qdbusobject.h"
+#include "qdbusinterface.h"
+#include "qdbusconnection.h"
+#include "qdbuserror.h"
+
+#define ANNOTATION_NO_WAIT "com.trolltech.DBus.NoWaitForReply"
+
+class QDBusInterfacePrivate
+{
+public:
+ QAtomic ref;
+ QDBusConnection conn;
+ QString service;
+ QString path;
+ QDBusError lastError;
+
+ //QConstSharedDataPointer<QDBusIntrospection::Interface> data;
+ const QDBusIntrospection::Interface* data;
+
+ inline bool needsIntrospection() const
+ { return data->introspection.isNull(); }
+
+ inline void introspect()
+ { if (needsIntrospection()) QDBusObject(conn, service, path).introspect(); }
+};
+
+
+#endif
diff --git a/qt/qdbusintrospection.cpp b/qt/qdbusintrospection.cpp
new file mode 100644
index 00000000..e1183b7c
--- /dev/null
+++ b/qt/qdbusintrospection.cpp
@@ -0,0 +1,380 @@
+/* -*- 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 "qdbusintrospection.h"
+#include "qdbusxmlparser_p.h"
+
+/*!
+ \class QDBusIntrospection
+ \brief Information about introspected objects and interfaces on D-Bus.
+
+ This class provides structures and methods for parsing the XML introspection data for D-Bus.
+ Normally, you don't have to use the methods provided here: QDBusInterface and QDBusObject will
+ do that for you.
+
+ But they may prove useful if the XML data was obtained through other means (like parsing a file).
+*/
+
+/*!
+ \struct QDBusIntrospection::Argument
+ \brief One argument to a D-Bus method or signal.
+
+ This struct represents one argument passed to a method or received from a method or signal in
+ D-Bus. The struct does not contain information on the direction (input or output).
+*/
+
+/*!
+ \var QDBusIntrospection::Argument::type
+ The argument type.
+*/
+
+/*!
+ \var QDBusIntrospection::Argument::name
+ The argument name. The argument name is optional, so this may be a null QString.
+*/
+
+/*!
+ \struct QDBusIntrospection::Method
+ \brief Information about one method.
+
+ This struct represents one method discovered through introspection. A method is composed of
+ its \a name, its input arguments, its output arguments, and, optionally, annotations. There are no
+ "in-out" arguments.
+*/
+
+/*!
+ \var QDBusIntrospection::Method::name
+ The method's name.
+*/
+
+/*!
+ \var QDBusIntrospection::Method::inputArgs
+ A list of the method's input arguments.
+*/
+
+/*!
+ \var QDBusIntrospection::Method::outputArgs
+ A list of the method's output arguments (i.e., return values).
+*/
+
+/*!
+ \var QDBusIntrospection::Method::annotations
+ The annotations associated with the method. Each annotation is a pair of strings, where the key
+ is of the same format as a D-Bus interface name. The value is arbitrary.
+*/
+
+/*!
+ \struct QDBusIntrospection::Signal
+ \brief Information about one signal.
+
+ This struct represents one signal discovered through introspection. A signal is composed of
+ its \a name, its output arguments, and, optionally, annotations.
+*/
+
+/*!
+ \var QDBusIntrospection::Signal::outputArgs
+ A list of the signal's arguments.
+*/
+
+/*!
+ \var QDBusIntrospection::Signal::annotations
+ The annotations associated with the signal. Each annotation is a pair of strings, where the key
+ is of the same format as a D-Bus interface name. The value is arbitrary.
+*/
+
+/*!
+ \struct QDBusIntrospection::Property
+ \brief Information about one property.
+
+ This struct represents one property discovered through introspection. A property is composed of
+ its \a name, its \a type, its \a access rights, and, optionally, annotations.
+*/
+
+/*!
+ \var QDBusIntrospection::Property::name
+ The property's name.
+*/
+
+/*!
+ \var QDBusIntrospection::Property::type
+ The property's type.
+*/
+
+/*!
+ \enum QDBusIntrospection::Property::Access
+ The possible access rights for a property:
+ - Read
+ - Write
+ - ReadWrite
+*/
+
+/*!
+ \var QDBusIntrospection::Property::access
+ The property's access rights.
+*/
+
+/*!
+ \var QDBusIntrospection::Property::annotations
+ The annotations associated with the property. Each annotation is a pair of strings, where the key
+ is of the same format as a D-Bus interface name. The value is arbitrary.
+*/
+
+/*!
+ \struct QDBusIntrospection::Interface
+ \brief Information about one interface on the bus.
+
+ Each interface on D-Bus has an unique \a name, identifying where that interface was defined.
+ Interfaces may have annotations, methods, signals and properties, but none are mandatory.
+*/
+
+/*!
+ \var QDBusIntrospection::Interface::name
+ The interface's name.
+*/
+
+/*!
+ \var QDBusIntrospection::Interface::introspection
+ The XML document fragment describing this interface.
+
+ If parsed again through parseInterface, the object returned should have the same contents as
+ this object.
+*/
+
+/*!
+ \var QDBusIntrospection::Interface::annotations
+ The annotations associated with the interface. Each annotation is a pair of strings, where the key
+ is of the same format as a D-Bus interface name. The value is arbitrary.
+*/
+
+/*!
+ \var QDBusIntrospection::Interface::methods
+ The methods available in this interface. Note that method names are not unique (i.e., methods
+ can be overloaded with multiple arguments types).
+*/
+
+/*!
+ \var QDBusIntrospection::Interface::signals_
+ The signals available in this interface. Note that signal names are not unique (i.e., signals
+ can be overloaded with multiple argument types).
+
+ This member is called "signals_" because "signals" is a reserved keyword in Qt.
+*/
+
+/*!
+ \var QDBusIntrospection::Interface::properties
+ The properties available in this interface. Property names are unique.
+*/
+
+/*!
+ \struct QDBusIntrospection::Object
+ \brief Information about one object on the bus.
+
+ An object on the D-Bus bus is represented by its service and path on the service but, unlike
+ interfaces, objects are mutable. That is, their contents can change with time. Therefore,
+ while the (service, path) pair uniquely identifies an object, the information contained in
+ this struct may no longer represent the object.
+
+ An object can contain interfaces and child (sub) objects.
+*/
+
+/*!
+ \var QDBusIntrospection::Object::service
+ The object's service name.
+
+ \sa parseObject, parseObjectTree
+*/
+
+/*!
+ \var QDBusIntrospection::Object::path
+ The object's path on the service. This is an absolute path.
+
+ \sa parseObject, parseObjectTree
+*/
+
+/*!
+ \var QDBusIntrospection::Object::introspection
+ The XML document fragment describing this object, its interfaces and sub-objects at the time
+ of the parsing.
+
+ The result of parseObject with this XML data should be the same as the Object struct.
+*/
+
+/*!
+ \var QDBusIntrospection::Object::interfaces
+ The list of interface names in this object.
+*/
+
+/*!
+ \var QDBusIntrospection::Object::childObjects
+ The list of child object names in this object. Note that this is a relative name, not an
+ absolute path. To obtain the absolute path, concatenate with \ref path.
+*/
+
+/*!
+ \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.
+*/
+
+/*!
+ \var QDBusIntrospection::ObjectTree::interfaceData
+ A map of interfaces and their names.
+*/
+
+/*!
+ \var QDBusIntrospection::ObjectTree::childObjectData
+ A map of object paths and their data. The map key contains the relative path to the object.
+
+ Note this map contains only the child notes that do have information about the sub-object's
+ contents. If the XML data did not contain the information, only the object name will be listed
+ in childObjects, but not in childObjectData.
+*/
+
+/*!
+ \typedef QDBusIntrospection::Annotations
+ Contains a QMap of an annotation pair. The annotation's name is stored in the QMap key and
+ must be unique. The annotation's value is stored in the QMap's value and is arbitrary.
+*/
+
+/*!
+ \typedef QDBusIntrospection::Arguments
+ Contains a list of arguments to either a Method or a Signal. The arguments' order is important.
+*/
+
+/*!
+ \typedef QDBusIntrospection::Methods
+ Contains a QMap of methods and their names. The method's name is stored in the map's key and
+ is not necessarily unique. The order in which multiple methods with the same name are stored
+ in this map is undefined.
+*/
+
+/*!
+ \typedef QDBusIntrospection::Signals
+ Contains a QMap of signals and their names. The signal's name is stored in the map's key and
+ is not necessarily unique. The order in which multiple signals with the same name are stored
+ in this map is undefined.
+*/
+
+/*!
+ \typedef QDBusIntrospection::Properties
+ Contains a QMap of properties and their names. Each property must have a unique name.
+*/
+
+/*!
+ \typedef QDBusIntrospection::Interfaces
+ Contains a QMap of interfaces and their names. Each interface has a unique name.
+*/
+
+/*!
+ \typedef QDBusIntrospection::Objects
+ Contains a QMap of objects and their paths relative to their immediate parent.
+
+ \sa parseObjectTree
+*/
+
+/*!
+ 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.
+
+ If there are multiple interfaces in this XML data, it is undefined which one will be
+ returned.
+
+ \param xml the XML data to be parsed
+ \returns the parsed interface
+*/
+QDBusIntrospection::Interface
+QDBusIntrospection::parseInterface(const QString &xml)
+{
+ // be lazy
+ Interfaces ifs = parseInterfaces(xml);
+ if (ifs.isEmpty())
+ return Interface();
+
+ // return the first in map order (probably alphabetical order)
+ return *ifs.constBegin().value();
+}
+
+/*!
+ 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.
+
+ \param xml the XML data to be parsed
+ \returns the parsed interfaces
+*/
+QDBusIntrospection::Interfaces
+QDBusIntrospection::parseInterfaces(const QString &xml)
+{
+ QDBusXmlParser parser(QString(), QString(), xml);
+ return parser.interfaces();
+}
+
+/*!
+ Parses the XML document fragment containing one object.
+
+ 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.
+
+ This function does not parse the interfaces contained in the node, nor sub-object's contents.
+ It will only list their names. If you need to know their contents, use parseObjectTree.
+
+ \param xml the XML data to be parsed
+ \param service the service where this object is found
+ \param path the absolute path to this node on the remote service
+ \returns the parsed object
+*/
+QDBusIntrospection::Object
+QDBusIntrospection::parseObject(const QString &xml, const QString &service, const QString &path)
+{
+ QDBusXmlParser parser(service, path, xml);
+ QSharedDataPointer<QDBusIntrospection::Object> retval = parser.object();
+ if (!retval)
+ return QDBusIntrospection::Object();
+ return *retval;
+}
+
+/*!
+ Parses the XML document fragment containing one object node and returns all the information
+ about the interfaces and sub-objects.
+
+ The Objects map returned will contain the absolute path names in the key.
+
+ \param xml the XML data to be parsed
+ \param service the service where this object is found
+ \param path the absolute path to this node on the remote service
+ \returns the parsed objects and interfaces
+*/
+QDBusIntrospection::ObjectTree
+QDBusIntrospection::parseObjectTree(const QString &xml, const QString &service, const QString &path)
+{
+ QDBusXmlParser parser(service, path, xml);
+ QSharedDataPointer<QDBusIntrospection::ObjectTree> retval = parser.objectTree();
+ if (!retval)
+ return QDBusIntrospection::ObjectTree();
+ return *retval;
+}
diff --git a/qt/qdbusintrospection.h b/qt/qdbusintrospection.h
new file mode 100644
index 00000000..4e1a8745
--- /dev/null
+++ b/qt/qdbusintrospection.h
@@ -0,0 +1,148 @@
+/* -*- 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.
+ *
+ */
+
+#ifndef QDBUSINTROSPECTION_H
+#define QDBUSINTROSPECTION_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qshareddata.h>
+#include "qdbustype.h"
+#include "qdbusmacros.h"
+
+class QDBUS_EXPORT QDBusIntrospection
+{
+public:
+ // forward declarations
+ struct Argument;
+ struct Method;
+ struct Signal;
+ struct Property;
+ struct Interface;
+ struct Object;
+ struct ObjectTree;
+
+ // typedefs
+ typedef QMap<QString, QString> Annotations;
+ typedef QList<Argument> Arguments;
+ typedef QMultiMap<QString, Method> Methods;
+ typedef QMultiMap<QString, Signal> Signals;
+ typedef QMap<QString, Property> Properties;
+ typedef QMap<QString, QSharedDataPointer<Interface> > Interfaces;
+ typedef QMap<QString, QSharedDataPointer<ObjectTree> > Objects;
+
+public:
+ // the structs
+
+ struct Argument
+ {
+ QDBusType type;
+ QString name;
+
+ inline bool operator==(const Argument& other) const
+ { return name == other.name && type == other.type; }
+ };
+
+ struct Method
+ {
+ QString name;
+ Arguments inputArgs;
+ Arguments outputArgs;
+ Annotations annotations;
+
+ inline bool operator==(const Method& other) const
+ { return name == other.name && annotations == other.annotations &&
+ inputArgs == other.inputArgs && outputArgs == other.outputArgs; }
+ };
+
+ struct Signal
+ {
+ QString name;
+ Arguments outputArgs;
+ Annotations annotations;
+
+ inline bool operator==(const Signal& other) const
+ { return name == other.name && annotations == other.annotations &&
+ outputArgs == other.outputArgs; }
+ };
+
+ struct Property
+ {
+ enum Access { Read, Write, ReadWrite };
+ QString name;
+ QDBusType type;
+ Access access;
+ Annotations annotations;
+
+ inline bool operator==(const Property& other) const
+ { return access == other.access && name == other.name &&
+ annotations == other.annotations && type == other.type; }
+ };
+
+ struct Interface: public QSharedData
+ {
+ QString name;
+ QString introspection;
+
+ Annotations annotations;
+ Methods methods;
+ Signals signals_;
+ Properties properties;
+
+ inline bool operator==(const Interface &other) const
+ { return name == other.name; }
+ };
+
+ struct Object: public QSharedData
+ {
+ QString service;
+ QString path;
+ QString introspection;
+
+ QStringList interfaces;
+ QStringList childObjects;
+ };
+
+ struct ObjectTree: public Object
+ {
+ Interfaces interfaceData;
+ Objects childObjectData;
+ };
+
+public:
+ static Interface parseInterface(const QString &xml);
+ static Interfaces parseInterfaces(const QString &xml);
+ static Object parseObject(const QString &xml, const QString &service = QString(),
+ const QString &path = QString());
+ static ObjectTree parseObjectTree(const QString &xml,
+ const QString &service,
+ const QString &path);
+
+private:
+ QDBusIntrospection();
+};
+
+#endif
diff --git a/qt/qdbusmacros.h b/qt/qdbusmacros.h
new file mode 100644
index 00000000..e36bfb7f
--- /dev/null
+++ b/qt/qdbusmacros.h
@@ -0,0 +1,36 @@
+/* qdbusmessage.h QDBusMessage object
+ *
+ * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef QDBUSMACROS_H
+#define QDBUSMACROS_H
+
+#include <QtCore/qglobal.h>
+
+#ifndef QDBUS_EXPORT
+#ifdef QDBUS_MAKEDLL
+# define QDBUS_EXPORT Q_DECL_EXPORT
+#else
+# define QDBUS_EXPORT Q_DECL_IMPORT
+#endif
+#endif
+
+#endif
diff --git a/qt/qdbusmarshall.cpp b/qt/qdbusmarshall.cpp
index 31c35896..19de0d91 100644
--- a/qt/qdbusmarshall.cpp
+++ b/qt/qdbusmarshall.cpp
@@ -1,6 +1,8 @@
/* qdbusmarshall.cpp
*
* 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,21 +17,22 @@
* 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.
*
*/
#include "qdbusmarshall.h"
+#include "qdbustype.h"
#include "qdbusvariant.h"
-#include <QtCore/qdebug.h>
-#include <QtCore/qvariant.h>
-#include <QtCore/qlist.h>
-#include <QtCore/qmap.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qvarlengtharray.h>
-#include <QtCore/qvector.h>
+#include <qdebug.h>
+#include <qvariant.h>
+#include <qlist.h>
+#include <qmap.h>
+#include <qstringlist.h>
+#include <qvarlengtharray.h>
+#include <qvector.h>
#include <dbus/dbus.h>
@@ -60,6 +63,10 @@ static QVariant qFetchParameter(DBusMessageIter *it)
switch (dbus_message_iter_get_arg_type(it)) {
case DBUS_TYPE_BYTE:
return qIterGet<unsigned char>(it);
+ case DBUS_TYPE_INT16:
+ return qIterGet<dbus_int16_t>(it);
+ case DBUS_TYPE_UINT16:
+ return qIterGet<dbus_uint16_t>(it);
case DBUS_TYPE_INT32:
return qIterGet<dbus_int32_t>(it);
case DBUS_TYPE_UINT32:
@@ -78,15 +85,26 @@ static QVariant qFetchParameter(DBusMessageIter *it)
return QString::fromUtf8(qIterGet<char *>(it));
case DBUS_TYPE_ARRAY: {
int arrayType = dbus_message_iter_get_element_type(it);
- if (arrayType == DBUS_TYPE_STRING || arrayType == DBUS_TYPE_OBJECT_PATH) {
+ if (arrayType == DBUS_TYPE_STRING || arrayType == DBUS_TYPE_OBJECT_PATH ||
+ arrayType == DBUS_TYPE_SIGNATURE) {
return qFetchStringList(it);
+ } else if (arrayType == DBUS_TYPE_BYTE) {
+ DBusMessageIter sub;
+ dbus_message_iter_recurse(it, &sub);
+ int len = dbus_message_iter_get_array_len(&sub);
+ char* data;
+ dbus_message_iter_get_fixed_array(&sub,&data,&len);
+ return QByteArray(data,len);
} else if (arrayType == DBUS_TYPE_DICT_ENTRY) {
// ### support other types of maps?
QMap<QString, QVariant> map;
DBusMessageIter sub;
+
dbus_message_iter_recurse(it, &sub);
if (!dbus_message_iter_has_next(&sub))
+ // empty map
return map;
+
do {
DBusMessageIter itemIter;
dbus_message_iter_recurse(&sub, &itemIter);
@@ -96,43 +114,30 @@ static QVariant qFetchParameter(DBusMessageIter *it)
map.insertMulti(key, qFetchParameter(&itemIter));
} while (dbus_message_iter_next(&sub));
return map;
- } else {
- QList<QVariant> list;
- DBusMessageIter sub;
- dbus_message_iter_recurse(it, &sub);
- if (!dbus_message_iter_has_next(&sub))
- return list;
- do {
- list.append(qFetchParameter(&sub));
- } while (dbus_message_iter_next(&sub));
- return list;
}
- break; }
+ }
+ // fall through
+ // common handling for structs and lists
+ case DBUS_TYPE_STRUCT: {
+ QList<QVariant> list;
+ DBusMessageIter sub;
+ dbus_message_iter_recurse(it, &sub);
+ if (!dbus_message_iter_has_next(&sub))
+ return list;
+ do {
+ list.append(qFetchParameter(&sub));
+ } while (dbus_message_iter_next(&sub));
+ return list;
+ }
case DBUS_TYPE_VARIANT: {
QDBusVariant dvariant;
DBusMessageIter sub;
dbus_message_iter_recurse(it, &sub);
- dvariant.signature = QString::fromUtf8(dbus_message_iter_get_signature(&sub));
+ dvariant.type = QDBusType(dbus_message_iter_get_signature(&sub));
dvariant.value = qFetchParameter(&sub);
return qVariantFromValue(dvariant);
}
-#if 0
- case DBUS_TYPE_DICT: {
- QMap<QString, QVariant> map;
- DBusMessageIter sub;
- dbus_message
- if (dbus_message_iter_init_dict_iterator(it, &dictIt)) {
- do {
- map[QString::fromUtf8(dbus_message_iter_get_dict_key(&dictIt))] =
- qFetchParameter(&dictIt);
- } while (dbus_message_iter_next(&dictIt));
- }
- return map;
- break; }
- case DBUS_TYPE_CUSTOM:
- return qGetCustomValue(it);
- break;
-#endif
+
default:
qWarning("Don't know how to handle type %d '%c'", dbus_message_iter_get_arg_type(it), dbus_message_iter_get_arg_type(it));
return QVariant();
@@ -153,148 +158,305 @@ void QDBusMarshall::messageToList(QList<QVariant> &list, DBusMessage *message)
} while (dbus_message_iter_next(&it));
}
-#define DBUS_APPEND(type,dtype,var) \
-type dtype##v=(var); \
-dbus_message_append_args(msg, dtype, &dtype##v, DBUS_TYPE_INVALID)
-#define DBUS_APPEND_LIST(type,dtype,var,size) \
-type dtype##v=(var); \
-dbus_message_append_args(msg, DBUS_TYPE_ARRAY, dtype, &dtype##v, size, DBUS_TYPE_INVALID)
-
-
-static void qAppendToMessage(DBusMessageIter *it, const QString &str)
-{
- QByteArray ba = str.toUtf8();
- const char *cdata = ba.constData();
- dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &cdata);
-}
-
-static QVariant::Type qVariantListType(const QList<QVariant> &list)
+// convert the variant to the given type and return true if it worked.
+// if the type is not known, guess it from the variant and set.
+// return false if conversion failed.
+static bool checkType(QVariant &var, QDBusType &type)
{
- // TODO - catch lists that have a list as first parameter
- QVariant::Type tp = list.value(0).type();
- if (tp < QVariant::Int || tp > QVariant::Double)
- return QVariant::Invalid;
-
- for (int i = 1; i < list.count(); ++i) {
- const QVariant &var = list.at(i);
- if (var.type() != tp
- && (var.type() != QVariant::List || qVariantListType(var.toList()) != tp))
- return QVariant::Invalid;
+ if (!type.isValid()) {
+ // guess it from the variant
+ type = QDBusType::guessFromVariant(var);
+ return true;
}
- return tp;
-}
-static const char *qDBusListType(const QList<QVariant> &list)
-{
- static const char *DBusArgs[] = { 0, 0, DBUS_TYPE_INT32_AS_STRING, DBUS_TYPE_UINT32_AS_STRING,
- DBUS_TYPE_INT64_AS_STRING, DBUS_TYPE_UINT64_AS_STRING, DBUS_TYPE_DOUBLE_AS_STRING };
-
- return DBusArgs[qVariantListType(list)];
-}
-
-static void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list);
+ // only catch the conversions that won't work
+ // let QVariant do the hard work
+
+ // QVariant can't convert QDBusVariant:
+ if (var.userType() == qMetaTypeId<QDBusVariant>()) {
+ if (type.dbusType() == DBUS_TYPE_VARIANT)
+ return true; // no change
-static void qVariantToIterator(DBusMessageIter *it, const QVariant &var)
-{
- static const int Variant2DBus[] = { DBUS_TYPE_INVALID,
- DBUS_TYPE_BOOLEAN, DBUS_TYPE_INT32, DBUS_TYPE_UINT32,
- DBUS_TYPE_INT64, DBUS_TYPE_UINT64, DBUS_TYPE_DOUBLE };
-
- // these really are static asserts
- Q_ASSERT(QVariant::Invalid == 0);
- Q_ASSERT(QVariant::Int == 2);
- Q_ASSERT(QVariant::Double == 6);
+ // convert manually
+ QDBusVariant dvariant = qvariant_cast<QDBusVariant>(var);
+ var = dvariant.value;
+ return checkType(var, type);
+ }
+
+ if (type.dbusType() == DBUS_TYPE_VARIANT) {
+ // variant can handle anything. Let it pass
+ return true;
+ }
- switch (var.type()) {
+ switch (var.userType()) {
+ case QMetaType::Short:
+ case QMetaType::UShort:
+ case QMetaType::UChar:
case QVariant::Int:
case QVariant::UInt:
case QVariant::LongLong:
case QVariant::ULongLong:
case QVariant::Double:
- dbus_message_iter_append_basic(it, Variant2DBus[var.type()],
- var.constData());
- break;
case QVariant::String:
- qAppendToMessage(it, var.toString());
- break;
+ if (type.isBasic())
+ // QVariant can handle this on its own
+ return true;
+
+ // cannot handle this
+ qWarning("Invalid conversion from %s to '%s'", var.typeName(),
+ type.dbusSignature().constData());
+ var.clear();
+ return false;
+
+ case QVariant::ByteArray:
+ // make sure it's an "ARRAY of BYTE"
+ if (type.qvariantType() != QVariant::ByteArray) {
+ qWarning("Invalid conversion from %s to '%s'", var.typeName(),
+ type.dbusSignature().constData());
+ var.clear();
+ return false;
+ }
+ return true;
+
+ case QVariant::StringList:
+ // make sure it's "ARRAY of STRING"
+ if (type.qvariantType() != QVariant::StringList) {
+ qWarning("Invalid conversion from %s to '%s'", var.typeName(),
+ type.dbusSignature().constData());
+ var.clear();
+ return false;
+ }
+ return true;
+
+ case QVariant::List:
+ // could be either struct or array
+ if (type.dbusType() != DBUS_TYPE_ARRAY && type.dbusType() != DBUS_TYPE_STRUCT) {
+ qWarning("Invalid conversion from %s to '%s'", var.typeName(),
+ type.dbusSignature().constData());
+ var.clear();
+ return false;
+ }
+
+ return true;
+
+ case QVariant::Map:
+ if (!type.isMap()) {
+ qWarning("Invalid conversion from %s to '%s'", var.typeName(),
+ type.dbusSignature().constData());
+ var.clear();
+ return false;
+ }
+
+ return true;
+
+ case QVariant::Invalid:
+ // create an empty variant
+ var.convert(type.qvariantType());
+ break;
+ }
+
+ qWarning("Found unknown QVariant type %d (%s) when converting to DBus", (int)var.type(),
+ var.typeName());
+ var.clear();
+ return false;
+}
+
+static void qVariantToIteratorInternal(DBusMessageIter *it, const QVariant &var,
+ const QDBusType &type);
+
+static void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list,
+ const QDBusTypeList &list);
+
+template<typename T>
+static void qIterAppend(DBusMessageIter *it, const QDBusType &type, T arg)
+{
+ dbus_message_iter_append_basic(it, type.dbusType(), &arg);
+}
+
+static void qAppendArrayToMessage(DBusMessageIter *it, const QDBusType &subType,
+ const QVariant &var)
+{
+ DBusMessageIter sub;
+ dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, subType.dbusSignature(), &sub);
+
+ switch (var.type())
+ {
case QVariant::StringList: {
const QStringList list = var.toStringList();
- DBusMessageIter sub;
- dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY,
- DBUS_TYPE_STRING_AS_STRING, &sub);
- for (int s = 0; s < list.count(); ++s)
- qAppendToMessage(&sub, list.at(s));
- dbus_message_iter_close_container(it, &sub);
+ foreach (QString str, list)
+ qIterAppend(&sub, subType, str.toUtf8().constData());
break;
}
- case QVariant::List: {
- const QList<QVariant> &list = var.toList();
- const char *listType = qDBusListType(list);
- if (!listType) {
- qWarning("Don't know how to marshall list.");
- break;
- }
- DBusMessageIter sub;
- dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, listType, &sub);
- qListToIterator(&sub, list);
- dbus_message_iter_close_container(it, &sub);
+
+ case QVariant::ByteArray: {
+ const QByteArray array = var.toByteArray();
+ const char* cdata = array.constData();
+ dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &cdata, array.length());
break;
}
+
case QVariant::Map: {
- // ### TODO - marshall more than qstring/qstring maps
- const QMap<QString, QVariant> &map = var.toMap();
- DBusMessageIter sub;
- QVarLengthArray<char, 16> sig;
- sig.append(DBUS_DICT_ENTRY_BEGIN_CHAR);
- sig.append(DBUS_TYPE_STRING);
- sig.append(DBUS_TYPE_STRING);
- sig.append(DBUS_DICT_ENTRY_END_CHAR);
- sig.append('\0');
- qDebug() << QString::fromAscii(sig.constData());
- dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, sig.constData(), &sub);
+ const QVariantMap map = var.toMap();
+ const QDBusTypeList& subTypes = subType.subTypes();
for (QMap<QString, QVariant>::const_iterator mit = map.constBegin();
mit != map.constEnd(); ++mit) {
DBusMessageIter itemIterator;
dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, 0, &itemIterator);
- qAppendToMessage(&itemIterator, mit.key());
- qAppendToMessage(&itemIterator, mit.value().toString());
+
+ // let the string be converted to QVariant
+ qVariantToIteratorInternal(&itemIterator, mit.key(), subTypes[0]);
+ qVariantToIteratorInternal(&itemIterator, mit.value(), subTypes[1]);
+
dbus_message_iter_close_container(&sub, &itemIterator);
}
- dbus_message_iter_close_container(it, &sub);
break;
}
- case QVariant::UserType: {
- if (var.userType() == QMetaTypeId<QDBusVariant>::qt_metatype_id()) {
- DBusMessageIter sub;
- QDBusVariant dvariant = qvariant_cast<QDBusVariant>(var);
- dbus_message_iter_open_container(it, DBUS_TYPE_VARIANT,
- dvariant.signature.toUtf8().constData(), &sub);
- qVariantToIterator(&sub, dvariant.value);
- dbus_message_iter_close_container(it, &sub);
- break;
- }
+
+ case QVariant::List: {
+ const QVariantList list = var.toList();
+ foreach (QVariant v, list)
+ qVariantToIteratorInternal(&sub, v, subType);
+ break;
}
- // fall through
+
default:
- qWarning("Don't know how to handle type %s", var.typeName());
+ qFatal("qAppendArrayToMessage got unknown type!");
break;
}
+
+ dbus_message_iter_close_container(it, &sub);
}
-void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list)
+static void qAppendStructToMessage(DBusMessageIter *it, const QDBusTypeList &typeList,
+ const QVariantList &list)
{
- if (list.isEmpty())
- return;
+ DBusMessageIter sub;
+ dbus_message_iter_open_container(it, DBUS_TYPE_STRUCT, NULL, &sub);
+ qListToIterator(&sub, list, typeList);
+ dbus_message_iter_close_container(it, &sub);
+}
+
+static void qAppendVariantToMessage(DBusMessageIter *it, const QDBusType & /* type */,
+ const QVariant &var)
+{
+ QVariant v;
+ QDBusType t;
+
+ if (var.userType() == qMetaTypeId<QDBusVariant>()) {
+ QDBusVariant dvariant = qvariant_cast<QDBusVariant>(var);
+ v = dvariant.value;
+ t = dvariant.type;
+ }
+ else {
+ v = var;
+ t = QDBusType::guessFromVariant(v);
+ }
+
+ // now add this variant
+ DBusMessageIter sub;
+ dbus_message_iter_open_container(it, DBUS_TYPE_VARIANT, t.dbusSignature(), &sub);
+ qVariantToIteratorInternal(&sub, v, t);
+ dbus_message_iter_close_container(it, &sub);
+}
+
+static void qVariantToIterator(DBusMessageIter *it, QVariant var, QDBusType type)
+{
+ if (var.isNull() && !type.isValid())
+ return; // cannot add a null like this
+ if (!checkType(var, type))
+ return; // type checking failed
+
+ qVariantToIteratorInternal(it, var, type);
+}
+
+static void qVariantToIteratorInternal(DBusMessageIter *it, const QVariant &var,
+ const QDBusType &type)
+{
+ switch (type.dbusType()) {
+ case DBUS_TYPE_BYTE:
+ qIterAppend( it, type, static_cast<unsigned char>(var.toUInt()) );
+ break;
+ case DBUS_TYPE_BOOLEAN:
+ qIterAppend( it, type, static_cast<dbus_bool_t>(var.toBool()) );
+ break;
+ case DBUS_TYPE_INT16:
+ qIterAppend( it, type, static_cast<dbus_int16_t>(var.toInt()) );
+ break;
+ case DBUS_TYPE_UINT16:
+ qIterAppend( it, type, static_cast<dbus_uint16_t>(var.toUInt()) );
+ break;
+ case DBUS_TYPE_INT32:
+ qIterAppend( it, type, static_cast<dbus_int32_t>(var.toInt()) );
+ break;
+ case DBUS_TYPE_UINT32:
+ qIterAppend( it, type, static_cast<dbus_uint32_t>(var.toUInt()) );
+ break;
+ case DBUS_TYPE_INT64:
+ qIterAppend( it, type, static_cast<dbus_int64_t>(var.toLongLong()) );
+ break;
+ case DBUS_TYPE_UINT64:
+ qIterAppend( it, type, static_cast<dbus_uint64_t>(var.toULongLong()) );
+ break;
+ case DBUS_TYPE_DOUBLE:
+ qIterAppend( it, type, var.toDouble() );
+ break;
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_SIGNATURE:
+ qIterAppend( it, type, var.toString().toUtf8().constData() );
+ break;
+
+ // compound types:
+ case DBUS_TYPE_ARRAY:
+ // could be many things
+ qAppendArrayToMessage( it, type.arrayElement(), var );
+ break;
+
+ case DBUS_TYPE_VARIANT:
+ qAppendVariantToMessage( it, type, var );
+ break;
+ case DBUS_TYPE_STRUCT:
+ qAppendStructToMessage( it, type.subTypes(), var.toList() );
+ break;
+
+ case DBUS_TYPE_DICT_ENTRY:
+ qFatal("qVariantToIterator got a DICT_ENTRY!");
+ break;
+
+ default:
+ qWarning("Found unknown DBus type '%s'", type.dbusSignature().constData());
+ break;
+ }
+}
+
+void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list)
+{
for (int i = 0; i < list.count(); ++i)
- qVariantToIterator(it, list.at(i));
+ qVariantToIterator(it, list.at(i), QDBusType());
}
-void QDBusMarshall::listToMessage(const QList<QVariant> &list, DBusMessage *msg)
+void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list, const QDBusTypeList &types)
+{
+ int min = qMin(list.count(), types.count());
+ for (int i = 0; i < min; ++i)
+ qVariantToIterator(it, list.at(i), types.at(i));
+
+ for (int i = min; i < types.count(); ++i)
+ // we're missing a few arguments, so add default parameters
+ qVariantToIterator(it, QVariant(), types.at(i));
+}
+
+void QDBusMarshall::listToMessage(const QList<QVariant> &list, DBusMessage *msg,
+ const QString &signature)
{
Q_ASSERT(msg);
DBusMessageIter it;
dbus_message_iter_init_append(msg, &it);
- qListToIterator(&it, list);
+
+ if (signature.isEmpty())
+ qListToIterator(&it, list);
+ else
+ qListToIterator(&it, list, QDBusTypeList(signature.toUtf8()));
}
diff --git a/qt/qdbusmarshall.h b/qt/qdbusmarshall.h
index 4fc1a1ab..6ec8caec 100644
--- a/qt/qdbusmarshall.h
+++ b/qt/qdbusmarshall.h
@@ -1,6 +1,8 @@
/* qdbusmarshall.h QDBusMarshall 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,8 +17,8 @@
* 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.
*
*/
@@ -25,14 +27,15 @@
struct DBusMessage;
+template <typename T> class QList;
class QVariant;
-template <typename T>
-class QList;
+class QString;
class QDBusMarshall
{
public:
- static void listToMessage(const QList<QVariant> &list, DBusMessage *message);
+ static void listToMessage(const QList<QVariant> &list, DBusMessage *message,
+ const QString& signature);
static void messageToList(QList<QVariant> &list, DBusMessage *message);
};
diff --git a/qt/qdbusmessage.cpp b/qt/qdbusmessage.cpp
index a77c22f0..5c604c13 100644
--- a/qt/qdbusmessage.cpp
+++ b/qt/qdbusmessage.cpp
@@ -1,6 +1,8 @@
/* qdbusmessage.cpp
*
* 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,19 +17,20 @@
* 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.
*
*/
#include "qdbusmessage.h"
-#include <QtCore/qdebug.h>
-#include <QtCore/qstringlist.h>
+#include <qdebug.h>
+#include <qstringlist.h>
#include <dbus/dbus.h>
#include "qdbusmarshall.h"
+#include "qdbuserror.h"
#include "qdbusmessage_p.h"
QDBusMessagePrivate::QDBusMessagePrivate(QDBusMessage *qq)
@@ -44,8 +47,31 @@ QDBusMessagePrivate::~QDBusMessagePrivate()
}
///////////////
+/*!
+ \class QDBusMessage
+ \brief Represents one message sent or received over the DBus bus.
+
+ This object can represent any of four different types of messages possible on the bus
+ (see MessageType)
+ - Method calls
+ - Method return values
+ - Signal emissions
+ - Error codes
+ Objects of this type are created with the four static functions signal, methodCall,
+ methodReply and error.
+*/
+/*!
+ 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.
+
+ \param path the path of the object that is emitting the signal
+ \param interface the interface that is emitting the signal
+ \param name the name of the signal (a.k.a. method name)
+ \returns a QDBusMessage object that can be sent with with QDBusConnection::send
+*/
QDBusMessage QDBusMessage::signal(const QString &path, const QString &interface,
const QString &name)
{
@@ -58,19 +84,58 @@ QDBusMessage QDBusMessage::signal(const QString &path, const QString &interface,
return message;
}
+/*!
+ Constructs a new DBus message representing a method call. A method call always informs
+ its destination address (service, path, interface and method).
+
+ The DBus bus allows calling a method on a given remote object without specifying the
+ destination interface, if the method name is unique. However, if two interfaces on the
+ remote object export the same method name, the result is undefined (one of the two may be
+ called or an error may be returned).
+
+ When using DBus in a peer-to-peer context (i.e., not on a bus), the service parameter is
+ optional.
+
+ Optionally, a signature parameter can be passed, indicating the type of the parameters to
+ be marshalled over the bus. If there are more arguments thanentries in the signature, the
+ tailing arguments will be silently dropped and not sent. If there are less arguments,
+ default values will be inserted (default values are those created by QVariant::convert
+ when a variant of type QVariant::Invalid is converted to the type).
+
+ The QDBusObject and QDBusInterface classes provide a simpler abstraction to synchronous
+ method calling.
+
+ \param service the remote service to be called (can be a well-known name, a bus
+ address or null)
+ \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 &interface, const QString &method,
+ const QString &sig)
{
QDBusMessage message;
message.d->type = DBUS_MESSAGE_TYPE_METHOD_CALL;
message.d->service = service;
message.d->path = path;
message.d->interface = interface;
- message.d->method = method;
+ message.d->name = method;
+ message.d->signature = sig;
return message;
}
+/*!
+ Constructs a new DBus message representing the return values from a called method.
+
+ \param other the method call DBus message that this is a reply to
+ \returns a QDBusMessage object that can be sent with QDBusConnection::send
+*/
QDBusMessage QDBusMessage::methodReply(const QDBusMessage &other)
{
Q_ASSERT(other.d->msg);
@@ -82,11 +147,63 @@ QDBusMessage QDBusMessage::methodReply(const QDBusMessage &other)
return message;
}
+/*!
+ Constructs a DBus message representing an error condition.
+
+ \param other the QDBusMessage object that generated this error
+ \param name the DBus error name (error names must follow the same convention that
+ interface names do)
+ \param msg the error message
+ \return a QDBusMessage object that can be sent with QDBusMessage::send
+*/
+QDBusMessage QDBusMessage::error(const QDBusMessage &other, const QString &name,
+ const QString &msg)
+{
+ Q_ASSERT(other.d->msg);
+
+ QDBusMessage message;
+ message.d->type = DBUS_MESSAGE_TYPE_ERROR;
+ message.d->name = name;
+ message.d->message = msg;
+ message.d->reply = dbus_message_ref(other.d->msg);
+
+ return message;
+}
+
+/*!
+ \overload
+ Constructs a DBus message representing an error condition.
+
+ \param other the QDBusMessage object that generated this error
+ \param error the QDBusError object representing this error
+ \return a QDBusMessage object that can be sent with QDBusMessage::send
+*/
+QDBusMessage QDBusMessage::error(const QDBusMessage &other, const QDBusError &error)
+{
+ Q_ASSERT(other.d->msg);
+
+ QDBusMessage message;
+ message.d->type = DBUS_MESSAGE_TYPE_ERROR;
+ message.d->name = error.name();
+ message.d->message = error.message();
+ message.d->reply = dbus_message_ref(other.d->msg);
+
+ return message;
+}
+
+/*!
+ Constructs an empty, invalid QDBusMessage object.
+
+ \sa methodCall, methodReply, signal, error
+*/
QDBusMessage::QDBusMessage()
{
d = new QDBusMessagePrivate(this);
}
+/*!
+ Constructs a copy of the other object.
+*/
QDBusMessage::QDBusMessage(const QDBusMessage &other)
: QList<QVariant>(other)
{
@@ -94,12 +211,18 @@ QDBusMessage::QDBusMessage(const QDBusMessage &other)
d->ref.ref();
}
+/*!
+ Disposes of the object and frees any resources that were being held.
+*/
QDBusMessage::~QDBusMessage()
{
if (!d->ref.deref())
delete d;
}
+/*!
+ Copies the contents of the other object.
+*/
QDBusMessage &QDBusMessage::operator=(const QDBusMessage &other)
{
QList<QVariant>::operator=(other);
@@ -107,14 +230,20 @@ QDBusMessage &QDBusMessage::operator=(const QDBusMessage &other)
return *this;
}
+/*!
+ \internal
+ Constructs a DBusMessage object from this object. The returned value must be de-referenced
+ with dbus_message_unref.
+*/
DBusMessage *QDBusMessage::toDBusMessage() const
{
DBusMessage *msg = 0;
+
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->method.toUtf8().constData());
+ d->name.toUtf8().constData());
break;
case DBUS_MESSAGE_TYPE_SIGNAL:
msg = dbus_message_new_signal(d->path.toUtf8().constData(),
@@ -123,14 +252,22 @@ DBusMessage *QDBusMessage::toDBusMessage() const
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());
+ break;
}
if (!msg)
return 0;
- QDBusMarshall::listToMessage(*this, msg);
+ QDBusMarshall::listToMessage(*this, msg, d->signature);
return msg;
}
+/*!
+ \internal
+ Constructs a QDBusMessage by parsing the given DBusMessage object.
+*/
QDBusMessage QDBusMessage::fromDBusMessage(DBusMessage *dmsg)
{
QDBusMessage message;
@@ -140,40 +277,94 @@ QDBusMessage QDBusMessage::fromDBusMessage(DBusMessage *dmsg)
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));
- message.d->name = QString::fromUtf8(dbus_message_get_member(dmsg));
- message.d->sender = QString::fromUtf8(dbus_message_get_sender(dmsg));
+ message.d->name = message.d->type == DBUS_MESSAGE_TYPE_ERROR ?
+ QString::fromUtf8(dbus_message_get_error_name(dmsg)) :
+ QString::fromUtf8(dbus_message_get_member(dmsg));
+ message.d->service = QString::fromUtf8(dbus_message_get_sender(dmsg));
+ message.d->signature = QString::fromUtf8(dbus_message_get_signature(dmsg));
message.d->msg = dbus_message_ref(dmsg);
QDBusMarshall::messageToList(message, dmsg);
+ return message;
+}
+/*!
+ Creates a QDBusMessage that represents the same error as the QDBusError object.
+*/
+QDBusMessage QDBusMessage::fromError(const QDBusError &error)
+{
+ QDBusMessage message;
+ message.d->type = DBUS_MESSAGE_TYPE_ERROR;
+ message.d->name = error.name();
+ message << error.message();
return message;
}
+/*!
+ Returns the path of the object that this message is being sent to (in the case of a
+ method call) or being received from (for a signal).
+*/
QString QDBusMessage::path() const
{
return d->path;
}
+/*!
+ Returns the interface of the method being called (in the case of a method call) or of
+ the signal being received from.
+*/
QString QDBusMessage::interface() const
{
return d->interface;
}
+/*!
+ Returns the name of the signal that was emitted or the name of the error that was
+ received.
+ \sa member
+*/
QString QDBusMessage::name() const
{
return d->name;
}
-QString QDBusMessage::sender() const
+/*!
+ \fn QDBusMessage::member
+ Returns the name of the method being called.
+*/
+
+/*!
+ \fn QDBusMessage::method
+ \overload
+ Returns the name of the method being called.
+*/
+
+/*!
+ Returns the name of the service or the bus address of the remote method call.
+*/
+QString QDBusMessage::service() const
{
- return d->sender;
+ return d->service;
}
+/*!
+ \fn QDBusMessage::sender
+ Returns the unique name of the remote sender.
+*/
+
+/*!
+ Returns the timeout (in milliseconds) for this message to be processed.
+*/
int QDBusMessage::timeout() const
{
return d->timeout;
}
+/*!
+ Sets the timeout for this message to be processed.
+
+ \param ms the time, in milliseconds
+*/
void QDBusMessage::setTimeout(int ms)
{
d->timeout = ms;
@@ -205,6 +396,18 @@ int QDBusMessage::replySerialNumber() const
return dbus_message_get_reply_serial(d->msg);
}
+/*!
+ Returns the signature of the signal that was received or for the output arguments
+ of a method call.
+*/
+QString QDBusMessage::signature() const
+{
+ return d->signature;
+}
+
+/*!
+ Returns the message type.
+*/
QDBusMessage::MessageType QDBusMessage::type() const
{
switch (d->type) {
@@ -222,11 +425,32 @@ QDBusMessage::MessageType QDBusMessage::type() const
}
#ifndef QT_NO_DEBUG
+QDebug operator<<(QDebug dbg, QDBusMessage::MessageType t)
+{
+ switch (t)
+ {
+ case QDBusMessage::MethodCallMessage:
+ return dbg << "MethodCall";
+ case QDBusMessage::ReplyMessage:
+ return dbg << "MethodReturn";
+ case QDBusMessage::SignalMessage:
+ return dbg << "Signal";
+ case QDBusMessage::ErrorMessage:
+ return dbg << "Error";
+ default:
+ return dbg << "Invalid";
+ }
+}
+
QDebug operator<<(QDebug dbg, const QDBusMessage &msg)
{
- dbg.nospace() << "QDBusMessage(" << msg.path() << ", " << msg.interface() << ", "
- << msg.name() << ", " << msg.sender() << ", "
- << static_cast<QList<QVariant> >(msg) << ")";
+ dbg.nospace() << "QDBusMessage(type=" << msg.type()
+ << ", service=" << msg.service()
+ << ", path=" << msg.path()
+ << ", interface=" << msg.interface()
+ << ", name=" << msg.name()
+ << ", signature=" << msg.signature()
+ << ", contents=" << static_cast<QList<QVariant> >(msg) << ")";
return dbg.space();
}
#endif
diff --git a/qt/qdbusmessage.h b/qt/qdbusmessage.h
index 03a475eb..6c480444 100644
--- a/qt/qdbusmessage.h
+++ b/qt/qdbusmessage.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,22 @@
* 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.
*
*/
#ifndef QDBUSMESSAGE_H
#define QDBUSMESSAGE_H
-#include "dbus/qdbus.h"
-
+#include "qdbusmacros.h"
#include <QtCore/qlist.h>
#include <QtCore/qvariant.h>
#include <limits.h>
class QDBusMessagePrivate;
+class QDBusError;
struct DBusMessage;
class QDBUS_EXPORT QDBusMessage: public QList<QVariant>
@@ -49,23 +51,32 @@ public:
static QDBusMessage signal(const QString &path, const QString &interface,
const QString &name);
- static QDBusMessage methodCall(const QString &service, const QString &path,
- const QString &interface, const QString &method);
+ static QDBusMessage methodCall(const QString &destination, const QString &path,
+ const QString &interface, const QString &method,
+ const QString &signature = QString());
static QDBusMessage methodReply(const QDBusMessage &other);
+ static QDBusMessage error(const QDBusMessage &other, const QString &name,
+ const QString &message = QString());
+ static QDBusMessage error(const QDBusMessage &other, const QDBusError &error);
QString path() const;
QString interface() const;
- QString name() const; //rename to member?
- QString sender() const; //rename to service?
+ QString name() const;
+ inline QString member() const { return name(); }
+ inline QString method() const { return name(); }
+ QString service() const;
+ inline QString sender() const { return service(); }
MessageType type() const;
int timeout() const;
void setTimeout(int ms);
+ QString signature() const;
//protected:
DBusMessage *toDBusMessage() const;
static QDBusMessage fromDBusMessage(DBusMessage *dmsg);
+ 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 9655c9d9..9c48b084 100644
--- a/qt/qdbusmessage_p.h
+++ b/qt/qdbusmessage_p.h
@@ -1,6 +1,8 @@
/* qdbusmessage.h QDBusMessage private 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,17 +17,16 @@
* 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.
*
*/
#ifndef QDBUSMESSAGE_P_H
#define QDBUSMESSAGE_P_H
-#include <QtCore/qatomic.h>
-#include <QtCore/qstring.h>
-
+#include <qatomic.h>
+#include <qstring.h>
struct DBusMessage;
class QDBusMessagePrivate
@@ -34,7 +35,7 @@ public:
QDBusMessagePrivate(QDBusMessage *qq);
~QDBusMessagePrivate();
- QString path, interface, name, service, method, sender;
+ QString service, path, interface, name, message, signature;
DBusMessage *msg;
DBusMessage *reply;
QDBusMessage *q;
diff --git a/qt/qdbusobject.cpp b/qt/qdbusobject.cpp
new file mode 100644
index 00000000..139154e8
--- /dev/null
+++ b/qt/qdbusobject.cpp
@@ -0,0 +1,173 @@
+/* -*- C++ -*-
+ *
+ * Copyright (C) 2005 Thiago Macieira <thiago@kde.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
+ *
+ * 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 "qdbusmessage.h"
+#include "qdbusconnection.h"
+#include "qdbusobject.h"
+#include "qdbusinterface.h"
+#include "qdbusstandardinterfaces.h"
+#include "qdbuserror.h"
+
+#include "qdbusxmlparser_p.h"
+#include "qdbusobject_p.h"
+#include "qdbusutil.h"
+
+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);
+}
+
+QDBusObject::QDBusObject(const QDBusInterface& iface)
+ : m_conn(iface.connection())
+{
+ *this = m_conn.findObject(iface.service(), iface.path());
+}
+
+QDBusObject::QDBusObject(const QDBusObject& other)
+ : d(other.d), m_conn(other.m_conn)
+{
+}
+
+QDBusObject::~QDBusObject()
+{
+}
+
+QDBusObject& QDBusObject::operator=(const QDBusObject& other)
+{
+#if 0
+ if (other.d)
+ other.d->ref.ref();
+
+ QDBusObjectPrivate* old = qAtomicSetPtr(&d, other.d);
+ if (old && !old->ref.deref())
+ m_conn.d->disposeOf(d);
+#endif
+ d = other.d;
+
+ return *this;
+}
+
+QDBusConnection QDBusObject::connection() const
+{
+ return m_conn;
+}
+
+QString QDBusObject::service() const
+{
+ return d ? d->data->service : QString();
+}
+
+QString QDBusObject::path() const
+{
+ return d ? d->data->path : QString();
+}
+
+QString QDBusObject::introspect() const
+{
+ if (!d)
+ // not connected
+ return QString();
+
+ if (d->data->introspection.isNull()) {
+ // Try to introspect
+ QDBusIntrospectableInterface iface = *this;
+ QString xml = iface.introspect();
+
+ if (!m_conn.lastError().isValid()) {
+ // this will change the contents of d->data
+ QDBusXmlParser::parse(d, xml);
+ }
+ }
+ return d->data->introspection;
+}
+
+QSharedDataPointer<QDBusIntrospection::Object> QDBusObject::introspectionData() const
+{
+ QSharedDataPointer<QDBusIntrospection::Object> retval;
+ if (d)
+ retval = const_cast<QDBusIntrospection::Object*>(d->data);
+ return retval;
+}
+
+QStringList QDBusObject::interfaces() const
+{
+ introspect();
+ return d ? d->data->interfaces : QStringList();
+}
+
+QMap<QString, QDBusObject> QDBusObject::children() const
+{
+ QMap<QString, QDBusObject> retval;
+#if 0
+ if (!d)
+ return retval;
+
+ QString prefix = d->path;
+ if (!prefix.endsWith('/'))
+ prefix.append('/');
+ foreach (QString sub, d->childObjects)
+ retval.insert(sub, QDBusObject( m_conn.d->findObject(d->path, prefix + sub), m_conn ));
+
+ return retval;
+#endif
+ qFatal("fixme!");
+ return retval;
+}
+
+bool QDBusObject::isValid() const
+{
+ return d && m_conn.isConnected() && QDBusUtil::isValidBusName(d->data->service) &&
+ QDBusUtil::isValidObjectPath(d->data->path);
+}
+
+#if 0 // we don't have a way of determining if an object exists or not
+bool QDBusObject::exists() const
+{
+ if (!isValid())
+ return false;
+
+ // call a non-existant interface/method
+ QDBusMessage msg = QDBusMessage::methodCall(d->service, d->path,
+ "org.freedesktop.DBus.NonExistant", "NonExistant");
+ QDBusMessage reply = m_conn.sendWithReply(msg);
+ // ignore the reply
+
+ QDBusError err = m_conn.lastError();
+ if (!err.isValid()) {
+ qWarning("D-Bus call to %s:%s on a supposedly non-existant interface worked!",
+ qPrintable(d->service), qPrintable(d->path));
+ return true;
+ }
+
+ if (err.name == DBUS_ERROR_SERVICE_UNKNOWN ||
+ err.name == DBUS_ERROR_BAD_ADDRESS
+ return !m_conn.lastError().isValid();
+}
+#endif
diff --git a/qt/qdbusobject.h b/qt/qdbusobject.h
new file mode 100644
index 00000000..ffb41add
--- /dev/null
+++ b/qt/qdbusobject.h
@@ -0,0 +1,161 @@
+/* -*- C++ -*-
+ *
+ * Copyright (C) 2005 Thiago Macieira <thiago@kde.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
+ *
+ * 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 QDBUSOBJECT_H
+#define QDBUSOBJECT_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qshareddata.h>
+
+#include "qdbusconnection.h"
+#include "qdbusintrospection.h"
+
+class QDBusInterface;
+class QDBusObject;
+
+template<class Interface>
+inline Interface qdbus_cast(QDBusObject& obj, Interface * = 0);
+
+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.
+ */
+ 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); }
+
+private:
+ QDBusObject(QDBusObjectPrivate*, const QDBusConnection& conn);
+ QSharedDataPointer<QDBusObjectPrivate> d;
+ QDBusConnection m_conn;
+};
+
+template<class Interface>
+inline Interface qdbus_cast(QDBusObject& obj, Interface *)
+{
+ return Interface(obj);
+}
+
+template<class Interface>
+inline const Interface qdbus_cast(const QDBusObject& obj, Interface *)
+{
+ return Interface(obj);
+}
+
+#endif // QDBUSOBJECT_H
diff --git a/qt/qdbusobject_p.h b/qt/qdbusobject_p.h
new file mode 100644
index 00000000..181fceb8
--- /dev/null
+++ b/qt/qdbusobject_p.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * 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 QDBUSOBJECTPRIVATE_H
+#define QDBUSOBJECTPRIVATE_H
+
+#include "QtCore/qatomic.h"
+#include "QtCore/qstringlist.h"
+#include "qdbusobject.h"
+#include "qdbusinterface.h"
+#include "qdbusconnection_p.h"
+
+class QDBusObject;
+class QDBusInterface;
+class QDBusXmlParser;
+
+class QDBusObjectPrivate: public QSharedData
+{
+public:
+ inline QDBusObjectPrivate(QDBusConnectionPrivate* ptr, const QString &service,
+ const QString &path)
+ : parent(ptr),
+ data( )
+ {
+ QDBusIntrospection::Object * d = ptr->findObject(service, path);
+ d->ref.ref();
+ data = d;
+ }
+
+ inline ~QDBusObjectPrivate()
+ { parent->disposeOf(this); }
+
+ QDBusConnectionPrivate* parent;
+ const QDBusIntrospection::Object* data;
+};
+
+#endif
diff --git a/qt/qdbusserver.cpp b/qt/qdbusserver.cpp
index 08fea26d..b3b9835e 100644
--- a/qt/qdbusserver.cpp
+++ b/qt/qdbusserver.cpp
@@ -1,6 +1,8 @@
/* qdbusserver.cpp
*
* 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,8 +17,8 @@
* 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.
*
*/
diff --git a/qt/qdbusserver.h b/qt/qdbusserver.h
index bc191a28..55607860 100644
--- a/qt/qdbusserver.h
+++ b/qt/qdbusserver.h
@@ -23,7 +23,7 @@
#ifndef QDBUSSERVER_H
#define QDBUSSERVER_H
-#include "dbus/qdbus.h"
+#include "qdbusmacros.h"
#include <QtCore/qobject.h>
#include <QtCore/qstring.h>
diff --git a/qt/qdbusstandardinterfaces.cpp b/qt/qdbusstandardinterfaces.cpp
new file mode 100644
index 00000000..f8ea7e17
--- /dev/null
+++ b/qt/qdbusstandardinterfaces.cpp
@@ -0,0 +1,114 @@
+/* -*- C++ -*-
+ *
+ * Copyright (C) 2005 Thiago Macieira <thiago@kde.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
+ *
+ * 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 "qdbusstandardinterfaces.h"
+
+QDBusPeerInterface::~QDBusPeerInterface()
+{
+}
+
+QDBusIntrospectableInterface::~QDBusIntrospectableInterface()
+{
+}
+
+QDBusPropertiesInterface::~QDBusPropertiesInterface()
+{
+}
+
+QDBusBusInterface::~QDBusBusInterface()
+{
+}
+
+const char* QDBusBusInterface::staticIntrospectionData()
+{
+ // FIXME!
+ // This should be auto-generated!
+
+ return
+ "<interface name=\"org.freedesktop.DBus\">"
+ "<method name=\"RequestName\">"
+ "<arg direction=\"in\" type=\"s\"/>"
+ "<arg direction=\"in\" type=\"u\"/>"
+ "<arg direction=\"out\" type=\"u\"/>"
+ "</method>"
+ "<method name=\"ReleaseName\">"
+ "<arg direction=\"in\" type=\"s\"/>"
+ "<arg direction=\"out\" type=\"u\"/>"
+ "</method>"
+ "<method name=\"StartServiceByName\">"
+ "<arg direction=\"in\" type=\"s\"/>"
+ "<arg direction=\"in\" type=\"u\"/>"
+ "<arg direction=\"out\" type=\"u\"/>"
+ "</method>"
+ "<method name=\"Hello\">"
+ "<arg direction=\"out\" type=\"s\"/>"
+ "</method>"
+ "<method name=\"NameHasOwner\">"
+ "<arg direction=\"in\" type=\"s\"/>"
+ "<arg direction=\"out\" type=\"b\"/>"
+ "</method>"
+ "<method name=\"ListNames\">"
+ "<arg direction=\"out\" type=\"as\"/>"
+ "</method>"
+ "<method name=\"AddMatch\">"
+ "<arg direction=\"in\" type=\"s\"/>"
+ "</method>"
+ "<method name=\"RemoveMatch\">"
+ "<arg direction=\"in\" type=\"s\"/>"
+ "</method>"
+ "<method name=\"GetNameOwner\">"
+ "<arg direction=\"in\" type=\"s\"/>"
+ "<arg direction=\"out\" type=\"s\"/>"
+ "</method>"
+ "<method name=\"ListQueuedOwners\">"
+ "<arg direction=\"in\" type=\"s\"/>"
+ "<arg direction=\"out\" type=\"as\"/>"
+ "</method>"
+ "<method name=\"GetConnectionUnixUser\">"
+ "<arg direction=\"in\" type=\"s\"/>"
+ "<arg direction=\"out\" type=\"u\"/>"
+ "</method>"
+ "<method name=\"GetConnectionUnixProcessID\">"
+ "<arg direction=\"in\" type=\"s\"/>"
+ "<arg direction=\"out\" type=\"u\"/>"
+ "</method>"
+ "<method name=\"GetConnectionSELinuxSecurityContext\">"
+ "<arg direction=\"in\" type=\"s\"/>"
+ "<arg direction=\"out\" type=\"ay\"/>"
+ "</method>"
+ "<method name=\"ReloadConfig\">"
+ "</method>"
+ "<signal name=\"NameOwnerChanged\">"
+ "<arg type=\"s\"/>"
+ "<arg type=\"s\"/>"
+ "<arg type=\"s\"/>"
+ "</signal>"
+ "<signal name=\"NameLost\">"
+ "<arg type=\"s\"/>"
+ "</signal>"
+ "<signal name=\"NameAcquired\">"
+ "<arg type=\"s\"/>"
+ "</signal>"
+ "</interface>";
+}
diff --git a/qt/qdbusstandardinterfaces.h b/qt/qdbusstandardinterfaces.h
new file mode 100644
index 00000000..af2c8dde
--- /dev/null
+++ b/qt/qdbusstandardinterfaces.h
@@ -0,0 +1,218 @@
+/* -*- C++ -*-
+ *
+ * Copyright (C) 2005 Thiago Macieira <thiago@kde.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
+ *
+ * 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 QDBUS_STANDARD_INTERFACES_H
+#define QDBUS_STANDARD_INTERFACES_H
+
+#include "qdbusinterface.h"
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <dbus/dbus.h>
+
+class QDBusConnection;
+
+class QDBUS_EXPORT QDBusPeerInterface: public QDBusInterface
+{
+public:
+ static inline const char* staticInterfaceName()
+ { return DBUS_INTERFACE_PEER; }
+
+ static inline const char* staticIntrospectionData()
+ {
+ return
+ "<interface name=\"org.freedesktop.DBus.Peer\">"
+ "<method name=\"Ping\" />"
+ "</interface>";
+ }
+
+public:
+ explicit QDBusPeerInterface(const QDBusObject& obj)
+ : QDBusInterface(obj, staticInterfaceName())
+ { }
+
+ QDBusPeerInterface(QDBusConnection& conn, const QString& service, const QString& path)
+ : QDBusInterface(conn, service, path, staticInterfaceName())
+ { }
+
+ ~QDBusPeerInterface();
+
+ inline virtual QString introspectionData() const
+ { return staticIntrospectionData(); }
+
+ inline void ping()
+ { call(QLatin1String("Ping")); }
+};
+
+class QDBUS_EXPORT QDBusIntrospectableInterface: public QDBusInterface
+{
+public:
+ static inline const char* staticInterfaceName()
+ { return DBUS_INTERFACE_INTROSPECTABLE; }
+
+ 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>";
+ }
+public:
+ explicit QDBusIntrospectableInterface(const QDBusObject& obj)
+ : QDBusInterface(obj, staticInterfaceName())
+ { }
+
+ QDBusIntrospectableInterface(QDBusConnection& conn, const QString& service, const QString& path)
+ : QDBusInterface(conn, service, path, staticInterfaceName())
+ { }
+
+ ~QDBusIntrospectableInterface();
+
+ inline virtual QString introspectionData() const
+ { return staticIntrospectionData(); }
+
+ inline QString introspect()
+ { return call(QLatin1String("Introspect")).at(0).toString(); }
+};
+
+class QDBUS_EXPORT QDBusPropertiesInterface: public QDBusInterface
+{
+public:
+ static inline const char* staticInterfaceName()
+ { return DBUS_INTERFACE_PROPERTIES; }
+
+ 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>";
+ }
+public:
+ explicit QDBusPropertiesInterface(const QDBusObject& obj)
+ : QDBusInterface(obj, staticInterfaceName())
+ { }
+
+ QDBusPropertiesInterface(QDBusConnection& conn, const QString& service, const QString& path)
+ : QDBusInterface(conn, service, path, staticInterfaceName())
+ { }
+
+ ~QDBusPropertiesInterface();
+
+ inline virtual QString introspectionData() const
+ { return staticIntrospectionData(); }
+
+ inline void set(const QString& interfaceName, const QString& propertyName, QVariant value)
+ { 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); }
+};
+
+class QDBUS_EXPORT QDBusBusInterface: public QDBusInterface
+{
+public:
+ static inline const char* staticInterfaceName()
+ { return DBUS_INTERFACE_DBUS; }
+
+ static const char* staticIntrospectionData();
+
+public:
+ explicit QDBusBusInterface(const QDBusObject& obj)
+ : 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 unsigned releaseName(const QString& name)
+ { return call(QLatin1String("ReleaseName.s"), name).at(0).toUInt(); }
+
+ inline unsigned startServiceByName(const QString& name, unsigned flags)
+ { return call(QLatin1String("StartServiceByName.su"), name, flags).at(0).toUInt(); }
+
+ inline QString Hello()
+ { return call(QLatin1String("Hello")).at(0).toString(); }
+
+ inline bool nameHasOwner(const QString& name)
+ { return call(QLatin1String("NameHasOwner.s"), name).at(0).toBool(); }
+
+ inline QStringList listNames()
+ { return call(QLatin1String("ListNames")).at(0).toStringList(); }
+
+ inline void addMatch(const QString& rule)
+ { call(QLatin1String("AddMatch"), rule); }
+
+ inline void removeMatch(const QString& rule)
+ { call(QLatin1String("RemoveMatch"), rule); }
+
+ inline QString getNameOwner(const QString& name)
+ { return call(QLatin1String("GetNameOwner.s"), name).at(0).toString(); }
+
+ inline QStringList listQueuedOwners(const QString& name)
+ { return call(QLatin1String("ListQueuedOwners.s"), name).at(0).toStringList(); }
+
+ inline quint32 getConnectionUnixUser(const QString& connectionName)
+ { return call(QLatin1String("GetConnectionUnixUser.s"), connectionName).at(0).toUInt(); }
+
+ inline quint32 getConnectionUnixProcessID(const QString& connectionName)
+ { return call(QLatin1String("GetConnectionUnixProcessID.s"), connectionName).at(0).toUInt(); }
+
+ inline QByteArray getConnectionSELinuxSecurityContext(const QString& connectionName)
+ { return call(QLatin1String("GetConnectionSELinuxSecurityContext.s"), connectionName).at(0).toByteArray(); }
+
+ inline void reloadConfig()
+ { call(QLatin1String("ReloadConfig")); }
+};
+
+
+namespace org {
+ namespace freedesktop {
+ namespace DBus {
+ typedef ::QDBusPeerInterface Peer;
+ typedef ::QDBusIntrospectableInterface Introspectable;
+ typedef ::QDBusPropertiesInterface Properties;
+ }
+ }
+}
+
+#endif
diff --git a/qt/qdbusthread.cpp b/qt/qdbusthread.cpp
new file mode 100644
index 00000000..f45a0096
--- /dev/null
+++ b/qt/qdbusthread.cpp
@@ -0,0 +1,116 @@
+/* qdbusintegrator.cpp QDBusConnection private implementation
+ *
+ * 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
+ *
+ * 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 <QtCore/qmutex.h>
+#include <QtCore/qwaitcondition.h>
+
+#include <dbus/dbus.h>
+
+struct DBusMutex: public QMutex
+{
+ inline DBusMutex()
+ : QMutex( QMutex::NonRecursive )
+ { }
+
+ static DBusMutex* mutex_new()
+ {
+ return new DBusMutex;
+ }
+
+ static void mutex_free(DBusMutex *mutex)
+ {
+ delete mutex;
+ }
+
+ static dbus_bool_t mutex_lock(DBusMutex *mutex)
+ {
+ mutex->lock();
+ return true;
+ }
+
+ static dbus_bool_t mutex_unlock(DBusMutex *mutex)
+ {
+ mutex->unlock();
+ return true;
+ }
+};
+
+struct DBusCondVar: public QWaitCondition
+{
+ inline DBusCondVar()
+ { }
+
+ static DBusCondVar* condvar_new()
+ {
+ return new DBusCondVar;
+ }
+
+ static void condvar_free(DBusCondVar *cond)
+ {
+ delete cond;
+ }
+
+ static void condvar_wait(DBusCondVar *cond, DBusMutex *mutex)
+ {
+ cond->wait(mutex);
+ }
+
+ static dbus_bool_t condvar_wait_timeout(DBusCondVar *cond, DBusMutex *mutex, int msec)
+ {
+ return cond->wait(mutex, msec);
+ }
+
+ static void condvar_wake_one(DBusCondVar *cond)
+ {
+ cond->wakeOne();
+ }
+
+ static void condvar_wake_all(DBusCondVar *cond)
+ {
+ cond->wakeAll();
+ }
+};
+
+bool qDBusInitThreads()
+{
+ static DBusThreadFunctions fcn = {
+ DBUS_THREAD_FUNCTIONS_ALL_MASK,
+ DBusMutex::mutex_new,
+ DBusMutex::mutex_free,
+ DBusMutex::mutex_lock,
+ DBusMutex::mutex_unlock,
+ DBusCondVar::condvar_new,
+ DBusCondVar::condvar_free,
+ DBusCondVar::condvar_wait,
+ DBusCondVar::condvar_wait_timeout,
+ DBusCondVar::condvar_wake_one,
+ DBusCondVar::condvar_wake_all,
+ 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ dbus_threads_init(&fcn);
+ return true;
+}
+
+
diff --git a/qt/qdbustype.cpp b/qt/qdbustype.cpp
new file mode 100644
index 00000000..036bbe13
--- /dev/null
+++ b/qt/qdbustype.cpp
@@ -0,0 +1,1151 @@
+/* -*- C++ -*-
+ *
+ * Copyright (C) 2005 Thiago Macieira <thiago@kde.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
+ *
+ * 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 "qdbustype.h"
+#include "qdbusvariant.h"
+#include <dbus/dbus.h>
+
+#include <QtCore/qstringlist.h>
+
+/// \internal
+class QDBusPrettyTypeBase
+{
+public:
+ struct Entry
+ {
+ const char* prettyName;
+ char signature;
+ };
+
+ enum Direction
+ {
+ In,
+ Out
+ };
+
+ enum Access
+ {
+ Read,
+ Write,
+ ReadWrite
+ };
+
+ // so that the compiler doesn't complain
+ virtual ~QDBusPrettyTypeBase() { }
+
+ virtual QString addElementsToArray(const QString& subType) = 0;
+ virtual QString addElementsToMap(const QString& key, const QString& value) = 0;
+ virtual QString addElementsToStruct(const QStringList& subTypes) = 0;
+ virtual const Entry* entryMap() = 0;
+
+ QString toString(const QDBusType& type);
+ QString toString(const QDBusTypeList& list);
+};
+
+/// \internal
+class QDBusConventionalNames: public QDBusPrettyTypeBase
+{
+public:
+ virtual QString addElementsToArray(const QString& subType);
+ virtual QString addElementsToMap(const QString& key, const QString& value);
+ virtual QString addElementsToStruct(const QStringList& subTypes) ;
+ virtual const Entry* entryMap();
+};
+
+/// \internal
+class QDBusQtNames: public QDBusPrettyTypeBase
+{
+public:
+ virtual QString addElementsToArray(const QString& subType);
+ virtual QString addElementsToMap(const QString& key, const QString& value);
+ virtual QString addElementsToStruct(const QStringList& subTypes) ;
+ virtual const Entry* entryMap();
+};
+
+//! \internal
+class QDBusQVariantNames: public QDBusQtNames
+{
+public:
+ virtual QString addElementsToArray(const QString& subType);
+ virtual QString addElementsToMap(const QString& key, const QString& value);
+ 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)
+ if (type == map->signature)
+ return QLatin1String(map->prettyName);
+ return QString();
+}
+
+//
+// Input MUST be valid
+//
+inline QString QDBusPrettyTypeBase::toString(const QDBusType& type)
+{
+ const Entry* map = entryMap();
+
+ const QDBusTypeList subTypes = type.subTypes();
+ switch (type.dbusType()) {
+ case DBUS_TYPE_STRUCT: {
+ // handle a struct
+ // find its sub-types
+
+ QStringList subStrings;
+ QDBusTypeList subTypes = type.subTypes();
+ foreach (QDBusType t, subTypes)
+ subStrings << toString( t );
+
+ return addElementsToStruct(subStrings);
+ }
+
+ 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 );
+ }
+ case DBUS_TYPE_ARRAY: {
+ Q_ASSERT_X(subTypes.size() == 1, "QDBusType::toString",
+ "more than one element in array");
+
+ if (type.qvariantType() == QVariant::Map)
+ return toString( subTypes.first() );
+ return addElementsToArray( toString( subTypes.at(0) ) );
+ }
+
+ default: {
+ // normal, non-compound type
+ QString name = findInMap(type.dbusType(), map);
+ Q_ASSERT(!name.isNull());
+ return name;
+ }
+ }
+}
+
+const QDBusPrettyTypeBase::Entry* QDBusConventionalNames::entryMap()
+{
+ static QDBusPrettyTypeBase::Entry translation[] = {
+ { "BYTE", DBUS_TYPE_BYTE },
+ { "BOOLEAN", DBUS_TYPE_BOOLEAN },
+ { "INT16", DBUS_TYPE_INT16 },
+ { "UINT16", DBUS_TYPE_UINT16 },
+ { "INT32", DBUS_TYPE_INT32 },
+ { "UINT32", DBUS_TYPE_UINT32 },
+ { "INT64", DBUS_TYPE_INT64 },
+ { "UINT64", DBUS_TYPE_UINT64 },
+ { "DOUBLE", DBUS_TYPE_DOUBLE },
+ { "STRING", DBUS_TYPE_STRING },
+ { "OBJECT_PATH", DBUS_TYPE_OBJECT_PATH },
+ { "SIGNATURE", DBUS_TYPE_SIGNATURE },
+ { "VARIANT", DBUS_TYPE_VARIANT }
+ };
+ return translation;
+}
+
+QString QDBusConventionalNames::addElementsToStruct(const QStringList& subTypes)
+{
+ return QString( QLatin1String("STRUCT of (%1)") )
+ .arg( subTypes.join( QLatin1String(",") ) );
+}
+
+QString QDBusConventionalNames::addElementsToMap(const QString& key, const QString& value)
+{
+ return QString( QLatin1String("ARRAY of DICT_ENTRY of (%1,%2)") )
+ .arg(key).arg(value);
+}
+
+QString QDBusConventionalNames::addElementsToArray(const QString& subType)
+{
+ return QString( QLatin1String("ARRAY of %1") )
+ .arg(subType);
+}
+
+const QDBusPrettyTypeBase::Entry* QDBusQtNames::entryMap()
+{
+ static QDBusPrettyTypeBase::Entry translation[] = {
+ { "quint8", 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 },
+ { "double", DBUS_TYPE_DOUBLE },
+ { "QString", DBUS_TYPE_STRING },
+ { "QString", DBUS_TYPE_OBJECT_PATH },
+ { "QString", DBUS_TYPE_SIGNATURE },
+ { "QDBusVariant", DBUS_TYPE_VARIANT }
+ };
+ return translation;
+}
+
+static inline QString templateArg(const QString& input)
+{
+ if (input.endsWith('>'))
+ return input + ' ';
+ return input;
+}
+
+QString QDBusQtNames::addElementsToStruct(const QStringList& subTypes)
+{
+ Q_UNUSED(subTypes);
+
+ return QLatin1String("QList"); // CHANGEME in the future
+}
+
+QString QDBusQtNames::addElementsToMap(const QString& key, const QString& value)
+{
+ return QString( QLatin1String("QMap<%1, %2>") )
+ .arg(key)
+ .arg( templateArg(value) );
+}
+
+QString QDBusQtNames::addElementsToArray(const QString& subType)
+{
+ if (subType == QLatin1String("quint8"))
+ // special case
+ return QLatin1String("QByteArray");
+
+ return QString( QLatin1String("QList<%1>") )
+ .arg( templateArg(subType) );
+}
+
+QString QDBusQVariantNames::addElementsToStruct(const QStringList& subTypes)
+{
+ Q_UNUSED(subTypes);
+
+ return QLatin1String("QVariantList");
+}
+
+QString QDBusQVariantNames::addElementsToMap(const QString& key, const QString& value)
+{
+ Q_UNUSED(key);
+ Q_UNUSED(value);
+
+ return QLatin1String("QVariantMap");
+}
+
+QString QDBusQVariantNames::addElementsToArray(const QString& subType)
+{
+ if (subType == QLatin1String("quint8"))
+ // special case
+ return QLatin1String("QByteArray");
+
+ return QLatin1String("QVariantList");
+}
+
+/*!
+ \internal
+*/
+class QDBusTypePrivate: public QSharedData
+{
+public:
+ int code;
+ mutable QVariant::Type qvariantType;
+ mutable QByteArray signature;
+ QDBusTypeList subTypes;
+
+ inline QDBusTypePrivate()
+ : code(0), qvariantType(QVariant::Invalid)
+ { }
+};
+
+/*!
+ \class QDBusType
+
+ Represents one single DBus type.
+*/
+
+/*!
+ \enum QDBusType::StringFormat
+
+ This enum is used in QDBusType::toString to determine which type of formatting
+ to apply to the DBus types:
+
+ \value ConventionalNames Use the DBus conventional names, such as STRING, BOOLEAN or
+ ARRAY of BYTE.
+ \value QtNames Use the Qt type names, such as QString, bool and QList<quint32>
+ \value QVariantNames Same as QtNames, but for containers, use QVariantList and QVariantMap
+*/
+
+/*!
+ Constructs an empty (invalid) type.
+*/
+QDBusType::QDBusType()
+ : d(0)
+{
+}
+
+/*!
+ Constructs the type based on the given DBus type.
+
+ \param type the type
+*/
+QDBusType::QDBusType(int type)
+{
+ char c[2] = { type, 0 };
+ *this = QDBusType(c);
+}
+
+/*!
+ Constructs the type based on the given QVariant type.
+
+ \param type the type
+ \sa QVariant::Type
+*/
+QDBusType::QDBusType(QVariant::Type type)
+{
+ const char *sig = dbusSignature(type);
+
+ // it never returns NULL
+ if (sig[0] == '\0')
+ return;
+
+ d = new QDBusTypePrivate;
+ d->qvariantType = type;
+ d->code = sig[0];
+ if (sig[1] == '\0')
+ // single-letter type
+ return;
+ else if (sig[2] == '\0') {
+ // two-letter type
+ // must be an array
+ d->code = sig[0];
+ QDBusType t;
+ t.d = new QDBusTypePrivate;
+ t.d->code = sig[1];
+ d->subTypes << t;
+ }
+ else {
+ // the only longer type is "a{sv}"
+ Q_ASSERT(sig[1] == '{' && sig[5] == '\0');
+
+ static QDBusType map("a{sv}");
+ d->subTypes = map.d->subTypes;
+ }
+}
+
+/*!
+ 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
+ a container type.
+*/
+QDBusType::QDBusType(const char* signature)
+{
+ if ( !dbus_signature_validate_single(signature, 0) )
+ return;
+
+ DBusSignatureIter iter;
+ dbus_signature_iter_init(&iter, signature);
+ *this = QDBusType(&iter);
+ if (d)
+ d->signature = 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
+ a container type.
+*/
+QDBusType::QDBusType(const QString& str)
+{
+ *this = QDBusType( str.toUtf8().constData() );
+}
+
+/*!
+ 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
+ a container type.
+*/
+QDBusType::QDBusType(const QByteArray& str)
+{
+ *this = QDBusType( str.constData() );
+}
+
+/*!
+ Creates a QDBusType object based on the current element pointed to by \a iter.
+
+ \param iter the iterator. Can be pointing to container types.
+*/
+QDBusType::QDBusType(DBusSignatureIter* iter)
+ : d(new QDBusTypePrivate)
+{
+ if ( dbus_type_is_container( d->code = dbus_signature_iter_get_current_type(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);
+
+ d->subTypes = QDBusTypeList(&subiter);
+
+ // sanity checking:
+ if ( d->code == DBUS_TYPE_ARRAY )
+ Q_ASSERT_X(d->subTypes.size() == 1, "QDBusType",
+ "more than one element in array");
+ else if (d->code == DBUS_TYPE_DICT_ENTRY )
+ Q_ASSERT_X(d->subTypes.size() == 2, "QDBusType",
+ "maps must have exactly two elements");
+ }
+}
+
+/*!
+ Copies the type from the other object.
+*/
+QDBusType::QDBusType(const QDBusType& other)
+ : d(other.d)
+{
+}
+
+/*!
+ Release the resources associated with this type.
+*/
+QDBusType::~QDBusType()
+{
+}
+
+/*!
+ Copies the type from the other object.
+*/
+QDBusType& QDBusType::operator=(const QDBusType& other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Returns the DBus type for this type.
+*/
+int QDBusType::dbusType() const
+{
+ return d ? d->code : DBUS_TYPE_INVALID;
+}
+
+/*!
+ Returns the DBus signature for this type and subtypes.
+*/
+QByteArray QDBusType::dbusSignature() const
+{
+ if (!d)
+ return QByteArray();
+
+ if (!d->signature.isEmpty())
+ return d->signature;
+
+ if (d->subTypes.isEmpty())
+ return d->signature = QByteArray(1, d->code);
+
+ 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;
+
+ case DBUS_TYPE_DICT_ENTRY: {
+ Q_ASSERT_X(d->subTypes.size() == 2, "QDBusType::dbusSignature",
+ "maps must have exactly two elements");
+
+ QByteArray value = d->subTypes.at(1).dbusSignature();
+ char key = d->subTypes.at(0).dbusType();
+
+ 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;
+ retval += value;
+ retval += DBUS_DICT_ENTRY_END_CHAR;
+ break;
+ }
+
+ case DBUS_TYPE_STRUCT:
+ retval = d->subTypes.dbusSignature();
+ retval.prepend(DBUS_STRUCT_BEGIN_CHAR);
+ retval.append(DBUS_STRUCT_END_CHAR);
+ break;
+
+ default:
+ Q_ASSERT_X(false, "QDBusType::dbusSignature", "invalid container type");
+ }
+
+ d->signature = retval;
+ return retval;
+}
+
+/*!
+ Returns the QVariant::Type for this entry.
+*/
+QVariant::Type QDBusType::qvariantType() const
+{
+ if (d && d->qvariantType != QVariant::Invalid)
+ return d->qvariantType;
+
+ // check the special array cases:
+ if (isArray()) {
+ QDBusType t = arrayElement();
+
+ if (t.dbusType() == DBUS_TYPE_BYTE)
+ return QVariant::ByteArray;
+ else if (t.dbusType() == DBUS_TYPE_DICT_ENTRY)
+ return QVariant::Map;
+ else if (t.isBasic() && t.qvariantType() == QVariant::String)
+ return QVariant::StringList;
+ }
+
+ return qvariantType(dbusType());
+}
+
+/*!
+ Returns true if this type is a valid one.
+*/
+bool QDBusType::isValid() const
+{
+ return d && d->code != DBUS_TYPE_INVALID;
+}
+
+/*!
+ Returns true if this type is a basic one.
+
+ \sa dbus_type_is_basic
+*/
+bool QDBusType::isBasic() const
+{
+ return d && dbus_type_is_basic(d->code);
+}
+
+/*!
+ Returns true if this type is a container.
+
+ \sa dbus_type_is_container
+*/
+bool QDBusType::isContainer() const
+{
+ return d && dbus_type_is_container(d->code);
+}
+
+/*!
+ Returns the subtypes of this type, if this is a container.
+
+ \sa isContainer
+*/
+QDBusTypeList QDBusType::subTypes() const
+{
+ if (d)
+ return d->subTypes;
+ return QDBusTypeList();
+}
+
+/*!
+ Returns true if this type is an array.
+
+ \sa isContainer, arrayElement
+*/
+bool QDBusType::isArray() const
+{
+ return dbusType() == DBUS_TYPE_ARRAY;
+}
+
+/*!
+ This is a convenience function that returns the element type of an array.
+ If this object is not an array, it returns an invalid QDBusType.
+
+ \sa isArray
+*/
+QDBusType QDBusType::arrayElement() const
+{
+ if (isArray() && d->subTypes.count() == 1)
+ return d->subTypes.first();
+ return QDBusType();
+}
+
+/*!
+ Returns true if this type is a map (i.e., an array of dictionary entries).
+
+ \sa isContainer, isArray, arrayElement
+*/
+bool QDBusType::isMap() const
+{
+ return arrayElement().dbusType() == DBUS_TYPE_DICT_ENTRY;
+}
+
+/*!
+ If this object is a map, returns the (basic) type that corresponds to the key type.
+ If this object is not a map, returns an invalid QDBusType.
+
+ \sa isMap
+*/
+QDBusType QDBusType::mapKey() const
+{
+ if (isMap())
+ return arrayElement().d->subTypes.first();
+ return QDBusType();
+}
+
+/*!
+ If this object is a map, returns the type that corresponds to the value type.
+ If this object is not a map, returns an invalid QDBusType.
+
+ \sa isMap
+*/
+QDBusType QDBusType::mapValue() const
+{
+ if (isMap())
+ return arrayElement().d->subTypes.at(1);
+ return QDBusType();
+}
+
+/*!
+ Returns true if the two types match.
+*/
+bool QDBusType::operator==(const QDBusType& other) const
+{
+ if (!d && !other.d)
+ return true;
+ if (!d || !other.d)
+ return false;
+ return d->code == other.d->code && d->subTypes == other.d->subTypes;
+}
+
+/*!
+ Returns a string representation of this type.
+*/
+QString QDBusType::toString(StringFormat sf) const
+{
+ switch (sf) {
+ case ConventionalNames:
+ return QDBusConventionalNames().toString(*this);
+
+ case QtNames:
+ return QDBusQtNames().toString(*this);
+
+ case QVariantNames:
+ return QDBusQVariantNames().toString(*this);
+ }
+
+ return QString(); // invalid
+}
+
+/*!
+ Converts the DBus type to QVariant::Type
+*/
+QVariant::Type QDBusType::qvariantType(int type)
+{
+ char c[2] = { type, 0 };
+ return qvariantType(c);
+}
+
+/*!
+ Converts the DBus type signature to QVariant::Type.
+*/
+QVariant::Type QDBusType::qvariantType(const char* signature)
+{
+ if (!signature)
+ return QVariant::Invalid;
+
+ // three special cases that don't validate as single:
+ if (qstrlen(signature) == 1) {
+ if (signature[0] == DBUS_TYPE_STRUCT)
+ return QVariant::List;
+ else if (signature[0] == DBUS_TYPE_DICT_ENTRY)
+ return QVariant::Map;
+ else if (signature[0] == DBUS_TYPE_ARRAY)
+ return QVariant::List;
+ }
+
+ // now we can validate
+ if ( !dbus_signature_validate_single(signature, 0) )
+ return QVariant::Invalid;
+
+ switch (signature[0])
+ {
+ case DBUS_TYPE_BOOLEAN:
+ return QVariant::Bool;
+
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_INT32:
+ return QVariant::Int;
+
+ case DBUS_TYPE_BYTE:
+ case DBUS_TYPE_UINT16:
+ case DBUS_TYPE_UINT32:
+ return QVariant::UInt;
+
+ case DBUS_TYPE_INT64:
+ return QVariant::LongLong;
+
+ case DBUS_TYPE_UINT64:
+ return QVariant::ULongLong;
+
+ case DBUS_TYPE_DOUBLE:
+ return QVariant::Double;
+
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_SIGNATURE:
+ return QVariant::String;
+
+ case DBUS_STRUCT_BEGIN_CHAR:
+ return QVariant::List; // change to QDBusStruct in the future
+
+ 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)
+ return QVariant::StringList;
+
+ // maybe it's a byte array
+ if (DBUS_TYPE_BYTE == signature[1])
+ return QVariant::ByteArray;
+
+ // check if it's a dict
+ if (DBUS_DICT_ENTRY_BEGIN_CHAR == signature[1])
+ return QVariant::Map;
+
+ return QVariant::List;
+
+ default:
+ return QVariant::Invalid;
+
+ }
+}
+
+/*!
+ Converts the QVariant::Type to a DBus type code.
+
+ \param t the type to convert
+*/
+int QDBusType::dbusType(QVariant::Type t)
+{
+ switch (t)
+ {
+ case QVariant::Bool:
+ return DBUS_TYPE_BOOLEAN;
+
+ case QVariant::Int:
+ return DBUS_TYPE_INT32;
+
+ case QVariant::UInt:
+ case QVariant::Char:
+ return DBUS_TYPE_UINT32;
+
+ case QVariant::LongLong:
+ return DBUS_TYPE_INT64;
+
+ case QVariant::ULongLong:
+ return DBUS_TYPE_UINT64;
+
+ case QVariant::Double:
+ return DBUS_TYPE_DOUBLE;
+
+ // from QMetaType:
+ case QMetaType::Short:
+ return DBUS_TYPE_INT16;
+
+ case QMetaType::UShort:
+ return DBUS_TYPE_UINT16;
+
+ case QMetaType::UChar:
+ return DBUS_TYPE_BYTE;
+
+ case QVariant::String:
+ case QVariant::Date:
+ case QVariant::Time:
+ case QVariant::DateTime:
+ return DBUS_TYPE_STRING;
+
+ case QVariant::Map:
+ // internal type information has been lost
+ return DBUS_TYPE_DICT_ENTRY;
+
+ case QVariant::List:
+ case QVariant::StringList:
+ case QVariant::ByteArray:
+ // could also be a struct...
+ return DBUS_TYPE_ARRAY;
+
+ case QVariant::UserType:
+ return DBUS_TYPE_VARIANT;
+
+ default:
+ break; // avoid compiler warnings
+ }
+
+ if (int(t) == QMetaTypeId<QDBusVariant>::qt_metatype_id())
+ return DBUS_TYPE_VARIANT;
+
+ return DBUS_TYPE_INVALID;
+}
+
+/*!
+ Converts the QVariant::Type to a DBus type signature.
+
+ \param t the type to convert
+*/
+const char* QDBusType::dbusSignature(QVariant::Type t)
+{
+ switch (t)
+ {
+ case QVariant::Bool:
+ return DBUS_TYPE_BOOLEAN_AS_STRING;
+
+ case QVariant::Int:
+ return DBUS_TYPE_INT32_AS_STRING;
+
+ case QVariant::UInt:
+ case QVariant::Char:
+ return DBUS_TYPE_UINT32_AS_STRING;
+
+ case QMetaType::Short:
+ return DBUS_TYPE_INT16_AS_STRING;
+
+ case QMetaType::UShort:
+ return DBUS_TYPE_UINT16_AS_STRING;
+
+ case QMetaType::UChar:
+ return DBUS_TYPE_BYTE_AS_STRING;
+
+ case QVariant::LongLong:
+ return DBUS_TYPE_INT64_AS_STRING;
+
+ case QVariant::ULongLong:
+ return DBUS_TYPE_UINT64_AS_STRING;
+
+ case QVariant::Double:
+ return DBUS_TYPE_DOUBLE_AS_STRING;
+
+ case QVariant::String:
+ case QVariant::Date:
+ case QVariant::Time:
+ case QVariant::DateTime:
+ return DBUS_TYPE_STRING_AS_STRING;
+
+ case QVariant::Map:
+ // internal type information has been lost
+ return DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING; // a{sv}
+
+ 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
+ // could also be a struct...
+ return DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING; // av
+
+ default:
+ if (int(t) == qMetaTypeId<QDBusVariant>())
+ return DBUS_TYPE_VARIANT_AS_STRING;
+
+ return DBUS_TYPE_INVALID_AS_STRING;
+ }
+}
+
+/*!
+ Guesses the DBus type from the given variant.
+*/
+QDBusType QDBusType::guessFromVariant(const QVariant& variant, VariantListMode mode)
+{
+ if (variant.type() == QVariant::List) {
+ // investigate deeper
+ QDBusType t;
+ t.d = new QDBusTypePrivate;
+
+ if (mode == ListIsArray) {
+ t.d->code = DBUS_TYPE_ARRAY;
+
+ const QVariantList list = variant.toList();
+ if (!list.isEmpty()) {
+ // check if all elements have the same type
+ QVariant::Type type = list.first().type();
+ foreach (const QVariant& v, list)
+ if (type != v.type()) {
+ // at least one is different
+ type = QVariant::Invalid;
+ break;
+ }
+
+ if (type != QVariant::Invalid) {
+ // all are of the same type
+ t.d->subTypes << guessFromVariant(list.first());
+ return t;
+ }
+ }
+
+ // internal information has been lost or there are many types
+ QDBusType nested;
+ nested.d = new QDBusTypePrivate;
+ nested.d->code = DBUS_TYPE_VARIANT;
+ t.d->subTypes << nested;
+ return t;
+ }
+ else {
+ // treat it as a struct
+ t.d->code = DBUS_TYPE_STRUCT;
+
+ // add the elements:
+ const QVariantList list = variant.toList();
+ foreach (const QVariant& v, list)
+ t.d->subTypes << guessFromVariant(v, mode);
+
+ return t;
+ }
+ }
+ else if (variant.type() == QVariant::Map) {
+ // investigate deeper
+ QDBusType t, t2, t3;
+ t2.d = new QDBusTypePrivate;
+ t2.d->code = DBUS_TYPE_DICT_ENTRY;
+
+ // the key
+ t3.d = new QDBusTypePrivate;
+ t3.d->code = DBUS_TYPE_STRING;
+ t2.d->subTypes << t3;
+
+ const QVariantMap map = variant.toMap();
+ if (!map.isEmpty()) {
+ // check if all elements have the same type
+ QVariantMap::const_iterator it = map.constBegin(),
+ end = map.constEnd();
+ QVariant::Type type = it.value().type();
+ for ( ; it != end; ++it)
+ if (type != it.value().type()) {
+ // at least one is different
+ type = QVariant::Invalid;
+ break;
+ }
+
+ if (type != QVariant::Invalid)
+ t2.d->subTypes << guessFromVariant(map.constBegin().value());
+ else {
+ // multiple types
+ t3.d->code = DBUS_TYPE_VARIANT;
+ t2.d->subTypes << t3;
+ }
+ }
+ else {
+ // information lost
+ t3.d->code = DBUS_TYPE_VARIANT;
+ t2.d->subTypes << t3;
+ }
+
+ t.d = new QDBusTypePrivate;
+ t.d->code = DBUS_TYPE_ARRAY;
+ t.d->subTypes << t2;
+ return t;
+ }
+ 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
+ */
+
+/*!
+ \fn QDBusTypeList::QDBusTypeList(const QList<QDBusType>& other)
+
+ Copy constructor.
+ \param other the list to copy
+ */
+
+/*!
+ Constructs a type list by parsing the signature given.
+ \param signature the signature to be parsed
+ */
+QDBusTypeList::QDBusTypeList(const char* signature)
+{
+ if (!signature || !*signature)
+ return; // empty
+
+ // validate it first
+ if ( !dbus_signature_validate(signature, 0) )
+ return;
+
+ // split it into components
+ DBusSignatureIter iter;
+ dbus_signature_iter_init(&iter, signature);
+
+ do {
+ *this << QDBusType(&iter);
+ } while (dbus_signature_iter_next(&iter));
+}
+
+/*!
+ Constructs a type list by parsing the elements on this iterator level.
+
+ \param iter the iterator containing the elements on this level
+*/
+QDBusTypeList::QDBusTypeList(DBusSignatureIter* iter)
+{
+ do {
+ QDBusType item(iter);
+ if (!item.isValid()) {
+ clear();
+ return;
+ }
+
+ *this << item;
+ } while (dbus_signature_iter_next(iter));
+}
+
+/*!
+ Returns true if this type list can represent the inner components of a map.
+*/
+bool QDBusTypeList::canBeMap() const
+{
+ return size() == 2 && at(0).isBasic();
+}
+
+/*!
+ Reconstructs the type signature that this type list represents.
+*/
+QByteArray QDBusTypeList::dbusSignature() const
+{
+ QByteArray retval;
+ foreach (QDBusType t, *this)
+ retval += t.dbusSignature();
+ return retval;
+}
diff --git a/qt/qdbustype.h b/qt/qdbustype.h
new file mode 100644
index 00000000..c26d7c80
--- /dev/null
+++ b/qt/qdbustype.h
@@ -0,0 +1,119 @@
+/* -*- C++ -*-
+ *
+ * Copyright (C) 2005 Thiago Macieira <thiago@kde.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
+ *
+ * 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 QDBUSTYPE_H
+#define QDBUSTYPE_H
+
+#include <QtCore/qvariant.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qlist.h>
+#include "qdbusmacros.h"
+#include <dbus/dbus.h>
+
+// forward declaration
+class QDBusTypeList;
+
+class QDBusTypePrivate;
+class QDBUS_EXPORT QDBusType
+{
+public:
+ enum StringFormat
+ {
+ ConventionalNames,
+ QtNames,
+ QVariantNames
+ };
+
+ QDBusType();
+ explicit QDBusType(int type);
+ explicit QDBusType(QVariant::Type type);
+ explicit QDBusType(const char* signature);
+ explicit QDBusType(DBusSignatureIter*);
+ explicit QDBusType(const QString& str);
+ explicit QDBusType(const QByteArray& str);
+ QDBusType(const QDBusType& other);
+
+ ~QDBusType();
+
+ QDBusType& operator=(const QDBusType& other);
+
+ QVariant::Type qvariantType() const;
+
+ int dbusType() const;
+ QByteArray dbusSignature() const;
+ bool isValid() const;
+ bool isBasic() const;
+ bool isContainer() const;
+
+ QDBusTypeList subTypes() const;
+
+ bool isArray() const;
+ QDBusType arrayElement() const;
+
+ bool isMap() const;
+ QDBusType mapKey() const;
+ QDBusType mapValue() const;
+
+ bool operator==(const QDBusType& other) const;
+
+ QString toString(StringFormat = QtNames) const;
+
+ static QVariant::Type qvariantType(int type);
+ static QVariant::Type qvariantType(const char* signature);
+ static int dbusType(QVariant::Type);
+ static const char* dbusSignature(QVariant::Type);
+
+ enum VariantListMode {
+ ListIsArray,
+ ListIsStruct
+ };
+ static QDBusType guessFromVariant(const QVariant &variant, VariantListMode = ListIsArray);
+
+private:
+ QSharedDataPointer<QDBusTypePrivate> d;
+};
+
+class QDBUS_EXPORT QDBusTypeList: public QList<QDBusType>
+{
+public:
+ inline QDBusTypeList() { }
+ inline QDBusTypeList(const QDBusTypeList& other)
+ : QList<QDBusType>(other)
+ { }
+ inline QDBusTypeList(const QList<QDBusType>& other)
+ : QList<QDBusType>(other)
+ { }
+ QDBusTypeList(const char* signature);
+ QDBusTypeList(DBusSignatureIter*);
+
+ bool canBeMap() const;
+
+ inline QDBusTypeList& operator<<(const QDBusType& item)
+ { QList<QDBusType>::operator<<(item); return *this; }
+
+ QByteArray dbusSignature() const;
+};
+
+#endif // QDBUSTYPE_H
diff --git a/qt/qdbusutil.cpp b/qt/qdbusutil.cpp
new file mode 100644
index 00000000..e10b3020
--- /dev/null
+++ b/qt/qdbusutil.cpp
@@ -0,0 +1,133 @@
+/* -*- 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 "qdbusutil.h"
+
+#include <dbus/dbus.h>
+
+#include <QtCore/qstringlist.h>
+#include <QtCore/qregexp.h>
+
+namespace QDBusUtil
+{
+
+ bool isValidInterfaceName(const QString& ifaceName)
+ {
+ if (ifaceName.isEmpty() || ifaceName.length() > DBUS_MAXIMUM_NAME_LENGTH)
+ return false;
+
+ QStringList parts = ifaceName.split('.');
+ if (parts.count() < 2)
+ return false; // at least two parts
+
+ foreach (QString part, parts)
+ if (!isValidMemberName(part))
+ return false;
+
+ return true;
+ }
+
+ bool isValidUniqueConnectionName(const QString &connName)
+ {
+ if (connName.isEmpty() || connName.length() > DBUS_MAXIMUM_NAME_LENGTH ||
+ !connName.startsWith(':'))
+ return false;
+
+ QStringList parts = connName.mid(1).split('.');
+ if (parts.count() < 1)
+ return false;
+
+ QRegExp regex("[a-zA-Z0-9_-]+");
+ foreach (QString part, parts)
+ if (!regex.exactMatch(part))
+ return false;
+
+ return true;
+ }
+
+ bool isValidBusName(const QString &busName)
+ {
+ if (busName.isEmpty() || busName.length() > DBUS_MAXIMUM_NAME_LENGTH)
+ return false;
+
+ if (busName.startsWith(':'))
+ return isValidUniqueConnectionName(busName);
+
+ QStringList parts = busName.split('.');
+ if (parts.count() < 1)
+ return false;
+
+ QRegExp regex("[a-zA-Z_-][a-zA-Z0-9_-]*");
+ foreach (QString part, parts)
+ if (!regex.exactMatch(part))
+ return false;
+
+ return true;
+ }
+
+ bool isValidMemberName(const QString &memberName)
+ {
+ if (memberName.isEmpty() || memberName.length() > DBUS_MAXIMUM_NAME_LENGTH)
+ return false;
+
+ QRegExp regex("[a-zA-Z0-9_]+");
+ return regex.exactMatch(memberName);
+ }
+
+ bool isValidErrorName(const QString &errorName)
+ {
+ return isValidInterfaceName(errorName);
+ }
+
+ bool isValidObjectPath(const QString &path)
+ {
+ if (path == QLatin1String("/"))
+ return true;
+
+ if (!path.startsWith('/') || path.indexOf(QLatin1String("//")) != -1 ||
+ path.endsWith('/'))
+ return false;
+
+ QStringList parts = path.split('/');
+ Q_ASSERT(parts.count() >= 1);
+ parts.removeFirst(); // it starts with /, so we get an empty first part
+
+ QRegExp regex("[a-zA-Z0-9_]+");
+ foreach (QString part, parts)
+ if (!regex.exactMatch(part))
+ return false;
+
+ return true;
+ }
+
+ bool isValidSignature(const QString &signature)
+ {
+ return dbus_signature_validate(signature.toUtf8(), 0);
+ }
+
+ bool isValidSingleSignature(const QString &signature)
+ {
+ return dbus_signature_validate_single(signature.toUtf8(), 0);
+ }
+
+} // namespace QDBusUtil
diff --git a/qt/qdbusutil.h b/qt/qdbusutil.h
new file mode 100644
index 00000000..4c3bac65
--- /dev/null
+++ b/qt/qdbusutil.h
@@ -0,0 +1,50 @@
+/* -*- 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.
+ *
+ */
+
+#ifndef QDBUSUTIL_H
+#define QDBUSUTIL_H
+
+#include <QtCore/qstring.h>
+
+#include "qdbusmacros.h"
+
+namespace QDBusUtil
+{
+ bool isValidInterfaceName(const QString &ifaceName) QDBUS_EXPORT;
+
+ bool isValidUniqueConnectionName(const QString &busName) QDBUS_EXPORT;
+
+ bool isValidBusName(const QString &busName) QDBUS_EXPORT;
+
+ bool isValidMemberName(const QString &memberName) QDBUS_EXPORT;
+
+ bool isValidErrorName(const QString &errorName) QDBUS_EXPORT;
+
+ bool isValidObjectPath(const QString &path) QDBUS_EXPORT;
+
+ bool isValidSignature(const QString &signature) QDBUS_EXPORT;
+
+ bool isValidSingleSignature(const QString &signature) QDBUS_EXPORT;
+}
+
+#endif
diff --git a/qt/qdbusvariant.h b/qt/qdbusvariant.h
index bd18beff..eb7e3aac 100644
--- a/qt/qdbusvariant.h
+++ b/qt/qdbusvariant.h
@@ -1,6 +1,8 @@
/* qdbusvariant.h DBUS variant struct
*
* 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,23 +17,21 @@
* 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.
*
*/
#ifndef QDBUSVARIANT_H
#define QDBUSVARIANT_H
-#include "dbus/qdbus.h"
-
-#include <QtCore/qmetatype.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qvariant.h>
+#include "qdbusmacros.h"
+#include "qdbustype.h"
+#include <qvariant.h>
struct QDBUS_EXPORT QDBusVariant
{
- QString signature;
+ QDBusType type;
QVariant value;
};
Q_DECLARE_METATYPE(QDBusVariant)
diff --git a/qt/qdbusxmlparser.cpp b/qt/qdbusxmlparser.cpp
new file mode 100644
index 00000000..9d80fe22
--- /dev/null
+++ b/qt/qdbusxmlparser.cpp
@@ -0,0 +1,347 @@
+/* -*- C++ -*-
+ *
+ * Copyright (C) 2005 Thiago Macieira <thiago@kde.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
+ *
+ * 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 "qdbusxmlparser_p.h"
+#include "qdbusinterface.h"
+#include "qdbusinterface_p.h"
+#include "qdbusconnection_p.h"
+#include "qdbusobject_p.h"
+
+#include <QtXml/qdom.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qtextstream.h>
+
+static QDBusIntrospection::Annotations
+parseAnnotations(const QDomElement& elem)
+{
+ QDBusIntrospection::Annotations retval;
+ QDomNodeList list = elem.elementsByTagName("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");
+
+ if (name.isEmpty())
+ continue;
+
+ retval.insert(name, value);
+ }
+
+ return retval;
+}
+
+static QDBusType
+parseType(const QString& type)
+{
+ if (type.isEmpty())
+ return QDBusType();
+ return QDBusType(type);
+}
+
+static QDBusIntrospection::Arguments
+parseArgs(const QDomElement& elem, const QLatin1String& direction, bool acceptEmpty = false)
+{
+ QDBusIntrospection::Arguments retval;
+ QDomNodeList list = elem.elementsByTagName("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) {
+
+ QDBusIntrospection::Argument argData;
+ if (arg.hasAttribute("name"))
+ argData.name = arg.attribute("name"); // can be empty
+ argData.type = parseType(arg.attribute("type"));
+
+ if (!argData.type.isValid())
+ continue;
+
+ retval << argData;
+ }
+ }
+ return retval;
+}
+
+QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
+ const QString& xmlData, QDBusConnectionPrivate* store)
+ : m_service(service), m_path(path), m_store(store)
+{
+ QDomDocument doc;
+ doc.setContent(xmlData);
+ m_node = doc.firstChildElement("node");
+}
+
+QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
+ const QDomElement& node, QDBusConnectionPrivate* store)
+ : m_service(service), m_path(path), m_node(node), m_store(store)
+{
+}
+
+void QDBusXmlParser::parse(const QDBusObjectPrivate* d, const QString &xml)
+{
+ QDBusXmlParser parser(d->data->service, d->data->path, xml, d->parent);
+ parser.object();
+ parser.interfaces();
+}
+
+QDBusIntrospection::Interfaces
+QDBusXmlParser::interfaces() const
+{
+ QDBusIntrospection::Interfaces retval;
+
+ if (m_node.isNull())
+ return retval;
+
+ QDomNodeList interfaces = m_node.elementsByTagName("interface");
+ for (int i = 0; i < interfaces.count(); ++i)
+ {
+ QDomElement iface = interfaces.item(i).toElement();
+ QString ifaceName = iface.attribute("name");
+ if (iface.isNull() || ifaceName.isEmpty())
+ continue; // for whatever reason
+
+ QDBusIntrospection::Interface *ifaceData;
+ if (m_store) {
+ QSharedDataPointer<QDBusIntrospection::Interface> knownData =
+ m_store->findInterface(ifaceName);
+ if (!knownData.constData()->introspection.isEmpty()) {
+ // it's already known
+ // we don't have to re-parse
+ retval.insert(ifaceName, knownData);
+ continue;
+ }
+
+ // ugly, but ok
+ // we don't want to detach
+ // we *WANT* to modify the shared data
+ ifaceData = const_cast<QDBusIntrospection::Interface*>( knownData.constData() );
+ }
+ else {
+ ifaceData = new QDBusIntrospection::Interface;
+ ifaceData->name = ifaceName;
+ }
+
+ {
+ // save the data
+ QTextStream ts(&ifaceData->introspection);
+ iface.save(ts,2);
+ }
+
+ // parse annotations
+ ifaceData->annotations = parseAnnotations(iface);
+
+ // parse methods
+ QDomNodeList list = iface.elementsByTagName("method");
+ for (int j = 0; j < list.count(); ++j)
+ {
+ QDomElement method = list.item(j).toElement();
+ QString methodName = method.attribute("name");
+ if (method.isNull() || methodName.isEmpty())
+ continue;
+
+ QDBusIntrospection::Method methodData;
+ methodData.name = methodName;
+
+ // parse arguments
+ methodData.inputArgs = parseArgs(method, QLatin1String("in"));
+ methodData.outputArgs = parseArgs(method, QLatin1String("out"));
+ methodData.annotations = parseAnnotations(method);
+
+ // add it
+ ifaceData->methods.insert(methodName, methodData);
+ }
+
+ // parse signals
+ list = iface.elementsByTagName("signal");
+ for (int j = 0; j < list.count(); ++j)
+ {
+ QDomElement signal = list.item(j).toElement();
+ QString signalName = signal.attribute("name");
+ if (signal.isNull() || signalName.isEmpty())
+ continue;
+
+ QDBusIntrospection::Signal signalData;
+ signalData.name = signalName;
+
+ // parse data
+ signalData.outputArgs = parseArgs(signal, QLatin1String("out"), true);
+ signalData.annotations = parseAnnotations(signal);
+
+ // add it
+ ifaceData->signals_.insert(signalName, signalData);
+ }
+
+ // parse properties
+ list = iface.elementsByTagName("property");
+ for (int j = 0; j < list.count(); ++j)
+ {
+ QDomElement property = list.item(j).toElement();
+ QString propertyName = property.attribute("name");
+ if (property.isNull() || propertyName.isEmpty())
+ continue;
+
+ QDBusIntrospection::Property propertyData;
+
+ // parse data
+ propertyData.name = propertyName;
+ propertyData.type = parseType(property.attribute("type"));
+ propertyData.annotations = parseAnnotations(property);
+
+ if (!propertyData.type.isValid())
+ // cannot be!
+ continue;
+
+ QString access = property.attribute("access");
+ if (access.isEmpty())
+ // can't be empty either!
+ continue;
+ else if (access == QLatin1String("read"))
+ propertyData.access = QDBusIntrospection::Property::Read;
+ else if (access == QLatin1String("write"))
+ propertyData.access = QDBusIntrospection::Property::Write;
+ else if (access == QLatin1String("readwrite"))
+ propertyData.access = QDBusIntrospection::Property::ReadWrite;
+ else
+ continue; // invalid one!
+
+ // add it
+ ifaceData->properties.insert(propertyName, propertyData);
+ }
+
+ // add it
+ retval.insert(ifaceName, QSharedDataPointer<QDBusIntrospection::Interface>(ifaceData));
+ }
+
+ return retval;
+}
+
+QSharedDataPointer<QDBusIntrospection::Object>
+QDBusXmlParser::object() const
+{
+ QSharedDataPointer<QDBusIntrospection::Object> retval;
+
+ if (m_node.isNull())
+ return retval;
+
+ // check if the store knows about this one
+ QDBusIntrospection::Object* objData;
+ if (m_store) {
+ retval = 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");
+ for (int i = 0; i < objects.count(); ++i) {
+ QDomElement obj = objects.item(i).toElement();
+ QString objName = obj.attribute("name");
+ if (obj.isNull() || objName.isEmpty())
+ continue; // for whatever reason
+
+ objData->childObjects.append(objName);
+ }
+
+ QDomNodeList interfaces = m_node.elementsByTagName("interface");
+ for (int i = 0; i < interfaces.count(); ++i) {
+ QDomElement iface = interfaces.item(i).toElement();
+ QString ifaceName = iface.attribute("name");
+ if (iface.isNull() || ifaceName.isEmpty())
+ continue;
+
+ objData->interfaces.append(ifaceName);
+ }
+ }
+
+ return retval;
+}
+
+QSharedDataPointer<QDBusIntrospection::ObjectTree>
+QDBusXmlParser::objectTree() const
+{
+ QSharedDataPointer<QDBusIntrospection::ObjectTree> retval;
+
+ if (m_node.isNull())
+ return retval;
+
+ retval = new QDBusIntrospection::ObjectTree;
+
+ retval->service = m_service;
+ retval->path = m_path;
+
+ QTextStream ts(&retval->introspection);
+ 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");
+ for (int i = 0; i < objects.count(); ++i) {
+ QDomElement obj = objects.item(i).toElement();
+ QString objName = obj.attribute("name");
+ if (obj.isNull() || objName.isEmpty())
+ continue; // for whatever reason
+
+ // check if we have anything to process
+ if (!obj.firstChild().isNull()) {
+ // yes, introspect this object
+ QString xml;
+ QTextStream ts(&xml);
+ obj.save(ts,0);
+
+ // parse it
+ QString objAbsName = m_path;
+ if (!objAbsName.endsWith('/'))
+ objAbsName.append('/');
+ objAbsName += objName;
+
+ QDBusXmlParser parser(m_service, objAbsName, obj, m_store);
+ retval->childObjectData.insert(objName, parser.objectTree());
+ }
+
+ retval->childObjects << objName;
+ }
+
+ return QSharedDataPointer<QDBusIntrospection::ObjectTree>( retval );
+}
+
diff --git a/qt/qdbusxmlparser_p.h b/qt/qdbusxmlparser_p.h
new file mode 100644
index 00000000..c4c45be1
--- /dev/null
+++ b/qt/qdbusxmlparser_p.h
@@ -0,0 +1,59 @@
+/* -*- C++ -*-
+ *
+ * Copyright (C) 2005 Thiago Macieira <thiago@kde.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
+ *
+ * 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 QDBUSXMLPARSER_H
+#define QDBUSXMLPARSER_H
+
+#include <QtCore/qmap.h>
+#include <QtXml/qdom.h>
+#include "qdbusmacros.h"
+#include "qdbusintrospection.h"
+
+class QDBusConnectionPrivate;
+class QDBusObjectPrivate;
+
+/**
+ * @internal
+ */
+class QDBusXmlParser
+{
+ QString m_service;
+ QString m_path;
+ QDomElement m_node;
+ QDBusConnectionPrivate* m_store;
+
+public:
+ QDBusXmlParser(const QString& service, const QString& path,
+ const QString& xmlData, QDBusConnectionPrivate* store = 0);
+ QDBusXmlParser(const QString& service, const QString& path,
+ const QDomElement& node, QDBusConnectionPrivate* store = 0);
+
+ QDBusIntrospection::Interfaces interfaces() const;
+ QSharedDataPointer<QDBusIntrospection::Object> object() const;
+ QSharedDataPointer<QDBusIntrospection::ObjectTree> objectTree() const;
+
+ static void parse(const QDBusObjectPrivate* d, const QString &xml);
+};
+
+#endif