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
23QT_BEGIN_NAMESPACE
24
25using namespace Qt::StringLiterals;
26
27namespace {
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)
35class DisconnectRelayEvent : public QAbstractMetaCallEvent
36{
37public:
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
50static 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
73QDBusAbstractInterfacePrivate::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
93void 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
107bool 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
118bool 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
200bool 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
220void 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
231QDBusAbstractInterfaceBase::QDBusAbstractInterfaceBase(QDBusAbstractInterfacePrivate &d, QObject *parent)
232 : QObject(d, parent)
233{
234}
235
236int 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*/
290QDBusAbstractInterface::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*/
301QDBusAbstractInterface::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*/
314QDBusAbstractInterface::~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*/
326bool 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*/
340QDBusConnection 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*/
348QString QDBusAbstractInterface::service() const
349{
350 return d_func()->service;
351}
352
353/*!
354 Returns the object path that this interface is associated with.
355*/
356QString QDBusAbstractInterface::path() const
357{
358 return d_func()->path;
359}
360
361/*!
362 Returns the name of this interface.
363*/
364QString 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*/
373QDBusError 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*/
384void 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*/
395int 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*/
417QDBusMessage 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*/
481QDBusPendingCall 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 */
516bool 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*/
560bool 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*/
572void 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*/
595void 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*/
611void 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*/
641QVariant 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*/
653void 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*/
747QDBusMessage 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
755QDBusMessage 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
764QDBusPendingCall 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
773QT_END_NAMESPACE
774
775#endif // QT_NO_DBUS
776
777#include "moc_qdbusabstractinterface.cpp"
778

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