dbus/properties: decouple properties from AbstractDBusProperty

Importantly, this decouples properties from having to be QObjects,
allowing future property types to be much lighter.
This commit is contained in:
outfoxxed 2024-11-20 03:15:09 -08:00
parent dca75b7d6a
commit 4163713bc4
Signed by: outfoxxed
GPG key ID: 4C88A185FB89301E
2 changed files with 137 additions and 113 deletions

View file

@ -110,100 +110,25 @@ void asyncReadPropertyInternal(
QObject::connect(call, &QDBusPendingCallWatcher::finished, &interface, responseCallback);
}
void AbstractDBusProperty::tryUpdate(const QVariant& variant) {
this->mExists = true;
auto error = this->read(variant);
if (error.isValid()) {
qCWarning(logDbusProperties).noquote()
<< "Error demarshalling property update for" << this->toString();
qCWarning(logDbusProperties) << error;
} else {
qCDebug(logDbusProperties).noquote()
<< "Updated property" << this->toString() << "to" << this->valueString();
}
}
void AbstractDBusProperty::update() {
if (this->group == nullptr) {
qFatal(logDbusProperties) << "Tried to update dbus property" << this->name
qFatal(logDbusProperties) << "Tried to update dbus property" << this->nameRef()
<< "which is not attached to a group";
} else {
const QString propStr = this->toString();
if (this->group->interface == nullptr) {
qFatal(logDbusProperties).noquote()
<< "Tried to update property" << propStr << "of a disconnected interface";
}
qCDebug(logDbusProperties).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(logDbusProperties).noquote() << "Error updating property" << propStr;
qCWarning(logDbusProperties) << reply.error();
} else {
this->tryUpdate(reply.value().variant());
}
delete call;
};
QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
this->group->requestPropertyUpdate(this);
}
}
void AbstractDBusProperty::write() {
if (this->group == nullptr) {
qFatal(logDbusProperties) << "Tried to write dbus property" << this->name
qFatal(logDbusProperties) << "Tried to write dbus property" << this->nameRef()
<< "which is not attached to a group";
} else {
const QString propStr = this->toString();
if (this->group->interface == nullptr) {
qFatal(logDbusProperties).noquote()
<< "Tried to write property" << propStr << "of a disconnected interface";
}
qCDebug(logDbusProperties).noquote() << "Writing property" << propStr;
auto pendingCall = this->group->propertyInterface->Set(
this->group->interface->interface(),
this->name,
QDBusVariant(this->serialize())
);
auto* call = new QDBusPendingCallWatcher(pendingCall, this);
auto responseCallback = [propStr](QDBusPendingCallWatcher* call) {
const QDBusPendingReply<> reply = *call;
if (reply.isError()) {
qCWarning(logDbusProperties).noquote() << "Error writing property" << propStr;
qCWarning(logDbusProperties) << reply.error();
}
delete call;
};
QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
this->group->pushPropertyUpdate(this);
}
}
bool AbstractDBusProperty::exists() const { return this->mExists; }
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)
DBusPropertyGroup::DBusPropertyGroup(QVector<DBusPropertyCore*> properties, QObject* parent)
: QObject(parent)
, properties(std::move(properties)) {}
@ -246,7 +171,7 @@ void DBusPropertyGroup::updateAllDirect() {
}
for (auto* property: this->properties) {
property->update();
this->requestPropertyUpdate(property);
}
}
@ -287,27 +212,102 @@ void DBusPropertyGroup::updatePropertySet(const QVariantMap& properties, bool co
auto prop = std::find_if(
this->properties.begin(),
this->properties.end(),
[&name](AbstractDBusProperty* prop) { return prop->name == name; }
[&name](DBusPropertyCore* prop) { return prop->nameRef() == name; }
);
if (prop == this->properties.end()) {
qCDebug(logDbusProperties) << "Ignoring untracked property update" << name << "for"
<< this->toString();
} else {
(*prop)->tryUpdate(value);
this->tryUpdateProperty(*prop, value);
}
}
if (complainMissing) {
for (const auto* prop: this->properties) {
if (prop->required && !properties.contains(prop->name)) {
if (prop->isRequired() && !properties.contains(prop->name())) {
qCWarning(logDbusProperties)
<< prop->name << "missing from property set for" << this->toString();
<< prop->nameRef() << "missing from property set for" << this->toString();
}
}
}
}
void DBusPropertyGroup::tryUpdateProperty(DBusPropertyCore* property, const QVariant& variant)
const {
property->mExists = true;
auto error = property->store(variant);
if (error.isValid()) {
qCWarning(logDbusProperties).noquote()
<< "Error demarshalling property update for" << this->propertyString(property);
qCWarning(logDbusProperties) << error;
} else {
qCDebug(logDbusProperties).noquote()
<< "Updated property" << this->propertyString(property) << "to" << property->valueString();
}
}
void DBusPropertyGroup::requestPropertyUpdate(DBusPropertyCore* property) {
const QString propStr = this->propertyString(property);
if (this->interface == nullptr) {
qFatal(logDbusProperties).noquote()
<< "Tried to update property" << propStr << "of a disconnected interface";
}
qCDebug(logDbusProperties).noquote() << "Updating property" << propStr;
auto pendingCall = this->propertyInterface->Get(this->interface->interface(), property->name());
auto* call = new QDBusPendingCallWatcher(pendingCall, this);
auto responseCallback = [this, propStr, property](QDBusPendingCallWatcher* call) {
const QDBusPendingReply<QDBusVariant> reply = *call;
if (reply.isError()) {
qCWarning(logDbusProperties).noquote() << "Error updating property" << propStr;
qCWarning(logDbusProperties) << reply.error();
} else {
this->tryUpdateProperty(property, reply.value().variant());
}
delete call;
};
QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
}
void DBusPropertyGroup::pushPropertyUpdate(DBusPropertyCore* property) {
const QString propStr = this->propertyString(property);
if (this->interface == nullptr) {
qFatal(logDbusProperties).noquote()
<< "Tried to write property" << propStr << "of a disconnected interface";
}
qCDebug(logDbusProperties).noquote() << "Writing property" << propStr;
auto pendingCall = this->propertyInterface->Set(
this->interface->interface(),
property->name(),
QDBusVariant(property->serialize())
);
auto* call = new QDBusPendingCallWatcher(pendingCall, this);
auto responseCallback = [propStr](QDBusPendingCallWatcher* call) {
const QDBusPendingReply<> reply = *call;
if (reply.isError()) {
qCWarning(logDbusProperties).noquote() << "Error writing property" << propStr;
qCWarning(logDbusProperties) << reply.error();
}
delete call;
};
QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
}
QString DBusPropertyGroup::toString() const {
if (this->interface == nullptr) {
return "{ DISCONNECTED }";
@ -317,6 +317,12 @@ QString DBusPropertyGroup::toString() const {
}
}
QString DBusPropertyGroup::propertyString(const DBusPropertyCore* property) const {
return this->toString() % ':' % property->nameRef();
}
QString AbstractDBusProperty::toString() const { return this->group->propertyString(this); }
void DBusPropertyGroup::onPropertiesChanged(
const QString& interfaceName,
const QVariantMap& changedProperties,
@ -330,14 +336,14 @@ void DBusPropertyGroup::onPropertiesChanged(
auto prop = std::find_if(
this->properties.begin(),
this->properties.end(),
[&name](AbstractDBusProperty* prop) { return prop->name == name; }
[&name](DBusPropertyCore* prop) { return prop->nameRef() == name; }
);
if (prop == this->properties.end()) {
qCDebug(logDbusProperties) << "Ignoring untracked property invalidation" << name << "for"
<< this;
} else {
(*prop)->update();
this->requestPropertyUpdate(*prop);
}
}

View file

@ -14,6 +14,7 @@
#include <qlogging.h>
#include <qloggingcategory.h>
#include <qobject.h>
#include <qtclasshelpermacros.h>
#include <qtmetamacros.h>
#include <qvariant.h>
@ -75,24 +76,44 @@ void asyncReadProperty(
class DBusPropertyGroup;
class AbstractDBusProperty: public QObject {
class DBusPropertyCore {
public:
DBusPropertyCore() = default;
virtual ~DBusPropertyCore() = default;
Q_DISABLE_COPY_MOVE(DBusPropertyCore);
[[nodiscard]] virtual QString name() const = 0;
[[nodiscard]] virtual QStringView nameRef() const = 0;
[[nodiscard]] virtual QString valueString() = 0;
[[nodiscard]] virtual bool isRequired() const = 0;
[[nodiscard]] bool exists() const { return this->mExists; }
protected:
virtual QDBusError store(const QVariant& variant) = 0;
[[nodiscard]] virtual QVariant serialize() = 0;
private:
bool mExists : 1 = false;
friend class DBusPropertyGroup;
};
class AbstractDBusProperty
: public QObject
, public DBusPropertyCore {
Q_OBJECT;
public:
explicit AbstractDBusProperty(
QString name,
const QMetaType& type,
bool required,
QObject* parent = nullptr
)
explicit AbstractDBusProperty(QString name, bool required, QObject* parent = nullptr)
: QObject(parent)
, name(std::move(name))
, type(type)
, required(required) {}
, required(required)
, mName(std::move(name)) {}
[[nodiscard]] QString name() const override { return this->mName; };
[[nodiscard]] QStringView nameRef() const override { return this->mName; };
[[nodiscard]] bool isRequired() const override { return this->required; };
[[nodiscard]] bool exists() const;
[[nodiscard]] QString toString() const;
[[nodiscard]] virtual QString valueString() = 0;
public slots:
void update();
@ -101,19 +122,12 @@ public slots:
signals:
void changed();
protected:
virtual QDBusError read(const QVariant& variant) = 0;
virtual QVariant serialize() = 0;
private:
void tryUpdate(const QVariant& variant);
bool required : 1;
bool mExists : 1 = false;
DBusPropertyGroup* group = nullptr;
QString name;
QMetaType type;
bool required;
bool mExists = false;
QString mName;
friend class DBusPropertyGroup;
};
@ -123,7 +137,7 @@ class DBusPropertyGroup: public QObject {
public:
explicit DBusPropertyGroup(
QVector<AbstractDBusProperty*> properties = QVector<AbstractDBusProperty*>(),
QVector<DBusPropertyCore*> properties = QVector<DBusPropertyCore*>(),
QObject* parent = nullptr
);
@ -146,10 +160,14 @@ private slots:
private:
void updatePropertySet(const QVariantMap& properties, bool complainMissing);
void requestPropertyUpdate(DBusPropertyCore* property);
void pushPropertyUpdate(DBusPropertyCore* property);
void tryUpdateProperty(DBusPropertyCore* property, const QVariant& variant) const;
[[nodiscard]] QString propertyString(const DBusPropertyCore* property) const;
DBusPropertiesInterface* propertyInterface = nullptr;
QDBusAbstractInterface* interface = nullptr;
QVector<AbstractDBusProperty*> properties;
QVector<DBusPropertyCore*> properties;
friend class AbstractDBusProperty;
};
@ -163,7 +181,7 @@ public:
bool required = true,
QObject* parent = nullptr
)
: AbstractDBusProperty(std::move(name), QMetaType::fromType<T>(), required, parent)
: AbstractDBusProperty(std::move(name), required, parent)
, value(std::move(value)) {}
explicit DBusProperty(
@ -191,7 +209,7 @@ public:
}
protected:
QDBusError read(const QVariant& variant) override {
QDBusError store(const QVariant& variant) override {
auto result = demarshallVariant<T>(variant);
if (result.isValid()) {