diff options
Diffstat (limited to 'qt/src')
44 files changed, 10807 insertions, 0 deletions
diff --git a/qt/src/.cvsignore b/qt/src/.cvsignore new file mode 100644 index 0000000..f6454f2 --- /dev/null +++ b/qt/src/.cvsignore @@ -0,0 +1,11 @@ +.deps +.libs +Makefile +Makefile.in +*.lo +*.la +*.bb +*.bbg +*.da +*.gcov +*.moc diff --git a/qt/src/Makefile.am b/qt/src/Makefile.am new file mode 100644 index 0000000..41e3c5e --- /dev/null +++ b/qt/src/Makefile.am @@ -0,0 +1,70 @@ +INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_QT_CFLAGS) -DDBUS_COMPILATION + +lib_LTLIBRARIES=libdbus-qt4-1.la + +dbusincludedir=$(includedir)/dbus-1.0/dbus +dbusinclude_HEADERS= \ + qdbusbus.h \ + qdbusmacros.h \ + qdbuserror.h \ + qdbusmessage.h \ + qdbusserver.h \ + qdbusconnection.h \ + qdbusabstractinterface.h \ + qdbusinterface.h \ + qdbusutil.h \ + qdbusabstractadaptor.h \ + qdbusreply.h \ + qdbustypehelper_p.h + +noinst_HEADERS= \ + qdbusabstractadaptor_p.h \ + qdbusabstractinterface_p.h \ + qdbusconnection_p.h \ + qdbusinterface_p.h \ + qdbusintrospection_p.h \ + qdbusmarshall_p.h \ + qdbusmessage_p.h \ + qdbusmetaobject_p.h \ + qdbustype_p.h \ + qdbusxmlparser_p.h + +MOCS = qdbusabstractadaptor.moc qdbusserver.moc qdbusconnection_p.moc qdbusconnection_p.moc qdbusabstractadaptor_p.moc qdbusbus.moc qdbusabstractinterface.moc +CLEANFILES = $(MOCS) +BUILT_SOURCES = $(MOCS) + +libdbus_qt4_1_la_SOURCES = \ + qdbusbus.cpp \ + qdbusconnection.cpp \ + qdbuserror.cpp \ + qdbusintegrator.cpp \ + qdbusmarshall.cpp \ + qdbusmessage.cpp \ + qdbusserver.cpp \ + qdbustype.cpp \ + qdbusabstractinterface.cpp \ + qdbusinterface.cpp \ + qdbusxmlparser.cpp \ + qdbusutil.cpp \ + qdbusintrospection.cpp \ + qdbusabstractadaptor.cpp \ + qdbusthread.cpp \ + qdbusinternalfilters.cpp \ + qdbusmetaobject.cpp \ + qdbusmisc.cpp \ + qdbusxmlgenerator.cpp + +qdbusabstractadaptor.lo: qdbusabstractadaptor.moc qdbusabstractadaptor_p.moc +qdbusabstractinterface.lo: qdbusabstractinterface.moc +qdbusbus.lo: qdbusbus.moc +qdbusserver.lo: qdbusserver.moc +qdbusintegrator.lo: qdbusconnection_p.moc + +libdbus_qt4_1_la_LIBADD= $(DBUS_QT_LIBS) $(top_builddir)/dbus/libdbus-1.la +libdbus_qt4_1_la_LDFLAGS= -version-info 1:0 -no-undefined +libdbus_qt4_1_la_CPPFLAGS= -DQDBUS_MAKEDLL + +EXTRA_DIST = qt-dbus.qdocconf + +%.moc: %.h + $(QT_MOC) $< > $@ diff --git a/qt/src/qdbus.h b/qt/src/qdbus.h new file mode 100644 index 0000000..60c3582 --- /dev/null +++ b/qt/src/qdbus.h @@ -0,0 +1,52 @@ +/* qdbus.h precompiled header + * + * 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 QDBUS_H +#define QDBUS_H + +#include <QtCore/qglobal.h> + +#if !defined(DBUS_COMPILATION) +# include <dbus/qdbusabstractadaptor.h> +# include <dbus/qdbusabstractinterface.h> +# include <dbus/qdbusbus.h> +# include <dbus/qdbusconnection.h> +# include <dbus/qdbuserror.h> +# include <dbus/qdbusinterface.h> +# include <dbus/qdbusmessage.h> +# include <dbus/qdbusreply.h> +# include <dbus/qdbusserver.h> +# include <dbus/qdbusutil.h> +#else +# include "qdbusabstractadaptor.h" +# include "qdbusabstractinterface.h" +# include "qdbusbus.h" +# include "qdbusconnection.h" +# include "qdbuserror.h" +# include "qdbusinterface.h" +# include "qdbusmessage.h" +# include "qdbusreply.h" +# include "qdbusserver.h" +# include "qdbusutil.h" +#endif + +#endif diff --git a/qt/src/qdbusabstractadaptor.cpp b/qt/src/qdbusabstractadaptor.cpp new file mode 100644 index 0000000..b7c4188 --- /dev/null +++ b/qt/src/qdbusabstractadaptor.cpp @@ -0,0 +1,336 @@ +/* -*- 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> + +#include "qdbusconnection.h" + +#include "qdbusconnection_p.h" // for qDBusParametersForMethod +#include "qdbusabstractadaptor_p.h" + +struct QDBusAdaptorInit +{ + QSignalSpyCallbackSet callbacks; + QDBusAdaptorInit() + { + extern void qt_register_signal_spy_callbacks(const QSignalSpyCallbackSet &callback_set); + callbacks.signal_begin_callback = QDBusAdaptorConnector::signalBeginCallback; + callbacks.signal_end_callback = QDBusAdaptorConnector::signalEndCallback; + callbacks.slot_begin_callback = 0; + callbacks.slot_end_callback = 0; + qt_register_signal_spy_callbacks(callbacks); + + //QDBusAdaptorConnector::id = QObject::registerUserData(); + } +}; + +Q_GLOBAL_STATIC(QDBusAdaptorInit, qAdaptorInit) + +QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *obj) +{ + (void)qAdaptorInit(); + + if (!obj) + return 0; + QDBusAdaptorConnector *connector = qFindChild<QDBusAdaptorConnector *>(obj); + if (connector) + connector->polish(); + return connector; +} + +QDBusAdaptorConnector *qDBusFindAdaptorConnector(QDBusAbstractAdaptor *adaptor) +{ + return qDBusFindAdaptorConnector(adaptor->parent()); +} + +QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *obj) +{ + (void)qAdaptorInit(); + + QDBusAdaptorConnector *connector = qDBusFindAdaptorConnector(obj); + if (connector) + return connector; + return new QDBusAdaptorConnector(obj); +} + +QString QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor) +{ + return adaptor->d->xml; +} + +void QDBusAbstractAdaptorPrivate::saveIntrospectionXml(QDBusAbstractAdaptor *adaptor, + const QString &xml) +{ + adaptor->d->xml = xml; +} + +/*! + \page usingannotations.html + \title Using annotations in adaptors + + It is currently not possible to specify arbitrary annotations in adaptors. +*/ + +/*! + \class QDBusAbstractAdaptor + \brief Abstract adaptor for D-Bus adaptor classes. + + The QDBusAbstractAdaptor class is the starting point for all objects intending to provide + interfaces to the external world using D-Bus. This is accomplished by attaching a one or more + classes derived from QDBusAbstractAdaptor to a normal QObject and then registering that QObject + with QDBusConnection::registerObject. QDBusAbstractAdaptor objects are intended to be + light-weight wrappers, mostly just relaying calls into the real object (see object()) and the + signals from it. + + Each QDBusAbstractAdaptor-derived class should define the D-Bus interface it is implementing + using the Q_CLASSINFO macro in the class definition. + + QDBusAbstractAdaptor uses the standard QObject mechanism of signals, slots and properties to + determine what signals, methods and properties to export to the bus. Any signal emitted by + QDBusAbstractAdaptor-derived classes will be automatically be relayed through any D-Bus + connections the object is registered on. + + Classes derived from QDBusAbstractAdaptor must be created on the heap using the \a new operator + and must not be deleted by the user (they will be deleted automatically when the object they are + connected to is also deleted). + + \sa {usingadaptors.html}{Using adaptors}, QDBusConnection +*/ + +/*! + Constructs a QDBusAbstractAdaptor with \a parent as the object we refer to. +*/ +QDBusAbstractAdaptor::QDBusAbstractAdaptor(QObject* parent) + : QObject(parent), d(new QDBusAbstractAdaptorPrivate) +{ + QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(parent); + + connector->waitingForPolish = true; + QTimer::singleShot(0, connector, SLOT(polish())); +} + +/*! + Destroys the adaptor. + + \warning Adaptors are destroyed automatically when the real object they refer to is + destroyed. Do not delete the adaptors yourself. +*/ +QDBusAbstractAdaptor::~QDBusAbstractAdaptor() +{ + delete d; +} + +/*! + Returns the QObject that we're the adaptor for. This is the same object that was passed as an + argument to the QDBusAbstractAdaptor constructor. +*/ +QObject* QDBusAbstractAdaptor::object() const +{ + return parent(); +} + +/*! + Toggles automatic signal relaying from the real object (see object()). + + Automatic signal relaying consists of signal-to-signal connection of the signals on the parent + that have the exact same method signatue in both classes. + + If \a enable is set to true, connect the signals; if set to false, disconnect all signals. +*/ +void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable) +{ + const QMetaObject *us = metaObject(); + const QMetaObject *them = parent()->metaObject(); + for (int idx = staticMetaObject.methodCount(); idx < us->methodCount(); ++idx) { + QMetaMethod mm = us->method(idx); + + if (mm.methodType() != QMetaMethod::Signal) + continue; + + // try to connect/disconnect to a signal on the parent that has the same method signature + QByteArray sig = QMetaObject::normalizedSignature(mm.signature()); + if (them->indexOfSignal(sig) == -1) + continue; + sig.prepend(QSIGNAL_CODE + '0'); + parent()->disconnect(sig, this, sig); + if (enable) + connect(parent(), sig, sig); + } +} + +QDBusAdaptorConnector::QDBusAdaptorConnector(QObject *parent) + : QObject(parent), waitingForPolish(false), lastSignalIdx(0), argv(0) +{ +} + +QDBusAdaptorConnector::~QDBusAdaptorConnector() +{ +} + +void QDBusAdaptorConnector::addAdaptor(QDBusAbstractAdaptor *adaptor) +{ + // find the interface name + const QMetaObject *mo = adaptor->metaObject(); + while (mo != &QDBusAbstractAdaptor::staticMetaObject) { + int ciend = mo->classInfoCount(); + for (int i = mo->classInfoOffset(); i < ciend; ++i) { + QMetaClassInfo mci = mo->classInfo(i); + if (strcmp(mci.name(), QCLASSINFO_DBUS_INTERFACE) == 0 && *mci.value()) { + // find out if this interface exists first + QString interface = QString::fromUtf8(mci.value()); + AdaptorMap::Iterator it = qLowerBound(adaptors.begin(), adaptors.end(), interface); + if (it != adaptors.end() && it->interface == interface) { + // exists. Replace it (though it's probably the same) + it->adaptor = adaptor; + it->metaObject = mo; + } else { + // create a new one + AdaptorData entry; + entry.interface = interface; + entry.adaptor = adaptor; + entry.metaObject = mo; + adaptors << entry; + } + } + } + + mo = mo->superClass(); + } + + // connect the adaptor's signals to our relaySlot slot + mo = adaptor->metaObject(); + for (int i = QDBusAbstractAdaptor::staticMetaObject.methodCount(); + i < mo->methodCount(); ++i) { + QMetaMethod mm = mo->method(i); + + if (mm.methodType() != QMetaMethod::Signal) + continue; + + QByteArray sig = mm.signature(); + sig.prepend(QSIGNAL_CODE + '0'); + disconnect(adaptor, sig, this, SLOT(relaySlot())); + connect(adaptor, sig, this, SLOT(relaySlot())); + } +} + +void QDBusAdaptorConnector::polish() +{ + if (!waitingForPolish) + return; // avoid working multiple times if multiple adaptors were added + + waitingForPolish = false; + const QObjectList &objs = parent()->children(); + foreach (QObject *obj, objs) { + QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(obj); + if (adaptor) + addAdaptor(adaptor); + } + + // sort the adaptor list + qSort(adaptors); +} + +void QDBusAdaptorConnector::relaySlot() +{ + relay(sender()); +} + +void QDBusAdaptorConnector::relay(QObject *sender) +{ + // we're being called because there is a signal being emitted that we must relay + Q_ASSERT(lastSignalIdx); + Q_ASSERT(argv); + Q_ASSERT(senderMetaObject); + + if (senderMetaObject != sender->metaObject()) { + qWarning("Inconsistency detected: QDBusAdaptorConnector::relay got called with unexpected sender object!"); + } else { + QMetaMethod mm = senderMetaObject->method(lastSignalIdx); + QObject *object = static_cast<QDBusAbstractAdaptor *>(sender)->parent(); + + // break down the parameter list + QList<int> types; + int inputCount = qDBusParametersForMethod(mm, types); + if (inputCount == -1) + // invalid signal signature + // qDBusParametersForMethod has already complained + return; + if (inputCount + 1 != types.count() || + types.at(inputCount) == QDBusConnectionPrivate::messageMetaType) { + // invalid signal signature + // qDBusParametersForMethod has not yet complained about this one + qWarning("Cannot relay signal %s::%s", senderMetaObject->className(), mm.signature()); + return; + } + + QByteArray signature = QMetaObject::normalizedSignature(mm.signature()); + signature.truncate(signature.indexOf('(')); // remove parameter decoration + + QVariantList args; + for (int i = 1; i < types.count(); ++i) + args << QVariant(types.at(i), argv[i]); + + // find all the interfaces this signal belongs to + for (const QMetaObject *mo = senderMetaObject; mo != &QDBusAbstractAdaptor::staticMetaObject; + mo = mo->superClass()) { + if (lastSignalIdx < mo->methodOffset()) + break; + + for (int i = mo->classInfoOffset(); i < mo->classInfoCount(); ++i) { + QMetaClassInfo mci = mo->classInfo(i); + if (qstrcmp(mci.name(), QCLASSINFO_DBUS_INTERFACE) == 0 && *mci.value()) + // now emit the signal with all the information + emit relaySignal(object, mci.value(), signature.constData(), args); + } + } + } +} + +void QDBusAdaptorConnector::signalBeginCallback(QObject *caller, int method_index, void **argv) +{ + QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(caller); + if (adaptor) { + QDBusAdaptorConnector *data = qDBusFindAdaptorConnector(adaptor); + data->lastSignalIdx = method_index; + data->argv = argv; + data->senderMetaObject = caller->metaObject(); + data->polish(); // make sure it's polished + } +} + +void QDBusAdaptorConnector::signalEndCallback(QObject *caller, int) +{ + QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(caller); + if (adaptor) { + QDBusAdaptorConnector *data = qDBusFindAdaptorConnector(adaptor); + data->lastSignalIdx = 0; + data->argv = 0; + data->senderMetaObject = 0; + } +} + +#include "qdbusabstractadaptor.moc" +#include "qdbusabstractadaptor_p.moc" diff --git a/qt/src/qdbusabstractadaptor.h b/qt/src/qdbusabstractadaptor.h new file mode 100644 index 0000000..16fbf5d --- /dev/null +++ b/qt/src/qdbusabstractadaptor.h @@ -0,0 +1,50 @@ +/* -*- 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 QDBusAbstractAdaptorPrivate; +class QDBUS_EXPORT QDBusAbstractAdaptor: public QObject +{ + Q_OBJECT +protected: + QDBusAbstractAdaptor(QObject *parent); + +public: + ~QDBusAbstractAdaptor(); + + Q_DECL_DEPRECATED QObject *object() const; + +protected: + void setAutoRelaySignals(bool enable); + +private: + friend class QDBusAbstractAdaptorPrivate; + QDBusAbstractAdaptorPrivate *d; +}; + +#endif diff --git a/qt/src/qdbusabstractadaptor_p.h b/qt/src/qdbusabstractadaptor_p.h new file mode 100644 index 0000000..71bfb58 --- /dev/null +++ b/qt/src/qdbusabstractadaptor_p.h @@ -0,0 +1,127 @@ +/* -*- mode: C++; set-fill-width: 100 -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the public API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// +// + +#ifndef QDBUSABSTRACTADAPTORPRIVATE_H +#define QDBUSABSTRACTADAPTORPRIVATE_H + +#include <QtCore/qobject.h> +#include <QtCore/qmap.h> +#include <QtCore/qhash.h> +#include <QtCore/qreadwritelock.h> +#include <QtCore/qvariant.h> +#include <QtCore/qvector.h> + +#define QCLASSINFO_DBUS_INTERFACE "D-Bus Interface" +#define QCLASSINFO_DBUS_INTROSPECTION "D-Bus Introspection" + +class QDBusAbstractAdaptor; +class QDBusAdaptorConnector; +class QDBusAdaptorManager; +class QDBusConnectionPrivate; + +#if QT_VERSION < 0x040200 +/* mirrored in qobject_p.h, DON'T CHANGE without prior warning */ +struct QSignalSpyCallbackSet +{ + typedef void (*BeginCallback)(QObject *caller, int method_index, void **argv); + typedef void (*EndCallback)(QObject *caller, int method_index); + BeginCallback signal_begin_callback, + slot_begin_callback; + EndCallback signal_end_callback, + slot_end_callback; +}; +#else +# error Qt 4.2.0 is supposed to have a better solution! + CHOKE! +#endif // Qt 4.2.0 + +class QDBusAbstractAdaptorPrivate +{ +public: + QString xml; + + static QString retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor); + static void saveIntrospectionXml(QDBusAbstractAdaptor *adaptor, const QString &xml); +}; + +class QDBusAdaptorConnector: public QObject +{ + Q_OBJECT +public: // typedefs + struct AdaptorData + { + QString interface; + QDBusAbstractAdaptor *adaptor; + const QMetaObject *metaObject; + + inline bool operator<(const AdaptorData &other) const + { return interface < other.interface; } + inline bool operator<(const QString &other) const + { return interface < other; } + }; + typedef QVector<AdaptorData> AdaptorMap; + +public: // methods + explicit QDBusAdaptorConnector(QObject *parent); + ~QDBusAdaptorConnector(); + + void addAdaptor(QDBusAbstractAdaptor *adaptor); + void relay(QObject *sender); + +public slots: + void relaySlot(); + void polish(); + +signals: + void relaySignal(QObject *obj, const char *interface, const char *name, const QVariantList &args); + +public: // member variables + AdaptorMap adaptors; + bool waitingForPolish : 1; + + int lastSignalIdx; + void **argv; + const QMetaObject *senderMetaObject; + +public: // static members + static void signalBeginCallback(QObject *caller, int method_index, void **argv); + static void signalEndCallback(QObject *caller, int method_index); + //static int id; +}; + +extern QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *object); +extern QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *object); + +#endif // QDBUSABSTRACTADAPTORPRIVATE_H diff --git a/qt/src/qdbusabstractinterface.cpp b/qt/src/qdbusabstractinterface.cpp new file mode 100644 index 0000000..2a6bcf0 --- /dev/null +++ b/qt/src/qdbusabstractinterface.cpp @@ -0,0 +1,389 @@ +/* -*- 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 "qdbusabstractinterface.h" + +#include "qdbusabstractinterface_p.h" +#include "qdbusmetaobject_p.h" +#include "qdbusconnection_p.h" + +QVariant QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp) const +{ + // try to read this property + QDBusMessage msg = QDBusMessage::methodCall(service, path, DBUS_INTERFACE_PROPERTIES, + QLatin1String("Get")); + msg << interface << QString::fromUtf8(mp.name()); + QDBusMessage reply = connp->sendWithReply(msg, QDBusConnection::NoUseEventLoop); + + if (reply.type() == QDBusMessage::ReplyMessage && reply.count() == 1 && + reply.signature() == QLatin1String("v")) { + QVariant value = QDBusTypeHelper<QVariant>::fromVariant(reply.at(0)); + + // make sure the type is right + if (qstrcmp(mp.typeName(), value.typeName()) == 0) { + if (mp.type() == QVariant::LastType) + // QVariant is special in this context + return QDBusTypeHelper<QVariant>::fromVariant(value); + + return value; + } + } + + // there was an error... + if (reply.type() == QDBusMessage::ErrorMessage) + lastError = reply; + else if (reply.signature() != QLatin1String("v")) { + QString errmsg = QLatin1String("Invalid signature `%1' in return from call to " + DBUS_INTERFACE_PROPERTIES); + lastError = QDBusError(QDBusError::InvalidSignature, errmsg.arg(reply.signature())); + } else { + QString errmsg = QLatin1String("Unexpected type `%1' when retrieving property " + "`%2 %3.%4'"); + lastError = QDBusError(QDBusError::InvalidSignature, + errmsg.arg(QLatin1String(reply.at(0).typeName()), + QLatin1String(mp.typeName()), + interface, QString::fromUtf8(mp.name()))); + } + + return QVariant(); +} + +void QDBusAbstractInterfacePrivate::setProperty(const QMetaProperty &mp, const QVariant &value) +{ + // send the value + QDBusMessage msg = QDBusMessage::methodCall(service, path, DBUS_INTERFACE_PROPERTIES, + QLatin1String("Set")); + msg.setSignature(QLatin1String("ssv")); + msg << interface << QString::fromUtf8(mp.name()) << value; + QDBusMessage reply = connp->sendWithReply(msg, QDBusConnection::NoUseEventLoop); + + if (reply.type() != QDBusMessage::ReplyMessage) + lastError = reply; +} + +/*! + \class QDBusAbstractInterface + \brief Base class for all D-Bus interfaces in the QtDBus binding, allowing access to remote interfaces. + + Generated-code classes also derive from QDBusAbstractInterface, all methods described here are also + valid for generated-code classes. In addition to those described here, generated-code classes + provide member functions for the remote methods, which allow for compile-time checking of the + correct parameters and return values, as well as property type-matching and signal + parameter-matching. + + \sa {dbusidl2cpp.html}{The dbusidl2cpp compiler}, QDBusInterface +*/ + +/*! + \enum QDBusAbstractInterface::CallMode + + Specifies how a call should be placed. The valid options are: + \value NoWaitForReply place the call but don't wait for the reply (the reply's contents + will be discarded) + \value NoUseEventLoop don't use an event loop to wait for a reply, but instead block on + network operations while waiting. This option means the + user-interface may not be updated for the duration of the call. + \value UseEventLoop use the Qt event loop to wait for a reply. This option means the + user-interface will update, but it also means other events may + happen, like signal delivery and other D-Bus method calls. + + When using UseEventLoop, applications must be prepared for reentrancy in any function. +*/ + +/*! + \internal +*/ +QDBusAbstractInterface::QDBusAbstractInterface(QDBusAbstractInterfacePrivate* d) +#if QT_VERSION < 0x040200 + : d_ptr(d) +{ + d_ptr->q_ptr = this; +} +#endif + +/*! + Releases this object's resources. +*/ +QDBusAbstractInterface::~QDBusAbstractInterface() +{ + delete d_ptr; +} + +/*! + Returns true if this is a valid reference to a remote object. It returns false if + there was an error during the creation of this interface (for instance, if the remote + application does not exist). + + Note: when dealing with remote objects, it is not always possible to determine if it + exists when creating a QDBusInterface or QDBusInterfacePtr object. +*/ +bool QDBusAbstractInterface::isValid() const +{ + return d_func()->isValid; +} + +/*! + Returns the connection this interface is assocated with. +*/ +QDBusConnection QDBusAbstractInterface::connection() const +{ + return d_func()->conn; +} + +/*! + Returns the name of the service this interface is associated with. +*/ +QString QDBusAbstractInterface::service() const +{ + return d_func()->service; +} + +/*! + Returns the object path that this interface is associated with. +*/ +QString QDBusAbstractInterface::path() const +{ + return d_func()->path; +} + +/*! + Returns the name of this interface. +*/ +QString QDBusAbstractInterface::interface() const +{ + return d_func()->interface; +} + +/*! + Returns the error the last operation produced, or an invalid error if the last operation did not + produce an error. +*/ +QDBusError QDBusAbstractInterface::lastError() const +{ + return d_func()->lastError; +} + +/*! + \threadsafe + Places a call to the remote method specified by \a method on this interface, using \a args as + arguments. This function returns the message that was received as a reply, which can be a normal + QDBusMessage::ReplyMessage (indicating success) or QDBusMessage::ErrorMessage (if the call + failed). The \a mode parameter specifies how this call should be placed. + + If the call succeeds, lastError() will be cleared; otherwise, it will contain the error this + call produced. + + Normally, you should place calls using call(). + + \warning If you use \c UseEventLoop, your code must be prepared to deal with any reentrancy: + other method calls and signals may be delivered before this function returns, as well + as other Qt queued signals and events. +*/ +QDBusMessage QDBusAbstractInterface::callWithArgs(const QString& method, const QList<QVariant>& args, + CallMode mode) +{ + Q_D(QDBusAbstractInterface); + + QString m = method, sig; + // split out the signature from the method + int pos = method.indexOf(QLatin1Char('.')); + if (pos != -1) { + m.truncate(pos); + sig = method.mid(pos + 1); + } + + if (mode == AutoDetect) { + // determine if this a sync or async call + mode = NoUseEventLoop; + const QMetaObject *mo = metaObject(); + QByteArray match = method.toLatin1() + '('; + + for (int i = staticMetaObject.methodCount(); i < mo->methodCount(); ++i) { + QMetaMethod mm = mo->method(i); + if (QByteArray(mm.signature()).startsWith(match)) { + // found a method with the same name as what we're looking for + // hopefully, nobody is overloading asynchronous and synchronous methods with + // the same name + + QList<QByteArray> tags = QByteArray(mm.tag()).split(' '); + if (tags.contains("async") || tags.contains("Q_ASYNC")) + mode = NoWaitForReply; + + break; + } + } + } + + QDBusMessage msg = QDBusMessage::methodCall(service(), path(), interface(), m); + msg.setSignature(sig); + msg.QList<QVariant>::operator=(args); + + QDBusMessage reply; + if (mode != NoWaitForReply) + reply = d->conn.sendWithReply(msg, mode == UseEventLoop ? + QDBusConnection::UseEventLoop : QDBusConnection::NoUseEventLoop); + 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; +} + +/*! + \overload + Places a call to the remote method specified by \a method on this interface, using \a args as + arguments. This function will return immediately after queueing the call. The reply from the + remote function or any errors emitted by it will be delivered to the \a slot slot on object \a + receiver. + + This function returns true if the queueing succeeded: it does not indicate that the call + succeeded. If it failed, the slot will be called with an error message. lastError() will not be + set under those circumstances. + + \sa QDBusError, QDBusMessage +*/ +bool QDBusAbstractInterface::callWithArgs(const QString &method, QObject *receiver, const char *slot, + const QList<QVariant> &args) +{ + Q_D(QDBusAbstractInterface); + + QString m = method, sig; + // split out the signature from the method + int pos = method.indexOf(QLatin1Char('.')); + if (pos != -1) { + m.truncate(pos); + sig = method.mid(pos + 1); + } + + QDBusMessage msg = QDBusMessage::methodCall(service(), path(), interface(), m); + msg.setSignature(sig); + msg.QList<QVariant>::operator=(args); + + d->lastError = 0; // clear + return d->conn.sendWithReplyAsync(msg, receiver, slot); +} + +/*! + \internal + Catch signal connections. +*/ +void QDBusAbstractInterface::connectNotify(const char *signal) +{ + // someone connecting to one of our signals + Q_D(QDBusAbstractInterface); + + d->connp->connectRelay(d->service, d->path, d->interface, this, signal); +} + +/*! + \internal + Catch signal disconnections. +*/ +void QDBusAbstractInterface::disconnectNotify(const char *signal) +{ + // someone disconnecting from one of our signals + Q_D(QDBusAbstractInterface); + + d->connp->disconnectRelay(d->service, d->path, d->interface, this, signal); +} + +/*! + \internal + Get the value of the property \a propname. +*/ +QVariant QDBusAbstractInterface::internalPropGet(const char *propname) const +{ + // assume this property exists and is readable + // we're only called from generated code anyways + + int idx = metaObject()->indexOfProperty(propname); + if (idx != -1) + return d_func()->property(metaObject()->property(idx)); + qWarning("QDBusAbstractInterface::internalPropGet called with unknown property '%s'", propname); + return QVariant(); // error +} + +/*! + \internal + Set the value of the property \a propname to \a value. +*/ +void QDBusAbstractInterface::internalPropSet(const char *propname, const QVariant &value) +{ + Q_D(QDBusAbstractInterface); + + // assume this property exists and is writeable + // we're only called from generated code anyways + + int idx = metaObject()->indexOfProperty(propname); + if (idx != -1) + d->setProperty(metaObject()->property(idx), value); + else + qWarning("QDBusAbstractInterface::internalPropGet called with unknown property '%s'", propname); +} + +/*! + \overload + \fn QDBusMessage QDBusAbstractInterface::call(const QString &method) + + Calls the method \a method on this interface and passes the parameters to this function to the + method. + + The parameters to \c call are passed on to the remote function via D-Bus as input + arguments. Output arguments are returned in the QDBusMessage reply. If the reply is an error + reply, lastError() will also be set to the contents of the error message. + + This function is implemented by actually 9 different function overloads called \c call, so you + can pass up to 8 parameters to your function call, which can be of any type accepted by QtDBus + (see the \l {allowedparameters.html}{allowed parameters} page for information on what types are + accepted). + + It can be used the following way: + + \code + QString value = retrieveValue(); + QDBusMessage reply; + + QDBusReply<int> api = interface->call(QLatin1String("GetAPIVersion")); + if (api >= 14) + reply = interface->call(QLatin1String("ProcessWorkUnicode"), value); + else + reply = interface->call(QLatin1String("ProcessWork"), QLatin1String("UTF-8"), value.toUtf8()); + \endcode + + This example illustrates function calling with 0, 1 and 2 parameters and illustrates different + parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one + Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array). + + \warning This function reenters the Qt event loop in order to wait for the reply, excluding user + input. During the wait, it may deliver signals and other method calls to your + application. Therefore, it must be prepared to handle a reentrancy whenever a call is + placed with call(). +*/ + +#include "qdbusabstractinterface.moc" diff --git a/qt/src/qdbusabstractinterface.h b/qt/src/qdbusabstractinterface.h new file mode 100644 index 0000000..aa6d00d --- /dev/null +++ b/qt/src/qdbusabstractinterface.h @@ -0,0 +1,257 @@ +/* -*- 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 QDBUSABSTRACTINTERFACE_H +#define QDBUSABSTRACTINTERFACE_H + +#include <QtCore/qstring.h> +#include <QtCore/qvariant.h> +#include <QtCore/qlist.h> +#include <QtCore/qobject.h> + +#include "qdbusmessage.h" +#include "qdbustypehelper_p.h" + +class QDBusConnection; +class QDBusError; + +class QDBusAbstractInterfacePrivate; +class QDBUS_EXPORT QDBusAbstractInterface: public QObject +{ + Q_OBJECT + +public: + enum CallMode { + NoWaitForReply, + UseEventLoop, + NoUseEventLoop, + AutoDetect + }; + +public: + virtual ~QDBusAbstractInterface(); + bool isValid() const; + + QDBusConnection connection() const; + + QString service() const; + QString path() const; + QString interface() const; + + QDBusError lastError() const; + + QDBusMessage callWithArgs(const QString &method, const QList<QVariant> &args = QList<QVariant>(), + CallMode mode = AutoDetect); + bool callWithArgs(const QString &method, QObject *receiver, const char *slot, + const QList<QVariant> &args = QList<QVariant>()); + + inline QDBusMessage call(const QString &m) + { + return callWithArgs(m); + } + + inline QDBusMessage call(CallMode mode, const QString &m) + { + return callWithArgs(m, QList<QVariant>(), mode); + } + +#ifndef Q_QDOC +private: + template<typename T> inline QVariant qvfv(const T &t) + { return QDBusTypeHelper<T>::toVariant(t); } + +public: + template<typename T1> + inline QDBusMessage call(const QString &m, const T1 &t1) + { + QList<QVariant> args; + args << qvfv(t1); + return callWithArgs(m, args); + } + + template<typename T1, typename T2> + inline QDBusMessage call(const QString &m, const T1 &t1, const T2 &t2) + { + QList<QVariant> args; + args << qvfv(t1) << qvfv(t2); + return callWithArgs(m, args); + } + + template<typename T1, typename T2, typename T3> + inline QDBusMessage call(const QString &m, const T1 &t1, const T2 &t2, const T3 &t3) + { + QList<QVariant> args; + args << qvfv(t1) << qvfv(t2) << qvfv(t3); + return callWithArgs(m, args); + } + + template<typename T1, typename T2, typename T3, typename T4> + inline QDBusMessage call(const QString &m, const T1 &t1, const T2 &t2, const T3 &t3, + const T4 &t4) + { + QList<QVariant> args; + args << qvfv(t1) << qvfv(t2) << qvfv(t3) + << qvfv(t4); + return callWithArgs(m, args); + } + + template<typename T1, typename T2, typename T3, typename T4, typename T5> + inline QDBusMessage call(const QString &m, const T1 &t1, const T2 &t2, const T3 &t3, + const T4 &t4, const T5 &t5) + { + QList<QVariant> args; + args << qvfv(t1) << qvfv(t2) << qvfv(t3) + << qvfv(t4) << qvfv(t5); + return callWithArgs(m, args); + } + + template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6> + inline QDBusMessage call(const QString &m, const T1 &t1, const T2 &t2, const T3 &t3, + const T4 &t4, const T5 &t5, const T6 &t6) + { + QList<QVariant> args; + args << qvfv(t1) << qvfv(t2) << qvfv(t3) + << qvfv(t4) << qvfv(t5) << qvfv(t6); + return callWithArgs(m, args); + } + + template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7> + inline QDBusMessage call(const QString &m, const T1 &t1, const T2 &t2, const T3 &t3, + const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7) + { + QList<QVariant> args; + args << qvfv(t1) << qvfv(t2) << qvfv(t3) + << qvfv(t4) << qvfv(t5) << qvfv(t6) + << qvfv(t7); + return callWithArgs(m, args); + } + + template<typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8> + inline QDBusMessage call(const QString &m, const T1 &t1, const T2 &t2, const T3 &t3, + const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7, const T8 &t8) + { + QList<QVariant> args; + args << qvfv(t1) << qvfv(t2) << qvfv(t3) + << qvfv(t4) << qvfv(t5) << qvfv(t6) + << qvfv(t7) << qvfv(t8); + return callWithArgs(m, args); + } + + template<typename T1> + inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1) + { + QList<QVariant> args; + args << qvfv(t1); + return callWithArgs(m, args, mode); + } + + template<typename T1, typename T2> + inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1, const T2 &t2) + { + QList<QVariant> args; + args << qvfv(t1) << qvfv(t2); + return callWithArgs(m, args, mode); + } + + template<typename T1, typename T2, typename T3> + inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1, const T2 &t2, + const T3 &t3) + { + QList<QVariant> args; + args << qvfv(t1) << qvfv(t2) << qvfv(t3); + return callWithArgs(m, args, mode); + } + + template<typename T1, typename T2, typename T3, typename T4> + inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1, const T2 &t2, + const T3 &t3, const T4 &t4) + { + QList<QVariant> args; + args << qvfv(t1) << qvfv(t2) << qvfv(t3) + << qvfv(t4); + return callWithArgs(m, args, mode); + } + + template<typename T1, typename T2, typename T3, typename T4, typename T5> + inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1, const T2 &t2, + const T3 &t3, const T4 &t4, const T5 &t5) + { + QList<QVariant> args; + args << qvfv(t1) << qvfv(t2) << qvfv(t3) + << qvfv(t4) << qvfv(t5); + return callWithArgs(m, args, mode); + } + + template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6> + inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1, const T2 &t2, + const T3 &t3, const T4 &t4, const T5 &t5, const T6 &t6) + { + QList<QVariant> args; + args << qvfv(t1) << qvfv(t2) << qvfv(t3) + << qvfv(t4) << qvfv(t5) << qvfv(t6); + return callWithArgs(m, args, mode); + } + + template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7> + inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1, const T2 &t2, + const T3 &t3, const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7) + { + QList<QVariant> args; + args << qvfv(t1) << qvfv(t2) << qvfv(t3) + << qvfv(t4) << qvfv(t5) << qvfv(t6) + << qvfv(t7); + return callWithArgs(m, args, mode); + } + + template<typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8> + inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1, const T2 &t2, + const T3 &t3, const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7, + const T8 &t8) + { + QList<QVariant> args; + args << qvfv(t1) << qvfv(t2) << qvfv(t3) + << qvfv(t4) << qvfv(t5) << qvfv(t6) + << qvfv(t7) << qvfv(t8); + return callWithArgs(m, args, mode); + } +#endif + +protected: + QDBusAbstractInterface(QDBusAbstractInterfacePrivate *); + void connectNotify(const char *signal); + void disconnectNotify(const char *signal); + QVariant internalPropGet(const char *propname) const; + void internalPropSet(const char *propname, const QVariant &value); + +private: + friend class QDBusInterface; + QDBusAbstractInterfacePrivate *d_ptr; // remove for Qt 4.2.0 + + Q_DECLARE_PRIVATE(QDBusAbstractInterface) + Q_DISABLE_COPY(QDBusAbstractInterface) +}; + +#endif diff --git a/qt/src/qdbusabstractinterface_p.h b/qt/src/qdbusabstractinterface_p.h new file mode 100644 index 0000000..4380707 --- /dev/null +++ b/qt/src/qdbusabstractinterface_p.h @@ -0,0 +1,72 @@ +/* + * + * 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 QDBUSABSTRACTINTERFACEPRIVATE_H +#define QDBUSABSTRACTINTERFACEPRIVATE_H + +#include "qdbusabstractinterface.h" +#include "qdbusconnection.h" +#include "qdbuserror.h" + +#define ANNOTATION_NO_WAIT "org.freedesktop.DBus.Method.NoReply" + +class QDBusAbstractInterfacePrivate//: public QObjectPrivate +{ +public: + Q_DECLARE_PUBLIC(QDBusAbstractInterface) + + QDBusAbstractInterface *q_ptr; // remove in Qt 4.2 + QDBusConnection conn; + QDBusConnectionPrivate *connp; + QString service; + QString path; + QString interface; + mutable QDBusError lastError; + bool isValid; + + inline QDBusAbstractInterfacePrivate(const QDBusConnection& con, QDBusConnectionPrivate *conp, + const QString &serv, const QString &p, const QString &iface) + : conn(con), connp(conp), service(serv), path(p), interface(iface), isValid(true) + { } + virtual ~QDBusAbstractInterfacePrivate() { } + + // these functions do not check if the property is valid + QVariant property(const QMetaProperty &mp) const; + void setProperty(const QMetaProperty &mp, const QVariant &value); +}; + + +#endif diff --git a/qt/src/qdbusbus.cpp b/qt/src/qdbusbus.cpp new file mode 100644 index 0000000..02c231e --- /dev/null +++ b/qt/src/qdbusbus.cpp @@ -0,0 +1,302 @@ +/* -*- C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * 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. + * + */ + +/* + * This file was generated by dbusidl2cpp version 0.3 + * when processing input file - + * + * dbusidl2cpp is Copyright (C) 2006 Trolltech AS. All rights reserved. + * + * This file has been hand-edited! Be careful when re-generating it! + * + */ + +#include "qdbusbus.h" + +#include <QtCore/QByteArray> +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QVariant> + +/* + * Implementation of interface class QDBusBusService + */ + +/*! + \class QDBusBusService + \brief Provides access to the D-Bus bus daemon service. + +*/ + +/*! + \enum QDBusBusService::RequestNameOption + + Flags for requesting a name on the bus. + + \value AllowReplacingName Allow another application requesting the same name to take the name + from this application. + \value ReplaceExistingName If another application already has the name and allows replacing, + take the name and assign it to us. + \value DoNotQueueName Without this flag, if an application requests a name that is already + owned and does not allow replacing, it will be queued until the + name is given up. If this flag is given, no queueing will be + performed and the requestName() call will simply fail. +*/ + +/*! + \enum QDBusBusService::RequestNameReply + + The possible return values from requestName(): + + \value PrimaryOwnerReply The caller is now the primary owner of the name. + \value InQueueReply The caller is in queue for the name, but does not own it. + \value NameExistsReply The name exists and could not be replaced, or the caller did + specify DoNotQueueName. + \value AlreadyOwnerReply The caller tried to request a name that it already owns. +*/ + +/*! + \enum QDBusBusService::ReleaseNameReply + + The possible return values from releaseName(): + + \value NameReleasedReply The caller released his claim on the name. + \value NameNonExistentReply The caller tried to release a name that did not exist. + \value NotOwnerReply The caller tried to release a name that it did not own or was not in + queue for. +*/ + +/*! + \enum QDBusBusService::StartServiceReply + + The possible return values from startServiceByName(): + + \value Success The service was successfully started. + \value AlreadyRunning The service was already running. +*/ + +/*! + \internal +*/ +const char *QDBusBusService::staticInterfaceName() +{ return "org.freedesktop.DBus"; } + + +/*! + \internal +*/ +QDBusBusService::QDBusBusService(QDBusAbstractInterfacePrivate *p) + : QDBusAbstractInterface(p) +{ + connect(this, SIGNAL(NameAcquired(QString)), this, SIGNAL(nameAcquired(QString))); + connect(this, SIGNAL(NameLost(QString)), this, SIGNAL(nameLost(QString))); + connect(this, SIGNAL(NameOwnerChanged(QString,QString,QString)), + this, SIGNAL(nameOwnerChanged(QString,QString,QString))); +} + +/*! + \internal +*/ +QDBusBusService::~QDBusBusService() +{ +} + +/*! + \fn QDBusBusService::hello() + \internal + Sends a "Hello" request to the bus service. You do not want to call this. +*/ +QDBusReply<QString> QDBusBusService::Hello() +{ + return call(QLatin1String("Hello")); +} + +/*! + \fn QDBusBusService::nameOwner(const QString &name) + Returns the unique connection name of the primary owner of the name \a name. If the requested + name doesn't have an owner, returns a org.freedesktop.DBus.Error.NameHasNoOwner error. +*/ +QDBusReply<QString> QDBusBusService::GetNameOwner(const QString &name) +{ + return call(QLatin1String("GetNameOwner.s"), name); +} + +/*! + \fn QDBusBusService::listNames() + Lists all names currently existing on the bus. +*/ +QDBusReply<QStringList> QDBusBusService::ListNames() +{ + return call(QLatin1String("ListNames")); +} + +/*! + \fn QDBusBusService::listQueuedOwners(const QString &service) + Returns a list of all unique connection names in queue for the service name \a service. +*/ +QDBusReply<QStringList> QDBusBusService::ListQueuedOwners(const QString &service) +{ + return call(QLatin1String("ListQueuedOwners.s"), service); +} + +/*! + \fn QDBusBusService::nameHasOwner(const QString &service) + Returns true if the service name \a service has an owner. +*/ +QDBusReply<bool> QDBusBusService::NameHasOwner(const QString &service) +{ + return call(QLatin1String("NameHasOwner.s"), service); +} + +/*! + \fn QDBusBusService::addMatch(const QString &rule) + Adds the rule \a rule for requesting messages from the bus. + + \sa removeMatch() +*/ +QDBusReply<void> QDBusBusService::AddMatch(const QString &rule) +{ + return call(QLatin1String("AddMatch.s"), rule); +} + +/*! + \fn QDBusBusService::removeMatch(const QString &rule) + Removes the rule \a rule, that had previously been added with addMatch(). +*/ +QDBusReply<void> QDBusBusService::RemoveMatch(const QString &rule) +{ + return call(QLatin1String("RemoveMatch.s"), rule); +} + +/*! + \fn QDBusBusService::connectionSELinuxSecurityContext(const QString &service) + Returns the SELinux security context of the process currently holding the bus service \a + service. +*/ +QDBusReply<QByteArray> QDBusBusService::GetConnectionSELinuxSecurityContext(const QString &service) +{ + return call(QLatin1String("GetConnectionSELinuxSecurityContext.s"), service); +} + +/*! + \fn QDBusBusService::connectionUnixProcessID(const QString &service) + Returns the Unix Process ID (PID) for the process currently holding the bus service \a service. +*/ +QDBusReply<uint> QDBusBusService::GetConnectionUnixProcessID(const QString &service) +{ + return call(QLatin1String("GetConnectionUnixProcessID.s"), service); +} + +/*! + \fn QDBusBusService::connectionUnixUser(const QString &service) + Returns the Unix User ID (UID) for the process currently holding the bus service \a service. +*/ +QDBusReply<uint> QDBusBusService::GetConnectionUnixUser(const QString &service) +{ + return call(QLatin1String("GetConnectionUnixUser.s"), service); +} + +/*! + \fn QDBusBusService::reloadConfig() + Asks the D-Bus server daemon to reload its configuration. +*/ +QDBusReply<void> QDBusBusService::ReloadConfig() +{ + return call(QLatin1String("ReloadConfig")); +} + +inline QDBUS_EXPORT int qDBusMetaTypeId(QDBusBusService::StartServiceReply *) +{ return QVariant::Int; } + +/*! + \fn QDBusBusService::startServiceByName(const QString &name, uint flags) + Requests that the bus start the service given by the name \a name. + + The \a flags parameter is currently not used. +*/ +QDBusReply<QDBusBusService::StartServiceReply> +QDBusBusService::StartServiceByName(const QString &name, uint flags) +{ + return call(QLatin1String("StartServiceByName.su"), name, flags); +} + +inline QDBUS_EXPORT int qDBusMetaTypeId(QDBusBusService::RequestNameReply *) +{ return QVariant::Int; } + +/*! + \fn QDBusBusService::requestName(const QString &service, RequestNameOptions flags) + Requests the bus service name \a service from the bus. The \a flags parameter specifies how the + bus server daemon should act when the same name is requested by two different applications. + + \sa releaseName() +*/ +QDBusReply<QDBusBusService::RequestNameReply> +QDBusBusService::RequestName(const QString &service, RequestNameOptions flags) +{ + return call(QLatin1String("RequestName.su"), service, uint(int(flags))); +} + +inline QDBUS_EXPORT int qDBusMetaTypeId(QDBusBusService::ReleaseNameReply *) +{ return QVariant::Int; } + +/*! + \fn QDBusBusService::releaseName(const QString &service) + Releases the claim on the bus service name \a service, that had been previously requested with + requestName(). If this application had ownership of the name, it will be released for other + applications to claim. If it only had the name queued, it gives up its position in the queue. +*/ +QDBusReply<QDBusBusService::ReleaseNameReply> +QDBusBusService::ReleaseName(const QString &service) +{ + return call(QLatin1String("ReleaseName.s"), service); +} + +// signals +/*! + \fn QDBusBusService::nameAcquired(const QString &service) + + This signal is emitted by the D-Bus bus server when the bus service name (unique connection name + or well-known service name) given by \a service is acquired by this application. + + Name acquisition happens after the application requested a name using requestName(). +*/ + +/*! + \fn QDBusBusService::nameLost(const QString &service) + + This signal is emitted by the D-Bus bus server when the application loses ownership of the bus + service name given by \a service. +*/ + +/*! + \fn QDBusBusService::nameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) + + This signal is emitted by the D-Bus bus server whenever a name ownership change happens in the + bus, including apparition and disparition of names. + + This signal means the application \a oldOwner lost ownership of bus name \a name to application + \a newOwner. If \a oldOwner is an empty string, it means the name \a name has just been created; + if \a newOwner is empty, the name \a name has no current owner. +*/ + +#include "qdbusbus.moc" diff --git a/qt/src/qdbusbus.h b/qt/src/qdbusbus.h new file mode 100644 index 0000000..2260685 --- /dev/null +++ b/qt/src/qdbusbus.h @@ -0,0 +1,158 @@ +/* -*- C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * 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. + * + */ + +/* + * This file was generated by dbusidl2cpp version 0.3 + * when processing input file - + * + * dbusidl2cpp is Copyright (C) 2006 Trolltech AS. All rights reserved. + * + * This file has been hand-edited! Be careful when re-generating it! + * + */ + +#ifndef QDBUSBUS_H +#define QDBUSBUS_H + +#include <QtCore/qstringlist.h> + +#include "qdbusabstractinterface.h" +#include "qdbusreply.h" + +class QDBusConnection; +class QString; +class QByteArray; + +/* + * Proxy class for interface org.freedesktop.DBus + */ +class QDBUS_EXPORT QDBusBusService: public QDBusAbstractInterface +{ + Q_OBJECT + friend class QDBusConnection; + static inline const char *staticInterfaceName(); + + explicit QDBusBusService(QDBusAbstractInterfacePrivate *p); + + ~QDBusBusService(); + +public: + // taken out of http://dbus.freedesktop.org/doc/dbus-specification.html + // update if the standard updates + enum RequestNameOption { + AllowReplacingName = 0x1, + ReplaceExistingName = 0x2, + DoNotQueueName = 0x4 + }; + Q_DECLARE_FLAGS(RequestNameOptions, RequestNameOption) + + enum RequestNameReply { + PrimaryOwnerReply = 1, + InQueueReply = 2, + NameExistsReply = 3, + AlreadyOwnerReply = 4 + }; + + enum ReleaseNameReply { + NameReleasedReply = 1, + NameNonExistentReply = 2, + NotOwnerReply = 3 + }; + + enum StartServiceReply { + Success = 1, + AlreadyRunning = 2 + }; + +#ifndef Q_QDOC + // D-Bus names +public: // METHODS + QDBusReply<QString> Hello(); + QDBusReply<void> ReloadConfig(); + + QDBusReply<QStringList> ListNames(); + + QDBusReply<bool> NameHasOwner(const QString &service); + QDBusReply<QString> GetNameOwner(const QString &name); + QDBusReply<ReleaseNameReply> ReleaseName(const QString &service); + QDBusReply<RequestNameReply> RequestName(const QString &service, RequestNameOptions flags); + QDBusReply<QStringList> ListQueuedOwners(const QString &service); + + QDBusReply<void> AddMatch(const QString &rule); + QDBusReply<void> RemoveMatch(const QString &rule); + + QDBusReply<QByteArray> GetConnectionSELinuxSecurityContext(const QString &service); + QDBusReply<uint> GetConnectionUnixProcessID(const QString &service); + QDBusReply<uint> GetConnectionUnixUser(const QString &service); + + QDBusReply<StartServiceReply> StartServiceByName(const QString &name, uint flags); + +signals: // SIGNALS + void NameAcquired(const QString &service); + void NameLost(const QString &service); + void NameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); +#endif + + // Qt-style naming +public slots: + QDBusReply<QString> hello() + { return Hello(); } + QDBusReply<void> reloadConfig() + { return ReloadConfig(); } + + QDBusReply<QStringList> listNames() + { return ListNames(); } + + QDBusReply<bool> nameHasOwner(const QString &service) + { return NameHasOwner(service); } + QDBusReply<QString> nameOwner(const QString &name) + { return GetNameOwner(name); } + QDBusReply<ReleaseNameReply> releaseName(const QString &service) + { return ReleaseName(service); } + QDBusReply<RequestNameReply> requestName(const QString &service, RequestNameOptions flags) + { return RequestName(service, flags); } + QDBusReply<QStringList> listQueuedOwners(const QString &service) + { return ListQueuedOwners(service); } + + QDBusReply<void> addMatch(const QString &rule) + { return AddMatch(rule); } + QDBusReply<void> removeMatch(const QString &rule) + { return RemoveMatch(rule); } + + QDBusReply<QByteArray> connectionSELinuxSecurityContext(const QString &service) + { return GetConnectionSELinuxSecurityContext(service); } + QDBusReply<uint> connectionUnixProcessID(const QString &service) + { return GetConnectionUnixProcessID(service); } + QDBusReply<uint> connectionUnixUser(const QString &service) + { return GetConnectionUnixUser(service); } + + QDBusReply<StartServiceReply> startServiceByName(const QString &name, uint flags) + { return StartServiceByName(name, flags); } + +signals: + void nameAcquired(const QString &service); + void nameLost(const QString &service); + void nameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDBusBusService::RequestNameOptions) + +#endif diff --git a/qt/src/qdbusconnection.cpp b/qt/src/qdbusconnection.cpp new file mode 100644 index 0000000..6cd733c --- /dev/null +++ b/qt/src/qdbusconnection.cpp @@ -0,0 +1,753 @@ +/* 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 + * + * 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 <qdebug.h> +#include <qcoreapplication.h> +#include <qstringlist.h> + +#include "qdbusbus.h" +#include "qdbusconnection.h" +#include "qdbuserror.h" +#include "qdbusmessage.h" +#include "qdbusconnection_p.h" +#include "qdbusinterface_p.h" +#include "qdbusutil.h" + +class QDBusConnectionManager +{ +public: + QDBusConnectionManager() {} + ~QDBusConnectionManager(); + void bindToApplication(); + QDBusConnectionPrivate *connection(const QString &name) const; + void removeConnection(const QString &name); + void setConnection(const QString &name, QDBusConnectionPrivate *c); + +private: + mutable QMutex mutex; + QHash<QString, QDBusConnectionPrivate *> connectionHash; +}; + +Q_GLOBAL_STATIC(QDBusConnectionManager, manager) + +QDBusConnectionPrivate *QDBusConnectionManager::connection(const QString &name) const +{ + QMutexLocker locker(&mutex); + return connectionHash.value(name, 0); +} + +void QDBusConnectionManager::removeConnection(const QString &name) +{ + QMutexLocker locker(&mutex); + + QDBusConnectionPrivate *d = 0; + d = connectionHash.take(name); + if (d && !d->ref.deref()) + delete d; +} + +QDBusConnectionManager::~QDBusConnectionManager() +{ + for (QHash<QString, QDBusConnectionPrivate *>::const_iterator it = connectionHash.constBegin(); + it != connectionHash.constEnd(); ++it) { + delete it.value(); + } + connectionHash.clear(); +} + +void QDBusConnectionManager::bindToApplication() +{ + QMutexLocker locker(&mutex); + for (QHash<QString, QDBusConnectionPrivate *>::const_iterator it = connectionHash.constBegin(); + it != connectionHash.constEnd(); ++it) { + (*it)->bindToApplication(); + } +} + +QDBUS_EXPORT void qDBusBindToApplication(); +void qDBusBindToApplication() +{ + manager()->bindToApplication(); +} + +void QDBusConnectionManager::setConnection(const QString &name, QDBusConnectionPrivate *c) +{ + connectionHash[name] = c; + c->name = name; +} + +/*! + \fn QDBusConnection QDBus::sessionBus() + \relates QDBusConnection + + Returns a QDBusConnection object opened with the session bus. The object reference returned + by this function is valid until the QCoreApplication's destructor is run, when the + connection will be closed and the object, deleted. +*/ +/*! + \fn QDBusConnection QDBus::systemBus() + \relates QDBusConnection + + Returns a QDBusConnection object opened with the system bus. The object reference returned + by this function is valid until the QCoreApplication's destructor is run, when the + connection will be closed and the object, deleted. +*/ + +/*! + \class QDBusConnection + \brief A connection to the D-Bus bus daemon. + + This class is the initial point in a D-Bus session. Using it, you can get access to remote + objects, interfaces; connect remote signals to your object's slots; register objects, etc. + + D-Bus connections are created using the QDBusConnection::addConnection() function, which opens a + connection to the server daemon and does the initial handshaking, associating that connection + with a name. Further attempts to connect using the same name will return the same + connection. + + The connection is then torn down using the QDBusConnection::closeConnection() function. + + As a convenience for the two most common connection types, the QDBus::sessionBus() and + QDBus::systemBus() functions return open connections to the session server daemon and the system + server daemon, respectively. Those connections are opened when first used and are closed when + the QCoreApplication destructor is run. + + D-Bus also supports peer-to-peer connections, without the need for a bus server daemon. Using + this facility, two applications can talk to each other and exchange messages. This can be + achieved by passing an address to QDBusConnection::addConnection() + function, which was opened by another D-Bus application using QDBusServer. +*/ + +/*! + \enum QDBusConnection::BusType + Specifies the type of the bus connection. The valid bus types are: + + \value SessionBus the session bus, associated with the running desktop session + \value SystemBus the system bus, used to communicate with system-wide processes + \value ActivationBus the activation bus, whose purpose I have no idea... + + On the Session Bus, one can find other applications by the same user that are sharing the same + desktop session (hence the name). On the System Bus, however, processes shared for the whole + system are usually found. +*/ + +/*! + \enum QDBusConnection::WaitMode + Specifies the call waiting mode. + + \value UseEventLoop use the Qt Event Loop to wait for the reply + \value NoUseEventLoop don't use the event loop + + The \c UseEventLoop option allows for the application to continue to update its UI while the + call is performed, but it also opens up the possibility for reentrancy: socket notifiers may + fire, signals may be delivered and other D-Bus calls may be processed. The \c NoUseEventLoop + does not use the event loop, thus being safe from those problems, but it may block the + application for a noticeable period of time, in case the remote application fails to respond. + + Also note that calls that go back to the local application can only be placed in \c UseEventLoop + mode. +*/ + +/*! + \enum QDBusConnection::RegisterOption + Specifies the options for registering objects with the connection. The possible values are: + + \value ExportAdaptors export the contents of adaptors found in this object + + \value ExportSlots export this object's scriptable slots + \value ExportSignals export this object's scriptable signals + \value ExportProperties export this object's scriptable properties + \value ExportContents shorthand form for ExportSlots | ExportSignals | + ExportProperties + + \value ExportAllSlots export all of this object's slots, including + non-scriptable ones + \value ExportAllSignals export all of this object's signals, including + non-scriptable ones + \value ExportAllProperties export all of this object's properties, including + non-scriptable ones + \value ExportAllContents export all of this object's slots, signals and + properties, including non-scriptable ones + + \value ExportChildObjects export this object's child objects + + \warning It is currently not possible to export signals from objects. If you pass the flag + ExportSignals or ExportAllSignals, the registerObject() function will print a warning. + + \sa registerObject(), QDBusAbstractAdaptor, {usingadaptors.html}{Using adaptors} +*/ + +/*! + \enum QDBusConnection::UnregisterMode + The mode for unregistering an object path: + + \value UnregisterNode unregister this node only: do not unregister child objects + \value UnregisterTree unregister this node and all its sub-tree + + Note, however, if this object was registered with the ExportChildObjects option, UnregisterNode + will unregister the child objects too. +*/ + +/*! + Creates a QDBusConnection object attached to the connection with name \a name. + + This does not open the connection. You have to call QDBusConnection::addConnection to open it. +*/ +QDBusConnection::QDBusConnection(const QString &name) +{ + d = manager()->connection(name); + if (d) + d->ref.ref(); +} + +/*! + Creates a copy of the \a other connection. +*/ +QDBusConnection::QDBusConnection(const QDBusConnection &other) +{ + d = other.d; + if (d) + d->ref.ref(); +} + +/*! + Disposes of this object. This does not close the connection: you have to call + QDBusConnection::closeConnection to do that. +*/ +QDBusConnection::~QDBusConnection() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + Creates a copy of the connection \a other in this object. The connection this object referenced + before the copy is not spontaneously disconnected. See QDBusConnection::closeConnection for more + information. +*/ +QDBusConnection &QDBusConnection::operator=(const QDBusConnection &other) +{ + if (other.d) + other.d->ref.ref(); + QDBusConnectionPrivate *old = static_cast<QDBusConnectionPrivate *>( + q_atomic_set_ptr(&d, other.d)); + if (old && !old->ref.deref()) + delete old; + + return *this; +} + +/*! + Opens a connection of type \a type to one of the known busses and associate with it the + connection name \a name. Returns a QDBusConnection object associated with that connection. +*/ +QDBusConnection QDBusConnection::addConnection(BusType type, const QString &name) +{ +// Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection::addConnection", +// "Cannot create connection without a Q[Core]Application instance"); + + QDBusConnectionPrivate *d = manager()->connection(name); + if (d || name.isEmpty()) + return QDBusConnection(name); + + d = new QDBusConnectionPrivate; + DBusConnection *c = 0; + switch (type) { + case SystemBus: + c = dbus_bus_get_private(DBUS_BUS_SYSTEM, &d->error); + break; + case SessionBus: + c = dbus_bus_get_private(DBUS_BUS_SESSION, &d->error); + break; + case ActivationBus: + c = dbus_bus_get_private(DBUS_BUS_STARTER, &d->error); + break; + } + d->setConnection(c); //setConnection does the error handling for us + + manager()->setConnection(name, d); + + QDBusConnection retval(name); + + // create the bus service + QDBusAbstractInterfacePrivate *p; + p = retval.findInterface_helper(QLatin1String(DBUS_SERVICE_DBUS), + QLatin1String(DBUS_PATH_DBUS), + QLatin1String(DBUS_INTERFACE_DBUS)); + if (p) { + d->busService = new QDBusBusService(p); + d->busService->setParent(d); // auto-deletion + d->ref.deref(); // busService has a increased the refcounting to us + } + + return retval; +} + +/*! + Opens a peer-to-peer connection on address \a address and associate with it the + connection name \a name. Returns a QDBusConnection object associated with that connection. +*/ +QDBusConnection QDBusConnection::addConnection(const QString &address, + const QString &name) +{ +// Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection::addConnection", +// "Cannot create connection without a Q[Core]Application instance"); + + QDBusConnectionPrivate *d = manager()->connection(name); + if (d || name.isEmpty()) + return QDBusConnection(name); + + d = new QDBusConnectionPrivate; + // setConnection does the error handling for us + d->setConnection(dbus_connection_open(address.toUtf8().constData(), &d->error)); + + manager()->setConnection(name, d); + + QDBusConnection retval(name); + + // create the bus service + QDBusAbstractInterfacePrivate *p; + p = retval.findInterface_helper(QLatin1String(DBUS_SERVICE_DBUS), + QLatin1String(DBUS_PATH_DBUS), + QLatin1String(DBUS_INTERFACE_DBUS)); + if (p) { + d->busService = new QDBusBusService(p); + d->busService->setParent(d); // auto-deletion + d->ref.deref(); // busService has a increased the refcounting to us + } + + return retval; +} + +/*! + Closes the connection of name \a name. + + Note that if there are still QDBusConnection objects associated with the same connection, the + connection will not be closed until all references are dropped. However, no further references + can be created using the QDBusConnection::QDBusConnection constructor. +*/ +void QDBusConnection::closeConnection(const QString &name) +{ + manager()->removeConnection(name); +} + +/*! + Sends the \a message over this connection, without waiting for a reply. This is suitable for + errors, signals, and return values as well as calls whose return values are not necessary. + + Returns true if the message was queued successfully, false otherwise. +*/ +bool QDBusConnection::send(const QDBusMessage &message) const +{ + if (!d || !d->connection) + return false; + return d->send(message) != 0; +} + +/*! + Sends the \a message over this connection and returns immediately after queueing it. When the + reply is received, the slot \a method is called in the object \a receiver. This function is + suitable for method calls only. + + This function guarantees that the slot will be called exactly once with the reply, as long as + the parameter types match. If they don't, the reply cannot be delivered. + + Returns the identification of the message that was sent or 0 if nothing was sent. +*/ +int QDBusConnection::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, + const char *method) const +{ + if (!d || !d->connection) + return 0; + + return d->sendWithReplyAsync(message, receiver, method); +} + +/*! + Sends the \a message over this connection and blocks, waiting for a reply. This function is + suitable for method calls only. It returns the reply message as its return value, which will be + either of type QDBusMessage::ReplyMessage or QDBusMessage::ErrorMessage. + + See the QDBusInterface::call function for a more friendly way of placing calls. + + \warning If \a mode is \c UseEventLoop, this function will reenter the Qt event loop in order to + wait for the reply. During the wait, it may deliver signals and other method calls to + your application. Therefore, it must be prepared to handle a reentrancy whenever a call + is placed with sendWithReply. +*/ +QDBusMessage QDBusConnection::sendWithReply(const QDBusMessage &message, WaitMode mode) const +{ + if (!d || !d->connection) + return QDBusMessage(); + return d->sendWithReply(message, mode); +} + +/*! + Connects the signal specified by the \a service, \a path, \a interface and \a name parameters to + the slot \a slot in object \a receiver. The arguments \a service and \a path can be empty, + denoting a connection to any signal of the \a interface - \a name pair, from any remote + application. + + Returns true if the connection was successful. + + \warning The signal will only be delivered to the slot if the parameters match. This verification + can be done only when the signal is received, not at connection time. +*/ +bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface, + const QString &name, QObject *receiver, const char *slot) +{ + return connect(service, path, interface, name, QString(), receiver, slot); +} + +/*! + \overload + Connects the signal to the slot \a slot in object \a receiver. Unlike the other + QDBusConnection::connect overload, this function allows one to specify the parameter signature + to be connected using the \a signature variable. The function will then verify that this + signature can be delivered to the slot specified by \a slot and return false otherwise. +*/ +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 || !QDBusUtil::isValidInterfaceName(interface)) + return false; + + QString source; + if (!service.isEmpty()) { + source = d->getNameOwner(service); + if (source.isEmpty()) + return false; + } + + // check the slot + QDBusConnectionPrivate::SignalHook hook; + QString key; + hook.signature = signature; + if (!d->prepareHook(hook, key, source, path, interface, name, receiver, slot, 0, false)) + return false; // don't connect + + // avoid duplicating: + QWriteLocker locker(&d->lock); + QDBusConnectionPrivate::SignalHookHash::ConstIterator it = d->signalHooks.find(key); + for ( ; it != d->signalHooks.end() && it.key() == key; ++it) { + const QDBusConnectionPrivate::SignalHook &entry = it.value(); + if (entry.sender == hook.sender && + entry.path == hook.path && + entry.signature == hook.signature && + entry.obj == hook.obj && + entry.midx == hook.midx) { + // no need to compare the parameters if it's the same slot + return true; // already there + } + } + + + d->connectSignal(key, hook); + return true; +} + +/*! + Registers the object \a object at path \a path and returns true if the registration was + successful. The \a options parameter specifies how much of the object \a object will be exposed + through D-Bus. + + This function does not replace existing objects: if there is already an object registered at + path \a path, this function will return false. Use unregisterObject() to unregister it first. + + You cannot register an object as a child object of an object that was registered with + QDBusConnection::ExportChildObjects. +*/ +bool QDBusConnection::registerObject(const QString &path, QObject *object, RegisterOptions options) +{ + if (!d || !d->connection || !object || !options || !QDBusUtil::isValidObjectPath(path)) + return false; + + if (options & ExportSignals) { + qWarning("Cannot export signals from objects. Use an adaptor for that purpose."); + return false; + } + + QStringList pathComponents = path.split(QLatin1Char('/')); + if (pathComponents.last().isEmpty()) + pathComponents.removeLast(); + QWriteLocker locker(&d->lock); + + // lower-bound search for where this object should enter in the tree + QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode; + int i = 1; + while (node) { + if (pathComponents.count() == i) { + // this node exists + // consider it free if there's no object here and the user is not trying to + // replace the object sub-tree + if ((options & ExportChildObjects && !node->children.isEmpty()) || node->obj) + return false; + + // we can add the object here + node->obj = object; + node->flags = options; + + d->registerObject(node); + qDebug("REGISTERED FOR %s", path.toLocal8Bit().constData()); + return true; + } + + // find the position where we'd insert the node + QVector<QDBusConnectionPrivate::ObjectTreeNode::Data>::Iterator it = + qLowerBound(node->children.begin(), node->children.end(), pathComponents.at(i)); + if (it != node->children.constEnd() && it->name == pathComponents.at(i)) { + // match: this node exists + node = it->node; + + // are we allowed to go deeper? + if (node->flags & ExportChildObjects) { + // we're not + qDebug("Cannot register object at %s because %s exports its own child objects", + qPrintable(path), qPrintable(pathComponents.at(i))); + return false; + } + } else { + // add entry + QDBusConnectionPrivate::ObjectTreeNode::Data entry; + entry.name = pathComponents.at(i); + entry.node = new QDBusConnectionPrivate::ObjectTreeNode; + node->children.insert(it, entry); + + node = entry.node; + } + + // iterate + ++i; + } + + Q_ASSERT_X(false, "QDBusConnection::registerObject", "The impossible happened"); + return false; +} + +/*! + Unregisters an object that was registered with the registerObject() at the object path given by + \a path and, if \a mode is QDBusConnection::UnregisterTree, all of its sub-objects too. + + Note that you cannot unregister objects that were not registered with registerObject(). +*/ +void QDBusConnection::unregisterObject(const QString &path, UnregisterMode mode) +{ + if (!d || !d->connection || !QDBusUtil::isValidObjectPath(path)) + return; + + QStringList pathComponents = path.split(QLatin1Char('/')); + QWriteLocker locker(&d->lock); + QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode; + int i = 1; + + // find the object + while (node) { + if (pathComponents.count() == i) { + // found it + node->obj = 0; + node->flags = 0; + + if (mode == UnregisterTree) { + // clear the sub-tree as well + node->clear(); // can't disconnect the objects because we really don't know if they can + // be found somewhere else in the path too + } + + return; + } + + QVector<QDBusConnectionPrivate::ObjectTreeNode::Data>::ConstIterator it = + qLowerBound(node->children.constBegin(), node->children.constEnd(), pathComponents.at(i)); + if (it == node->children.constEnd() || it->name != pathComponents.at(i)) + break; // node not found + + node = it->node; + ++i; + } +} + +/*! + Returns a dynamic QDBusInterface associated with the interface \a interface on object at path \a + path on service \a service. + + This function creates a new object. It is your resposibility to ensure it is properly deleted + (you can use all normal QObject deletion mechanisms, including the QObject::deleteLater() slot + and QObject::setParent()). + + If the searching for this interface on the remote object failed, this function returns 0. +*/ +QDBusInterface *QDBusConnection::findInterface(const QString& service, const QString& path, + const QString& interface) +{ + if (!d) + return 0; + + QDBusInterfacePrivate *p = d->findInterface(service, path, interface); + QDBusInterface *retval = new QDBusInterface(p); + retval->setParent(d); + return retval; +} + +/*! + \fn QDBusConnection::findInterface(const QString &service, const QString &path) + Returns an interface of type \c Interface associated with the object on path \a path at service + \a service. + + \c Interface must be a class generated by \l {dbusidl2cpp.html}. + + This function creates a new object. It is your resposibility to ensure it is properly deleted + (you can use all normal QObject deletion mechanisms, including the QObject::deleteLater() slot + and QObject::setParent()). +*/ + +/*! + Returns a QDBusBusService object that represents the D-Bus bus service on this connection. + + This function returns 0 for peer-to-peer connections. +*/ +QDBusBusService *QDBusConnection::busService() const +{ + if (!d) + return 0; + return d->busService; +} + +QDBusAbstractInterfacePrivate * +QDBusConnection::findInterface_helper(const QString &service, const QString &path, + const QString &interface) +{ + if (!d) + return 0; + if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) + return 0; + + QString owner; + if (!service.isEmpty()) { + if (!QDBusUtil::isValidObjectPath(path)) + return 0; + + // check if it's there first -- FIXME: add binding mode + owner = d->getNameOwner(service); + if (owner.isEmpty()) + return 0; + } else if (!path.isEmpty()) + return 0; + + return new QDBusAbstractInterfacePrivate(*this, d, owner, path, interface); +} + +/*! + Returns true if this QDBusConnection object is connected. + + If it isn't connected, calling QDBusConnection::addConnection on the same connection name + will not make be connected. You need to call the QDBusConnection constructor again. +*/ +bool QDBusConnection::isConnected( ) const +{ + return d && d->connection && dbus_connection_get_is_connected(d->connection); +} + +/*! + Returns the last error that happened in this connection. + + This function is provided for low-level code. If you're using QDBusInterface::call, error codes are + reported by its return value. + + \sa QDBusInterface, QDBusMessage +*/ +QDBusError QDBusConnection::lastError() const +{ + return d ? d->lastError : QDBusError(); +} + +/*! + Returns the unique connection name for this connection, if this QDBusConnection object is + connected, or an empty QString otherwise. + + A Unique Connection Name is a string in the form ":x.xxx" (where x are decimal digits) that is + assigned by the D-Bus server daemon upon connection. It uniquely identifies this client in the + bus. + + This function returns an empty QString for peer-to-peer connections. +*/ +QString QDBusConnection::baseService() const +{ + return d && d->connection ? + QString::fromUtf8(dbus_bus_get_unique_name(d->connection)) + : QString(); +} + +Q_GLOBAL_STATIC(QMutex, defaultBussesMutex) +static const char sessionBusName[] = "qt_default_session_bus"; +static const char systemBusName[] = "qt_default_system_bus"; +static QDBusConnection *sessionBus = 0; +static QDBusConnection *systemBus = 0; + +static void closeConnections() +{ + QMutexLocker locker(defaultBussesMutex()); + delete sessionBus; + delete systemBus; + QDBusConnection::closeConnection(QLatin1String(sessionBusName)); + QDBusConnection::closeConnection(QLatin1String(systemBusName)); + sessionBus = systemBus = 0; +} + +static QDBusConnection *openConnection(QDBusConnection::BusType type) +{ + QMutexLocker locker(defaultBussesMutex()); + qAddPostRoutine(closeConnections); + + if (type == QDBusConnection::SystemBus) { + if (systemBus) + // maybe it got created before we locked the mutex + return systemBus; + systemBus = new QDBusConnection(QDBusConnection::addConnection(QDBusConnection::SystemBus, + QLatin1String(systemBusName))); + return systemBus; + } else { + if (sessionBus) + // maybe it got created before we locked the mutex + return sessionBus; + sessionBus = new QDBusConnection(QDBusConnection::addConnection(QDBusConnection::SessionBus, + QLatin1String(sessionBusName))); + return sessionBus; + } +} + +namespace QDBus { + QDBusConnection &sessionBus() + { + if (::sessionBus) return *::sessionBus; + return *openConnection(QDBusConnection::SessionBus); + } + + QDBusConnection &systemBus() + { + if (::systemBus) return *::systemBus; + return *openConnection(QDBusConnection::SystemBus); + } +} + diff --git a/qt/src/qdbusconnection.h b/qt/src/qdbusconnection.h new file mode 100644 index 0000000..c1c420a --- /dev/null +++ b/qt/src/qdbusconnection.h @@ -0,0 +1,124 @@ +/* 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 + * + * 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 QDBUSCONNECTION_H +#define QDBUSCONNECTION_H + +#include "qdbusmacros.h" +#include <QtCore/qstring.h> + +class QDBusAbstractInterfacePrivate; +class QDBusInterface; +class QDBusError; +class QDBusMessage; +class QDBusBusService; +class QObject; + +class QDBusConnectionPrivate; +class QDBUS_EXPORT QDBusConnection +{ +public: + enum BusType { SessionBus, SystemBus, ActivationBus }; + enum WaitMode { UseEventLoop, NoUseEventLoop }; + enum RegisterOption { + ExportAdaptors = 0x01, + + ExportSlots = 0x10, + ExportSignals = 0x20, + ExportProperties = 0x40, + ExportContents = 0xf0, + + ExportAllSlots = 0x110, + ExportAllSignals = 0x220, + ExportAllProperties = 0x440, + ExportAllContents = 0xff0, + + ExportChildObjects = 0x1000 + }; + enum UnregisterMode { + UnregisterNode, + UnregisterTree + }; + + Q_DECLARE_FLAGS(RegisterOptions, RegisterOption) + + QDBusConnection(const QString &name); + QDBusConnection(const QDBusConnection &other); + ~QDBusConnection(); + + QDBusConnection &operator=(const QDBusConnection &other); + + bool isConnected() const; + QString baseService() const; + QDBusError lastError() const; + + bool send(const QDBusMessage &message) const; + QDBusMessage sendWithReply(const QDBusMessage &message, WaitMode mode = NoUseEventLoop) const; + int sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, + const char *slot) const; + + bool connect(const QString &service, const QString &path, const QString &interface, + const QString &name, QObject *receiver, const char *slot); + bool connect(const QString &service, const QString &path, const QString &interface, + const QString &name, const QString& signature, + QObject *receiver, const char *slot); + + bool registerObject(const QString &path, QObject *object, + RegisterOptions options = ExportAdaptors); + void unregisterObject(const QString &path, UnregisterMode mode = UnregisterNode); + + template<class Interface> + inline Interface *findInterface(const QString &service, const QString &path); + QDBusInterface *findInterface(const QString& service, const QString& path, + const QString& interface = QString()); + + QDBusBusService *busService() const; + + static QDBusConnection addConnection(BusType type, const QString &name); + static QDBusConnection addConnection(const QString &address, const QString &name); + static void closeConnection(const QString &name); + +private: + QDBusAbstractInterfacePrivate *findInterface_helper(const QString &, const QString &, + const QString&); + QDBusConnectionPrivate *d; +}; + +template<class Interface> +inline Interface *QDBusConnection::findInterface(const QString &service, const QString &path) +{ + register QDBusAbstractInterfacePrivate *d; + d = findInterface_helper(service, path, Interface::staticInterfaceName()); + if (d) + return new Interface(d); + return 0; +} + +namespace QDBus { + QDBUS_EXPORT QDBusConnection &sessionBus(); + QDBUS_EXPORT QDBusConnection &systemBus(); +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDBusConnection::RegisterOptions) +#endif diff --git a/qt/src/qdbusconnection_p.h b/qt/src/qdbusconnection_p.h new file mode 100644 index 0000000..bab0b65 --- /dev/null +++ b/qt/src/qdbusconnection_p.h @@ -0,0 +1,245 @@ +/* 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 + * + * 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 QDBUSCONNECTION_P_H +#define QDBUSCONNECTION_P_H + +#include "qdbuserror.h" + +#include <QtCore/qatomic.h> +#include <QtCore/qeventloop.h> +#include <QtCore/qhash.h> +#include <QtCore/qmutex.h> +#include <QtCore/qobject.h> +#include <QtCore/qpointer.h> +#include <QtCore/qreadwritelock.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qvector.h> + +#include <dbus/dbus.h> + +#include "qdbusmessage.h" + +class QDBusMessage; +class QSocketNotifier; +class QTimerEvent; +class QDBusObjectPrivate; +class CallDeliveryEvent; +class QMetaMethod; +class QDBusInterfacePrivate; +struct QDBusMetaObject; +class QDBusAbstractInterface; +class QDBusBusService; + +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 + { + inline SignalHook() : obj(0), midx(-1) { } + QString sender, path, signature; + QObject* obj; + int midx; + QList<int> params; + }; + + struct ObjectTreeNode + { + struct Data + { + QString name; + ObjectTreeNode *node; + + inline bool operator<(const QString &other) const + { return name < other; } + }; + + inline ObjectTreeNode() : obj(0), flags(0) { } + inline ~ObjectTreeNode() { clear(); } + inline void clear() + { + foreach (const Data &entry, children) { + entry.node->clear(); + delete entry.node; + } + children.clear(); + } + + QObject* obj; + int flags; + QVector<Data> children; + }; + +public: + // typedefs + typedef QMultiHash<int, Watcher> WatcherHash; + typedef QHash<int, DBusTimeout *> TimeoutHash; + typedef QMultiHash<QString, SignalHook> SignalHookHash; + typedef QHash<QString, QDBusMetaObject* > MetaObjectHash; + +public: + // public methods + QDBusConnectionPrivate(QObject *parent = 0); + ~QDBusConnectionPrivate(); + + void bindToApplication(); + + void setConnection(DBusConnection *connection); + void setServer(DBusServer *server); + void closeConnection(); + void timerEvent(QTimerEvent *e); + + QString getNameOwner(const QString &service); + + int send(const QDBusMessage &message) const; + QDBusMessage sendWithReply(const QDBusMessage &message, int mode); + int sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, + const char *method); + void connectSignal(const QString &key, const SignalHook &hook); + void registerObject(const ObjectTreeNode *node); + void connectRelay(const QString &service, const QString &path, const QString &interface, + QDBusAbstractInterface *receiver, const char *signal); + void disconnectRelay(const QString &service, const QString &path, const QString &interface, + QDBusAbstractInterface *receiver, const char *signal); + + bool handleSignal(const QString &key, const QDBusMessage &msg); + bool handleSignal(const QDBusMessage &msg); + bool handleObjectCall(const QDBusMessage &message); + bool handleError(); + + bool activateSignal(const SignalHook& hook, const QDBusMessage &msg); + bool activateCall(QObject* object, int flags, const QDBusMessage &msg); + bool activateObject(const ObjectTreeNode *node, const QDBusMessage &msg); + bool activateInternalFilters(const ObjectTreeNode *node, const QDBusMessage &msg); + + void postCallDeliveryEvent(CallDeliveryEvent *data); + CallDeliveryEvent *postedCallDeliveryEvent(); + void deliverCall(const CallDeliveryEvent &data) const; + + QDBusInterfacePrivate *findInterface(const QString &service, const QString &path, + const QString &interface); + +protected: + virtual void customEvent(QEvent *event); + +private: + QDBusMetaObject *findMetaObject(const QString &service, const QString &path, + const QString &interface); + +public slots: + // public slots + void doDispatch(); + void socketRead(int); + void socketWrite(int); + void objectDestroyed(QObject *o); + void relaySignal(QObject *obj, const char *interface, const char *name, const QVariantList &args); + +public: + // public member variables + QString name; // this connection's name + + DBusError error; + QDBusError lastError; + + QAtomic ref; + QReadWriteLock lock; + ConnectionMode mode; + DBusConnection *connection; + DBusServer *server; + QDBusBusService *busService; + + WatcherHash watchers; + TimeoutHash timeouts; + SignalHookHash signalHooks; + QList<DBusTimeout *> pendingTimeouts; + + ObjectTreeNode rootNode; + MetaObjectHash cachedMetaObjects; + + QMutex callDeliveryMutex; + CallDeliveryEvent *callDeliveryState; // protected by the callDeliveryMutex mutex + +public: + // static methods + static int messageMetaType; + static int registerMessageMetaType(); + static int findSlot(QObject *obj, const QByteArray &normalizedName, QList<int>& params); + static bool prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key, + const QString &service, const QString &path, + const QString &interface, const QString &name, + QObject *receiver, const char *signal, int minMIdx, + bool buildSignature); + static DBusHandlerResult messageFilter(DBusConnection *, DBusMessage *, void *); + static void messageResultReceived(DBusPendingCall *, void *); +}; + +class QDBusReplyWaiter: public QEventLoop +{ + Q_OBJECT +public: + QDBusMessage replyMsg; + +public slots: + void reply(const QDBusMessage &msg); +}; + +// in qdbusmisc.cpp +extern int qDBusParametersForMethod(const QMetaMethod &mm, QList<int>& metaTypes); +extern int qDBusNameToTypeId(const char *name); +extern bool qDBusCheckAsyncTag(const char *tag); + +// in qdbusinternalfilters.cpp +extern QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode *node); +extern void qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode *node, + const QDBusMessage &msg); +extern void qDBusPropertyGet(const QDBusConnectionPrivate::ObjectTreeNode *node, + const QDBusMessage &msg); +extern void qDBusPropertySet(const QDBusConnectionPrivate::ObjectTreeNode *node, + const QDBusMessage &msg); + +#endif diff --git a/qt/src/qdbuserror.cpp b/qt/src/qdbuserror.cpp new file mode 100644 index 0000000..d5cd675 --- /dev/null +++ b/qt/src/qdbuserror.cpp @@ -0,0 +1,244 @@ +/* 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 + * + * 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 "qdbuserror.h" + +#include <qdebug.h> +#include <qvarlengtharray.h> + +#include <dbus/dbus.h> +#include "qdbusmessage.h" + +struct ErrorMessageMapping +{ + ErrorMessageMapping(); + QVarLengthArray<const char*, QDBusError::qKnownErrorsMax> messages; + + inline const char *get(QDBusError::KnownErrors code) const + { + if (code <= QDBusError::Other || code > QDBusError::qKnownErrorsMax) + return messages[int(QDBusError::Other) - 1]; + return messages[int(code) - 1]; + } + + inline QDBusError::KnownErrors get(const char *name) const + { + if (!name || !*name) + return QDBusError::NoError; + for (int i = QDBusError::Other; i <= QDBusError::qKnownErrorsMax; ++i) + if (strcmp(name, messages[i - 1]) == 0) + return QDBusError::KnownErrors(i); + return QDBusError::Other; + } +}; + +static const char errorMessages_string[] = + // in the same order as KnownErrors! + "other\0" // Other -- shouldn't happen + DBUS_ERROR_FAILED "\0" // Failed + DBUS_ERROR_NO_MEMORY "\0" // NoMemory + DBUS_ERROR_SERVICE_UNKNOWN "\0" // ServiceUnknown + DBUS_ERROR_NO_REPLY "\0" // NoReply + DBUS_ERROR_BAD_ADDRESS "\0" // BadAddress + DBUS_ERROR_NOT_SUPPORTED "\0" // NotSupported + DBUS_ERROR_LIMITS_EXCEEDED "\0" // LimitsExceeded + DBUS_ERROR_ACCESS_DENIED "\0" // AccessDenied + DBUS_ERROR_NO_SERVER "\0" // NoServer + DBUS_ERROR_TIMEOUT "\0" // Timeout + DBUS_ERROR_NO_NETWORK "\0" // NoNetwork + DBUS_ERROR_ADDRESS_IN_USE "\0" // AddressInUse + DBUS_ERROR_DISCONNECTED "\0" // Disconnected + DBUS_ERROR_INVALID_ARGS "\0" // InvalidArgs + DBUS_ERROR_UNKNOWN_METHOD "\0" // UnknownMethod + DBUS_ERROR_TIMED_OUT "\0" // TimedOut + DBUS_ERROR_INVALID_SIGNATURE "\0" // InvalidSignature + "com.trolltech.QtDBus.Error.UnknownInterface\0" // UnknownInterface + "com.trolltech.QtDBus.Error.InternalError\0" // InternalError + "\0"; + +ErrorMessageMapping::ErrorMessageMapping() + : messages(int(QDBusError::qKnownErrorsMax)) +{ + // create the list: + const char *p = errorMessages_string; + int i = 0; + while (*p) { + messages[i] = p; + p += strlen(p) + 1; + ++i; + } +} + +Q_GLOBAL_STATIC(ErrorMessageMapping, errorMessages) + +/*! + \class QDBusError + \brief Represents an error received from the D-Bus bus or from remote applications found in the bus. + + When dealing with the D-Bus bus service or with remote applications over D-Bus, a number of + error conditions can happen. This error conditions are sometimes signalled by a returned error + value or by a QDBusError. + + C++ and Java exceptions are a valid analogy for D-Bus errors: instead of returning normally with + a return value, remote applications and the bus may decide to throw an error condition. However, + the QtDBus implementation does not use the C++ exception-throwing mechanism, so you will receive + QDBusErrors in the return reply (see QDBusReply::error()). + + QDBusError objects are used to inspect the error name and message as received from the bus and + remote applications. You should not create such objects yourself to signal error conditions when + called from D-Bus: instead, use QDBusMessage::error and QDBusConnection::send. + + \sa QDBusConnection::send(), QDBusMessage, QDBusReply +*/ + +/*! + \enum QDBusError::KnownErrors + + In order to facilitate verification of the most common D-Bus errors generated by the D-Bus + implementation and by the bus daemon itself, QDBusError can be compared to a set of pre-defined + values: + + \value NoError QDBusError is invalid (i.e., the call succeeded) + \value Other QDBusError contains an error that is one of the well-known ones + \value Failed The call failed (\c org.freedesktop.DBus.Error.Failed) + \value NoMemory Out of memory (\c org.freedesktop.DBus.Error.NoMemory) + \value ServiceUnknown The called service is not known + (\c org.freedesktop.DBus.Error.ServiceUnknown) + \value NoReply The called method did not reply within the specified timeout + (\c org.freedesktop.DBus.Error.NoReply) + \value BadAddress The address given is not valid + (\c org.freedesktop.DBus.Error.BadAddress) + \value NotSupported The call/operation is not supported + (\c org.freedesktop.DBus.Error.NotSupported) + \value LimitsExceeded The limits allocated to this process/call/connection exceeded the + pre-defined values (\c org.freedesktop.DBus.Error.LimitsExceeded) + \value AccessDenied The call/operation tried to access a resource it isn't allowed to + (\c org.freedesktop.DBus.Error.AccessDenied) + \value NoServer \i{Documentation doesn't say what this is for} + (\c org.freedesktop.DBus.Error.NoServer) + \value Timeout \i{Documentation doesn't say what this is for or how it's used} + (\c org.freedesktop.DBus.Error.Timeout) + \value NoNetwork \i{Documentation doesn't say what this is for} + (\c org.freedesktop.DBus.Error.NoNetwork) + \value AddressInUse QDBusServer tried to bind to an address that is already in use + (\c org.freedesktop.DBus.Error.AddressInUse) + \value Disconnected The call/process/message was sent after QDBusConnection disconnected + (\c org.freedesktop.DBus.Error.Disconnected) + \value InvalidArgs The arguments passed to this call/operation are not valid + (\c org.freedesktop.DBus.Error.InvalidArgs) + \value UnknownMethod The method called was not found in this object/interface with the + given parameters (\c org.freedesktop.DBus.Error.UnknownMethod) + \value TimedOut \i{Documentation doesn't say...} + (\c org.freedesktop.DBus.Error.TimedOut) + \value InvalidSignature The type signature is not valid or compatible + (\c org.freedesktop.DBus.Error.InvalidSignature) + \value UnknownInterface The interface is not known + \value InternalError An internal error occurred + (\c com.trolltech.QtDBus.Error.InternalError) + +*/ + +/*! + \internal + Constructs a QDBusError from a DBusError structure. +*/ +QDBusError::QDBusError(const DBusError *error) + : code(NoError) +{ + if (!error || !dbus_error_is_set(error)) + return; + + code = errorMessages()->get(error->name); + nm = QString::fromUtf8(error->name); + msg = QString::fromUtf8(error->message); +} + +/*! + \internal + Constructs a QDBusError from a QDBusMessage. +*/ +QDBusError::QDBusError(const QDBusMessage &qdmsg) + : code(Other) +{ + if (qdmsg.type() != QDBusMessage::ErrorMessage) + return; + + nm = qdmsg.name(); + if (qdmsg.count()) + msg = qdmsg[0].toString(); + code = errorMessages()->get(nm.toUtf8().constData()); +} + +/*! + \internal + Constructs a QDBusError from a well-known error code +*/ +QDBusError::QDBusError(KnownErrors error, const QString &message) + : code(error) +{ + nm = errorMessages()->get(error); + msg = message; +} + +/*! + \fn QDBusError::name() const + Returns this error's name. Error names are similar to D-Bus Interface names, like + "org.freedesktop.DBus.InvalidArgs". +*/ + +/*! + \fn QDBusError::message() const + Returns the message that the callee associated with this error. Error messages are + implementation defined and usually contain a human-readable error code, though this does not + mean it is suitable for your end-users. +*/ + +/*! + \fn QDBusError::isValid() const + Returns true if this is a valid error condition (i.e., if there was an error), false otherwise. +*/ + +/*! + \fn QDBusError::operator==(KnownErrors error) const + Compares this QDBusError against the well-known error code \a error and returns true if they + match. +*/ + +/*! + \fn operator==(QDBusError::KnownErrors p1, const QDBusError &p2) + \relates QDBusError + + Compares the QDBusError \a p2 against the well-known error code \a p1 and returns true if they + match. +*/ + +#ifndef QT_NO_DEBUG +QDebug operator<<(QDebug dbg, const QDBusError &msg) +{ + dbg.nospace() << "QDBusError(" << msg.name() << ", " << msg.message() << ")"; + return dbg.space(); +} +#endif + + diff --git a/qt/src/qdbuserror.h b/qt/src/qdbuserror.h new file mode 100644 index 0000000..71c636d --- /dev/null +++ b/qt/src/qdbuserror.h @@ -0,0 +1,93 @@ +/* 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 + * + * 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 QDBUSERROR_H +#define QDBUSERROR_H + +#include "qdbusmacros.h" +#include <QtCore/qstring.h> + +struct DBusError; +class QDBusMessage; + +class QDBUS_EXPORT QDBusError +{ +public: + enum KnownErrors { + NoError = 0, + Other = 1, + Failed, + NoMemory, + ServiceUnknown, + NoReply, + BadAddress, + NotSupported, + LimitsExceeded, + AccessDenied, + NoServer, + Timeout, + NoNetwork, + AddressInUse, + Disconnected, + InvalidArgs, + UnknownMethod, + TimedOut, + InvalidSignature, + UnknownInterface, + InternalError, + +#ifndef Q_QDOC + // don't use this one! + qKnownErrorsMax = InternalError +#endif + }; + + QDBusError(const DBusError *error = 0); + QDBusError(const QDBusMessage& msg); + QDBusError(KnownErrors error, const QString &message); + + inline QString name() const { return nm; } + inline QString message() const { return msg; } + inline bool isValid() const { return !nm.isNull() && !msg.isNull(); } + + inline bool operator==(KnownErrors error) const + { return code == error; } + +private: + KnownErrors code; + QString nm, msg; +}; + +inline bool operator==(QDBusError::KnownErrors p1, const QDBusError &p2) +{ return p2 == p1; } +inline bool operator!=(QDBusError::KnownErrors p1, const QDBusError &p2) +{ return !(p2 == p1); } +inline bool operator!=(const QDBusError &p1, QDBusError::KnownErrors p2) +{ return !(p1 == p2); } + +#ifndef QT_NO_DEBUG +QDebug operator<<(QDebug, const QDBusError &); +#endif + +#endif diff --git a/qt/src/qdbusintegrator.cpp b/qt/src/qdbusintegrator.cpp new file mode 100644 index 0000000..07921cf --- /dev/null +++ b/qt/src/qdbusintegrator.cpp @@ -0,0 +1,1552 @@ +/* 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 <qcoreapplication.h> +#include <qcoreevent.h> +#include <qdebug.h> +#include <qmetaobject.h> +#include <qobject.h> +#include <qsocketnotifier.h> +#include <qstringlist.h> +#include <qtimer.h> + +#include "qdbusconnection_p.h" +#include "qdbusinterface_p.h" +#include "qdbusmessage.h" +#include "qdbusabstractadaptor.h" +#include "qdbusabstractadaptor_p.h" +#include "qdbustypehelper_p.h" +#include "qdbusutil.h" +#include "qdbustype_p.h" + +#ifndef USE_OUTSIDE_DISPATCH +# define USE_OUTSIDE_DISPATCH 0 +#endif + +int QDBusConnectionPrivate::messageMetaType = 0; + +typedef void (*QDBusSpyHook)(const QDBusMessage&); +typedef QVarLengthArray<QDBusSpyHook, 4> QDBusSpyHookList; +Q_GLOBAL_STATIC(QDBusSpyHookList, qDBusSpyHookList) + +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; +}; + +static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data) +{ + Q_ASSERT(timeout); + Q_ASSERT(data); + + // qDebug("addTimeout %d", dbus_timeout_get_interval(timeout)); + + QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + + if (!dbus_timeout_get_enabled(timeout)) + return true; + + if (!QCoreApplication::instance()) { + d->pendingTimeouts.append(timeout); + return true; + } + int timerId = d->startTimer(dbus_timeout_get_interval(timeout)); + if (!timerId) + return false; + + d->timeouts[timerId] = timeout; + return true; +} + +static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data) +{ + Q_ASSERT(timeout); + Q_ASSERT(data); + + // qDebug("removeTimeout"); + + QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + d->pendingTimeouts.removeAll(timeout); + + QDBusConnectionPrivate::TimeoutHash::iterator it = d->timeouts.begin(); + while (it != d->timeouts.end()) { + if (it.value() == timeout) { + d->killTimer(it.key()); + it = d->timeouts.erase(it); + } else { + ++it; + } + } +} + +static void qDBusToggleTimeout(DBusTimeout *timeout, void *data) +{ + Q_ASSERT(timeout); + Q_ASSERT(data); + + //qDebug("ToggleTimeout"); + + qDBusRemoveTimeout(timeout, data); + qDBusAddTimeout(timeout, data); +} + +static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data) +{ + Q_ASSERT(watch); + Q_ASSERT(data); + + QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + + int flags = dbus_watch_get_flags(watch); + int fd = dbus_watch_get_fd(watch); + + QDBusConnectionPrivate::Watcher watcher; + if (flags & DBUS_WATCH_READABLE) { + //qDebug("addReadWatch %d", fd); + 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))); + } + } + if (flags & DBUS_WATCH_WRITABLE) { + //qDebug("addWriteWatch %d", fd); + 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))); + } + } + d->watchers.insertMulti(fd, watcher); + + return true; +} + +static void qDBusRemoveWatch(DBusWatch *watch, void *data) +{ + Q_ASSERT(watch); + Q_ASSERT(data); + + //qDebug("remove watch"); + + QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + int fd = dbus_watch_get_fd(watch); + + QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd); + while (i != d->watchers.end() && i.key() == fd) { + if (i.value().watch == watch) { + delete i.value().read; + delete i.value().write; + d->watchers.erase(i); + return; + } + ++i; + } +} + +static void qDBusToggleWatch(DBusWatch *watch, void *data) +{ + Q_ASSERT(watch); + Q_ASSERT(data); + + QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + int fd = dbus_watch_get_fd(watch); + + QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd); + while (i != d->watchers.end() && i.key() == fd) { + if (i.value().watch == watch) { + bool enabled = dbus_watch_get_enabled(watch); + int flags = dbus_watch_get_flags(watch); + + //qDebug("toggle watch %d to %d (write: %d, read: %d)", dbus_watch_get_fd(watch), enabled, flags & DBUS_WATCH_WRITABLE, flags & DBUS_WATCH_READABLE); + + if (flags & DBUS_WATCH_READABLE && i.value().read) + i.value().read->setEnabled(enabled); + if (flags & DBUS_WATCH_WRITABLE && i.value().write) + i.value().write->setEnabled(enabled); + return; + } + ++i; + } +} + +static void qDBusNewConnection(DBusServer *server, DBusConnection *c, void *data) +{ + Q_ASSERT(data); Q_ASSERT(server); Q_ASSERT(c); + Q_UNUSED(data); Q_UNUSED(server); Q_UNUSED(c); + + qDebug("SERVER: GOT A NEW CONNECTION"); // TODO +} + +extern QDBUS_EXPORT void qDBusAddSpyHook(QDBusSpyHook); +void qDBusAddSpyHook(QDBusSpyHook hook) +{ + qDBusSpyHookList()->append(hook); +} + +#if USE_OUTSIDE_DISPATCH +# define HANDLED DBUS_HANDLER_RESULT_HANDLED_OUTSIDE_DISPATCH +static DBusHandlerResult qDBusSignalFilterOutside(DBusConnection *connection, + DBusMessage *message, void *data) +{ + Q_ASSERT(data); + Q_UNUSED(connection); + Q_UNUSED(message); + + QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + if (d->mode == QDBusConnectionPrivate::InvalidMode) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; // internal error, actually + + CallDeliveryEvent *e = d->postedCallDeliveryEvent(); + + d->deliverCall(*e); + delete e; + + return DBUS_HANDLER_RESULT_HANDLED; +} +#else +# define HANDLED DBUS_HANDLER_RESULT_HANDLED +#endif + +extern "C" { +static DBusHandlerResult +qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data) +{ + return QDBusConnectionPrivate::messageFilter(connection, message, data); +} +} + +DBusHandlerResult QDBusConnectionPrivate::messageFilter(DBusConnection *connection, + DBusMessage *message, void *data) +{ + Q_ASSERT(data); + Q_UNUSED(connection); + + QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + if (d->mode == QDBusConnectionPrivate::InvalidMode) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + QDBusMessage amsg = QDBusMessage::fromDBusMessage(message, QDBusConnection(d->name)); + qDebug() << "got message:" << amsg; + + const QDBusSpyHookList *list = qDBusSpyHookList(); + for (int i = 0; i < list->size(); ++i) { + qDebug() << "calling the message spy hook"; + (*(*list)[i])(amsg); + } + + bool handled = false; + int msgType = dbus_message_get_type(message); + if (msgType == DBUS_MESSAGE_TYPE_SIGNAL) { + handled = d->handleSignal(amsg); + } else if (msgType == DBUS_MESSAGE_TYPE_METHOD_CALL) { + handled = d->handleObjectCall(amsg); + } + + return handled ? HANDLED : + DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode *haystack) +{ + foreach (const QDBusConnectionPrivate::ObjectTreeNode::Data &entry, haystack->children) + huntAndDestroy(needle, entry.node); + + if (needle == haystack->obj) { + haystack->obj = 0; + haystack->flags = 0; + } +} + +static void huntAndEmit(DBusConnection *connection, DBusMessage *msg, + QObject *needle, QDBusConnectionPrivate::ObjectTreeNode *haystack, + const QString &path = QString()) +{ + foreach (const QDBusConnectionPrivate::ObjectTreeNode::Data &entry, haystack->children) + huntAndEmit(connection, msg, needle, entry.node, path + QLatin1String("/") + entry.name); + + if (needle == haystack->obj && haystack->flags & QDBusConnection::ExportAdaptors) { + QByteArray p = path.toLatin1(); + if (p.isEmpty()) + p = "/"; + //qDebug() << p; + DBusMessage *msg2 = dbus_message_copy(msg); + dbus_message_set_path(msg2, p); + dbus_connection_send(connection, msg2, 0); + dbus_message_unref(msg2); + } +} + +static bool typesMatch(int metaId, int variantType) +{ + if (metaId == int(variantType)) + return true; + + if (variantType == QVariant::Int && metaId == QMetaType::Short) + return true; + + if (variantType == QVariant::UInt && (metaId == QMetaType::UShort || + metaId == QMetaType::UChar)) + return true; + + if (variantType == QVariant::List) { + if (metaId == QDBusTypeHelper<bool>::listId() || + metaId == QDBusTypeHelper<short>::listId() || + metaId == QDBusTypeHelper<ushort>::listId() || + metaId == QDBusTypeHelper<int>::listId() || + metaId == QDBusTypeHelper<uint>::listId() || + metaId == QDBusTypeHelper<qlonglong>::listId() || + metaId == QDBusTypeHelper<qulonglong>::listId() || + metaId == QDBusTypeHelper<double>::listId()) + return true; + } + + return false; // no match +} + +static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, + const QDBusTypeList &types, QList<int>& metaTypes) +{ + // find the first slot + const QMetaObject *super = mo; + while (super != &QObject::staticMetaObject && + super != &QDBusAbstractAdaptor::staticMetaObject) + super = super->superClass(); + + int attributeMask = (flags & QDBusConnection::ExportAllSlots) ? + 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 = qDBusNameToTypeId(mm.typeName()); + bool isAsync = qDBusCheckAsyncTag(mm.tag()); + + // consistency check: + if (isAsync && returnType != QMetaType::Void) + continue; + + int inputCount = qDBusParametersForMethod(mm, metaTypes); + if (inputCount == -1) + continue; // problem parsing + + metaTypes[0] = returnType; + bool hasMessage = false; + if (inputCount > 0 && + metaTypes.at(inputCount) == QDBusConnectionPrivate::messageMetaType) { + // "no input parameters" is allowed as long as the message meta type is there + hasMessage = true; + --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 (hasMessage && (mm.attributes() & attributeMask) != attributeMask) + continue; // not exported + + // if we got here, this slot matched + return idx; + } + + // no slot matched + return -1; +} + +static CallDeliveryEvent* prepareReply(QObject *object, int idx, const QList<int> &metaTypes, + const QDBusMessage &msg) +{ + Q_ASSERT(object); + + 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 0; // no match + + // we can deliver + // prepare for the call + CallDeliveryEvent *data = new CallDeliveryEvent; + data->object = object; + data->flags = 0; + data->message = msg; + data->metaTypes = metaTypes; + data->slotIdx = idx; + + return data; +} + +bool QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook, + const QDBusMessage &msg) +{ + // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal + // that was received from D-Bus + // + // Signals are delivered to slots if the parameters match + // Slots can have less parameters than there are on the message + // Slots can optionally have one final parameter that is a QDBusMessage + // Slots receive read-only copies of the message (i.e., pass by value or by const-ref) + CallDeliveryEvent *call = prepareReply(hook.obj, hook.midx, hook.params, msg); + if (call) { + postCallDeliveryEvent(call); + return true; + } + return false; +} + +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. + // + // The D-Bus specification requires that all MethodCall messages be replied to, unless the + // caller specifically waived this requirement. This means that we inspect if the user slot + // generated a reply and, if it didn't, we will. Obviously, if the user slot doesn't take a + // QDBusMessage parameter, it cannot generate a reply. + // + // 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. + + if (!object) + return false; + + QList<int> metaTypes; + int idx; + + { + const QMetaObject *mo = object->metaObject(); + QDBusTypeList typeList(msg.signature().toUtf8()); + QByteArray name = msg.name().toUtf8(); + + // find a slot that matches according to the rules above + idx = ::findSlot(mo, name, flags, typeList, metaTypes); + if (idx == -1) { + // try with no parameters, but with a QDBusMessage + idx = ::findSlot(mo, name, flags, QDBusTypeList(), metaTypes); + if (metaTypes.count() != 2 || metaTypes.at(1) != messageMetaType) + return false; + } + } + + // found the slot to be called + // prepare for the call: + CallDeliveryEvent *call = new CallDeliveryEvent; + + // parameters: + call->object = object; + call->flags = flags; + call->message = msg; + + // save our state: + call->metaTypes = metaTypes; + call->slotIdx = idx; + + postCallDeliveryEvent(call); + + // ready + return true; +} + +void QDBusConnectionPrivate::postCallDeliveryEvent(CallDeliveryEvent *data) +{ + Q_ASSERT(data); + data->conn = this; +#if USE_OUTSIDE_DISPATCH + callDeliveryMutex.lock(); + callDeliveryState = data; +#else + QCoreApplication::postEvent( this, data ); +#endif +} + +CallDeliveryEvent *QDBusConnectionPrivate::postedCallDeliveryEvent() +{ + CallDeliveryEvent *e = callDeliveryState; + Q_ASSERT(e && e->conn == this); + + // release it: + callDeliveryState = 0; + callDeliveryMutex.unlock(); + + return e; +} + +void QDBusConnectionPrivate::deliverCall(const CallDeliveryEvent& data) const +{ + // resume state: + const QList<int>& metaTypes = data.metaTypes; + const QDBusMessage& msg = data.message; + + QVarLengthArray<void *, 10> params; + params.reserve(metaTypes.count()); + + QVariantList auxParameters; + // let's create the parameter list + + // first one is the return type -- add it below + params.append(0); + + // add the input parameters + int i; + for (i = 1; i <= msg.count(); ++i) { + int id = metaTypes[i]; + if (id == QDBusConnectionPrivate::messageMetaType) + break; + + if (id == int(msg.at(i - 1).userType())) + // no conversion needed + params.append(const_cast<void *>( msg.at(i - 1).constData() )); + else { + // convert to what the function expects + auxParameters.append(QVariant()); + + const QVariant &in = msg.at(i - 1); + QVariant &out = auxParameters[auxParameters.count() - 1]; + + bool error = false; + if (id == QVariant::List) { + int mid = in.userType(); + // the only conversion possible here is from a specialised QList<T> to QVariantList + if (mid == QDBusTypeHelper<bool>::listId()) + out = qVariantFromValue(QDBusTypeHelper<bool>::toVariantList(in)); + else if (mid == QDBusTypeHelper<short>::listId()) + out = qVariantFromValue(QDBusTypeHelper<short>::toVariantList(in)); + else if (mid == QDBusTypeHelper<ushort>::listId()) + out = qVariantFromValue(QDBusTypeHelper<ushort>::toVariantList(in)); + else if (mid == QDBusTypeHelper<int>::listId()) + out = qVariantFromValue(QDBusTypeHelper<int>::toVariantList(in)); + else if (mid == QDBusTypeHelper<uint>::listId()) + out = qVariantFromValue(QDBusTypeHelper<uint>::toVariantList(in)); + else if (mid == QDBusTypeHelper<qlonglong>::listId()) + out = qVariantFromValue(QDBusTypeHelper<qlonglong>::toVariantList(in)); + else if (mid == QDBusTypeHelper<qulonglong>::listId()) + out = qVariantFromValue(QDBusTypeHelper<qulonglong>::toVariantList(in)); + else if (mid == QDBusTypeHelper<double>::listId()) + out = qVariantFromValue(QDBusTypeHelper<double>::toVariantList(in)); + else + error = true; + } else if (in.type() == QVariant::UInt) { + if (id == QMetaType::UChar) { + uchar uc = in.toUInt(); + out = qVariantFromValue(uc); + } else if (id == QMetaType::UShort) { + ushort us = in.toUInt(); + out = qVariantFromValue(us); + } else { + error = true; + } + } else if (in.type() == QVariant::Int) { + if (id == QMetaType::Short) { + short s = in.toInt(); + out = qVariantFromValue(s); + } else { + error = true; + } + } else { + error = true; + } + + if (error) + qFatal("Internal error: got invalid meta type %d when trying to convert to meta type %d", + in.userType(), id); + + params.append( const_cast<void *>(out.constData()) ); + } + } + + bool takesMessage = false; + if (metaTypes.count() > i && metaTypes[i] == 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); + outputArgs.append( arg ); + params[0] = const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData()); + } + for ( ; i < metaTypes.count(); ++i) { + QVariant arg(metaTypes[i], null); + outputArgs.append( arg ); + params.append( const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData()) ); + } + + // 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? Only if the caller is waiting for a reply and one hasn't been sent + // yet. + if (!msg.noReply() && !msg.wasRepliedTo()) { + if (!fail) { + // normal reply + QDBusMessage reply = QDBusMessage::methodReply(msg); + reply += outputArgs; + + qDebug() << "Automatically sending reply:" << reply; + send(reply); + } + else { + // generate internal error + QDBusMessage reply = QDBusMessage::error(msg, QDBusError(QDBusError::InternalError, + QLatin1String("Failed to deliver message"))); + qWarning("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), busService(0) +{ + extern bool qDBusInitThreads(); + static const int msgType = registerMessageMetaType(); + static const bool threads = qDBusInitThreads(); + static const bool metatypes = QDBusMetaTypeId::innerInitialize(); + + Q_UNUSED(msgType); + Q_UNUSED(threads); + Q_UNUSED(metatypes); + + dbus_error_init(&error); + + rootNode.flags = 0; +} + +QDBusConnectionPrivate::~QDBusConnectionPrivate() +{ + if (dbus_error_is_set(&error)) + dbus_error_free(&error); + + closeConnection(); + rootNode.clear(); // free resources + qDeleteAll(cachedMetaObjects); +} + +void QDBusConnectionPrivate::closeConnection() +{ + QWriteLocker locker(&lock); + ConnectionMode oldMode = mode; + mode = InvalidMode; // prevent reentrancy + if (oldMode == ServerMode) { + if (server) { + dbus_server_disconnect(server); + dbus_server_unref(server); + server = 0; + } + } else if (oldMode == ClientMode) { + if (connection) { + dbus_connection_close(connection); + // send the "close" message + while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) + ; + dbus_connection_unref(connection); + connection = 0; + } + } +} + +bool QDBusConnectionPrivate::handleError() +{ + lastError = QDBusError(&error); + if (dbus_error_is_set(&error)) + dbus_error_free(&error); + return lastError.isValid(); +} + +void QDBusConnectionPrivate::bindToApplication() +{ + // Yay, now that we have an application we are in business + Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection", + "qDBusBindToApplication called without an application"); + moveToThread(QCoreApplication::instance()->thread()); + + // Re-add all watchers + WatcherHash oldWatchers = watchers; + watchers.clear(); + QHashIterator<int, QDBusConnectionPrivate::Watcher> it(oldWatchers); + while (it.hasNext()) { + it.next(); + if (!it.value().read && !it.value().write) { + qDBusAddWatch(it.value().watch, this); + } else { + watchers.insertMulti(it.key(), it.value()); + } + } + + // Re-add all timeouts + while (!pendingTimeouts.isEmpty()) + qDBusAddTimeout(pendingTimeouts.takeFirst(), this); +} + +void QDBusConnectionPrivate::timerEvent(QTimerEvent *e) +{ + DBusTimeout *timeout = timeouts.value(e->timerId(), 0); + dbus_timeout_handle(timeout); +} + +void QDBusConnectionPrivate::doDispatch() +{ + if (mode == ClientMode) + while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS); +} + +void QDBusConnectionPrivate::socketRead(int fd) +{ + QHashIterator<int, QDBusConnectionPrivate::Watcher> it(watchers); + while (it.hasNext()) { + it.next(); + if (it.key() == fd && it.value().read && it.value().read->isEnabled()) { + if (!dbus_watch_handle(it.value().watch, DBUS_WATCH_READABLE)) + qDebug("OUT OF MEM"); + } + } + + doDispatch(); +} + +void QDBusConnectionPrivate::socketWrite(int fd) +{ + QHashIterator<int, QDBusConnectionPrivate::Watcher> it(watchers); + while (it.hasNext()) { + it.next(); + if (it.key() == fd && it.value().write && it.value().write->isEnabled()) { + if (!dbus_watch_handle(it.value().watch, DBUS_WATCH_WRITABLE)) + qDebug("OUT OF MEM"); + } + } +} + +void QDBusConnectionPrivate::objectDestroyed(QObject *obj) +{ + QWriteLocker locker(&lock); + huntAndDestroy(obj, &rootNode); + + SignalHookHash::iterator sit = signalHooks.begin(); + while (sit != signalHooks.end()) { + if (static_cast<QObject *>(sit.value().obj) == obj) + sit = signalHooks.erase(sit); + else + ++sit; + } + + obj->disconnect(this); +} + +void QDBusConnectionPrivate::relaySignal(QObject *obj, const char *interface, const char *name, + const QVariantList &args) +{ + QReadLocker locker(&lock); + QDBusMessage message = QDBusMessage::signal(QLatin1String("/"), QLatin1String(interface), + QLatin1String(name)); + message += args; + DBusMessage *msg = message.toDBusMessage(); + if (!msg) { + qWarning("Could not emit signal %s.%s", interface, name); + return; + } + + //qDebug() << "Emitting signal" << message; + //qDebug() << "for paths:"; + dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything + huntAndEmit(connection, msg, obj, &rootNode); + dbus_message_unref(msg); +} + +int QDBusConnectionPrivate::registerMessageMetaType() +{ + int tp = messageMetaType = qRegisterMetaType<QDBusMessage>("QDBusMessage"); + return tp; +} + +int QDBusConnectionPrivate::findSlot(QObject* obj, const QByteArray &normalizedName, + QList<int> ¶ms) +{ + int midx = obj->metaObject()->indexOfMethod(normalizedName); + if (midx == -1) { + qWarning("No such slot '%s' while connecting D-Bus", normalizedName.constData()); + return -1; + } + + int inputCount = qDBusParametersForMethod(obj->metaObject()->method(midx), params); + if ( inputCount == -1 || inputCount + 1 != params.count() ) + return -1; // failed to parse or invalid arguments or output arguments + + return midx; +} + +bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key, + const QString &service, const QString &path, + const QString &interface, const QString &name, + QObject *receiver, const char *signal, int minMIdx, + bool buildSignature) +{ + QByteArray normalizedName = QMetaObject::normalizedSignature(signal + 1); + hook.midx = findSlot(receiver, normalizedName, hook.params); + if (hook.midx < minMIdx) + return false; + + hook.sender = service; + hook.path = path; + hook.obj = receiver; + + // build the D-Bus signal name and signature + QString mname = name; + if (mname.isEmpty()) { + normalizedName.truncate(normalizedName.indexOf('(')); + mname = QString::fromUtf8(normalizedName); + } + key = mname; + key.reserve(interface.length() + 1 + mname.length()); + key += ':'; + key += interface; + + if (buildSignature) { + hook.signature.clear(); + for (int i = 1; i < hook.params.count(); ++i) + if (hook.params.at(i) != messageMetaType) + hook.signature += QLatin1String( QDBusType::dbusSignature( QVariant::Type(hook.params.at(i)) ) ); + } + + return true; // connect to this signal +} + +bool QDBusConnectionPrivate::activateInternalFilters(const ObjectTreeNode *node, const QDBusMessage &msg) +{ + // object may be null + + if (msg.interface().isEmpty() || msg.interface() == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE)) { + if (msg.method() == QLatin1String("Introspect") && msg.signature().isEmpty()) + qDBusIntrospectObject(node, msg); + if (msg.interface() == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE)) + return true; + } + + if (node->obj && (msg.interface().isEmpty() || + msg.interface() == QLatin1String(DBUS_INTERFACE_PROPERTIES))) { + if (msg.method() == QLatin1String("Get") && msg.signature() == QLatin1String("ss")) + qDBusPropertyGet(node, msg); + else if (msg.method() == QLatin1String("Set") && msg.signature() == QLatin1String("ssv")) + qDBusPropertySet(node, msg); + + if (msg.interface() == QLatin1String(DBUS_INTERFACE_PROPERTIES)) + return true; + } + + return false; +} + +bool QDBusConnectionPrivate::activateObject(const ObjectTreeNode *node, const QDBusMessage &msg) +{ + // This is called by QDBusConnectionPrivate::handleObjectCall to place a call to a slot + // on the object. + // + // The call is routed through the adaptor sub-objects if we have any + + // object may be null + + QDBusAdaptorConnector *connector; + if (node->flags & QDBusConnection::ExportAdaptors && + (connector = qDBusFindAdaptorConnector(node->obj))) { + int newflags = node->flags | QDBusConnection::ExportAllSlots; + + if (msg.interface().isEmpty()) { + // place the call in all interfaces + // let the first one that handles it to work + foreach (const QDBusAdaptorConnector::AdaptorData &entry, connector->adaptors) + if (activateCall(entry.adaptor, newflags, msg)) + return true; + } else { + // check if we have an interface matching the name that was asked: + QDBusAdaptorConnector::AdaptorMap::ConstIterator it; + it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(), + msg.interface()); + if (it != connector->adaptors.end() && it->interface == msg.interface()) + if (activateCall(it->adaptor, newflags, msg)) + return true; + } + } + + // no adaptors matched + // try our standard filters + if (activateInternalFilters(node, msg)) + return true; + + // try the object itself: + if (node->flags & QDBusConnection::ExportSlots && activateCall(node->obj, node->flags, msg)) + return true; +#if 0 + // nothing matched + qDebug("Call failed: no match for %s%s%s at %s", + qPrintable(msg.interface()), msg.interface().isEmpty() ? "" : ".", + qPrintable(msg.name()), + qPrintable(msg.path())); +#endif + return false; +} + +template<typename Func> +static bool applyForObject(QDBusConnectionPrivate::ObjectTreeNode *root, const QString &fullpath, + Func& functor) +{ + // walk the object tree + QStringList path = fullpath.split(QLatin1Char('/')); + if (path.last().isEmpty()) + path.removeLast(); // happens if path is "/" + int i = 1; + QDBusConnectionPrivate::ObjectTreeNode *node = root; + + // try our own tree first + while (node && !(node->flags & QDBusConnection::ExportChildObjects) ) { + if (i == path.count()) { + // found our object + functor(node); + return true; + } + + QVector<QDBusConnectionPrivate::ObjectTreeNode::Data>::ConstIterator it = + qLowerBound(node->children.constBegin(), node->children.constEnd(), path.at(i)); + if (it != node->children.constEnd() && it->name == path.at(i)) + // match + node = it->node; + else + node = 0; + + ++i; + } + + // any object in the tree can tell us to switch to its own object tree: + if (node && node->flags & QDBusConnection::ExportChildObjects) { + QObject *obj = node->obj; + + while (obj) { + if (i == path.count()) { + // we're at the correct level + QDBusConnectionPrivate::ObjectTreeNode fakenode(*node); + fakenode.obj = obj; + functor(&fakenode); + return true; + } + + const QObjectList children = obj->children(); + + // find a child with the proper name + QObject *next = 0; + foreach (QObject *child, children) + if (child->objectName() == path.at(i)) { + next = child; + break; + } + + if (!next) + break; + + ++i; + obj = next; + } + } + + // object not found + return false; +} + +struct qdbus_activateObject +{ + QDBusConnectionPrivate *self; + const QDBusMessage &msg; + bool returnVal; + inline qdbus_activateObject(QDBusConnectionPrivate *s, const QDBusMessage &m) + : self(s), msg(m) + { } + + inline void operator()(QDBusConnectionPrivate::ObjectTreeNode *node) + { returnVal = self->activateObject(node, msg); } +}; + +bool QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg) +{ + QReadLocker locker(&lock); + + qdbus_activateObject apply(this, msg); + if (applyForObject(&rootNode, msg.path(), apply)) + return apply.returnVal; + + qDebug("Call failed: no object found at %s", qPrintable(msg.path())); + return false; +} + +bool QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage& msg) +{ + bool result = false; + SignalHookHash::const_iterator it = signalHooks.find(key); + //qDebug("looking for: %s", path.toLocal8Bit().constData()); + //qDebug() << signalHooks.keys(); + for ( ; it != signalHooks.constEnd() && it.key() == key; ++it) { + const SignalHook &hook = it.value(); + if ( !hook.sender.isEmpty() && hook.sender != msg.sender() ) + continue; + if ( !hook.path.isEmpty() && hook.path != msg.path() ) + continue; + if ( !hook.signature.isEmpty() && hook.signature != msg.signature() ) + continue; + if ( hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty()) + continue; + + // yes, |= + result |= activateSignal(hook, msg); + } + return result; +} + +bool QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg) +{ + QString key = msg.member(); + key.reserve(key.length() + 1 + msg.interface().length()); + key += ':'; + key += msg.interface(); + + QReadLocker locker(&lock); + bool result = handleSignal(key, msg); // one try + + key.truncate(msg.member().length() + 1); // keep the ':' + result |= handleSignal(key, msg); // second try + return result; +} + +static dbus_int32_t server_slot = -1; + +void QDBusConnectionPrivate::setServer(DBusServer *s) +{ + if (!server) { + handleError(); + return; + } + + server = s; + mode = ServerMode; + + dbus_server_allocate_data_slot(&server_slot); + if (server_slot < 0) + return; + + dbus_server_set_watch_functions(server, qDBusAddWatch, qDBusRemoveWatch, + qDBusToggleWatch, this, 0); // ### check return type? + dbus_server_set_timeout_functions(server, qDBusAddTimeout, qDBusRemoveTimeout, + qDBusToggleTimeout, this, 0); + dbus_server_set_new_connection_function(server, qDBusNewConnection, this, 0); + + dbus_server_set_data(server, server_slot, this, 0); +} + +void QDBusConnectionPrivate::setConnection(DBusConnection *dbc) +{ + if (!dbc) { + handleError(); + return; + } + + connection = dbc; + mode = ClientMode; + + dbus_connection_set_exit_on_disconnect(connection, false); + dbus_connection_set_watch_functions(connection, qDBusAddWatch, qDBusRemoveWatch, + qDBusToggleWatch, this, 0); + dbus_connection_set_timeout_functions(connection, qDBusAddTimeout, qDBusRemoveTimeout, + qDBusToggleTimeout, this, 0); +// dbus_bus_add_match(connection, "type='signal',interface='com.trolltech.dbus.Signal'", &error); +// dbus_bus_add_match(connection, "type='signal'", &error); + + dbus_bus_add_match(connection, "type='signal'", &error); + if (handleError()) { + closeConnection(); + return; + } + + const char *service = dbus_bus_get_unique_name(connection); + if (service) { + QVarLengthArray<char, 56> filter; + filter.append("destination='", 13); + filter.append(service, qstrlen(service)); + filter.append("\'\0", 2); + + dbus_bus_add_match(connection, filter.constData(), &error); + if (handleError()) { + closeConnection(); + return; + } + } else { + qWarning("QDBusConnectionPrivate::SetConnection: Unable to get base service"); + } + +#if USE_OUTSIDE_DISPATCH + dbus_connection_add_filter_outside(connection, qDBusSignalFilter, qDBusSignalFilterOutside, this, 0); +#else + dbus_connection_add_filter(connection, qDBusSignalFilter, this, 0); +#endif + + //qDebug("base service: %s", service); + + // schedule a dispatch: + QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection); +} + +extern "C"{ +static void qDBusResultReceived(DBusPendingCall *pending, void *user_data) +{ + QDBusConnectionPrivate::messageResultReceived(pending, user_data); +} +} + +void QDBusConnectionPrivate::messageResultReceived(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); + + // Deliver the return values of a remote function call. + // + // There is only one connection and it is specified by idx + // The slot must have the same parameter types that the message does + // The slot may have less parameters than the message + // The slot may optionally have one final parameter that is QDBusMessage + // The slot receives read-only copies of the message (i.e., pass by value or by const-ref) + + QDBusMessage msg = QDBusMessage::fromDBusMessage(reply, QDBusConnection(connection->name)); + qDebug() << "got message: " << msg; + CallDeliveryEvent *e = prepareReply(call->receiver, call->methodIdx, call->metaTypes, msg); + if (e) + connection->postCallDeliveryEvent(e); + else + qDebug() << "Deliver failed!"; + } + dbus_pending_call_unref(pending); + delete call; +} + +int QDBusConnectionPrivate::send(const QDBusMessage& message) const +{ + DBusMessage *msg = message.toDBusMessage(); + if (!msg) + return 0; + + 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); + int serial = 0; + if (isOk) + serial = dbus_message_get_serial(msg); + + dbus_message_unref(msg); + return serial; +} + +QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message, + int mode) +{ + if (!QCoreApplication::instance() || mode == QDBusConnection::NoUseEventLoop) { + DBusMessage *msg = message.toDBusMessage(); + if (!msg) + return QDBusMessage(); + + qDebug() << "sending message:" << message; + DBusMessage *reply = dbus_connection_send_with_reply_and_block(connection, msg, + -1, &error); + handleError(); + dbus_message_unref(msg); + + if (lastError.isValid()) + return QDBusMessage::fromError(lastError); + + QDBusMessage amsg = QDBusMessage::fromDBusMessage(reply, QDBusConnection(name)); + qDebug() << "got message:" << amsg; + + if (dbus_connection_get_dispatch_status(connection) == DBUS_DISPATCH_DATA_REMAINS) + QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection); + return amsg; + } else { // use the event loop + QDBusReplyWaiter waiter; + if (sendWithReplyAsync(message, &waiter, SLOT(reply(const QDBusMessage&))) > 0) { + // enter the event loop and wait for a reply + waiter.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents); + + lastError = waiter.replyMsg; // set or clear error + return waiter.replyMsg; + } + + return QDBusMessage(); + } +} + +int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, + const char *method) +{ + if (!receiver || !method || !*method) + // would not be able to deliver a reply + return send(message); + + int slotIdx = -1; + QList<int> metaTypes; + QByteArray normalizedName = QMetaObject::normalizedSignature(method + 1); + slotIdx = findSlot(receiver, normalizedName, metaTypes); + if (slotIdx == -1) + // would not be able to deliver a reply + return send(message); + + DBusMessage *msg = message.toDBusMessage(); + if (!msg) + return 0; + + 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; +} + +void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook &hook) +{ + signalHooks.insertMulti(key, hook); + connect(hook.obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*))); +} + +void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node) +{ + connect(node->obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*))); + + if (node->flags & QDBusConnection::ExportAdaptors) { + QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(node->obj); + + // disconnect and reconnect to avoid duplicates + connector->disconnect(SIGNAL(relaySignal(QObject*,const char*,const char*,QVariantList)), + this, SLOT(relaySignal(QObject*,const char*,const char*,QVariantList))); + connect(connector, SIGNAL(relaySignal(QObject*,const char*,const char*,QVariantList)), + SLOT(relaySignal(QObject*,const char*,const char*,QVariantList))); + } +} + +void QDBusConnectionPrivate::connectRelay(const QString &service, const QString &path, + const QString &interface, + QDBusAbstractInterface *receiver, + const char *signal) +{ + // this function is called by QDBusAbstractInterface when one of its signals is connected + // we set up a relay from D-Bus into it + SignalHook hook; + QString key; + if (!prepareHook(hook, key, service, path, interface, QString(), receiver, signal, + QDBusAbstractInterface::staticMetaObject.methodCount(), true)) + return; // don't connect + + // add it to our list: + QWriteLocker locker(&lock); + SignalHookHash::ConstIterator it = signalHooks.find(key); + SignalHookHash::ConstIterator end = signalHooks.end(); + for ( ; it != end && it.key() == key; ++it) { + const SignalHook &entry = it.value(); + if (entry.sender == hook.sender && + entry.path == hook.path && + entry.signature == hook.signature && + entry.obj == hook.obj && + entry.midx == hook.midx) + return; // already there, no need to re-add + } + + connectSignal(key, hook); +} + +void QDBusConnectionPrivate::disconnectRelay(const QString &service, const QString &path, + const QString &interface, + QDBusAbstractInterface *receiver, + const char *signal) +{ + // this function is called by QDBusAbstractInterface when one of its signals is disconnected + // we remove relay from D-Bus into it + SignalHook hook; + QString key; + if (!prepareHook(hook, key, service, path, interface, QString(), receiver, signal, + QDBusAbstractInterface::staticMetaObject.methodCount(), true)) + return; // don't connect + + // remove it from our list: + QWriteLocker locker(&lock); + SignalHookHash::Iterator it = signalHooks.find(key); + SignalHookHash::Iterator end = signalHooks.end(); + for ( ; it != end && it.key() == key; ++it) { + const SignalHook &entry = it.value(); + if (entry.sender == hook.sender && + entry.path == hook.path && + entry.signature == hook.signature && + entry.obj == hook.obj && + entry.midx == hook.midx) { + // found it + signalHooks.erase(it); + return; + } + } + + qWarning("QDBusConnectionPrivate::disconnectRelay called for a signal that was not found"); +} + +QString QDBusConnectionPrivate::getNameOwner(const QString& name) +{ + if (QDBusUtil::isValidUniqueConnectionName(name)) + return name; + if (!connection || !QDBusUtil::isValidBusName(name)) + return QString(); + + QDBusMessage msg = QDBusMessage::methodCall(QLatin1String(DBUS_SERVICE_DBUS), + QLatin1String(DBUS_PATH_DBUS), QLatin1String(DBUS_INTERFACE_DBUS), + QLatin1String("GetNameOwner")); + msg << name; + QDBusMessage reply = sendWithReply(msg, QDBusConnection::NoUseEventLoop); + if (!lastError.isValid() && reply.type() == QDBusMessage::ReplyMessage) + return reply.first().toString(); + return QString(); +} + +QDBusInterfacePrivate * +QDBusConnectionPrivate::findInterface(const QString &service, + const QString &path, + const QString &interface) +{ + // check if it's there first -- FIXME: add binding mode + QDBusMetaObject *mo = 0; + QString owner = getNameOwner(service); + if (connection && !owner.isEmpty() && QDBusUtil::isValidObjectPath(path) && + (interface.isEmpty() || QDBusUtil::isValidInterfaceName(interface))) + // always call here with the unique connection name + mo = findMetaObject(owner, path, interface); + + QDBusInterfacePrivate *p = new QDBusInterfacePrivate(QDBusConnection(name), this, owner, path, interface, mo); + + if (!mo) { + // invalid object + p->isValid = false; + p->lastError = lastError; + if (!lastError.isValid()) { + // try to determine why we couldn't get the data + if (!connection) + p->lastError = QDBusError(QDBusError::Disconnected, + QLatin1String("Not connected to D-Bus server")); + else if (owner.isEmpty()) + p->lastError = QDBusError(QDBusError::ServiceUnknown, + QString(QLatin1String("Service %1 is unknown")).arg(service)); + else if (!QDBusUtil::isValidObjectPath(path)) + p->lastError = QDBusError(QDBusError::InvalidArgs, + QString(QLatin1String("Object path %1 is invalid")).arg(path)); + else if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) + p->lastError = QDBusError(QDBusError::InvalidArgs, + QString(QLatin1String("Interface %1 is invalid")).arg(interface)); + else + p->lastError = QDBusError(QDBusError::Other, QLatin1String("Unknown error")); + } + } + + return p; +} + +struct qdbus_Introspect +{ + QString xml; + inline void operator()(QDBusConnectionPrivate::ObjectTreeNode *node) + { xml = qDBusIntrospectObject(node); } +}; + +QDBusMetaObject * +QDBusConnectionPrivate::findMetaObject(const QString &service, const QString &path, + const QString &interface) +{ + // service must be a unique connection name + if (!interface.isEmpty()) { + QReadLocker locker(&lock); + QDBusMetaObject *mo = cachedMetaObjects.value(interface, 0); + if (mo) + return mo; + } + if (service == QString::fromUtf8(dbus_bus_get_unique_name(connection))) { + // it's one of our own + QWriteLocker locker(&lock); + QDBusMetaObject *mo = 0; + if (!interface.isEmpty()) + mo = cachedMetaObjects.value(interface, 0); + if (mo) + // maybe it got created when we switched from read to write lock + return mo; + + qdbus_Introspect apply; + if (!applyForObject(&rootNode, path, apply)) { + lastError = QDBusError(QDBusError::InvalidArgs, + QString(QLatin1String("No object at %1")).arg(path)); + return 0; // no object at path + } + + // release the lock and return + return QDBusMetaObject::createMetaObject(interface, apply.xml, cachedMetaObjects, lastError); + } + + // not local: introspect the target object: + QDBusMessage msg = QDBusMessage::methodCall(service, path, + QLatin1String(DBUS_INTERFACE_INTROSPECTABLE), + QLatin1String("Introspect")); + + + QDBusMessage reply = sendWithReply(msg, QDBusConnection::NoUseEventLoop); + + // it doesn't exist yet, we have to create it + QWriteLocker locker(&lock); + QDBusMetaObject *mo = 0; + if (!interface.isEmpty()) + mo = cachedMetaObjects.value(interface, 0); + if (mo) + // maybe it got created when we switched from read to write lock + return mo; + + QString xml; + if (reply.type() == QDBusMessage::ReplyMessage) + // fetch the XML description + xml = reply.first().toString(); + else { + lastError = reply; + if (reply.type() != QDBusMessage::ErrorMessage || lastError != QDBusError::UnknownMethod) + return 0; // error + } + + // release the lock and return + return QDBusMetaObject::createMetaObject(interface, xml, cachedMetaObjects, lastError); +} + +void QDBusReplyWaiter::reply(const QDBusMessage &msg) +{ + replyMsg = msg; + QTimer::singleShot(0, this, SLOT(quit())); +} + +#include "qdbusconnection_p.moc" diff --git a/qt/src/qdbusinterface.cpp b/qt/src/qdbusinterface.cpp new file mode 100644 index 0000000..6367654 --- /dev/null +++ b/qt/src/qdbusinterface.cpp @@ -0,0 +1,203 @@ +/* -*- 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 "qdbusinterface.h" + +#include <dbus/dbus.h> +#include <QtCore/qpointer.h> + +#include "qdbusinterface_p.h" +#include "qdbusconnection_p.h" + +/*! + \class QDBusInterface + \brief Proxy class for interfaces on remote objects. + + QDBusInterface is a generic accessor class that is used to place calls to remote objects, + connect to signals exported by remote objects and get/set the value of remote properties. This + class is useful for dynamic access to remote objects: that is, when you do not have a generated + code that represents the remote interface. + + Calls are usually placed by using the call() function, which constructs the message, sends it + over the bus, waits for the reply and decodes the reply. Signals are connected to by using the + normal QObject::connect() function. Finally, properties are accessed using the + QObject::property() and QObject::setProperty() functions. +*/ + +QDBusInterface::QDBusInterface(QDBusInterfacePrivate *p) + : QDBusAbstractInterface(p) +{ +} + +/*! + Destroy the object interface and frees up any resource used. +*/ +QDBusInterface::~QDBusInterface() +{ + // resources are freed in QDBusInterfacePrivate::~QDBusInterfacePrivate() +} + +/*! + \internal + Overrides QObject::metaObject to return our own copy. +*/ +const QMetaObject *QDBusInterface::metaObject() const +{ + return d_func()->isValid ? d_func()->metaObject : &QDBusAbstractInterface::staticMetaObject; +} + +/*! + \internal + Override QObject::qt_metacast to catch the interface name too. +*/ +void *QDBusInterface::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, "QDBusInterface")) + return static_cast<void*>(const_cast<QDBusInterface*>(this)); + if (d_func()->interface == _clname) + return static_cast<void*>(const_cast<QDBusInterface*>(this)); + return QDBusAbstractInterface::qt_metacast(_clname); +} + +/*! + \internal + Dispatch the call through the private. +*/ +int QDBusInterface::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QDBusAbstractInterface::qt_metacall(_c, _id, _a); + if (_id < 0 || !d_func()->isValid) + return _id; + return d_func()->metacall(_c, _id, _a); +} + +int QDBusInterfacePrivate::metacall(QMetaObject::Call c, int id, void **argv) +{ + Q_Q(QDBusInterface); + + if (c == QMetaObject::InvokeMetaMethod) { + int offset = metaObject->methodOffset(); + QMetaMethod mm = metaObject->method(id + offset); + + if (mm.methodType() == QMetaMethod::Signal) { + // signal relay from D-Bus world to Qt world + QMetaObject::activate(q, metaObject, id, argv); + + } else if (mm.methodType() == QMetaMethod::Slot) { + // method call relay from Qt world to D-Bus world + // get D-Bus equivalent signature + QString methodName = metaObject->dbusNameForMethod(id); + const int *inputTypes = metaObject->inputTypesForMethod(id); + const int *outputTypes = metaObject->outputTypesForMethod(id); + + int inputTypesCount = *inputTypes; + int outputTypesCount = *outputTypes++; + + // we will assume that the input arguments were passed correctly + QVariantList args; + for (int i = 1; i <= inputTypesCount; ++i) + args << QVariant(inputTypes[i], argv[i]); + + // make the call + QPointer<QDBusInterface> qq = q; + QDBusMessage reply = q->callWithArgs(methodName, args); + args.clear(); + + // access to "this" or to "q" below this point must check for "qq" + // we may have been deleted! + + // check if we got the right number of parameters back: + bool success = false; + if (reply.count() == outputTypesCount) { + // copy the values out + for (int i = 0; i < outputTypesCount; ++i) { + // treat the return value specially, since it may be null: + if (i == 0 && argv[0] == 0) + continue; + + // ensure that the types are correct: + const QVariant &item = reply.at(i); + if (outputTypes[i] != item.userType()) { + success = false; + break; + } + + if (i == 0) + QDBusMetaObject::assign(argv[0], item); + else + QDBusMetaObject::assign(argv[inputTypesCount + i], item); + } + } + + // bail out, something weird happened + if (!success && !qq.isNull()) { + QString errmsg = QLatin1String("Invalid signature `%1' in return from call to %2.%3"); + lastError = QDBusError(QDBusError::InvalidSignature, + errmsg.arg(reply.signature(), interface, methodName)); + } + + // done + return -1; + } + } else if (c == QMetaObject::ReadProperty) { + // Qt doesn't support non-readable properties + // we have to re-check + QMetaProperty mp = metaObject->property(id + metaObject->propertyOffset()); + if (!mp.isReadable()) + return -1; // don't read + + QVariant value = property(mp); + if (value.type() == QVariant::Invalid) + // an error occurred -- property already set lastError + return -1; + else if (mp.type() == QVariant::LastType) + // QVariant is special in this context + *reinterpret_cast<QVariant *>(argv[0]) = value; + else + QDBusMetaObject::assign(argv[0], value); + + return -1; // handled + } else if (c == QMetaObject::WriteProperty) { + // QMetaProperty::write has already checked that we're writable + // it has also checked that the type is right + QVariant value(metaObject->propertyMetaType(id), argv[0]); + QMetaProperty mp = metaObject->property(id + metaObject->propertyOffset()); + + setProperty(mp, value); + return -1; + } + return id; +} + +QDBusInterfacePtr::QDBusInterfacePtr(QDBusConnection &conn, const QString &service, const QString &path, + const QString &interface) + : d(conn.findInterface(service, path, interface)) +{ +} + +QDBusInterfacePtr::QDBusInterfacePtr(const QString &service, const QString &path, const QString &interface) + : d(QDBus::sessionBus().findInterface(service, path, interface)) +{ +} + diff --git a/qt/src/qdbusinterface.h b/qt/src/qdbusinterface.h new file mode 100644 index 0000000..0b4d187 --- /dev/null +++ b/qt/src/qdbusinterface.h @@ -0,0 +1,63 @@ +/* -*- 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 QDBUSINTERFACE_H +#define QDBUSINTERFACE_H + +#include "qdbusabstractinterface.h" + +class QDBusInterfacePrivate; +class QDBUS_EXPORT QDBusInterface: public QDBusAbstractInterface +{ + friend class QDBusConnection; +private: + QDBusInterface(QDBusInterfacePrivate *p); + +public: + ~QDBusInterface(); + + virtual const QMetaObject *metaObject() const; + virtual void *qt_metacast(const char *); + virtual int qt_metacall(QMetaObject::Call, int, void **); + +private: + Q_DECLARE_PRIVATE(QDBusInterface) + Q_DISABLE_COPY(QDBusInterface) +}; + +struct QDBUS_EXPORT QDBusInterfacePtr +{ + QDBusInterfacePtr(QDBusInterface *iface) : d(iface) { } + QDBusInterfacePtr(QDBusConnection &conn, const QString &service, const QString &path, + const QString &interface = QString()); + QDBusInterfacePtr(const QString &service, const QString &path, const QString &interface = QString()); + ~QDBusInterfacePtr() { delete d; } + + QDBusInterface *interface() { return d; } + QDBusInterface *operator->() { return d; } +private: + QDBusInterface *const d; + Q_DISABLE_COPY(QDBusInterfacePtr) +}; + +#endif diff --git a/qt/src/qdbusinterface_p.h b/qt/src/qdbusinterface_p.h new file mode 100644 index 0000000..25cb9ff --- /dev/null +++ b/qt/src/qdbusinterface_p.h @@ -0,0 +1,65 @@ +/* + * + * 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 "qdbusabstractinterface_p.h" +#include "qdbusmetaobject_p.h" +#include "qdbusinterface.h" + +class QDBusInterfacePrivate: public QDBusAbstractInterfacePrivate +{ +public: + Q_DECLARE_PUBLIC(QDBusInterface) + + QDBusMetaObject *metaObject; + + inline QDBusInterfacePrivate(const QDBusConnection &con, QDBusConnectionPrivate *conp, + const QString &serv, const QString &p, const QString &iface, + QDBusMetaObject *mo = 0) + : QDBusAbstractInterfacePrivate(con, conp, serv, p, iface), metaObject(mo) + { + } + ~QDBusInterfacePrivate() + { + if (metaObject && !metaObject->cached) + delete metaObject; + } + + int metacall(QMetaObject::Call c, int id, void **argv); +}; + +#endif diff --git a/qt/src/qdbusinternalfilters.cpp b/qt/src/qdbusinternalfilters.cpp new file mode 100644 index 0000000..8886d3b --- /dev/null +++ b/qt/src/qdbusinternalfilters.cpp @@ -0,0 +1,235 @@ +/* -*- mode: C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "qdbusconnection_p.h" + +#include <dbus/dbus.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qstringlist.h> + +#include "qdbusabstractadaptor.h" +#include "qdbusabstractadaptor_p.h" +#include "qdbusconnection.h" +#include "qdbusmessage.h" +#include "qdbustypehelper_p.h" +#include "qdbusutil.h" + +// defined in qdbusxmlgenerator.cpp +extern QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo, + const QMetaObject *base, int flags); + +static const char introspectableInterfaceXml[] = + " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" + " <method name=\"Introspect\">\n" + " <arg name=\"xml_data\" type=\"s\" direction=\"out\"/>\n" + " </method>\n" + " </interface>\n"; + +static const char propertiesInterfaceXml[] = + " <interface name=\"org.freedesktop.DBus.Properties\">\n" + " <method name=\"Get\">\n" + " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n" + " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n" + " <arg name=\"value\" type=\"v\" direction=\"out\"/>\n" + " </method>\n" + " <method name=\"Set\">\n" + " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n" + " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n" + " <arg name=\"value\" type=\"v\" direction=\"in\"/>\n" + " </method>\n" + " </interface>\n"; + +static QString generateSubObjectXml(QObject *object) +{ + QString retval; + foreach (QObject *child, object->children()) { + QString name = child->objectName(); + if (!name.isEmpty()) + retval += QString(QLatin1String(" <node name=\"%1\"/>\n")) + .arg(name); + } + return retval; +} + +QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode *node) +{ + // object may be null + + QString xml_data(QLatin1String(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE)); + xml_data += QLatin1String("<node>\n"); + + if (node->obj) { + if (node->flags & QDBusConnection::ExportContents) { + const QMetaObject *mo = node->obj->metaObject(); + for ( ; mo != &QObject::staticMetaObject; mo = mo->superClass()) + xml_data += qDBusGenerateMetaObjectXml(QString(), mo, mo->superClass(), + node->flags); + } + + // does this object have adaptors? + QDBusAdaptorConnector *connector; + if (node->flags & QDBusConnection::ExportAdaptors && + (connector = qDBusFindAdaptorConnector(node->obj))) { + + // trasverse every adaptor in this object + QDBusAdaptorConnector::AdaptorMap::ConstIterator it = connector->adaptors.constBegin(); + QDBusAdaptorConnector::AdaptorMap::ConstIterator end = connector->adaptors.constEnd(); + for ( ; it != end; ++it) { + // add the interface: + QString ifaceXml = QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(it->adaptor); + if (ifaceXml.isEmpty()) { + // add the interface's contents: + ifaceXml += qDBusGenerateMetaObjectXml(it->interface, it->metaObject, + &QDBusAbstractAdaptor::staticMetaObject, + QDBusConnection::ExportAllContents); + + QDBusAbstractAdaptorPrivate::saveIntrospectionXml(it->adaptor, ifaceXml); + } + + xml_data += ifaceXml; + } + } + + xml_data += QLatin1String( introspectableInterfaceXml ); + xml_data += QLatin1String( propertiesInterfaceXml ); + } + + if (node->flags & QDBusConnection::ExportChildObjects) { + xml_data += generateSubObjectXml(node->obj); + } else { + // generate from the object tree + foreach (const QDBusConnectionPrivate::ObjectTreeNode::Data &entry, node->children) { + if (entry.node && (entry.node->obj || !entry.node->children.isEmpty())) + xml_data += QString(QLatin1String(" <node name=\"%1\"/>\n")) + .arg(entry.name); + } + } + + xml_data += QLatin1String("</node>\n"); + return xml_data; +} + +void qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode *node, + const QDBusMessage &msg) +{ + // now send it + QDBusMessage reply = QDBusMessage::methodReply(msg); + reply << qDBusIntrospectObject(node); + msg.connection().send(reply); +} + +// implement the D-Bus interface org.freedesktop.DBus.Properties + +static void sendPropertyError(const QDBusMessage &msg, const QString &interface_name) +{ + QDBusMessage error = QDBusMessage::error(msg, QLatin1String(DBUS_ERROR_INVALID_ARGS), + QString::fromLatin1("Interface %1 was not found in object %2") + .arg(interface_name) + .arg(msg.path())); + msg.connection().send(error); +} + +void qDBusPropertyGet(const QDBusConnectionPrivate::ObjectTreeNode *node, const QDBusMessage &msg) +{ + Q_ASSERT(msg.count() == 2); + QString interface_name = msg.at(0).toString(); + QByteArray property_name = msg.at(1).toString().toUtf8(); + + QDBusAdaptorConnector *connector; + QVariant value; + if (node->flags & QDBusConnection::ExportAdaptors && + (connector = qDBusFindAdaptorConnector(node->obj))) { + + // find the class that implements interface_name + QDBusAdaptorConnector::AdaptorMap::ConstIterator it; + it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(), + interface_name); + if (it != connector->adaptors.end() && it->interface == interface_name) + value = it->adaptor->property(property_name); + } + + if (!value.isValid() && node->flags & QDBusConnection::ExportProperties) { + // try the object itself + int pidx = node->obj->metaObject()->indexOfProperty(property_name); + if (pidx != -1) { + QMetaProperty mp = node->obj->metaObject()->property(pidx); + if (mp.isScriptable() || (node->flags & QDBusConnection::ExportAllProperties) == + QDBusConnection::ExportAllProperties) + value = mp.read(node->obj); + } + } + + if (!value.isValid()) { + // the property was not found + sendPropertyError(msg, interface_name); + return; + } + + QDBusMessage reply = QDBusMessage::methodReply(msg); + reply.setSignature(QLatin1String("v")); + reply << value; + msg.connection().send(reply); +} + +void qDBusPropertySet(const QDBusConnectionPrivate::ObjectTreeNode *node, const QDBusMessage &msg) +{ + Q_ASSERT(msg.count() == 3); + QString interface_name = msg.at(0).toString(); + QByteArray property_name = msg.at(1).toString().toUtf8(); + QVariant value = QDBusTypeHelper<QVariant>::fromVariant(msg.at(2)); + + QDBusAdaptorConnector *connector; + if (node->flags & QDBusConnection::ExportAdaptors && + (connector = qDBusFindAdaptorConnector(node->obj))) { + + // find the class that implements interface_name + QDBusAdaptorConnector::AdaptorMap::ConstIterator it; + it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(), + interface_name); + if (it != connector->adaptors.end() && it->interface == interface_name) + if (it->adaptor->setProperty(property_name, value)) { + msg.connection().send(QDBusMessage::methodReply(msg)); + return; + } + } + + if (node->flags & QDBusConnection::ExportProperties) { + // try the object itself + int pidx = node->obj->metaObject()->indexOfProperty(property_name); + if (pidx != -1) { + QMetaProperty mp = node->obj->metaObject()->property(pidx); + if (mp.isScriptable() || (node->flags & QDBusConnection::ExportAllProperties) == + QDBusConnection::ExportAllProperties) { + + if (mp.write(node->obj, value)) { + msg.connection().send(QDBusMessage::methodReply(msg)); + return; + } + } + } + } + + // the property was not found or not written to + sendPropertyError(msg, interface_name); +} diff --git a/qt/src/qdbusintrospection.cpp b/qt/src/qdbusintrospection.cpp new file mode 100644 index 0000000..20acbd2 --- /dev/null +++ b/qt/src/qdbusintrospection.cpp @@ -0,0 +1,403 @@ +/* -*- 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_p.h" +#include "qdbusxmlparser_p.h" + +/*! + \class QDBusIntrospection + \brief Information about introspected objects and interfaces on D-Bus. + \internal + + 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). +*/ + +/*! + \class 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. +*/ + +/*! + \fn QDBusIntrospection::Argument::operator==(const Argument &other) const + Compares this object against \a other and return true if they are the same. +*/ + +/*! + \class 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. +*/ + +/*! + \fn QDBusIntrospection::Method::operator==(const Method &other) const + Compares this object against \a other and return true if they are the same. +*/ + +/*! + \class 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::name + The signal's name. +*/ + +/*! + \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. +*/ + +/*! + \fn QDBusIntrospection::Signal::operator==(const Signal& other) const + Compares this object against \a other and return true if they are the same. +*/ + +/*! + \class 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: + \value Read + \value Write + \value 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. +*/ + +/*! + \fn QDBusIntrospection::Property::operator==(const Property &other) const + Compares this object against \a other and return true if they are the same. +*/ + +/*! + \class 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. +*/ + +/*! + \fn QDBusIntrospection::Interface::operator==(const Interface &other) const + Compares this object against \a other and return true if they are the same. + + Note that two interfaces are considered to be the same if they have the same name. The internal + structures in the objects are not compared. +*/ + +/*! + \class 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 \l + {QDBusIntrospection::Object::path}{path}. +*/ + +/*! + \class 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 (given by \a xml) 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. +*/ +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 (given by \a xml) 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. +*/ +QDBusIntrospection::Interfaces +QDBusIntrospection::parseInterfaces(const QString &xml) +{ + QString null; + QDBusXmlParser parser(null, null, xml); + return parser.interfaces(); +} + +/*! + Parses the XML document fragment (given by \a xml) containing one object, found at the service + \a service and path \a path. + + 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. +*/ +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 (given by \a xml) containing one object node and returns all + the information about the interfaces and sub-objects, found at the service \a service and path + \a path. + + The Objects map returned will contain the absolute path names in the key. +*/ +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/src/qdbusintrospection_p.h b/qt/src/qdbusintrospection_p.h new file mode 100644 index 0000000..0e4e901 --- /dev/null +++ b/qt/src/qdbusintrospection_p.h @@ -0,0 +1,147 @@ +/* -*- 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 "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 + { + QString 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; + QString 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.isEmpty() && 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/src/qdbusmacros.h b/qt/src/qdbusmacros.h new file mode 100644 index 0000000..0af5b5b --- /dev/null +++ b/qt/src/qdbusmacros.h @@ -0,0 +1,60 @@ +/* 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 + * + * 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. + * + */ + +/*! + \file qdbusmacros.h +*/ + +#ifndef QDBUSMACROS_H +#define QDBUSMACROS_H + +#include <QtCore/qglobal.h> +#include <QtCore/qmetatype.h> +#include <QtCore/qvariant.h> + +#ifdef QT_NO_MEMBER_TEMPLATES +# error Sorry, you need a compiler with support for template member functions to compile QtDBus. +#endif + +#if defined(QDBUS_MAKEDLL) +# define QDBUS_EXPORT Q_DECL_EXPORT +#else +# define QDBUS_EXPORT Q_DECL_IMPORT +#endif + +#ifndef Q_MOC_RUN +# define Q_ASYNC +#endif + +#ifdef Q_CC_MSVC +#include <QtCore/qlist.h> +#include <QtCore/qset.h> +#include <QtCore/qhash.h> +#include <QtCore/qvector.h> +class QDBusType; +inline uint qHash(const QVariant&) { Q_ASSERT(0); return 0; } +inline uint qHash(const QDBusType&) { Q_ASSERT(0); return 0; } +#endif + +#endif diff --git a/qt/src/qdbusmarshall.cpp b/qt/src/qdbusmarshall.cpp new file mode 100644 index 0000000..6209ffb --- /dev/null +++ b/qt/src/qdbusmarshall.cpp @@ -0,0 +1,554 @@ +/* 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 + * + * 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 "qdbusmarshall_p.h" +#include "qdbustype_p.h" +#include "qdbustypehelper_p.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> + +static QVariant qFetchParameter(DBusMessageIter *it); + +template <typename T> +inline T qIterGet(DBusMessageIter *it) +{ + T t; + dbus_message_iter_get_basic(it, &t); + return t; +} + +template<> +inline QVariant qIterGet(DBusMessageIter *it) +{ + DBusMessageIter sub; + dbus_message_iter_recurse(it, &sub); + return QDBusTypeHelper<QVariant>::toVariant(qFetchParameter(&sub)); +} + +template <typename DBusType, typename QtType> +inline QVariant qFetchList(DBusMessageIter *arrayIt) +{ + QList<QtType> list; + + DBusMessageIter it; + dbus_message_iter_recurse(arrayIt, &it); + if (dbus_message_iter_get_array_len(&it) == 0) + return QDBusTypeHelper<QList<QtType> >::toVariant(list); + + do { + list.append( static_cast<QtType>( qIterGet<DBusType>(&it) ) ); + } while (dbus_message_iter_next(&it)); + + return QDBusTypeHelper<QList<QtType> >::toVariant(list); +} + +static QStringList qFetchStringList(DBusMessageIter *arrayIt) +{ + QStringList list; + + DBusMessageIter it; + dbus_message_iter_recurse(arrayIt, &it); + if (dbus_message_iter_get_array_len(&it) == 0) + return list; + + do { + list.append(QString::fromUtf8(qIterGet<char *>(&it))); + } while (dbus_message_iter_next(&it)); + + return list; +} + +static QVariant qFetchParameter(DBusMessageIter *it) +{ + switch (dbus_message_iter_get_arg_type(it)) { + case DBUS_TYPE_BYTE: + return qVariantFromValue(qIterGet<unsigned char>(it)); + case DBUS_TYPE_INT16: + return qVariantFromValue(qIterGet<dbus_int16_t>(it)); + case DBUS_TYPE_UINT16: + return qVariantFromValue(qIterGet<dbus_uint16_t>(it)); + case DBUS_TYPE_INT32: + return qIterGet<dbus_int32_t>(it); + case DBUS_TYPE_UINT32: + return qIterGet<dbus_uint32_t>(it); + case DBUS_TYPE_DOUBLE: + return qIterGet<double>(it); + case DBUS_TYPE_BOOLEAN: + return bool(qIterGet<dbus_bool_t>(it)); + case DBUS_TYPE_INT64: + return static_cast<qlonglong>(qIterGet<dbus_int64_t>(it)); + case DBUS_TYPE_UINT64: + return static_cast<qulonglong>(qIterGet<dbus_uint64_t>(it)); + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + return QString::fromUtf8(qIterGet<char *>(it)); + case DBUS_TYPE_VARIANT: + return qIterGet<QVariant>(it); + case DBUS_TYPE_ARRAY: { + int arrayType = dbus_message_iter_get_element_type(it); + switch (arrayType) + { + case DBUS_TYPE_BYTE: { + // QByteArray + 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); + } + case DBUS_TYPE_INT16: + return qFetchList<dbus_int16_t, short>(it); + case DBUS_TYPE_UINT16: + return qFetchList<dbus_uint16_t, ushort>(it); + case DBUS_TYPE_INT32: + return qFetchList<dbus_int32_t, int>(it); + case DBUS_TYPE_UINT32: + return qFetchList<dbus_uint32_t, uint>(it); + case DBUS_TYPE_BOOLEAN: + return qFetchList<dbus_bool_t, bool>(it); + case DBUS_TYPE_DOUBLE: + return qFetchList<double, double>(it); + case DBUS_TYPE_INT64: + return qFetchList<dbus_int64_t, qlonglong>(it); + case DBUS_TYPE_UINT64: + return qFetchList<dbus_uint64_t, qulonglong>(it); + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + return qFetchStringList(it); + case DBUS_TYPE_VARIANT: + return qFetchList<QVariant, QVariant>(it); + case 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_get_array_len(&sub) == 0) + // empty map + return map; + + do { + DBusMessageIter itemIter; + dbus_message_iter_recurse(&sub, &itemIter); + Q_ASSERT(dbus_message_iter_has_next(&itemIter)); + QString key = qFetchParameter(&itemIter).toString(); + dbus_message_iter_next(&itemIter); + map.insertMulti(key, qFetchParameter(&itemIter)); + } while (dbus_message_iter_next(&sub)); + return map; + } + } + } + // fall through + // common handling for structs and lists of lists (for now) + case DBUS_TYPE_STRUCT: { + QList<QVariant> list; + DBusMessageIter sub; + dbus_message_iter_recurse(it, &sub); + if (dbus_message_iter_get_array_len(&sub) == 0) + return list; + do { + list.append(qFetchParameter(&sub)); + } while (dbus_message_iter_next(&sub)); + return list; + } + + 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(); + break; + } +} + +void QDBusMarshall::messageToList(QList<QVariant> &list, DBusMessage *message) +{ + Q_ASSERT(message); + + DBusMessageIter it; + if (!dbus_message_iter_init(message, &it)) + return; + + do { + list.append(qFetchParameter(&it)); + } while (dbus_message_iter_next(&it)); +} + +// 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) +{ + if (!type.isValid()) { + // guess it from the variant + type = QDBusType::guessFromVariant(var); + return true; + } + + int id = var.userType(); + + if (type.dbusType() == DBUS_TYPE_VARIANT) { + // this is a non symmetrical operation: + // nest a QVariant if we want variant and it isn't so + if (id != QDBusTypeHelper<QVariant>::id()) { + QVariant tmp = var; + var = QDBusTypeHelper<QVariant>::toVariant(tmp); + } + return true; + } + + switch (id) { + case QVariant::Bool: + case QMetaType::Short: + case QMetaType::UShort: + case QMetaType::UChar: + case QVariant::Int: + case QVariant::UInt: + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::Double: + case QVariant::String: + 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 + void *null = 0; + var = QVariant(type.qvariantType(), null); + break; + } + + default: + if (id == QDBusTypeHelper<QVariant>::id()) { + // if we got here, it means the match above for DBUS_TYPE_VARIANT didn't work + qWarning("Invalid conversion from nested variant to '%s'", + type.dbusSignature().constData()); + return false; + } else if (type.dbusType() == DBUS_TYPE_ARRAY) { + int subType = type.arrayElement().dbusType(); + if ((id == QDBusTypeHelper<bool>::listId() && subType == DBUS_TYPE_BOOLEAN) || + (id == QDBusTypeHelper<short>::listId() && subType == DBUS_TYPE_INT16) || + (id == QDBusTypeHelper<ushort>::listId() && subType == DBUS_TYPE_UINT16) || + (id == QDBusTypeHelper<int>::listId() && subType == DBUS_TYPE_INT32) || + (id == QDBusTypeHelper<uint>::listId() && subType == DBUS_TYPE_UINT32) || + (id == QDBusTypeHelper<qlonglong>::listId() && subType == DBUS_TYPE_INT64) || + (id == QDBusTypeHelper<qulonglong>::listId() && subType == DBUS_TYPE_UINT64) || + (id == QDBusTypeHelper<double>::listId() && subType == DBUS_TYPE_DOUBLE)) + return true; + } + + qWarning("Invalid conversion from %s to '%s'", var.typeName(), + type.dbusSignature().constData()); + return false; + } + + 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 &typelist); + +template<typename T> +static void qIterAppend(DBusMessageIter *it, const QDBusType &type, T arg) +{ + dbus_message_iter_append_basic(it, type.dbusType(), &arg); +} + +template<typename DBusType, typename QtType> +static void qAppendListToMessage(DBusMessageIter *it, const QDBusType &subType, + const QVariant &var) +{ + QList<QtType> list = QDBusTypeHelper<QList<QtType> >::fromVariant(var); + foreach (const QtType &item, list) + qIterAppend(it, subType, static_cast<DBusType>(item)); +} + +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(); + foreach (QString str, list) + qIterAppend(&sub, subType, str.toUtf8().constData()); + break; + } + + 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: { + 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); + + // 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); + } + break; + } + + case QVariant::List: { + const QVariantList list = var.toList(); + foreach (QVariant v, list) + qVariantToIteratorInternal(&sub, v, subType); + break; + } + + default: { + int id = var.userType(); + if (id == QDBusTypeHelper<bool>::listId()) + qAppendListToMessage<dbus_bool_t,bool>(&sub, subType, var); + else if (id == QDBusTypeHelper<short>::listId()) + qAppendListToMessage<dbus_int16_t,short>(&sub, subType, var); + else if (id == QDBusTypeHelper<ushort>::listId()) + qAppendListToMessage<dbus_uint16_t,ushort>(&sub, subType, var); + else if (id == QDBusTypeHelper<int>::listId()) + qAppendListToMessage<dbus_int32_t,int>(&sub, subType, var); + else if (id == QDBusTypeHelper<uint>::listId()) + qAppendListToMessage<dbus_uint32_t,uint>(&sub, subType, var); + else if (id == QDBusTypeHelper<qlonglong>::listId()) + qAppendListToMessage<dbus_int64_t,qlonglong>(&sub, subType, var); + else if (id == QDBusTypeHelper<qulonglong>::listId()) + qAppendListToMessage<dbus_uint64_t,qulonglong>(&sub, subType, var); + else if (id == QDBusTypeHelper<double>::listId()) + qAppendListToMessage<double,double>(&sub, subType, var); +#if 0 // never reached, since QVariant::List mached + else if (id == QDBusTypeHelper<QVariant>::listId()) + qAppendListToMessage<QVariant,QVariant>(&sub, subType, var); +#endif + else + qFatal("qAppendArrayToMessage got unknown type!"); + break; + } + } + + dbus_message_iter_close_container(it, &sub); +} + +static void qAppendStructToMessage(DBusMessageIter *it, const QDBusTypeList &typeList, + const QVariantList &list) +{ + 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) +{ + Q_UNUSED(type); // type is 'v' + + QVariant arg = var; + if (var.userType() == QDBusTypeHelper<QVariant>::id()) + arg = QDBusTypeHelper<QVariant>::fromVariant(var); // extract the inner variant + + QDBusType t = QDBusType::guessFromVariant(arg); + + // now add this variant + DBusMessageIter sub; + dbus_message_iter_open_container(it, DBUS_TYPE_VARIANT, t.dbusSignature(), &sub); + qVariantToIteratorInternal(&sub, arg, 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, QDBusTypeHelper<uchar>::fromVariant(var) ); + break; + case DBUS_TYPE_BOOLEAN: + qIterAppend( it, type, static_cast<dbus_bool_t>(var.toBool()) ); + break; + case DBUS_TYPE_INT16: + qIterAppend( it, type, QDBusTypeHelper<short>::fromVariant(var) ); + break; + case DBUS_TYPE_UINT16: + qIterAppend( it, type, QDBusTypeHelper<ushort>::fromVariant(var) ); + 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), QDBusType()); +} + +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); + + if (signature.isEmpty()) + qListToIterator(&it, list); + else + qListToIterator(&it, list, QDBusTypeList(signature.toUtf8())); +} diff --git a/qt/src/qdbusmarshall_p.h b/qt/src/qdbusmarshall_p.h new file mode 100644 index 0000000..7a2d46f --- /dev/null +++ b/qt/src/qdbusmarshall_p.h @@ -0,0 +1,57 @@ +/* qdbusmarshall_p.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 + * + * 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 QDBUSMARSHALLPRIVATE_H +#define QDBUSMARSHALLPRIVATE_H + +struct DBusMessage; + +template <typename T> class QList; +class QVariant; +class QString; + +/*! + \internal +*/ +class QDBusMarshall +{ +public: + static void listToMessage(const QList<QVariant> &list, DBusMessage *message, + const QString& signature); + static void messageToList(QList<QVariant> &list, DBusMessage *message); +}; + +#endif diff --git a/qt/src/qdbusmessage.cpp b/qt/src/qdbusmessage.cpp new file mode 100644 index 0000000..d0a0f1e --- /dev/null +++ b/qt/src/qdbusmessage.cpp @@ -0,0 +1,706 @@ +/* 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 + * + * 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 <qdebug.h> +#include <qstringlist.h> + +#include <dbus/dbus.h> + +#include "qdbuserror.h" +#include "qdbusmarshall_p.h" +#include "qdbusmessage_p.h" +#include "qdbustypehelper_p.h" + +QDBusMessagePrivate::QDBusMessagePrivate() + : connection(QString()), msg(0), reply(0), type(DBUS_MESSAGE_TYPE_INVALID), + timeout(-1), ref(1), repliedTo(false) +{ +} + +QDBusMessagePrivate::~QDBusMessagePrivate() +{ + if (msg) + dbus_message_unref(msg); + if (reply) + dbus_message_unref(reply); +} + +/////////////// +/*! + \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) + \list + \o Method calls + \o Method return values + \o Signal emissions + \o Error codes + \endlist + + Objects of this type are created with the four static functions signal, methodCall, + methodReply and error. +*/ + +/*! + \enum QDBusMessage::MessageType + The possible message types: + + \value MethodCallMessage a message representing an outgoing or incoming method call + \value SignalMessage a message representing an outgoing or incoming signal emission + \value ReplyMessage a message representing the return values of a method call + \value ErrorMessage a message representing an error condition in response to a method call + \value InvalidMessage an invalid message: this is never set on messages received from D-Bus +*/ + +/*! + Constructs a new DBus message representing a signal emission. A DBus signal is emitted + from one application and is received by all applications that are listening for that signal + from that interface. + + The signal will be constructed to represent a signal coming from the path \a path, interface \a + interface and signal name \a name. + + The QDBusMessage object that is returned can be sent with QDBusConnection::send(). +*/ +QDBusMessage QDBusMessage::signal(const QString &path, const QString &interface, + const QString &name) +{ + QDBusMessage message; + message.d_ptr->type = DBUS_MESSAGE_TYPE_SIGNAL; + message.d_ptr->path = path; + message.d_ptr->interface = interface; + message.d_ptr->name = name; + + return message; +} + +/*! + Constructs a new DBus message representing a method call. A method call always informs + its destination address (\a service, \a path, \a interface and \a 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 \a service parameter is + optional. + + The QDBusObject and QDBusInterface classes provide a simpler abstraction to synchronous + method calling. + + This function 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) +{ + QDBusMessage message; + message.d_ptr->type = DBUS_MESSAGE_TYPE_METHOD_CALL; + message.d_ptr->service = service; + message.d_ptr->path = path; + message.d_ptr->interface = interface; + message.d_ptr->name = method; + + return message; +} + +/*! + Constructs a new DBus message representing the return values from a called method. The \a other + variable represents the method call that the reply will be for. + + This function returns a QDBusMessage object that can be sent with QDBusConnection::send(). +*/ +QDBusMessage QDBusMessage::methodReply(const QDBusMessage &other) +{ + Q_ASSERT(other.d_ptr->msg); + + QDBusMessage message; + message.d_ptr->connection = other.d_ptr->connection; + message.d_ptr->type = DBUS_MESSAGE_TYPE_METHOD_RETURN; + message.d_ptr->reply = dbus_message_ref(other.d_ptr->msg); + other.d_ptr->repliedTo = true; + + return message; +} + +/*! + Constructs a DBus message representing an error condition described by the \a name + parameter. The \a msg parameter is optional and may contain a human-readable description of the + error. The \a other variable represents the method call that this error relates to. + + This function returns 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_ptr->msg); + + QDBusMessage message; + message.d_ptr->connection = other.d_ptr->connection; + message.d_ptr->type = DBUS_MESSAGE_TYPE_ERROR; + message.d_ptr->name = name; + message.d_ptr->message = msg; + message.d_ptr->reply = dbus_message_ref(other.d_ptr->msg); + other.d_ptr->repliedTo = true; + + return message; +} + +/*! + \overload + Constructs a DBus message representing an error, where \a other is the method call that + generated this error and \a error is the error code. +*/ +QDBusMessage QDBusMessage::error(const QDBusMessage &other, const QDBusError &error) +{ + Q_ASSERT(other.d_ptr->msg); + + QDBusMessage message; + message.d_ptr->connection = other.d_ptr->connection; + message.d_ptr->type = DBUS_MESSAGE_TYPE_ERROR; + message.d_ptr->name = error.name(); + message.d_ptr->message = error.message(); + message.d_ptr->reply = dbus_message_ref(other.d_ptr->msg); + other.d_ptr->repliedTo = true; + + return message; +} + +/*! + Constructs an empty, invalid QDBusMessage object. + + \sa methodCall(), methodReply(), signal(), error() +*/ +QDBusMessage::QDBusMessage() +{ + d_ptr = new QDBusMessagePrivate; +} + +/*! + Constructs a copy of the object given by \a other. +*/ +QDBusMessage::QDBusMessage(const QDBusMessage &other) + : QList<QVariant>(other) +{ + d_ptr = other.d_ptr; + d_ptr->ref.ref(); +} + +/*! + Disposes of the object and frees any resources that were being held. +*/ +QDBusMessage::~QDBusMessage() +{ + if (!d_ptr->ref.deref()) + delete d_ptr; +} + +/*! + Copies the contents of the object given by \a other. +*/ +QDBusMessage &QDBusMessage::operator=(const QDBusMessage &other) +{ + QList<QVariant>::operator=(other); + qAtomicAssign(d_ptr, other.d_ptr); + return *this; +} + +static inline const char *data(const QByteArray &arr) +{ + return arr.isEmpty() ? 0 : arr.constData(); +} + +/*! + \internal + Constructs a DBusMessage object from this object. The returned value must be de-referenced + with dbus_message_unref. +*/ +DBusMessage *QDBusMessage::toDBusMessage() const +{ + DBusMessage *msg = 0; + + switch (d_ptr->type) { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + msg = dbus_message_new_method_call(data(d_ptr->service.toUtf8()), data(d_ptr->path.toUtf8()), + data(d_ptr->interface.toUtf8()), data(d_ptr->name.toUtf8())); + break; + case DBUS_MESSAGE_TYPE_SIGNAL: + msg = dbus_message_new_signal(data(d_ptr->path.toUtf8()), data(d_ptr->interface.toUtf8()), + data(d_ptr->name.toUtf8())); + break; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + msg = dbus_message_new_method_return(d_ptr->reply); + break; + case DBUS_MESSAGE_TYPE_ERROR: + msg = dbus_message_new_error(d_ptr->reply, data(d_ptr->name.toUtf8()), data(d_ptr->message.toUtf8())); + break; + } + if (!msg) + return 0; + + QDBusMarshall::listToMessage(*this, msg, d_ptr->signature); + return msg; +} + +/*! + \internal + Constructs a QDBusMessage by parsing the given DBusMessage object. +*/ +QDBusMessage QDBusMessage::fromDBusMessage(DBusMessage *dmsg, const QDBusConnection &connection) +{ + QDBusMessage message; + if (!dmsg) + return message; + + message.d_ptr->connection = connection; + message.d_ptr->type = dbus_message_get_type(dmsg); + message.d_ptr->path = QString::fromUtf8(dbus_message_get_path(dmsg)); + message.d_ptr->interface = QString::fromUtf8(dbus_message_get_interface(dmsg)); + message.d_ptr->name = message.d_ptr->type == DBUS_MESSAGE_TYPE_ERROR ? + QString::fromUtf8(dbus_message_get_error_name(dmsg)) : + QString::fromUtf8(dbus_message_get_member(dmsg)); + message.d_ptr->service = QString::fromUtf8(dbus_message_get_sender(dmsg)); + message.d_ptr->signature = QString::fromUtf8(dbus_message_get_signature(dmsg)); + message.d_ptr->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_ptr->type = DBUS_MESSAGE_TYPE_ERROR; + message.d_ptr->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_ptr->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_ptr->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_ptr->name; +} + +/*! + \fn QDBusMessage::member() const + Returns the name of the method being called. +*/ + +/*! + \fn QDBusMessage::method() const + \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_ptr->service; +} + +/*! + \fn QDBusMessage::sender() const + 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_ptr->timeout; +} + +/*! + Sets the timeout for this message to be processed, given by \a ms, in milliseconds. +*/ +void QDBusMessage::setTimeout(int ms) +{ + qAtomicDetach(d_ptr); + d_ptr->timeout = ms; +} + +/*! + Returns the flag that indicates if this message should see a reply or not. This is only + meaningful for MethodCall messages: any other kind of message cannot have replies and this + function will always return false for them. +*/ +bool QDBusMessage::noReply() const +{ + if (!d_ptr->msg) + return false; + return dbus_message_get_no_reply(d_ptr->msg); +} + +/*! + Returns the unique serial number assigned to this message + or 0 if the message was not sent yet. + */ +int QDBusMessage::serialNumber() const +{ + if (!d_ptr->msg) + return 0; + return dbus_message_get_serial(d_ptr->msg); +} + +/*! + Returns the unique serial number assigned to the message + that triggered this reply message. + + If this message is not a reply to another message, 0 + is returned. + + */ +int QDBusMessage::replySerialNumber() const +{ + if (!d_ptr->msg) + return 0; + return dbus_message_get_reply_serial(d_ptr->msg); +} + +/*! + Returns true if this is a MethodCall message and a reply for it has been generated using + QDBusMessage::methodReply or QDBusMessage::error. +*/ +bool QDBusMessage::wasRepliedTo() const +{ + return d_ptr->repliedTo; +} + +/*! + Returns the signature of the signal that was received or for the output arguments + of a method call. +*/ +QString QDBusMessage::signature() const +{ + return d_ptr->signature; +} + +/*! + Sets the signature for the output arguments of this method call to be the value of \a + signature. This function has no meaning in other types of messages or when dealing with received + method calls. + + A message's signature indicate the type of the parameters to + be marshalled over the bus. If there are more arguments than entries 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). + +*/ +void QDBusMessage::setSignature(const QString &signature) +{ + qAtomicDetach(d_ptr); + d_ptr->signature = signature; +} + +/*! + Returns the connection this message was received on or an unconnected QDBusConnection object if + this isn't a message that has been received. +*/ +QDBusConnection QDBusMessage::connection() const +{ + return d_ptr->connection; +} + +/*! + Returns the message type. +*/ +QDBusMessage::MessageType QDBusMessage::type() const +{ + switch (d_ptr->type) { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + return MethodCallMessage; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + return ReplyMessage; + case DBUS_MESSAGE_TYPE_ERROR: + return ErrorMessage; + case DBUS_MESSAGE_TYPE_SIGNAL: + return SignalMessage; + default: + return InvalidMessage; + } +} + +// Document QDBusReply here +/*! + \class QDBusReply + \brief The reply for a method call to a remote object. + + A QDBusReply object is a subset of the QDBusMessage object that represents a method call's + reply. It contains only the first output argument or the error code and is used by + QDBusInterface-derived classes to allow returning the error code as the function's return + argument. + + It can be used in the following manner: + \code + QDBusReply<QString> reply = interface->call("RemoteMethod"); + if (reply.isSuccess()) + // use the returned value + useValue(reply.value()); + else + // call failed. Show an error condition. + showError(reply.error()); + \endcode + + If the remote method call cannot fail, you can skip the error checking: + \code + QString reply = interface->call("RemoteMethod"); + \endcode + + However, if it does fail under those conditions, the value returned by QDBusReply::value() is + undefined. It may be undistinguishable from a valid return value. + + QDBusReply objects are used for remote calls that have no output arguments or return values + (i.e., they have a "void" return type). In this case, you can only test if the reply succeeded + or not, by calling isError() and isSuccess(), and inspecting the error condition by calling + error(). You cannot call value(). + + \sa QDBusMessage, QDBusInterface +*/ + +/*! + \fn QDBusReply::QDBusReply(const QDBusMessage &reply) + Automatically construct a QDBusReply object from the reply message \a reply, extracting the + first return value from it if it is a success reply. +*/ + +/*! + \fn QDBusReply::QDBusReply(const QDBusError &error) + Constructs an error reply from the D-Bus error code given by \a error. +*/ + +/*! + \fn QDBusReply::isError() const + Returns true if this reply is an error reply. You can extract the error contents using the + error() function. +*/ + +/*! + \fn QDBusReply::isSuccess() const + Returns true if this reply is a normal error reply (not an error). You can extract the returned + value with value() +*/ + +/*! + \fn QDBusReply::error() + Returns the error code that was returned from the remote function call. If the remote call did + not return an error (i.e., if it succeeded), then the QDBusError object that is returned will + not be a valid error code (QDBusError::isValid() will return false). +*/ + +/*! + \fn QDBusReply::value() + Returns the remote function's calls return value. If the remote call returned with an error, + the return value of this function is undefined and may be undistinguishable from a valid return + value. + + This function is not available if the remote call returns \c void. +*/ + +/*! + \fn QDBusReply::operator Type() + Returns the same as value(). + + This function is not available if the remote call returns \c void. +*/ + +/*! + \internal + \fn QDBusReply::fromVariant(const QDBusReply<QVariant> &variantReply) + Converts the QDBusReply<QVariant> object to this type by converting the variant contained in + \a variantReply to the template's type and copying the error condition. + + If the QVariant in variantReply is not convertible to this type, it will assume an undefined + value. +*/ + +#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"; + } +} + +static void debugVariantList(QDebug dbg, const QVariantList &list); +static void debugVariantMap(QDebug dbg, const QVariantMap &map); + +static void debugVariant(QDebug dbg, const QVariant &v) +{ + dbg.nospace() << v.typeName() << "("; + switch (v.userType()) + { + case QVariant::Bool: + dbg.nospace() << v.toBool(); + break; + case QMetaType::UChar: + dbg.nospace() << qvariant_cast<uchar>(v); + break; + case QMetaType::Short: + dbg.nospace() << qvariant_cast<short>(v); + break; + case QMetaType::UShort: + dbg.nospace() << qvariant_cast<ushort>(v); + break; + case QVariant::Int: + dbg.nospace() << v.toInt(); + break; + case QVariant::UInt: + dbg.nospace() << v.toUInt(); + break; + case QVariant::LongLong: + dbg.nospace() << v.toLongLong(); + break; + case QVariant::ULongLong: + dbg.nospace() << v.toULongLong(); + break; + case QVariant::Double: + dbg.nospace() << v.toDouble(); + break; + case QVariant::String: + dbg.nospace() << v.toString(); + break; + case QVariant::ByteArray: + dbg.nospace() << v.toByteArray(); + break; + case QVariant::StringList: + dbg.nospace() << v.toStringList(); + break; + case QVariant::List: + debugVariantList(dbg, v.toList()); + break; + case QVariant::Map: + debugVariantMap(dbg, v.toMap()); + break; + + default: { + int id = v.userType(); + if (id == QDBusTypeHelper<QVariant>::id()) + debugVariant(dbg, QDBusTypeHelper<QVariant>::fromVariant(v)); + else if (id == QDBusTypeHelper<bool>::listId()) + dbg.nospace() << QDBusTypeHelper<QList<bool> >::fromVariant(v); + else if (id == QDBusTypeHelper<short>::listId()) + dbg.nospace() << QDBusTypeHelper<QList<short> >::fromVariant(v); + else if (id == QDBusTypeHelper<ushort>::listId()) + dbg.nospace() << QDBusTypeHelper<QList<ushort> >::fromVariant(v); + else if (id == QDBusTypeHelper<int>::listId()) + dbg.nospace() << QDBusTypeHelper<QList<int> >::fromVariant(v); + else if (id == QDBusTypeHelper<uint>::listId()) + dbg.nospace() << QDBusTypeHelper<QList<uint> >::fromVariant(v); + else if (id == QDBusTypeHelper<qlonglong>::listId()) + dbg.nospace() << QDBusTypeHelper<QList<qlonglong> >::fromVariant(v); + else if (id == QDBusTypeHelper<qulonglong>::listId()) + dbg.nospace() << QDBusTypeHelper<QList<qulonglong> >::fromVariant(v); + else if (id == QDBusTypeHelper<double>::listId()) + dbg.nospace() << QDBusTypeHelper<QList<double> >::fromVariant(v); + else + dbg.nospace() << "unknown"; + } + } + dbg.nospace() << ")"; +} + +static void debugVariantList(QDebug dbg, const QVariantList &list) +{ + bool first = true; + QVariantList::ConstIterator it = list.constBegin(); + QVariantList::ConstIterator end = list.constEnd(); + for ( ; it != end; ++it) { + if (!first) + dbg.nospace() << ", "; + debugVariant(dbg, *it); + first = false; + } +} + +static void debugVariantMap(QDebug dbg, const QVariantMap &map) +{ + QVariantMap::ConstIterator it = map.constBegin(); + QVariantMap::ConstIterator end = map.constEnd(); + for ( ; it != end; ++it) { + dbg << "(" << it.key() << ", "; + debugVariant(dbg, it.value()); + dbg << ") "; + } +} + +QDebug operator<<(QDebug dbg, const QDBusMessage &msg) +{ + dbg.nospace() << "QDBusMessage(type=" << msg.type() + << ", service=" << msg.service() + << ", path=" << msg.path() + << ", interface=" << msg.interface() + << ", name=" << msg.name() + << ", signature=" << msg.signature() + << ", contents=("; + debugVariantList(dbg, msg); + dbg.nospace() << " ) )"; + return dbg.space(); +} +#endif + diff --git a/qt/src/qdbusmessage.h b/qt/src/qdbusmessage.h new file mode 100644 index 0000000..f8feeae --- /dev/null +++ b/qt/src/qdbusmessage.h @@ -0,0 +1,99 @@ +/* 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 + * + * 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 QDBUSMESSAGE_H +#define QDBUSMESSAGE_H + +#include "qdbusmacros.h" +#include "qdbuserror.h" +#include <QtCore/qlist.h> +#include <QtCore/qvariant.h> + +#include <limits.h> + +class QDBusMessagePrivate; +class QDBusConnection; +class QDBusConnectionPrivate; +struct DBusMessage; + +class QDBUS_EXPORT QDBusMessage: public QList<QVariant> +{ + //friend class QDBusConnection; + friend class QDBusConnectionPrivate; +public: + enum { DefaultTimeout = -1, NoTimeout = INT_MAX}; + enum MessageType { InvalidMessage, MethodCallMessage, ReplyMessage, + ErrorMessage, SignalMessage }; + + QDBusMessage(); + QDBusMessage(const QDBusMessage &other); + ~QDBusMessage(); + + QDBusMessage &operator=(const QDBusMessage &other); + + static QDBusMessage signal(const QString &path, const QString &interface, + const QString &name); + static QDBusMessage methodCall(const QString &destination, const QString &path, + const QString &interface, const QString &method); + 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; + 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); + + bool noReply() const; + + QString signature() const; + void setSignature(const QString &signature); + + QDBusConnection connection() const; + + int serialNumber() const; + int replySerialNumber() const; + bool wasRepliedTo() const; + +private: + DBusMessage *toDBusMessage() const; + static QDBusMessage fromDBusMessage(DBusMessage *dmsg, const QDBusConnection &connection); + static QDBusMessage fromError(const QDBusError& error); + QDBusMessagePrivate *d_ptr; +}; + +#ifndef QT_NO_DEBUG +QDebug operator<<(QDebug, const QDBusMessage &); +#endif + +#endif + diff --git a/qt/src/qdbusmessage_p.h b/qt/src/qdbusmessage_p.h new file mode 100644 index 0000000..ea958b2 --- /dev/null +++ b/qt/src/qdbusmessage_p.h @@ -0,0 +1,50 @@ +/* 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 + * + * 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 QDBUSMESSAGE_P_H +#define QDBUSMESSAGE_P_H + +#include <qatomic.h> +#include <qstring.h> +#include "qdbusconnection.h" +struct DBusMessage; + +class QDBusMessagePrivate +{ +public: + QDBusMessagePrivate(); + ~QDBusMessagePrivate(); + + QString service, path, interface, name, message, signature; + QDBusConnection connection; + DBusMessage *msg; + DBusMessage *reply; + int type; + int timeout; + QAtomic ref; + + mutable bool repliedTo : 1; +}; + +#endif diff --git a/qt/src/qdbusmetaobject.cpp b/qt/src/qdbusmetaobject.cpp new file mode 100644 index 0000000..a923d79 --- /dev/null +++ b/qt/src/qdbusmetaobject.cpp @@ -0,0 +1,689 @@ +/* -*- 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 "qdbusmetaobject_p.h" + +#include <QtCore/qbytearray.h> +#include <QtCore/qhash.h> +#include <QtCore/qstring.h> +#include <QtCore/qvarlengtharray.h> + +#include "qdbusutil.h" +#include "qdbuserror.h" +#include "qdbusintrospection_p.h" +#include "qdbusabstractinterface_p.h" + +class QDBusMetaObjectGenerator +{ +public: + QDBusMetaObjectGenerator(const QString &interface, + const QDBusIntrospection::Interface *parsedData); + void write(QDBusMetaObject *obj); + void writeWithoutXml(QDBusMetaObject *obj); + +private: + struct Method { + QByteArray parameters; + QByteArray typeName; + QByteArray tag; + QByteArray inputSignature; + QByteArray outputSignature; + QVarLengthArray<int, 4> inputTypes; + QVarLengthArray<int, 4> outputTypes; + int flags; + }; + + struct Property { + QByteArray typeName; + QByteArray signature; + int type; + int flags; + }; + + enum PropertyFlags { + Invalid = 0x00000000, + Readable = 0x00000001, + Writable = 0x00000002, + Resettable = 0x00000004, + EnumOrFlag = 0x00000008, + StdCppSet = 0x00000100, + // Override = 0x00000200, + Designable = 0x00001000, + ResolveDesignable = 0x00002000, + Scriptable = 0x00004000, + ResolveScriptable = 0x00008000, + Stored = 0x00010000, + ResolveStored = 0x00020000, + Editable = 0x00040000, + ResolveEditable = 0x00080000, + User = 0x00100000, + ResolveUser = 0x00200000 + }; + + enum MethodFlags { + AccessPrivate = 0x00, + AccessProtected = 0x01, + AccessPublic = 0x02, + AccessMask = 0x03, //mask + + MethodMethod = 0x00, + MethodSignal = 0x04, + MethodSlot = 0x08, + MethodTypeMask = 0x0c, + + MethodCompatibility = 0x10, + MethodCloned = 0x20, + MethodScriptable = 0x40 + }; + + QMap<QByteArray, Method> methods; + QMap<QByteArray, Property> properties; + + const QDBusIntrospection::Interface *data; + QString interface; + + void parseMethods(); + void parseSignals(); + void parseProperties(); +}; + +static const int intsPerProperty = 2; +static const int intsPerMethod = 4; + +// ### from kernel/qmetaobject.cpp (Qt 4.1.2): +struct QDBusMetaObjectPrivate +{ + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + int propertyCount, propertyData; + int enumeratorCount, enumeratorData; + + // this is specific for QDBusMetaObject: + int propertyDBusData; + int methodDBusData; +}; + +QDBusMetaObjectGenerator::QDBusMetaObjectGenerator(const QString &interfaceName, + const QDBusIntrospection::Interface *parsedData) + : data(parsedData), interface(interfaceName) +{ + if (data) { + parseProperties(); + parseSignals(); // call parseSignals first so that slots override signals + parseMethods(); + } +} + +void QDBusMetaObjectGenerator::parseMethods() +{ + foreach (const QDBusIntrospection::Method &m, data->methods) { + Method mm; + + QByteArray prototype = m.name.toLatin1(); + prototype += '('; + + bool ok = true; + + // build the input argument list + foreach (const QDBusIntrospection::Argument &arg, m.inputArgs) { + int typeId = QDBusUtil::signatureToType(arg.type); + if (typeId == QVariant::Invalid) { + ok = false; + break; + } + + mm.inputSignature += arg.type; + mm.inputTypes.append(typeId); + + mm.parameters.append(arg.name.toLatin1()); + mm.parameters.append(','); + + prototype.append( QVariant::typeToName( QVariant::Type(typeId) ) ); + prototype.append(','); + } + if (!ok) continue; + + // build the output argument list: + for (int i = 0; i < m.outputArgs.count(); ++i) { + const QDBusIntrospection::Argument &arg = m.outputArgs.at(i); + + int typeId = QDBusUtil::signatureToType(arg.type); + if (typeId == QVariant::Invalid) { + ok = false; + break; + } + + mm.outputSignature += arg.type; + mm.outputTypes.append(typeId); + + if (i == 0) { + // return value + mm.typeName = QVariant::typeToName( QVariant::Type(typeId) ); + } else { + // non-const ref parameter + mm.parameters.append(arg.name.toLatin1()); + mm.parameters.append(','); + + prototype.append( QVariant::typeToName( QVariant::Type(typeId) ) ); + prototype.append("&,"); + } + } + if (!ok) continue; + + // convert the last commas: + if (!mm.parameters.isEmpty()) { + mm.parameters.truncate(mm.parameters.length() - 1); + prototype[prototype.length() - 1] = ')'; + } else { + prototype.append(')'); + } + + // check the async tag + if (m.annotations.value(QLatin1String(ANNOTATION_NO_WAIT)) == QLatin1String("true")) + mm.tag = "Q_ASYNC"; + + // meta method flags + mm.flags = AccessPublic | MethodSlot | MethodScriptable; + + // add + methods.insert(QMetaObject::normalizedSignature(prototype), mm); + } +} + +void QDBusMetaObjectGenerator::parseSignals() +{ + foreach (const QDBusIntrospection::Signal &s, data->signals_) { + Method mm; + + QByteArray prototype = s.name.toLatin1(); + prototype += '('; + + bool ok = true; + + // build the output argument list + foreach (const QDBusIntrospection::Argument &arg, s.outputArgs) { + int typeId = QDBusUtil::signatureToType(arg.type); + if (typeId == QVariant::Invalid) { + ok = false; + break; + } + + mm.inputSignature += arg.type; + mm.inputTypes.append(typeId); + + mm.parameters.append(arg.name.toLatin1()); + mm.parameters.append(','); + + prototype.append( QVariant::typeToName( QVariant::Type(typeId) ) ); + prototype.append(','); + } + if (!ok) continue; + + // convert the last commas: + if (!mm.parameters.isEmpty()) { + mm.parameters.truncate(mm.parameters.length() - 1); + prototype[prototype.length() - 1] = ')'; + } else { + prototype.append(')'); + } + + // meta method flags + mm.flags = AccessProtected | MethodSignal | MethodScriptable; + + // add + methods.insert(QMetaObject::normalizedSignature(prototype), mm); + } +} + +void QDBusMetaObjectGenerator::parseProperties() +{ + foreach (const QDBusIntrospection::Property &p, data->properties) { + Property mp; + mp.type = QDBusUtil::signatureToType(p.type); + if (mp.type == QVariant::Invalid) + continue; + + QByteArray name = p.name.toLatin1(); + mp.signature = p.type.toLatin1(); + mp.typeName = QVariant::typeToName( QVariant::Type(mp.type) ); + + // build the flags: + mp.flags = StdCppSet | Scriptable | Stored; + if (p.access != QDBusIntrospection::Property::Write) + mp.flags |= Readable; + if (p.access != QDBusIntrospection::Property::Read) + mp.flags |= Writable; + + if (mp.typeName == "QVariant") + mp.flags |= 0xff << 24; + else if (mp.type < 0xff) + // encode the type in the flags + mp.flags |= mp.type << 24; + + // add the property: + properties.insert(name, mp); + } +} + +void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) +{ + // this code here is mostly copied from qaxbase.cpp + // with a few modifications to make it cleaner + + QString className = interface; + className.replace(QLatin1Char('.'), QLatin1String("::")); + if (className.isEmpty()) + className = QLatin1String("QDBusInterface"); + + QVarLengthArray<int> data; + data.resize(sizeof(QDBusMetaObjectPrivate) / sizeof(int)); + + QDBusMetaObjectPrivate *header = reinterpret_cast<QDBusMetaObjectPrivate *>(data.data()); + header->revision = 1; + header->className = 0; + header->classInfoCount = 0; + header->classInfoData = 0; + header->methodCount = methods.count(); + header->methodData = data.size(); + header->propertyCount = properties.count(); + header->propertyData = header->methodData + header->methodCount * 5; + header->enumeratorCount = 0; + header->enumeratorData = 0; + header->propertyDBusData = header->propertyData + header->propertyCount * 3; + header->methodDBusData = header->propertyDBusData + header->propertyCount * intsPerProperty; + + int data_size = data.size() + + (header->methodCount * (5+intsPerMethod)) + + (header->propertyCount * (3+intsPerProperty)); + foreach (const Method &mm, methods) + data_size += 2 + mm.inputTypes.count() + mm.outputTypes.count(); + data.resize(data_size + 1); + + char null('\0'); + QByteArray stringdata = className.toLatin1(); + stringdata += null; + stringdata.reserve(8192); + + int offset = header->methodData; + int signatureOffset = header->methodDBusData; + int typeidOffset = header->methodDBusData + header->methodCount * intsPerMethod; + data[typeidOffset++] = 0; // eod + + // add each method: + for (QMap<QByteArray, Method>::ConstIterator it = methods.constBegin(); + it != methods.constEnd(); ++it) { + // form "prototype\0parameters\0typeName\0tag\0inputSignature\0outputSignature" + const Method &mm = it.value(); + + data[offset++] = stringdata.length(); + stringdata += it.key(); // prototype + stringdata += null; + data[offset++] = stringdata.length(); + stringdata += mm.parameters; + stringdata += null; + data[offset++] = stringdata.length(); + stringdata += mm.typeName; + stringdata += null; + data[offset++] = stringdata.length(); + stringdata += mm.tag; + stringdata += null; + data[offset++] = mm.flags; + + data[signatureOffset++] = stringdata.length(); + stringdata += mm.inputSignature; + stringdata += null; + data[signatureOffset++] = stringdata.length(); + stringdata += mm.outputSignature; + stringdata += null; + + data[signatureOffset++] = typeidOffset; + data[typeidOffset++] = mm.inputTypes.count(); + memcpy(data.data() + typeidOffset, mm.inputTypes.data(), mm.inputTypes.count() * sizeof(int)); + typeidOffset += mm.inputTypes.count(); + + data[signatureOffset++] = typeidOffset; + data[typeidOffset++] = mm.outputTypes.count(); + memcpy(data.data() + typeidOffset, mm.outputTypes.data(), mm.outputTypes.count() * sizeof(int)); + typeidOffset += mm.outputTypes.count(); + } + + Q_ASSERT(offset == header->propertyData); + Q_ASSERT(signatureOffset == header->methodDBusData + header->methodCount * intsPerMethod); + Q_ASSERT(typeidOffset == data.size()); + + // add each property + signatureOffset = header->propertyDBusData; + for (QMap<QByteArray, Property>::ConstIterator it = properties.constBegin(); + it != properties.constEnd(); ++it) { + const Property &mp = it.value(); + + // form is "name\0typeName\0signature\0" + data[offset++] = stringdata.length(); + stringdata += it.key(); // name + stringdata += null; + data[offset++] = stringdata.length(); + stringdata += mp.typeName; + stringdata += null; + data[offset++] = mp.flags; + + data[signatureOffset++] = stringdata.length(); + stringdata += mp.signature; + stringdata += null; + data[signatureOffset++] = mp.type; + } + + Q_ASSERT(offset == header->propertyDBusData); + Q_ASSERT(signatureOffset == header->methodDBusData); + + char *string_data = new char[stringdata.length()]; + memcpy(string_data, stringdata, stringdata.length()); + + uint *uint_data = new uint[data.size()]; + memcpy(uint_data, data.data(), data.size() * sizeof(int)); + + // put the metaobject together + obj->d.data = uint_data; + obj->d.extradata = 0; + obj->d.stringdata = string_data; + obj->d.superdata = &QDBusAbstractInterface::staticMetaObject; +} + +#if 0 +void QDBusMetaObjectGenerator::writeWithoutXml(const QString &interface) +{ + // no XML definition + QString tmp(interface); + tmp.replace(QLatin1Char('.'), QLatin1String("::")); + QByteArray name(tmp.toLatin1()); + + QDBusMetaObjectPrivate *header = new QDBusMetaObjectPrivate; + memset(header, 0, sizeof *header); + header->revision = 1; + // leave the rest with 0 + + char *stringdata = new char[name.length() + 1]; + stringdata[name.length()] = '\0'; + + d.data = reinterpret_cast<uint*>(header); + d.extradata = 0; + d.stringdata = stringdata; + d.superdata = &QDBusAbstractInterface::staticMetaObject; + cached = false; +} +#endif + +///////// +// class QDBusMetaObject + +QDBusMetaObject *QDBusMetaObject::createMetaObject(const QString &interface, const QString &xml, + QHash<QString, QDBusMetaObject *> &cache, + QDBusError &error) +{ + error = QDBusError(); + QDBusIntrospection::Interfaces parsed = QDBusIntrospection::parseInterfaces(xml); + + QDBusMetaObject *we = 0; + QDBusIntrospection::Interfaces::ConstIterator it = parsed.constBegin(); + QDBusIntrospection::Interfaces::ConstIterator end = parsed.constEnd(); + for ( ; it != end; ++it) { + // check if it's in the cache + QDBusMetaObject *obj = cache.value(it.key(), 0); + if (!obj) { + // not in cache; create + obj = new QDBusMetaObject; + QDBusMetaObjectGenerator generator(it.key(), it.value().constData()); + generator.write(obj); + + if ( (obj->cached = !it.key().startsWith( QLatin1String("local.") )) ) + // cache it + cache.insert(it.key(), obj); + + } + + if (it.key() == interface) + // it's us + we = obj; + } + + if (we) + return we; + // still nothing? + + if (parsed.isEmpty()) { + // object didn't return introspection + we = new QDBusMetaObject; + QDBusMetaObjectGenerator generator(interface, 0); + generator.write(we); + we->cached = false; + return we; + } else if (interface.isEmpty()) { + // merge all interfaces + it = parsed.constBegin(); + QDBusIntrospection::Interface merged = *it.value().constData(); + + for (++it; it != end; ++it) { + merged.annotations.unite(it.value()->annotations); + merged.methods.unite(it.value()->methods); + merged.signals_.unite(it.value()->signals_); + merged.properties.unite(it.value()->properties); + } + + merged.name = QLatin1String("local.Merged"); + merged.introspection.clear(); + + we = new QDBusMetaObject; + QDBusMetaObjectGenerator generator(merged.name, &merged); + generator.write(we); + we->cached = false; + return we; + } + + // mark as an error + error = QDBusError(QDBusError::UnknownInterface, + QString( QLatin1String("Interface '%1' was not found") ) + .arg(interface)); + return 0; +} + +QDBusMetaObject::QDBusMetaObject() +{ +} + +inline const QDBusMetaObjectPrivate *priv(const uint* data) +{ + return reinterpret_cast<const QDBusMetaObjectPrivate *>(data); +} + +const char *QDBusMetaObject::dbusNameForMethod(int id) const +{ + //id -= methodOffset(); + if (id >= 0 && id < priv(d.data)->methodCount) { + int handle = priv(d.data)->methodDBusData + id*intsPerMethod; + return d.stringdata + d.data[handle]; + } + return 0; +} + +const char *QDBusMetaObject::inputSignatureForMethod(int id) const +{ + //id -= methodOffset(); + if (id >= 0 && id < priv(d.data)->methodCount) { + int handle = priv(d.data)->methodDBusData + id*intsPerMethod; + return d.stringdata + d.data[handle + 1]; + } + return 0; +} + +const char *QDBusMetaObject::outputSignatureForMethod(int id) const +{ + //id -= methodOffset(); + if (id >= 0 && id < priv(d.data)->methodCount) { + int handle = priv(d.data)->methodDBusData + id*intsPerMethod; + return d.stringdata + d.data[handle + 2]; + } + return 0; +} + +const int *QDBusMetaObject::inputTypesForMethod(int id) const +{ + //id -= methodOffset(); + if (id >= 0 && id < priv(d.data)->methodCount) { + int handle = priv(d.data)->methodDBusData + id*intsPerMethod; + return reinterpret_cast<const int*>(d.data + d.data[handle + 3]); + } + return 0; +} + +const int *QDBusMetaObject::outputTypesForMethod(int id) const +{ + //id -= methodOffset(); + if (id >= 0 && id < priv(d.data)->methodCount) { + int handle = priv(d.data)->methodDBusData + id*intsPerMethod; + return reinterpret_cast<const int*>(d.data + d.data[handle + 4]); + } + return 0; +} + +int QDBusMetaObject::propertyMetaType(int id) const +{ + //id -= propertyOffset(); + if (id >= 0 && id < priv(d.data)->propertyCount) { + int handle = priv(d.data)->propertyDBusData + id*intsPerProperty; + return d.data[handle + 1]; + } + return 0; +} + +template<typename T> +static inline void assign_helper(void *ptr, const QVariant &value) +{ + *reinterpret_cast<T *>(ptr) = qvariant_cast<T>(value); +} + +void QDBusMetaObject::assign(void *ptr, const QVariant &value) +{ + switch (value.userType()) + { + case QVariant::Bool: + assign_helper<bool>(ptr, value); + return; + + case QMetaType::UChar: + assign_helper<uchar>(ptr, value); + return; + + case QMetaType::Short: + assign_helper<short>(ptr, value); + return; + + case QMetaType::UShort: + assign_helper<ushort>(ptr, value); + return; + + case QVariant::Int: + assign_helper<int>(ptr, value); + return; + + case QVariant::UInt: + assign_helper<uint>(ptr, value); + return; + + case QVariant::LongLong: + assign_helper<qlonglong>(ptr, value); + return; + + case QVariant::ULongLong: + assign_helper<qulonglong>(ptr, value); + return; + + case QVariant::Double: + assign_helper<double>(ptr, value); + return; + + case QVariant::String: + assign_helper<QString>(ptr, value); + return; + + case QVariant::ByteArray: + assign_helper<QByteArray>(ptr, value); + return; + + case QVariant::List: + assign_helper<QVariantList>(ptr, value); + return; + + case QVariant::Map: + assign_helper<QVariantMap>(ptr, value); + return; + + default: + ; + } +} + +bool QDBusMetaTypeId::initialized = false; +int QDBusMetaTypeId::variant = 0; +int QDBusMetaTypeId::boollist = 0; +int QDBusMetaTypeId::shortlist = 0; +int QDBusMetaTypeId::ushortlist = 0; +int QDBusMetaTypeId::intlist = 0; +int QDBusMetaTypeId::uintlist = 0; +int QDBusMetaTypeId::longlonglist = 0; +int QDBusMetaTypeId::ulonglonglist = 0; +int QDBusMetaTypeId::doublelist = 0; + +bool QDBusMetaTypeId::innerInitialize() +{ + variant = qRegisterMetaType<QVariant>("QVariant"); + boollist = qRegisterMetaType<QList<bool> >("QList<bool>"); + shortlist = qRegisterMetaType<QList<short> >("QList<short>"); + ushortlist = qRegisterMetaType<QList<ushort> >("QList<ushort>"); + intlist = qRegisterMetaType<QList<int> >("QList<int>"); + uintlist = qRegisterMetaType<QList<uint> >("QList<uint>"); + longlonglist = qRegisterMetaType<QList<qlonglong> >("QList<qlonglong>"); + ulonglonglist = qRegisterMetaType<QList<qulonglong> >("QList<qulonglong>"); + doublelist = qRegisterMetaType<QList<double> >("QList<double>"); + initialized = true; + return true; +} + +int qDBusMetaTypeId(QVariant *) +{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::variant; } +int qDBusMetaTypeId(QList<bool> *) +{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::boollist; } +int qDBusMetaTypeId(QList<short> *) +{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::shortlist; } +int qDBusMetaTypeId(QList<ushort> *) +{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::ushortlist; } +int qDBusMetaTypeId(QList<int> *) +{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::intlist; } +int qDBusMetaTypeId(QList<uint> *) +{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::uintlist; } +int qDBusMetaTypeId(QList<qlonglong> *) +{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::longlonglist; } +int qDBusMetaTypeId(QList<qulonglong> *) +{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::ulonglonglist; } +int qDBusMetaTypeId(QList<double> *) +{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::doublelist; } diff --git a/qt/src/qdbusmetaobject_p.h b/qt/src/qdbusmetaobject_p.h new file mode 100644 index 0000000..746240d --- /dev/null +++ b/qt/src/qdbusmetaobject_p.h @@ -0,0 +1,96 @@ +/* + * + * 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 QDBUSMETAOBJECTPRIVATE_H +#define QDBUSMETAOBJECTPRIVATE_H + +#include <QtCore/qmetaobject.h> +#include "qdbusmacros.h" + +class QDBusError; + +struct QDBusMetaObjectPrivate; +struct QDBUS_EXPORT QDBusMetaObject: public QMetaObject +{ + bool cached; + + static QDBusMetaObject *createMetaObject(const QString &interface, const QString &xml, + QHash<QString, QDBusMetaObject *> &map, + QDBusError &error); + ~QDBusMetaObject() + { + delete [] d.stringdata; + delete [] d.data; + } + + // methods (slots & signals): + const char *dbusNameForMethod(int id) const; + const char *inputSignatureForMethod(int id) const; + const char *outputSignatureForMethod(int id) const; + const int *inputTypesForMethod(int id) const; + const int *outputTypesForMethod(int id) const; + + // properties: + int propertyMetaType(int id) const; + + // helper function: + static void assign(void *, const QVariant &value); + +private: + QDBusMetaObject(); +}; + +struct QDBusMetaTypeId +{ + static bool innerInitialize(); + static bool initialized; + static inline void initialize() + { + if (initialized) return; + innerInitialize(); + } + + static int variant; + static int boollist; + static int shortlist; + static int ushortlist; + static int intlist; + static int uintlist; + static int longlonglist; + static int ulonglonglist; + static int doublelist; +}; + +#endif diff --git a/qt/src/qdbusmisc.cpp b/qt/src/qdbusmisc.cpp new file mode 100644 index 0000000..9aaf9f2 --- /dev/null +++ b/qt/src/qdbusmisc.cpp @@ -0,0 +1,156 @@ +/* qdbusmisc.cpp Miscellaneous routines that didn't fit anywhere else + * + * 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 <string.h> + +#include <QtCore/qvariant.h> +#include <QtCore/qmetaobject.h> + +#include "qdbusconnection_p.h" +#include "qdbustypehelper_p.h" + +bool qDBusCheckAsyncTag(const char *tag) +{ + if (!tag || !*tag) + return false; + + const char *p = strstr(tag, "async"); + if (p != NULL && + (p == tag || *(p-1) == ' ') && + (p[5] == '\0' || p[5] == ' ')) + return true; + + p = strstr(tag, "Q_ASYNC"); + if (p != NULL && + (p == tag || *(p-1) == ' ') && + (p[7] == '\0' || p[7] == ' ')) + return true; + + return false; +} + +int qDBusNameToTypeId(const char *name) +{ + int id = static_cast<int>( QVariant::nameToType(name) ); + if (id == QVariant::UserType) + id = QMetaType::type(name); + + 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 == QDBusConnectionPrivate::registerMessageMetaType() || + id == QDBusTypeHelper<QVariant>::id() || + id == QDBusTypeHelper<bool>::listId() || + id == QDBusTypeHelper<short>::listId() || + id == QDBusTypeHelper<ushort>::listId() || + id == QDBusTypeHelper<int>::listId() || + id == QDBusTypeHelper<qlonglong>::listId() || + id == QDBusTypeHelper<qulonglong>::listId() || + id == QDBusTypeHelper<double>::listId()) + 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 +int qDBusParametersForMethod(const QMetaMethod &mm, QList<int>& metaTypes) +{ + QList<QByteArray> parameterTypes = mm.parameterTypes(); + 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'", mm.signature()); + // pointer? + return -1; + } + + if (type.endsWith('&')) { + type.truncate(type.length() - 1); + int id = qDBusNameToTypeId(type); + if (id == 0) { + //qWarning("Could not parse the method '%s'", mm.signature()); + // invalid type in method parameter list + return -1; + } + + metaTypes.append( id ); + seenMessage = true; // it cannot appear anymore anyways + continue; + } + + if (seenMessage) { // && !type.endsWith('&') + //qWarning("Could not parse the method '%s'", mm.signature()); + // non-output parameters after message or after output params + return -1; // not allowed + } + + int id = qDBusNameToTypeId(type); + if (id == 0) { + //qWarning("Could not parse the method '%s'", mm.signature()); + // invalid type in method parameter list + return -1; + } + metaTypes.append(id); + ++inputCount; + + if (id == QDBusConnectionPrivate::registerMessageMetaType()) + seenMessage = true; + } + + return inputCount; +} diff --git a/qt/src/qdbusreply.h b/qt/src/qdbusreply.h new file mode 100644 index 0000000..0b5c12d --- /dev/null +++ b/qt/src/qdbusreply.h @@ -0,0 +1,132 @@ +/* qdbusreply.h QDBusReply object - a reply from D-Bus + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef QDBUSREPLY_H +#define QDBUSREPLY_H + +#include <QtCore/qglobal.h> +#include <QtCore/qvariant.h> + +#include "qdbusmacros.h" +#include "qdbusmessage.h" +#include "qdbuserror.h" + +#include "qdbustypehelper_p.h" + +template<typename T> +class QDBusReply +{ + typedef T Type; +public: + inline QDBusReply(const QDBusMessage &reply) + : m_data(Type()) + { + *this = reply; + } + inline QDBusReply& operator=(const QDBusMessage& reply) + { + m_error = reply; + if (isSuccess()) + m_data = QDBusTypeHelper<Type>::fromVariant(reply.at(0)); + else + m_data = Type(); + return *this; + } + + inline QDBusReply(const QDBusError &error = QDBusError()) + : m_error(error), m_data(Type()) + { + } + inline QDBusReply& operator=(const QDBusError& error) + { + m_error = error; + m_data = Type(); + return *this; + } + + inline QDBusReply& operator=(const QDBusReply& other) + { + m_error = other.m_error; + m_data = other.m_data; + return *this; + } + + inline bool isError() const { return m_error.isValid(); } + inline bool isSuccess() const { return !m_error.isValid(); } + + inline const QDBusError& error() { return m_error; } + + inline Type value() + { + return m_data; + } + + inline operator Type () + { + return m_data; + } + + static QDBusReply<T> fromVariant(const QDBusReply<QVariant> &variantReply) + { + QDBusReply<T> retval; + retval.m_error = variantReply.m_error; + if (retval.isSuccess()) { + retval.m_data = qvariant_cast<Type>(variantReply.m_data); + if (!qVariantCanConvert<Type>(variantReply.m_data)) + retval.m_error = QDBusError(QDBusError::InvalidSignature, + QLatin1String("Unexpected reply signature")); + } + return retval; + } + +private: + QDBusError m_error; + Type m_data; +}; + +# ifndef Q_QDOC +// specialize for void: +template<> +class QDBUS_EXPORT QDBusReply<void> +{ +public: + inline QDBusReply(const QDBusMessage &reply) + : m_error(reply) + { + } + inline QDBusReply(const QDBusError &error) + : m_error(error) + { + } + + inline bool isError() const { return m_error.isValid(); } + inline bool isSuccess() const { return !m_error.isValid(); } + + inline const QDBusError& error() { return m_error; } + +private: + QDBusError m_error; +}; +# endif + +#endif diff --git a/qt/src/qdbusserver.cpp b/qt/src/qdbusserver.cpp new file mode 100644 index 0000000..b3b9835 --- /dev/null +++ b/qt/src/qdbusserver.cpp @@ -0,0 +1,61 @@ +/* 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 + * + * 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 "qdbusserver.h" +#include "qdbusconnection_p.h" + +QDBusServer::QDBusServer(const QString &addr, QObject *parent) + : QObject(parent) +{ + d = new QDBusConnectionPrivate(this); + + if (addr.isEmpty()) + return; + + d->setServer(dbus_server_listen(addr.toUtf8().constData(), &d->error)); +} + +bool QDBusServer::isConnected() const +{ + return d->server && dbus_server_get_is_connected(d->server); +} + +QDBusError QDBusServer::lastError() const +{ + return d->lastError; +} + +QString QDBusServer::address() const +{ + QString addr; + if (d->server) { + char *c = dbus_server_get_address(d->server); + addr = QString::fromUtf8(c); + dbus_free(c); + } + + return addr; +} + +#include "qdbusserver.moc" diff --git a/qt/src/qdbusserver.h b/qt/src/qdbusserver.h new file mode 100644 index 0000000..5560786 --- /dev/null +++ b/qt/src/qdbusserver.h @@ -0,0 +1,48 @@ +/* qdbusserver.h QDBusServer 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 QDBUSSERVER_H +#define QDBUSSERVER_H + +#include "qdbusmacros.h" +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> + +class QDBusConnectionPrivate; +class QDBusError; + +class QDBUS_EXPORT QDBusServer: public QObject +{ + Q_OBJECT +public: + QDBusServer(const QString &address, QObject *parent = 0); + + bool isConnected() const; + QDBusError lastError() const; + QString address() const; + +private: + Q_DISABLE_COPY(QDBusServer) + QDBusConnectionPrivate *d; +}; + +#endif diff --git a/qt/src/qdbusthread.cpp b/qt/src/qdbusthread.cpp new file mode 100644 index 0000000..f45a009 --- /dev/null +++ b/qt/src/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/src/qdbustype.cpp b/qt/src/qdbustype.cpp new file mode 100644 index 0000000..7f17a37 --- /dev/null +++ b/qt/src/qdbustype.cpp @@ -0,0 +1,847 @@ +/* -*- 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_p.h" +#include "qdbustypehelper_p.h" +#include <dbus/dbus.h> + +#include <QtCore/qstringlist.h> + +class QDBusTypePrivate: public QSharedData +{ +public: + int code; + mutable int qvariantType; + mutable QByteArray signature; + QDBusTypeList subTypes; + + inline QDBusTypePrivate() + : code(0), qvariantType(QVariant::Invalid) + { } +}; + +/*! + \class QDBusType + \brief Represents one single D-Bus type. + \internal + + D-Bus provides a set of primitive types that map to normal, C++ types and to QString, as well as + the possibility to extend the set with the so-called "container" types. The available types are + as follows: + + - Primitive (or basic): integers of 16, 32 and 64 bits, both signed and unsigned; byte (8 bits); + double-precision floating point and Unicode strings + - Arrays: a homogeneous, ordered list of zero or more entries + - Maps: an unordered list of (key, value) pairs, where key must be a primitive type and value + can be any D-Bus type + - Structs: an ordered list of a fixed number of entries of any type + - Variants: a "wildcard" container that can assume the value of any other type, including + structs and arrays + + Any type can be placed inside an array (including other arrays), but only entries of the same + type can be placed inside the same array. The analogous type for D-Bus arrays are the Qt + #QList template classes. + + Structs have a fixed number of entries and each entry has a fixed type. They are analogous to C + and C++ structs (hence the name). + + Maps or dictionaries are analogous to the Qt #QMap template class, with the additional + restriction that the key type must be a primitive one. D-Bus implements maps by using arrays of + a special type (a "dictionary entry"), so inspecting a QDBusType of a Map will reveal that it is + an array (see isArray()). + + Variants contain exactly one entry, but the type can vary freely. It is analogous to the Qt + class #QVariant, but the QtDBus implementation uses #QDBusVariant to represent D-Bus Variants. +*/ + +/*! + Constructs an empty (invalid) type. +*/ +QDBusType::QDBusType() + : d(0) +{ +} + +/*! + Constructs the type based on the D-Bus type given by \a type. +*/ +QDBusType::QDBusType(int type) +{ + char c[2] = { type, 0 }; + *this = QDBusType(c); +} + +/*! + Constructs the type based on the QVariant type given by \a type. + + \sa QVariant::Type +*/ +QDBusType::QDBusType(QVariant::Type type) +{ + const char *sig = dbusSignature(type); + + // it never returns NULL + // but it may return an empty string: + if (sig[0] == '\0') + return; + + if (qstrlen(sig) > 2) { + *this = QDBusType(sig); + } else { + d = new QDBusTypePrivate; + d->qvariantType = type; + d->code = sig[0]; + if (sig[1] == '\0') + // single-letter type + return; + else { + // 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; + } + } +} + +/*! + Parses the D-Bus signature given by \a signature and constructs the type it represents. +*/ +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; +} + +/*! + \overload + Parses the D-Bus signature given by \a str and constructs the type it represents. +*/ +QDBusType::QDBusType(const QString& str) +{ + *this = QDBusType( str.toUtf8().constData() ); +} + +/*! + \overload + Parses the D-Bus signature given by \a str and constructs the type it represents. +*/ +QDBusType::QDBusType(const QByteArray& str) +{ + *this = QDBusType( str.constData() ); +} + +/*! + \internal + Creates a QDBusType object based on the current element pointed to by \a iter. +*/ +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 object \a other. +*/ +QDBusType::QDBusType(const QDBusType& other) + : d(other.d) +{ +} + +/*! + Release the resources associated with this type. +*/ +QDBusType::~QDBusType() +{ +} + +/*! + Copies the type from the object given by \a other. +*/ +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. +*/ +int QDBusType::qvariantType() const +{ + if (d && d->qvariantType != QVariant::Invalid) + return d->qvariantType; + + if (!d) + return QVariant::Invalid; + + return d->qvariantType = qvariantType(dbusSignature().constData()); +} + +/*! + 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. +*/ +bool QDBusType::isBasic() const +{ + return d && dbus_type_is_basic(d->code); +} + +/*! + Returns true if this type is a 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 this type is the same one as \a other. +*/ +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; +} + +/*! + \fn QDBusType::operator!=(const QDBusType &other) const + Returns true if the this type and the one given by \a other are different. +*/ + +/*! + Converts the DBus type code \a type to QVariant::Type. +*/ +int QDBusType::qvariantType(int type) +{ + char c[2] = { type, 0 }; + return qvariantType(c); +} + +/*! + Converts the DBus type signature \a signature to QVariant::Type. +*/ +int 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_BYTE: + return QMetaType::UChar; + + case DBUS_TYPE_INT16: + return QMetaType::Short; + + case DBUS_TYPE_UINT16: + return QMetaType::UShort; + + case DBUS_TYPE_INT32: + return QVariant::Int; + + 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 QDBusTypeHelper<QVariant>::id(); + + case DBUS_TYPE_ARRAY: // special case + switch (signature[1]) { + case DBUS_TYPE_BOOLEAN: + return QDBusTypeHelper<bool>::listId(); + + case DBUS_TYPE_BYTE: + return QVariant::ByteArray; + + case DBUS_TYPE_INT16: + return QDBusTypeHelper<short>::listId(); + + case DBUS_TYPE_UINT16: + return QDBusTypeHelper<ushort>::listId(); + + case DBUS_TYPE_INT32: + return QDBusTypeHelper<int>::listId(); + + case DBUS_TYPE_UINT32: + return QDBusTypeHelper<uint>::listId(); + + case DBUS_TYPE_INT64: + return QDBusTypeHelper<qlonglong>::listId(); + + case DBUS_TYPE_UINT64: + return QDBusTypeHelper<qulonglong>::listId(); + + case DBUS_TYPE_DOUBLE: + return QDBusTypeHelper<double>::listId(); + + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + return QVariant::StringList; + + case DBUS_TYPE_VARIANT: + return QVariant::List; + + case DBUS_DICT_ENTRY_BEGIN_CHAR: + return QVariant::Map; + + default: + return QVariant::List; + } + default: + return QVariant::Invalid; + + } +} + +/*! + Converts the QVariant::Type \a t to a DBus type code. +*/ +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: + 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: + 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_INVALID; // invalid + + default: + break; // avoid compiler warnings + } + + if (int(t) == QDBusTypeHelper<QVariant>::id()) + return DBUS_TYPE_VARIANT; + + return DBUS_TYPE_INVALID; +} + +/*! + Converts the QVariant::Type \a t to a DBus type signature. +*/ +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: + 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: + 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) == QDBusTypeHelper<QVariant>::id()) + return DBUS_TYPE_VARIANT_AS_STRING; + if (int(t) == QDBusTypeHelper<bool>::listId()) + return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BOOLEAN_AS_STRING; + if (int(t) == QDBusTypeHelper<short>::listId()) + return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_INT16_AS_STRING; + if (int(t) == QDBusTypeHelper<ushort>::listId()) + return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_UINT16_AS_STRING; + if (int(t) == QDBusTypeHelper<int>::listId()) + return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_INT32_AS_STRING; + if (int(t) == QDBusTypeHelper<uint>::listId()) + return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_UINT32_AS_STRING; + if (int(t) == QDBusTypeHelper<qlonglong>::listId()) + return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_INT64_AS_STRING; + if (int(t) == QDBusTypeHelper<qulonglong>::listId()) + return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_UINT64_AS_STRING; + if (int(t) == QDBusTypeHelper<double>::listId()) + return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_DOUBLE_AS_STRING; + + return DBUS_TYPE_INVALID_AS_STRING; + } +} + +/*! + \enum QDBusType::VariantListMode + Defines how the guessFromVariant() function will behave when the QVariant is of type + QVariant::List. +*/ + +/*! + Guesses the DBus type from the given \a variant. +*/ +QDBusType QDBusType::guessFromVariant(const QVariant& variant, VariantListMode mode) +{ + if (variant.type() == QVariant::List) { + // investigate deeper + QDBusType t; + t.d = new QDBusTypePrivate; + const QVariantList list = variant.toList(); + + t.d->code = DBUS_TYPE_ARRAY; + 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; + } + } else { + // an array of "something" + t.d->subTypes << QDBusType('v'); + return t; + } + + // treat it as a struct + t.d->code = DBUS_TYPE_STRUCT; + 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( QVariant::Type( variant.userType() ) ); +} + +/*! + \class QDBusTypeList + \brief A list of DBus types. + \internal + + 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: copies the type list from \a other. +*/ + +/*! + \fn QDBusTypeList::QDBusTypeList(const QList<QDBusType>& other) + + Copy constructor: copies the type list from \a other. +*/ + +/*! + Constructs a type list by parsing the given \a signature. +*/ +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)); +} + +/*! + \internal + Constructs a type list by parsing the elements on this iterator 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/src/qdbustype_p.h b/qt/src/qdbustype_p.h new file mode 100644 index 0000000..b719960 --- /dev/null +++ b/qt/src/qdbustype_p.h @@ -0,0 +1,109 @@ +/* -*- 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: + 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); + + int 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; + inline bool operator!=(const QDBusType &other) const + { return !(*this == other); } + + static int qvariantType(int type); + static int 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; + + QByteArray dbusSignature() const; +}; + +#endif // QDBUSTYPE_H diff --git a/qt/src/qdbustypehelper_p.h b/qt/src/qdbustypehelper_p.h new file mode 100644 index 0000000..7ebd7f3 --- /dev/null +++ b/qt/src/qdbustypehelper_p.h @@ -0,0 +1,231 @@ +/* qdbuslisthelper_p.h Helper class to convert to and from QVariantList + * + * 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. + * + */ + +// +// 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 QDBUSTYPEHELPERPRIVATE_H +#define QDBUSTYPEHELPERPRIVATE_H + +#include <QtCore/qlist.h> +#include <QtCore/qvariant.h> +#include <QtCore/qmetatype.h> + +// we're going to support all D-Bus primitive types here: +// uchar -- not needed: QByteArray +// bool +// short +// ushort +// int +// uint +// qlonglong +// qulonglong +// double +// QString -- not needed: QStringList +// QList -- not possible: will use QVariant +// QVariant +// QDBusStruct -- not yet existant +// QMap -- not possible: will use QVariant + +inline QDBUS_EXPORT int qDBusMetaTypeId(bool *) { return QVariant::Bool; } +inline QDBUS_EXPORT int qDBusMetaTypeId(uchar *) { return QMetaType::UChar; } +inline QDBUS_EXPORT int qDBusMetaTypeId(short *) { return QMetaType::Short; } +inline QDBUS_EXPORT int qDBusMetaTypeId(ushort *) { return QMetaType::UShort; } +inline QDBUS_EXPORT int qDBusMetaTypeId(int *) { return QVariant::Int; } +inline QDBUS_EXPORT int qDBusMetaTypeId(uint *) { return QVariant::UInt; } +inline QDBUS_EXPORT int qDBusMetaTypeId(qlonglong *) { return QVariant::LongLong; } +inline QDBUS_EXPORT int qDBusMetaTypeId(qulonglong *) { return QVariant::ULongLong; } +inline QDBUS_EXPORT int qDBusMetaTypeId(double *) { return QVariant::Double; } +inline QDBUS_EXPORT int qDBusMetaTypeId(QString *) { return QVariant::String; } +QDBUS_EXPORT int qDBusMetaTypeId(QVariant *); +QDBUS_EXPORT int qDBusMetaTypeId(QList<bool> *); +inline QDBUS_EXPORT int qDBusMetaTypeId(QByteArray *) { return QVariant::ByteArray; } +QDBUS_EXPORT int qDBusMetaTypeId(QList<short> *); +QDBUS_EXPORT int qDBusMetaTypeId(QList<ushort> *); +QDBUS_EXPORT int qDBusMetaTypeId(QList<int> *); +QDBUS_EXPORT int qDBusMetaTypeId(QList<uint> *); +QDBUS_EXPORT int qDBusMetaTypeId(QList<qlonglong> *); +QDBUS_EXPORT int qDBusMetaTypeId(QList<qulonglong> *); +QDBUS_EXPORT int qDBusMetaTypeId(QList<double> *); +inline QDBUS_EXPORT int qDBusMetaTypeId(QStringList *) { return QVariant::StringList; } +inline QDBUS_EXPORT int qDBusMetaTypeId(QVariantList *) { return QVariant::List; } +inline QDBUS_EXPORT int qDBusMetaTypeId(QVariantMap *) { return QVariant::Map; } + +// implement the copy mechanism +template<class T> +struct QDBusTypeHelper +{ + typedef T Type; + typedef QList<T> List; + + static inline int id() + { + Type* t = 0; + return qDBusMetaTypeId(t); + } + + static inline int listId() + { + List *l = 0; + return qDBusMetaTypeId(l); + } + + static inline QVariant toVariant(const Type &t) + { + return QVariant(id(), &t); + } + + static bool canSpecialConvert(const QVariant &); + static Type specialConvert(const QVariant &); + + static inline Type fromVariant(const QVariant &v) + { + if (canSpecialConvert(v)) + return specialConvert(v); + + QVariant copy(v); + if (copy.convert( QVariant::Type(id()) )) + return *reinterpret_cast<const Type *>(copy.constData()); + return Type(); + } + + static inline QVariantList toVariantList(const List list) + { + QVariantList tmp; + Q_FOREACH (const Type &t, list) + tmp.append(toVariant(t)); + return tmp; + } + + static inline QVariantList toVariantList(const QVariant &v) + { + return toVariantList(QDBusTypeHelper<List>::fromVariant(v)); + } + + static inline List fromVariantList(const QVariantList list) + { + List tmp; + Q_FOREACH (const QVariant &v, list) + tmp.append(fromVariant(v)); + return tmp; + } +}; + +template<> +struct QDBusTypeHelper<QVariant> +{ + static inline int id() + { + QVariant *t = 0; + return qDBusMetaTypeId(t); + } + + static inline int listId() + { + return QVariant::List; + } + + static inline QVariant toVariant(const QVariant &t) + { + return QVariant(id(), &t); + } + + static inline QVariant fromVariant(const QVariant &v) + { + if (v.userType() == id()) + return *reinterpret_cast<const QVariant *>(v.constData()); + return v; + } + + static inline QVariantList toVariantList(const QVariantList &list) + { + return list; + } + + static inline QVariantList fromVariantList(const QVariantList &list) + { + return list; + } +}; + +#if !defined(QT_NO_CAST_FROM_ASCII) && !defined(QT_NO_CAST_TO_ASCII) +template<> +struct QDBusTypeHelper<char *> +{ + static inline int id() + { return QVariant::String; } + + static inline QVariant toVariant(const char *t) + { return QVariant(t); } + + static inline QByteArray fromVariant(const QVariant &v) + { return v.toString().toAscii(); } +}; + +template<> +struct QDBusTypeHelper<const char *> +{ + static inline int id() + { return QVariant::String; } + + static inline QVariant toVariant(const char *t) + { return QVariant(t); } + + static inline QByteArray fromVariant(const QVariant &v) + { return v.toString().toAscii(); } +}; +#endif + +// support three exceptions: uchar, short and ushort +// we have to do this as long as QVariant can't convert to/from the integer metatypes +template<> inline bool QDBusTypeHelper<short>::canSpecialConvert(const QVariant &v) +{ return v.userType() < int(QVariant::UserType); } +template<> inline short QDBusTypeHelper<short>::specialConvert(const QVariant &v) +{ return v.toInt(); } + +template<> inline bool QDBusTypeHelper<ushort>::canSpecialConvert(const QVariant &v) +{ return v.userType() < int(QVariant::UserType); } +template<> inline ushort QDBusTypeHelper<ushort>::specialConvert(const QVariant &v) +{ return v.toUInt(); } + +template<> inline bool QDBusTypeHelper<uchar>::canSpecialConvert(const QVariant &v) +{ return v.userType() < int(QVariant::UserType); } +template<> inline uchar QDBusTypeHelper<uchar>::specialConvert(const QVariant &v) +{ return v.toUInt(); } + +template<typename T> inline bool QDBusTypeHelper<T>::canSpecialConvert(const QVariant &) +{ return false; } +template<typename T> inline T QDBusTypeHelper<T>::specialConvert(const QVariant &) +{ return T(); } + +#endif diff --git a/qt/src/qdbusutil.cpp b/qt/src/qdbusutil.cpp new file mode 100644 index 0000000..872434c --- /dev/null +++ b/qt/src/qdbusutil.cpp @@ -0,0 +1,235 @@ +/* -*- 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> + +#include "qdbustype_p.h" + +/*! + \namespace QDBusUtil + The QDBusUtil namespace contains a few functions that are of general use when dealing with D-Bus + strings. +*/ +namespace QDBusUtil +{ + /*! + \fn QDBusUtil::isValidInterfaceName(const QString &ifaceName) + Returns true if this is \a ifaceName is a valid interface name. + + Valid interface names must: + \list + \o not be empty + \o not exceed 255 characters in length + \o be composed of dot-separated string components that contain only ASCII letters, digits + and the underscore ("_") character + \o contain at least two such components + \endlist + */ + bool isValidInterfaceName(const QString& ifaceName) + { + if (ifaceName.isEmpty() || ifaceName.length() > DBUS_MAXIMUM_NAME_LENGTH) + return false; + + QStringList parts = ifaceName.split(QLatin1Char('.')); + if (parts.count() < 2) + return false; // at least two parts + + foreach (QString part, parts) + if (!isValidMemberName(part)) + return false; + + return true; + } + + /*! + \fn QDBusUtil::isValidUniqueConnectionName(const QString &connName) + Returns true if \a connName is a valid unique connection name. + + Unique connection names start with a colon (":") and are followed by a list of dot-separated + components composed of ASCII letters, digits, the hypen or the underscore ("_") character. + */ + bool isValidUniqueConnectionName(const QString &connName) + { + if (connName.isEmpty() || connName.length() > DBUS_MAXIMUM_NAME_LENGTH || + !connName.startsWith(QLatin1Char(':'))) + return false; + + QStringList parts = connName.mid(1).split(QLatin1Char('.')); + if (parts.count() < 1) + return false; + + QRegExp regex(QLatin1String("[a-zA-Z0-9_-]+")); + foreach (QString part, parts) + if (!regex.exactMatch(part)) + return false; + + return true; + } + + /*! + \fn QDBusUtil::isValidBusName(const QString &busName) + Returns true if \a busName is a valid bus name. + + A valid bus name is either a valid unique connection name or follows the rules: + \list + \o is not empty + \o does not exceed 255 characters in length + \o be composed of dot-separated string components that contain only ASCII letters, digits, + hyphens or underscores ("_"), but don't start with a digit + \o contains at least two such elements + \endlist + + \sa isValidUniqueConnectionName() + */ + bool isValidBusName(const QString &busName) + { + if (busName.isEmpty() || busName.length() > DBUS_MAXIMUM_NAME_LENGTH) + return false; + + if (busName.startsWith(QLatin1Char(':'))) + return isValidUniqueConnectionName(busName); + + QStringList parts = busName.split(QLatin1Char('.')); + if (parts.count() < 1) + return false; + + QRegExp regex(QLatin1String("[a-zA-Z_-][a-zA-Z0-9_-]*")); + foreach (QString part, parts) + if (!regex.exactMatch(part)) + return false; + + return true; + } + + /*! + \fn QDBusUtil::isValidMemberName(const QString &memberName) + Returns true if \a memberName is a valid member name. A valid member name does not exceed + 255 characters in length, is not empty, is composed only of ASCII letters, digits and + underscores, but does not start with a digit. + */ + bool isValidMemberName(const QString &memberName) + { + if (memberName.isEmpty() || memberName.length() > DBUS_MAXIMUM_NAME_LENGTH) + return false; + + QRegExp regex(QLatin1String("[a-zA-Z_][a-zA-Z0-9_]+")); + return regex.exactMatch(memberName); + } + + /*! + \fn QDBusUtil::isValidErrorName(const QString &errorName) + Returns true if \a errorName is a valid error name. Valid error names are valid interface + names and vice-versa, so this function is actually an alias for isValidInterfaceName. + */ + bool isValidErrorName(const QString &errorName) + { + return isValidInterfaceName(errorName); + } + + /*! + \fn QDBusUtil::isValidObjectPath(const QString &path) + Returns true if \a path is valid object path. + + Valid object paths follow the rules: + \list + \o start with the slash character ("/") + \o do not end in a slash, unless the path is just the initial slash + \o do not contain any two slashes in sequence + \o contain slash-separated parts, each of which is composed of ASCII letters, digits and + underscores ("_") + \endlist + */ + bool isValidObjectPath(const QString &path) + { + if (path == QLatin1String("/")) + return true; + + if (!path.startsWith(QLatin1Char('/')) || path.indexOf(QLatin1String("//")) != -1 || + path.endsWith(QLatin1Char('/'))) + return false; + + QStringList parts = path.split(QLatin1Char('/')); + Q_ASSERT(parts.count() >= 1); + parts.removeFirst(); // it starts with /, so we get an empty first part + + QRegExp regex(QLatin1String("[a-zA-Z0-9_]+")); + foreach (QString part, parts) + if (!regex.exactMatch(part)) + return false; + + return true; + } + + /*! + \fn QDBusUtil::isValidSignature(const QString &signature) + Returns true if \a signature is a valid D-Bus type signature for one or more types. + This function returns true if it can all of \a signature into valid, individual types and no + characters remain in \a signature. + + \sa isValidSingleSignature() + */ + bool isValidSignature(const QString &signature) + { + return dbus_signature_validate(signature.toUtf8(), 0); + } + + /*! + \fn QDBusUtil::isValidSingleSignature(const QString &signature) + Returns true if \a signature is a valid D-Bus type signature for exactly one full type. This + function tries to convert the type signature into a D-Bus type and, if it succeeds and no + characters remain in the signature, it returns true. + */ + bool isValidSingleSignature(const QString &signature) + { + return dbus_signature_validate_single(signature.toUtf8(), 0); + } + + /*! + \fn QDBusUtil::signatureToType(const QString &signature) + Returns the Qt meta type id for the given D-Bus signature for exactly one full type, given + by \a signature. + + \sa isValidSingleSignature(), typeToSignature(), QVariant::type(), QVariant::userType() + */ + QVariant::Type signatureToType(const QString &signature) + { + return QVariant::Type( QDBusType::qvariantType(signature.toLatin1().constData()) ); + } + + /*! + \fn QDBusUtil::typeToSignature(QVariant::Type type) + Returns the D-Bus signature equivalent to the supplied meta type id \a type. + + \sa isValidSingleSignature(), signatureToType(), QVariant::type(), QVariant::userType() + */ + const char *typeToSignature(QVariant::Type type) + { + return QDBusType::dbusSignature( type ); + } + +} // namespace QDBusUtil diff --git a/qt/src/qdbusutil.h b/qt/src/qdbusutil.h new file mode 100644 index 0000000..dd2b4df --- /dev/null +++ b/qt/src/qdbusutil.h @@ -0,0 +1,55 @@ +/* -*- 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 <QtCore/qvariant.h> + +#include "qdbusmacros.h" + +namespace QDBusUtil +{ + QDBUS_EXPORT 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 QVariant::Type signatureToType(const QString &signature); + + QDBUS_EXPORT const char *typeToSignature(QVariant::Type type); +} + +#endif diff --git a/qt/src/qdbusxmlgenerator.cpp b/qt/src/qdbusxmlgenerator.cpp new file mode 100644 index 0000000..cbd5388 --- /dev/null +++ b/qt/src/qdbusxmlgenerator.cpp @@ -0,0 +1,194 @@ +/* -*- 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 <QtCore/qcoreapplication.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qstringlist.h> + +#include "qdbusinterface_p.h" // for ANNOTATION_NO_WAIT +#include "qdbusabstractadaptor_p.h" // for QCLASSINFO_DBUS_* +#include "qdbusconnection_p.h" // for the flags +#include "qdbusutil.h" + +extern QDBUS_EXPORT QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo, + const QMetaObject *base, int flags); + +// implement the D-Bus org.freedesktop.DBus.Introspectable interface +// we do that by analysing the metaObject of all the adaptor interfaces + +static QString generateInterfaceXml(const QMetaObject *mo, int flags, int methodOffset, int propOffset) +{ + QString retval; + + // start with properties: + if (flags & QDBusConnection::ExportProperties) { + for (int i = propOffset; i < mo->propertyCount(); ++i) { + static const char *accessvalues[] = {0, "read", "write", "readwrite"}; + + QMetaProperty mp = mo->property(i); + + if (!mp.isScriptable() && (flags & QDBusConnection::ExportAllProperties) != + QDBusConnection::ExportAllProperties) + continue; + + int access = 0; + if (mp.isReadable()) + access |= 1; + if (mp.isWritable()) + access |= 2; + + int typeId = qDBusNameToTypeId(mp.typeName()); + if (!typeId) + continue; + + retval += QString(QLatin1String(" <property name=\"%1\" type=\"%2\" access=\"%3\" />\n")) + .arg(mp.name()) + .arg(QLatin1String( QDBusUtil::typeToSignature( QVariant::Type(typeId) ))) + .arg(QLatin1String( accessvalues[access] )); + } + } + + // now add methods: + for (int i = methodOffset; i < mo->methodCount(); ++i) { + QMetaMethod mm = mo->method(i); + QByteArray signature = mm.signature(); + int paren = signature.indexOf('('); + + bool isSignal; + if (mm.methodType() == QMetaMethod::Signal) + // adding a signal + isSignal = true; + else if (mm.methodType() == QMetaMethod::Slot && mm.access() == QMetaMethod::Public) + isSignal = false; + else + continue; // neither signal nor public slot + + if ((isSignal && !(flags & QDBusConnection::ExportSignals)) || + (!isSignal && !(flags & QDBusConnection::ExportSlots))) + continue; + + QString xml = QString(QLatin1String(" <%1 name=\"%2\">\n")) + .arg(isSignal ? QLatin1String("signal") : QLatin1String("method")) + .arg(QLatin1String(signature.left(paren))); + + // check the return type first + int typeId = qDBusNameToTypeId(mm.typeName()); + if (typeId) + xml += QString(QLatin1String(" <arg type=\"%1\" direction=\"out\"/>\n")) + .arg(QLatin1String(QDBusUtil::typeToSignature( QVariant::Type(typeId) ))); + else if (*mm.typeName()) + continue; // wasn't a valid type + + QList<QByteArray> names = mm.parameterNames(); + QList<int> types; + int inputCount = qDBusParametersForMethod(mm, types); + if (inputCount == -1) + continue; // invalid form + if (isSignal && inputCount + 1 != types.count()) + continue; // signal with output arguments? + if (isSignal && types.at(inputCount) == QDBusConnectionPrivate::messageMetaType) + continue; // signal with QDBusMessage argument? + + int j; + bool isScriptable = mm.attributes() & QMetaMethod::Scriptable; + for (j = 1; j < types.count(); ++j) { + // input parameter for a slot or output for a signal + if (types.at(j) == QDBusConnectionPrivate::messageMetaType) { + isScriptable = true; + continue; + } + + QString name; + if (!names.at(j - 1).isEmpty()) + name = QString(QLatin1String("name=\"%1\" ")).arg(QLatin1String(names.at(j - 1))); + + bool isOutput = isSignal || j > inputCount; + + xml += QString(QLatin1String(" <arg %1type=\"%2\" direction=\"%3\"/>\n")) + .arg(name) + .arg(QLatin1String(QDBusUtil::typeToSignature( QVariant::Type(types.at(j)) ))) + .arg(isOutput ? QLatin1String("out") : QLatin1String("in")); + } + + if (!isScriptable) { + // check if this was added by other means + if (isSignal && (flags & QDBusConnection::ExportAllSignals) != QDBusConnection::ExportAllSignals) + continue; + if (!isSignal && (flags & QDBusConnection::ExportAllSlots) != QDBusConnection::ExportAllSlots) + continue; + } + + if (qDBusCheckAsyncTag(mm.tag())) + // add the no-reply annotation + xml += QLatin1String(" <annotation name=\"" ANNOTATION_NO_WAIT "\"" + " value=\"true\"/>\n"); + + retval += xml; + retval += QString(QLatin1String(" </%1>\n")) + .arg(isSignal ? QLatin1String("signal") : QLatin1String("method")); + } + + return retval; +} + +QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo, const QMetaObject *base, + int flags) +{ + if (interface.isEmpty()) { + // generate the interface name from the meta object + int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE); + if (idx >= mo->classInfoOffset()) { + interface = QLatin1String(mo->classInfo(idx).value()); + } else { + interface = QLatin1String(mo->className()); + interface.replace(QLatin1String("::"), QLatin1String(".")); + + if (interface.startsWith( QLatin1String("QDBus") )) { + interface.prepend( QLatin1String("com.trolltech.QtDBus.") ); + } else if (interface.startsWith( QLatin1Char('Q') )) { + // assume it's Qt + interface.prepend( QLatin1String("com.trolltech.Qt.") ); + } else if (!QCoreApplication::instance() || + QCoreApplication::instance()->applicationName().isEmpty()) { + interface.prepend( QLatin1String("local.") ); + } else { + interface.prepend(QLatin1Char('.')).prepend( QCoreApplication::instance()->applicationName() ); + QStringList domainName = QCoreApplication::instance()->organizationDomain().split(QLatin1Char('.')); + foreach (const QString &part, domainName) + interface.prepend(QLatin1Char('.')).prepend(part); + } + } + } + + QString xml; + int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTROSPECTION); + if (idx >= mo->classInfoOffset()) + return QString::fromUtf8(mo->classInfo(idx).value()); + else + xml = generateInterfaceXml(mo, flags, base->methodCount(), base->propertyCount()); + + if (xml.isEmpty()) + return QString(); // don't add an empty interface + return QString(QLatin1String(" <interface name=\"%1\">\n%2 </interface>\n")) + .arg(interface, xml); +} diff --git a/qt/src/qdbusxmlparser.cpp b/qt/src/qdbusxmlparser.cpp new file mode 100644 index 0000000..0370cb2 --- /dev/null +++ b/qt/src/qdbusxmlparser.cpp @@ -0,0 +1,308 @@ +/* -*- 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 "qdbusutil.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(QLatin1String("annotation")); + for (int i = 0; i < list.count(); ++i) + { + QDomElement ann = list.item(i).toElement(); + if (ann.isNull()) + continue; + + QString name = ann.attribute(QLatin1String("name")), + value = ann.attribute(QLatin1String("value")); + + if (name.isEmpty()) + continue; + + retval.insert(name, value); + } + + return retval; +} + +static QDBusIntrospection::Arguments +parseArgs(const QDomElement& elem, const QLatin1String& direction, bool acceptEmpty = false) +{ + QDBusIntrospection::Arguments retval; + QDomNodeList list = elem.elementsByTagName(QLatin1String("arg")); + for (int i = 0; i < list.count(); ++i) + { + QDomElement arg = list.item(i).toElement(); + if (arg.isNull()) + continue; + + if ((acceptEmpty && !arg.hasAttribute(QLatin1String("direction"))) || + arg.attribute(QLatin1String("direction")) == direction) { + + QDBusIntrospection::Argument argData; + if (arg.hasAttribute(QLatin1String("name"))) + argData.name = arg.attribute(QLatin1String("name")); // can be empty + argData.type = arg.attribute(QLatin1String("type")); + if (!QDBusUtil::isValidSingleSignature(argData.type)) + continue; + + retval << argData; + } + } + return retval; +} + +QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path, + const QString& xmlData) + : m_service(service), m_path(path) +{ + QDomDocument doc; + doc.setContent(xmlData); + m_node = doc.firstChildElement(QLatin1String("node")); +} + +QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path, + const QDomElement& node) + : m_service(service), m_path(path), m_node(node) +{ +} + +QDBusIntrospection::Interfaces +QDBusXmlParser::interfaces() const +{ + QDBusIntrospection::Interfaces retval; + + if (m_node.isNull()) + return retval; + + QDomNodeList interfaces = m_node.elementsByTagName(QLatin1String("interface")); + for (int i = 0; i < interfaces.count(); ++i) + { + QDomElement iface = interfaces.item(i).toElement(); + QString ifaceName = iface.attribute(QLatin1String("name")); + if (iface.isNull() || ifaceName.isEmpty()) + continue; // for whatever reason + + QDBusIntrospection::Interface *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(QLatin1String("method")); + for (int j = 0; j < list.count(); ++j) + { + QDomElement method = list.item(j).toElement(); + QString methodName = method.attribute(QLatin1String("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(QLatin1String("signal")); + for (int j = 0; j < list.count(); ++j) + { + QDomElement signal = list.item(j).toElement(); + QString signalName = signal.attribute(QLatin1String("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(QLatin1String("property")); + for (int j = 0; j < list.count(); ++j) + { + QDomElement property = list.item(j).toElement(); + QString propertyName = property.attribute(QLatin1String("name")); + if (property.isNull() || propertyName.isEmpty()) + continue; + + QDBusIntrospection::Property propertyData; + + // parse data + propertyData.name = propertyName; + propertyData.type = property.attribute(QLatin1String("type")); + propertyData.annotations = parseAnnotations(property); + + if (!QDBusUtil::isValidSingleSignature(propertyData.type)) + // cannot be! + continue; + + QString access = property.attribute(QLatin1String("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 +{ + if (m_node.isNull()) + return QSharedDataPointer<QDBusIntrospection::Object>(); + + QDBusIntrospection::Object* objData; + 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(QLatin1String("node")); + for (int i = 0; i < objects.count(); ++i) { + QDomElement obj = objects.item(i).toElement(); + QString objName = obj.attribute(QLatin1String("name")); + if (obj.isNull() || objName.isEmpty()) + continue; // for whatever reason + + objData->childObjects.append(objName); + } + + QDomNodeList interfaces = m_node.elementsByTagName(QLatin1String("interface")); + for (int i = 0; i < interfaces.count(); ++i) { + QDomElement iface = interfaces.item(i).toElement(); + QString ifaceName = iface.attribute(QLatin1String("name")); + if (iface.isNull() || ifaceName.isEmpty()) + continue; + + objData->interfaces.append(ifaceName); + } + } else { + objData->introspection = QLatin1String("<node/>\n"); + } + + QSharedDataPointer<QDBusIntrospection::Object> retval; + retval = objData; + return retval; +} + +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(QLatin1String("node")); + for (int i = 0; i < objects.count(); ++i) { + QDomElement obj = objects.item(i).toElement(); + QString objName = obj.attribute(QLatin1String("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(QLatin1Char('/'))) + objAbsName.append(QLatin1Char('/')); + objAbsName += objName; + + QDBusXmlParser parser(m_service, objAbsName, obj); + retval->childObjectData.insert(objName, parser.objectTree()); + } + + retval->childObjects << objName; + } + + return QSharedDataPointer<QDBusIntrospection::ObjectTree>( retval ); +} + diff --git a/qt/src/qdbusxmlparser_p.h b/qt/src/qdbusxmlparser_p.h new file mode 100644 index 0000000..8f4f69b --- /dev/null +++ b/qt/src/qdbusxmlparser_p.h @@ -0,0 +1,53 @@ +/* -*- 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_p.h" + +/*! + \internal +*/ +class QDBusXmlParser +{ + QString m_service; + QString m_path; + QDomElement m_node; + +public: + QDBusXmlParser(const QString& service, const QString& path, + const QString& xmlData); + QDBusXmlParser(const QString& service, const QString& path, + const QDomElement& node); + + QDBusIntrospection::Interfaces interfaces() const; + QSharedDataPointer<QDBusIntrospection::Object> object() const; + QSharedDataPointer<QDBusIntrospection::ObjectTree> objectTree() const; +}; + +#endif |