1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the QtSystems module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL21$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 or version 3 as published by the Free |
20 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and |
21 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the |
22 | ** following information to ensure the GNU Lesser General Public License |
23 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and |
24 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
25 | ** |
26 | ** As a special exception, The Qt Company gives you certain additional |
27 | ** rights. These rights are described in The Qt Company LGPL Exception |
28 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
29 | ** |
30 | ** $QT_END_LICENSE$ |
31 | ** |
32 | ****************************************************************************/ |
33 | |
34 | #include "qservicemetaobject_dbus_p.h" |
35 | #include <private/qmetaobjectbuilder_p.h> |
36 | #include "qsignalintercepter_p.h" |
37 | #include <QDebug> |
38 | |
39 | #include <stdlib.h> |
40 | |
41 | QT_BEGIN_NAMESPACE |
42 | |
43 | class ServiceMetaSignalIntercepter : public QSignalIntercepter |
44 | { |
45 | |
46 | public: |
47 | ServiceMetaSignalIntercepter(QObject* sender, const QByteArray& signal, QServiceMetaObjectDBus* parent) |
48 | : QSignalIntercepter(sender, signal, parent), serviceDBus(parent) |
49 | { |
50 | |
51 | } |
52 | |
53 | void setMetaIndex(int index) |
54 | { |
55 | metaIndex = index; |
56 | } |
57 | |
58 | protected: |
59 | void activated( const QList<QVariant>& args ) |
60 | { |
61 | serviceDBus->activateMetaSignal(id: metaIndex, args); |
62 | } |
63 | private: |
64 | QServiceMetaObjectDBus* serviceDBus; |
65 | int metaIndex; |
66 | }; |
67 | |
68 | |
69 | class QServiceMetaObjectDBusPrivate |
70 | { |
71 | public: |
72 | QObject *service; |
73 | const QMetaObject *serviceMeta; |
74 | const QMetaObject *dbusMeta; |
75 | }; |
76 | |
77 | QServiceMetaObjectDBus::QServiceMetaObjectDBus(QObject* service, bool signalsObject) |
78 | : QDBusAbstractAdaptor(service) |
79 | { |
80 | // Register our DBus custom type object |
81 | qRegisterMetaType<QServiceUserTypeDBus>(); |
82 | qDBusRegisterMetaType<QServiceUserTypeDBus>(); |
83 | |
84 | // Generate our DBus meta object |
85 | d = new QServiceMetaObjectDBusPrivate(); |
86 | d->service = service; |
87 | d->serviceMeta = service->metaObject(); |
88 | d->dbusMeta = dbusMetaObject(signalsObject); |
89 | |
90 | // Relay signals from the service to the constructed DBus meta object |
91 | connectMetaSignals(signalsObject); |
92 | } |
93 | |
94 | QServiceMetaObjectDBus::~QServiceMetaObjectDBus() |
95 | { |
96 | if (d->dbusMeta) |
97 | free(ptr: const_cast<QMetaObject*>(d->dbusMeta)); |
98 | |
99 | delete d; |
100 | } |
101 | |
102 | /*! |
103 | Relays all signals from the service object to the converted DBus service adaptor so |
104 | that when a service signal is emitted the DBus object will emit the counterpart signal |
105 | */ |
106 | void QServiceMetaObjectDBus::connectMetaSignals(bool signalsObject) { |
107 | if (!signalsObject) { |
108 | // Automatically relay signals from service object to adaptor |
109 | setAutoRelaySignals(true); |
110 | |
111 | // Connect signals with custom arguments |
112 | int methodCount = d->serviceMeta->methodCount(); |
113 | for (int i = 0; i < methodCount; i++) { |
114 | QMetaMethod mm = d->serviceMeta->method(index: i); |
115 | |
116 | |
117 | if (mm.methodType() == QMetaMethod::Signal) { |
118 | QByteArray sig(mm.methodSignature()); |
119 | bool customType = false; |
120 | const QList<QByteArray> pTypes = mm.parameterTypes(); |
121 | const int pTypesCount = pTypes.count(); |
122 | |
123 | // Ignore all QObject calls |
124 | const QMetaObject *mo = QObject::metaObject(); |
125 | int qobjectIndex = mo->indexOfMethod(method: sig); |
126 | if (qobjectIndex >= 0) |
127 | continue; |
128 | |
129 | // Detects custom types as passed arguments |
130 | for (int arg = 0; arg < pTypesCount; arg++) { |
131 | const QByteArray& type = pTypes[arg]; |
132 | int variantType = QMetaType::type(typeName: type); |
133 | if (variantType >= QMetaType::User || variantType == QMetaType::QVariant) { |
134 | sig.replace(before: QByteArray(type), after: QByteArray("QDBusVariant" )); |
135 | customType = true; |
136 | } |
137 | } |
138 | |
139 | // Connects the service signal to the corresponding DBus service signal |
140 | if (customType) { |
141 | QByteArray signal = mm.methodSignature(); |
142 | ServiceMetaSignalIntercepter *intercept = |
143 | new ServiceMetaSignalIntercepter(d->service, "2" +signal, this); |
144 | intercept->setMetaIndex(i); |
145 | } |
146 | } |
147 | } |
148 | } |
149 | } |
150 | |
151 | /*! |
152 | Relays the activation to the DBus object signal with the given id and arguments list when the |
153 | intercepter for signals with custom arguments has been activated. This bypasses the metacall |
154 | which usually does the relaying for signals with standard arguments since no pre-connection |
155 | conversions are required. |
156 | */ |
157 | void QServiceMetaObjectDBus::activateMetaSignal(int id, const QVariantList& args) |
158 | { |
159 | QMetaMethod method = d->serviceMeta->method(index: id); |
160 | |
161 | // Converts the argument list to values supported by the QtDBus type system |
162 | QVariantList convertedList = args; |
163 | QByteArray sig(method.methodSignature()); |
164 | QList<QByteArray> params = method.parameterTypes(); |
165 | |
166 | for (int i = 0; i < params.size(); i++) { |
167 | QVariant dbusVariant = args[i]; |
168 | |
169 | // Convert custom types |
170 | const QByteArray& type = params[i]; |
171 | int variantType = QMetaType::type(typeName: type); |
172 | if (variantType >= QMetaType::User || variantType == QMetaType::QVariant) { |
173 | if (variantType != QMetaType::QVariant) { |
174 | // Wrap custom types in a QDBusVariant of the type name and |
175 | // a buffer of its variant-wrapped data |
176 | QByteArray buffer; |
177 | QDataStream stream(&buffer, QIODevice::ReadWrite | QIODevice::Append); |
178 | stream << args[i]; |
179 | |
180 | QServiceUserTypeDBus customType; |
181 | customType.typeName = type; |
182 | customType.variantBuffer = buffer; |
183 | |
184 | QDBusVariant replacement(QVariant::fromValue(value: customType)); |
185 | convertedList.replace(i, t: QVariant::fromValue(value: replacement)); |
186 | } |
187 | |
188 | sig.replace(before: QByteArray(type), after: QByteArray("QDBusVariant" )); |
189 | } |
190 | } |
191 | |
192 | // Activate the DBus object signal call |
193 | const int numArgs = convertedList.size(); |
194 | QVarLengthArray<void *, 32> a( numArgs+1 ); |
195 | a[0] = 0; |
196 | |
197 | const QList<QByteArray> pTypes = method.parameterTypes(); |
198 | for ( int arg = 0; arg < numArgs; ++arg ) { |
199 | if (pTypes.at(i: arg) == "QVariant" ) |
200 | a[arg+1] = (void *)&( convertedList[arg] ); |
201 | else |
202 | a[arg+1] = (void *)( convertedList[arg].data() ); |
203 | } |
204 | |
205 | int dbusIndex = d->dbusMeta->indexOfSignal(signal: sig); |
206 | QMetaObject::activate(sender: this, signal_index: dbusIndex, argv: a.data()); |
207 | } |
208 | |
209 | |
210 | /*! |
211 | Build a metaobject that represents the service object as a valid service that |
212 | satisfies the QtDBus type system. |
213 | */ |
214 | const QMetaObject* QServiceMetaObjectDBus::dbusMetaObject(bool signalsObject) const |
215 | { |
216 | // Create a meta-object to represent all the contents of our service on DBus |
217 | QMetaObjectBuilder builder; |
218 | |
219 | builder.setClassName(d->serviceMeta->className()); |
220 | builder.setSuperClass(d->serviceMeta->superClass()); // needed? |
221 | |
222 | const QMetaObject* mo = d->serviceMeta; |
223 | while (mo && strcmp(s1: mo->className(), s2: "QObject" )) { |
224 | // Add our methods, signals and slots |
225 | for (int i = mo->methodOffset(); i < mo->methodCount(); i++) { |
226 | QMetaMethod mm = mo->method(index: i); |
227 | |
228 | if (signalsObject && mm.methodType() != QMetaMethod::Signal) |
229 | continue; |
230 | |
231 | // Convert QVariant and custom return types to QDBusVariants |
232 | QByteArray ret(mm.typeName()); |
233 | const QByteArray& type = mm.typeName(); |
234 | int variantType = QMetaType::type(typeName: type); |
235 | if (variantType >= QMetaType::User || variantType == QMetaType::QVariant) { |
236 | ret = QByteArray("QDBusVariant" ); |
237 | } |
238 | |
239 | // Convert QVariant and custom argument types to QDBusVariants |
240 | QByteArray sig(mm.methodSignature()); |
241 | const QList<QByteArray> pTypes = mm.parameterTypes(); |
242 | const int pTypesCount = pTypes.count(); |
243 | for (int i=0; i < pTypesCount; i++) { |
244 | const QByteArray& type = pTypes[i]; |
245 | int variantType = QMetaType::type(typeName: type); |
246 | if (variantType >= QMetaType::User || variantType == QMetaType::QVariant) { |
247 | sig.replace(before: QByteArray(type), after: QByteArray("QDBusVariant" )); |
248 | } |
249 | } |
250 | |
251 | // Add a MetaMethod with converted signature to our builder |
252 | QMetaMethodBuilder method; |
253 | switch (mm.methodType()) { |
254 | case QMetaMethod::Method: |
255 | method = builder.addMethod(signature: sig); |
256 | break; |
257 | case QMetaMethod::Slot: |
258 | method = builder.addSlot(signature: sig); |
259 | break; |
260 | case QMetaMethod::Signal: |
261 | method = builder.addSignal(signature: sig); |
262 | break; |
263 | default: |
264 | break; |
265 | } |
266 | |
267 | // Make sure our built MetaMethod is identical, excluding conversion |
268 | method.setReturnType(ret); |
269 | method.setParameterNames(mm.parameterNames()); |
270 | method.setTag(mm.tag()); |
271 | method.setAccess(mm.access()); |
272 | method.setAttributes(mm.attributes()); |
273 | } |
274 | |
275 | if (signalsObject) |
276 | return builder.toMetaObject(); |
277 | |
278 | // Add our property accessor methods |
279 | // NOTE: required because read/reset properties over DBus require adaptors |
280 | // otherwise a metacall won't be invoked as QMetaObject::ReadProperty |
281 | // or QMetaObject::ResetProperty |
282 | QMetaMethodBuilder readProp; |
283 | readProp = builder.addMethod(signature: QByteArray("propertyRead(QString)" )); |
284 | readProp.setReturnType(QByteArray("QDBusVariant" )); |
285 | QList<QByteArray> params; |
286 | params << QByteArray("name" ); |
287 | readProp.setParameterNames(params); |
288 | |
289 | QMetaMethodBuilder resetProp; |
290 | resetProp = builder.addMethod(signature: QByteArray("propertyReset(QString)" )); |
291 | QList<QByteArray> paramsReset; |
292 | paramsReset << QByteArray("name" ); |
293 | resetProp.setParameterNames(paramsReset); |
294 | |
295 | |
296 | // Add our properties/enums |
297 | int propCount = d->serviceMeta->propertyCount(); |
298 | for (int i=0; i<propCount; i++) { |
299 | QMetaProperty mp = d->serviceMeta->property(index: i); |
300 | |
301 | QMetaPropertyBuilder property = builder.addProperty(name: mp.name(), type: mp.typeName()); |
302 | property.setReadable(mp.isReadable()); |
303 | property.setWritable(mp.isWritable()); |
304 | property.setResettable(mp.isResettable()); |
305 | property.setDesignable(mp.isDesignable()); |
306 | property.setScriptable(mp.isScriptable()); |
307 | property.setStored(mp.isStored()); |
308 | property.setEditable(mp.isEditable()); |
309 | property.setUser(mp.isUser()); |
310 | property.setStdCppSet(mp.hasStdCppSet()); |
311 | property.setEnumOrFlag(mp.isEnumType()); |
312 | } |
313 | |
314 | // Needs Enumerators support when QtDBus supports |
315 | |
316 | mo = mo->superClass(); |
317 | } |
318 | |
319 | // return our constructed dbus metaobject |
320 | return builder.toMetaObject(); |
321 | } |
322 | |
323 | /*! |
324 | Provide custom Q_OBJECT implementation of the metaObject |
325 | */ |
326 | const QMetaObject* QServiceMetaObjectDBus::metaObject() const |
327 | { |
328 | // Provide our construected DBus service metaobject |
329 | return d->dbusMeta; |
330 | } |
331 | |
332 | /*! |
333 | Overrides metacall which relays the DBus service call to the actual service |
334 | meta object. Positive return will indicate that the metacall was unsuccessful |
335 | */ |
336 | int QServiceMetaObjectDBus::qt_metacall(QMetaObject::Call c, int id, void **a) |
337 | { |
338 | int dbusIndex = id; |
339 | |
340 | // Relay the meta-object call to the service object |
341 | if (c == QMetaObject::InvokeMetaMethod) { |
342 | // METHOD CALL |
343 | QMetaMethod method = d->dbusMeta->method(index: id); |
344 | |
345 | const bool isSignal = (method.methodType() == QMetaMethod::Signal); |
346 | |
347 | ///////////////////// CHECK SPECIAL PROPERTY /////////////////////// |
348 | // This is required because property READ/RESET doesn't function |
349 | // as desired over DBus without the use of adaptors. Temporary |
350 | // methods propertyRead and propertyReset are added to the published |
351 | // meta object and relay the correct property call |
352 | QString methodName(QLatin1String(method.methodSignature().constData())); |
353 | methodName.truncate(pos: methodName.indexOf(s: QLatin1String("(" ))); |
354 | |
355 | if (methodName == QLatin1String("propertyRead" )) { |
356 | QString propertyName = *reinterpret_cast<QString*>(a[1]); |
357 | int index = d->dbusMeta->indexOfProperty(name: propertyName.toLatin1().constData()); |
358 | id = qt_metacall(c: QMetaObject::ReadProperty, id: index, a); |
359 | return id; |
360 | |
361 | } else if (methodName == QLatin1String("propertyReset" )) { |
362 | QString propertyName = *reinterpret_cast<QString*>(a[1]); |
363 | int index = d->dbusMeta->indexOfProperty(name: propertyName.toLatin1().constData()); |
364 | id = qt_metacall(c: QMetaObject::ResetProperty, id: index, a); |
365 | return id; |
366 | } |
367 | //////////////////////////////////////////////////////////////////// |
368 | |
369 | // Find the corresponding signature to our service object |
370 | QByteArray sig(method.methodSignature()); |
371 | int count = methodName.size() + 1; |
372 | const QList<QByteArray> xTypes = method.parameterTypes(); |
373 | const int xTypesCount = xTypes.count(); |
374 | for (int i=0; i < xTypesCount; i++) { |
375 | const QByteArray& t = xTypes[i]; |
376 | int variantType = QMetaType::type(typeName: t); |
377 | |
378 | // Check for QVariants or custom types, represented as QDBusVariants |
379 | if (t == "QDBusVariant" ) { |
380 | // Convert QDBusVariant to QVariant |
381 | QVariant convert = QVariant(variantType, a[i+1]); |
382 | QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(v: convert); |
383 | QVariant variant = dbusVariant.variant(); |
384 | |
385 | // Is a custom argument if castable to QDBusArgument |
386 | bool hasCustomType = variant.canConvert<QDBusArgument>(); |
387 | QByteArray replacement("QVariant" ); |
388 | |
389 | // Custom types will have QDBusArgument streaming operators for |
390 | // the QServiceUserTypeDBus object. Extract the real type name |
391 | // and buffered QVariant from this |
392 | if (hasCustomType) { |
393 | QDBusArgument demarshall = variant.value<QDBusArgument>(); |
394 | QServiceUserTypeDBus userType = qdbus_cast<QServiceUserTypeDBus>(arg: demarshall); |
395 | *reinterpret_cast<QVariant*>(a[i+1]) = userType.variantBuffer; |
396 | |
397 | replacement = userType.typeName; |
398 | } |
399 | |
400 | // Replace "QDBusVariant" with "QVariant" or custom type name |
401 | sig.replace(index: count, len: 12, s: replacement); |
402 | count += replacement.size(); |
403 | |
404 | } else { |
405 | // Supported type so skip this paramater |
406 | count += t.size(); |
407 | } |
408 | |
409 | // Skips the comma if not last parameter |
410 | if (i < xTypesCount) |
411 | count += 1; |
412 | } |
413 | |
414 | // Find the corresponding method metaindex to our service object |
415 | id = d->serviceMeta->indexOfMethod(method: sig); |
416 | QMetaMethod mm = d->serviceMeta->method(index: id); |
417 | |
418 | const QList<QByteArray> pTypes = mm.parameterTypes(); |
419 | const int pTypesCount = pTypes.count(); |
420 | |
421 | const char* typeNames[] = {0,0,0,0,0,0,0,0,0,0}; |
422 | const void* params[] = {0,0,0,0,0,0,0,0,0,0}; |
423 | bool hasCustomType = false; |
424 | |
425 | // Process arguments |
426 | for (int i=0; i < pTypesCount; i++) { |
427 | const QByteArray& t = pTypes[i]; |
428 | int variantType = QMetaType::type(typeName: t); |
429 | |
430 | if (variantType >= QMetaType::User) { |
431 | // Custom argument |
432 | QVariant convert = QVariant(QVariant::ByteArray, a[i+1]); |
433 | QByteArray buffer = convert.toByteArray(); |
434 | QDataStream stream(&buffer, QIODevice::ReadWrite); |
435 | |
436 | // Load our buffered variant-wrapped custom type |
437 | QVariant *customType = new QVariant(variantType, (const void*)0); |
438 | QMetaType::load(stream, type: QMetaType::QVariant, data: customType); |
439 | |
440 | typeNames[i] = customType->typeName(); |
441 | params[i] = customType->constData(); |
442 | hasCustomType = true; |
443 | } else { |
444 | typeNames[i] = t.constData(); |
445 | params[i] = a[i+1]; |
446 | } |
447 | } |
448 | |
449 | // Check if this is a signal emit and activate |
450 | if (isSignal) { |
451 | QMetaObject::activate(sender: this, signal_index: dbusIndex, argv: a); |
452 | return id; |
453 | } |
454 | |
455 | // Check for custom return types and make the metacall |
456 | const QByteArray& type = mm.typeName(); |
457 | int retType = QMetaType::type(typeName: type); |
458 | if (retType >= QMetaType::User) { |
459 | // Invoke the object method directly for custom return types |
460 | bool result = false; |
461 | QVariant returnValue = QVariant(retType, (const void*)0); |
462 | QGenericReturnArgument ret(type, returnValue.data()); |
463 | result = mm.invoke(object: d->service, returnValue: ret, |
464 | val0: QGenericArgument(typeNames[0], params[0]), |
465 | val1: QGenericArgument(typeNames[1], params[1]), |
466 | val2: QGenericArgument(typeNames[2], params[2]), |
467 | val3: QGenericArgument(typeNames[3], params[3]), |
468 | val4: QGenericArgument(typeNames[4], params[4]), |
469 | val5: QGenericArgument(typeNames[5], params[5]), |
470 | val6: QGenericArgument(typeNames[6], params[6]), |
471 | val7: QGenericArgument(typeNames[7], params[7]), |
472 | val8: QGenericArgument(typeNames[8], params[8]), |
473 | val9: QGenericArgument(typeNames[9], params[9])); |
474 | |
475 | if (result) { |
476 | // Wrap custom return type in a QDBusVariant of the type |
477 | // and a buffer of its variant-wrapped data |
478 | QByteArray buffer; |
479 | QDataStream stream(&buffer, QIODevice::WriteOnly | QIODevice::Append); |
480 | stream << returnValue; |
481 | |
482 | QServiceUserTypeDBus customType; |
483 | customType.typeName = type; |
484 | customType.variantBuffer = buffer; |
485 | |
486 | QDBusVariant replacement(QVariant::fromValue(value: customType)); |
487 | *reinterpret_cast<QDBusVariant*>(a[0]) = replacement; |
488 | |
489 | // Return negative id to say metacall was handled externally |
490 | return -1; |
491 | } |
492 | |
493 | } else { |
494 | // Void or standard return types |
495 | if (hasCustomType == true) { |
496 | // Invoke the object method directly for custom arguments |
497 | bool result = false; |
498 | result = mm.invoke(object: d->service, |
499 | val0: QGenericArgument(typeNames[0], params[0]), |
500 | val1: QGenericArgument(typeNames[1], params[1]), |
501 | val2: QGenericArgument(typeNames[2], params[2]), |
502 | val3: QGenericArgument(typeNames[3], params[3]), |
503 | val4: QGenericArgument(typeNames[4], params[4]), |
504 | val5: QGenericArgument(typeNames[5], params[5]), |
505 | val6: QGenericArgument(typeNames[6], params[6]), |
506 | val7: QGenericArgument(typeNames[7], params[7]), |
507 | val8: QGenericArgument(typeNames[8], params[8]), |
508 | val9: QGenericArgument(typeNames[9], params[9])); |
509 | if (result) { |
510 | // Return negative id to say metacall was handled externally |
511 | return -1; |
512 | } |
513 | } else { |
514 | // Relay standard metacall to service object |
515 | id = d->service->qt_metacall(c, id, a); |
516 | } |
517 | } |
518 | |
519 | } else { |
520 | // PROPERTY CALL |
521 | |
522 | // Find the corresponding property metaindex of our service object |
523 | QMetaProperty property = d->dbusMeta->property(index: id); |
524 | QByteArray name(property.name()); |
525 | id = d->serviceMeta->indexOfProperty(name); |
526 | |
527 | if (c == QMetaObject::ReadProperty) { |
528 | // Convert to DBusVariant |
529 | QMetaProperty mp = d->serviceMeta->property(index: id); |
530 | QVariant value = mp.read(obj: d->service); |
531 | QDBusVariant replacement(value); |
532 | *reinterpret_cast<QDBusVariant*>(a[0]) = replacement; |
533 | |
534 | // Return negative id to say metacall was handled externally |
535 | return -1; |
536 | } |
537 | |
538 | // Metacall our service object property |
539 | id = d->service->qt_metacall(c, id, a); |
540 | } |
541 | |
542 | return id; |
543 | } |
544 | |
545 | void *QServiceMetaObjectDBus::qt_metacast(const char* className) |
546 | { |
547 | if (!className) return 0; |
548 | //this object should not be castable to anything but QObject |
549 | return QObject::qt_metacast(className); |
550 | } |
551 | |
552 | QDBusArgument &operator<<(QDBusArgument &argument, const QServiceUserTypeDBus &myType) |
553 | { |
554 | argument.beginStructure(); |
555 | argument << myType.typeName << myType.variantBuffer; |
556 | argument.endStructure(); |
557 | return argument; |
558 | } |
559 | |
560 | const QDBusArgument &operator>>(const QDBusArgument &argument, QServiceUserTypeDBus &myType) |
561 | { |
562 | argument.beginStructure(); |
563 | argument >> myType.typeName >> myType.variantBuffer; |
564 | argument.endStructure(); |
565 | return argument; |
566 | } |
567 | |
568 | QT_END_NAMESPACE |
569 | |