1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtDBus module of the Qt Toolkit. |
7 | ** |
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 The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://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 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qdbusmetatype.h" |
41 | #include "qdbusmetatype_p.h" |
42 | |
43 | #include <string.h> |
44 | #include "qdbus_symbols_p.h" |
45 | |
46 | #include <qbytearray.h> |
47 | #include <qglobal.h> |
48 | #include <qreadwritelock.h> |
49 | #include <qvector.h> |
50 | |
51 | #include "qdbusargument_p.h" |
52 | #include "qdbusutil_p.h" |
53 | #include "qdbusunixfiledescriptor.h" |
54 | #ifndef QT_BOOTSTRAPPED |
55 | #include "qdbusmessage.h" |
56 | #endif |
57 | |
58 | #ifndef QT_NO_DBUS |
59 | |
60 | #ifndef DBUS_TYPE_UNIX_FD |
61 | # define DBUS_TYPE_UNIX_FD int('h') |
62 | # define DBUS_TYPE_UNIX_FD_AS_STRING "h" |
63 | #endif |
64 | |
65 | QT_BEGIN_NAMESPACE |
66 | |
67 | class QDBusCustomTypeInfo |
68 | { |
69 | public: |
70 | QDBusCustomTypeInfo() : signature(), marshall(nullptr), demarshall(nullptr) |
71 | { } |
72 | |
73 | // Suggestion: |
74 | // change 'signature' to char* and make QDBusCustomTypeInfo a Movable type |
75 | QByteArray signature; |
76 | QDBusMetaType::MarshallFunction marshall; |
77 | QDBusMetaType::DemarshallFunction demarshall; |
78 | }; |
79 | |
80 | template<typename T> |
81 | inline static void registerHelper(T * = nullptr) |
82 | { |
83 | void (*mf)(QDBusArgument &, const T *) = qDBusMarshallHelper<T>; |
84 | void (*df)(const QDBusArgument &, T *) = qDBusDemarshallHelper<T>; |
85 | QDBusMetaType::registerMarshallOperators(typeId: qMetaTypeId<T>(), |
86 | reinterpret_cast<QDBusMetaType::MarshallFunction>(mf), |
87 | reinterpret_cast<QDBusMetaType::DemarshallFunction>(df)); |
88 | } |
89 | |
90 | void QDBusMetaTypeId::init() |
91 | { |
92 | static QBasicAtomicInt initialized = Q_BASIC_ATOMIC_INITIALIZER(false); |
93 | |
94 | // reentrancy is not a problem since everything else is locked on their own |
95 | // set the guard variable at the end |
96 | if (!initialized.loadRelaxed()) { |
97 | // register our types with Qt Core (calling qMetaTypeId<T>() does this implicitly) |
98 | (void)message(); |
99 | (void)argument(); |
100 | (void)variant(); |
101 | (void)objectpath(); |
102 | (void)signature(); |
103 | (void)error(); |
104 | (void)unixfd(); |
105 | |
106 | #ifndef QDBUS_NO_SPECIALTYPES |
107 | // and register Qt Core's with us |
108 | registerHelper<QDate>(); |
109 | registerHelper<QTime>(); |
110 | registerHelper<QDateTime>(); |
111 | registerHelper<QRect>(); |
112 | registerHelper<QRectF>(); |
113 | registerHelper<QSize>(); |
114 | registerHelper<QSizeF>(); |
115 | registerHelper<QPoint>(); |
116 | registerHelper<QPointF>(); |
117 | registerHelper<QLine>(); |
118 | registerHelper<QLineF>(); |
119 | registerHelper<QVariantList>(); |
120 | registerHelper<QVariantMap>(); |
121 | registerHelper<QVariantHash>(); |
122 | |
123 | qDBusRegisterMetaType<QList<bool> >(); |
124 | qDBusRegisterMetaType<QList<short> >(); |
125 | qDBusRegisterMetaType<QList<ushort> >(); |
126 | qDBusRegisterMetaType<QList<int> >(); |
127 | qDBusRegisterMetaType<QList<uint> >(); |
128 | qDBusRegisterMetaType<QList<qlonglong> >(); |
129 | qDBusRegisterMetaType<QList<qulonglong> >(); |
130 | qDBusRegisterMetaType<QList<double> >(); |
131 | qDBusRegisterMetaType<QList<QDBusObjectPath> >(); |
132 | qDBusRegisterMetaType<QList<QDBusSignature> >(); |
133 | qDBusRegisterMetaType<QList<QDBusUnixFileDescriptor> >(); |
134 | |
135 | qDBusRegisterMetaType<QVector<bool> >(); |
136 | qDBusRegisterMetaType<QVector<short> >(); |
137 | qDBusRegisterMetaType<QVector<ushort> >(); |
138 | qDBusRegisterMetaType<QVector<int> >(); |
139 | qDBusRegisterMetaType<QVector<uint> >(); |
140 | qDBusRegisterMetaType<QVector<qlonglong> >(); |
141 | qDBusRegisterMetaType<QVector<qulonglong> >(); |
142 | qDBusRegisterMetaType<QVector<double> >(); |
143 | qDBusRegisterMetaType<QVector<QDBusObjectPath> >(); |
144 | qDBusRegisterMetaType<QVector<QDBusSignature> >(); |
145 | qDBusRegisterMetaType<QVector<QDBusUnixFileDescriptor> >(); |
146 | #endif |
147 | |
148 | initialized.storeRelaxed(newValue: true); |
149 | } |
150 | } |
151 | |
152 | Q_GLOBAL_STATIC(QVector<QDBusCustomTypeInfo>, customTypes) |
153 | Q_GLOBAL_STATIC(QReadWriteLock, customTypesLock) |
154 | |
155 | /*! |
156 | \class QDBusMetaType |
157 | \inmodule QtDBus |
158 | \brief Meta-type registration system for the Qt D-Bus module. |
159 | \internal |
160 | |
161 | The QDBusMetaType class allows you to register class types for |
162 | marshalling and demarshalling over D-Bus. D-Bus supports a very |
163 | limited set of primitive types, but allows one to extend the type |
164 | system by creating compound types, such as arrays (lists) and |
165 | structs. In order to use them with Qt D-Bus, those types must be |
166 | registered. |
167 | |
168 | See \l {qdbustypesystem.html}{Qt D-Bus Type System} for more |
169 | information on the type system and how to register additional |
170 | types. |
171 | |
172 | \sa {qdbustypesystem.html}{Qt D-Bus Type System}, |
173 | qDBusRegisterMetaType(), QMetaType, QVariant, QDBusArgument |
174 | */ |
175 | |
176 | /*! |
177 | \fn int qDBusRegisterMetaType() |
178 | \relates QDBusArgument |
179 | \threadsafe |
180 | \since 4.2 |
181 | |
182 | Registers \c{T} with the |
183 | \l {qdbustypesystem.html}{Qt D-Bus Type System} and the Qt \l |
184 | {QMetaType}{meta-type system}, if it's not already registered. |
185 | |
186 | To register a type, it must be declared as a meta-type with the |
187 | Q_DECLARE_METATYPE() macro, and then registered as in the |
188 | following example: |
189 | |
190 | \snippet code/src_qdbus_qdbusmetatype.cpp 0 |
191 | |
192 | If \c{T} isn't one of |
193 | Qt's \l{container classes}, the \c{operator<<} and |
194 | \c{operator>>} streaming operators between \c{T} and QDBusArgument |
195 | must be already declared. See the \l {qdbustypesystem.html}{Qt D-Bus |
196 | Type System} page for more information on how to declare such |
197 | types. |
198 | |
199 | This function returns the Qt meta type id for the type (the same |
200 | value that is returned from qRegisterMetaType()). |
201 | |
202 | \note The feature that a \c{T} inheriting a streamable type (including |
203 | the containers QList, QHash or QMap) can be streamed without providing |
204 | custom \c{operator<<} and \c{operator>>} is deprecated as of Qt 5.7, |
205 | because it ignores everything in \c{T} except the base class. There is |
206 | no diagnostic. You should always provide these operators for all types |
207 | you wish to stream and not rely on Qt-provided stream operators for |
208 | base classes. |
209 | |
210 | \sa {qdbustypesystem.html}{Qt D-Bus Type System}, qRegisterMetaType(), QMetaType |
211 | */ |
212 | |
213 | /*! |
214 | \typedef QDBusMetaType::MarshallFunction |
215 | \internal |
216 | */ |
217 | |
218 | /*! |
219 | \typedef QDBusMetaType::DemarshallFunction |
220 | \internal |
221 | */ |
222 | |
223 | /*! |
224 | \internal |
225 | Registers the marshalling and demarshalling functions for meta |
226 | type \a id. |
227 | */ |
228 | void QDBusMetaType::registerMarshallOperators(int id, MarshallFunction mf, |
229 | DemarshallFunction df) |
230 | { |
231 | QVector<QDBusCustomTypeInfo> *ct = customTypes(); |
232 | if (id < 0 || !mf || !df || !ct) |
233 | return; // error! |
234 | |
235 | QWriteLocker locker(customTypesLock()); |
236 | if (id >= ct->size()) |
237 | ct->resize(asize: id + 1); |
238 | QDBusCustomTypeInfo &info = (*ct)[id]; |
239 | info.marshall = mf; |
240 | info.demarshall = df; |
241 | } |
242 | |
243 | /*! |
244 | \internal |
245 | Executes the marshalling of type \a id (whose data is contained in |
246 | \a data) to the D-Bus marshalling argument \a arg. Returns \c true if |
247 | the marshalling succeeded, or false if an error occurred. |
248 | */ |
249 | bool QDBusMetaType::marshall(QDBusArgument &arg, int id, const void *data) |
250 | { |
251 | QDBusMetaTypeId::init(); |
252 | |
253 | MarshallFunction mf; |
254 | { |
255 | QReadLocker locker(customTypesLock()); |
256 | QVector<QDBusCustomTypeInfo> *ct = customTypes(); |
257 | if (id >= ct->size()) |
258 | return false; // non-existent |
259 | |
260 | const QDBusCustomTypeInfo &info = (*ct).at(i: id); |
261 | if (!info.marshall) { |
262 | mf = nullptr; // make gcc happy |
263 | return false; |
264 | } else |
265 | mf = info.marshall; |
266 | } |
267 | |
268 | mf(arg, data); |
269 | return true; |
270 | } |
271 | |
272 | /*! |
273 | \internal |
274 | Executes the demarshalling of type \a id (whose data will be placed in |
275 | \a data) from the D-Bus marshalling argument \a arg. Returns \c true if |
276 | the demarshalling succeeded, or false if an error occurred. |
277 | */ |
278 | bool QDBusMetaType::demarshall(const QDBusArgument &arg, int id, void *data) |
279 | { |
280 | QDBusMetaTypeId::init(); |
281 | |
282 | DemarshallFunction df; |
283 | { |
284 | QReadLocker locker(customTypesLock()); |
285 | QVector<QDBusCustomTypeInfo> *ct = customTypes(); |
286 | if (id >= ct->size()) |
287 | return false; // non-existent |
288 | |
289 | const QDBusCustomTypeInfo &info = (*ct).at(i: id); |
290 | if (!info.demarshall) { |
291 | df = nullptr; // make gcc happy |
292 | return false; |
293 | } else |
294 | df = info.demarshall; |
295 | } |
296 | #ifndef QT_BOOTSTRAPPED |
297 | QDBusArgument copy = arg; |
298 | df(copy, data); |
299 | #else |
300 | Q_UNUSED(arg); |
301 | Q_UNUSED(data); |
302 | Q_UNUSED(df); |
303 | #endif |
304 | return true; |
305 | } |
306 | |
307 | /*! |
308 | \fn QDBusMetaType::signatureToType(const char *signature) |
309 | \internal |
310 | |
311 | Returns the Qt meta type id for the given D-Bus signature for exactly one full type, given |
312 | by \a signature. |
313 | |
314 | Note: this function only handles the basic D-Bus types. |
315 | |
316 | \sa QDBusUtil::isValidSingleSignature(), typeToSignature(), |
317 | QVariant::type(), QVariant::userType() |
318 | */ |
319 | int QDBusMetaType::signatureToType(const char *signature) |
320 | { |
321 | if (!signature) |
322 | return QMetaType::UnknownType; |
323 | |
324 | QDBusMetaTypeId::init(); |
325 | switch (signature[0]) |
326 | { |
327 | case DBUS_TYPE_BOOLEAN: |
328 | return QMetaType::Bool; |
329 | |
330 | case DBUS_TYPE_BYTE: |
331 | return QMetaType::UChar; |
332 | |
333 | case DBUS_TYPE_INT16: |
334 | return QMetaType::Short; |
335 | |
336 | case DBUS_TYPE_UINT16: |
337 | return QMetaType::UShort; |
338 | |
339 | case DBUS_TYPE_INT32: |
340 | return QMetaType::Int; |
341 | |
342 | case DBUS_TYPE_UINT32: |
343 | return QMetaType::UInt; |
344 | |
345 | case DBUS_TYPE_INT64: |
346 | return QMetaType::LongLong; |
347 | |
348 | case DBUS_TYPE_UINT64: |
349 | return QMetaType::ULongLong; |
350 | |
351 | case DBUS_TYPE_DOUBLE: |
352 | return QMetaType::Double; |
353 | |
354 | case DBUS_TYPE_STRING: |
355 | return QMetaType::QString; |
356 | |
357 | case DBUS_TYPE_OBJECT_PATH: |
358 | return QDBusMetaTypeId::objectpath(); |
359 | |
360 | case DBUS_TYPE_SIGNATURE: |
361 | return QDBusMetaTypeId::signature(); |
362 | |
363 | case DBUS_TYPE_UNIX_FD: |
364 | return QDBusMetaTypeId::unixfd(); |
365 | |
366 | case DBUS_TYPE_VARIANT: |
367 | return QDBusMetaTypeId::variant(); |
368 | |
369 | case DBUS_TYPE_ARRAY: // special case |
370 | switch (signature[1]) { |
371 | case DBUS_TYPE_BYTE: |
372 | return QMetaType::QByteArray; |
373 | |
374 | case DBUS_TYPE_STRING: |
375 | return QMetaType::QStringList; |
376 | |
377 | case DBUS_TYPE_VARIANT: |
378 | return QMetaType::QVariantList; |
379 | |
380 | case DBUS_TYPE_OBJECT_PATH: |
381 | return qMetaTypeId<QList<QDBusObjectPath> >(); |
382 | |
383 | case DBUS_TYPE_SIGNATURE: |
384 | return qMetaTypeId<QList<QDBusSignature> >(); |
385 | |
386 | } |
387 | Q_FALLTHROUGH(); |
388 | default: |
389 | return QMetaType::UnknownType; |
390 | } |
391 | } |
392 | |
393 | /*! |
394 | \fn QDBusMetaType::typeToSignature(int type) |
395 | \internal |
396 | |
397 | Returns the D-Bus signature equivalent to the supplied meta type id \a type. |
398 | |
399 | More types can be registered with the qDBusRegisterMetaType() function. |
400 | |
401 | \sa QDBusUtil::isValidSingleSignature(), signatureToType(), |
402 | QVariant::type(), QVariant::userType() |
403 | */ |
404 | const char *QDBusMetaType::typeToSignature(int type) |
405 | { |
406 | // check if it's a static type |
407 | switch (type) |
408 | { |
409 | case QMetaType::UChar: |
410 | return DBUS_TYPE_BYTE_AS_STRING; |
411 | |
412 | case QMetaType::Bool: |
413 | return DBUS_TYPE_BOOLEAN_AS_STRING; |
414 | |
415 | case QMetaType::Short: |
416 | return DBUS_TYPE_INT16_AS_STRING; |
417 | |
418 | case QMetaType::UShort: |
419 | return DBUS_TYPE_UINT16_AS_STRING; |
420 | |
421 | case QMetaType::Int: |
422 | return DBUS_TYPE_INT32_AS_STRING; |
423 | |
424 | case QMetaType::UInt: |
425 | return DBUS_TYPE_UINT32_AS_STRING; |
426 | |
427 | case QMetaType::LongLong: |
428 | return DBUS_TYPE_INT64_AS_STRING; |
429 | |
430 | case QMetaType::ULongLong: |
431 | return DBUS_TYPE_UINT64_AS_STRING; |
432 | |
433 | case QMetaType::Double: |
434 | return DBUS_TYPE_DOUBLE_AS_STRING; |
435 | |
436 | case QMetaType::QString: |
437 | return DBUS_TYPE_STRING_AS_STRING; |
438 | |
439 | case QMetaType::QStringList: |
440 | return DBUS_TYPE_ARRAY_AS_STRING |
441 | DBUS_TYPE_STRING_AS_STRING; // as |
442 | |
443 | case QMetaType::QByteArray: |
444 | return DBUS_TYPE_ARRAY_AS_STRING |
445 | DBUS_TYPE_BYTE_AS_STRING; // ay |
446 | } |
447 | |
448 | QDBusMetaTypeId::init(); |
449 | if (type == QDBusMetaTypeId::variant()) |
450 | return DBUS_TYPE_VARIANT_AS_STRING; |
451 | else if (type == QDBusMetaTypeId::objectpath()) |
452 | return DBUS_TYPE_OBJECT_PATH_AS_STRING; |
453 | else if (type == QDBusMetaTypeId::signature()) |
454 | return DBUS_TYPE_SIGNATURE_AS_STRING; |
455 | else if (type == QDBusMetaTypeId::unixfd()) |
456 | return DBUS_TYPE_UNIX_FD_AS_STRING; |
457 | |
458 | // try the database |
459 | QVector<QDBusCustomTypeInfo> *ct = customTypes(); |
460 | { |
461 | QReadLocker locker(customTypesLock()); |
462 | if (type >= ct->size()) |
463 | return nullptr; // type not registered with us |
464 | |
465 | const QDBusCustomTypeInfo &info = (*ct).at(i: type); |
466 | |
467 | if (!info.signature.isNull()) |
468 | return info.signature; |
469 | |
470 | if (!info.marshall) |
471 | return nullptr; // type not registered with us |
472 | } |
473 | |
474 | // call to user code to construct the signature type |
475 | QDBusCustomTypeInfo *info; |
476 | { |
477 | // createSignature will never return a null QByteArray |
478 | // if there was an error, it'll return "" |
479 | QByteArray signature = QDBusArgumentPrivate::createSignature(id: type); |
480 | |
481 | // re-acquire lock |
482 | QWriteLocker locker(customTypesLock()); |
483 | info = &(*ct)[type]; |
484 | info->signature = signature; |
485 | } |
486 | return info->signature; |
487 | } |
488 | |
489 | QT_END_NAMESPACE |
490 | |
491 | #endif // QT_NO_DBUS |
492 | |