1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the QtSystems module of the Qt Toolkit.
8
** $QT_BEGIN_LICENSE:LGPL$
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 Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
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 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
40
****************************************************************************/
42
#include "qservicemetaobject_dbus_p.h"
43
#include <private/qmetaobjectbuilder_p.h>
44
#include "qsignalintercepter_p.h"
51
class ServiceMetaSignalIntercepter : public QSignalIntercepter
55
ServiceMetaSignalIntercepter(QObject* sender, const QByteArray& signal, QServiceMetaObjectDBus* parent)
56
: QSignalIntercepter(sender, signal, parent), serviceDBus(parent)
61
void setMetaIndex(int index)
67
void activated( const QList<QVariant>& args )
69
serviceDBus->activateMetaSignal(metaIndex, args);
72
QServiceMetaObjectDBus* serviceDBus;
77
class QServiceMetaObjectDBusPrivate
81
const QMetaObject *serviceMeta;
82
const QMetaObject *dbusMeta;
85
QServiceMetaObjectDBus::QServiceMetaObjectDBus(QObject* service, bool signalsObject)
86
: QDBusAbstractAdaptor(service)
88
// Register our DBus custom type object
89
qRegisterMetaType<QServiceUserTypeDBus>();
90
qDBusRegisterMetaType<QServiceUserTypeDBus>();
92
// Generate our DBus meta object
93
d = new QServiceMetaObjectDBusPrivate();
95
d->serviceMeta = service->metaObject();
96
d->dbusMeta = dbusMetaObject(signalsObject);
98
// Relay signals from the service to the constructed DBus meta object
99
connectMetaSignals(signalsObject);
102
QServiceMetaObjectDBus::~QServiceMetaObjectDBus()
105
free(const_cast<QMetaObject*>(d->dbusMeta));
111
Relays all signals from the service object to the converted DBus service adaptor so
112
that when a service signal is emitted the DBus object will emit the counterpart signal
114
void QServiceMetaObjectDBus::connectMetaSignals(bool signalsObject) {
115
if (!signalsObject) {
116
// Automatically relay signals from service object to adaptor
117
setAutoRelaySignals(true);
119
// Connect signals with custom arguments
120
int methodCount = d->serviceMeta->methodCount();
121
for (int i = 0; i < methodCount; i++) {
122
QMetaMethod mm = d->serviceMeta->method(i);
125
if (mm.methodType() == QMetaMethod::Signal) {
126
QByteArray sig(mm.methodSignature());
127
bool customType = false;
128
const QList<QByteArray> pTypes = mm.parameterTypes();
129
const int pTypesCount = pTypes.count();
131
// Ignore all QObject calls
132
const QMetaObject *mo = QObject::metaObject();
133
int qobjectIndex = mo->indexOfMethod(sig);
134
if (qobjectIndex >= 0)
137
// Detects custom types as passed arguments
138
for (int arg = 0; arg < pTypesCount; arg++) {
139
const QByteArray& type = pTypes[arg];
140
int variantType = QMetaType::type(type);
141
if (variantType >= QMetaType::User || variantType == QMetaType::QVariant) {
142
sig.replace(QByteArray(type), QByteArray("QDBusVariant"));
147
// Connects the service signal to the corresponding DBus service signal
149
QByteArray signal = mm.methodSignature();
150
ServiceMetaSignalIntercepter *intercept =
151
new ServiceMetaSignalIntercepter(d->service, "2"+signal, this);
152
intercept->setMetaIndex(i);
160
Relays the activation to the DBus object signal with the given id and arguments list when the
161
intercepter for signals with custom arguments has been activated. This bypasses the metacall
162
which usually does the relaying for signals with standard arguments since no pre-connection
163
conversions are required.
165
void QServiceMetaObjectDBus::activateMetaSignal(int id, const QVariantList& args)
167
QMetaMethod method = d->serviceMeta->method(id);
169
// Converts the argument list to values supported by the QtDBus type system
170
QVariantList convertedList = args;
171
QByteArray sig(method.methodSignature());
172
QList<QByteArray> params = method.parameterTypes();
174
for (int i = 0; i < params.size(); i++) {
175
QVariant dbusVariant = args[i];
177
// Convert custom types
178
const QByteArray& type = params[i];
179
int variantType = QMetaType::type(type);
180
if (variantType >= QMetaType::User || variantType == QMetaType::QVariant) {
181
if (variantType != QMetaType::QVariant) {
182
// Wrap custom types in a QDBusVariant of the type name and
183
// a buffer of its variant-wrapped data
185
QDataStream stream(&buffer, QIODevice::ReadWrite | QIODevice::Append);
188
QServiceUserTypeDBus customType;
189
customType.typeName = type;
190
customType.variantBuffer = buffer;
192
QDBusVariant replacement(QVariant::fromValue(customType));
193
convertedList.replace(i, QVariant::fromValue(replacement));
196
sig.replace(QByteArray(type), QByteArray("QDBusVariant"));
200
// Activate the DBus object signal call
201
const int numArgs = convertedList.size();
202
QVarLengthArray<void *, 32> a( numArgs+1 );
205
const QList<QByteArray> pTypes = method.parameterTypes();
206
for ( int arg = 0; arg < numArgs; ++arg ) {
207
if (pTypes.at(arg) == "QVariant")
208
a[arg+1] = (void *)&( convertedList[arg] );
210
a[arg+1] = (void *)( convertedList[arg].data() );
213
int dbusIndex = d->dbusMeta->indexOfSignal(sig);
214
QMetaObject::activate(this, dbusIndex, a.data());
219
Build a metaobject that represents the service object as a valid service that
220
satisfies the QtDBus type system.
222
const QMetaObject* QServiceMetaObjectDBus::dbusMetaObject(bool signalsObject) const
224
// Create a meta-object to represent all the contents of our service on DBus
225
QMetaObjectBuilder builder;
227
builder.setClassName(d->serviceMeta->className());
228
builder.setSuperClass(d->serviceMeta->superClass()); // needed?
230
const QMetaObject* mo = d->serviceMeta;
231
while (mo && strcmp(mo->className(), "QObject")) {
232
// Add our methods, signals and slots
233
for (int i = mo->methodOffset(); i < mo->methodCount(); i++) {
234
QMetaMethod mm = mo->method(i);
236
if (signalsObject && mm.methodType() != QMetaMethod::Signal)
239
// Convert QVariant and custom return types to QDBusVariants
240
QByteArray ret(mm.typeName());
241
const QByteArray& type = mm.typeName();
242
int variantType = QMetaType::type(type);
243
if (variantType >= QMetaType::User || variantType == QMetaType::QVariant) {
244
ret = QByteArray("QDBusVariant");
247
// Convert QVariant and custom argument types to QDBusVariants
248
QByteArray sig(mm.methodSignature());
249
const QList<QByteArray> pTypes = mm.parameterTypes();
250
const int pTypesCount = pTypes.count();
251
for (int i=0; i < pTypesCount; i++) {
252
const QByteArray& type = pTypes[i];
253
int variantType = QMetaType::type(type);
254
if (variantType >= QMetaType::User || variantType == QMetaType::QVariant) {
255
sig.replace(QByteArray(type), QByteArray("QDBusVariant"));
259
// Add a MetaMethod with converted signature to our builder
260
QMetaMethodBuilder method;
261
switch (mm.methodType()) {
262
case QMetaMethod::Method:
263
method = builder.addMethod(sig);
265
case QMetaMethod::Slot:
266
method = builder.addSlot(sig);
268
case QMetaMethod::Signal:
269
method = builder.addSignal(sig);
275
// Make sure our built MetaMethod is identical, excluding conversion
276
method.setReturnType(ret);
277
method.setParameterNames(mm.parameterNames());
278
method.setTag(mm.tag());
279
method.setAccess(mm.access());
280
method.setAttributes(mm.attributes());
284
return builder.toMetaObject();
286
// Add our property accessor methods
287
// NOTE: required because read/reset properties over DBus require adaptors
288
// otherwise a metacall won't be invoked as QMetaObject::ReadProperty
289
// or QMetaObject::ResetProperty
290
QMetaMethodBuilder readProp;
291
readProp = builder.addMethod(QByteArray("propertyRead(QString)"));
292
readProp.setReturnType(QByteArray("QDBusVariant"));
293
QList<QByteArray> params;
294
params << QByteArray("name");
295
readProp.setParameterNames(params);
297
QMetaMethodBuilder resetProp;
298
resetProp = builder.addMethod(QByteArray("propertyReset(QString)"));
299
QList<QByteArray> paramsReset;
300
paramsReset << QByteArray("name");
301
resetProp.setParameterNames(paramsReset);
304
// Add our properties/enums
305
int propCount = d->serviceMeta->propertyCount();
306
for (int i=0; i<propCount; i++) {
307
QMetaProperty mp = d->serviceMeta->property(i);
309
QMetaPropertyBuilder property = builder.addProperty(mp.name(), mp.typeName());
310
property.setReadable(mp.isReadable());
311
property.setWritable(mp.isWritable());
312
property.setResettable(mp.isResettable());
313
property.setDesignable(mp.isDesignable());
314
property.setScriptable(mp.isScriptable());
315
property.setStored(mp.isStored());
316
property.setEditable(mp.isEditable());
317
property.setUser(mp.isUser());
318
property.setStdCppSet(mp.hasStdCppSet());
319
property.setEnumOrFlag(mp.isEnumType());
322
// Needs Enumerators support when QtDBus supports
324
mo = mo->superClass();
327
// return our constructed dbus metaobject
328
return builder.toMetaObject();
332
Provide custom Q_OBJECT implementation of the metaObject
334
const QMetaObject* QServiceMetaObjectDBus::metaObject() const
336
// Provide our construected DBus service metaobject
341
Overrides metacall which relays the DBus service call to the actual service
342
meta object. Positive return will indicate that the metacall was unsuccessful
344
int QServiceMetaObjectDBus::qt_metacall(QMetaObject::Call c, int id, void **a)
348
// Relay the meta-object call to the service object
349
if (c == QMetaObject::InvokeMetaMethod) {
351
QMetaMethod method = d->dbusMeta->method(id);
353
const bool isSignal = (method.methodType() == QMetaMethod::Signal);
355
///////////////////// CHECK SPECIAL PROPERTY ///////////////////////
356
// This is required because property READ/RESET doesn't function
357
// as desired over DBus without the use of adaptors. Temporary
358
// methods propertyRead and propertyReset are added to the published
359
// meta object and relay the correct property call
360
QString methodName(QLatin1String(method.methodSignature().constData()));
361
methodName.truncate(methodName.indexOf(QLatin1String("(")));
363
if (methodName == QLatin1String("propertyRead")) {
364
QString propertyName = *reinterpret_cast<QString*>(a[1]);
365
int index = d->dbusMeta->indexOfProperty(propertyName.toLatin1().constData());
366
id = qt_metacall(QMetaObject::ReadProperty, index, a);
369
} else if (methodName == QLatin1String("propertyReset")) {
370
QString propertyName = *reinterpret_cast<QString*>(a[1]);
371
int index = d->dbusMeta->indexOfProperty(propertyName.toLatin1().constData());
372
id = qt_metacall(QMetaObject::ResetProperty, index, a);
375
////////////////////////////////////////////////////////////////////
377
// Find the corresponding signature to our service object
378
QByteArray sig(method.methodSignature());
379
int count = methodName.size() + 1;
380
const QList<QByteArray> xTypes = method.parameterTypes();
381
const int xTypesCount = xTypes.count();
382
for (int i=0; i < xTypesCount; i++) {
383
const QByteArray& t = xTypes[i];
384
int variantType = QMetaType::type(t);
386
// Check for QVariants or custom types, represented as QDBusVariants
387
if (t == "QDBusVariant") {
388
// Convert QDBusVariant to QVariant
389
QVariant convert = QVariant(variantType, a[i+1]);
390
QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(convert);
391
QVariant variant = dbusVariant.variant();
393
// Is a custom argument if castable to QDBusArgument
394
bool hasCustomType = variant.canConvert<QDBusArgument>();
395
QByteArray replacement("QVariant");
397
// Custom types will have QDBusArgument streaming operators for
398
// the QServiceUserTypeDBus object. Extract the real type name
399
// and buffered QVariant from this
401
QDBusArgument demarshall = variant.value<QDBusArgument>();
402
QServiceUserTypeDBus userType = qdbus_cast<QServiceUserTypeDBus>(demarshall);
403
*reinterpret_cast<QVariant*>(a[i+1]) = userType.variantBuffer;
405
replacement = userType.typeName;
408
// Replace "QDBusVariant" with "QVariant" or custom type name
409
sig.replace(count, 12, replacement);
410
count += replacement.size();
413
// Supported type so skip this paramater
417
// Skips the comma if not last parameter
422
// Find the corresponding method metaindex to our service object
423
id = d->serviceMeta->indexOfMethod(sig);
424
QMetaMethod mm = d->serviceMeta->method(id);
426
const QList<QByteArray> pTypes = mm.parameterTypes();
427
const int pTypesCount = pTypes.count();
429
const char* typeNames[] = {0,0,0,0,0,0,0,0,0,0};
430
const void* params[] = {0,0,0,0,0,0,0,0,0,0};
431
bool hasCustomType = false;
434
for (int i=0; i < pTypesCount; i++) {
435
const QByteArray& t = pTypes[i];
436
int variantType = QMetaType::type(t);
438
if (variantType >= QMetaType::User) {
440
QVariant convert = QVariant(QVariant::ByteArray, a[i+1]);
441
QByteArray buffer = convert.toByteArray();
442
QDataStream stream(&buffer, QIODevice::ReadWrite);
444
// Load our buffered variant-wrapped custom type
445
QVariant *customType = new QVariant(variantType, (const void*)0);
446
QMetaType::load(stream, QMetaType::QVariant, customType);
448
typeNames[i] = customType->typeName();
449
params[i] = customType->constData();
450
hasCustomType = true;
454
// Check if this is a signal emit and activate
456
QMetaObject::activate(this, dbusIndex, a);
460
// Check for custom return types and make the metacall
461
const QByteArray& type = mm.typeName();
462
int retType = QMetaType::type(type);
463
if (retType >= QMetaType::User) {
464
// Invoke the object method directly for custom return types
466
QVariant returnValue = QVariant(retType, (const void*)0);
467
QGenericReturnArgument ret(type, returnValue.data());
468
result = mm.invoke(d->service, ret,
469
QGenericArgument(typeNames[0], params[0]),
470
QGenericArgument(typeNames[1], params[1]),
471
QGenericArgument(typeNames[2], params[2]),
472
QGenericArgument(typeNames[3], params[3]),
473
QGenericArgument(typeNames[4], params[4]),
474
QGenericArgument(typeNames[5], params[5]),
475
QGenericArgument(typeNames[6], params[6]),
476
QGenericArgument(typeNames[7], params[7]),
477
QGenericArgument(typeNames[8], params[8]),
478
QGenericArgument(typeNames[9], params[9]));
481
// Wrap custom return type in a QDBusVariant of the type
482
// and a buffer of its variant-wrapped data
484
QDataStream stream(&buffer, QIODevice::WriteOnly | QIODevice::Append);
485
stream << returnValue;
487
QServiceUserTypeDBus customType;
488
customType.typeName = type;
489
customType.variantBuffer = buffer;
491
QDBusVariant replacement(QVariant::fromValue(customType));
492
*reinterpret_cast<QDBusVariant*>(a[0]) = replacement;
494
// Return negative id to say metacall was handled externally
499
// Void or standard return types
500
if (hasCustomType == true) {
501
// Invoke the object method directly for custom arguments
503
result = mm.invoke(d->service,
504
QGenericArgument(typeNames[0], params[0]),
505
QGenericArgument(typeNames[1], params[1]),
506
QGenericArgument(typeNames[2], params[2]),
507
QGenericArgument(typeNames[3], params[3]),
508
QGenericArgument(typeNames[4], params[4]),
509
QGenericArgument(typeNames[5], params[5]),
510
QGenericArgument(typeNames[6], params[6]),
511
QGenericArgument(typeNames[7], params[7]),
512
QGenericArgument(typeNames[8], params[8]),
513
QGenericArgument(typeNames[9], params[9]));
515
// Return negative id to say metacall was handled externally
519
// Relay standard metacall to service object
520
id = d->service->qt_metacall(c, id, a);
527
// Find the corresponding property metaindex of our service object
528
QMetaProperty property = d->dbusMeta->property(id);
529
QByteArray name(property.name());
530
id = d->serviceMeta->indexOfProperty(name);
532
if (c == QMetaObject::ReadProperty) {
533
// Convert to DBusVariant
534
QMetaProperty mp = d->serviceMeta->property(id);
535
QVariant value = mp.read(d->service);
536
QDBusVariant replacement(value);
537
*reinterpret_cast<QDBusVariant*>(a[0]) = replacement;
539
// Return negative id to say metacall was handled externally
543
// Metacall our service object property
544
id = d->service->qt_metacall(c, id, a);
550
void *QServiceMetaObjectDBus::qt_metacast(const char* className)
552
if (!className) return 0;
553
//this object should not be castable to anything but QObject
554
return QObject::qt_metacast(className);
557
QDBusArgument &operator<<(QDBusArgument &argument, const QServiceUserTypeDBus &myType)
559
argument.beginStructure();
560
argument << myType.typeName << myType.variantBuffer;
561
argument.endStructure();
565
const QDBusArgument &operator>>(const QDBusArgument &argument, QServiceUserTypeDBus &myType)
567
argument.beginStructure();
568
argument >> myType.typeName >> myType.variantBuffer;
569
argument.endStructure();