File indexing completed on 2024-04-21 16:19:52
0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0002 // SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org> 0003 0004 #include "dbusobjectmanagerserver.h" 0005 0006 #include <QDBusConnection> 0007 #include <QDBusConnectionInterface> 0008 #include <QDBusMetaType> 0009 0010 #include "kded_debug.h" 0011 0012 // QtDBus doesn't implement PropertiesChanged for some reason. 0013 // This is meant to be childed' on an object to track that 0014 // objects' property notifications. 0015 class KDBusPropertiesChangedAdaptor : public QObject 0016 { 0017 Q_OBJECT 0018 public: 0019 KDBusPropertiesChangedAdaptor(const QString &objectPath, QObject *adaptee) 0020 : QObject(adaptee) 0021 , m_objectPath(objectPath) 0022 { 0023 auto mo = adaptee->metaObject(); 0024 for (int i = 0; i < mo->propertyCount(); ++i) { 0025 QMetaProperty property = mo->property(i); 0026 if (!property.hasNotifySignal()) { 0027 continue; 0028 } 0029 const int fooIndex = metaObject()->indexOfMethod("onPropertyChanged()"); // of adaptor 0030 Q_ASSERT(fooIndex != -1); 0031 connect(adaptee, property.notifySignal(), this, metaObject()->method(fooIndex)); 0032 } 0033 } 0034 0035 private Q_SLOTS: 0036 void onPropertyChanged() 0037 { 0038 if (!sender() || senderSignalIndex() == -1) { 0039 return; 0040 } 0041 auto mo = sender()->metaObject(); 0042 for (int i = 0; i < mo->propertyCount(); ++i) { 0043 QMetaProperty property = mo->property(i); 0044 if (!property.hasNotifySignal()) { 0045 continue; 0046 } 0047 if (property.notifySignalIndex() != senderSignalIndex()) { 0048 continue; 0049 } 0050 const int ciid = mo->indexOfClassInfo("D-Bus Interface"); 0051 if (ciid == -1) { 0052 continue; 0053 } 0054 QDBusMessage signal = QDBusMessage::createSignal(m_objectPath, // 0055 QStringLiteral("org.freedesktop.DBus.Properties"), 0056 QStringLiteral("PropertiesChanged")); 0057 signal << mo->classInfo(ciid).value(); 0058 signal << QVariantMap({{QString::fromLatin1(property.name()), property.read(sender())}}); // changed properties DICT<STRING,VARIANT> 0059 signal << QStringList(); // invalidated property names no clue what invalidation means 0060 QDBusConnection::sessionBus().send(signal); 0061 } 0062 } 0063 0064 private: 0065 const QString m_objectPath; 0066 }; 0067 0068 KDBusObjectManagerServer::KDBusObjectManagerServer(QObject *parent) 0069 : QObject(parent) 0070 { 0071 registerTypes(); 0072 QDBusConnection connection = QDBusConnection::sessionBus(); 0073 if (!connection.registerObject(m_path, 0074 this, 0075 QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals | QDBusConnection::ExportAllProperties 0076 | QDBusConnection::ExportAllInvokables | QDBusConnection::ExportAllContents | QDBusConnection::ExportAdaptors)) { 0077 qCDebug(KDED) << "failed to register" << m_path; 0078 return; 0079 } 0080 } 0081 0082 bool KDBusObjectManagerServer::serve(QObject *object) 0083 { 0084 m_managedObjects << object; 0085 Q_EMIT InterfacesAdded(path(object), interfacePropertiesMap(object)); 0086 connect(object, &QObject::destroyed, this, [this](QObject *obj) { 0087 unserve(obj); 0088 }); // auto-unserve 0089 const QString dbusPath = path(object).path(); 0090 new KDBusPropertiesChangedAdaptor(dbusPath, object); 0091 QDBusConnection connection = QDBusConnection::sessionBus(); 0092 return connection.registerObject(dbusPath, 0093 object, 0094 QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals | QDBusConnection::ExportAllProperties 0095 | QDBusConnection::ExportAllInvokables | QDBusConnection::ExportAllContents | QDBusConnection::ExportAdaptors); 0096 } 0097 0098 void KDBusObjectManagerServer::unserve(QObject *object) 0099 { 0100 const QStringList interfaces = metaObjectsFor(object).keys(); 0101 Q_EMIT InterfacesRemoved(path(object), {interfaces}); 0102 QDBusConnection::sessionBus().unregisterObject(path(object).path()); 0103 m_managedObjects.removeAll(object); 0104 } 0105 0106 void KDBusObjectManagerServer::registerTypes() 0107 { 0108 static bool registered = false; 0109 if (registered) { 0110 return; 0111 } 0112 registered = true; 0113 0114 qDBusRegisterMetaType<KDBusObjectManagerPropertiesMap>(); 0115 qDBusRegisterMetaType<KDBusObjectManagerInterfacePropertiesMap>(); 0116 qDBusRegisterMetaType<KDBusObjectManagerObjectPathInterfacePropertiesMap>(); 0117 0118 // For some reason we need to manually map the names. No idea why it works 0119 // for the maps though. 0120 qRegisterMetaType<KDBusObjectManagerInterfaceList>("KDBusObjectManagerInterfaceList"); 0121 qDBusRegisterMetaType<KDBusObjectManagerInterfaceList>(); 0122 } 0123 0124 KDBusObjectManagerObjectPathInterfacePropertiesMap KDBusObjectManagerServer::GetManagedObjects() 0125 { 0126 QMap<QDBusObjectPath, QMap<QString, QMap<QString, QVariant>>> map; 0127 for (const auto *object : m_managedObjects) { 0128 const QDBusObjectPath dbusPath = path(object); 0129 if (dbusPath.path().isEmpty()) { 0130 qCDebug(KDED) << "Invalid dbus path for" << object->objectName(); 0131 continue; 0132 } 0133 map[dbusPath] = interfacePropertiesMap(object); 0134 } 0135 return map; 0136 } 0137 0138 QDBusObjectPath KDBusObjectManagerServer::path(const QObject *object) 0139 { 0140 const QString path = m_path + "/" + object->objectName(); 0141 0142 qCDebug(KDED) << "path for " << object->objectName() << object->metaObject()->className() << ":" << path; 0143 return QDBusObjectPath(path); 0144 } 0145 0146 KDBusObjectManagerInterfacePropertiesMap KDBusObjectManagerServer::interfacePropertiesMap(const QObject *child) 0147 { 0148 // Technically this is duplicating qdbus' internal GetAll implementation, so we could actually 0149 // shoot dbus queries to ourself and have Qt figure things out. All we need to do is 0150 // compose it into the dbuspath tree we manage. 0151 0152 QMap<QString, QVariantMap> interfaceMap; 0153 0154 const auto metaObjectHash = metaObjectsFor(child); 0155 for (auto it = metaObjectHash.cbegin(); it != metaObjectHash.cend(); ++it) { 0156 const QString interface = it.key(); 0157 const QMetaObject *mo = it.value(); 0158 QVariantMap properties; 0159 for (int i = mo->propertyOffset(); i < mo->propertyCount(); ++i) { 0160 auto property = mo->property(i); 0161 properties[property.name()] = property.read(child); 0162 } 0163 interfaceMap[interface] = properties; 0164 } 0165 0166 return interfaceMap; 0167 } 0168 0169 KDBusObjectManagerServer::InterfaceMetaObjectHash KDBusObjectManagerServer::metaObjectsFor(const QObject *object) 0170 { 0171 InterfaceMetaObjectHash map; 0172 for (auto mo = object->metaObject(); mo; mo = mo->superClass()) { 0173 if (strcmp(mo->className(), "QObject") == 0) { 0174 continue; 0175 } 0176 0177 int ciid = mo->indexOfClassInfo("D-Bus Interface"); 0178 if (ciid == -1) { 0179 qCWarning(KDED) << mo->className() << "defines no interface"; 0180 continue; 0181 } 0182 0183 map[QString::fromLatin1(mo->classInfo(ciid).value())] = mo; 0184 } 0185 return map; 0186 } 0187 0188 #include "dbusobjectmanagerserver.moc"