From 26ab02e91671548e2b55a16bb953b3d9e0a82497 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 29 May 2006 18:17:09 +0000 Subject: * qt/*: Update the QtDBus bindings up to revision 546310 in Subversion. This adds the dbuscpp2xml tool, that parses a C++ header and outputs a D-BUS Introspection XML. --- qt/Makefile.am | 16 +- qt/dbuscpp2xml.cpp | 404 ++++++++++++++++++++++++++++++++++++++++++ qt/dbusidl2cpp.cpp | 2 +- qt/qdbusabstractadaptor.cpp | 9 +- qt/qdbusabstractinterface.cpp | 13 ++ qt/qdbusabstractinterface.h | 1 + qt/qdbusconnection.cpp | 29 ++- qt/qdbusconnection_p.h | 15 +- qt/qdbusintegrator.cpp | 353 +++++++++++++++--------------------- qt/qdbusinterface.cpp | 13 -- qt/qdbusinterface.h | 2 +- qt/qdbusinternalfilters.cpp | 173 ++---------------- qt/qdbusmacros.h | 5 +- qt/qdbusmetaobject.cpp | 6 +- qt/qdbusmisc.cpp | 156 ++++++++++++++++ qt/qdbusxmlgenerator.cpp | 192 ++++++++++++++++++++ 16 files changed, 961 insertions(+), 428 deletions(-) create mode 100644 qt/dbuscpp2xml.cpp create mode 100644 qt/qdbusmisc.cpp create mode 100644 qt/qdbusxmlgenerator.cpp (limited to 'qt') diff --git a/qt/Makefile.am b/qt/Makefile.am index 38cf1d71..c91c3422 100644 --- a/qt/Makefile.am +++ b/qt/Makefile.am @@ -41,20 +41,25 @@ libdbus_qt4_1_la_SOURCES = \ qdbusmessage.cpp \ qdbusserver.cpp \ qdbustype.cpp \ - qdbusabstractinterface.cpp \ + qdbusabstractinterface.cpp \ qdbusinterface.cpp \ qdbusxmlparser.cpp \ qdbusutil.cpp \ qdbusintrospection.cpp \ - qdbusabstractadaptor.cpp \ + qdbusabstractadaptor.cpp \ qdbusthread.cpp \ - qdbusinternalfilters.cpp \ - qdbusmetaobject.cpp + qdbusinternalfilters.cpp \ + qdbusmetaobject.cpp \ + qdbusmisc.cpp \ + qdbusxmlgenerator.cpp -bin_PROGRAMS = dbusidl2cpp +bin_PROGRAMS = dbusidl2cpp dbuscpp2xml dbusidl2cpp_SOURCES = dbusidl2cpp.cpp dbusidl2cpp_LDFLAGS = -no-undefined dbusidl2cpp_LDADD = $(DBUS_QT_LIBS) libdbus-qt4-1.la +dbuscpp2xml_SOURCES = dbuscpp2xml.cpp +dbuscpp2xml_LDFLAGS = -no-undefined +dbuscpp2xml_LDADD = $(DBUS_QT_LIBS) libdbus-qt4-1.la qdbusabstractadaptor.lo: qdbusabstractadaptor.moc qdbusabstractadaptor_p.moc qdbusabstractinterface.lo: qdbusabstractinterface.moc @@ -66,6 +71,7 @@ CLEANFILES=qdbusabstractadaptor.moc qdbusserver.moc qdbusconnection_p.moc qdbusc 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 diff --git a/qt/dbuscpp2xml.cpp b/qt/dbuscpp2xml.cpp new file mode 100644 index 00000000..e111f113 --- /dev/null +++ b/qt/dbuscpp2xml.cpp @@ -0,0 +1,404 @@ +/* -*- C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "qdbusconnection.h" // for the Export* flags +#include // for the XML DOCTYPE declaration + +// in qdbusxmlgenerator.cpp +extern QDBUS_EXPORT QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo, + const QMetaObject *base, int flags); + +#define PROGRAMNAME "dbuscpp2xml" +#define PROGRAMVERSION "0.1" +#define PROGRAMCOPYRIGHT "Copyright (C) 2006 Trolltech AS. All rights reserved." + +static const char cmdlineOptions[] = "psmaPSMAo:"; +static const char *outputFile; +static int flags; + +static const char help[] = + "Usage: " PROGRAMNAME " [options...] [files...]\n" + "Parses the C++ source or header file containing a QObject-derived class and\n" + "produces the D-Bus Introspection XML." + "\n" + "Options:\n" + " -p|-s|-m Only parse scriptable Properties, Signals and Methods (slots)\n" + " -P|-S|-M Parse all Properties, Signals and Methods (slots)\n" + " -a Output all scriptable contents (equivalent to -psm)\n" + " -A Output all contents (equivalent to -PSM)\n" + " -o Write the output to file \n" + " -h Show this information\n" + " -V Show the program version and quit.\n" + "\n"; + +class MocParser +{ + void parseError(); + QByteArray readLine(); + void loadIntData(uint *&data); + void loadStringData(char *&stringdata); + + QIODevice *input; + const char *filename; + int line; +public: + ~MocParser(); + void parse(const char *filename, QIODevice *input, int lineNumber = 0); + + QList objects; +}; + +void MocParser::parseError() +{ + fprintf(stderr, PROGRAMNAME ": error parsing input file '%s' line %d \n", filename, line); + exit(1); +} + +QByteArray MocParser::readLine() +{ + ++line; + return input->readLine(); +} + +void MocParser::loadIntData(uint *&data) +{ + data = 0; // initialise + QVarLengthArray array; + QRegExp rx("(\\d+|0x[0-9abcdef]+)", Qt::CaseInsensitive); + + while (!input->atEnd()) { + QString line = QLatin1String(readLine()); + int pos = line.indexOf("//"); + if (pos != -1) + line.truncate(pos); // drop comments + + if (line == "};\n") { + // end of data + data = new uint[array.count()]; + memcpy(data, array.data(), array.count() * sizeof(*data)); + return; + } + + pos = 0; + while ((pos = rx.indexIn(line, pos)) != -1) { + QString num = rx.cap(1); + if (num.startsWith("0x")) + array.append(num.mid(2).toUInt(0, 16)); + else + array.append(num.toUInt()); + pos += rx.matchedLength(); + } + } + + parseError(); +} + +void MocParser::loadStringData(char *&stringdata) +{ + stringdata = 0; + QVarLengthArray array; + + while (!input->atEnd()) { + QByteArray line = readLine(); + if (line == "};\n") { + // end of data + stringdata = new char[array.count()]; + memcpy(stringdata, array.data(), array.count() * sizeof(*stringdata)); + return; + } + + int start = line.indexOf('"'); + if (start == -1) + parseError(); + + int len = line.length() - 1; + line.truncate(len); // drop ending \n + if (line.at(len - 1) != '"') + parseError(); + + --len; + ++start; + for ( ; start < len; ++start) + if (line.at(start) == '\\') { + // parse escaped sequence + ++start; + if (start == len) + parseError(); + + QChar c(QLatin1Char(line.at(start))); + if (!c.isDigit()) { + switch (c.toLatin1()) { + case 'a': + array.append('\a'); + break; + case 'b': + array.append('\b'); + break; + case 'f': + array.append('\f'); + break; + case 'n': + array.append('\n'); + break; + case 'r': + array.append('\r'); + break; + case 't': + array.append('\t'); + break; + case 'v': + array.append('\v'); + break; + case '\\': + case '?': + case '\'': + case '"': + array.append(c.toLatin1()); + break; + + case 'x': + if (start + 2 <= len) + parseError(); + array.append(char(line.mid(start + 1, 2).toInt(0, 16))); + break; + + default: + array.append(c.toLatin1()); + fprintf(stderr, PROGRAMNAME ": warning: invalid escape sequence '\\%c' found in input", + c.toLatin1()); + } + } else { + // octal + QRegExp octal("([0-7]+)"); + if (octal.indexIn(QLatin1String(line), start) == -1) + parseError(); + array.append(char(octal.cap(1).toInt(0, 8))); + } + } else { + array.append(line.at(start)); + } + } + + parseError(); +} + +void MocParser::parse(const char *fname, QIODevice *io, int lineNumber) +{ + filename = fname; + input = io; + line = lineNumber; + + while (!input->atEnd()) { + QByteArray line = readLine(); + if (line.startsWith("static const uint qt_meta_data_")) { + // start of new class data + uint *data; + loadIntData(data); + + // find the start of the string data + do { + line = readLine(); + if (input->atEnd()) + parseError(); + } while (!line.startsWith("static const char qt_meta_stringdata_")); + + char *stringdata; + loadStringData(stringdata); + + QMetaObject mo; + mo.d.superdata = &QObject::staticMetaObject; + mo.d.stringdata = stringdata; + mo.d.data = data; + mo.d.extradata = 0; + objects.append(mo); + } + } + + fname = 0; + input = 0; +} + +MocParser::~MocParser() +{ + foreach (QMetaObject mo, objects) { + delete const_cast(mo.d.stringdata); + delete const_cast(mo.d.data); + } +} + +static void showHelp() +{ + printf("%s", help); + exit(0); +} + +static void showVersion() +{ + printf("%s version %s\n", PROGRAMNAME, PROGRAMVERSION); + printf("D-Bus QObject-to-XML converter\n"); + exit(0); +} + +static void parseCmdLine(int argc, char **argv) +{ + int c; + opterr = true; + while ((c = getopt(argc, argv, cmdlineOptions)) != -1) + switch (c) + { + case 'p': + flags |= QDBusConnection::ExportProperties; + break; + + case 's': + flags |= QDBusConnection::ExportSignals; + break; + + case 'm': + flags |= QDBusConnection::ExportSlots; + break; + + case 'a': + flags |= QDBusConnection::ExportContents; + break; + + case 'P': + flags |= QDBusConnection::ExportAllProperties; + break; + + case 'S': + flags |= QDBusConnection::ExportAllSignals; + break; + + case 'M': + flags |= QDBusConnection::ExportAllSlots; + break; + + case 'A': + flags |= QDBusConnection::ExportAllContents; + break; + + case 'o': + outputFile = optarg; + break; + + case 'h': + showHelp(); + break; + + case 'V': + showVersion(); + break; + + case '?': + exit(1); + default: + abort(); + } + + if (flags == 0) + flags = QDBusConnection::ExportAllContents; +} + +int main(int argc, char **argv) +{ + MocParser parser; + parseCmdLine(argc, argv); + + for (int i = 1; i < argc; ++i) { + FILE *in = fopen(argv[i], "r"); + if (in == 0) { + fprintf(stderr, PROGRAMNAME ": could not open '%s': %s\n", + argv[i], strerror(errno)); + return 1; + } + + QFile f; + f.open(in, QIODevice::ReadOnly); + f.readLine(); + + QByteArray line = f.readLine(); + if (line.contains("Meta object code from reading C++ file")) + // this is a moc-generated file + parser.parse(argv[i], &f, 3); + else { + // run moc on this file + QProcess proc; + proc.start("moc", QStringList() << QFile::encodeName(argv[i])); + + if (!proc.waitForStarted()) { + fprintf(stderr, PROGRAMNAME ": could not execute moc! Aborting.\n"); + return 1; + } + + proc.closeWriteChannel(); + + if (!proc.waitForFinished() || proc.exitStatus() != QProcess::NormalExit || + proc.exitCode() != 0) { + // output the moc errors: + fprintf(stderr, "%s", proc.readAllStandardError().constData()); + fprintf(stderr, PROGRAMNAME ": exit code %d from moc. Aborting\n", proc.exitCode()); + return 1; + } + fprintf(stderr, "%s", proc.readAllStandardError().constData()); + + parser.parse(argv[i], &proc, 1); + } + + f.close(); + fclose(in); + } + + FILE *output = stdout; + if (outputFile != 0) { + output = fopen(outputFile, "w"); + if (output == 0) { + fprintf(stderr, PROGRAMNAME ": could not open output file '%s': %s", + outputFile, strerror(errno)); + return 1; + } + } + + fprintf(output, "%s\n", DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); + foreach (QMetaObject mo, parser.objects) { + QString xml = qDBusGenerateMetaObjectXml(QString(), &mo, &QObject::staticMetaObject, + flags); + fprintf(output, "%s", qPrintable(xml)); + } + fprintf(output, "\n"); + + if (output != stdout) + fclose(output); +} + diff --git a/qt/dbusidl2cpp.cpp b/qt/dbusidl2cpp.cpp index 8097a075..d329c18e 100644 --- a/qt/dbusidl2cpp.cpp +++ b/qt/dbusidl2cpp.cpp @@ -375,7 +375,7 @@ static QString stringify(const QString &data) retval += "\\\""; else retval += data[i]; - retval += "\"\n"; + retval += "\\n\"\n"; } return retval; } diff --git a/qt/qdbusabstractadaptor.cpp b/qt/qdbusabstractadaptor.cpp index 2794293d..b7c41888 100644 --- a/qt/qdbusabstractadaptor.cpp +++ b/qt/qdbusabstractadaptor.cpp @@ -242,12 +242,11 @@ void QDBusAdaptorConnector::polish() return; // avoid working multiple times if multiple adaptors were added waitingForPolish = false; - const QObjectList &objs = children(); + const QObjectList &objs = parent()->children(); foreach (QObject *obj, objs) { - Q_ASSERT(qobject_cast(obj)); - - QDBusAbstractAdaptor *adaptor = static_cast(obj); - addAdaptor(adaptor); + QDBusAbstractAdaptor *adaptor = qobject_cast(obj); + if (adaptor) + addAdaptor(adaptor); } // sort the adaptor list diff --git a/qt/qdbusabstractinterface.cpp b/qt/qdbusabstractinterface.cpp index 7c09b510..2a6bcf04 100644 --- a/qt/qdbusabstractinterface.cpp +++ b/qt/qdbusabstractinterface.cpp @@ -130,6 +130,19 @@ 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. */ diff --git a/qt/qdbusabstractinterface.h b/qt/qdbusabstractinterface.h index 1ad1a532..aa6d00d7 100644 --- a/qt/qdbusabstractinterface.h +++ b/qt/qdbusabstractinterface.h @@ -51,6 +51,7 @@ public: public: virtual ~QDBusAbstractInterface(); + bool isValid() const; QDBusConnection connection() const; diff --git a/qt/qdbusconnection.cpp b/qt/qdbusconnection.cpp index 52875a07..7bbde9ac 100644 --- a/qt/qdbusconnection.cpp +++ b/qt/qdbusconnection.cpp @@ -85,6 +85,7 @@ void QDBusConnectionManager::bindToApplication() } } +QDBUS_EXPORT void qDBusBindToApplication(); void qDBusBindToApplication() { manager()->bindToApplication(); @@ -351,12 +352,6 @@ void QDBusConnection::closeConnection(const QString &name) manager()->removeConnection(name); } -void QDBusConnectionPrivate::timerEvent(QTimerEvent *e) -{ - DBusTimeout *timeout = timeouts.value(e->timerId(), 0); - dbus_timeout_handle(timeout); -} - /*! 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. @@ -367,7 +362,7 @@ bool QDBusConnection::send(const QDBusMessage &message) const { if (!d || !d->connection) return false; - return d->send(message); + return d->send(message) != 0; } /*! @@ -445,25 +440,21 @@ bool QDBusConnection::connect(const QString &service, const QString &path, const if (source.isEmpty()) return false; } - source += path; // check the slot QDBusConnectionPrivate::SignalHook hook; - if ((hook.midx = QDBusConnectionPrivate::findSlot(receiver, slot + 1, hook.params)) == -1) - return false; - - hook.interface = interface; - hook.name = name; + QString key; hook.signature = signature; - hook.obj = receiver; + 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(source); - for ( ; it != d->signalHooks.end() && it.key() == source; ++it) { + 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.interface == hook.interface && - entry.name == hook.name && + if (entry.sender == hook.sender && + entry.path == hook.path && entry.signature == hook.signature && entry.obj == hook.obj && entry.midx == hook.midx) { @@ -473,7 +464,7 @@ bool QDBusConnection::connect(const QString &service, const QString &path, const } - d->connectSignal(source, hook); + d->connectSignal(key, hook); return true; } diff --git a/qt/qdbusconnection_p.h b/qt/qdbusconnection_p.h index af572084..a9034498 100644 --- a/qt/qdbusconnection_p.h +++ b/qt/qdbusconnection_p.h @@ -85,7 +85,7 @@ public: struct SignalHook { inline SignalHook() : obj(0), midx(-1) { } - QString interface, name, signature; + QString sender, path, signature; QObject* obj; int midx; QList params; @@ -139,7 +139,7 @@ public: QString getNameOwner(const QString &service); - bool send(const QDBusMessage &message) const; + int send(const QDBusMessage &message) const; QDBusMessage sendWithReply(const QDBusMessage &message, int mode); int sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, const char *method); @@ -150,7 +150,7 @@ public: void disconnectRelay(const QString &service, const QString &path, const QString &interface, QDBusAbstractInterface *receiver, const char *signal); - bool handleSignal(const QString &path, const QDBusMessage &msg); + bool handleSignal(const QString &key, const QDBusMessage &msg); bool handleSignal(const QDBusMessage &msg); bool handleObjectCall(const QDBusMessage &message); bool handleError(); @@ -176,6 +176,7 @@ private: public slots: // public slots + void doDispatch(); void socketRead(int); void socketWrite(int); void objectDestroyed(QObject *o); @@ -210,7 +211,12 @@ public: // static methods static int messageMetaType; static int registerMessageMetaType(); - static int findSlot(QObject *obj, const char *slotName, QList& params); + static int findSlot(QObject *obj, const QByteArray &normalizedName, QList& 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 *); }; @@ -225,6 +231,7 @@ public slots: void reply(const QDBusMessage &msg); }; +// in qdbusmisc.cpp extern int qDBusParametersForMethod(const QMetaMethod &mm, QList& metaTypes); extern int qDBusNameToTypeId(const char *name); extern bool qDBusCheckAsyncTag(const char *tag); diff --git a/qt/qdbusintegrator.cpp b/qt/qdbusintegrator.cpp index dc038abb..190c4686 100644 --- a/qt/qdbusintegrator.cpp +++ b/qt/qdbusintegrator.cpp @@ -46,6 +46,9 @@ int QDBusConnectionPrivate::messageMetaType = 0; +typedef void (*qDBusSpyHook)(const QDBusMessage&); +static qDBusSpyHook messageSpyHook; + struct QDBusPendingCall { QPointer receiver; @@ -217,6 +220,12 @@ static void qDBusNewConnection(DBusServer *server, DBusConnection *c, void *data qDebug("SERVER: GOT A NEW CONNECTION"); // TODO } +extern QDBUS_EXPORT void qDBusSetSpyHook(qDBusSpyHook); +void qDBusSetSpyHook(qDBusSpyHook hook) +{ + messageSpyHook = hook; +} + #if USE_OUTSIDE_DISPATCH # define HANDLED DBUS_HANDLER_RESULT_HANDLED_OUTSIDE_DISPATCH static DBusHandlerResult qDBusSignalFilterOutside(DBusConnection *connection, @@ -262,6 +271,11 @@ DBusHandlerResult QDBusConnectionPrivate::messageFilter(DBusConnection *connecti QDBusMessage amsg = QDBusMessage::fromDBusMessage(message, QDBusConnection(d->name)); qDebug() << "got message:" << amsg; + if (messageSpyHook) { + qDebug() << "calling the message spy hook"; + (*messageSpyHook)(amsg); + } + bool handled = false; int msgType = dbus_message_get_type(message); if (msgType == DBUS_MESSAGE_TYPE_SIGNAL) { @@ -304,26 +318,6 @@ static void huntAndEmit(DBusConnection *connection, DBusMessage *msg, } } -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; -} - static bool typesMatch(int metaId, int variantType) { if (metaId == int(variantType)) @@ -351,112 +345,6 @@ static bool typesMatch(int metaId, int variantType) return false; // no match } -int qDBusNameToTypeId(const char *name) -{ - int id = static_cast( 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::messageMetaType || - id == QDBusTypeHelper::id() || - id == QDBusTypeHelper::listId() || - id == QDBusTypeHelper::listId() || - id == QDBusTypeHelper::listId() || - id == QDBusTypeHelper::listId() || - id == QDBusTypeHelper::listId() || - id == QDBusTypeHelper::listId() || - id == QDBusTypeHelper::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& metaTypes) -{ - QList 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::messageMetaType) - seenMessage = true; - } - - return inputCount; -} - static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, const QDBusTypeList &types, QList& metaTypes) { @@ -681,7 +569,7 @@ void QDBusConnectionPrivate::deliverCall(const CallDeliveryEvent& data) const QVarLengthArray params; params.reserve(metaTypes.count()); - QVarLengthArray auxParameters; + QVariantList auxParameters; // let's create the parameter list // first one is the return type -- add it below @@ -879,6 +767,10 @@ bool QDBusConnectionPrivate::handleError() 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(); @@ -887,6 +779,8 @@ void QDBusConnectionPrivate::bindToApplication() it.next(); if (!it.value().read && !it.value().write) { qDBusAddWatch(it.value().watch, this); + } else { + watchers.insertMulti(it.key(), it.value()); } } @@ -895,6 +789,18 @@ void QDBusConnectionPrivate::bindToApplication() 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 it(watchers); @@ -905,8 +811,8 @@ void QDBusConnectionPrivate::socketRead(int fd) qDebug("OUT OF MEM"); } } - if (mode == ClientMode) - while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS); + + doDispatch(); } void QDBusConnectionPrivate::socketWrite(int fd) @@ -963,13 +869,12 @@ int QDBusConnectionPrivate::registerMessageMetaType() return tp; } -int QDBusConnectionPrivate::findSlot(QObject* obj, const char *slotName, QList& params) +int QDBusConnectionPrivate::findSlot(QObject* obj, const QByteArray &normalizedName, + QList ¶ms) { - Q_ASSERT(slotName); - QByteArray normalizedName = QMetaObject::normalizedSignature(slotName); - int midx = obj->metaObject()->indexOfSlot(normalizedName); + int midx = obj->metaObject()->indexOfMethod(normalizedName); if (midx == -1) { - qWarning("No such slot '%s' while connecting D-Bus", slotName); + qWarning("No such slot '%s' while connecting D-Bus", normalizedName.constData()); return -1; } @@ -980,6 +885,42 @@ int QDBusConnectionPrivate::findSlot(QObject* obj, const char *slotName, QListobj && (msg.interface().isEmpty() || @@ -996,10 +938,9 @@ bool QDBusConnectionPrivate::activateInternalFilters(const ObjectTreeNode *node, qDBusPropertyGet(node, msg); else if (msg.method() == QLatin1String("Set") && msg.signature() == QLatin1String("ssv")) qDBusPropertySet(node, msg); - else - return false; - - return true; + + if (msg.interface() == QLatin1String(DBUS_INTERFACE_PROPERTIES)) + return true; } return false; @@ -1144,19 +1085,17 @@ bool QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg) return false; } -bool QDBusConnectionPrivate::handleSignal(const QString &path, const QDBusMessage &msg) +bool QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage& msg) { - QReadLocker locker(&lock); - bool result = false; - SignalHookHash::const_iterator it = signalHooks.find(path); + SignalHookHash::const_iterator it = signalHooks.find(key); //qDebug("looking for: %s", path.toLocal8Bit().constData()); //qDebug() << signalHooks.keys(); - for ( ; it != signalHooks.constEnd() && it.key() == path; ++ it) { + for ( ; it != signalHooks.constEnd() && it.key() == key; ++it) { const SignalHook &hook = it.value(); - if ( !hook.name.isEmpty() && hook.name != msg.name() ) + if ( !hook.sender.isEmpty() && hook.sender != msg.sender() ) continue; - if ( !hook.interface.isEmpty() && hook.interface != msg.interface() ) + if ( !hook.path.isEmpty() && hook.path != msg.path() ) continue; if ( !hook.signature.isEmpty() && hook.signature != msg.signature() ) continue; @@ -1171,8 +1110,17 @@ bool QDBusConnectionPrivate::handleSignal(const QString &path, const QDBusMessag bool QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg) { - // yes, it is a single "|" below... - return handleSignal(QString(), msg) | handleSignal(msg.sender() + msg.path(), 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; @@ -1247,6 +1195,9 @@ void QDBusConnectionPrivate::setConnection(DBusConnection *dbc) #endif //qDebug("base service: %s", service); + + // schedule a dispatch: + QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection); } extern "C"{ @@ -1255,6 +1206,7 @@ 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(user_data); @@ -1284,18 +1236,22 @@ void QDBusConnectionPrivate::messageResultReceived(DBusPendingCall *pending, voi delete call; } -bool QDBusConnectionPrivate::send(const QDBusMessage& message) const +int QDBusConnectionPrivate::send(const QDBusMessage& message) const { DBusMessage *msg = message.toDBusMessage(); if (!msg) - return false; + 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 isOk; + return serial; } QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message, @@ -1306,6 +1262,7 @@ QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message, if (!msg) return QDBusMessage(); + qDebug() << "sending message:" << message; DBusMessage *reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &error); handleError(); @@ -1314,7 +1271,12 @@ QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message, if (lastError.isValid()) return QDBusMessage::fromError(lastError); - return QDBusMessage::fromDBusMessage(reply, QDBusConnection(name)); + 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) { @@ -1332,14 +1294,21 @@ QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message, int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, const char *method) { - DBusMessage *msg = message.toDBusMessage(); - if (!msg) - return 0; + if (!receiver || !method || !*method) + // would not be able to deliver a reply + return send(message); int slotIdx = -1; QList metaTypes; - if (receiver && method && *method) - slotIdx = findSlot(receiver, method + 1, 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; @@ -1388,45 +1357,27 @@ void QDBusConnectionPrivate::connectRelay(const QString &service, const QString { // this function is called by QDBusAbstractInterface when one of its signals is connected // we set up a relay from D-Bus into it - - // similar to QDBusConnectionPrivate::findSlot! Merge! - QByteArray normalizedName = QMetaObject::normalizedSignature(signal + 1); SignalHook hook; - hook.midx = receiver->metaObject()->indexOfSignal(normalizedName); - Q_ASSERT(hook.midx != -1); // cannot happen - if (hook.midx < QDBusAbstractInterface::staticMetaObject.methodCount()) - return; // don't connect to this signal - - int inputCount = qDBusParametersForMethod(receiver->metaObject()->method(hook.midx), hook.params); - if ( inputCount == -1 || inputCount + 1 != hook.params.count() ) - return; // failed to parse or invalid arguments or output arguments - - // build the D-Bus signal name and signature - QString source = service; - source += path; - normalizedName.truncate(normalizedName.indexOf('(')); - hook.name = QString::fromUtf8(normalizedName); - hook.interface = interface; - hook.obj = receiver; - for (int i = 1; i <= inputCount; ++i) - if (hook.params.at(i) != messageMetaType) - hook.signature += QLatin1String( QDBusType::dbusSignature( QVariant::Type(hook.params.at(i)) ) ); + 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(source); + SignalHookHash::ConstIterator it = signalHooks.find(key); SignalHookHash::ConstIterator end = signalHooks.end(); - for ( ; it != end && it.key() == source; ++it) { + for ( ; it != end && it.key() == key; ++it) { const SignalHook &entry = it.value(); - if (entry.interface == hook.interface && - entry.name == hook.name && + 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(source, hook); + connectSignal(key, hook); } void QDBusConnectionPrivate::disconnectRelay(const QString &service, const QString &path, @@ -1436,38 +1387,20 @@ void QDBusConnectionPrivate::disconnectRelay(const QString &service, const QStri { // this function is called by QDBusAbstractInterface when one of its signals is disconnected // we remove relay from D-Bus into it - - // similar to QDBusConnectionPrivate::findSlot! Merge! - QByteArray normalizedName = QMetaObject::normalizedSignature(signal + 1); SignalHook hook; - hook.midx = receiver->metaObject()->indexOfSignal(normalizedName); - Q_ASSERT(hook.midx != -1); // cannot happen - if (hook.midx < QDBusAbstractInterface::staticMetaObject.methodCount()) - return; // we won't find it, so don't bother - - int inputCount = qDBusParametersForMethod(receiver->metaObject()->method(hook.midx), hook.params); - if ( inputCount == -1 || inputCount + 1 != hook.params.count() ) - return; // failed to parse or invalid arguments or output arguments - - // build the D-Bus signal name and signature - QString source = service; - source += path; - normalizedName.truncate(normalizedName.indexOf('(')); - hook.name = QString::fromUtf8(normalizedName); - hook.interface = interface; - hook.obj = receiver; - for (int i = 1; i <= inputCount; ++i) - if (hook.params.at(i) != messageMetaType) - hook.signature += QLatin1String( QDBusType::dbusSignature( QVariant::Type(hook.params.at(i)) ) ); + 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(source); + SignalHookHash::Iterator it = signalHooks.find(key); SignalHookHash::Iterator end = signalHooks.end(); - for ( ; it != end && it.key() == source; ++it) { + for ( ; it != end && it.key() == key; ++it) { const SignalHook &entry = it.value(); - if (entry.interface == hook.interface && - entry.name == hook.name && + if (entry.sender == hook.sender && + entry.path == hook.path && entry.signature == hook.signature && entry.obj == hook.obj && entry.midx == hook.midx) { diff --git a/qt/qdbusinterface.cpp b/qt/qdbusinterface.cpp index 0afa452b..6367654d 100644 --- a/qt/qdbusinterface.cpp +++ b/qt/qdbusinterface.cpp @@ -57,19 +57,6 @@ QDBusInterface::~QDBusInterface() // resources are freed in QDBusInterfacePrivate::~QDBusInterfacePrivate() } -/*! - 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 QDBusInterface::isValid() const -{ - return d_func()->isValid; -} - /*! \internal Overrides QObject::metaObject to return our own copy. diff --git a/qt/qdbusinterface.h b/qt/qdbusinterface.h index 2e16e5ce..716ca8ac 100644 --- a/qt/qdbusinterface.h +++ b/qt/qdbusinterface.h @@ -35,7 +35,6 @@ private: public: ~QDBusInterface(); - bool isValid() const; virtual const QMetaObject *metaObject() const; virtual void *qt_metacast(const char *); @@ -43,6 +42,7 @@ public: private: Q_DECLARE_PRIVATE(QDBusInterface); + Q_DISABLE_COPY(QDBusInterface) }; struct QDBUS_EXPORT QDBusInterfacePtr diff --git a/qt/qdbusinternalfilters.cpp b/qt/qdbusinternalfilters.cpp index 3d454277..8886d3bf 100644 --- a/qt/qdbusinternalfilters.cpp +++ b/qt/qdbusinternalfilters.cpp @@ -30,10 +30,15 @@ #include "qdbusabstractadaptor.h" #include "qdbusabstractadaptor_p.h" -#include "qdbusinterface_p.h" // for ANNOTATION_NO_WAIT +#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[] = " \n" " \n" @@ -55,164 +60,6 @@ static const char propertiesInterfaceXml[] = " \n" " \n"; -// 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(" \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(" \n")) - .arg(QLatin1String(QDBusUtil::typeToSignature( QVariant::Type(typeId) ))); - else if (*mm.typeName()) - continue; // wasn't a valid type - - QList names = mm.parameterNames(); - QList 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(" \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(" \n"); - - retval += xml; - retval += QString(QLatin1String(" \n")) - .arg(isSignal ? QLatin1String("signal") : QLatin1String("method")); - } - - return retval; -} - -static QString generateMetaObjectXml(QString interface, const QMetaObject *mo, const QMetaObject *base, - int flags) -{ - if (interface.isEmpty()) { - // generate the interface name from the meta object - int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE); - if (idx >= mo->classInfoOffset()) { - interface = QLatin1String(mo->classInfo(idx).value()); - } else { - interface = QLatin1String(mo->className()); - interface.replace(QLatin1String("::"), QLatin1String(".")); - - if (interface.startsWith( QLatin1String("QDBus") )) { - interface.prepend( QLatin1String("com.trolltech.QtDBus.") ); - } else if (interface.startsWith( QLatin1Char('Q') )) { - // assume it's Qt - interface.prepend( QLatin1String("com.trolltech.Qt.") ); - } else if (!QCoreApplication::instance() || - QCoreApplication::instance()->applicationName().isEmpty()) { - interface.prepend( QLatin1String("local.") ); - } else { - interface.prepend(QLatin1Char('.')).prepend( QCoreApplication::instance()->applicationName() ); - QStringList domainName = QCoreApplication::instance()->organizationDomain().split(QLatin1Char('.')); - foreach (const QString &part, domainName) - interface.prepend(QLatin1Char('.')).prepend(part); - } - } - } - - QString xml; - int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTROSPECTION); - if (idx >= mo->classInfoOffset()) - xml = QString::fromUtf8(mo->classInfo(idx).value()); - else - xml = generateInterfaceXml(mo, flags, base->methodCount(), base->propertyCount()); - - return QString(QLatin1String(" \n%2 \n")) - .arg(interface, xml); -} - static QString generateSubObjectXml(QObject *object) { QString retval; @@ -236,7 +83,7 @@ QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode *node if (node->flags & QDBusConnection::ExportContents) { const QMetaObject *mo = node->obj->metaObject(); for ( ; mo != &QObject::staticMetaObject; mo = mo->superClass()) - xml_data += generateMetaObjectXml(QString(), mo, mo->superClass(), + xml_data += qDBusGenerateMetaObjectXml(QString(), mo, mo->superClass(), node->flags); } @@ -253,9 +100,9 @@ QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode *node QString ifaceXml = QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(it->adaptor); if (ifaceXml.isEmpty()) { // add the interface's contents: - ifaceXml += generateMetaObjectXml(it->interface, it->metaObject, - &QDBusAbstractAdaptor::staticMetaObject, - QDBusConnection::ExportAllContents); + ifaceXml += qDBusGenerateMetaObjectXml(it->interface, it->metaObject, + &QDBusAbstractAdaptor::staticMetaObject, + QDBusConnection::ExportAllContents); QDBusAbstractAdaptorPrivate::saveIntrospectionXml(it->adaptor, ifaceXml); } diff --git a/qt/qdbusmacros.h b/qt/qdbusmacros.h index a0553275..5b3c7d30 100644 --- a/qt/qdbusmacros.h +++ b/qt/qdbusmacros.h @@ -37,7 +37,7 @@ # error Sorry, you need a compiler with support for template member functions to compile QtDBus. #endif -#if defined(DBUS_COMPILATION) && defined(QDBUS_MAKEDLL) +#if defined(QDBUS_MAKEDLL) # define QDBUS_EXPORT Q_DECL_EXPORT #else # define QDBUS_EXPORT Q_DECL_IMPORT @@ -46,8 +46,5 @@ #ifndef Q_MOC_RUN # define Q_ASYNC #endif -#ifndef QT_NO_KEYWORDS -# define async Q_ASYNC -#endif #endif diff --git a/qt/qdbusmetaobject.cpp b/qt/qdbusmetaobject.cpp index 2992c703..a923d79a 100644 --- a/qt/qdbusmetaobject.cpp +++ b/qt/qdbusmetaobject.cpp @@ -48,8 +48,8 @@ private: QByteArray tag; QByteArray inputSignature; QByteArray outputSignature; - QVarLengthArray inputTypes; - QVarLengthArray outputTypes; + QVarLengthArray inputTypes; + QVarLengthArray outputTypes; int flags; }; @@ -297,7 +297,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) if (className.isEmpty()) className = QLatin1String("QDBusInterface"); - QVarLengthArray data; + QVarLengthArray data; data.resize(sizeof(QDBusMetaObjectPrivate) / sizeof(int)); QDBusMetaObjectPrivate *header = reinterpret_cast(data.data()); diff --git a/qt/qdbusmisc.cpp b/qt/qdbusmisc.cpp new file mode 100644 index 00000000..9aaf9f24 --- /dev/null +++ b/qt/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 + * + * 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 + +#include +#include + +#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( 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::id() || + id == QDBusTypeHelper::listId() || + id == QDBusTypeHelper::listId() || + id == QDBusTypeHelper::listId() || + id == QDBusTypeHelper::listId() || + id == QDBusTypeHelper::listId() || + id == QDBusTypeHelper::listId() || + id == QDBusTypeHelper::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& metaTypes) +{ + QList 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/qdbusxmlgenerator.cpp b/qt/qdbusxmlgenerator.cpp new file mode 100644 index 00000000..561985ab --- /dev/null +++ b/qt/qdbusxmlgenerator.cpp @@ -0,0 +1,192 @@ +/* -*- mode: C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira + * + * 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 +#include +#include + +#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(" \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(" \n")) + .arg(QLatin1String(QDBusUtil::typeToSignature( QVariant::Type(typeId) ))); + else if (*mm.typeName()) + continue; // wasn't a valid type + + QList names = mm.parameterNames(); + QList 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(" \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(" \n"); + + retval += xml; + retval += QString(QLatin1String(" \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()); + + return QString(QLatin1String(" \n%2 \n")) + .arg(interface, xml); +} -- cgit