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)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (DBUS)
 | 
			
		||||
	list(APPEND QT_DEPS Qt6::DBus)
 | 
			
		||||
	list(APPEND QT_FPDEPS DBus)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
find_package(Qt6 REQUIRED COMPONENTS ${QT_FPDEPS})
 | 
			
		||||
 | 
			
		||||
qt_standard_project_setup(REQUIRES 6.6)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,10 @@ install(TARGETS quickshell RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
 | 
			
		|||
add_subdirectory(core)
 | 
			
		||||
add_subdirectory(io)
 | 
			
		||||
 | 
			
		||||
if (DBUS)
 | 
			
		||||
	add_subdirectory(dbus)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (WAYLAND)
 | 
			
		||||
	add_subdirectory(wayland)
 | 
			
		||||
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