/************************************************************************ * * Copyright 2010-2012 Jakob Leben (jakob.leben@gmail.com) * * This file is part of SuperCollider Qt GUI. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ************************************************************************/ #include "primitives.h" #include "../QObjectProxy.h" #include "../QcObjectFactory.h" #include "../QcApplication.h" #include "../Common.h" #include "../type_codec.hpp" #include "../metatype.hpp" #include "../painting.h" #include #include #include #include #include #include #include #if defined _WIN32 # include "SC_Win32Utils.h" #elif defined __FreeBSD__ # include #else # include #endif #define IS_OBJECT_NIL(a) IsNil(slotRawObject(a)->slots) #define QOBJECT_FROM_SLOT(s) ((QObjectProxy*)slotRawPtr(slotRawObject(s)->slots)) #define CLASS_NAME(slot) slotRawSymbol(&slotRawObject(slot)->classptr->name)->name namespace QtCollider { int QObject_Finalize(struct VMGlobals*, struct PyrObject*); QC_LANG_PRIMITIVE(QObject_New, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) { if (NotSym(a + 0)) return errWrongType; PyrObject* scObject = slotRawObject(r); QString qtClassName = QString(slotRawSymbol(a + 0)->name); qcSCObjectDebugMsg(1, scObject, QStringLiteral("CREATE: %2").arg(qtClassName)); if (!QcApplication::compareThread()) return QtCollider::wrongThreadError(); QcAbstractFactory* f = QtCollider::factories().value(qtClassName); if (!f) { qcErrorMsg(QStringLiteral("Factory for class '%1' not found!").arg(qtClassName)); return errFailed; } QObjectProxy* proxy = 0; MetaValue args[10]; PyrSlot* argSlot = a + 1; int argc; if (isKindOfSlot(argSlot, class_array)) { PyrObject* array = slotRawObject(argSlot); argSlot = array->slots; argc = array->size; } else { argc = 1; } for (int i = 0; i < argc && i < 10; ++i) { PyrSlot* slot = argSlot + i; MetaType* type = MetaType::find(slot); if (type) { void* mem = alloca(type->size()); Q_ASSERT(mem); args[i].read(mem, type, slot); } } proxy = f->newInstance(scObject, args); if (!proxy) return errFailed; SetPtr(scObject->slots, proxy); InstallFinalizer(g, scObject, 1, QObject_Finalize); return errNone; } QC_LANG_PRIMITIVE(QObject_Destroy, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) { qcSCObjectDebugMsg(1, slotRawObject(r), "DESTROY"); QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); if (!proxy->compareThread()) return QtCollider::wrongThreadError(); // Post the destruction event asynchronously; DestroyEvent* e = new DestroyEvent(QObjectProxy::DestroyObject); QApplication::postEvent(proxy, e); return errNone; } int QObject_Finalize(struct VMGlobals*, struct PyrObject* obj) { qcSCObjectDebugMsg(1, obj, "FINALIZE"); QObjectProxy* proxy = (QObjectProxy*)slotRawPtr(obj->slots); // Invalidate proxy's SC object pointer directly. // Note that it is protected by language mutex; proxy->finalize(); // Post the destruction event asynchronously; DestroyEvent* e = new DestroyEvent(QObjectProxy::DestroyProxyAndObject); QApplication::postEvent(proxy, e); return errNone; } QC_LANG_PRIMITIVE(QObject_SetParent, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) { QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); QObjectProxy* parent = QtCollider::get(a); if (!parent) return errWrongType; qcSCObjectDebugMsg(1, slotRawObject(r), "SET PARENT"); if (!proxy->compareThread()) return QtCollider::wrongThreadError(); bool ok = proxy->setParent(parent); return ok ? errNone : errFailed; } static void qcGetProperties(const QMetaObject* mo, PyrSlot* r, VMGlobals* g) { int count = mo->propertyCount(); PyrObject* array = newPyrArray(g->gc, count, 0, true); SetObject(r, array); PyrSlot* s = array->slots; for (int i = 0; i < count; ++i, ++s) { SetSymbol(s, getsym(mo->property(i).name())); array->size++; } } static void qcGetMethods(const QMetaObject* mo, bool getPlain, bool getSignals, bool getSlots, PyrSlot* r, VMGlobals* g) { int count = mo->methodCount(); PyrObject* array = newPyrArray(g->gc, count, 0, true); SetObject(r, array); PyrSlot* s = array->slots; for (int i = 0; i < count; ++i) { QMetaMethod method = mo->method(i); switch (method.methodType()) { case QMetaMethod::Method: if (!getPlain || (method.access() != QMetaMethod::Public)) continue; break; case QMetaMethod::Signal: if (!getSignals) continue; break; case QMetaMethod::Slot: if (!getSlots || (method.access() != QMetaMethod::Public)) continue; break; default: continue; } QtCollider::set(s, QString::fromLatin1(method.methodSignature())); array->size++; g->gc->GCWrite(array, s); ++s; } } QC_LANG_PRIMITIVE(QMetaObject_Properties, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) { if (!QcApplication::compareThread()) return QtCollider::wrongThreadError(); PyrSlot* sClassName = slotRawObject(r)->slots + 0; if (NotSym(sClassName)) return errWrongType; QString className(slotRawSymbol(sClassName)->name); QcAbstractFactory* f = QtCollider::factories().value(className); if (!f) { qcErrorMsg(QStringLiteral("Factory for class '%1' not found!").arg(className)); return errFailed; } const QMetaObject* mo = f->metaObject(); qcGetProperties(mo, r, g); return errNone; } QC_LANG_PRIMITIVE(QMetaObject_Methods, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) { if (!QcApplication::compareThread()) return QtCollider::wrongThreadError(); PyrSlot* sClassName = slotRawObject(r)->slots + 0; if (NotSym(sClassName)) return errWrongType; QString className(slotRawSymbol(sClassName)->name); QcAbstractFactory* f = QtCollider::factories().value(className); if (!f) { qcErrorMsg(QStringLiteral("Factory for class '%1' not found!").arg(className)); return errFailed; } bool getPlain = IsTrue(a + 0); bool getSignals = IsTrue(a + 1); bool getSlots = IsTrue(a + 2); const QMetaObject* mo = f->metaObject(); qcGetMethods(mo, getPlain, getSignals, getSlots, r, g); return errNone; } QC_LANG_PRIMITIVE(QObject_GetProperties, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) { QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); if (!proxy->compareThread()) return QtCollider::wrongThreadError(); QObject* obj = proxy->object(); if (!obj) { SetNil(r); return errNone; } const QMetaObject* mo = obj->metaObject(); qcGetProperties(mo, r, g); return errNone; } QC_LANG_PRIMITIVE(QObject_GetMethods, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) { QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); if (!proxy->compareThread()) return QtCollider::wrongThreadError(); QObject* obj = proxy->object(); if (!obj) { SetNil(r); return errNone; } bool getPlain = IsTrue(a + 0); bool getSignals = IsTrue(a + 1); bool getSlots = IsTrue(a + 2); const QMetaObject* mo = obj->metaObject(); qcGetMethods(mo, getPlain, getSignals, getSlots, r, g); return errNone; } QC_LANG_PRIMITIVE(QObject_SetProperty, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) { QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); if (NotSym(a)) return errWrongType; PyrSymbol* property = slotRawSymbol(a); bool sync = IsTrue(a + 2); qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("SET: %1").arg(property->name)); if (!proxy->compareThread()) return QtCollider::wrongThreadError(); QVariant val = QtCollider::get(a + 1); if (sync && !QtCollider::isPaintingObject(proxy)) { proxy->setProperty(property->name, val); } else { SetPropertyEvent* e = new SetPropertyEvent(); e->property = property; e->value = val; QApplication::postEvent(proxy, e); } return errNone; } QC_LANG_PRIMITIVE(QObject_GetProperty, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) { QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); if (NotSym(a)) return errWrongType; PyrSymbol* symProp = slotRawSymbol(a); qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("GET: %1").arg(symProp->name)); if (!proxy->compareThread()) return QtCollider::wrongThreadError(); QVariant val = proxy->property(symProp->name); if (!val.isValid()) { qcDebugMsg(1, QStringLiteral("WARNING: Invalid property '%1'").arg(symProp->name)); SetNil(r); return errNone; } if (QtCollider::set(r, val)) return errNone; else return errFailed; } QC_LANG_PRIMITIVE(QObject_SetEventHandler, 4, PyrSlot* r, PyrSlot* a, VMGlobals* g) { QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); if (NotInt(a + 0) || NotSym(a + 1)) return errWrongType; int eventType = QtCollider::get(a + 0); PyrSymbol* method = 0; slotSymbolVal(a + 1, &method); Synchronicity sync = IsTrue(a + 2) ? Synchronous : Asynchronous; bool enabled = IsTrue(a + 3); qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("SET EVENT HANDLER: type %1 -> %2 [%3, %4]") .arg(eventType) .arg(method->name) .arg(sync == Synchronous ? "SYNC" : "ASYNC") .arg(enabled ? "on" : "off")); if (!proxy->compareThread()) return QtCollider::wrongThreadError(); bool ok = proxy->setEventHandler(eventType, method, sync, enabled); return ok ? errNone : errFailed; } QC_LANG_PRIMITIVE(QObject_SetEventHandlerEnabled, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) { if (NotInt(a + 0)) return errWrongType; bool enabled = IsTrue(a + 1); if (!enabled && !IsFalse(a + 1)) return errWrongType; int type = QtCollider::get(a + 0); qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("SET EVENT HANDLER STATE: type %1 = %2").arg(type).arg(enabled ? "on" : "off")); QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); if (!proxy->compareThread()) return QtCollider::wrongThreadError(); bool ok = proxy->setEventHandlerEnabled(type, enabled); return ok ? errNone : errFailed; } QC_LANG_PRIMITIVE(QObject_ConnectMethod, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) { if (NotSym(a + 0) || NotSym(a + 1)) return errWrongType; PyrSymbol* signal = slotRawSymbol(a + 0); PyrSymbol* handler = slotRawSymbol(a + 1); Qt::ConnectionType ctype = IsTrue(a + 2) ? Qt::DirectConnection : Qt::QueuedConnection; qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("CONNECT METHOD: %1 -> %2 [%3]") .arg(signal->name) .arg(handler->name) .arg(IsTrue(a + 2) ? "SYNC" : "ASYNC")); QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); if (!proxy->compareThread()) return QtCollider::wrongThreadError(); bool ok = proxy->connectMethod(signal->name, handler, ctype); return ok ? errNone : errFailed; } QC_LANG_PRIMITIVE(QObject_DisconnectMethod, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) { if (NotSym(a + 0) || NotSym(a + 1)) return errWrongType; PyrSymbol* signal = slotRawSymbol(a + 0); PyrSymbol* handler = slotRawSymbol(a + 1); qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("DISCONNECT METHOD: %1 -> %2").arg(signal->name).arg(handler->name)); QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); if (!proxy->compareThread()) return QtCollider::wrongThreadError(); bool ok = proxy->disconnectMethod(signal->name, handler); return ok ? errNone : errFailed; } QC_LANG_PRIMITIVE(QObject_ConnectObject, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) { if (NotSym(a + 0) || NotObj(a + 1)) return errWrongType; PyrSymbol* signal = slotRawSymbol(a + 0); PyrObject* handlerObj = slotRawObject(a + 1); Qt::ConnectionType ctype = IsTrue(a + 2) ? Qt::DirectConnection : Qt::QueuedConnection; qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("CONNECT OBJECT: %1 -> %2 [%3]") .arg(signal->name) .arg(slotRawSymbol(&handlerObj->classptr->name)->name) .arg(IsTrue(a + 2) ? "SYNC" : "ASYNC")); QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); if (!proxy->compareThread()) return QtCollider::wrongThreadError(); bool ok = proxy->connectObject(signal->name, handlerObj, ctype); return ok ? errNone : errFailed; } QC_LANG_PRIMITIVE(QObject_DisconnectObject, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) { if (NotSym(a + 0) || NotObj(a + 1)) return errWrongType; PyrSymbol* signal = slotRawSymbol(a + 0); PyrObject* handlerObj = slotRawObject(a + 1); qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("DISCONNECT OBJECT: %1").arg(signal->name)); QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); if (!proxy->compareThread()) return QtCollider::wrongThreadError(); bool ok = proxy->disconnectObject(signal->name, handlerObj); return ok ? errNone : errFailed; } QC_LANG_PRIMITIVE(QObject_ConnectSlot, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) { // Args: signal, receiver, slot if (!isKindOfSlot(a + 1, SC_CLASS(QObject)) || NotSym(a + 0) || NotSym(a + 2)) return errWrongType; PyrSymbol* symSig = slotRawSymbol(a + 0); PyrSymbol* symSlot = slotRawSymbol(a + 2); QObjectProxy* sndProxy = QOBJECT_FROM_SLOT(r); QObjectProxy* rcvProxy = QOBJECT_FROM_SLOT(a + 1); qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("CONNECT TO SLOT: %1 -> %2").arg(symSig->name).arg(symSlot->name)); QString strSig = QStringLiteral("2") + symSig->name; QString strSlot = QStringLiteral("1") + symSlot->name; sndProxy->lock(); rcvProxy->lock(); bool ok; if (!sndProxy->object() || !rcvProxy->object()) { ok = true; } else { ok = QObject::connect(sndProxy->object(), strSig.toStdString().c_str(), rcvProxy->object(), strSlot.toStdString().c_str()); } sndProxy->unlock(); rcvProxy->unlock(); if (!ok) { qcErrorMsg(QStringLiteral("Failed to connect %1::%2 to %3::%4!\n") .arg(sndProxy->scClassName()) .arg(symSig->name) .arg(rcvProxy->scClassName()) .arg(symSlot->name)); return errFailed; } return errNone; } QC_LANG_PRIMITIVE(QObject_InvokeMethod, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) { if (NotSym(a + 0)) return errWrongType; PyrSymbol* method = slotRawSymbol(a + 0); PyrSlot* methodArgs = a + 1; bool sync = !IsFalse(a + 2); qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("INVOKE: '%1' [%2]").arg(method->name).arg(sync ? "SYNC" : "ASYNC")); QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); PyrSlot* retSlot; Qt::ConnectionType cType; if (sync) { if (!proxy->compareThread()) return QtCollider::wrongThreadError(); retSlot = r; cType = Qt::DirectConnection; } else { retSlot = 0; cType = Qt::QueuedConnection; } bool ok = proxy->invokeMethod(method->name, retSlot, methodArgs, cType); return ok ? errNone : errFailed; } QC_LANG_PRIMITIVE(QObject_IsValid, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) { QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); bool needLock = !proxy->compareThread(); if (needLock) proxy->lock(); bool hasObject = proxy->object() != 0; if (needLock) proxy->unlock(); SetBool(r, hasObject); return errNone; } QC_LANG_PRIMITIVE(QObject_GetChildren, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) { if (NotSym(a) && NotNil(a)) return errWrongType; QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); PyrSymbol* className = IsSym(a) ? slotRawSymbol(a) : 0; qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("GET CHILDREN: of class '%1'").arg(className ? className->name : "QObject")); if (!proxy->compareThread()) return QtCollider::wrongThreadError(); QList children = proxy->children(className); int count = children.count(); PyrObject* array = newPyrArray(g->gc, count, 0, true); SetObject(r, array); PyrSlot* s = array->slots; Q_FOREACH (PyrObject* obj, children) { SetObject(s, obj); ++array->size; g->gc->GCWrite(array, s); ++s; } return errNone; } QC_LANG_PRIMITIVE(QObject_GetParent, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) { if (NotSym(a) && NotNil(a)) return errWrongType; QObjectProxy* proxy = QOBJECT_FROM_SLOT(r); PyrSymbol* className = IsSym(a) ? slotRawSymbol(a) : 0; qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("GET PARENT")); if (!proxy->compareThread()) return QtCollider::wrongThreadError(); PyrObject* parent = proxy->parent(className); if (parent) SetObject(r, parent); else SetNil(r); return errNone; } void defineQObjectPrimitives() { LangPrimitiveDefiner definer; definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); definer.define(); } } // namespace QtCollider