Music Hub ..
A session-wide music playback service
 
Loading...
Searching...
No Matches
dbus_property_notifier.cpp
Go to the documentation of this file.
1/*
2 * Copyright © 2021-2022 UBports Foundation.
3 *
4 * Contact: Alberto Mardegan <mardy@users.sourceforge.net>
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License version 3,
8 * as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
20
21#include <QDBusConnection>
22#include <QDBusMessage>
23#include <QDebug>
24#include <QMetaObject>
25#include <QMetaMethod>
26#include <QMetaProperty>
27#include <QString>
28#include <QVariantList>
29#include <QVariantMap>
30#include <QVector>
31
33{
34 Q_OBJECT
35
36public:
37 DBusPropertyNotifierPrivate(const QDBusConnection &connection,
38 const QString &objectPath,
39 QObject *target);
40
41 void findInterfaceName(const QMetaObject *mo);
42 void connectAllProperties(const QMetaObject *mo);
44 void notify(const QStringList &propertyFilter);
45
46private Q_SLOTS:
47 void onChanged(int propertyIndex);
48 void onPropertyChanged0() { onChanged(0); }
49 void onPropertyChanged1() { onChanged(1); }
50 void onPropertyChanged2() { onChanged(2); }
51 void onPropertyChanged3() { onChanged(3); }
52 void onPropertyChanged4() { onChanged(4); }
53 void onPropertyChanged5() { onChanged(5); }
54 void onPropertyChanged6() { onChanged(6); }
55 void onPropertyChanged7() { onChanged(7); }
56 void onPropertyChanged8() { onChanged(8); }
57 void onPropertyChanged9() { onChanged(9); }
58 void onPropertyChanged10() { onChanged(10); }
59 void onPropertyChanged11() { onChanged(11); }
60 void onPropertyChanged12() { onChanged(12); }
61 void onPropertyChanged13() { onChanged(13); }
62 void onPropertyChanged14() { onChanged(14); }
63 void onPropertyChanged15() { onChanged(15); }
64 void onPropertyChanged16() { onChanged(16); }
65 void onPropertyChanged17() { onChanged(17); }
66 void onPropertyChanged18() { onChanged(18); }
67 void onPropertyChanged19() { onChanged(19); }
68
69private:
70 QDBusConnection m_connection;
71 QString m_objectPath;
72 QString m_interface;
73 QObject *m_target;
74 QVector<int> m_changedPropertyIndexes;
75 QVector<QMetaProperty> m_properties;
76 QVariantMap m_lastValues;
77};
78
80 const QDBusConnection &connection,
81 const QString &objectPath,
82 QObject *target):
83 m_connection(connection),
84 m_objectPath(objectPath),
85 m_target(target)
86{
87 const QMetaObject *mo = target->metaObject();
90}
91
93{
94 int index = mo->indexOfClassInfo("D-Bus Interface");
95 m_interface = QString::fromUtf8(mo->classInfo(index).value());
96}
97
99{
100 QStringList properties;
101 int nextFreeSlot = 0;
102 int indexOfFirstSlot = -1;
103 const QMetaObject *ourMo = metaObject();
104 for (int i = ourMo->methodOffset(); i < ourMo->methodCount(); i++) {
105 if (ourMo->method(i).name().startsWith("onPropertyChanged")) {
106 indexOfFirstSlot = i;
107 break;
108 }
109 }
110
111 for (int i = mo->propertyOffset(); i < mo->propertyCount(); i++) {
112 const QMetaProperty p = mo->property(i);
113 if (!p.hasNotifySignal()) continue;
114 const QMetaMethod signalMethod = p.notifySignal();
115 const QMetaMethod slotMethod =
116 ourMo->method(indexOfFirstSlot + nextFreeSlot);
117 if (Q_UNLIKELY(!slotMethod.isValid())) {
118 qWarning() << "No more slots available!";
119 break;
120 }
121 QObject::connect(m_target, signalMethod, this, slotMethod);
122 m_properties.insert(nextFreeSlot, p);
123 nextFreeSlot++;
124 }
125}
126
127void DBusPropertyNotifierPrivate::onChanged(int propertyIndex)
128{
129 if (m_changedPropertyIndexes.contains(propertyIndex)) return;
130
131 /* If the property value didn't really change, do nothing */
132 const QMetaProperty &p = m_properties[propertyIndex];
133 const QVariant value = p.read(m_target);
134 if (m_lastValues.value(p.name()) == value) {
135 return;
136 }
137
138 /* If the list of properties is empty it means we haven't queued the
139 * emission of the notify signal.
140 */
141 if (m_changedPropertyIndexes.isEmpty()) {
142 QMetaObject::invokeMethod(this,
144 Qt::QueuedConnection);
145 }
146 m_changedPropertyIndexes.append(propertyIndex);
147}
148
150{
151 QVariantMap changedProperties;
152 for (int propertyIndex: m_changedPropertyIndexes) {
153 const QMetaProperty &p = m_properties[propertyIndex];
154 const QVariant value = p.read(m_target);
155 if (m_lastValues.value(p.name()) != value) {
156 changedProperties.insert(p.name(), value);
157 m_lastValues.insert(p.name(), value);
158 }
159 }
160
161 if (!changedProperties.isEmpty()) {
162 QDBusMessage msg = QDBusMessage::createSignal(m_objectPath,
163 QStringLiteral("org.freedesktop.DBus.Properties"),
164 QStringLiteral("PropertiesChanged"));
165 msg.setArguments({
166 m_interface,
167 changedProperties,
168 QStringList {},
169 });
170 m_connection.send(msg);
171 }
172
173 m_changedPropertyIndexes.clear();
174}
175
176void DBusPropertyNotifierPrivate::notify(const QStringList &propertyFilter)
177{
178 QVariantMap changedProperties;
179 const QMetaObject *mo = m_target->metaObject();
180 for (int i = mo->propertyOffset(); i < mo->propertyCount(); i++) {
181 const QMetaProperty p = mo->property(i);
182 const QString propertyName = p.name();
183 if (!propertyFilter.isEmpty() &&
184 !propertyFilter.contains(propertyName)) {
185 continue;
186 }
187 const QVariant value = p.read(m_target);
188 if (m_lastValues.value(propertyName) != value) {
189 changedProperties.insert(propertyName, value);
190 m_lastValues.insert(propertyName, value);
191 }
192 }
193
194 if (!changedProperties.isEmpty()) {
195 QDBusMessage msg = QDBusMessage::createSignal(m_objectPath,
196 QStringLiteral("org.freedesktop.DBus.Properties"),
197 QStringLiteral("PropertiesChanged"));
198 msg.setArguments({
199 m_interface,
200 changedProperties,
201 QStringList {},
202 });
203 m_connection.send(msg);
204 }
205}
206
207DBusPropertyNotifier::DBusPropertyNotifier(const QDBusConnection &connection,
208 const QString &objectPath,
209 QObject *target):
210 QObject(target),
211 d_ptr(new DBusPropertyNotifierPrivate(connection, objectPath, target))
212{
213}
214
216
217void DBusPropertyNotifier::notify(const QStringList &propertyFilter)
218{
220 d->notify(propertyFilter);
221}
222
223#include "dbus_property_notifier.moc"
void notify(const QStringList &propertyFilter)
void findInterfaceName(const QMetaObject *mo)
DBusPropertyNotifierPrivate(const QDBusConnection &connection, const QString &objectPath, QObject *target)
void connectAllProperties(const QMetaObject *mo)
void notify(const QStringList &propertyFilter={})
DBusPropertyNotifier(const QDBusConnection &connection, const QString &objectPath, QObject *target)
virtual ~DBusPropertyNotifier()