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 | interactiveAuthorizationAllowed(false), |
83 | isValid(!lastError.isValid()) |
84 | { |
85 | if (!isValid) |
86 | return; |
87 | |
88 | if (!connection.isConnected()) { |
89 | lastError = QDBusError(QDBusError::Disconnected, |
90 | QDBusUtil::disconnectedErrorMessage()); |
91 | } |
92 | } |
93 | |
94 | void QDBusAbstractInterfacePrivate::initOwnerTracking() |
95 | { |
96 | if (!isValid || !connection.isConnected() || !connectionPrivate()->shouldWatchService(service)) |
97 | return; |
98 | |
99 | QObject::connect(sender: new QDBusServiceWatcher(service, connection, QDBusServiceWatcher::WatchForOwnerChange, q_func()), |
100 | SIGNAL(serviceOwnerChanged(QString,QString,QString)), |
101 | receiver: q_func(), SLOT(_q_serviceOwnerChanged(QString,QString,QString))); |
102 | |
103 | currentOwner = connectionPrivate()->getNameOwner(service); |
104 | if (currentOwner.isEmpty()) |
105 | lastError = connectionPrivate()->lastError; |
106 | } |
107 | |
108 | bool QDBusAbstractInterfacePrivate::canMakeCalls() const |
109 | { |
110 | // recheck only if we have a wildcard (i.e. empty) service or path |
111 | // if any are empty, set the error message according to QDBusUtil |
112 | if (service.isEmpty() && connectionPrivate()->mode != QDBusConnectionPrivate::PeerMode) |
113 | return QDBusUtil::checkBusName(name: service, empty: QDBusUtil::EmptyNotAllowed, error: &lastError); |
114 | if (path.isEmpty()) |
115 | return QDBusUtil::checkObjectPath(path, empty: QDBusUtil::EmptyNotAllowed, error: &lastError); |
116 | return true; |
117 | } |
118 | |
119 | bool QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp, void *returnValuePtr) const |
120 | { |
121 | if (!isValid || !canMakeCalls()) // can't make calls |
122 | return false; |
123 | |
124 | QMetaType type = mp.metaType(); |
125 | // is this metatype registered? |
126 | const char *expectedSignature = ""; |
127 | if (type.id() != QMetaType::QVariant) { |
128 | expectedSignature = QDBusMetaType::typeToSignature(type); |
129 | if (expectedSignature == nullptr) { |
130 | qWarning(msg: "QDBusAbstractInterface: type %s must be registered with Qt D-Bus before it can be " |
131 | "used to read property %s.%s", |
132 | mp.typeName(), qPrintable(interface), mp.name()); |
133 | lastError = QDBusError(QDBusError::Failed, "Unregistered type %1 cannot be handled"_L1 |
134 | .arg(args: QLatin1StringView(mp.typeName()))); |
135 | return false; |
136 | } |
137 | } |
138 | |
139 | // try to read this property |
140 | QDBusMessage msg = QDBusMessage::createMethodCall(destination: service, path, |
141 | interface: QDBusUtil::dbusInterfaceProperties(), |
142 | QStringLiteral("Get")); |
143 | QDBusMessagePrivate::setParametersValidated(msg, enable: true); |
144 | msg << interface << QString::fromUtf8(utf8: mp.name()); |
145 | QDBusMessage reply = connection.call(message: msg, mode: QDBus::Block, timeout); |
146 | |
147 | if (reply.type() != QDBusMessage::ReplyMessage) { |
148 | lastError = QDBusError(reply); |
149 | return false; |
150 | } |
151 | if (reply.signature() != "v"_L1) { |
152 | QString errmsg = |
153 | "Invalid signature '%1' in return from call to "DBUS_INTERFACE_PROPERTIES ""_L1; |
154 | lastError = QDBusError(QDBusError::InvalidSignature, std::move(errmsg).arg(a: reply.signature())); |
155 | return false; |
156 | } |
157 | |
158 | QByteArray foundSignature; |
159 | const char *foundType = nullptr; |
160 | QVariant value = qvariant_cast<QDBusVariant>(v: reply.arguments().at(i: 0)).variant(); |
161 | |
162 | if (value.metaType() == type || type.id() == QMetaType::QVariant |
163 | || (expectedSignature[0] == 'v' && expectedSignature[1] == '\0')) { |
164 | // simple match |
165 | if (type.id() == QMetaType::QVariant) { |
166 | *reinterpret_cast<QVariant*>(returnValuePtr) = value; |
167 | } else { |
168 | QMetaType(type).destruct(data: returnValuePtr); |
169 | QMetaType(type).construct(where: returnValuePtr, copy: value.constData()); |
170 | } |
171 | return true; |
172 | } |
173 | |
174 | if (value.metaType() == QMetaType::fromType<QDBusArgument>()) { |
175 | QDBusArgument arg = qvariant_cast<QDBusArgument>(v: value); |
176 | |
177 | foundType = "user type"; |
178 | foundSignature = arg.currentSignature().toLatin1(); |
179 | if (foundSignature == expectedSignature) { |
180 | // signatures match, we can demarshall |
181 | return QDBusMetaType::demarshall(arg, id: QMetaType(type), data: returnValuePtr); |
182 | } |
183 | } else { |
184 | foundType = value.typeName(); |
185 | foundSignature = QDBusMetaType::typeToSignature(type: value.metaType()); |
186 | } |
187 | |
188 | // there was an error... |
189 | const auto errmsg = "Unexpected '%1' (%2) when retrieving property '%3.%4' " |
190 | "(expected type '%5' (%6))"_L1; |
191 | lastError = QDBusError(QDBusError::InvalidSignature, |
192 | errmsg.arg(args: QLatin1StringView(foundType), |
193 | args: QLatin1StringView(foundSignature), |
194 | args: interface, |
195 | args: QLatin1StringView(mp.name()), |
196 | args: QLatin1StringView(mp.typeName()), |
197 | args: QLatin1StringView(expectedSignature))); |
198 | return false; |
199 | } |
200 | |
201 | bool QDBusAbstractInterfacePrivate::setProperty(const QMetaProperty &mp, const QVariant &value) |
202 | { |
203 | if (!isValid || !canMakeCalls()) // can't make calls |
204 | return false; |
205 | |
206 | // send the value |
207 | QDBusMessage msg = QDBusMessage::createMethodCall(destination: service, path, |
208 | interface: QDBusUtil::dbusInterfaceProperties(), |
209 | QStringLiteral("Set")); |
210 | QDBusMessagePrivate::setParametersValidated(msg, enable: true); |
211 | msg << interface << QString::fromUtf8(utf8: mp.name()) << QVariant::fromValue(value: QDBusVariant(value)); |
212 | QDBusMessage reply = connection.call(message: msg, mode: QDBus::Block, timeout); |
213 | |
214 | if (reply.type() != QDBusMessage::ReplyMessage) { |
215 | lastError = QDBusError(reply); |
216 | return false; |
217 | } |
218 | return true; |
219 | } |
220 | |
221 | void QDBusAbstractInterfacePrivate::_q_serviceOwnerChanged(const QString &name, |
222 | const QString &oldOwner, |
223 | const QString &newOwner) |
224 | { |
225 | Q_UNUSED(oldOwner); |
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 | Configures whether, for asynchronous calls, the caller |
402 | is prepared to wait for interactive authorization. |
403 | |
404 | If \a enable is set to \c true, the D-Bus messages generated for |
405 | asynchronous calls via this interface will set the |
406 | \c ALLOW_INTERACTIVE_AUTHORIZATION flag. |
407 | |
408 | This flag is only useful when unprivileged code calls a more privileged |
409 | method call, and an authorization framework is deployed that allows |
410 | possibly interactive authorization. |
411 | |
412 | The default is \c false. |
413 | |
414 | \since 6.7 |
415 | \sa QDBusMessage::setInteractiveAuthorizationAllowed() |
416 | */ |
417 | void QDBusAbstractInterface::setInteractiveAuthorizationAllowed(bool enable) |
418 | { |
419 | d_func()->interactiveAuthorizationAllowed = enable; |
420 | } |
421 | |
422 | /*! |
423 | Returns whether, for asynchronous calls, the caller |
424 | is prepared to wait for interactive authorization. |
425 | |
426 | The default is \c false. |
427 | |
428 | \since 6.7 |
429 | \sa setInteractiveAuthorizationAllowed(), |
430 | QDBusMessage::setInteractiveAuthorizationAllowed() |
431 | */ |
432 | bool QDBusAbstractInterface::isInteractiveAuthorizationAllowed() const |
433 | { |
434 | return d_func()->interactiveAuthorizationAllowed; |
435 | } |
436 | |
437 | /*! |
438 | Places a call to the remote method specified by \a method on this interface, using \a args as |
439 | arguments. This function returns the message that was received as a reply, which can be a normal |
440 | QDBusMessage::ReplyMessage (indicating success) or QDBusMessage::ErrorMessage (if the call |
441 | failed). The \a mode parameter specifies how this call should be placed. |
442 | |
443 | If the call succeeds, lastError() will be cleared; otherwise, it will contain the error this |
444 | call produced. |
445 | |
446 | Normally, you should place calls using call(). |
447 | |
448 | \warning If you use \c UseEventLoop, your code must be prepared to deal with any reentrancy: |
449 | other method calls and signals may be delivered before this function returns, as well |
450 | as other Qt queued signals and events. |
451 | |
452 | \threadsafe |
453 | */ |
454 | QDBusMessage QDBusAbstractInterface::callWithArgumentList(QDBus::CallMode mode, |
455 | const QString& method, |
456 | const QList<QVariant>& args) |
457 | { |
458 | Q_D(QDBusAbstractInterface); |
459 | |
460 | if (!d->isValid || !d->canMakeCalls()) |
461 | return QDBusMessage::createError(err: d->lastError); |
462 | |
463 | QString m = method; |
464 | // split out the signature from the method |
465 | int pos = method.indexOf(ch: u'.'); |
466 | if (pos != -1) |
467 | m.truncate(pos); |
468 | |
469 | if (mode == QDBus::AutoDetect) { |
470 | // determine if this a sync or async call |
471 | mode = QDBus::Block; |
472 | const QMetaObject *mo = metaObject(); |
473 | QByteArray match = m.toLatin1(); |
474 | |
475 | for (int i = staticMetaObject.methodCount(); i < mo->methodCount(); ++i) { |
476 | QMetaMethod mm = mo->method(index: i); |
477 | if (mm.name() == match) { |
478 | // found a method with the same name as what we're looking for |
479 | // hopefully, nobody is overloading asynchronous and synchronous methods with |
480 | // the same name |
481 | |
482 | QList<QByteArray> tags = QByteArray(mm.tag()).split(sep: ' '); |
483 | if (tags.contains(t: "Q_NOREPLY")) |
484 | mode = QDBus::NoBlock; |
485 | |
486 | break; |
487 | } |
488 | } |
489 | } |
490 | |
491 | // qDebug() << "QDBusAbstractInterface" << "Service" << service() << "Path:" << path(); |
492 | QDBusMessage msg = QDBusMessage::createMethodCall(destination: service(), path: path(), interface: interface(), method: m); |
493 | QDBusMessagePrivate::setParametersValidated(msg, enable: true); |
494 | msg.setArguments(args); |
495 | |
496 | QDBusMessage reply = d->connection.call(message: msg, mode, timeout: d->timeout); |
497 | if (thread() == QThread::currentThread()) |
498 | d->lastError = QDBusError(reply); // will clear if reply isn't an error |
499 | |
500 | // ensure that there is at least one element |
501 | if (reply.arguments().isEmpty()) |
502 | reply << QVariant(); |
503 | |
504 | return reply; |
505 | } |
506 | |
507 | /*! |
508 | \since 4.5 |
509 | Places a call to the remote method specified by \a method on this |
510 | interface, using \a args as arguments. This function returns a |
511 | QDBusPendingCall object that can be used to track the status of the |
512 | reply and access its contents once it has arrived. |
513 | |
514 | Normally, you should place calls using asyncCall(). |
515 | |
516 | \note Method calls to objects registered by the application itself are never |
517 | asynchronous due to implementation limitations. |
518 | |
519 | \threadsafe |
520 | */ |
521 | QDBusPendingCall QDBusAbstractInterface::asyncCallWithArgumentList(const QString& method, |
522 | const QList<QVariant>& args) |
523 | { |
524 | Q_D(QDBusAbstractInterface); |
525 | |
526 | if (!d->isValid || !d->canMakeCalls()) |
527 | return QDBusPendingCall::fromError(error: d->lastError); |
528 | |
529 | QDBusMessage msg = QDBusMessage::createMethodCall(destination: service(), path: path(), interface: interface(), method); |
530 | QDBusMessagePrivate::setParametersValidated(msg, enable: true); |
531 | msg.setArguments(args); |
532 | if (d->interactiveAuthorizationAllowed) |
533 | msg.setInteractiveAuthorizationAllowed(true); |
534 | return d->connection.asyncCall(message: msg, timeout: d->timeout); |
535 | } |
536 | |
537 | /*! |
538 | Places a call to the remote method specified by \a method |
539 | on this interface, using \a args as arguments. This function |
540 | returns immediately after queueing the call. The reply from |
541 | the remote function is delivered to the \a returnMethod on |
542 | object \a receiver. If an error occurs, the \a errorMethod |
543 | on object \a receiver is called instead. |
544 | |
545 | This function returns \c true if the queueing succeeds. It does |
546 | not indicate that the executed call succeeded. If it fails, |
547 | the \a errorMethod is called. If the queueing failed, this |
548 | function returns \c false and no slot will be called. |
549 | |
550 | The \a returnMethod must have as its parameters the types returned |
551 | by the function call. Optionally, it may have a QDBusMessage |
552 | parameter as its last or only parameter. The \a errorMethod must |
553 | have a QDBusError as its only parameter. |
554 | |
555 | \note Method calls to objects registered by the application itself are never |
556 | asynchronous due to implementation limitations. |
557 | |
558 | \since 4.3 |
559 | \sa QDBusError, QDBusMessage |
560 | */ |
561 | bool QDBusAbstractInterface::callWithCallback(const QString &method, |
562 | const QList<QVariant> &args, |
563 | QObject *receiver, |
564 | const char *returnMethod, |
565 | const char *errorMethod) |
566 | { |
567 | Q_D(QDBusAbstractInterface); |
568 | |
569 | if (!d->isValid || !d->canMakeCalls()) |
570 | return false; |
571 | |
572 | QDBusMessage msg = QDBusMessage::createMethodCall(destination: service(), |
573 | path: path(), |
574 | interface: interface(), |
575 | method); |
576 | QDBusMessagePrivate::setParametersValidated(msg, enable: true); |
577 | msg.setArguments(args); |
578 | |
579 | d->lastError = QDBusError(); |
580 | return d->connection.callWithCallback(message: msg, |
581 | receiver, |
582 | returnMethod, |
583 | errorMethod, |
584 | timeout: d->timeout); |
585 | } |
586 | |
587 | /*! |
588 | \overload |
589 | |
590 | This function is deprecated. Please use the overloaded version. |
591 | |
592 | Places a call to the remote method specified by \a method |
593 | on this interface, using \a args as arguments. This function |
594 | returns immediately after queueing the call. The reply from |
595 | the remote function or any errors emitted by it are delivered |
596 | to the \a slot slot on object \a receiver. |
597 | |
598 | This function returns \c true if the queueing succeeded: it does |
599 | not indicate that the call succeeded. If it failed, the slot |
600 | will be called with an error message. lastError() will not be |
601 | set under those circumstances. |
602 | |
603 | \sa QDBusError, QDBusMessage |
604 | */ |
605 | bool QDBusAbstractInterface::callWithCallback(const QString &method, |
606 | const QList<QVariant> &args, |
607 | QObject *receiver, |
608 | const char *slot) |
609 | { |
610 | return callWithCallback(method, args, receiver, returnMethod: slot, errorMethod: nullptr); |
611 | } |
612 | |
613 | /*! |
614 | \internal |
615 | Catch signal connections. |
616 | */ |
617 | void QDBusAbstractInterface::connectNotify(const QMetaMethod &signal) |
618 | { |
619 | // someone connecting to one of our signals |
620 | Q_D(QDBusAbstractInterface); |
621 | if (!d->isValid) |
622 | return; |
623 | |
624 | // we end up recursing here, so optimize away |
625 | static const QMetaMethod destroyedSignal = QMetaMethod::fromSignal(signal: &QDBusAbstractInterface::destroyed); |
626 | if (signal == destroyedSignal) |
627 | return; |
628 | |
629 | QDBusConnectionPrivate *conn = d->connectionPrivate(); |
630 | if (conn) { |
631 | conn->connectRelay(service: d->service, path: d->path, interface: d->interface, |
632 | receiver: this, signal); |
633 | } |
634 | } |
635 | |
636 | /*! |
637 | \internal |
638 | Catch signal disconnections. |
639 | */ |
640 | void QDBusAbstractInterface::disconnectNotify(const QMetaMethod &signal) |
641 | { |
642 | // someone disconnecting from one of our signals |
643 | Q_D(QDBusAbstractInterface); |
644 | if (!d->isValid) |
645 | return; |
646 | |
647 | // disconnection is just resource freeing, so it can be delayed; |
648 | // let's do that later, after all the QObject mutexes have been unlocked. |
649 | QCoreApplication::postEvent(receiver: this, event: new DisconnectRelayEvent(this, signal)); |
650 | } |
651 | |
652 | /*! |
653 | \internal |
654 | Continues the disconnect notification from above. |
655 | */ |
656 | void QDBusAbstractInterfacePrivate::finishDisconnectNotify(QDBusAbstractInterface *ptr, int signalId) |
657 | { |
658 | QDBusAbstractInterfacePrivate *d = ptr->d_func(); |
659 | QDBusConnectionPrivate *conn = d->connectionPrivate(); |
660 | if (!conn) |
661 | return; |
662 | |
663 | const QMetaObject *mo = ptr->metaObject(); |
664 | QMetaMethod signal = signalId >= 0 ? mo->method(index: signalId) : QMetaMethod(); |
665 | if (signal.isValid()) { |
666 | if (!ptr->isSignalConnected(signal)) |
667 | return conn->disconnectRelay(service: d->service, path: d->path, interface: d->interface, |
668 | receiver: ptr, signal); |
669 | } else { |
670 | // wildcard disconnecting, we need to figure out which of our signals are |
671 | // no longer connected to anything |
672 | int midx = QObject::staticMetaObject.methodCount(); |
673 | const int end = mo->methodCount(); |
674 | for ( ; midx < end; ++midx) { |
675 | QMetaMethod mm = mo->method(index: midx); |
676 | if (mm.methodType() == QMetaMethod::Signal && !ptr->isSignalConnected(signal: mm)) |
677 | conn->disconnectRelay(service: d->service, path: d->path, interface: d->interface, receiver: ptr, signal: mm); |
678 | } |
679 | } |
680 | } |
681 | |
682 | /*! |
683 | \internal |
684 | Get the value of the property \a propname. |
685 | */ |
686 | QVariant QDBusAbstractInterface::internalPropGet(const char *propname) const |
687 | { |
688 | // assume this property exists and is readable |
689 | // we're only called from generated code anyways |
690 | |
691 | return property(name: propname); |
692 | } |
693 | |
694 | /*! |
695 | \internal |
696 | Set the value of the property \a propname to \a value. |
697 | */ |
698 | void QDBusAbstractInterface::internalPropSet(const char *propname, const QVariant &value) |
699 | { |
700 | setProperty(name: propname, value); |
701 | } |
702 | |
703 | /*! |
704 | \fn QDBusAbstractInterface::call(const QString &message) |
705 | \internal |
706 | */ |
707 | |
708 | /*! |
709 | \fn template <typename...Args> QDBusMessage QDBusAbstractInterface::call(const QString &method, Args&&...args) |
710 | |
711 | Calls the method \a method on this interface and passes \a args to the method. |
712 | All \a args must be convertible to QVariant. |
713 | |
714 | The parameters to \c call are passed on to the remote function via D-Bus as input |
715 | arguments. Output arguments are returned in the QDBusMessage reply. If the reply is an error |
716 | reply, lastError() will also be set to the contents of the error message. |
717 | |
718 | It can be used the following way: |
719 | |
720 | \snippet code/src_qdbus_qdbusabstractinterface.cpp 0 |
721 | |
722 | This example illustrates function calling with 0, 1 and 2 parameters and illustrates different |
723 | parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one |
724 | Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array). |
725 | See asyncCall() for the same example in non-blocking (asynchronous) calls. |
726 | |
727 | \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments. |
728 | |
729 | \sa callWithArgumentList() |
730 | */ |
731 | |
732 | /*! |
733 | \fn QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &message) |
734 | \internal |
735 | */ |
736 | |
737 | /*! |
738 | \fn template <typename...Args> QDBusMessage QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &method, Args&&...args) |
739 | |
740 | \overload |
741 | |
742 | Calls the method \a method on this interface and passes \a args to the method. |
743 | All \a args must be convertible to QVariant. |
744 | |
745 | If \a mode is \c NoWaitForReply, then this function will return immediately after |
746 | placing the call, without waiting for a reply from the remote |
747 | method. Otherwise, \a mode indicates whether this function should |
748 | activate the Qt Event Loop while waiting for the reply to arrive. |
749 | |
750 | If this function reenters the Qt event loop in order to wait for the |
751 | reply, it will exclude user input. During the wait, it may deliver |
752 | signals and other method calls to your application. Therefore, it |
753 | must be prepared to handle a reentrancy whenever a call is placed |
754 | with call(). |
755 | |
756 | \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments. |
757 | |
758 | \sa callWithArgumentList() |
759 | */ |
760 | |
761 | /*! |
762 | \fn QDBusAbstractInterface::asyncCall(const QString &message) |
763 | \internal |
764 | */ |
765 | |
766 | /*! |
767 | \fn template <typename...Args> QDBusPendingCall QDBusAbstractInterface::asyncCall(const QString &method, Args&&...args) |
768 | |
769 | Calls the method \a method on this interface and passes \a args to the method. |
770 | All \a args must be convertible to QVariant. |
771 | |
772 | The parameters to \c call are passed on to the remote function via D-Bus as input |
773 | arguments. The returned QDBusPendingCall object can be used to find out information about |
774 | the reply. |
775 | |
776 | It can be used the following way: |
777 | |
778 | \snippet code/src_qdbus_qdbusabstractinterface.cpp 1 |
779 | |
780 | This example illustrates function calling with 0, 1 and 2 parameters and illustrates different |
781 | parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one |
782 | Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array). |
783 | See call() for the same example in blocking (synchronous) calls. |
784 | |
785 | \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments. |
786 | |
787 | \note Method calls to local \c{QDBusServer}'s are never asynchronous |
788 | due to implementation limitations. |
789 | |
790 | \sa asyncCallWithArgumentList() |
791 | */ |
792 | |
793 | |
794 | /*! |
795 | \internal |
796 | */ |
797 | QDBusMessage QDBusAbstractInterface::internalConstCall(QDBus::CallMode mode, |
798 | const QString &method, |
799 | const QList<QVariant> &args) const |
800 | { |
801 | // ### move the code here, and make the other functions call this |
802 | return const_cast<QDBusAbstractInterface*>(this)->callWithArgumentList(mode, method, args); |
803 | } |
804 | |
805 | QDBusMessage QDBusAbstractInterface::doCall(QDBus::CallMode mode, const QString &method, const QVariant *args, size_t numArgs) |
806 | { |
807 | QList<QVariant> list; |
808 | list.reserve(size: int(numArgs)); |
809 | for (size_t i = 0; i < numArgs; ++i) |
810 | list.append(t: args[i]); |
811 | return callWithArgumentList(mode, method, args: list); |
812 | } |
813 | |
814 | QDBusPendingCall QDBusAbstractInterface::doAsyncCall(const QString &method, const QVariant *args, size_t numArgs) |
815 | { |
816 | QList<QVariant> list; |
817 | list.reserve(size: int(numArgs)); |
818 | for (size_t i = 0; i < numArgs; ++i) |
819 | list.append(t: args[i]); |
820 | return asyncCallWithArgumentList(method, args: list); |
821 | } |
822 | |
823 | QT_END_NAMESPACE |
824 | |
825 | #endif // QT_NO_DBUS |
826 | |
827 | #include "moc_qdbusabstractinterface.cpp" |
828 |
Definitions
- DisconnectRelayEvent
- DisconnectRelayEvent
- placeMetaCall
- checkIfValid
- QDBusAbstractInterfacePrivate
- initOwnerTracking
- canMakeCalls
- property
- setProperty
- _q_serviceOwnerChanged
- QDBusAbstractInterfaceBase
- qt_metacall
- QDBusAbstractInterface
- QDBusAbstractInterface
- ~QDBusAbstractInterface
- isValid
- connection
- service
- path
- interface
- lastError
- setTimeout
- timeout
- setInteractiveAuthorizationAllowed
- isInteractiveAuthorizationAllowed
- callWithArgumentList
- asyncCallWithArgumentList
- callWithCallback
- callWithCallback
- connectNotify
- disconnectNotify
- finishDisconnectNotify
- internalPropGet
- internalPropSet
- internalConstCall
- doCall
Start learning QML with our Intro Training
Find out more