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:
parent
dca75b7d6a
commit
4163713bc4
|
@ -110,100 +110,25 @@ void asyncReadPropertyInternal(
|
||||||
QObject::connect(call, &QDBusPendingCallWatcher::finished, &interface, responseCallback);
|
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() {
|
void AbstractDBusProperty::update() {
|
||||||
if (this->group == nullptr) {
|
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";
|
<< "which is not attached to a group";
|
||||||
} else {
|
} else {
|
||||||
const QString propStr = this->toString();
|
this->group->requestPropertyUpdate(this);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractDBusProperty::write() {
|
void AbstractDBusProperty::write() {
|
||||||
if (this->group == nullptr) {
|
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";
|
<< "which is not attached to a group";
|
||||||
} else {
|
} else {
|
||||||
const QString propStr = this->toString();
|
this->group->pushPropertyUpdate(this);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AbstractDBusProperty::exists() const { return this->mExists; }
|
DBusPropertyGroup::DBusPropertyGroup(QVector<DBusPropertyCore*> properties, QObject* parent)
|
||||||
|
|
||||||
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)
|
: QObject(parent)
|
||||||
, properties(std::move(properties)) {}
|
, properties(std::move(properties)) {}
|
||||||
|
|
||||||
|
@ -246,7 +171,7 @@ void DBusPropertyGroup::updateAllDirect() {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto* property: this->properties) {
|
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(
|
auto prop = std::find_if(
|
||||||
this->properties.begin(),
|
this->properties.begin(),
|
||||||
this->properties.end(),
|
this->properties.end(),
|
||||||
[&name](AbstractDBusProperty* prop) { return prop->name == name; }
|
[&name](DBusPropertyCore* prop) { return prop->nameRef() == name; }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (prop == this->properties.end()) {
|
if (prop == this->properties.end()) {
|
||||||
qCDebug(logDbusProperties) << "Ignoring untracked property update" << name << "for"
|
qCDebug(logDbusProperties) << "Ignoring untracked property update" << name << "for"
|
||||||
<< this->toString();
|
<< this->toString();
|
||||||
} else {
|
} else {
|
||||||
(*prop)->tryUpdate(value);
|
this->tryUpdateProperty(*prop, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (complainMissing) {
|
if (complainMissing) {
|
||||||
for (const auto* prop: this->properties) {
|
for (const auto* prop: this->properties) {
|
||||||
if (prop->required && !properties.contains(prop->name)) {
|
if (prop->isRequired() && !properties.contains(prop->name())) {
|
||||||
qCWarning(logDbusProperties)
|
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 {
|
QString DBusPropertyGroup::toString() const {
|
||||||
if (this->interface == nullptr) {
|
if (this->interface == nullptr) {
|
||||||
return "{ DISCONNECTED }";
|
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(
|
void DBusPropertyGroup::onPropertiesChanged(
|
||||||
const QString& interfaceName,
|
const QString& interfaceName,
|
||||||
const QVariantMap& changedProperties,
|
const QVariantMap& changedProperties,
|
||||||
|
@ -330,14 +336,14 @@ void DBusPropertyGroup::onPropertiesChanged(
|
||||||
auto prop = std::find_if(
|
auto prop = std::find_if(
|
||||||
this->properties.begin(),
|
this->properties.begin(),
|
||||||
this->properties.end(),
|
this->properties.end(),
|
||||||
[&name](AbstractDBusProperty* prop) { return prop->name == name; }
|
[&name](DBusPropertyCore* prop) { return prop->nameRef() == name; }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (prop == this->properties.end()) {
|
if (prop == this->properties.end()) {
|
||||||
qCDebug(logDbusProperties) << "Ignoring untracked property invalidation" << name << "for"
|
qCDebug(logDbusProperties) << "Ignoring untracked property invalidation" << name << "for"
|
||||||
<< this;
|
<< this;
|
||||||
} else {
|
} else {
|
||||||
(*prop)->update();
|
this->requestPropertyUpdate(*prop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qloggingcategory.h>
|
#include <qloggingcategory.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
|
#include <qtclasshelpermacros.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
#include <qvariant.h>
|
#include <qvariant.h>
|
||||||
|
|
||||||
|
@ -75,24 +76,44 @@ void asyncReadProperty(
|
||||||
|
|
||||||
class DBusPropertyGroup;
|
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;
|
Q_OBJECT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AbstractDBusProperty(
|
explicit AbstractDBusProperty(QString name, bool required, QObject* parent = nullptr)
|
||||||
QString name,
|
|
||||||
const QMetaType& type,
|
|
||||||
bool required,
|
|
||||||
QObject* parent = nullptr
|
|
||||||
)
|
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, name(std::move(name))
|
, required(required)
|
||||||
, type(type)
|
, mName(std::move(name)) {}
|
||||||
, required(required) {}
|
|
||||||
|
[[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]] QString toString() const;
|
||||||
[[nodiscard]] virtual QString valueString() = 0;
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void update();
|
void update();
|
||||||
|
@ -101,19 +122,12 @@ public slots:
|
||||||
signals:
|
signals:
|
||||||
void changed();
|
void changed();
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual QDBusError read(const QVariant& variant) = 0;
|
|
||||||
virtual QVariant serialize() = 0;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void tryUpdate(const QVariant& variant);
|
bool required : 1;
|
||||||
|
bool mExists : 1 = false;
|
||||||
|
|
||||||
DBusPropertyGroup* group = nullptr;
|
DBusPropertyGroup* group = nullptr;
|
||||||
|
QString mName;
|
||||||
QString name;
|
|
||||||
QMetaType type;
|
|
||||||
bool required;
|
|
||||||
bool mExists = false;
|
|
||||||
|
|
||||||
friend class DBusPropertyGroup;
|
friend class DBusPropertyGroup;
|
||||||
};
|
};
|
||||||
|
@ -123,7 +137,7 @@ class DBusPropertyGroup: public QObject {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit DBusPropertyGroup(
|
explicit DBusPropertyGroup(
|
||||||
QVector<AbstractDBusProperty*> properties = QVector<AbstractDBusProperty*>(),
|
QVector<DBusPropertyCore*> properties = QVector<DBusPropertyCore*>(),
|
||||||
QObject* parent = nullptr
|
QObject* parent = nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -146,10 +160,14 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updatePropertySet(const QVariantMap& properties, bool complainMissing);
|
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;
|
DBusPropertiesInterface* propertyInterface = nullptr;
|
||||||
QDBusAbstractInterface* interface = nullptr;
|
QDBusAbstractInterface* interface = nullptr;
|
||||||
QVector<AbstractDBusProperty*> properties;
|
QVector<DBusPropertyCore*> properties;
|
||||||
|
|
||||||
friend class AbstractDBusProperty;
|
friend class AbstractDBusProperty;
|
||||||
};
|
};
|
||||||
|
@ -163,7 +181,7 @@ public:
|
||||||
bool required = true,
|
bool required = true,
|
||||||
QObject* parent = nullptr
|
QObject* parent = nullptr
|
||||||
)
|
)
|
||||||
: AbstractDBusProperty(std::move(name), QMetaType::fromType<T>(), required, parent)
|
: AbstractDBusProperty(std::move(name), required, parent)
|
||||||
, value(std::move(value)) {}
|
, value(std::move(value)) {}
|
||||||
|
|
||||||
explicit DBusProperty(
|
explicit DBusProperty(
|
||||||
|
@ -191,7 +209,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QDBusError read(const QVariant& variant) override {
|
QDBusError store(const QVariant& variant) override {
|
||||||
auto result = demarshallVariant<T>(variant);
|
auto result = demarshallVariant<T>(variant);
|
||||||
|
|
||||||
if (result.isValid()) {
|
if (result.isValid()) {
|
||||||
|
|
Loading…
Reference in a new issue