summaryrefslogtreecommitdiff
path: root/qt/qdbusintegrator.cpp
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@trolltech.com>2006-02-15 16:25:11 +0000
committerJohn Palmieri <johnp@remedyz.boston.redhat.com>2006-06-28 08:15:12 -0400
commit84d192a8e5b0c68843ddac3eb631edda2ebe3f32 (patch)
tree211daf3f27e7894cdbb6a0753460f3efa2899318 /qt/qdbusintegrator.cpp
parentb33ea977f43fbbf5b6f855c4f2945413afea52fd (diff)
Merge the changes to the bindings from the KDE Subversion server.
This is a major change: library is source- and binary-incompatible to what it used to be. All testcases are green, functionality is preserved. It is not feature-complete. Development will continue in the branch in the Subversion server for a while.
Diffstat (limited to 'qt/qdbusintegrator.cpp')
-rw-r--r--qt/qdbusintegrator.cpp916
1 files changed, 765 insertions, 151 deletions
diff --git a/qt/qdbusintegrator.cpp b/qt/qdbusintegrator.cpp
index 4a19d33..79baa10 100644
--- a/qt/qdbusintegrator.cpp
+++ b/qt/qdbusintegrator.cpp
@@ -1,6 +1,8 @@
/* qdbusintegrator.cpp QDBusConnection private implementation
*
* Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org>
+ * Copyright (C) 2006 Trolltech AS. All rights reserved.
+ * Author: Thiago Macieira <thiago.macieira@trolltech.com>
*
* Licensed under the Academic Free License version 2.1
*
@@ -15,22 +17,54 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, write to the Free Software Foundation
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qcoreevent.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qmetaobject.h>
-#include <QtCore/qsocketnotifier.h>
+#include <qcoreapplication.h>
+#include <qcoreevent.h>
+#include <qdebug.h>
+#include <qmetaobject.h>
+#include <qsocketnotifier.h>
+#include <qcoreevent.h>
+#include <qtimer.h>
+#include "qdbusvariant.h"
#include "qdbusconnection_p.h"
+#include "qdbusinterface_p.h"
+#include "qdbusobject_p.h"
#include "qdbusmessage.h"
+#include "qdbusabstractadaptor.h"
int QDBusConnectionPrivate::messageMetaType = 0;
+struct QDBusPendingCall
+{
+ QPointer<QObject> receiver;
+ QList<int> metaTypes;
+ int methodIdx;
+ DBusPendingCall *pending;
+ const QDBusConnectionPrivate *connection;
+};
+
+class CallDeliveryEvent: public QEvent
+{
+public:
+ CallDeliveryEvent()
+ : QEvent(QEvent::User), object(0), flags(0), slotIdx(-1)
+ { }
+
+ const QDBusConnectionPrivate *conn;
+ QPointer<QObject> object;
+ QDBusMessage message;
+ QList<int> metaTypes;
+
+ int flags;
+ int slotIdx;
+ bool generateReply : 1;
+};
+
static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
{
Q_ASSERT(timeout);
@@ -103,6 +137,7 @@ static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
watcher.watch = watch;
if (QCoreApplication::instance()) {
watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d);
+ watcher.read->setEnabled(dbus_watch_get_enabled(watch));
d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int)));
}
}
@@ -111,6 +146,7 @@ static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
watcher.watch = watch;
if (QCoreApplication::instance()) {
watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d);
+ watcher.write->setEnabled(dbus_watch_get_enabled(watch));
d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int)));
}
}
@@ -188,168 +224,560 @@ static DBusHandlerResult qDBusSignalFilter(DBusConnection *connection,
bool handled = false;
QDBusMessage amsg = QDBusMessage::fromDBusMessage(message);
- qDebug() << "got message: " << dbus_message_get_type(message) << amsg;
+ qDebug() << "got message:" << amsg;
if (msgType == DBUS_MESSAGE_TYPE_SIGNAL) {
- handled = d->handleSignal(message);
+ handled = d->handleSignal(amsg);
} else if (msgType == DBUS_MESSAGE_TYPE_METHOD_CALL) {
- handled = d->handleObjectCall(message);
+ handled = d->handleObjectCall(amsg);
}
return handled ? DBUS_HANDLER_RESULT_HANDLED :
DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
-static bool qInvokeDBusSlot(const QDBusConnectionPrivate::SignalHook& hook, const QDBusMessage &msg)
+static bool checkAsyncTag(const char *tag)
{
- int count = msg.count();
- if (!(count == hook.params.count()
- || (count + 1 == hook.params.count()
- && hook.params[count] == QDBusConnectionPrivate::messageMetaType)))
+ if (!tag || !*tag)
return false;
- QVarLengthArray<void *, 16> params;
- params.append(0); // return value
- for (int i = 0; i < msg.count(); ++i) {
- const QVariant &v = msg.at(i);
- if (int(v.type()) != hook.params[i]) {
- return false;
- }
- params.append(const_cast<void *>(v.constData()));
+ const char *p = strstr(tag, "async");
+ if (p != NULL &&
+ (p == tag || *(p-1) == ' ') &&
+ (p[6] == '\0' || p[6] == ' '))
+ return true;
+
+ p = strstr(tag, "Q_ASYNC");
+ if (p != NULL &&
+ (p == tag || *(p-1) == ' ') &&
+ (p[8] == '\0' || p[8] == ' '))
+ return true;
+
+ return false;
+}
+
+static QList<QByteArray> splitParameters(const char *p)
+{
+ QList<QByteArray> retval;
+ ++p;
+ const char *e = p;
+ while (*e != ')') {
+ while (*e != ')' && *e != ',')
+ ++e;
+
+ // found the end of this parameter
+ retval += QByteArray(p, e - p);
+
+ if (*e != ')')
+ p = ++e;
}
- if (count + 1 == hook.params.count())
- params.append(const_cast<QDBusMessage *>(&msg));
- return hook.obj->qt_metacall(QMetaObject::InvokeMetaMethod, hook.midx, params.data()) < 0;
+ return retval;
}
-static bool qInvokeDBusSlot(QObject *object, int idx, const QDBusMessage &msg)
+static bool typesMatch(int metaId, QVariant::Type variantType)
{
- Q_ASSERT(object);
+ if (metaId == (int)variantType)
+ return true;
- const QMetaMethod method = object->metaObject()->method(idx);
- if (!method.signature())
- return false;
+ if (variantType == QVariant::Int && metaId == QMetaType::Short)
+ return true;
- QVarLengthArray<void *> params;
- params.append(0); // ### return type
+ if (variantType == QVariant::UInt && (metaId == QMetaType::UShort ||
+ metaId == QMetaType::UChar))
+ return true;
- QList<QByteArray> parameterTypes = method.parameterTypes();
+ return false; // no match
+}
- // check parameters, the slot should have <= parameters than the message
- // also allow the QDBusMessage itself as last parameter slot
- if ((parameterTypes.count() > msg.count())
- || (parameterTypes.count() + 1 != msg.count())
- && parameterTypes.last() != "QDBusMessage") {
- qWarning("Cannot deliver asynchronous reply to object named '%s' because of parameter "
- "mismatch. Please check your sendWithReplyAsync() statements.",
- object->objectName().toLocal8Bit().constData());
- return false;
+static int returnTypeId(const char *name)
+{
+ if (!name || !*name)
+ return QMetaType::Void;
+
+ // force normalizedSignature to work for us
+ QVarLengthArray<char, 32> buf(strlen(name) + 3);
+ buf.append("_(", 2);
+ buf.append(name, strlen(name));
+ buf.append(')');
+
+ QByteArray normalized = QMetaObject::normalizedSignature( buf.data() );
+ normalized.truncate(normalized.length() - 1);
+ return QMetaType::type(normalized.constData() + 2);
+}
+
+static int typeId(const char *type)
+{
+ int id = static_cast<int>( QVariant::nameToType(type) );
+ if (id == QVariant::UserType)
+ id = QMetaType::type(type);
+
+ switch (id) {
+ case QVariant::Bool:
+ case QVariant::Int:
+ case QVariant::UInt:
+ case QVariant::Char:
+ case QMetaType::Short:
+ case QMetaType::UShort:
+ case QMetaType::UChar:
+ case QVariant::LongLong:
+ case QVariant::ULongLong:
+ case QVariant::Double:
+ case QVariant::String:
+ case QVariant::Date:
+ case QVariant::Time:
+ case QVariant::DateTime:
+ case QVariant::Map:
+ case QVariant::StringList:
+ case QVariant::ByteArray:
+ case QVariant::List:
+ return id;
+
+ default:
+ if (id == qMetaTypeId<QDBusVariant>() || id == QDBusConnectionPrivate::messageMetaType)
+ return id;
+
+ return 0; // invalid
+ }
+}
+
+
+// calculates the metatypes for the method
+// the slot must have the parameters in the following form:
+// - zero or more value or const-ref parameters of any kind
+// - zero or one const ref of QDBusMessage
+// - zero or more non-const ref parameters
+// No parameter may be a template.
+// this function returns -1 if the parameters don't match the above form
+// this function returns the number of *input* parameters, including the QDBusMessage one if any
+// this function does not check the return type, so metaTypes[0] is always 0 and always present
+// metaTypes.count() >= retval + 1 in all cases
+//
+// sig must be the normalised signature for the method
+static int parametersForMethod(const QByteArray &sig, QList<int>& metaTypes)
+{
+ if (sig.indexOf('<') != -1) {
+ qWarning("Could not parse the method '%s'", sig.constData());
+ // there's no type with templates that we can handle
+ return -1;
}
- int i;
- for (i = 0; i < parameterTypes.count(); ++i) {
- const QByteArray param = parameterTypes.at(i);
- if (param == msg.at(i).typeName()) {
- params.append(const_cast<void *>(msg.at(i).constData()));
- } else if (i == parameterTypes.count() - 1 && param == "QDBusMessage") {
- params.append(const_cast<void *>(static_cast<const void *>(&msg)));
- } else {
- qWarning("Parameter mismatch while delivering message, expected '%s', got '%s'",
- msg.at(i).typeName(), param.constData());
- return false;
+ int paren = sig.indexOf('(');
+ QList<QByteArray> parameterTypes = splitParameters(sig.data() + paren);
+ metaTypes.clear();
+
+ metaTypes.append(0); // return type
+ int inputCount = 0;
+ bool seenMessage = false;
+ foreach (QByteArray type, parameterTypes) {
+ if (type.endsWith('*')) {
+ qWarning("Could not parse the method '%s'", sig.constData());
+ // pointer?
+ return -1;
+ }
+
+ if (type.endsWith('&')) {
+ type.truncate(type.length() - 1);
+ int id = typeId(type);
+ if (id == 0) {
+ qWarning("Could not parse the method '%s'", sig.constData());
+ // invalid type in method parameter list
+ return -1;
+ }
+
+ metaTypes.append( id );
+
+ if (metaTypes.last() == 0) {
+ qWarning("Could not parse the method '%s'", sig.constData());
+ // void?
+ return -1;
+ }
+
+ continue;
+ }
+
+ if (seenMessage) { // && !type.endsWith('&')
+ qWarning("Could not parse the method '%s'", sig.constData());
+ // non-output parameters after message or after output params
+ return -1; // not allowed
+ }
+
+ int id = typeId(type);
+ if (id == 0) {
+ qWarning("Could not parse the method '%s'", sig.constData());
+ // invalid type in method parameter list
+ return -1;
}
+ metaTypes.append(id);
+ ++inputCount;
+
+ if (id == QDBusConnectionPrivate::messageMetaType)
+ seenMessage = true;
}
- return object->qt_metacall(QMetaObject::InvokeMetaMethod, idx, params.data()) < 0;
+
+ return inputCount;
}
-static bool qInvokeDBusSlot(QObject *object, QDBusMessage *msg)
+static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags,
+ const QDBusTypeList &types, QList<int>& metaTypes, bool &isAsync, int &msgPos)
+{
+ // find the first slot
+ const QMetaObject *super = mo;
+ while (qstrcmp(super->className(), "QObject") != 0 &&
+ qstrcmp(super->className(), "QDBusAbstractAdaptor") != 0)
+ super = super->superClass();
+
+ int attributeMask = (flags & QDBusConnection::ExportNonScriptableSlots) ?
+ 0 : QMetaMethod::Scriptable;
+
+ for (int idx = super->methodCount() ; idx <= mo->methodCount(); ++idx) {
+ QMetaMethod mm = mo->method(idx);
+
+ // check access:
+ if (mm.access() != QMetaMethod::Public)
+ continue;
+
+ // check type:
+ // unnecessary, since slots are never public:
+ //if (mm.methodType() != QMetaMethod::Slot)
+ // continue;
+
+ // check name:
+ QByteArray sig = QMetaObject::normalizedSignature(mm.signature());
+ int paren = sig.indexOf('(');
+ if (paren != name.length() || !sig.startsWith( name ))
+ continue;
+
+ int returnType = returnTypeId(mm.typeName());
+ isAsync = checkAsyncTag(mm.tag());
+
+ // consistency check:
+ if (isAsync && returnType != QMetaType::Void)
+ continue;
+
+ int inputCount = parametersForMethod(sig, metaTypes);
+ if (inputCount == -1)
+ continue; // problem parsing
+
+ metaTypes[0] = returnType;
+ msgPos = 0;
+ if (inputCount > 0 &&
+ metaTypes.at(inputCount) == QDBusConnectionPrivate::messageMetaType) {
+ // no input parameters is allowed as long as the message meta type is there
+ msgPos = inputCount;
+ --inputCount;
+ }
+
+ if (inputCount) {
+ // try to match the parameters
+ if (inputCount < types.count())
+ continue; // not enough parameters
+
+ bool matches = true;
+ int i;
+ for (i = 0; i < types.count(); ++i)
+ if ( !typesMatch(metaTypes.at(i + 1), types.at(i).qvariantType()) ) {
+ matches = false;
+ break;
+ }
+
+ if (!matches)
+ continue; // we didn't match them all
+
+ // consistency check:
+ if (isAsync && metaTypes.count() > i + 1)
+ continue;
+ }
+
+ if (!msgPos && (mm.attributes() & attributeMask) != attributeMask)
+ continue; // not exported
+
+ // if we got here, this slot matched
+ return idx;
+ }
+
+ // no slot matched
+ return -1;
+}
+
+bool QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook,
+ const QDBusMessage &msg)
{
+ // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal
+ // that was received from D-Bus
+ //
+ // Signals are delivered to slots if the parameters match
+ // Slots can have less parameters than there are on the message
+ // Slots can optionally have one final parameter that is a QDBusMessage
+ // Slots receive read-only copies of the message (i.e., pass by value or by const-ref)
+ return activateReply(hook.obj, hook.midx, hook.params, msg);
+}
+
+bool QDBusConnectionPrivate::activateReply(QObject *object, int idx, const QList<int> &metaTypes,
+ const QDBusMessage &msg)
+{
+ // This is called by qDBusResultReceived and is used to deliver the return value
+ // of a remote function call.
+ //
+ // There is only one connection and it is specified by idx
+ // The slot must have the same parameter types that the message does
+ // The slot may have less parameters than the message
+ // The slot may optionally have one final parameter that is QDBusMessage
+ // The slot receives read-only copies of the message (i.e., pass by value or by const-ref)
Q_ASSERT(object);
- Q_ASSERT(msg);
- const QMetaObject *mo = object->metaObject();
- QVarLengthArray<void *> params;
- params.append(0); // ### return type
+ int n = metaTypes.count() - 1;
+ if (metaTypes[n] == QDBusConnectionPrivate::messageMetaType)
+ --n;
+
+ // check that types match
+ for (int i = 0; i < n; ++i)
+ if (!typesMatch(metaTypes.at(i + 1), msg.at(i).type()))
+ return false; // no match
+
+ // we can deliver
+ // prepare for the call
+ CallDeliveryEvent *data = new CallDeliveryEvent;
+ data->conn = this;
+ data->object = object;
+ data->flags = 0;
+ data->message = msg;
+ data->metaTypes = metaTypes;
+ data->slotIdx = idx;
+ data->generateReply = false;
+
+ QCoreApplication::postEvent( this, data );
+
+ return true;
+}
+
+bool QDBusConnectionPrivate::activateCall(QObject* object, int flags,
+ const QDBusMessage &msg)
+{
+ // This is called by QDBusConnectionPrivate::handleObjectCall to place a call
+ // to a slot on the object.
+ //
+ // The call is delivered to the first slot that matches the following conditions:
+ // - has the same name as the message's target name
+ // - ALL of the message's types are found in slot's parameter list
+ // - optionally has one more parameter of type QDBusMessage
+ // If none match, then the slot of the same name as the message target and with
+ // the first type of QDBusMessage is delivered.
+ //
+ // Because the marshalling of D-Bus data into QVariant loses the information on
+ // the original types, the message signature is used to determine the original type.
+ // Aside from that, the "int" and "unsigned" types will be tried as well.
+ //
+ // Return message handling depends on whether the asynchronous tag ("async" or "Q_ASYNC")
+ // tag is found, whether the slot takes a QDBusMessage parameter and whether there are
+ // return values (non-const reference parameters or a return type).
+ // The table indicates the possibilities:
+ // async QDBusMessage parameter return values return message generated
+ // yes irrelevant irrelevant no
+ // no irrelevant yes yes
+ // no yes no no
+ // no no no yes
+ //
+ // When a return message is generated, the slot's return type, if any, will be placed
+ // in the message's first position. If there are non-const reference parameters to the
+ // slot, they must appear at the end and will be placed in the subsequent message
+ // positions.
+
+ Q_ASSERT(object);
- /* Try to find a slot with all args and the QDBusMessage */
- QByteArray slotName = msg->name().toUtf8(); // QVarLengthArray?
- slotName.append("(");
- for (int i = 0; i < msg->count(); ++i) {
- slotName.append(msg->at(i).typeName()).append(",");
- params.append(const_cast<void *>(msg->at(i).constData()));
+ QList<int> metaTypes;
+ int idx;
+ bool isAsync;
+ int msgPos;
+
+ {
+ const QMetaObject *mo = object->metaObject();
+ QDBusTypeList typeList(msg.signature().toUtf8());
+
+ // find a slot that matches according to the rules above
+ idx = ::findSlot(mo, msg.name().toUtf8(), flags, typeList, metaTypes, isAsync, msgPos);
+ if (idx == -1)
+ // no match
+ return false;
}
- slotName.append("QDBusMessage)");
- int idx = mo->indexOfSlot(slotName.constData());
- if (idx >= 0) {
- params.append(msg);
- return object->qt_metacall(QMetaObject::InvokeMetaMethod, idx, params.data()) < 0;
+ bool generateReply;
+ if (isAsync)
+ generateReply = false;
+ else if (metaTypes[0] != QMetaType::Void)
+ generateReply = true;
+ else {
+ if (msgPos != 0)
+ // generate a reply if there are more parameters past QDBusMessage
+ generateReply = metaTypes.count() > msgPos + 1;
+ else
+ // generate a reply if there are more parameters than input parameters
+ generateReply = metaTypes.count() > msg.count() + 1;
}
- /* Try to find only args, without the QDBusMessage */
- slotName.chop(13);
- slotName[slotName.count() - 1] = ')';
+ // found the slot to be called
+ // prepare for the call:
+ CallDeliveryEvent *call = new CallDeliveryEvent;
+ call->conn = this;
- idx = mo->indexOfSlot(slotName.constData());
- if (idx >= 0 && (mo->method(idx).attributes() & QMetaMethod::Scriptable))
- return object->qt_metacall(QMetaObject::InvokeMetaMethod, idx, params.data()) < 0;
+ // parameters:
+ call->object = object;
+ call->flags = flags;
+ call->message = msg;
- /* Try to find a slot with only QDBusMessage */
- slotName = msg->name().toUtf8();
- slotName.append("(QDBusMessage)");
+ // save our state:
+ call->metaTypes = metaTypes;
+ call->slotIdx = idx;
+ call->generateReply = generateReply;
- idx = mo->indexOfSlot(slotName.constData());
- if (idx >= 0)
- return QMetaObject::invokeMethod(object, msg->name().toUtf8().constData(),
- Q_ARG(QDBusMessage, *msg));
+ QCoreApplication::postEvent( this, call );
- return false;
+ // ready
+ return true;
}
-int QDBusConnectionPrivate::registerMessageMetaType()
+void QDBusConnectionPrivate::deliverCall(const CallDeliveryEvent& data) const
{
- int tp = messageMetaType = qRegisterMetaType<QDBusMessage>("QDBusMessage");
- return tp;
-}
+ // resume state:
+ const QList<int>& metaTypes = data.metaTypes;
+ const QDBusMessage& msg = data.message;
+
+ QVarLengthArray<void *, 10> params;
+ params.reserve(metaTypes.count());
+
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+ union integer
+ {
+ short s;
+ unsigned short us;
+ unsigned char uc;
+ }
+ QVarLengthArray<integer, 4> auxParameters;
+#endif
+ // let's create the parameter list
-bool QDBusConnectionPrivate::SignalHook::setSlot(const char *slotName)
-{
- Q_ASSERT(static_cast<QObject *>(obj)); Q_ASSERT(slotName);
+ // first one is the return type -- add it below
+ params.append(0);
- QByteArray normalizedName = QMetaObject::normalizedSignature(slotName);
- const QMetaObject *mo = obj->metaObject();
- midx = mo->indexOfMethod(normalizedName.constData());
- if (midx < 0)
- return false;
+ // add the input parameters
+ int i;
+ for (i = 0; i < msg.count(); ++i) {
+ int id = metaTypes[i + 1];
+ if (id == QDBusConnectionPrivate::messageMetaType)
+ break;
- const QList<QByteArray> ptypes = mo->method(midx).parameterTypes();
- for (int i = 0; i < ptypes.count(); ++i) {
- int t = QVariant::nameToType(ptypes.at(i).constData());
- if (t == QVariant::UserType)
- t = QMetaType::type(ptypes.at(i).constData());
- if (t == QVariant::Invalid)
- return false;
- params.append(t);
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ params.append(const_cast<void *>( msg.at(i).constData() ));
+#else
+ if (id == msg.at(i).type())
+ params.append(const_cast<void *>( msg.at(i).constData() ));
+ else {
+ // need some help
+ integer aux;
+ const QVariant &var = msg.at(i);
+ if (id == QMetaType::Short)
+ aux.s = var.toInt();
+ else if (id == QMetaType::UShort)
+ aux.us = var.toUInt();
+ else
+ aux.uc = var.toUInt();
+ auxParameters.append(aux);
+ params.append( &auxParameters[auxParameters.count()] );
+ }
+#endif
}
- return true;
+ bool takesMessage = false;
+ if (metaTypes.count() > i + 1 && metaTypes[i + 1] == QDBusConnectionPrivate::messageMetaType) {
+ params.append(const_cast<void*>(static_cast<const void*>(&msg)));
+ takesMessage = true;
+ ++i;
+ }
+
+ // output arguments
+ QVariantList outputArgs;
+ void *null = 0;
+ if (metaTypes[0] != QMetaType::Void) {
+ QVariant arg(metaTypes[0], null);
+ params.append( arg.data() );
+ outputArgs.append( arg );
+ }
+ for ( ; i < metaTypes.count(); ++i) {
+ QVariant arg(metaTypes[i], null);
+ params.append( arg.data() );
+ outputArgs.append( arg );
+ }
+
+ // make call:
+ bool fail;
+ if (data.object.isNull())
+ fail = true;
+ else
+ fail = data.object->qt_metacall(QMetaObject::InvokeMetaMethod,
+ data.slotIdx, params.data()) >= 0;
+
+ // do we create a reply?
+ if (data.generateReply) {
+ if (!fail) {
+ // yes
+ QDBusMessage reply = QDBusMessage::methodReply(msg);
+ reply += outputArgs;
+
+ qDebug() << "Automatically sending reply:" << reply;
+ send(reply);
+ }
+ else {
+ // generate internal error
+ QDBusMessage reply = QDBusMessage::error(msg, "com.trolltech.QtDBus.InternalError",
+ "Failed to deliver message");
+ qDebug("Internal error: Failed to deliver message");
+ send(reply);
+ }
+ }
+
+ return;
+}
+
+void QDBusConnectionPrivate::customEvent(QEvent *event)
+{
+ // nothing else should be sending custom events at us
+ CallDeliveryEvent* call = static_cast<CallDeliveryEvent *>(event);
+
+ // self check:
+ Q_ASSERT(call->conn == this);
+
+ deliverCall(*call);
}
QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *parent)
: QObject(parent), ref(1), mode(InvalidMode), connection(0), server(0)
{
+ extern bool qDBusInitThreads();
static const int msgType = registerMessageMetaType();
+ static const bool threads = qDBusInitThreads();
+
Q_UNUSED(msgType);
+ Q_UNUSED(threads);
dbus_error_init(&error);
}
QDBusConnectionPrivate::~QDBusConnectionPrivate()
{
+ Q_ASSERT(knownObjects.isEmpty());
+
if (dbus_error_is_set(&error))
dbus_error_free(&error);
closeConnection();
+
+ KnownInterfacesHash::iterator it = knownInterfaces.begin();
+ while (it != knownInterfaces.end()) {
+ const QSharedDataPointer<QDBusIntrospection::Interface>& item = *it;
+
+ const_cast<QDBusIntrospection::Interface*>(item.constData())->ref.deref();
+
+ it = knownInterfaces.erase(it);
+ }
}
void QDBusConnectionPrivate::closeConnection()
@@ -413,7 +841,6 @@ void QDBusConnectionPrivate::socketRead(int fd)
}
if (mode == ClientMode)
while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS);
- // ### break out of loop?
}
void QDBusConnectionPrivate::socketWrite(int fd)
@@ -432,11 +859,20 @@ void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
{
ObjectHookHash::iterator it = objectHooks.begin();
while (it != objectHooks.end()) {
- if (static_cast<QObject *>(it.value().obj) == obj)
+ ObjectDataHash::iterator dit = it->begin();
+ while (dit != it->end()) {
+ if (static_cast<QObject *>(dit.value().obj) == obj)
+ dit = it->erase(dit);
+ else
+ ++dit;
+ }
+
+ if (it->isEmpty())
it = objectHooks.erase(it);
else
++it;
}
+
SignalHookHash::iterator sit = signalHooks.begin();
while (sit != signalHooks.end()) {
if (static_cast<QObject *>(sit.value().obj) == obj)
@@ -447,56 +883,135 @@ void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
obj->disconnect(this);
}
-bool QDBusConnectionPrivate::handleObjectCall(DBusMessage *message) const
+bool QDBusConnectionPrivate::activateAdaptor(QObject* object, int flags,
+ const QDBusMessage &msg)
{
- QDBusMessage msg = QDBusMessage::fromDBusMessage(message);
+ // This is called by QDBusConnectionPrivate::handleObjectCall to place a call to a slot
+ // on the object.
+ //
+ // The call is routed through the adaptor sub-objects
- ObjectHook hook;
- ObjectHookHash::ConstIterator it = objectHooks.find(msg.path());
- while (it != objectHooks.constEnd() && it.key() == msg.path()) {
- if (it.value().interface == msg.interface()) {
- hook = it.value();
- break;
- } else if (it.value().interface.isEmpty()) {
- hook = it.value();
+ Q_ASSERT(object);
+ flags |= QDBusConnection::ExportNonScriptableSlots;
+
+ const QObjectList& children = object->children();
+ QObjectList::const_iterator child = children.begin(),
+ end = children.end();
+ for ( ; child != end; ++child) {
+ // check if this is an adaptor
+ if (!qobject_cast<QDBusAbstractAdaptor *>(*child))
+ continue; // not adaptor
+
+ const QMetaObject *mo = (*child)->metaObject();
+ int ciend = mo->classInfoCount();
+ for (int i = 0; i < ciend; ++i) {
+ QMetaClassInfo mci = mo->classInfo(i);
+ if (strcmp(mci.name(), "DBus Interface") == 0 && *mci.value()) {
+ // one interface.
+ // is this it?
+ if (msg.interface().isEmpty() || msg.interface() == mci.value())
+ return activateCall(*child, flags, msg);
+ }
}
- ++it;
}
+ return false;
+}
- if (!hook.obj) {
- qDebug("NO OBJECT for %s", msg.path().toLocal8Bit().constData());
- return false;
+int QDBusConnectionPrivate::registerMessageMetaType()
+{
+ int tp = messageMetaType = qRegisterMetaType<QDBusMessage>("QDBusMessage");
+ return tp;
+}
+
+int QDBusConnectionPrivate::findSlot(QObject* obj, const char *slotName, QList<int>& params)
+{
+ Q_ASSERT(slotName);
+ QByteArray normalizedName = QMetaObject::normalizedSignature(slotName);
+ int midx = obj->metaObject()->indexOfMethod(normalizedName);
+ if (midx == -1) {
+ qWarning("No such slot '%s' while connecting D-Bus", slotName);
+ return -1;
}
- if (!qInvokeDBusSlot(hook.obj, &msg)) {
- qDebug("NO SUCH SLOT: %s(QDBusMessage)", msg.name().toLocal8Bit().constData());
+ int inputCount = parametersForMethod(normalizedName, params);
+ if ( inputCount == -1 || inputCount + 1 != params.count() )
+ return -1; // failed to parse or invalid arguments or output arguments
+
+ return midx;
+}
+
+bool QDBusConnectionPrivate::activateObject(const QDBusConnectionPrivate::ObjectData &hook,
+ const QDBusMessage &msg)
+{
+ if (!hook.obj)
+ return false; // object is gone
+
+ if (hook.flags & QDBusConnection::ExportAdaptors)
+ return activateAdaptor(hook.obj, hook.flags, msg);
+ else
+ return activateCall(hook.obj, hook.flags, msg);
+}
+
+bool QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg)
+{
+ ObjectHookHash::ConstIterator it = objectHooks.find(msg.path());
+ if (it == objectHooks.constEnd())
return false;
+
+ bool ok = false;
+ const ObjectDataHash& hook = it.value();
+ ObjectDataHash::ConstIterator hit;
+ if (msg.interface().isEmpty()) {
+ // we must go through all the objects and interfaces
+
+ for (hit = hook.begin(); hit != hook.end(); ++hit) {
+ ok = activateObject(hit.value(), msg);
+ if (ok)
+ break; // processed
+ }
+ } else {
+ // find the interface:
+ hit = hook.find(msg.interface());
+ if (hit != hook.end())
+ ok = activateObject(hit.value(), msg);
+
+ if (!ok) {
+ // try adaptors (or any interface)
+ hit = hook.find(QString());
+ if (hit != hook.end())
+ ok = activateObject(hit.value(), msg);
+ }
}
- return true;
+ qDebug(ok ? "Call scheduled" : "Call failed");
+ return ok;
}
-bool QDBusConnectionPrivate::handleSignal(const QString &path, const QDBusMessage &msg) const
+bool QDBusConnectionPrivate::handleSignal(const QString &path, const QDBusMessage &msg)
{
SignalHookHash::const_iterator it = signalHooks.find(path);
qDebug("looking for: %s", path.toLocal8Bit().constData());
qDebug() << signalHooks.keys();
- while (it != signalHooks.constEnd() && it.key() == path) {
+ for ( ; it != signalHooks.constEnd() && it.key() == path; ++ it) {
const SignalHook &hook = it.value();
- if ((hook.name.isEmpty() || hook.name == msg.name())
- && (hook.interface.isEmpty() || hook.interface == msg.interface()))
- qInvokeDBusSlot(hook, msg);
- ++it;
+ if ( hook.obj.isNull() )
+ continue;
+ if ( !hook.name.isEmpty() && hook.name != msg.name() )
+ continue;
+ if ( !hook.interface.isEmpty() && hook.interface != msg.interface() )
+ continue;
+ if ( !hook.signature.isEmpty() && hook.signature != msg.signature() )
+ continue;
+
+ activateSignal(hook, msg);
}
return true;
}
-bool QDBusConnectionPrivate::handleSignal(DBusMessage *message) const
+bool QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg)
{
- QDBusMessage msg = QDBusMessage::fromDBusMessage(message);
-
// yes, it is a single "|" below...
- return handleSignal(QString(), msg) | handleSignal(msg.path(), msg);
+ return handleSignal(QString(), msg) | handleSignal(msg.sender() + msg.path(), msg);
}
static dbus_int32_t server_slot = -1;
@@ -569,53 +1084,152 @@ void QDBusConnectionPrivate::setConnection(DBusConnection *dbc)
qDebug("base service: %s", service);
}
-struct QDBusPendingCall
-{
- QPointer<QObject> receiver;
- int methodIdx;
- DBusPendingCall *pending;
-};
-
static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
{
QDBusPendingCall *call = reinterpret_cast<QDBusPendingCall *>(user_data);
+ QDBusConnectionPrivate *connection = const_cast<QDBusConnectionPrivate *>(call->connection);
Q_ASSERT(call->pending == pending);
if (!call->receiver.isNull() && call->methodIdx != -1) {
DBusMessage *reply = dbus_pending_call_steal_reply(pending);
- qInvokeDBusSlot(call->receiver, call->methodIdx, QDBusMessage::fromDBusMessage(reply));
+ connection->activateReply(call->receiver, call->methodIdx, call->metaTypes,
+ QDBusMessage::fromDBusMessage(reply));
}
dbus_pending_call_unref(pending);
delete call;
}
+bool QDBusConnectionPrivate::send(const QDBusMessage& message) const
+{
+ DBusMessage *msg = message.toDBusMessage();
+ if (!msg)
+ return false;
+
+ dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
+
+ qDebug() << "sending message:" << message;
+ bool isOk = dbus_connection_send(connection, msg, 0);
+ dbus_message_unref(msg);
+ return isOk;
+}
+
int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
- const char *method) const
+ const char *method) const
{
DBusMessage *msg = message.toDBusMessage();
if (!msg)
return 0;
int slotIdx = -1;
- if (receiver && method && *method) {
- QByteArray normalized = QMetaObject::normalizedSignature(method + 1);
- slotIdx = receiver->metaObject()->indexOfMethod(normalized.constData());
- if (slotIdx == -1)
- qWarning("QDBusConnection::sendWithReplyAsync: no such method: '%s'",
- normalized.constData());
- }
+ QList<int> metaTypes;
+ if (receiver && method && *method)
+ slotIdx = findSlot(receiver, method + 1, metaTypes);
+ qDebug() << "sending message:" << message;
DBusPendingCall *pending = 0;
if (dbus_connection_send_with_reply(connection, msg, &pending, message.timeout())) {
if (slotIdx != -1) {
QDBusPendingCall *pcall = new QDBusPendingCall;
pcall->receiver = receiver;
+ pcall->metaTypes = metaTypes;
pcall->methodIdx = slotIdx;
+ pcall->connection = this;
pcall->pending = dbus_pending_call_ref(pending);
dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, 0);
}
+ dbus_pending_call_unref(pending);
return dbus_message_get_serial(msg);
}
return 0;
}
+
+QSharedDataPointer<QDBusIntrospection::Interface>
+QDBusConnectionPrivate::findInterface(const QString& name)
+{
+ QMutexLocker locker(&mutex);
+ QSharedDataPointer<QDBusIntrospection::Interface> data = knownInterfaces.value(name);
+ if (!data) {
+ data = new QDBusIntrospection::Interface;
+ data->name = name;
+ data->ref.ref(); // don't delete
+
+ knownInterfaces.insert(name, data);
+ }
+ return data;
+}
+
+QDBusIntrospection::Object*
+QDBusConnectionPrivate::findObject(const QString& service, const QString& path)
+{
+ QMutexLocker locker(&mutex);
+ QDBusIntrospection::Object* data = knownObjects.value(service + path);
+ if (!data) {
+ data = new QDBusIntrospection::Object;
+ data->service = service;
+ data->path = path;
+
+ knownObjects.insert(service + path, data);
+ }
+
+ return data;
+}
+
+void QDBusConnectionPrivate::disposeOfLocked(QDBusIntrospection::Object* p)
+{
+ if (p && !p->ref.deref()) { // ref--
+ // no one else is using it
+ // get rid of the reference
+ QString objName = p->service + p->path;
+
+#ifndef QT_NO_DEBUG
+ // debug code
+ Q_ASSERT(p == knownObjects.take(objName));
+#else
+ // non-debug
+ knownObjects.remove(objName);
+#endif
+
+ // remove sub-objects too
+ if (!objName.endsWith('/'))
+ objName.append('/');
+ foreach (QString subObjName, p->childObjects)
+ disposeOfLocked(knownObjects.value(objName + subObjName));
+
+ delete p;
+ }
+}
+
+void QDBusConnectionPrivate::disposeOf(QDBusObjectPrivate* p)
+{
+ // We're called from QDBusConnectionPrivate's destructor
+ // that means the object it represents is going out of scope
+
+ QMutexLocker locker(&mutex);
+ disposeOfLocked( const_cast<QDBusIntrospection::Object*>(p->data) );
+}
+
+#ifndef QT_NO_DEBUG
+int QDBusReplyWaiter::exec(QEventLoop::ProcessEventsFlags flags)
+{
+ static int eventlevel;
+ level = ++eventlevel;
+ qDebug("QDBusReplyWaiter::exec %p level %d starting", this, level);
+ int retcode = QEventLoop::exec(flags);
+ qDebug("QDBusReplyWaiter::exec %p level %d exiting", this, level);
+ --eventlevel;
+ return retcode;
+}
+
+void QDBusReplyWaiter::exit(int retcode)
+{
+ qDebug("QDBusReplyWaiter::exit %p level %d called", this, level);
+ QEventLoop::exit(retcode);
+}
+#endif
+
+void QDBusReplyWaiter::reply(const QDBusMessage &msg)
+{
+ replyMsg = msg;
+ QTimer::singleShot(0, this, SLOT(quit()));
+}