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 "qdbusabstractinterface.h" |
6 | #include "qdbusabstractinterface_p.h" |
7 | |
8 | #include <qcoreapplication.h> |
9 | #include <qthread.h> |
10 | |
11 | #include "qdbusargument.h" |
12 | #include "qdbuspendingcall.h" |
13 | #include "qdbusmessage_p.h" |
14 | #include "qdbusmetaobject_p.h" |
15 | #include "qdbusmetatype_p.h" |
16 | #include "qdbusservicewatcher.h" |
17 | #include "qdbusutil_p.h" |
18 | |
19 | #include <qdebug.h> |
20 | |
21 | #ifndef QT_NO_DBUS |
22 | |
23 | QT_BEGIN_NAMESPACE |
24 | |
25 | using namespace Qt::StringLiterals; |
26 | |
27 | namespace { |
28 | // ### Qt6: change to a regular QEvent (customEvent) |
29 | // We need to use a QMetaCallEvent here because we can't override customEvent() in |
30 | // Qt 5. Since QDBusAbstractInterface is meant to be derived from, the vtables of |
31 | // classes in generated code will have a pointer to QObject::customEvent instead |
32 | // of to QDBusAbstractInterface::customEvent. |
33 | // See solution in Patch Set 1 of this change in the Qt Gerrit servers. |
34 | // (https://codereview.qt-project.org/#/c/126384/1) |
35 | class DisconnectRelayEvent : public QAbstractMetaCallEvent |
36 | { |
37 | public: |
38 | DisconnectRelayEvent(QObject *sender, const QMetaMethod &m) |
39 | : QAbstractMetaCallEvent(sender, m.methodIndex()) |
40 | {} |
41 | |
42 | void placeMetaCall(QObject *object) override |
43 | { |
44 | QDBusAbstractInterface *iface = static_cast<QDBusAbstractInterface *>(object); |
45 | QDBusAbstractInterfacePrivate::finishDisconnectNotify(iface, signalId: signalId()); |
46 | } |
47 | }; |
48 | } |
49 | |
50 | static QDBusError checkIfValid(const QString &service, const QString &path, |
51 | const QString &interface, bool isDynamic, bool isPeer) |
52 | { |
53 | // We should be throwing exceptions here... oh well |
54 | QDBusError error; |
55 | |
56 | // dynamic interfaces (QDBusInterface) can have empty interfaces, but not service and object paths |
57 | // non-dynamic is the opposite: service and object paths can be empty, but not the interface |
58 | if (!isDynamic) { |
59 | // use assertion here because this should never happen, at all |
60 | Q_ASSERT_X(!interface.isEmpty(), "QDBusAbstractInterface" , "Interface name cannot be empty" ); |
61 | } |
62 | if (!QDBusUtil::checkBusName(name: service, empty: (isDynamic && !isPeer) ? QDBusUtil::EmptyNotAllowed : QDBusUtil::EmptyAllowed, error: &error)) |
63 | return error; |
64 | if (!QDBusUtil::checkObjectPath(path, empty: isDynamic ? QDBusUtil::EmptyNotAllowed : QDBusUtil::EmptyAllowed, error: &error)) |
65 | return error; |
66 | if (!QDBusUtil::checkInterfaceName(name: interface, empty: QDBusUtil::EmptyAllowed, error: &error)) |
67 | return error; |
68 | |
69 | // no error |
70 | return QDBusError(); |
71 | } |
72 | |
73 | QDBusAbstractInterfacePrivate::QDBusAbstractInterfacePrivate(const QString &serv, |
74 | const QString &p, |
75 | const QString &iface, |
76 | const QDBusConnection& con, |
77 | bool isDynamic) |
78 | : connection(con), service(serv), path(p), interface(iface), |
79 | lastError(checkIfValid(service: serv, path: p, interface: iface, isDynamic, isPeer: (connectionPrivate() && |
80 | connectionPrivate()->mode == QDBusConnectionPrivate::PeerMode))), |
81 | timeout(-1), |
82 | isValid(!lastError.isValid()) |
83 | { |
84 | if (!isValid) |
85 | return; |
86 | |
87 | if (!connection.isConnected()) { |
88 | lastError = QDBusError(QDBusError::Disconnected, |
89 | QDBusUtil::disconnectedErrorMessage()); |
90 | } |
91 | } |
92 | |
93 | void QDBusAbstractInterfacePrivate::initOwnerTracking() |
94 | { |
95 | if (!isValid || !connection.isConnected() || !connectionPrivate()->shouldWatchService(service)) |
96 | return; |
97 | |
98 | QObject::connect(sender: new QDBusServiceWatcher(service, connection, QDBusServiceWatcher::WatchForOwnerChange, q_func()), |
99 | SIGNAL(serviceOwnerChanged(QString,QString,QString)), |
100 | receiver: q_func(), SLOT(_q_serviceOwnerChanged(QString,QString,QString))); |
101 | |
102 | currentOwner = connectionPrivate()->getNameOwner(service); |
103 | if (currentOwner.isEmpty()) |
104 | lastError = connectionPrivate()->lastError; |
105 | } |
106 | |
107 | bool QDBusAbstractInterfacePrivate::canMakeCalls() const |
108 | { |
109 | // recheck only if we have a wildcard (i.e. empty) service or path |
110 | // if any are empty, set the error message according to QDBusUtil |
111 | if (service.isEmpty() && connectionPrivate()->mode != QDBusConnectionPrivate::PeerMode) |
112 | return QDBusUtil::checkBusName(name: service, empty: QDBusUtil::EmptyNotAllowed, error: &lastError); |
113 | if (path.isEmpty()) |
114 | return QDBusUtil::checkObjectPath(path, empty: QDBusUtil::EmptyNotAllowed, error: &lastError); |
115 | return true; |
116 | } |
117 | |
118 | bool QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp, void *returnValuePtr) const |
119 | { |
120 | if (!isValid || !canMakeCalls()) // can't make calls |
121 | return false; |
122 | |
123 | QMetaType type = mp.metaType(); |
124 | // is this metatype registered? |
125 | const char *expectedSignature = "" ; |
126 | if (type.id() != QMetaType::QVariant) { |
127 | expectedSignature = QDBusMetaType::typeToSignature(type); |
128 | if (expectedSignature == nullptr) { |
129 | qWarning(msg: "QDBusAbstractInterface: type %s must be registered with Qt D-Bus before it can be " |
130 | "used to read property %s.%s" , |
131 | mp.typeName(), qPrintable(interface), mp.name()); |
132 | lastError = QDBusError(QDBusError::Failed, "Unregistered type %1 cannot be handled"_L1 |
133 | .arg(args: QLatin1StringView(mp.typeName()))); |
134 | return false; |
135 | } |
136 | } |
137 | |
138 | // try to read this property |
139 | QDBusMessage msg = QDBusMessage::createMethodCall(destination: service, path, |
140 | interface: QDBusUtil::dbusInterfaceProperties(), |
141 | QStringLiteral("Get" )); |
142 | QDBusMessagePrivate::setParametersValidated(msg, enable: true); |
143 | msg << interface << QString::fromUtf8(utf8: mp.name()); |
144 | QDBusMessage reply = connection.call(message: msg, mode: QDBus::Block, timeout); |
145 | |
146 | if (reply.type() != QDBusMessage::ReplyMessage) { |
147 | lastError = QDBusError(reply); |
148 | return false; |
149 | } |
150 | if (reply.signature() != "v"_L1 ) { |
151 | QString errmsg = |
152 | "Invalid signature '%1' in return from call to " DBUS_INTERFACE_PROPERTIES ""_L1 ; |
153 | lastError = QDBusError(QDBusError::InvalidSignature, std::move(errmsg).arg(a: reply.signature())); |
154 | return false; |
155 | } |
156 | |
157 | QByteArray foundSignature; |
158 | const char *foundType = nullptr; |
159 | QVariant value = qvariant_cast<QDBusVariant>(v: reply.arguments().at(i: 0)).variant(); |
160 | |
161 | if (value.metaType() == type || type.id() == QMetaType::QVariant |
162 | || (expectedSignature[0] == 'v' && expectedSignature[1] == '\0')) { |
163 | // simple match |
164 | if (type.id() == QMetaType::QVariant) { |
165 | *reinterpret_cast<QVariant*>(returnValuePtr) = value; |
166 | } else { |
167 | QMetaType(type).destruct(data: returnValuePtr); |
168 | QMetaType(type).construct(where: returnValuePtr, copy: value.constData()); |
169 | } |
170 | return true; |
171 | } |
172 | |
173 | if (value.metaType() == QMetaType::fromType<QDBusArgument>()) { |
174 | QDBusArgument arg = qvariant_cast<QDBusArgument>(v: value); |
175 | |
176 | foundType = "user type" ; |
177 | foundSignature = arg.currentSignature().toLatin1(); |
178 | if (foundSignature == expectedSignature) { |
179 | // signatures match, we can demarshall |
180 | return QDBusMetaType::demarshall(arg, id: QMetaType(type), data: returnValuePtr); |
181 | } |
182 | } else { |
183 | foundType = value.typeName(); |
184 | foundSignature = QDBusMetaType::typeToSignature(type: value.metaType()); |
185 | } |
186 | |
187 | // there was an error... |
188 | const auto errmsg = "Unexpected '%1' (%2) when retrieving property '%3.%4' " |
189 | "(expected type '%5' (%6))"_L1 ; |
190 | lastError = QDBusError(QDBusError::InvalidSignature, |
191 | errmsg.arg(args: QLatin1StringView(foundType), |
192 | args: QLatin1StringView(foundSignature), |
193 | args: interface, |
194 | args: QLatin1StringView(mp.name()), |
195 | args: QLatin1StringView(mp.typeName()), |
196 | args: QLatin1StringView(expectedSignature))); |
197 | return false; |
198 | } |
199 | |
200 | bool QDBusAbstractInterfacePrivate::setProperty(const QMetaProperty &mp, const QVariant &value) |
201 | { |
202 | if (!isValid || !canMakeCalls()) // can't make calls |
203 | return false; |
204 | |
205 | // send the value |
206 | QDBusMessage msg = QDBusMessage::createMethodCall(destination: service, path, |
207 | interface: QDBusUtil::dbusInterfaceProperties(), |
208 | QStringLiteral("Set" )); |
209 | QDBusMessagePrivate::setParametersValidated(msg, enable: true); |
210 | msg << interface << QString::fromUtf8(utf8: mp.name()) << QVariant::fromValue(value: QDBusVariant(value)); |
211 | QDBusMessage reply = connection.call(message: msg, mode: QDBus::Block, timeout); |
212 | |
213 | if (reply.type() != QDBusMessage::ReplyMessage) { |
214 | lastError = QDBusError(reply); |
215 | return false; |
216 | } |
217 | return true; |
218 | } |
219 | |
220 | void QDBusAbstractInterfacePrivate::_q_serviceOwnerChanged(const QString &name, |
221 | const QString &oldOwner, |
222 | const QString &newOwner) |
223 | { |
224 | Q_UNUSED(oldOwner); |
225 | Q_UNUSED(name); |
226 | //qDebug() << "QDBusAbstractInterfacePrivate serviceOwnerChanged" << name << oldOwner << newOwner; |
227 | Q_ASSERT(name == service); |
228 | currentOwner = newOwner; |
229 | } |
230 | |
231 | QDBusAbstractInterfaceBase::QDBusAbstractInterfaceBase(QDBusAbstractInterfacePrivate &d, QObject *parent) |
232 | : QObject(d, parent) |
233 | { |
234 | } |
235 | |
236 | int QDBusAbstractInterfaceBase::qt_metacall(QMetaObject::Call _c, int _id, void **_a) |
237 | { |
238 | int saved_id = _id; |
239 | _id = QObject::qt_metacall(_c, _id, _a); |
240 | if (_id < 0) |
241 | return _id; |
242 | |
243 | if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty) { |
244 | QMetaProperty mp = metaObject()->property(index: saved_id); |
245 | int &status = *reinterpret_cast<int *>(_a[2]); |
246 | |
247 | if (_c == QMetaObject::WriteProperty) { |
248 | QVariant value; |
249 | if (mp.metaType() == QMetaType::fromType<QDBusVariant>()) |
250 | value = reinterpret_cast<const QDBusVariant*>(_a[0])->variant(); |
251 | else |
252 | value = QVariant(mp.metaType(), _a[0]); |
253 | status = d_func()->setProperty(mp, value) ? 1 : 0; |
254 | } else { |
255 | bool readStatus = d_func()->property(mp, returnValuePtr: _a[0]); |
256 | // Caller supports QVariant returns? Then we can also report errors |
257 | // by storing an invalid variant. |
258 | if (!readStatus && _a[1]) { |
259 | status = 0; |
260 | reinterpret_cast<QVariant*>(_a[1])->clear(); |
261 | } |
262 | } |
263 | _id = -1; |
264 | } |
265 | return _id; |
266 | } |
267 | |
268 | /*! |
269 | \class QDBusAbstractInterface |
270 | \inmodule QtDBus |
271 | \since 4.2 |
272 | |
273 | \brief The QDBusAbstractInterface class is the base class for all D-Bus interfaces in the Qt D-Bus binding, allowing access to remote interfaces. |
274 | |
275 | Generated-code classes also derive from QDBusAbstractInterface, |
276 | all methods described here are also valid for generated-code |
277 | classes. In addition to those described here, generated-code |
278 | classes provide member functions for the remote methods, which |
279 | allow for compile-time checking of the correct parameters and |
280 | return values, as well as property type-matching and signal |
281 | parameter-matching. |
282 | |
283 | \sa {qdbusxml2cpp.html}{The QDBus compiler}, QDBusInterface |
284 | */ |
285 | |
286 | /*! |
287 | \internal |
288 | This is the constructor called from QDBusInterface::QDBusInterface. |
289 | */ |
290 | QDBusAbstractInterface::QDBusAbstractInterface(QDBusAbstractInterfacePrivate &d, QObject *parent) |
291 | : QDBusAbstractInterfaceBase(d, parent) |
292 | { |
293 | d.initOwnerTracking(); |
294 | } |
295 | |
296 | /*! |
297 | \internal |
298 | This is the constructor called from static classes derived from |
299 | QDBusAbstractInterface (i.e., those generated by dbusxml2cpp). |
300 | */ |
301 | QDBusAbstractInterface::QDBusAbstractInterface(const QString &service, const QString &path, |
302 | const char *interface, const QDBusConnection &con, |
303 | QObject *parent) |
304 | : QDBusAbstractInterfaceBase(*new QDBusAbstractInterfacePrivate(service, path, QString::fromLatin1(ba: interface), |
305 | con, false), parent) |
306 | { |
307 | // keep track of the service owner |
308 | d_func()->initOwnerTracking(); |
309 | } |
310 | |
311 | /*! |
312 | Releases this object's resources. |
313 | */ |
314 | QDBusAbstractInterface::~QDBusAbstractInterface() |
315 | { |
316 | } |
317 | |
318 | /*! |
319 | Returns \c true if this is a valid reference to a remote object. It returns \c false if |
320 | there was an error during the creation of this interface (for instance, if the remote |
321 | application does not exist). |
322 | |
323 | Note: when dealing with remote objects, it is not always possible to determine if it |
324 | exists when creating a QDBusInterface. |
325 | */ |
326 | bool QDBusAbstractInterface::isValid() const |
327 | { |
328 | Q_D(const QDBusAbstractInterface); |
329 | /* We don't retrieve the owner name for peer connections */ |
330 | if (d->connectionPrivate() && d->connectionPrivate()->mode == QDBusConnectionPrivate::PeerMode) { |
331 | return d->isValid; |
332 | } else { |
333 | return !d->currentOwner.isEmpty(); |
334 | } |
335 | } |
336 | |
337 | /*! |
338 | Returns the connection this interface is associated with. |
339 | */ |
340 | QDBusConnection QDBusAbstractInterface::connection() const |
341 | { |
342 | return d_func()->connection; |
343 | } |
344 | |
345 | /*! |
346 | Returns the name of the service this interface is associated with. |
347 | */ |
348 | QString QDBusAbstractInterface::service() const |
349 | { |
350 | return d_func()->service; |
351 | } |
352 | |
353 | /*! |
354 | Returns the object path that this interface is associated with. |
355 | */ |
356 | QString QDBusAbstractInterface::path() const |
357 | { |
358 | return d_func()->path; |
359 | } |
360 | |
361 | /*! |
362 | Returns the name of this interface. |
363 | */ |
364 | QString QDBusAbstractInterface::interface() const |
365 | { |
366 | return d_func()->interface; |
367 | } |
368 | |
369 | /*! |
370 | Returns the error the last operation produced, or an invalid error if the last operation did not |
371 | produce an error. |
372 | */ |
373 | QDBusError QDBusAbstractInterface::lastError() const |
374 | { |
375 | return d_func()->lastError; |
376 | } |
377 | |
378 | /*! |
379 | Sets the timeout in milliseconds for all future DBus calls to \a timeout. |
380 | -1 means the default DBus timeout (usually 25 seconds). |
381 | |
382 | \since 4.8 |
383 | */ |
384 | void QDBusAbstractInterface::setTimeout(int timeout) |
385 | { |
386 | d_func()->timeout = timeout; |
387 | } |
388 | |
389 | /*! |
390 | Returns the current value of the timeout in milliseconds. |
391 | -1 means the default DBus timeout (usually 25 seconds). |
392 | |
393 | \since 4.8 |
394 | */ |
395 | int QDBusAbstractInterface::timeout() const |
396 | { |
397 | return d_func()->timeout; |
398 | } |
399 | |
400 | /*! |
401 | Places a call to the remote method specified by \a method on this interface, using \a args as |
402 | arguments. This function returns the message that was received as a reply, which can be a normal |
403 | QDBusMessage::ReplyMessage (indicating success) or QDBusMessage::ErrorMessage (if the call |
404 | failed). The \a mode parameter specifies how this call should be placed. |
405 | |
406 | If the call succeeds, lastError() will be cleared; otherwise, it will contain the error this |
407 | call produced. |
408 | |
409 | Normally, you should place calls using call(). |
410 | |
411 | \warning If you use \c UseEventLoop, your code must be prepared to deal with any reentrancy: |
412 | other method calls and signals may be delivered before this function returns, as well |
413 | as other Qt queued signals and events. |
414 | |
415 | \threadsafe |
416 | */ |
417 | QDBusMessage QDBusAbstractInterface::callWithArgumentList(QDBus::CallMode mode, |
418 | const QString& method, |
419 | const QList<QVariant>& args) |
420 | { |
421 | Q_D(QDBusAbstractInterface); |
422 | |
423 | if (!d->isValid || !d->canMakeCalls()) |
424 | return QDBusMessage::createError(err: d->lastError); |
425 | |
426 | QString m = method; |
427 | // split out the signature from the method |
428 | int pos = method.indexOf(c: u'.'); |
429 | if (pos != -1) |
430 | m.truncate(pos); |
431 | |
432 | if (mode == QDBus::AutoDetect) { |
433 | // determine if this a sync or async call |
434 | mode = QDBus::Block; |
435 | const QMetaObject *mo = metaObject(); |
436 | QByteArray match = m.toLatin1(); |
437 | |
438 | for (int i = staticMetaObject.methodCount(); i < mo->methodCount(); ++i) { |
439 | QMetaMethod mm = mo->method(index: i); |
440 | if (mm.name() == match) { |
441 | // found a method with the same name as what we're looking for |
442 | // hopefully, nobody is overloading asynchronous and synchronous methods with |
443 | // the same name |
444 | |
445 | QList<QByteArray> tags = QByteArray(mm.tag()).split(sep: ' '); |
446 | if (tags.contains(t: "Q_NOREPLY" )) |
447 | mode = QDBus::NoBlock; |
448 | |
449 | break; |
450 | } |
451 | } |
452 | } |
453 | |
454 | // qDebug() << "QDBusAbstractInterface" << "Service" << service() << "Path:" << path(); |
455 | QDBusMessage msg = QDBusMessage::createMethodCall(destination: service(), path: path(), interface: interface(), method: m); |
456 | QDBusMessagePrivate::setParametersValidated(msg, enable: true); |
457 | msg.setArguments(args); |
458 | |
459 | QDBusMessage reply = d->connection.call(message: msg, mode, timeout: d->timeout); |
460 | if (thread() == QThread::currentThread()) |
461 | d->lastError = QDBusError(reply); // will clear if reply isn't an error |
462 | |
463 | // ensure that there is at least one element |
464 | if (reply.arguments().isEmpty()) |
465 | reply << QVariant(); |
466 | |
467 | return reply; |
468 | } |
469 | |
470 | /*! |
471 | \since 4.5 |
472 | Places a call to the remote method specified by \a method on this |
473 | interface, using \a args as arguments. This function returns a |
474 | QDBusPendingCall object that can be used to track the status of the |
475 | reply and access its contents once it has arrived. |
476 | |
477 | Normally, you should place calls using asyncCall(). |
478 | |
479 | \threadsafe |
480 | */ |
481 | QDBusPendingCall QDBusAbstractInterface::asyncCallWithArgumentList(const QString& method, |
482 | const QList<QVariant>& args) |
483 | { |
484 | Q_D(QDBusAbstractInterface); |
485 | |
486 | if (!d->isValid || !d->canMakeCalls()) |
487 | return QDBusPendingCall::fromError(error: d->lastError); |
488 | |
489 | QDBusMessage msg = QDBusMessage::createMethodCall(destination: service(), path: path(), interface: interface(), method); |
490 | QDBusMessagePrivate::setParametersValidated(msg, enable: true); |
491 | msg.setArguments(args); |
492 | return d->connection.asyncCall(message: msg, timeout: d->timeout); |
493 | } |
494 | |
495 | /*! |
496 | Places a call to the remote method specified by \a method |
497 | on this interface, using \a args as arguments. This function |
498 | returns immediately after queueing the call. The reply from |
499 | the remote function is delivered to the \a returnMethod on |
500 | object \a receiver. If an error occurs, the \a errorMethod |
501 | on object \a receiver is called instead. |
502 | |
503 | This function returns \c true if the queueing succeeds. It does |
504 | not indicate that the executed call succeeded. If it fails, |
505 | the \a errorMethod is called. If the queueing failed, this |
506 | function returns \c false and no slot will be called. |
507 | |
508 | The \a returnMethod must have as its parameters the types returned |
509 | by the function call. Optionally, it may have a QDBusMessage |
510 | parameter as its last or only parameter. The \a errorMethod must |
511 | have a QDBusError as its only parameter. |
512 | |
513 | \since 4.3 |
514 | \sa QDBusError, QDBusMessage |
515 | */ |
516 | bool QDBusAbstractInterface::callWithCallback(const QString &method, |
517 | const QList<QVariant> &args, |
518 | QObject *receiver, |
519 | const char *returnMethod, |
520 | const char *errorMethod) |
521 | { |
522 | Q_D(QDBusAbstractInterface); |
523 | |
524 | if (!d->isValid || !d->canMakeCalls()) |
525 | return false; |
526 | |
527 | QDBusMessage msg = QDBusMessage::createMethodCall(destination: service(), |
528 | path: path(), |
529 | interface: interface(), |
530 | method); |
531 | QDBusMessagePrivate::setParametersValidated(msg, enable: true); |
532 | msg.setArguments(args); |
533 | |
534 | d->lastError = QDBusError(); |
535 | return d->connection.callWithCallback(message: msg, |
536 | receiver, |
537 | returnMethod, |
538 | errorMethod, |
539 | timeout: d->timeout); |
540 | } |
541 | |
542 | /*! |
543 | \overload |
544 | |
545 | This function is deprecated. Please use the overloaded version. |
546 | |
547 | Places a call to the remote method specified by \a method |
548 | on this interface, using \a args as arguments. This function |
549 | returns immediately after queueing the call. The reply from |
550 | the remote function or any errors emitted by it are delivered |
551 | to the \a slot slot on object \a receiver. |
552 | |
553 | This function returns \c true if the queueing succeeded: it does |
554 | not indicate that the call succeeded. If it failed, the slot |
555 | will be called with an error message. lastError() will not be |
556 | set under those circumstances. |
557 | |
558 | \sa QDBusError, QDBusMessage |
559 | */ |
560 | bool QDBusAbstractInterface::callWithCallback(const QString &method, |
561 | const QList<QVariant> &args, |
562 | QObject *receiver, |
563 | const char *slot) |
564 | { |
565 | return callWithCallback(method, args, receiver, returnMethod: slot, errorMethod: nullptr); |
566 | } |
567 | |
568 | /*! |
569 | \internal |
570 | Catch signal connections. |
571 | */ |
572 | void QDBusAbstractInterface::connectNotify(const QMetaMethod &signal) |
573 | { |
574 | // someone connecting to one of our signals |
575 | Q_D(QDBusAbstractInterface); |
576 | if (!d->isValid) |
577 | return; |
578 | |
579 | // we end up recursing here, so optimize away |
580 | static const QMetaMethod destroyedSignal = QMetaMethod::fromSignal(signal: &QDBusAbstractInterface::destroyed); |
581 | if (signal == destroyedSignal) |
582 | return; |
583 | |
584 | QDBusConnectionPrivate *conn = d->connectionPrivate(); |
585 | if (conn) { |
586 | conn->connectRelay(service: d->service, path: d->path, interface: d->interface, |
587 | receiver: this, signal); |
588 | } |
589 | } |
590 | |
591 | /*! |
592 | \internal |
593 | Catch signal disconnections. |
594 | */ |
595 | void QDBusAbstractInterface::disconnectNotify(const QMetaMethod &signal) |
596 | { |
597 | // someone disconnecting from one of our signals |
598 | Q_D(QDBusAbstractInterface); |
599 | if (!d->isValid) |
600 | return; |
601 | |
602 | // disconnection is just resource freeing, so it can be delayed; |
603 | // let's do that later, after all the QObject mutexes have been unlocked. |
604 | QCoreApplication::postEvent(receiver: this, event: new DisconnectRelayEvent(this, signal)); |
605 | } |
606 | |
607 | /*! |
608 | \internal |
609 | Continues the disconnect notification from above. |
610 | */ |
611 | void QDBusAbstractInterfacePrivate::finishDisconnectNotify(QDBusAbstractInterface *ptr, int signalId) |
612 | { |
613 | QDBusAbstractInterfacePrivate *d = ptr->d_func(); |
614 | QDBusConnectionPrivate *conn = d->connectionPrivate(); |
615 | if (!conn) |
616 | return; |
617 | |
618 | const QMetaObject *mo = ptr->metaObject(); |
619 | QMetaMethod signal = signalId >= 0 ? mo->method(index: signalId) : QMetaMethod(); |
620 | if (signal.isValid()) { |
621 | if (!ptr->isSignalConnected(signal)) |
622 | return conn->disconnectRelay(service: d->service, path: d->path, interface: d->interface, |
623 | receiver: ptr, signal); |
624 | } else { |
625 | // wildcard disconnecting, we need to figure out which of our signals are |
626 | // no longer connected to anything |
627 | int midx = QObject::staticMetaObject.methodCount(); |
628 | const int end = mo->methodCount(); |
629 | for ( ; midx < end; ++midx) { |
630 | QMetaMethod mm = mo->method(index: midx); |
631 | if (mm.methodType() == QMetaMethod::Signal && !ptr->isSignalConnected(signal: mm)) |
632 | conn->disconnectRelay(service: d->service, path: d->path, interface: d->interface, receiver: ptr, signal: mm); |
633 | } |
634 | } |
635 | } |
636 | |
637 | /*! |
638 | \internal |
639 | Get the value of the property \a propname. |
640 | */ |
641 | QVariant QDBusAbstractInterface::internalPropGet(const char *propname) const |
642 | { |
643 | // assume this property exists and is readable |
644 | // we're only called from generated code anyways |
645 | |
646 | return property(name: propname); |
647 | } |
648 | |
649 | /*! |
650 | \internal |
651 | Set the value of the property \a propname to \a value. |
652 | */ |
653 | void QDBusAbstractInterface::internalPropSet(const char *propname, const QVariant &value) |
654 | { |
655 | setProperty(name: propname, value); |
656 | } |
657 | |
658 | /*! |
659 | \fn QDBusAbstractInterface::call(const QString &message) |
660 | \internal |
661 | */ |
662 | |
663 | /*! |
664 | \fn template <typename...Args> QDBusMessage QDBusAbstractInterface::call(const QString &method, Args&&...args) |
665 | |
666 | Calls the method \a method on this interface and passes \a args to the method. |
667 | All \a args must be convertible to QVariant. |
668 | |
669 | The parameters to \c call are passed on to the remote function via D-Bus as input |
670 | arguments. Output arguments are returned in the QDBusMessage reply. If the reply is an error |
671 | reply, lastError() will also be set to the contents of the error message. |
672 | |
673 | It can be used the following way: |
674 | |
675 | \snippet code/src_qdbus_qdbusabstractinterface.cpp 0 |
676 | |
677 | This example illustrates function calling with 0, 1 and 2 parameters and illustrates different |
678 | parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one |
679 | Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array). |
680 | |
681 | \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments. |
682 | |
683 | \sa callWithArgumentList() |
684 | */ |
685 | |
686 | /*! |
687 | \fn QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &message) |
688 | \internal |
689 | */ |
690 | |
691 | /*! |
692 | \fn template <typename...Args> QDBusMessage QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &method, Args&&...args) |
693 | |
694 | \overload |
695 | |
696 | Calls the method \a method on this interface and passes \a args to the method. |
697 | All \a args must be convertible to QVariant. |
698 | |
699 | If \a mode is \c NoWaitForReply, then this function will return immediately after |
700 | placing the call, without waiting for a reply from the remote |
701 | method. Otherwise, \a mode indicates whether this function should |
702 | activate the Qt Event Loop while waiting for the reply to arrive. |
703 | |
704 | If this function reenters the Qt event loop in order to wait for the |
705 | reply, it will exclude user input. During the wait, it may deliver |
706 | signals and other method calls to your application. Therefore, it |
707 | must be prepared to handle a reentrancy whenever a call is placed |
708 | with call(). |
709 | |
710 | \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments. |
711 | |
712 | \sa callWithArgumentList() |
713 | */ |
714 | |
715 | /*! |
716 | \fn QDBusAbstractInterface::asyncCall(const QString &message) |
717 | \internal |
718 | */ |
719 | |
720 | /*! |
721 | \fn template <typename...Args> QDBusPendingCall QDBusAbstractInterface::asyncCall(const QString &method, Args&&...args) |
722 | |
723 | Calls the method \a method on this interface and passes \a args to the method. |
724 | All \a args must be convertible to QVariant. |
725 | |
726 | The parameters to \c call are passed on to the remote function via D-Bus as input |
727 | arguments. The returned QDBusPendingCall object can be used to find out information about |
728 | the reply. |
729 | |
730 | It can be used the following way: |
731 | |
732 | \snippet code/src_qdbus_qdbusabstractinterface.cpp 1 |
733 | |
734 | This example illustrates function calling with 0, 1 and 2 parameters and illustrates different |
735 | parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one |
736 | Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array). |
737 | |
738 | \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments. |
739 | |
740 | \sa asyncCallWithArgumentList() |
741 | */ |
742 | |
743 | |
744 | /*! |
745 | \internal |
746 | */ |
747 | QDBusMessage QDBusAbstractInterface::internalConstCall(QDBus::CallMode mode, |
748 | const QString &method, |
749 | const QList<QVariant> &args) const |
750 | { |
751 | // ### move the code here, and make the other functions call this |
752 | return const_cast<QDBusAbstractInterface*>(this)->callWithArgumentList(mode, method, args); |
753 | } |
754 | |
755 | QDBusMessage QDBusAbstractInterface::doCall(QDBus::CallMode mode, const QString &method, const QVariant *args, size_t numArgs) |
756 | { |
757 | QList<QVariant> list; |
758 | list.reserve(size: int(numArgs)); |
759 | for (size_t i = 0; i < numArgs; ++i) |
760 | list.append(t: args[i]); |
761 | return callWithArgumentList(mode, method, args: list); |
762 | } |
763 | |
764 | QDBusPendingCall QDBusAbstractInterface::doAsyncCall(const QString &method, const QVariant *args, size_t numArgs) |
765 | { |
766 | QList<QVariant> list; |
767 | list.reserve(size: int(numArgs)); |
768 | for (size_t i = 0; i < numArgs; ++i) |
769 | list.append(t: args[i]); |
770 | return asyncCallWithArgumentList(method, args: list); |
771 | } |
772 | |
773 | QT_END_NAMESPACE |
774 | |
775 | #endif // QT_NO_DBUS |
776 | |
777 | #include "moc_qdbusabstractinterface.cpp" |
778 | |