1 // This is the implementation of the various Chimera helpers.
2 //
3 // Copyright (c) 2021 Riverbank Computing Limited <info@riverbankcomputing.com>
4 //
5 // This file is part of PyQt5.
6 //
7 // This file may be used under the terms of the GNU General Public License
8 // version 3.0 as published by the Free Software Foundation and appearing in
9 // the file LICENSE included in the packaging of this file.  Please review the
10 // following information to ensure the GNU General Public License version 3.0
11 // requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 //
13 // If you do not wish to use this file under the terms of the GPL version 3.0
14 // then you may purchase a commercial license.  For more information contact
15 // info@riverbankcomputing.com.
16 //
17 // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
18 // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 
20 
21 #include <Python.h>
22 
23 #include <QDBusArgument>
24 #include <QDBusObjectPath>
25 #include <QDBusSignature>
26 #include <QDBusVariant>
27 #include <QMetaType>
28 
29 #include "qpydbus_chimera_helpers.h"
30 
31 #include "sipAPIQtDBus.h"
32 
33 
34 // Forward declarations.
35 static PyObject *from_variant_type(const QDBusArgument &arg);
36 static PyObject *from_array_type(const QDBusArgument &arg);
37 static PyObject *from_structure_type(const QDBusArgument &arg);
38 static PyObject *from_map_type(const QDBusArgument &arg);
39 static PyObject *from_qstring(const QString &arg);
40 static PyObject *from_qvariant(const QVariant &arg);
41 
42 
43 // Convert a QVariant to a Python object.
qpydbus_from_qvariant_convertor(const QVariant & var,PyObject ** objp)44 bool qpydbus_from_qvariant_convertor(const QVariant &var, PyObject **objp)
45 {
46     // Handle QDBusObjectPath.
47     if (var.userType() == qMetaTypeId<QDBusObjectPath>())
48     {
49         *objp = from_qstring(var.value<QDBusObjectPath>().path());
50 
51         return true;
52     }
53 
54     // Handle QDBusSignature.
55     if (var.userType() == qMetaTypeId<QDBusSignature>())
56     {
57         *objp = from_qstring(var.value<QDBusSignature>().signature());
58 
59         return true;
60     }
61 
62     // Handle QDBusVariant.
63     if (var.userType() == qMetaTypeId<QDBusVariant>())
64     {
65         *objp = from_qvariant(var.value<QDBusVariant>().variant());
66 
67         return true;
68     }
69 
70     // Anything else must be a QDBusArgument.
71     if (var.userType() != qMetaTypeId<QDBusArgument>())
72         return false;
73 
74     QDBusArgument arg = var.value<QDBusArgument>();
75 
76     switch (arg.currentType())
77     {
78     case QDBusArgument::BasicType:
79         *objp = from_qvariant(arg.asVariant());
80         break;
81 
82     case QDBusArgument::VariantType:
83         *objp = from_variant_type(arg);
84         break;
85 
86     case QDBusArgument::ArrayType:
87         *objp = from_array_type(arg);
88         break;
89 
90     case QDBusArgument::StructureType:
91         *objp = from_structure_type(arg);
92         break;
93 
94     case QDBusArgument::MapType:
95         *objp = from_map_type(arg);
96         break;
97 
98     default:
99         PyErr_Format(PyExc_TypeError, "unsupported DBus argument type %d",
100                 (int)arg.currentType());
101         *objp = 0;
102     }
103 
104     return true;
105 }
106 
107 
108 // Convert a QDBusArgument variant type to a Python object.
from_variant_type(const QDBusArgument & arg)109 static PyObject *from_variant_type(const QDBusArgument &arg)
110 {
111     QDBusVariant dbv;
112 
113     arg >> dbv;
114 
115     return from_qvariant(dbv.variant());
116 }
117 
118 
119 // Convert a QDBusArgument array type to a Python object.
from_array_type(const QDBusArgument & arg)120 static PyObject *from_array_type(const QDBusArgument &arg)
121 {
122     QVariantList vl;
123 
124     arg.beginArray();
125 
126     while (!arg.atEnd())
127         vl.append(arg.asVariant());
128 
129     arg.endArray();
130 
131     PyObject *obj = PyList_New(vl.count());
132 
133     if (!obj)
134         return 0;
135 
136     for (int i = 0; i < vl.count(); ++i)
137     {
138         PyObject *itm = from_qvariant(vl.at(i));
139 
140         if (!itm)
141         {
142             Py_DECREF(obj);
143             return 0;
144         }
145 
146         PyList_SetItem(obj, i, itm);
147     }
148 
149     return obj;
150 }
151 
152 
153 // Convert a QDBusArgument structure type to a Python object.
from_structure_type(const QDBusArgument & arg)154 static PyObject *from_structure_type(const QDBusArgument &arg)
155 {
156     QVariantList vl;
157 
158     arg.beginStructure();
159 
160     while (!arg.atEnd())
161         vl.append(arg.asVariant());
162 
163     arg.endStructure();
164 
165     PyObject *obj = PyTuple_New(vl.count());
166 
167     if (!obj)
168         return 0;
169 
170     for (int i = 0; i < vl.count(); ++i)
171     {
172         PyObject *itm = from_qvariant(vl.at(i));
173 
174         if (!itm)
175         {
176             Py_DECREF(obj);
177             return 0;
178         }
179 
180         PyTuple_SetItem(obj, i, itm);
181     }
182 
183     return obj;
184 }
185 
186 
187 // Convert a QDBusArgument map type to a Python object.
from_map_type(const QDBusArgument & arg)188 static PyObject *from_map_type(const QDBusArgument &arg)
189 {
190     PyObject *obj = PyDict_New();
191 
192     if (!obj)
193         return 0;
194 
195     arg.beginMap();
196 
197     while (!arg.atEnd())
198     {
199         arg.beginMapEntry();
200 
201         PyObject *key = from_qvariant(arg.asVariant());
202         PyObject *value = from_qvariant(arg.asVariant());
203 
204         arg.endMapEntry();
205 
206         if (!key || !value)
207         {
208             Py_XDECREF(key);
209             Py_XDECREF(value);
210             Py_DECREF(obj);
211 
212             return 0;
213         }
214 
215         int rc = PyDict_SetItem(obj, key, value);
216 
217         Py_DECREF(key);
218         Py_DECREF(value);
219 
220         if (rc < 0)
221         {
222             Py_DECREF(obj);
223 
224             return 0;
225         }
226     }
227 
228     arg.endMap();
229 
230     return obj;
231 }
232 
233 
234 // Convert a QString to a Python object.
from_qstring(const QString & arg)235 static PyObject *from_qstring(const QString &arg)
236 {
237     QString *heap = new QString(arg);
238     PyObject *obj = sipConvertFromNewType(heap, sipType_QString, 0);
239 
240     if (!obj)
241         delete heap;
242 
243     return obj;
244 }
245 
246 
247 // Convert a QVariant to a Python object.
from_qvariant(const QVariant & arg)248 static PyObject *from_qvariant(const QVariant &arg)
249 {
250     QVariant *heap = new QVariant(arg);
251     PyObject *obj = sipConvertFromNewType(heap, sipType_QVariant, 0);
252 
253     if (!obj)
254         delete heap;
255 
256     return obj;
257 }
258