1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt for Python.
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 "pysideqmlregistertype.h"
41 
42 // shiboken
43 #include <shiboken.h>
44 #include <signature.h>
45 
46 // pyside
47 #include <pyside.h>
48 #include <pyside_p.h>
49 #include <pysideproperty.h>
50 
51 // auto generated headers
52 #include "pyside2_qtcore_python.h"
53 #include "pyside2_qtqml_python.h"
54 
55 #ifndef PYSIDE_MAX_QML_TYPES
56 // Maximum number of different Qt QML types the user can export to QML using
57 // qmlRegisterType. This limit exists because the QML engine instantiates objects
58 // by calling a function with one argument (a void *pointer where the object should
59 // be created), and thus does not allow us to choose which object to create. Thus
60 // we create a C++ factory function for each new registered type at compile time.
61 #define PYSIDE_MAX_QML_TYPES 50
62 #endif
63 
64 // Forward declarations.
65 static void propListMetaCall(PySideProperty *pp, PyObject *self, QMetaObject::Call call,
66                              void **args);
67 
68 // All registered python types and their creation functions.
69 static PyObject *pyTypes[PYSIDE_MAX_QML_TYPES];
70 static void (*createFuncs[PYSIDE_MAX_QML_TYPES])(void *);
71 
72 // Mutex used to avoid race condition on PySide::nextQObjectMemoryAddr.
73 static QMutex nextQmlElementMutex;
74 
75 template<int N>
76 struct ElementFactoryBase
77 {
createIntoElementFactoryBase78     static void createInto(void *memory)
79     {
80         QMutexLocker locker(&nextQmlElementMutex);
81         PySide::setNextQObjectMemoryAddr(memory);
82         Shiboken::GilState state;
83         PyObject *obj = PyObject_CallObject(pyTypes[N], 0);
84         if (!obj || PyErr_Occurred())
85             PyErr_Print();
86         PySide::setNextQObjectMemoryAddr(0);
87     }
88 };
89 
90 template<int N>
91 struct ElementFactory : ElementFactoryBase<N>
92 {
initElementFactory93     static void init()
94     {
95         createFuncs[N] = &ElementFactoryBase<N>::createInto;
96         ElementFactory<N-1>::init();
97     }
98 };
99 
100 template<>
101 struct  ElementFactory<0> : ElementFactoryBase<0>
102 {
initElementFactory103     static void init()
104     {
105         createFuncs[0] = &ElementFactoryBase<0>::createInto;
106     }
107 };
108 
qmlRegisterType(PyObject * pyObj,const char * uri,int versionMajor,int versionMinor,const char * qmlName)109 int PySide::qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor,
110                             int versionMinor, const char *qmlName)
111 {
112     using namespace Shiboken;
113 
114     static PyTypeObject *qobjectType = Shiboken::Conversions::getPythonTypeObject("QObject*");
115     assert(qobjectType);
116     static int nextType = 0;
117 
118     if (nextType >= PYSIDE_MAX_QML_TYPES) {
119         PyErr_Format(PyExc_TypeError, "You can only export %d custom QML types to QML.",
120                      PYSIDE_MAX_QML_TYPES);
121         return -1;
122     }
123 
124     PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
125     if (!PySequence_Contains(pyObjType->tp_mro, reinterpret_cast<PyObject *>(qobjectType))) {
126         PyErr_Format(PyExc_TypeError, "A type inherited from %s expected, got %s.",
127                      qobjectType->tp_name, pyObjType->tp_name);
128         return -1;
129     }
130 
131     const QMetaObject *metaObject = PySide::retrieveMetaObject(pyObjType);
132     Q_ASSERT(metaObject);
133 
134     QQmlPrivate::RegisterType type;
135     type.version = 0;
136 
137     // Allow registering Qt Quick items.
138     bool registered = false;
139 #ifdef PYSIDE_QML_SUPPORT
140     QuickRegisterItemFunction quickRegisterItemFunction = getQuickRegisterItemFunction();
141     if (quickRegisterItemFunction) {
142         registered = quickRegisterItemFunction(pyObj, uri, versionMajor, versionMinor,
143                                                qmlName, &type);
144     }
145 #endif
146 
147     // Register as simple QObject rather than Qt Quick item.
148     if (!registered) {
149         // Incref the type object, don't worry about decref'ing it because
150         // there's no way to unregister a QML type.
151         Py_INCREF(pyObj);
152 
153         pyTypes[nextType] = pyObj;
154 
155         // FIXME: Fix this to assign new type ids each time.
156         type.typeId = qMetaTypeId<QObject *>();
157         type.listId = qMetaTypeId<QQmlListProperty<QObject> >();
158         type.attachedPropertiesFunction = QQmlPrivate::attachedPropertiesFunc<QObject>();
159         type.attachedPropertiesMetaObject = QQmlPrivate::attachedPropertiesMetaObject<QObject>();
160 
161         type.parserStatusCast =
162                 QQmlPrivate::StaticCastSelector<QObject, QQmlParserStatus>::cast();
163         type.valueSourceCast =
164                 QQmlPrivate::StaticCastSelector<QObject, QQmlPropertyValueSource>::cast();
165         type.valueInterceptorCast =
166                 QQmlPrivate::StaticCastSelector<QObject, QQmlPropertyValueInterceptor>::cast();
167 
168         int objectSize = static_cast<int>(PySide::getSizeOfQObject(
169                                               reinterpret_cast<SbkObjectType *>(pyObj)));
170         type.objectSize = objectSize;
171         type.create = createFuncs[nextType];
172         type.uri = uri;
173         type.versionMajor = versionMajor;
174         type.versionMinor = versionMinor;
175         type.elementName = qmlName;
176 
177         type.extensionObjectCreate = 0;
178         type.extensionMetaObject = 0;
179         type.customParser = 0;
180         ++nextType;
181     }
182     type.metaObject = metaObject; // Snapshot may have changed.
183 
184     int qmlTypeId = QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
185     if (qmlTypeId == -1) {
186         PyErr_Format(PyExc_TypeError, "QML meta type registration of \"%s\" failed.",
187                      qmlName);
188     }
189     return qmlTypeId;
190 }
191 
192 extern "C"
193 {
194 
195 // This is the user data we store in the property.
196 struct QmlListProperty
197 {
198     PyTypeObject *type;
199     PyObject *append;
200     PyObject *at;
201     PyObject *clear;
202     PyObject *count;
203 };
204 
propListTpInit(PyObject * self,PyObject * args,PyObject * kwds)205 static int propListTpInit(PyObject *self, PyObject *args, PyObject *kwds)
206 {
207     static const char *kwlist[] = {"type", "append", "at", "clear", "count", 0};
208     PySideProperty *pySelf = reinterpret_cast<PySideProperty *>(self);
209     QmlListProperty *data = new QmlListProperty;
210     memset(data, 0, sizeof(QmlListProperty));
211 
212     if (!PyArg_ParseTupleAndKeywords(args, kwds,
213                                      "OO|OOO:QtQml.ListProperty", (char **) kwlist,
214                                      &data->type,
215                                      &data->append,
216                                      &data->at,
217                                      &data->clear,
218                                      &data->count)) {
219         return -1;
220     }
221     PySide::Property::setMetaCallHandler(pySelf, &propListMetaCall);
222     PySide::Property::setTypeName(pySelf, "QQmlListProperty<QObject>");
223     PySide::Property::setUserData(pySelf, data);
224 
225     return 0;
226 }
227 
propListTpFree(void * self)228 void propListTpFree(void *self)
229 {
230     auto pySelf = reinterpret_cast<PySideProperty *>(self);
231     delete reinterpret_cast<QmlListProperty *>(PySide::Property::userData(pySelf));
232     // calls base type constructor
233     Py_TYPE(pySelf)->tp_base->tp_free(self);
234 }
235 
236 static PyType_Slot PropertyListType_slots[] = {
237     {Py_tp_init, (void *)propListTpInit},
238     {Py_tp_free, (void *)propListTpFree},
239     {Py_tp_dealloc, (void *)Sbk_object_dealloc},
240     {0, 0}
241 };
242 static PyType_Spec PropertyListType_spec = {
243     "2:PySide2.QtQml.ListProperty",
244     sizeof(PySideProperty),
245     0,
246     Py_TPFLAGS_DEFAULT,
247     PropertyListType_slots,
248 };
249 
250 
PropertyListTypeF(void)251 PyTypeObject *PropertyListTypeF(void)
252 {
253     static PyTypeObject *type = nullptr;
254     if (!type) {
255         PyObject *bases = Py_BuildValue("(O)", PySidePropertyTypeF());
256         type = (PyTypeObject *)SbkType_FromSpecWithBases(&PropertyListType_spec, bases);
257         Py_XDECREF(bases);
258     }
259     return type;
260 }
261 
262 } // extern "C"
263 
264 // Implementation of QQmlListProperty<T>::AppendFunction callback
propListAppender(QQmlListProperty<QObject> * propList,QObject * item)265 void propListAppender(QQmlListProperty<QObject> *propList, QObject *item)
266 {
267     Shiboken::GilState state;
268 
269     Shiboken::AutoDecRef args(PyTuple_New(2));
270     PyTuple_SET_ITEM(args, 0, Shiboken::Conversions::pointerToPython((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], propList->object));
271     PyTuple_SET_ITEM(args, 1, Shiboken::Conversions::pointerToPython((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], item));
272 
273     auto data = reinterpret_cast<QmlListProperty *>(propList->data);
274     Shiboken::AutoDecRef retVal(PyObject_CallObject(data->append, args));
275 
276     if (PyErr_Occurred())
277         PyErr_Print();
278 }
279 
280 // Implementation of QQmlListProperty<T>::CountFunction callback
propListCount(QQmlListProperty<QObject> * propList)281 int propListCount(QQmlListProperty<QObject> *propList)
282 {
283     Shiboken::GilState state;
284 
285     Shiboken::AutoDecRef args(PyTuple_New(1));
286     PyTuple_SET_ITEM(args, 0, Shiboken::Conversions::pointerToPython((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], propList->object));
287 
288     auto data = reinterpret_cast<QmlListProperty *>(propList->data);
289     Shiboken::AutoDecRef retVal(PyObject_CallObject(data->count, args));
290 
291     // Check return type
292     int cppResult = 0;
293     PythonToCppFunc pythonToCpp = 0;
294     if (PyErr_Occurred())
295         PyErr_Print();
296     else if ((pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(Shiboken::Conversions::PrimitiveTypeConverter<int>(), retVal)))
297         pythonToCpp(retVal, &cppResult);
298     return cppResult;
299 }
300 
301 // Implementation of QQmlListProperty<T>::AtFunction callback
propListAt(QQmlListProperty<QObject> * propList,int index)302 QObject *propListAt(QQmlListProperty<QObject> *propList, int index)
303 {
304     Shiboken::GilState state;
305 
306     Shiboken::AutoDecRef args(PyTuple_New(2));
307     PyTuple_SET_ITEM(args, 0, Shiboken::Conversions::pointerToPython((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], propList->object));
308     PyTuple_SET_ITEM(args, 1, Shiboken::Conversions::copyToPython(Shiboken::Conversions::PrimitiveTypeConverter<int>(), &index));
309 
310     auto data = reinterpret_cast<QmlListProperty *>(propList->data);
311     Shiboken::AutoDecRef retVal(PyObject_CallObject(data->at, args));
312 
313     QObject *result = 0;
314     if (PyErr_Occurred())
315         PyErr_Print();
316     else if (PyType_IsSubtype(Py_TYPE(retVal), data->type))
317         Shiboken::Conversions::pythonToCppPointer((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], retVal, &result);
318     return result;
319 }
320 
321 // Implementation of QQmlListProperty<T>::ClearFunction callback
propListClear(QQmlListProperty<QObject> * propList)322 void propListClear(QQmlListProperty<QObject> * propList)
323 {
324     Shiboken::GilState state;
325 
326     Shiboken::AutoDecRef args(PyTuple_New(1));
327     PyTuple_SET_ITEM(args, 0, Shiboken::Conversions::pointerToPython((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], propList->object));
328 
329     auto data = reinterpret_cast<QmlListProperty *>(propList->data);
330     Shiboken::AutoDecRef retVal(PyObject_CallObject(data->clear, args));
331 
332     if (PyErr_Occurred())
333         PyErr_Print();
334 }
335 
336 // qt_metacall specialization for ListProperties
propListMetaCall(PySideProperty * pp,PyObject * self,QMetaObject::Call call,void ** args)337 static void propListMetaCall(PySideProperty *pp, PyObject *self, QMetaObject::Call call, void **args)
338 {
339     if (call != QMetaObject::ReadProperty)
340         return;
341 
342     auto data = reinterpret_cast<QmlListProperty *>(PySide::Property::userData(pp));
343     QObject *qobj;
344     Shiboken::Conversions::pythonToCppPointer((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], self, &qobj);
345     QQmlListProperty<QObject> declProp(qobj, data, &propListAppender, &propListCount, &propListAt, &propListClear);
346 
347     // Copy the data to the memory location requested by the meta call
348     void *v = args[0];
349     *reinterpret_cast<QQmlListProperty<QObject> *>(v) = declProp;
350 }
351 
352 // VolatileBool (volatile bool) type definition.
353 
354 static PyObject *
QtQml_VolatileBoolObject_new(PyTypeObject * type,PyObject * args,PyObject * kwds)355 QtQml_VolatileBoolObject_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
356 {
357     static const char *kwlist[] = {"x", 0};
358     PyObject *x = Py_False;
359     long ok;
360 
361     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:bool", const_cast<char **>(kwlist), &x))
362         return Q_NULLPTR;
363     ok = PyObject_IsTrue(x);
364     if (ok < 0)
365         return Q_NULLPTR;
366 
367     QtQml_VolatileBoolObject *self
368             = reinterpret_cast<QtQml_VolatileBoolObject *>(type->tp_alloc(type, 0));
369 
370     if (self != Q_NULLPTR)
371         self->flag = ok;
372 
373     return reinterpret_cast<PyObject *>(self);
374 }
375 
376 static PyObject *
QtQml_VolatileBoolObject_get(QtQml_VolatileBoolObject * self)377 QtQml_VolatileBoolObject_get(QtQml_VolatileBoolObject *self)
378 {
379     if (self->flag)
380         return Py_True;
381     return Py_False;
382 }
383 
384 static PyObject *
QtQml_VolatileBoolObject_set(QtQml_VolatileBoolObject * self,PyObject * args)385 QtQml_VolatileBoolObject_set(QtQml_VolatileBoolObject *self, PyObject *args)
386 {
387     PyObject *value = Py_False;
388     long ok;
389 
390     if (!PyArg_ParseTuple(args, "O:bool", &value)) {
391         return Q_NULLPTR;
392     }
393 
394     ok = PyObject_IsTrue(value);
395     if (ok < 0) {
396         PyErr_SetString(PyExc_TypeError, "Not a boolean value.");
397         return Q_NULLPTR;
398     }
399 
400     if (ok > 0)
401         self->flag = true;
402     else
403         self->flag = false;
404 
405     Py_RETURN_NONE;
406 }
407 
408 static PyMethodDef QtQml_VolatileBoolObject_methods[] = {
409     {"get", reinterpret_cast<PyCFunction>(QtQml_VolatileBoolObject_get), METH_NOARGS,
410      "B.get() -> Bool. Returns the value of the volatile boolean"
411     },
412     {"set", reinterpret_cast<PyCFunction>(QtQml_VolatileBoolObject_set), METH_VARARGS,
413      "B.set(a) -> None. Sets the value of the volatile boolean"
414     },
415     {Q_NULLPTR}  /* Sentinel */
416 };
417 
418 static PyObject *
QtQml_VolatileBoolObject_repr(QtQml_VolatileBoolObject * self)419 QtQml_VolatileBoolObject_repr(QtQml_VolatileBoolObject *self)
420 {
421     PyObject *s;
422 
423     if (self->flag)
424         s = PyBytes_FromFormat("%s(True)",
425                                 Py_TYPE(self)->tp_name);
426     else
427         s = PyBytes_FromFormat("%s(False)",
428                                 Py_TYPE(self)->tp_name);
429     Py_XINCREF(s);
430     return s;
431 }
432 
433 static PyObject *
QtQml_VolatileBoolObject_str(QtQml_VolatileBoolObject * self)434 QtQml_VolatileBoolObject_str(QtQml_VolatileBoolObject *self)
435 {
436     PyObject *s;
437 
438     if (self->flag)
439         s = PyBytes_FromFormat("%s(True) -> %p",
440                                 Py_TYPE(self)->tp_name, &(self->flag));
441     else
442         s = PyBytes_FromFormat("%s(False) -> %p",
443                                 Py_TYPE(self)->tp_name, &(self->flag));
444     Py_XINCREF(s);
445     return s;
446 }
447 
448 static PyType_Slot QtQml_VolatileBoolType_slots[] = {
449     {Py_tp_repr, (void *)reinterpret_cast<reprfunc>(QtQml_VolatileBoolObject_repr)},
450     {Py_tp_str, (void *)reinterpret_cast<reprfunc>(QtQml_VolatileBoolObject_str)},
451     {Py_tp_methods, (void *)QtQml_VolatileBoolObject_methods},
452     {Py_tp_new, (void *)QtQml_VolatileBoolObject_new},
453     {Py_tp_dealloc, (void *)Sbk_object_dealloc},
454     {0, 0}
455 };
456 static PyType_Spec QtQml_VolatileBoolType_spec = {
457     "2:PySide2.QtQml.VolatileBool",
458     sizeof(QtQml_VolatileBoolObject),
459     0,
460     Py_TPFLAGS_DEFAULT,
461     QtQml_VolatileBoolType_slots,
462 };
463 
464 
QtQml_VolatileBoolTypeF(void)465 PyTypeObject *QtQml_VolatileBoolTypeF(void)
466 {
467     static PyTypeObject *type = reinterpret_cast<PyTypeObject *>(
468         SbkType_FromSpec(&QtQml_VolatileBoolType_spec));
469     return type;
470 }
471 
472 static const char *PropertyList_SignatureStrings[] = {
473     "PySide2.QtQml.ListProperty(type:type,append:typing.Callable,"
474         "at:typing.Callable=None,clear:typing.Callable=None,count:typing.Callable=None)",
475     nullptr}; // Sentinel
476 
477 static const char *VolatileBool_SignatureStrings[] = {
478     "PySide2.QtQml.VolatileBool.get()->bool",
479     "PySide2.QtQml.VolatileBool.set(a:object)",
480     nullptr}; // Sentinel
481 
initQmlSupport(PyObject * module)482 void PySide::initQmlSupport(PyObject *module)
483 {
484     ElementFactory<PYSIDE_MAX_QML_TYPES - 1>::init();
485 
486     // Export QmlListProperty type
487     if (InitSignatureStrings(PropertyListTypeF(), PropertyList_SignatureStrings) < 0) {
488         PyErr_Print();
489         qWarning() << "Error initializing PropertyList type.";
490         return;
491     }
492 
493     Py_INCREF(reinterpret_cast<PyObject *>(PropertyListTypeF()));
494     PyModule_AddObject(module, PepType_GetNameStr(PropertyListTypeF()),
495                        reinterpret_cast<PyObject *>(PropertyListTypeF()));
496 
497     if (InitSignatureStrings(QtQml_VolatileBoolTypeF(), VolatileBool_SignatureStrings) < 0) {
498         PyErr_Print();
499         qWarning() << "Error initializing VolatileBool type.";
500         return;
501     }
502 
503     Py_INCREF(QtQml_VolatileBoolTypeF());
504     PyModule_AddObject(module, PepType_GetNameStr(QtQml_VolatileBoolTypeF()),
505                        reinterpret_cast<PyObject *>(QtQml_VolatileBoolTypeF()));
506 }
507