1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qdbusabstractadaptor.h"
6#include "qdbusabstractadaptor_p.h"
7
8#include <QtCore/qcoreapplication.h>
9#include <QtCore/qmetaobject.h>
10#include <QtCore/qset.h>
11#include <QtCore/qtimer.h>
12#include <QtCore/qthread.h>
13
14#include "qdbusconnection.h"
15
16#include "qdbusconnection_p.h" // for qDBusParametersForMethod
17#include "qdbusmetatype_p.h"
18
19#include <algorithm>
20
21#ifndef QT_NO_DBUS
22
23QT_BEGIN_NAMESPACE
24
25int QDBusAdaptorConnector::relaySlotMethodIndex()
26{
27 static const int cachedRelaySlotMethodIndex = staticMetaObject.indexOfMethod(method: "relaySlot()");
28 Q_ASSERT(cachedRelaySlotMethodIndex != 0); // 0 should be deleteLater() or destroyed()
29 return cachedRelaySlotMethodIndex;
30}
31
32QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *obj)
33{
34 if (!obj)
35 return nullptr;
36
37 for (QObject *child : std::as_const(t: obj->children())) {
38 QDBusAdaptorConnector *connector = qobject_cast<QDBusAdaptorConnector *>(object: child);
39 if (connector) {
40 connector->polish();
41 return connector;
42 }
43 }
44 return nullptr;
45}
46
47QDBusAdaptorConnector *qDBusFindAdaptorConnector(QDBusAbstractAdaptor *adaptor)
48{
49 return qDBusFindAdaptorConnector(obj: adaptor->parent());
50}
51
52QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *obj)
53{
54 QDBusAdaptorConnector *connector = qDBusFindAdaptorConnector(obj);
55 if (connector)
56 return connector;
57 return new QDBusAdaptorConnector(obj);
58}
59
60QString QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor)
61{
62 return adaptor->d_func()->xml;
63}
64
65void QDBusAbstractAdaptorPrivate::saveIntrospectionXml(QDBusAbstractAdaptor *adaptor,
66 const QString &xml)
67{
68 adaptor->d_func()->xml = xml;
69}
70
71/*!
72 \class QDBusAbstractAdaptor
73 \inmodule QtDBus
74 \since 4.2
75
76 \brief The QDBusAbstractAdaptor class is the base class of D-Bus adaptor classes.
77
78 The QDBusAbstractAdaptor class is the starting point for all objects intending to provide
79 interfaces to the external world using D-Bus. This is accomplished by attaching a one or more
80 classes derived from QDBusAbstractAdaptor to a normal QObject and then registering that QObject
81 with QDBusConnection::registerObject. QDBusAbstractAdaptor objects are intended to be
82 light-weight wrappers, mostly just relaying calls into the real object (its parent) and the
83 signals from it.
84
85 Each QDBusAbstractAdaptor-derived class should define the D-Bus interface it is implementing
86 using the Q_CLASSINFO macro in the class definition. Note that only one interface can be
87 exposed in this way.
88
89 QDBusAbstractAdaptor uses the standard QObject mechanism of signals, slots and properties to
90 determine what signals, methods and properties to export to the bus. Any signal emitted by
91 QDBusAbstractAdaptor-derived classes will be automatically be relayed through any D-Bus
92 connections the object is registered on.
93
94 Classes derived from QDBusAbstractAdaptor must be created on the heap using the \a new operator
95 and must not be deleted by the user (they will be deleted automatically when the object they are
96 connected to is also deleted).
97
98 \sa {usingadaptors.html}{Using adaptors}, QDBusConnection
99*/
100
101/*!
102 Constructs a QDBusAbstractAdaptor with \a obj as the parent object.
103*/
104QDBusAbstractAdaptor::QDBusAbstractAdaptor(QObject* obj)
105 : QObject(*new QDBusAbstractAdaptorPrivate, obj)
106{
107
108 Q_ASSERT_X(obj, Q_FUNC_INFO, "Expected non-null parent");
109
110 QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(obj);
111
112 connector->waitingForPolish = true;
113 QMetaObject::invokeMethod(object: connector, function: &QDBusAdaptorConnector::polish, type: Qt::QueuedConnection);
114}
115
116/*!
117 Destroys the adaptor.
118
119 \warning Adaptors are destroyed automatically when the real object they refer to is
120 destroyed. Do not delete the adaptors yourself.
121*/
122QDBusAbstractAdaptor::~QDBusAbstractAdaptor()
123{
124}
125
126/*!
127 Toggles automatic signal relaying from the real object (see object()).
128
129 Automatic signal relaying consists of signal-to-signal connection of the signals on the parent
130 that have the exact same method signature in both classes.
131
132 If \a enable is set to true, connect the signals; if set to false, disconnect all signals.
133*/
134void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable)
135{
136 const QMetaObject *us = metaObject();
137 const QMetaObject *them = parent()->metaObject();
138 bool connected = false;
139 for (int idx = staticMetaObject.methodCount(); idx < us->methodCount(); ++idx) {
140 QMetaMethod mm = us->method(index: idx);
141
142 if (mm.methodType() != QMetaMethod::Signal)
143 continue;
144
145 // try to connect/disconnect to a signal on the parent that has the same method signature
146 QByteArray sig = QMetaObject::normalizedSignature(method: mm.methodSignature().constData());
147 if (them->indexOfSignal(signal: sig) == -1)
148 continue;
149 sig.prepend(QSIGNAL_CODE + '0');
150 parent()->disconnect(signal: sig, receiver: this, member: sig);
151 if (enable)
152 connected = connect(asender: parent(), asignal: sig, amember: sig) || connected;
153 }
154 d_func()->autoRelaySignals = connected;
155}
156
157/*!
158 Returns \c true if automatic signal relaying from the real object (see object()) is enabled,
159 otherwiser returns \c false.
160
161 \sa setAutoRelaySignals()
162*/
163bool QDBusAbstractAdaptor::autoRelaySignals() const
164{
165 return d_func()->autoRelaySignals;
166}
167
168QDBusAdaptorConnector::QDBusAdaptorConnector(QObject *obj)
169 : QObject(obj), waitingForPolish(false)
170{
171}
172
173QDBusAdaptorConnector::~QDBusAdaptorConnector()
174{
175}
176
177void QDBusAdaptorConnector::addAdaptor(QDBusAbstractAdaptor *adaptor)
178{
179 // find the interface name
180 const QMetaObject *mo = adaptor->metaObject();
181 int ciid = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE);
182 if (ciid != -1) {
183 QMetaClassInfo mci = mo->classInfo(index: ciid);
184 if (*mci.value()) {
185 // find out if this interface exists first
186 const char *interface = mci.value();
187 AdaptorMap::Iterator it = std::lower_bound(first: adaptors.begin(), last: adaptors.end(),
188 val: QByteArray(interface));
189 if (it != adaptors.end() && qstrcmp(str1: interface, str2: it->interface) == 0) {
190 // exists. Replace it (though it's probably the same)
191 if (it->adaptor != adaptor) {
192 // reconnect the signals
193 disconnectAllSignals(object: it->adaptor);
194 connectAllSignals(object: adaptor);
195 }
196 it->adaptor = adaptor;
197 } else {
198 // create a new one
199 AdaptorData entry;
200 entry.interface = interface;
201 entry.adaptor = adaptor;
202 adaptors << entry;
203
204 // connect the adaptor's signals to our relaySlot slot
205 connectAllSignals(object: adaptor);
206 }
207 }
208 }
209}
210
211void QDBusAdaptorConnector::disconnectAllSignals(QObject *obj)
212{
213 QMetaObject::disconnect(sender: obj, signal_index: -1, receiver: this, method_index: relaySlotMethodIndex());
214}
215
216void QDBusAdaptorConnector::connectAllSignals(QObject *obj)
217{
218 QMetaObject::connect(sender: obj, signal_index: -1, receiver: this, method_index: relaySlotMethodIndex(), type: Qt::DirectConnection);
219}
220
221void QDBusAdaptorConnector::polish()
222{
223 if (!waitingForPolish)
224 return; // avoid working multiple times if multiple adaptors were added
225
226 waitingForPolish = false;
227 for (QObject *child : std::as_const(t: parent()->children())) {
228 QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(object: child);
229 if (adaptor)
230 addAdaptor(adaptor);
231 }
232
233 // sort the adaptor list
234 std::sort(first: adaptors.begin(), last: adaptors.end());
235}
236
237void QDBusAdaptorConnector::relaySlot(QMethodRawArguments argv)
238{
239 QObject *sndr = sender();
240 if (Q_LIKELY(sndr)) {
241 relay(sender: sndr, id: senderSignalIndex(), argv.arguments);
242 } else {
243 qWarning(msg: "QtDBus: cannot relay signals from parent %s(%p \"%s\") unless they are emitted in the object's thread %s(%p \"%s\"). "
244 "Current thread is %s(%p \"%s\").",
245 parent()->metaObject()->className(), parent(), qPrintable(parent()->objectName()),
246 parent()->thread()->metaObject()->className(), parent()->thread(), qPrintable(parent()->thread()->objectName()),
247 QThread::currentThread()->metaObject()->className(), QThread::currentThread(), qPrintable(QThread::currentThread()->objectName()));
248 }
249}
250
251void QDBusAdaptorConnector::relay(QObject *senderObj, int lastSignalIdx, void **argv)
252{
253 if (lastSignalIdx < QObject::staticMetaObject.methodCount())
254 // QObject signal (destroyed(QObject *)) -- ignore
255 return;
256
257 QMetaMethod mm = senderObj->metaObject()->method(index: lastSignalIdx);
258 const QMetaObject *senderMetaObject = mm.enclosingMetaObject();
259
260 QObject *realObject = senderObj;
261 if (qobject_cast<QDBusAbstractAdaptor *>(object: senderObj))
262 // it's an adaptor, so the real object is in fact its parent
263 realObject = realObject->parent();
264
265 // break down the parameter list
266 QList<QMetaType> types;
267 QString errorMsg;
268 int inputCount = qDBusParametersForMethod(mm, metaTypes&: types, errorMsg);
269 if (inputCount == -1) {
270 // invalid signal signature
271 qWarning(msg: "QDBusAbstractAdaptor: Cannot relay signal %s::%s: %s",
272 senderMetaObject->className(), mm.methodSignature().constData(),
273 qPrintable(errorMsg));
274 return;
275 }
276 if (inputCount + 1 != types.size() ||
277 types.at(i: inputCount) == QDBusMetaTypeId::message()) {
278 // invalid signal signature
279 qWarning(msg: "QDBusAbstractAdaptor: Cannot relay signal %s::%s",
280 senderMetaObject->className(), mm.methodSignature().constData());
281 return;
282 }
283
284 QVariantList args;
285 const int numTypes = types.size();
286 args.reserve(asize: numTypes - 1);
287 for (int i = 1; i < numTypes; ++i)
288 args << QVariant(QMetaType(types.at(i)), argv[i]);
289
290 // now emit the signal with all the information
291 emit relaySignal(obj: realObject, metaObject: senderMetaObject, sid: lastSignalIdx, args);
292}
293
294QT_END_NAMESPACE
295
296#include "moc_qdbusabstractadaptor_p.cpp"
297#include "moc_qdbusabstractadaptor.cpp"
298
299#endif // QT_NO_DBUS
300

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/dbus/qdbusabstractadaptor.cpp