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()); |
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(service, (isDynamic && !isPeer) ? QDBusUtil::EmptyNotAllowed : QDBusUtil::EmptyAllowed, &error)) |
97 | return error; |
98 | if (!QDBusUtil::checkObjectPath(path, isDynamic ? QDBusUtil::EmptyNotAllowed : QDBusUtil::EmptyAllowed, &error)) |
99 | return error; |
100 | if (!QDBusUtil::checkInterfaceName(interface, QDBusUtil::EmptyAllowed, &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(serv, p, iface, isDynamic, (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(new QDBusServiceWatcher(service, connection, QDBusServiceWatcher::WatchForOwnerChange, q_func()), |
133 | SIGNAL(serviceOwnerChanged(QString,QString,QString)), |
134 | 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(service, QDBusUtil::EmptyNotAllowed, &lastError); |
147 | if (path.isEmpty()) |
148 | return QDBusUtil::checkObjectPath(path, QDBusUtil::EmptyNotAllowed, &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 | QMetaType type = mp.metaType(); |
158 | // is this metatype registered? |
159 | const char *expectedSignature = "" ; |
160 | if (type.id() != QMetaType::QVariant) { |
161 | expectedSignature = QDBusMetaType::typeToSignature(type); |
162 | if (expectedSignature == nullptr) { |
163 | qWarning("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(QLatin1String(mp.typeName()))); |
169 | return false; |
170 | } |
171 | } |
172 | |
173 | // try to read this property |
174 | QDBusMessage msg = QDBusMessage::createMethodCall(service, path, |
175 | QDBusUtil::dbusInterfaceProperties(), |
176 | QStringLiteral("Get" )); |
177 | QDBusMessagePrivate::setParametersValidated(msg, true); |
178 | msg << interface << QString::fromUtf8(mp.name()); |
179 | QDBusMessage reply = connection.call(msg, 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(reply.signature())); |
189 | return false; |
190 | } |
191 | |
192 | QByteArray foundSignature; |
193 | const char *foundType = nullptr; |
194 | QVariant value = qvariant_cast<QDBusVariant>(reply.arguments().at(0)).variant(); |
195 | |
196 | if (value.metaType() == type || type.id() == QMetaType::QVariant |
197 | || (expectedSignature[0] == 'v' && expectedSignature[1] == '\0')) { |
198 | // simple match |
199 | if (type.id() == QMetaType::QVariant) { |
200 | *reinterpret_cast<QVariant*>(returnValuePtr) = value; |
201 | } else { |
202 | QMetaType(type).destruct(returnValuePtr); |
203 | QMetaType(type).construct(returnValuePtr, value.constData()); |
204 | } |
205 | return true; |
206 | } |
207 | |
208 | if (value.metaType() == QMetaType::fromType<QDBusArgument>()) { |
209 | QDBusArgument arg = qvariant_cast<QDBusArgument>(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, QMetaType(type), returnValuePtr); |
216 | } |
217 | } else { |
218 | foundType = value.typeName(); |
219 | foundSignature = QDBusMetaType::typeToSignature(value.metaType()); |
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(QLatin1String(foundType), |
227 | QLatin1String(foundSignature), |
228 | interface, |
229 | QLatin1String(mp.name()), |
230 | QLatin1String(mp.typeName()), |
231 | 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(service, path, |
242 | QDBusUtil::dbusInterfaceProperties(), |
243 | QStringLiteral("Set" )); |
244 | QDBusMessagePrivate::setParametersValidated(msg, true); |
245 | msg << interface << QString::fromUtf8(mp.name()) << QVariant::fromValue(QDBusVariant(value)); |
246 | QDBusMessage reply = connection.call(msg, 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(saved_id); |
280 | int &status = *reinterpret_cast<int *>(_a[2]); |
281 | |
282 | if (_c == QMetaObject::WriteProperty) { |
283 | QVariant value; |
284 | if (mp.metaType() == QMetaType::fromType<QDBusVariant>()) |
285 | value = reinterpret_cast<const QDBusVariant*>(_a[0])->variant(); |
286 | else |
287 | value = QVariant(mp.metaType(), _a[0]); |
288 | status = d_func()->setProperty(mp, value) ? 1 : 0; |
289 | } else { |
290 | bool readStatus = d_func()->property(mp, _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(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(d->lastError); |
460 | |
461 | QString m = method; |
462 | // split out the signature from the method |
463 | int pos = method.indexOf(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(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(' '); |
481 | if (tags.contains("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(service(), path(), interface(), m); |
491 | QDBusMessagePrivate::setParametersValidated(msg, true); |
492 | msg.setArguments(args); |
493 | |
494 | QDBusMessage reply = d->connection.call(msg, mode, 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(d->lastError); |
523 | |
524 | QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), interface(), method); |
525 | QDBusMessagePrivate::setParametersValidated(msg, true); |
526 | msg.setArguments(args); |
527 | return d->connection.asyncCall(msg, 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(service(), |
563 | path(), |
564 | interface(), |
565 | method); |
566 | QDBusMessagePrivate::setParametersValidated(msg, true); |
567 | msg.setArguments(args); |
568 | |
569 | d->lastError = QDBusError(); |
570 | return d->connection.callWithCallback(msg, |
571 | receiver, |
572 | returnMethod, |
573 | errorMethod, |
574 | 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, slot, 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(&QDBusAbstractInterface::destroyed); |
616 | if (signal == destroyedSignal) |
617 | return; |
618 | |
619 | QDBusConnectionPrivate *conn = d->connectionPrivate(); |
620 | if (conn) { |
621 | conn->connectRelay(d->service, d->path, d->interface, |
622 | 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(this, 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(signalId) : QMetaMethod(); |
655 | if (signal.isValid()) { |
656 | if (!ptr->isSignalConnected(signal)) |
657 | return conn->disconnectRelay(d->service, d->path, d->interface, |
658 | 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(midx); |
666 | if (mm.methodType() == QMetaMethod::Signal && !ptr->isSignalConnected(mm)) |
667 | conn->disconnectRelay(d->service, d->path, d->interface, ptr, 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(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(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 | /*! |
722 | \fn QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &message) |
723 | \internal |
724 | */ |
725 | |
726 | /*! |
727 | \fn template <typename...Args> QDBusMessage QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &method, Args&&...args) |
728 | |
729 | \overload |
730 | |
731 | Calls the method \a method on this interface and passes \a args to the method. |
732 | All \a args must be convertible to QVariant. |
733 | |
734 | If \a mode is \c NoWaitForReply, then this function will return immediately after |
735 | placing the call, without waiting for a reply from the remote |
736 | method. Otherwise, \a mode indicates whether this function should |
737 | activate the Qt Event Loop while waiting for the reply to arrive. |
738 | |
739 | If this function reenters the Qt event loop in order to wait for the |
740 | reply, it will exclude user input. During the wait, it may deliver |
741 | signals and other method calls to your application. Therefore, it |
742 | must be prepared to handle a reentrancy whenever a call is placed |
743 | with call(). |
744 | |
745 | \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments. |
746 | |
747 | \sa callWithArgumentList() |
748 | */ |
749 | |
750 | /*! |
751 | \fn QDBusAbstractInterface::asyncCall(const QString &message) |
752 | \internal |
753 | */ |
754 | |
755 | /*! |
756 | \fn template <typename...Args> QDBusPendingCall QDBusAbstractInterface::asyncCall(const QString &method, Args&&...args) |
757 | |
758 | Calls the method \a method on this interface and passes \a args to the method. |
759 | All \a args must be convertible to QVariant. |
760 | |
761 | The parameters to \c call are passed on to the remote function via D-Bus as input |
762 | arguments. The returned QDBusPendingCall object can be used to find out information about |
763 | the reply. |
764 | |
765 | It can be used the following way: |
766 | |
767 | \snippet code/src_qdbus_qdbusabstractinterface.cpp 1 |
768 | |
769 | This example illustrates function calling with 0, 1 and 2 parameters and illustrates different |
770 | parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one |
771 | Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array). |
772 | |
773 | \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments. |
774 | |
775 | \sa asyncCallWithArgumentList() |
776 | */ |
777 | |
778 | |
779 | /*! |
780 | \internal |
781 | */ |
782 | QDBusMessage QDBusAbstractInterface::internalConstCall(QDBus::CallMode mode, |
783 | const QString &method, |
784 | const QList<QVariant> &args) const |
785 | { |
786 | // ### move the code here, and make the other functions call this |
787 | return const_cast<QDBusAbstractInterface*>(this)->callWithArgumentList(mode, method, args); |
788 | } |
789 | |
790 | QDBusMessage QDBusAbstractInterface::doCall(QDBus::CallMode mode, const QString &method, const QVariant *args, size_t numArgs) |
791 | { |
792 | QList<QVariant> list; |
793 | list.reserve(int(numArgs)); |
794 | for (size_t i = 0; i < numArgs; ++i) |
795 | list.append(args[i]); |
796 | return callWithArgumentList(mode, method, list); |
797 | } |
798 | |
799 | QDBusPendingCall QDBusAbstractInterface::doAsyncCall(const QString &method, const QVariant *args, size_t numArgs) |
800 | { |
801 | QList<QVariant> list; |
802 | list.reserve(int(numArgs)); |
803 | for (size_t i = 0; i < numArgs; ++i) |
804 | list.append(args[i]); |
805 | return asyncCallWithArgumentList(method, list); |
806 | } |
807 | |
808 | QT_END_NAMESPACE |
809 | |
810 | #endif // QT_NO_DBUS |
811 | |
812 | #include "moc_qdbusabstractinterface.cpp" |
813 | |