📄 qdbusintegrator.cpp
字号:
}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] == QDBusMetaTypeId::message) --n; // check that types match for (int i = 0; i < n; ++i) if (metaTypes.at(i + 1) != msg.arguments().at(i).userType() && msg.arguments().at(i).userType() != qMetaTypeId<QDBusArgument>()) 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 member // - 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. // // 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(); QByteArray memberName = msg.member().toUtf8(); // find a slot that matches according to the rules above idx = ::findSlot(mo, memberName, flags, msg.signature(), metaTypes); if (idx == -1) { // ### this is where we want to add the connection as an arg too // try with no parameters, but with a QDBusMessage idx = ::findSlot(mo, memberName, flags, QString(), metaTypes); if (metaTypes.count() != 2 || metaTypes.at(1) != QDBusMetaTypeId::message) { 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; if (QDBusMessagePrivate::isLocal(msg)) { //qDebug() << "QDBusConnectionPrivate::activateCall" << msg.d_ptr->msg; sendCallDeliveryEvent(call); } else { postCallDeliveryEvent(call); } // ready return true;}void QDBusConnectionPrivate::sendCallDeliveryEvent(CallDeliveryEvent *data){ Q_ASSERT(data); data->conn = this;#if USE_OUTSIDE_DISPATCH callDeliveryMutex.lock(); callDeliveryState = data;#else QCoreApplication::sendEvent(this, data);#endif}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 <= qMin(msg.arguments().count(), metaTypes.count() - 1); ++i) { int id = metaTypes[i]; if (id == QDBusMetaTypeId::message) break; if (id == int(msg.arguments().at(i - 1).userType())) // no conversion needed params.append(const_cast<void *>(msg.arguments().at(i - 1).constData() )); else if (msg.arguments().at(i - 1).userType() == qMetaTypeId<QDBusArgument>()) { // convert to what the function expects void *null = 0; auxParameters.append(QVariant(id, null)); const QDBusArgument &in = *reinterpret_cast<const QDBusArgument *>(msg.arguments().at(i - 1).constData()); QVariant &out = auxParameters[auxParameters.count() - 1]; if (!QDBusMetaType::demarshall(in, out.userType(), out.data())) qFatal("Internal error: demarshalling function for type '%s' (%d) failed!", out.typeName(), out.userType()); params.append(const_cast<void *>(out.constData()) ); } else { qFatal("Internal error: got invalid meta type %d when trying to convert to meta type %d", msg.arguments().at(i - 1).userType(), id); } } bool takesMessage = false; if (metaTypes.count() > i && metaTypes[i] == QDBusMetaTypeId::message) { 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 { QDBusConnectionPrivate::setSender(this); fail = data.object->qt_metacall(QMetaObject::InvokeMetaMethod, data.slotIdx, params.data()) >= 0; QDBusConnectionPrivate::setSender(0); } // do we create a reply? Only if the caller is waiting for a reply and one hasn't been sent // yet. if (QDBusMessagePrivate::isLocal(msg) && !fail) { // a little hack to enable local calls to return values //qDebug() << "QDBusConnectionPrivate::deliverCall" << outputArgs; QDBusMessagePrivate::setArguments(&msg, outputArgs); QDBusMessagePrivate::setType(&msg, QDBusMessage::ReplyMessage); return; } if (!msg.isReplyRequired() && !msg.isDelayedReply()) { if (!fail) { // normal reply qDBusDebug() << "Automatically sending reply:" << outputArgs; send(msg.createReply(outputArgs)); } else { // generate internal error qWarning("Internal error: Failed to deliver message"); send(QDBusMessage::createError(QDBusError(QDBusError::InternalError, QLatin1String("Failed to deliver message")))); } } return;}void QDBusConnectionPrivate::customEvent(QEvent *e){ // nothing else should be sending custom events at us CallDeliveryEvent* call = static_cast<CallDeliveryEvent *>(e); // self check: Q_ASSERT(call->conn == this); deliverCall(*call);}QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p) : QObject(p), ref(1), mode(InvalidMode), connection(0), server(0), busService(0){ extern bool qDBusInitThreads(); static const bool threads = qDBusInitThreads(); static const bool debugging = !qgetenv("QDBUS_DEBUG").isEmpty(); Q_UNUSED(threads); ::isDebugging = debugging; QDBusMetaTypeId::init(); dbus_error_init(&error); rootNode.flags = 0; connect(this, SIGNAL(serviceOwnerChanged(QString,QString,QString)), this, SLOT(_q_serviceOwnerChanged(QString,QString,QString)));}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 QMetaObject *mo, int signalId, const QVariantList &args){ int mciid = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE); Q_ASSERT(mciid != -1); QMetaClassInfo mci = mo->classInfo(mciid); Q_ASSERT(mci.value()); const char *interface = mci.value(); QMetaMethod mm = mo->method(signalId); QByteArray memberName = mm.signature(); memberName.truncate(memberName.indexOf('(')); // check if it's scriptable bool isScriptable = mm.attributes() & QMetaMethod::Scriptable; bool isAdaptor = false; for ( ; mo; mo = mo->superClass()) if (mo == &QDBusAbstractAdaptor::staticMetaObject) { isAdaptor = true; break; } QReadLocker locker(&lock);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -