Music Hub  ..
A session-wide music playback service
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 
19 #include "dbus_property_notifier.h"
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 
36 public:
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);
43  void deliverNotifySignal();
44  void notify(const QStringList &propertyFilter);
45 
46 private 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 
69 private:
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 
127 void 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 
176 void 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 
207 DBusPropertyNotifier::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 
217 void DBusPropertyNotifier::notify(const QStringList &propertyFilter)
218 {
220  d->notify(propertyFilter);
221 }
222 
223 #include "dbus_property_notifier.moc"
QObject
DBusPropertyNotifier::~DBusPropertyNotifier
virtual ~DBusPropertyNotifier()
dbus_property_notifier.h
mpris::objectPath
const QString objectPath
Definition: media_player2.cpp:39
DBusPropertyNotifier::notify
void notify(const QStringList &propertyFilter={})
Definition: dbus_property_notifier.cpp:217
DBusPropertyNotifierPrivate::DBusPropertyNotifierPrivate
DBusPropertyNotifierPrivate(const QDBusConnection &connection, const QString &objectPath, QObject *target)
Definition: dbus_property_notifier.cpp:79
DBusPropertyNotifierPrivate::connectAllProperties
void connectAllProperties(const QMetaObject *mo)
Definition: dbus_property_notifier.cpp:98
DBusPropertyNotifierPrivate::findInterfaceName
void findInterfaceName(const QMetaObject *mo)
Definition: dbus_property_notifier.cpp:92
DBusPropertyNotifierPrivate::notify
void notify(const QStringList &propertyFilter)
Definition: dbus_property_notifier.cpp:176
DBusPropertyNotifierPrivate::deliverNotifySignal
void deliverNotifySignal()
Definition: dbus_property_notifier.cpp:149
DBusPropertyNotifier
Definition: dbus_property_notifier.h:28
DBusPropertyNotifier::DBusPropertyNotifier
DBusPropertyNotifier(const QDBusConnection &connection, const QString &objectPath, QObject *target)
Definition: dbus_property_notifier.cpp:207
DBusPropertyNotifierPrivate
Definition: dbus_property_notifier.cpp:32