forked from quickshell/quickshell
		
	dbus: create property helper classes
Handles asynchronous property updates, part of the work for StatusNotifierItems.
This commit is contained in:
		
							parent
							
								
									8529a2eb22
								
							
						
					
					
						commit
						8e530b6b77
					
				
					 6 changed files with 530 additions and 0 deletions
				
			
		| 
						 | 
					@ -73,6 +73,11 @@ if (WAYLAND)
 | 
				
			||||||
	list(APPEND QT_FPDEPS WaylandClient)
 | 
						list(APPEND QT_FPDEPS WaylandClient)
 | 
				
			||||||
endif()
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if (DBUS)
 | 
				
			||||||
 | 
						list(APPEND QT_DEPS Qt6::DBus)
 | 
				
			||||||
 | 
						list(APPEND QT_FPDEPS DBus)
 | 
				
			||||||
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
find_package(Qt6 REQUIRED COMPONENTS ${QT_FPDEPS})
 | 
					find_package(Qt6 REQUIRED COMPONENTS ${QT_FPDEPS})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
qt_standard_project_setup(REQUIRES 6.6)
 | 
					qt_standard_project_setup(REQUIRES 6.6)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,10 @@ install(TARGETS quickshell RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
 | 
				
			||||||
add_subdirectory(core)
 | 
					add_subdirectory(core)
 | 
				
			||||||
add_subdirectory(io)
 | 
					add_subdirectory(io)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if (DBUS)
 | 
				
			||||||
 | 
						add_subdirectory(dbus)
 | 
				
			||||||
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (WAYLAND)
 | 
					if (WAYLAND)
 | 
				
			||||||
	add_subdirectory(wayland)
 | 
						add_subdirectory(wayland)
 | 
				
			||||||
endif ()
 | 
					endif ()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										22
									
								
								src/dbus/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/dbus/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					set_source_files_properties(org.freedesktop.DBus.Properties.xml PROPERTIES
 | 
				
			||||||
 | 
						CLASSNAME DBusPropertiesInterface
 | 
				
			||||||
 | 
						#INCLUDE dbus_properties_types.hpp
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					qt_add_dbus_interface(DBUS_INTERFACES
 | 
				
			||||||
 | 
						org.freedesktop.DBus.Properties.xml
 | 
				
			||||||
 | 
						dbus_properties
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					qt_add_library(quickshell-dbus STATIC
 | 
				
			||||||
 | 
						dbusutil.cpp
 | 
				
			||||||
 | 
						${DBUS_INTERFACES}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# dbus headers
 | 
				
			||||||
 | 
					target_include_directories(quickshell-dbus PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					target_link_libraries(quickshell-dbus PRIVATE ${QT_DEPS})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					qs_pch(quickshell-dbus)
 | 
				
			||||||
 | 
					#qs_pch(quickshell-dbusplugin)
 | 
				
			||||||
							
								
								
									
										284
									
								
								src/dbus/dbusutil.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								src/dbus/dbusutil.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,284 @@
 | 
				
			||||||
 | 
					#include "dbusutil.hpp"
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qcontainerfwd.h>
 | 
				
			||||||
 | 
					#include <qdbusabstractinterface.h>
 | 
				
			||||||
 | 
					#include <qdbusargument.h>
 | 
				
			||||||
 | 
					#include <qdbuserror.h>
 | 
				
			||||||
 | 
					#include <qdbusextratypes.h>
 | 
				
			||||||
 | 
					#include <qdbusmessage.h>
 | 
				
			||||||
 | 
					#include <qdbusmetatype.h>
 | 
				
			||||||
 | 
					#include <qdbuspendingcall.h>
 | 
				
			||||||
 | 
					#include <qdbuspendingreply.h>
 | 
				
			||||||
 | 
					#include <qdebug.h>
 | 
				
			||||||
 | 
					#include <qlogging.h>
 | 
				
			||||||
 | 
					#include <qloggingcategory.h>
 | 
				
			||||||
 | 
					#include <qmetatype.h>
 | 
				
			||||||
 | 
					#include <qobject.h>
 | 
				
			||||||
 | 
					#include <qpolygon.h>
 | 
				
			||||||
 | 
					#include <qvariant.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "dbus_properties.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Q_LOGGING_CATEGORY(logDbus, "quickshell.dbus", QtWarningMsg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace qs::dbus {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDBusError demarshallVariant(const QVariant& variant, const QMetaType& type, void* slot) {
 | 
				
			||||||
 | 
						const char* expectedSignature = "v";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (type.id() != QMetaType::QVariant) {
 | 
				
			||||||
 | 
							expectedSignature = QDBusMetaType::typeToSignature(type);
 | 
				
			||||||
 | 
							if (expectedSignature == nullptr) {
 | 
				
			||||||
 | 
								qFatal() << "failed to demarshall unregistered dbus meta-type" << type << "with" << variant;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (variant.metaType() == type) {
 | 
				
			||||||
 | 
							if (type.id() == QMetaType::QVariant) {
 | 
				
			||||||
 | 
								*reinterpret_cast<QVariant*>(slot) = variant; // NOLINT
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								type.destruct(slot);
 | 
				
			||||||
 | 
								type.construct(slot, variant.constData());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if (variant.metaType() == QMetaType::fromType<QDBusArgument>()) {
 | 
				
			||||||
 | 
							auto arg = qvariant_cast<QDBusArgument>(variant);
 | 
				
			||||||
 | 
							auto signature = arg.currentSignature();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (signature == expectedSignature) {
 | 
				
			||||||
 | 
								if (!QDBusMetaType::demarshall(arg, type, slot)) {
 | 
				
			||||||
 | 
									QString error;
 | 
				
			||||||
 | 
									QDebug(&error) << "failed to deserialize dbus value" << variant << "into" << type;
 | 
				
			||||||
 | 
									return QDBusError(QDBusError::InvalidArgs, error);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							QString error;
 | 
				
			||||||
 | 
							QDebug(&error) << "failed to deserialize variant" << variant
 | 
				
			||||||
 | 
							               << "which is not a primitive type or a dbus argument (what?)";
 | 
				
			||||||
 | 
							return QDBusError(QDBusError::InvalidArgs, error);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return QDBusError();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void asyncReadPropertyInternal(
 | 
				
			||||||
 | 
					    const QMetaType& type,
 | 
				
			||||||
 | 
					    QDBusAbstractInterface& interface,
 | 
				
			||||||
 | 
					    const QString& property,
 | 
				
			||||||
 | 
					    std::function<void(std::function<QDBusError(void*)>)> callback // NOLINT
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						if (type.id() != QMetaType::QVariant) {
 | 
				
			||||||
 | 
							const char* expectedSignature = QDBusMetaType::typeToSignature(type);
 | 
				
			||||||
 | 
							if (expectedSignature == nullptr) {
 | 
				
			||||||
 | 
								qFatal() << "qs::dbus::asyncReadPropertyInternal called with unregistered dbus meta-type"
 | 
				
			||||||
 | 
								         << type;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto callMessage = QDBusMessage::createMethodCall(
 | 
				
			||||||
 | 
						    interface.service(),
 | 
				
			||||||
 | 
						    interface.path(),
 | 
				
			||||||
 | 
						    "org.freedesktop.DBus.Properties",
 | 
				
			||||||
 | 
						    "Get"
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						callMessage << interface.interface() << property;
 | 
				
			||||||
 | 
						auto pendingCall = interface.connection().asyncCall(callMessage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto* call = new QDBusPendingCallWatcher(pendingCall, &interface);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto responseCallback = [type, callback](QDBusPendingCallWatcher* call) {
 | 
				
			||||||
 | 
							QDBusPendingReply<QDBusVariant> reply = *call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							callback([&](void* slot) {
 | 
				
			||||||
 | 
								if (reply.isError()) {
 | 
				
			||||||
 | 
									return reply.error();
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									return demarshallVariant(reply.value().variant(), type, slot);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QObject::connect(call, &QDBusPendingCallWatcher::finished, &interface, responseCallback);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AbstractDBusProperty::tryUpdate(const QVariant& variant) {
 | 
				
			||||||
 | 
						auto error = this->read(variant);
 | 
				
			||||||
 | 
						if (error.isValid()) {
 | 
				
			||||||
 | 
							qCWarning(logDbus).noquote() << "Error demarshalling property update for" << this->toString();
 | 
				
			||||||
 | 
							qCWarning(logDbus) << error;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							qCDebug(logDbus).noquote() << "Updated property" << this->toString() << "to"
 | 
				
			||||||
 | 
							                           << this->valueString();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AbstractDBusProperty::update() {
 | 
				
			||||||
 | 
						if (this->group == nullptr) {
 | 
				
			||||||
 | 
							qFatal(logDbus) << "Tried to update dbus property" << this->name
 | 
				
			||||||
 | 
							                << "which is not attached to a group";
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							const QString propStr = this->toString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (this->group->interface == nullptr) {
 | 
				
			||||||
 | 
								qFatal(logDbus).noquote() << "Tried to update property" << propStr
 | 
				
			||||||
 | 
								                          << "of a disconnected interface";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							qCDebug(logDbus).noquote() << "Updating property" << propStr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto pendingCall =
 | 
				
			||||||
 | 
							    this->group->propertyInterface->Get(this->group->interface->interface(), this->name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto* call = new QDBusPendingCallWatcher(pendingCall, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							auto responseCallback = [this, propStr](QDBusPendingCallWatcher* call) {
 | 
				
			||||||
 | 
								const QDBusPendingReply<QDBusVariant> reply = *call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (reply.isError()) {
 | 
				
			||||||
 | 
									qCWarning(logDbus).noquote() << "Error updating property" << propStr;
 | 
				
			||||||
 | 
									qCWarning(logDbus) << reply.error();
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									this->tryUpdate(reply.value().variant());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								delete call;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QString AbstractDBusProperty::toString() const {
 | 
				
			||||||
 | 
						const QString group = this->group == nullptr ? "{ NO GROUP }" : this->group->toString();
 | 
				
			||||||
 | 
						return group + ':' + this->name;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DBusPropertyGroup::DBusPropertyGroup(QVector<AbstractDBusProperty*> properties, QObject* parent)
 | 
				
			||||||
 | 
					    : QObject(parent)
 | 
				
			||||||
 | 
					    , properties(std::move(properties)) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusPropertyGroup::setInterface(QDBusAbstractInterface* interface) {
 | 
				
			||||||
 | 
						if (this->interface != nullptr) {
 | 
				
			||||||
 | 
							delete this->propertyInterface;
 | 
				
			||||||
 | 
							this->propertyInterface = nullptr;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (interface != nullptr) {
 | 
				
			||||||
 | 
							this->interface = interface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this->propertyInterface = new DBusPropertiesInterface(
 | 
				
			||||||
 | 
							    interface->service(),
 | 
				
			||||||
 | 
							    interface->path(),
 | 
				
			||||||
 | 
							    interface->connection(),
 | 
				
			||||||
 | 
							    this
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							QObject::connect(
 | 
				
			||||||
 | 
							    this->propertyInterface,
 | 
				
			||||||
 | 
							    &DBusPropertiesInterface::PropertiesChanged,
 | 
				
			||||||
 | 
							    this,
 | 
				
			||||||
 | 
							    &DBusPropertyGroup::onPropertiesChanged
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusPropertyGroup::attachProperty(AbstractDBusProperty* property) {
 | 
				
			||||||
 | 
						this->properties.append(property);
 | 
				
			||||||
 | 
						property->group = this;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusPropertyGroup::updateAllDirect() {
 | 
				
			||||||
 | 
						qCDebug(logDbus).noquote() << "Updating all properties of" << this->toString()
 | 
				
			||||||
 | 
						                           << "via individual queries";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (this->interface == nullptr) {
 | 
				
			||||||
 | 
							qFatal() << "Attempted to update properties of disconnected property group";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (auto* property: this->properties) {
 | 
				
			||||||
 | 
							property->update();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusPropertyGroup::updateAllViaGetAll() {
 | 
				
			||||||
 | 
						qCDebug(logDbus).noquote() << "Updating all properties of" << this->toString() << "via GetAll";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (this->interface == nullptr) {
 | 
				
			||||||
 | 
							qFatal() << "Attempted to update properties of disconnected property group";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto pendingCall = this->propertyInterface->GetAll(this->interface->interface());
 | 
				
			||||||
 | 
						auto* call = new QDBusPendingCallWatcher(pendingCall, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto responseCallback = [this](QDBusPendingCallWatcher* call) {
 | 
				
			||||||
 | 
							const QDBusPendingReply<QVariantMap> reply = *call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (reply.isError()) {
 | 
				
			||||||
 | 
								qCWarning(logDbus).noquote()
 | 
				
			||||||
 | 
								    << "Error updating properties of" << this->toString() << "via GetAll";
 | 
				
			||||||
 | 
								qCWarning(logDbus) << reply.error();
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								qCDebug(logDbus).noquote() << "Received GetAll property set for" << this->toString();
 | 
				
			||||||
 | 
								this->updatePropertySet(reply.value());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							delete call;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusPropertyGroup::updatePropertySet(const QVariantMap& properties) {
 | 
				
			||||||
 | 
						for (const auto [name, value]: properties.asKeyValueRange()) {
 | 
				
			||||||
 | 
							auto prop = std::find_if(
 | 
				
			||||||
 | 
							    this->properties.begin(),
 | 
				
			||||||
 | 
							    this->properties.end(),
 | 
				
			||||||
 | 
							    [&name](AbstractDBusProperty* prop) { return prop->name == name; }
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (prop == this->properties.end()) {
 | 
				
			||||||
 | 
								qCDebug(logDbus) << "Ignoring untracked property update" << name << "for" << this;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								(*prop)->tryUpdate(value);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QString DBusPropertyGroup::toString() const {
 | 
				
			||||||
 | 
						if (this->interface == nullptr) {
 | 
				
			||||||
 | 
							return "{ DISCONNECTED }";
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return this->interface->service() + this->interface->path() + "/"
 | 
				
			||||||
 | 
							     + this->interface->interface();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DBusPropertyGroup::onPropertiesChanged(
 | 
				
			||||||
 | 
					    const QString& interfaceName,
 | 
				
			||||||
 | 
					    const QVariantMap& changedProperties,
 | 
				
			||||||
 | 
					    const QStringList& invalidatedProperties
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						if (interfaceName != this->interface->interface()) return;
 | 
				
			||||||
 | 
						qCDebug(logDbus) << "Received property change set and invalidations for" << this->toString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (const auto& name: invalidatedProperties) {
 | 
				
			||||||
 | 
							auto prop = std::find_if(
 | 
				
			||||||
 | 
							    this->properties.begin(),
 | 
				
			||||||
 | 
							    this->properties.end(),
 | 
				
			||||||
 | 
							    [&name](AbstractDBusProperty* prop) { return prop->name == name; }
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (prop == this->properties.end()) {
 | 
				
			||||||
 | 
								qCDebug(logDbus) << "Ignoring untracked property invalidation" << name << "for" << this;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								(*prop)->update();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this->updatePropertySet(changedProperties);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace qs::dbus
 | 
				
			||||||
							
								
								
									
										189
									
								
								src/dbus/dbusutil.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								src/dbus/dbusutil.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,189 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <functional>
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <qcontainerfwd.h>
 | 
				
			||||||
 | 
					#include <qdbusabstractinterface.h>
 | 
				
			||||||
 | 
					#include <qdbuserror.h>
 | 
				
			||||||
 | 
					#include <qdbusextratypes.h>
 | 
				
			||||||
 | 
					#include <qdbuspendingcall.h>
 | 
				
			||||||
 | 
					#include <qdbusreply.h>
 | 
				
			||||||
 | 
					#include <qdbusservicewatcher.h>
 | 
				
			||||||
 | 
					#include <qdebug.h>
 | 
				
			||||||
 | 
					#include <qlogging.h>
 | 
				
			||||||
 | 
					#include <qloggingcategory.h>
 | 
				
			||||||
 | 
					#include <qobject.h>
 | 
				
			||||||
 | 
					#include <qtmetamacros.h>
 | 
				
			||||||
 | 
					#include <qvariant.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DBusPropertiesInterface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Q_DECLARE_LOGGING_CATEGORY(logDbus);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace qs::dbus {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QDBusError demarshallVariant(const QVariant& variant, const QMetaType& type, void* slot);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <typename T>
 | 
				
			||||||
 | 
					class DBusResult {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						explicit DBusResult() = default;
 | 
				
			||||||
 | 
						explicit DBusResult(T value): value(std::move(value)) {}
 | 
				
			||||||
 | 
						explicit DBusResult(QDBusError error): error(std::move(error)) {}
 | 
				
			||||||
 | 
						explicit DBusResult(T value, QDBusError error)
 | 
				
			||||||
 | 
						    : value(std::move(value))
 | 
				
			||||||
 | 
						    , error(std::move(error)) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool isValid() { return !this->error.isValid(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						T value;
 | 
				
			||||||
 | 
						QDBusError error;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <typename T>
 | 
				
			||||||
 | 
					DBusResult<T> demarshallVariant(const QVariant& variant) {
 | 
				
			||||||
 | 
						T value;
 | 
				
			||||||
 | 
						auto error = demarshallVariant(variant, QMetaType::fromType<T>(), &value);
 | 
				
			||||||
 | 
						return DBusResult<T>(value, error);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void asyncReadPropertyInternal(
 | 
				
			||||||
 | 
					    const QMetaType& type,
 | 
				
			||||||
 | 
					    QDBusAbstractInterface& interface,
 | 
				
			||||||
 | 
					    const QString& property,
 | 
				
			||||||
 | 
					    std::function<void(std::function<QDBusError(void*)>)> callback
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <typename T>
 | 
				
			||||||
 | 
					void asyncReadProperty(
 | 
				
			||||||
 | 
					    QDBusAbstractInterface& interface,
 | 
				
			||||||
 | 
					    const QString& property,
 | 
				
			||||||
 | 
					    std::function<void(T, QDBusError)> callback
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						asyncReadPropertyInternal(
 | 
				
			||||||
 | 
						    QMetaType::fromType<T>(),
 | 
				
			||||||
 | 
						    interface,
 | 
				
			||||||
 | 
						    property,
 | 
				
			||||||
 | 
						    [callback](std::function<QDBusError(void*)> internalCallback) { // NOLINT
 | 
				
			||||||
 | 
							    T slot;
 | 
				
			||||||
 | 
							    auto error = internalCallback(static_cast<void*>(&slot));
 | 
				
			||||||
 | 
							    callback(slot, error);
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DBusPropertyGroup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AbstractDBusProperty: public QObject {
 | 
				
			||||||
 | 
						Q_OBJECT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						explicit AbstractDBusProperty(QString name, const QMetaType& type, QObject* parent = nullptr)
 | 
				
			||||||
 | 
						    : QObject(parent)
 | 
				
			||||||
 | 
						    , name(std::move(name))
 | 
				
			||||||
 | 
						    , type(type) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] QString toString() const;
 | 
				
			||||||
 | 
						[[nodiscard]] virtual QString valueString() = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public slots:
 | 
				
			||||||
 | 
						void update();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					signals:
 | 
				
			||||||
 | 
						void changed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						virtual QDBusError read(const QVariant& variant) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						void tryUpdate(const QVariant& variant);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DBusPropertyGroup* group = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QString name;
 | 
				
			||||||
 | 
						QMetaType type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						friend class DBusPropertyGroup;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DBusPropertyGroup: public QObject {
 | 
				
			||||||
 | 
						Q_OBJECT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						explicit DBusPropertyGroup(
 | 
				
			||||||
 | 
						    QVector<AbstractDBusProperty*> properties = QVector<AbstractDBusProperty*>(),
 | 
				
			||||||
 | 
						    QObject* parent = nullptr
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void setInterface(QDBusAbstractInterface* interface);
 | 
				
			||||||
 | 
						void attachProperty(AbstractDBusProperty* property);
 | 
				
			||||||
 | 
						void updateAllDirect();
 | 
				
			||||||
 | 
						void updateAllViaGetAll();
 | 
				
			||||||
 | 
						[[nodiscard]] QString toString() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private slots:
 | 
				
			||||||
 | 
						void onPropertiesChanged(
 | 
				
			||||||
 | 
						    const QString& interfaceName,
 | 
				
			||||||
 | 
						    const QVariantMap& changedProperties,
 | 
				
			||||||
 | 
						    const QStringList& invalidatedProperties
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						void updatePropertySet(const QVariantMap& properties);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DBusPropertiesInterface* propertyInterface = nullptr;
 | 
				
			||||||
 | 
						QDBusAbstractInterface* interface = nullptr;
 | 
				
			||||||
 | 
						QVector<AbstractDBusProperty*> properties;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						friend class AbstractDBusProperty;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <typename T>
 | 
				
			||||||
 | 
					class DBusProperty: public AbstractDBusProperty {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						explicit DBusProperty(QString name, QObject* parent = nullptr, T value = T())
 | 
				
			||||||
 | 
						    : AbstractDBusProperty(std::move(name), QMetaType::fromType<T>(), parent)
 | 
				
			||||||
 | 
						    , value(std::move(value)) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						explicit DBusProperty(
 | 
				
			||||||
 | 
						    DBusPropertyGroup& group,
 | 
				
			||||||
 | 
						    QString name,
 | 
				
			||||||
 | 
						    QObject* parent = nullptr,
 | 
				
			||||||
 | 
						    T value = T()
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						    : DBusProperty(std::move(name), parent, std::move(value)) {
 | 
				
			||||||
 | 
							group.attachProperty(this);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] QString valueString() override {
 | 
				
			||||||
 | 
							QString str;
 | 
				
			||||||
 | 
							QDebug(&str) << this->value;
 | 
				
			||||||
 | 
							return str;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[[nodiscard]] T get() const { return this->value; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void set(T value) {
 | 
				
			||||||
 | 
							this->value = std::move(value);
 | 
				
			||||||
 | 
							emit this->changed();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						QDBusError read(const QVariant& variant) override {
 | 
				
			||||||
 | 
							auto result = demarshallVariant<T>(variant);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (result.isValid()) {
 | 
				
			||||||
 | 
								this->set(std::move(result.value));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return result.error;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						T value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						friend class DBusPropertyGroup;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace qs::dbus
 | 
				
			||||||
							
								
								
									
										26
									
								
								src/dbus/org.freedesktop.DBus.Properties.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/dbus/org.freedesktop.DBus.Properties.xml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,26 @@
 | 
				
			||||||
 | 
					<node>
 | 
				
			||||||
 | 
						<interface name="org.freedesktop.DBus.Properties">
 | 
				
			||||||
 | 
							<method name="Get">
 | 
				
			||||||
 | 
								<arg name="interface_name" direction="in" type="s"/>
 | 
				
			||||||
 | 
								<arg name="property_name" direction="in" type="s"/>
 | 
				
			||||||
 | 
								<arg name="value" direction="out" type="v"/>
 | 
				
			||||||
 | 
							</method>
 | 
				
			||||||
 | 
							<method name="Set">
 | 
				
			||||||
 | 
								<arg name="interface_name" direction="in" type="s"/>
 | 
				
			||||||
 | 
								<arg name="property_name" direction="in" type="s"/>
 | 
				
			||||||
 | 
								<arg name="value" direction="in" type="v"/>
 | 
				
			||||||
 | 
							</method>
 | 
				
			||||||
 | 
							<method name="GetAll">
 | 
				
			||||||
 | 
								<arg name="interface_name" direction="in" type="s"/>
 | 
				
			||||||
 | 
								<arg name="props" direction="out" type="a{sv}"/>
 | 
				
			||||||
 | 
								<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
 | 
				
			||||||
 | 
							</method>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<signal name="PropertiesChanged">
 | 
				
			||||||
 | 
								<arg type="s" name="interface_name"/>
 | 
				
			||||||
 | 
								<arg type="a{sv}" name="changed_properties"/>
 | 
				
			||||||
 | 
								<arg type="as" name="invalidated_properties"/>
 | 
				
			||||||
 | 
								<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap" />
 | 
				
			||||||
 | 
							</signal>
 | 
				
			||||||
 | 
						</interface>
 | 
				
			||||||
 | 
					</node>
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue