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