aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside2/libpyside/globalreceiver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside2/libpyside/globalreceiver.cpp')
-rw-r--r--sources/pyside2/libpyside/globalreceiver.cpp330
1 files changed, 330 insertions, 0 deletions
diff --git a/sources/pyside2/libpyside/globalreceiver.cpp b/sources/pyside2/libpyside/globalreceiver.cpp
new file mode 100644
index 000000000..e183e09ba
--- /dev/null
+++ b/sources/pyside2/libpyside/globalreceiver.cpp
@@ -0,0 +1,330 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of PySide2.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "globalreceiver.h"
+#include "dynamicqmetaobject_p.h"
+#include "pysideweakref.h"
+
+#include <QMetaMethod>
+#include <QDebug>
+#include <QEvent>
+#include <QLinkedList>
+#include <autodecref.h>
+#include <sbkconverter.h>
+#include <gilstate.h>
+
+#include "typeresolver.h"
+#include "signalmanager.h"
+
+#define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)"
+
+namespace PySide
+{
+class DynamicSlotData
+{
+ public:
+ DynamicSlotData(int id, PyObject* callback, GlobalReceiver* parent);
+ void addRef(const QObject* o);
+ void decRef(const QObject* o);
+ void clear();
+ int hasRefTo(const QObject* o) const;
+ int refCount() const;
+ int id() const;
+ PyObject* call(PyObject* args);
+ ~DynamicSlotData();
+ static void onCallbackDestroyed(void* data);
+
+ private:
+ int m_id;
+ bool m_isMethod;
+ PyObject* m_callback;
+ PyObject* m_pythonSelf;
+ PyObject* m_pyClass;
+ PyObject* m_weakRef;
+ GlobalReceiver* m_parent;
+ QLinkedList<const QObject*> m_refs;
+};
+
+}
+
+using namespace PySide;
+
+DynamicSlotData::DynamicSlotData(int id, PyObject* callback, GlobalReceiver* parent)
+ : m_id(id), m_pythonSelf(0), m_pyClass(0), m_weakRef(0), m_parent(parent)
+{
+ Shiboken::GilState gil;
+
+ m_isMethod = PyMethod_Check(callback);
+ if (m_isMethod) {
+ //Can not store calback pointe because this will be destroyed at the end of the scope
+ //To avoid increment intance reference keep the callback information
+ m_callback = PyMethod_GET_FUNCTION(callback);
+#ifdef IS_PY3K
+ m_pyClass = 0;
+#else
+ m_pyClass = PyMethod_GET_CLASS(callback);
+#endif
+
+ m_pythonSelf = PyMethod_GET_SELF(callback);
+
+ //monitor class from method lifetime
+ m_weakRef = WeakRef::create(m_pythonSelf, DynamicSlotData::onCallbackDestroyed, this);
+ } else {
+ m_callback = callback;
+ Py_INCREF(m_callback);
+ }
+}
+
+PyObject* DynamicSlotData::call(PyObject* args)
+{
+ PyObject* callback = m_callback;
+
+ //create a callback based on method data
+ Shiboken::GilState gil;
+ if (m_isMethod)
+#ifdef IS_PY3K
+ callback = PyMethod_New(callback, m_pythonSelf);
+#else
+ callback = PyMethod_New(callback, m_pythonSelf, m_pyClass);
+#endif
+
+ PyObject* result = PyObject_CallObject(callback, args);
+
+ if (m_isMethod)
+ Py_DECREF(callback);
+
+ return result;
+}
+
+void DynamicSlotData::addRef(const QObject *o)
+{
+ m_refs.append(o);
+}
+
+void DynamicSlotData::decRef(const QObject *o)
+{
+ m_refs.removeOne(o);
+}
+
+int DynamicSlotData::refCount() const
+{
+ return m_refs.size();
+}
+
+int DynamicSlotData::id() const
+{
+ return m_id;
+}
+
+int DynamicSlotData::hasRefTo(const QObject *o) const
+{
+ return m_refs.count(o);
+}
+
+void DynamicSlotData::clear()
+{
+ Shiboken::GilState gil;
+ Py_XDECREF(m_weakRef);
+ m_weakRef = 0;
+ m_refs.clear();
+}
+
+DynamicSlotData::~DynamicSlotData()
+{
+ Shiboken::GilState gil;
+ clear();
+ if (!m_isMethod)
+ Py_DECREF(m_callback);
+}
+
+void DynamicSlotData::onCallbackDestroyed(void *data)
+{
+ Shiboken::GilState gil;
+ DynamicSlotData* self = reinterpret_cast<DynamicSlotData*>(data);
+
+ //Disconnect all sources
+ QMetaMethod m = self->m_parent->metaObject()->method(self->m_id);
+ QByteArray methodName = QByteArray::number(m.methodType()).append(m.methodSignature());
+ QLinkedList<const QObject*> sources = self->m_refs;
+ foreach(const QObject* src, sources)
+ const_cast<QObject*>(src)->disconnect(self->m_parent, methodName);
+ self->m_weakRef = 0;
+}
+
+GlobalReceiver::GlobalReceiver()
+ : m_metaObject(GLOBAL_RECEIVER_CLASS_NAME, &QObject::staticMetaObject)
+{
+ //slot used to be notifyed of object destrouction
+ m_metaObject.addSlot(RECEIVER_DESTROYED_SLOT_NAME);
+ m_metaObject.update();
+ setObjectName(QLatin1String("GLOBAL RECEIVER"));
+}
+
+GlobalReceiver::~GlobalReceiver()
+{
+ while(!m_slotReceivers.empty()) {
+ DynamicSlotData* data = m_slotReceivers.take(m_slotReceivers.begin().key());
+ data->clear();
+ delete data;
+ }
+}
+
+void GlobalReceiver::connectNotify(QObject* source, int slotId)
+{
+ if (m_slotReceivers.contains(slotId)) {
+ DynamicSlotData* data = m_slotReceivers[slotId];
+ if (!data->hasRefTo(source))
+ QObject::connect(source, SIGNAL(destroyed(QObject*)), this, "1" RECEIVER_DESTROYED_SLOT_NAME);
+ data->addRef(source);
+ }
+}
+
+void GlobalReceiver::disconnectNotify(QObject* source, int slotId)
+{
+ if (m_slotReceivers.contains(slotId)) {
+ DynamicSlotData *data = m_slotReceivers[slotId];
+ data->decRef(source);
+ if (data->refCount() == 0)
+ removeSlot(slotId);
+
+ if (!hasConnectionWith(source))
+ QObject::disconnect(source, SIGNAL(destroyed(QObject*)), this, "1" RECEIVER_DESTROYED_SLOT_NAME);
+ }
+}
+
+const QMetaObject* GlobalReceiver::metaObject() const
+{
+ return m_metaObject.update();
+}
+
+int GlobalReceiver::addSlot(const char* slot, PyObject* callback)
+{
+ int slotId = m_metaObject.addSlot(slot);
+ if (!m_slotReceivers.contains(slotId))
+ m_slotReceivers[slotId] = new DynamicSlotData(slotId, callback, this);
+
+ bool isShortCircuit = true;
+ for (int i = 0; slot[i]; ++i) {
+ if (slot[i] == '(') {
+ isShortCircuit = false;
+ break;
+ }
+ }
+
+ if (isShortCircuit)
+ m_shortCircuitSlots << slotId;
+
+ Q_ASSERT(slotId >= QObject::staticMetaObject.methodCount());
+ return slotId;
+}
+
+void GlobalReceiver::removeSlot(int slotId)
+{
+ if (m_slotReceivers.contains(slotId)) {
+ delete m_slotReceivers.take(slotId);
+ m_metaObject.removeSlot(slotId);
+ m_shortCircuitSlots.remove(slotId);
+ }
+}
+
+bool GlobalReceiver::hasConnectionWith(const QObject *object)
+{
+ QHash<int, DynamicSlotData*>::iterator i = m_slotReceivers.begin();
+ while(i != m_slotReceivers.end()) {
+ if (i.value()->hasRefTo(object)) {
+ return true;
+ }
+ i++;
+ }
+ return false;
+}
+
+int GlobalReceiver::qt_metacall(QMetaObject::Call call, int id, void** args)
+{
+ Q_ASSERT(call == QMetaObject::InvokeMetaMethod);
+ Q_ASSERT(id >= QObject::staticMetaObject.methodCount());
+ QMetaMethod slot = metaObject()->method(id);
+ Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
+
+ if (strcmp(slot.methodSignature(), RECEIVER_DESTROYED_SLOT_NAME) == 0) {
+ QObject *arg = *(QObject**)args[1];
+
+ //avoid hash changes during the destruction
+ QHash<int, DynamicSlotData*> copy = m_slotReceivers;
+ QHash<int, DynamicSlotData*>::iterator i = copy.begin();
+ while(i != copy.end()) {
+ //Remove all refs
+ int refs = i.value()->hasRefTo(arg);
+ while(refs) {
+ disconnectNotify(arg, i.key());
+ refs--;
+ }
+ i++;
+ }
+ return -1;
+ }
+
+ DynamicSlotData* data = m_slotReceivers.value(id);
+ if (!data) {
+ qWarning() << "Unknown global slot, id:" << id;
+ return -1;
+ }
+
+ Shiboken::GilState gil;
+ PyObject* retval = 0;
+ if (m_shortCircuitSlots.contains(id)) {
+ retval = data->call(reinterpret_cast<PyObject*>(args[1]));
+ } else {
+ QList<QByteArray> paramTypes = slot.parameterTypes();
+ Shiboken::AutoDecRef preparedArgs(PyTuple_New(paramTypes.count()));
+ for (int i = 0, max = paramTypes.count(); i < max; ++i) {
+ const QByteArray& paramType = paramTypes[i];
+ Shiboken::Conversions::SpecificConverter converter(paramType.constData());
+ PyTuple_SET_ITEM(preparedArgs.object(), i, converter.toPython(args[i+1]));
+ }
+ retval = data->call(preparedArgs);
+ }
+
+ if (!retval)
+ PyErr_Print();
+ else
+ Py_DECREF(retval);
+
+ return -1;
+}