hsqml 0.3.1.1 → 0.3.2.0
raw patch · 24 files changed
+1410/−277 lines, 24 filesdep ~text
Dependency ranges changed: text
Files
- CHANGELOG +9/−0
- cbits/Canvas.cpp +465/−26
- cbits/Canvas.h +131/−6
- cbits/CanvasImpl.cpp +0/−56
- cbits/CanvasImpl.h +0/−41
- cbits/Manager.cpp +3/−1
- cbits/Object.cpp +90/−7
- cbits/Object.h +31/−1
- cbits/hsqml.h +62/−0
- hsqml.cabal +9/−3
- src/Graphics/QML.hs +3/−1
- src/Graphics/QML/Canvas.hs +157/−0
- src/Graphics/QML/Engine.hs +10/−13
- src/Graphics/QML/Internal/BindCanvas.chs +88/−0
- src/Graphics/QML/Internal/BindCore.chs +0/−6
- src/Graphics/QML/Internal/BindObj.chs +46/−2
- src/Graphics/QML/Internal/BindPrim.chs +2/−2
- src/Graphics/QML/Internal/Marshal.hs +26/−10
- src/Graphics/QML/Internal/MetaObj.hs +17/−20
- src/Graphics/QML/Internal/Objects.hs +78/−0
- src/Graphics/QML/Internal/Types.hs +0/−1
- src/Graphics/QML/Marshal.hs +59/−3
- src/Graphics/QML/Objects.hs +4/−78
- src/Graphics/QML/Objects/Weak.hs +120/−0
CHANGELOG view
@@ -1,5 +1,14 @@ HsQML - Release History +release-0.3.2.0 - 2014.11.13++ * Added OpenGL canvas support.+ * Added weak references and object finalisers.+ * Added FactoryPool abstraction.+ * Added To-only custom marshallers.+ * Added Ignored type.+ * Relaxed Cabal dependency constraint on 'text'.+ release-0.3.1.1 - 2014.07.31 * Fixed crash when storing Haskell objects in QML variants.
cbits/Canvas.cpp view
@@ -1,29 +1,325 @@ #include "Canvas.h"-#include "CanvasImpl.h"+#include "Manager.h" -HsQMLGLDelegate::HsQMLGLDelegate(- HsQMLGLPaintCb paintCb)- : mImpl(new HsQMLGLDelegateImpl(paintCb))+#include <QtCore/qmath.h>+#include <QtQuick/QSGSimpleTextureNode>+#include <QtQuick/QSGTexture>+#include <QtQuick/QSGTransformNode>+#include <QtQuick/QQuickWindow>++HsQMLGLCallbacks::HsQMLGLCallbacks(+ HsQMLGLSetupCb setupCb, HsQMLGLCleanupCb cleanupCb,+ HsQMLGLSyncCb syncCb, HsQMLGLPaintCb paintCb)+ : mSetupCb(setupCb)+ , mCleanupCb(cleanupCb)+ , mSyncCb(syncCb)+ , mPaintCb(paintCb)+{}++HsQMLGLCallbacks::~HsQMLGLCallbacks() {+ gManager->freeFun(reinterpret_cast<HsFunPtr>(mSyncCb));+ gManager->freeFun(reinterpret_cast<HsFunPtr>(mPaintCb)); } +HsQMLGLDelegateImpl::HsQMLGLDelegateImpl(HsQMLGLMakeCallbacksCb makeContextCb)+ : mMakeCallbacksCb(makeContextCb)+{}++HsQMLGLDelegateImpl::~HsQMLGLDelegateImpl()+{+ gManager->freeFun(reinterpret_cast<HsFunPtr>(mMakeCallbacksCb));+}++HsQMLGLDelegate::HsQMLGLDelegate()+{+}+ HsQMLGLDelegate::~HsQMLGLDelegate() { } +void HsQMLGLDelegate::setup(HsQMLGLMakeCallbacksCb makeContextCb)+{+ mImpl = new HsQMLGLDelegateImpl(makeContextCb);+}++HsQMLGLDelegate::CallbacksRef HsQMLGLDelegate::makeCallbacks()+{+ CallbacksRef dataPtr;+ if (mImpl) {+ HsQMLGLSetupCb setupCb;+ HsQMLGLCleanupCb cleanupCb;+ HsQMLGLSyncCb syncCb;+ HsQMLGLPaintCb paintCb;+ mImpl->mMakeCallbacksCb(&setupCb, &cleanupCb, &syncCb, &paintCb);+ dataPtr = new HsQMLGLCallbacks(setupCb, cleanupCb, syncCb, paintCb);+ }+ return dataPtr;+}++HsQMLWindowInfoImpl::HsQMLWindowInfoImpl(QQuickWindow* win)+ : mWin(win)+ , mBelowCount(0)+ , mBelowClear(false)+{}++HsQMLWindowInfo::HsQMLWindowInfo()+{}++HsQMLWindowInfo HsQMLWindowInfo::getWindowInfo(QQuickWindow* win)+{+ const char* propName = "_hsqml_window_info";+ HsQMLWindowInfo info = win->property(propName).value<HsQMLWindowInfo>();+ if (!info.mImpl) {+ info.mImpl = new HsQMLWindowInfoImpl(win);+ win->setProperty(propName, QVariant::fromValue(info));+ }+ return info;+}++void HsQMLWindowInfo::addBelow()+{+ Q_ASSERT(mImpl);+ if (0 == mImpl->mBelowCount++) {+ mImpl->mWin->setClearBeforeRendering(false);+ }+}++void HsQMLWindowInfo::removeBelow()+{+ Q_ASSERT(mImpl);+ if (0 == --mImpl->mBelowCount) {+ mImpl->mWin->setClearBeforeRendering(true);+ }+}++bool HsQMLWindowInfo::needsBelowClear()+{+ Q_ASSERT(mImpl);+ if (mImpl->mBelowCount && !mImpl->mBelowClear) {+ mImpl->mBelowClear = true;+ return true;+ }+ return false;+}++void HsQMLWindowInfo::endFrame()+{+ Q_ASSERT(mImpl);+ mImpl->mBelowClear = false;+}++HsQMLCanvasBackEnd::HsQMLCanvasBackEnd(+ QQuickWindow* win,+ const HsQMLGLDelegate::CallbacksRef& cbs,+ HsQMLCanvas::DisplayMode mode)+ : mWindow(win)+ , mWinInfo(HsQMLWindowInfo::getWindowInfo(win))+ , mGLCallbacks(cbs)+ , mGL(NULL)+ , mStatus(HsQMLCanvas::Okay)+ , mDisplayMode(mode)+ , mItemWidth(0)+ , mItemHeight(0)+ , mTransformNode(NULL)+ , mCanvasWidth(0)+ , mCanvasHeight(0)+{+ if (HsQMLCanvas::Above == mDisplayMode) {+ QObject::connect(+ win, SIGNAL(afterRendering()),+ this, SLOT(doRendering()));+ }+ else {+ QObject::connect(+ win, SIGNAL(beforeRendering()),+ this, SLOT(doRendering()));++ if (HsQMLCanvas::Below == mDisplayMode) {+ mWinInfo.addBelow();+ QObject::connect(+ win, SIGNAL(frameSwapped()),+ this, SLOT(doEndFrame()));+ }+ }+}++HsQMLCanvasBackEnd::~HsQMLCanvasBackEnd()+{+ if (HsQMLCanvas::Below == mDisplayMode) {+ mWinInfo.removeBelow();+ }+}++void HsQMLCanvasBackEnd::setTransformNode(+ QSGTransformNode* tn, qreal w, qreal h)+{+ mTransformNode = tn;+ mItemWidth = w;+ mItemHeight = h;+}++QSGTexture* HsQMLCanvasBackEnd::updateFBO(qreal w, qreal h)+{+ if (HsQMLCanvas::Inline == mDisplayMode) {+ if (!mFBO || w != mCanvasWidth || h != mCanvasHeight) {+ mCanvasWidth = w;+ mCanvasHeight = h;+ QSize dims(qCeil(mCanvasWidth), qCeil(mCanvasHeight));+ mFBO.reset(new QOpenGLFramebufferObject(+ dims, QOpenGLFramebufferObject::Depth));+ mTexture.reset(mWindow->createTextureFromId(+ mFBO->texture(), dims, QQuickWindow::TextureHasAlphaChannel));+ }+ }+ else {+ mTexture.reset();+ mFBO.reset();+ }+ return mTexture.data();+}++HsQMLCanvas::Status HsQMLCanvasBackEnd::status() const+{+ return mStatus;+}++void HsQMLCanvasBackEnd::setStatus(HsQMLCanvas::Status status)+{+ bool change = mStatus != status;+ mStatus = status;+ if (change) {+ statusChanged(mStatus);+ }+}++void HsQMLCanvasBackEnd::doRendering()+{+ if (!mGL) {+ mGL = mWindow->openglContext();+ QObject::connect(+ mGL, SIGNAL(aboutToBeDestroyed()), this, SLOT(doCleanup()));+ HsQMLGLCanvasType ctype;+ QSurfaceFormat format = mGL->format();+ switch (format.renderableType()) {+ case QSurfaceFormat::OpenGL: ctype = HSQML_GL_DESKTOP; break;+ case QSurfaceFormat::OpenGLES: ctype = HSQML_GL_ES; break;+ default: setStatus(HsQMLCanvas::BadConfig); return;+ }+ mGLViewportFn = reinterpret_cast<GLViewportFn>(+ mGL->getProcAddress("glViewport"));+ mGLClearColorFn = reinterpret_cast<GLClearColorFn>(+ mGL->getProcAddress("glClearColor"));+ mGLClearFn = reinterpret_cast<GLClearFn>(+ mGL->getProcAddress("glClear"));+ if (!mGLViewportFn || !mGLClearColorFn || !mGLClearFn) {+ setStatus(HsQMLCanvas::BadProcs);+ return;+ }+ mGLCallbacks->mSetupCb(+ ctype, format.majorVersion(), format.minorVersion());+ }++ // Reset OpenGL state before rendering+#if QT_VERSION >= 0x050200+ mWindow->resetOpenGLState();+#else+#warning Resetting OpenGL state requires Qt 5.2 or later+#endif++ // Clear window if painting below the scenegraph+ if (mWinInfo.needsBelowClear()) {+ QColor bg = mWindow->color();+ mGLClearColorFn(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF());+ mGLClearFn(GL_COLOR_BUFFER_BIT);+ }++ // Setup prior to paint callback+ QMatrix4x4 matrix;+ bool inlineMode = HsQMLCanvas::Inline == mDisplayMode;+ if (inlineMode) {+ if (!mFBO->bind()) {+ setStatus(HsQMLCanvas::BadBind);+ return;+ }+ mGLViewportFn(0, 0, qCeil(mCanvasWidth), qCeil(mCanvasHeight));++ // Clear FBO to transparent+ mGLClearColorFn(0, 0, 0, 0);+ mGLClearFn(GL_COLOR_BUFFER_BIT);+ }+ else {+ // Calculate matrix for non-inline display modes+ QMatrix4x4 smatrix;+ QSGNode* node = mTransformNode;+ while (node) {+ if (QSGNode::TransformNodeType == node->type()) {+ QSGTransformNode* tnode = static_cast<QSGTransformNode*>(node);+ smatrix = tnode->matrix() * smatrix;+ }+ node = node->parent();+ }+ matrix.translate(-1, 1);+ matrix.scale(2.0f/mWindow->width(), -2.0f/mWindow->height());+ matrix *= smatrix;+ matrix.scale(mItemWidth/2.0f, mItemHeight/2.0f);+ matrix.translate(1, 1);++ mGLViewportFn(0, 0, mWindow->width(), mWindow->height());+ }++ setStatus(HsQMLCanvas::Okay);+ mGLCallbacks->mPaintCb(matrix.data(), mItemWidth, mItemHeight);++ if (inlineMode) {+ mFBO->release();+ }+}++void HsQMLCanvasBackEnd::doEndFrame()+{+ mWinInfo.endFrame();+}++void HsQMLCanvasBackEnd::doCleanup()+{+ if (mGL) {+ mGL->makeCurrent(mWindow);+ mGL = NULL;++ mTexture.reset();+ mFBO.reset();++ mGLCallbacks->mCleanupCb();+ }+}++void HsQMLCanvasBackEnd::doCleanupKill()+{+ doCleanup();+ delete this;+}+ HsQMLCanvas::HsQMLCanvas(QQuickItem* parent) : QQuickItem(parent) , mWindow(NULL) , mBackEnd(NULL)+ , mStatus(Okay)+ , mFrontStatus(Okay)+ , mBackStatus(Okay) , mDisplayMode(Inline) , mCanvasWidth(0) , mCanvasWidthSet(false) , mCanvasHeight(0) , mCanvasHeightSet(false)+ , mLoadModel(false)+ , mValidModel(false) { QObject::connect( this, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(doWindowChanged(QQuickWindow*)));+ setFlag(ItemHasContents, true); } HsQMLCanvas::~HsQMLCanvas()@@ -39,14 +335,86 @@ if (!mCanvasHeightSet) { setCanvasHeight(rect.height(), false); }+ update(); } +QSGNode* HsQMLCanvas::updatePaintNode(+ QSGNode* oldNode, UpdatePaintNodeData* paintData)+{+ // Window always needs repainting+ mWindow->update();++ // Lazily create new callbacks+ if (!mGLCallbacks) {+ mGLCallbacks = mDelegate.value<HsQMLGLDelegate>().makeCallbacks();+ mLoadModel = true;+ mValidModel = false;++ // Display nothing without a valid delegate+ if (!mGLCallbacks) {+ setStatus(BadDelegate);+ delete oldNode;+ return NULL;+ }+ }++ // Process model update+ if (mLoadModel) {+ mValidModel = mGLCallbacks->mSyncCb(+ reinterpret_cast<HsQMLJValHandle*>(&mModel));+ mLoadModel = false;+ }++ // Display nothing if there's no valid model+ if (!mValidModel) {+ setStatus(BadModel);+ detachBackEnd();+ delete oldNode;+ return NULL;+ }++ // Create back-end on the rendering thread+ if (!mBackEnd) {+ mBackEnd = new HsQMLCanvasBackEnd(mWindow, mGLCallbacks, mDisplayMode);++ // Monitor back-end's status+ QObject::connect(+ mBackEnd, SIGNAL(statusChanged(HsQMLCanvas::Status)),+ this, SLOT(doBackEndStatusChanged(HsQMLCanvas::Status)));+ setStatus(mBackEnd->status(), true);+ }+ setStatus(Okay);++ // Save pointer to transform node+ mBackEnd->setTransformNode(+ Inline != mDisplayMode ? paintData->transformNode : NULL,+ width(), height());++ // Produce texture node if needed+ if (QSGTexture* texture =+ mBackEnd->updateFBO(mCanvasWidth, mCanvasHeight)) {+ QSGSimpleTextureNode* n = static_cast<QSGSimpleTextureNode*>(oldNode);+ if (!n) {+ n = new QSGSimpleTextureNode();+ }+ n->setRect(0, 0, width(), height());+ n->setTexture(texture);+ return n;+ }++ delete oldNode;+ return NULL;+}+ void HsQMLCanvas::detachBackEnd() { if (mBackEnd) { // The back-end belongs to the rendering thread- mBackEnd->deleteLater();+ QMetaObject::invokeMethod(+ mBackEnd, "doCleanupKill", Qt::QueuedConnection);+ QObject::disconnect(mBackEnd, 0, this, 0); mBackEnd = NULL;+ mGLCallbacks.reset(); } } @@ -60,7 +428,9 @@ bool change = mDisplayMode != mode; mDisplayMode = mode; if (change) {+ detachBackEnd(); displayModeChanged();+ update(); } } @@ -76,6 +446,7 @@ mCanvasWidthSet |= set; if (change) { canvasWidthChanged();+ update(); } } @@ -97,6 +468,7 @@ mCanvasHeightSet |= set; if (change) { canvasHeightChanged();+ update(); } } @@ -106,37 +478,104 @@ setCanvasHeight(height(), false); } -void HsQMLCanvas::doWindowChanged(QQuickWindow* win)+QVariant HsQMLCanvas::delegate() const {- if (mWindow) {- QObject::disconnect(mWindow, NULL, this, NULL);+ return mDelegate;+}++void HsQMLCanvas::setDelegate(const QVariant& d)+{+ bool change = mDelegate != d;+ mDelegate = d;+ if (change) { detachBackEnd();+ delegateChanged();+ update(); }- mWindow = win;- if (mWindow) {- QObject::connect(- mWindow, SIGNAL(beforeSynchronizing()),- this, SLOT(doPreSynchronise()), Qt::DirectConnection);+}++QJSValue HsQMLCanvas::model() const+{+ return mModel;+}++void HsQMLCanvas::setModel(const QJSValue& m)+{+ bool change = !mModel.strictlyEquals(m);+ mModel = m;+ if (change) {+ modelChanged();+ mLoadModel = true;+ update(); } } -void HsQMLCanvas::doPreSynchronise()+HsQMLCanvas::Status HsQMLCanvas::status() const {- // This executes on the rendering thread- if (!mBackEnd) {- mBackEnd = new HsQMLCanvasBackEnd(mWindow);+ return mStatus;+}++void HsQMLCanvas::setStatus(Status status, bool backEnd)+{+ if (backEnd) {+ mBackStatus = status; }- mBackEnd->setModeSize(mDisplayMode, mCanvasWidth, mCanvasHeight);+ else {+ mFrontStatus = status;+ }+ Status newStatus = mFrontStatus == Okay ? mBackStatus : mFrontStatus;+ bool change = mStatus != newStatus;+ if (change) {+ statusChanged();+ } } -HsQMLGLControl::HsQMLGLControl(QQuickItem* parent)- : QQuickItem(parent)-{}+void HsQMLCanvas::doWindowChanged(QQuickWindow* win)+{+ detachBackEnd();+ mWindow = win;+} -void hsqml_init_jval_gldelegate(- HsQMLJValHandle* hndl,- HsQMLGLPaintCb paintCb)+void HsQMLCanvas::doBackEndStatusChanged(Status status) {- QJSValue* value = reinterpret_cast<QJSValue*>(hndl);- HsQMLGLDelegate delegate(paintCb);+ setStatus(status, true);+}++HsQMLGLDelegateHandle* hsqml_create_gldelegate()+{+ return reinterpret_cast<HsQMLGLDelegateHandle*>(new HsQMLGLDelegate());+}++void hsqml_finalise_gldelegate_handle(+ HsQMLGLDelegateHandle* hndl)+{+ HsQMLGLDelegate* delegate = reinterpret_cast<HsQMLGLDelegate*>(hndl);+ delete delegate;+}++void hsqml_gldelegate_setup(+ HsQMLGLDelegateHandle* hndl,+ HsQMLGLMakeCallbacksCb makeContextCb)+{+ HsQMLGLDelegate* delegate = reinterpret_cast<HsQMLGLDelegate*>(hndl);+ delegate->setup(makeContextCb);+}++void hsqml_gldelegate_to_jval(+ HsQMLGLDelegateHandle* hndl,+ HsQMLJValHandle* jhndl)+{+ HsQMLGLDelegate* delegate = reinterpret_cast<HsQMLGLDelegate*>(hndl);+ new((void*)jhndl) QJSValue(+ gManager->activeEngine()->declEngine()->toScriptValue(*delegate));+}++void hsqml_gldelegate_from_jval(+ HsQMLGLDelegateHandle* hndl,+ HsQMLJValHandle* jhndl)+{+ HsQMLGLDelegate* delegate = reinterpret_cast<HsQMLGLDelegate*>(hndl);+ QJSValue* value = reinterpret_cast<QJSValue*>(jhndl);+ *delegate = gManager->activeEngine()->declEngine()->+ fromScriptValue<HsQMLGLDelegate>(*value); }
cbits/Canvas.h view
@@ -1,27 +1,93 @@ #ifndef HSQML_CANVAS_H #define HSQML_CANVAS_H +#include <QtCore/QObject> #include <QtCore/QExplicitlySharedDataPointer>+#include <QtCore/QMetaType>+#include <QtCore/QScopedPointer>+#include <QtCore/QSharedData>+#include <QtGui/qopengl.h>+#include <QtGui/QOpenGLContext>+#include <QtGui/QOpenGLFramebufferObject> #include <QtQuick/QQuickItem> #include "hsqml.h" +class QSGTexture;+class QSGTransformNode;+class QQuickWindow; class HsQMLCanvasBackEnd;-class HsQMLGLDelegateImpl;+class HsQMLWindowInfoImpl; +class HsQMLGLCallbacks : public QSharedData+{+public:+ HsQMLGLCallbacks(+ HsQMLGLSetupCb, HsQMLGLCleanupCb, HsQMLGLSyncCb, HsQMLGLPaintCb);+ ~HsQMLGLCallbacks();++ HsQMLGLSetupCb mSetupCb;+ HsQMLGLCleanupCb mCleanupCb;+ HsQMLGLSyncCb mSyncCb;+ HsQMLGLPaintCb mPaintCb;+};++class HsQMLGLDelegateImpl : public QSharedData+{+public:+ HsQMLGLDelegateImpl(HsQMLGLMakeCallbacksCb);+ ~HsQMLGLDelegateImpl();++ HsQMLGLMakeCallbacksCb mMakeCallbacksCb;+};+ class HsQMLGLDelegate { public:- HsQMLGLDelegate(HsQMLGLPaintCb);+ HsQMLGLDelegate(); ~HsQMLGLDelegate();+ void setup(HsQMLGLMakeCallbacksCb);+ typedef QExplicitlySharedDataPointer<HsQMLGLCallbacks> CallbacksRef;+ CallbacksRef makeCallbacks(); private: QExplicitlySharedDataPointer<HsQMLGLDelegateImpl> mImpl; }; +Q_DECLARE_METATYPE(HsQMLGLDelegate)++class HsQMLWindowInfoImpl : public QSharedData+{+public:+ HsQMLWindowInfoImpl(QQuickWindow*);+ +private:+ friend class HsQMLWindowInfo;+ QQuickWindow* mWin;+ int mBelowCount;+ bool mBelowClear;+};++class HsQMLWindowInfo+{+public:+ HsQMLWindowInfo();+ static HsQMLWindowInfo getWindowInfo(QQuickWindow*);+ void addBelow();+ void removeBelow();+ bool needsBelowClear();+ void endFrame();++private:+ QExplicitlySharedDataPointer<HsQMLWindowInfoImpl> mImpl;+};++Q_DECLARE_METATYPE(HsQMLWindowInfo)+ class HsQMLCanvas : public QQuickItem { Q_OBJECT+ Q_ENUMS(Status) Q_ENUMS(DisplayMode) Q_PROPERTY(DisplayMode displayMode READ displayMode WRITE setDisplayMode NOTIFY displayModeChanged)@@ -29,8 +95,14 @@ RESET unsetCanvasWidth NOTIFY canvasWidthChanged) Q_PROPERTY(qreal canvasHeight READ canvasHeight WRITE setCanvasHeight RESET unsetCanvasHeight NOTIFY canvasHeightChanged)+ Q_PROPERTY(QVariant delegate READ delegate WRITE setDelegate+ NOTIFY delegateChanged)+ Q_PROPERTY(QJSValue model READ model WRITE setModel+ NOTIFY modelChanged)+ Q_PROPERTY(Status status READ status NOTIFY statusChanged) public:+ enum Status {Okay, BadDelegate, BadModel, BadConfig, BadProcs, BadBind}; enum DisplayMode {Above, Below, Inline}; HsQMLCanvas(QQuickItem* = NULL);@@ -40,6 +112,7 @@ Q_DISABLE_COPY(HsQMLCanvas); void geometryChanged(const QRectF&, const QRectF&) Q_DECL_OVERRIDE;+ QSGNode* updatePaintNode(QSGNode*, UpdatePaintNodeData*) Q_DECL_OVERRIDE; void detachBackEnd(); DisplayMode displayMode() const; void setDisplayMode(DisplayMode);@@ -49,30 +122,82 @@ qreal canvasHeight() const; void setCanvasHeight(qreal, bool = true); void unsetCanvasHeight();+ QVariant delegate() const;+ void setDelegate(const QVariant&);+ QJSValue model() const;+ void setModel(const QJSValue&);+ Status status() const;+ void setStatus(Status, bool = false); Q_SIGNAL void displayModeChanged(); Q_SIGNAL void canvasWidthChanged(); Q_SIGNAL void canvasHeightChanged();+ Q_SIGNAL void delegateChanged();+ Q_SIGNAL void modelChanged();+ Q_SIGNAL void statusChanged(); Q_SLOT void doWindowChanged(QQuickWindow*);- Q_SLOT void doPreSynchronise();+ Q_SLOT void doBackEndStatusChanged(HsQMLCanvas::Status); QQuickWindow* mWindow; HsQMLCanvasBackEnd* mBackEnd;+ Status mStatus;+ Status mFrontStatus;+ Status mBackStatus; DisplayMode mDisplayMode; qreal mCanvasWidth; bool mCanvasWidthSet; qreal mCanvasHeight; bool mCanvasHeightSet;+ QVariant mDelegate;+ HsQMLGLDelegate::CallbacksRef mGLCallbacks;+ QJSValue mModel;+ bool mLoadModel;+ bool mValidModel; }; -class HsQMLGLControl : public QQuickItem+class HsQMLCanvasBackEnd : public QObject { Q_OBJECT public:- HsQMLGLControl(QQuickItem* = NULL);+ HsQMLCanvasBackEnd(+ QQuickWindow*,+ const HsQMLGLDelegate::CallbacksRef&,+ HsQMLCanvas::DisplayMode);+ ~HsQMLCanvasBackEnd();+ void setTransformNode(QSGTransformNode*, qreal, qreal);+ QSGTexture* updateFBO(qreal, qreal);+ HsQMLCanvas::Status status() const; private:- Q_DISABLE_COPY(HsQMLGLControl);+ Q_DISABLE_COPY(HsQMLCanvasBackEnd)++ void setStatus(HsQMLCanvas::Status);+ Q_SLOT void doRendering();+ Q_SLOT void doEndFrame();+ Q_SLOT void doCleanup();+ Q_SLOT void doCleanupKill();+ Q_SIGNAL void statusChanged(HsQMLCanvas::Status);++ typedef void (*GLViewportFn)(GLint,GLint,GLsizei,GLsizei);+ typedef void (*GLClearColorFn)(GLclampf,GLclampf,GLclampf,GLclampf);+ typedef void (*GLClearFn)(GLbitfield);++ QQuickWindow* mWindow;+ HsQMLWindowInfo mWinInfo;+ HsQMLGLDelegate::CallbacksRef mGLCallbacks;+ QOpenGLContext* mGL;+ GLViewportFn mGLViewportFn;+ GLClearColorFn mGLClearColorFn;+ GLClearFn mGLClearFn;+ HsQMLCanvas::Status mStatus;+ HsQMLCanvas::DisplayMode mDisplayMode;+ qreal mItemWidth;+ qreal mItemHeight;+ QSGTransformNode* mTransformNode;+ qreal mCanvasWidth;+ qreal mCanvasHeight;+ QScopedPointer<QOpenGLFramebufferObject> mFBO;+ QScopedPointer<QSGTexture> mTexture; }; #endif //HSQML_CANVAS_H
− cbits/CanvasImpl.cpp
@@ -1,56 +0,0 @@-#include <QtCore/qmath.h>--#include "CanvasImpl.h"-#include "Manager.h"--HsQMLGLDelegateImpl::HsQMLGLDelegateImpl(HsQMLGLPaintCb paintCb)- : mPaintCb(paintCb)-{}--HsQMLGLDelegateImpl::~HsQMLGLDelegateImpl()-{- gManager->freeFun(reinterpret_cast<HsFunPtr>(mPaintCb));-}--HsQMLCanvasBackEnd::HsQMLCanvasBackEnd(QQuickWindow* win)-{- QObject::connect(- win, SIGNAL(beforeRendering()),- this, SLOT(doRendering()));-}--HsQMLCanvasBackEnd::~HsQMLCanvasBackEnd()-{-}--void HsQMLCanvasBackEnd::setModeSize(- HsQMLCanvas::DisplayMode mode, qreal w, qreal h)-{- mDisplayMode = mode;- mCanvasWidth = w;- mCanvasHeight = h;-- if (HsQMLCanvas::Inline == mode) {- if (!mFBO || mFBO->width() != w || mFBO->height() != h) {- mFBO.reset(new QOpenGLFramebufferObject(- qCeil(w), qCeil(h), QOpenGLFramebufferObject::Depth));- }- }- else {- mFBO.reset();- }-}--void HsQMLCanvasBackEnd::doRendering()-{- bool inlineMode = HsQMLCanvas::Inline == mDisplayMode;- if (inlineMode && !mFBO) {- return;- }- if (inlineMode) {- mFBO->bind();- }- if (inlineMode) {- mFBO->release();- }-}
− cbits/CanvasImpl.h
@@ -1,41 +0,0 @@-#ifndef HSQML_CANVAS_IMPL_H-#define HSQML_CANVAS_IMPL_H--#include <QtCore/QObject>-#include <QtCore/QScopedPointer>-#include <QtCore/QSharedData>-#include <QtGui/QOpenGLFramebufferObject>-#include <QtQuick/QQuickWindow>--#include "Canvas.h"--class HsQMLGLDelegateImpl : public QSharedData-{-public:- HsQMLGLDelegateImpl(HsQMLGLPaintCb);- ~HsQMLGLDelegateImpl();-- HsQMLGLPaintCb mPaintCb;-};--class HsQMLCanvasBackEnd : public QObject-{- Q_OBJECT--public:- HsQMLCanvasBackEnd(QQuickWindow*);- ~HsQMLCanvasBackEnd();- void setModeSize(HsQMLCanvas::DisplayMode, qreal, qreal);--private:- Q_DISABLE_COPY(HsQMLCanvasBackEnd)-- Q_SLOT void doRendering();-- HsQMLCanvas::DisplayMode mDisplayMode;- qreal mCanvasWidth;- qreal mCanvasHeight;- QScopedPointer<QOpenGLFramebufferObject> mFBO;-};--#endif //HSQML_CANVAS_IMPL_H
cbits/Manager.cpp view
@@ -8,6 +8,7 @@ #include <pthread.h> #endif +#include "Canvas.h" #include "Manager.h" #include "Object.h" @@ -211,8 +212,9 @@ // Install hooked handler for QVariants QVariantPrivate::registerHandler(0, &mHookedHandler); - // Register custom type+ // Register custom types qRegisterMetaType<HsQMLEngineConfig>("HsQMLEngineConfig");+ qmlRegisterType<HsQMLCanvas>("HsQML.Canvas", 1, 0, "HaskellCanvas"); // Create application object mApp = new HsQMLManagerApp();
cbits/Object.cpp view
@@ -1,13 +1,28 @@ #include <HsFFI.h> #include <QtCore/QString>+#include <QtCore/QMutexLocker> #include <QtQml/QQmlEngine> #include "Object.h" #include "Class.h" #include "Manager.h" -static const char* cRefSrcNames[] = {"Hndl", "Obj", "Event", "Var"};+static const char* cRefSrcNames[] = {"Hndl", "Weak", "Obj", "Event", "Var"}; +HsQMLObjectFinaliser::HsQMLObjectFinaliser(HsQMLObjFinaliserCb cb)+ : mFinaliseCb(cb)+{}++HsQMLObjectFinaliser::~HsQMLObjectFinaliser()+{+ gManager->freeFun(reinterpret_cast<HsFunPtr>(mFinaliseCb));+}++void HsQMLObjectFinaliser::finalise(HsQMLObjectProxy* proxy) const+{+ mFinaliseCb(reinterpret_cast<HsQMLObjectHandle*>(proxy));+}+ HsQMLObjectProxy::HsQMLObjectProxy(HsStablePtr haskell, HsQMLClass* klass) : mHaskell(haskell) , mKlass(klass)@@ -60,6 +75,7 @@ mKlass->name(), mSerial, mObject)); mObject = NULL;+ runFinalisers(); } void HsQMLObjectProxy::tryGCLock()@@ -79,15 +95,43 @@ { Q_ASSERT(gManager->isEventThread()); - if (mObject && mHndlCount.loadAcquire() == 0 && mObject->isGCLocked()) {- mObject->clearGCLock();+ if (mObject && mHndlCount.loadAcquire() == 0) {+ if (mObject->isGCLocked()) {+ mObject->clearGCLock(); - HSQML_LOG(5,- QString().sprintf("Unlock QObject, class=%s, id=%d, qptr=%p.",- mKlass->name(), mSerial, mObject));+ HSQML_LOG(5,+ QString().sprintf("Unlock QObject, class=%s, id=%d, qptr=%p.",+ mKlass->name(), mSerial, mObject));+ }+ else {+ // If there had been a QML object then this would have happened+ // when the QML GC collected it.+ runFinalisers();+ } } } +void HsQMLObjectProxy::addFinaliser(const HsQMLObjectFinaliser::Ref& f)+{+ QMutexLocker locker(&mFinaliseMutex);+ mFinalisers.append(f);+}++void HsQMLObjectProxy::runFinalisers()+{+ // Copy and clear finalisers under lock+ mFinaliseMutex.lock();+ const Finalisers fs = mFinalisers;+ mFinalisers.clear();+ mFinaliseMutex.unlock();++ // Call finalisers outside lock so they can re-addFinaliser()+ Q_FOREACH(const HsQMLObjectFinaliser::Ref& f, fs) {+ ref(Handle);+ f->finalise(this);+ }+}+ HsQMLEngine* HsQMLObjectProxy::engine() const { if (mObject != NULL) {@@ -112,7 +156,7 @@ void HsQMLObjectProxy::deref(RefSrc src) {- // Remove JavaScript GC lock when there are no handles+ // Remove JavaScript GC lock when there are no strong handles if (src == Handle || src == Variant) { int hndlCount = mHndlCount.fetchAndAddOrdered(-1); if (hndlCount == 1 && mObject) {@@ -340,6 +384,14 @@ return hsqml_get_object_from_pointer(jval->toQObject()); } +extern void hsqml_object_reference_handle(+ HsQMLObjectHandle* hndl,+ int weak)+{+ HsQMLObjectProxy* proxy = (HsQMLObjectProxy*)hndl;+ proxy->ref(weak ? HsQMLObjectProxy::WeakHandle : HsQMLObjectProxy::Handle);+}+ extern void hsqml_finalise_object_handle( HsQMLObjectHandle* hndl) {@@ -349,6 +401,15 @@ } } +extern void hsqml_finalise_object_weak_handle(+ HsQMLObjectHandle* hndl)+{+ if (hndl) {+ HsQMLObjectProxy* proxy = (HsQMLObjectProxy*)hndl;+ proxy->deref(HsQMLObjectProxy::WeakHandle);+ }+}+ extern void hsqml_fire_signal( HsQMLObjectHandle* hndl, int idx, void** args) {@@ -362,4 +423,26 @@ HsQMLObject* obj = proxy->object(engine); QMetaObject::activate(obj, proxy->klass()->metaObj(), idx, args); }+}++extern HsQMLObjFinaliserHandle* hsqml_create_obj_finaliser(+ HsQMLObjFinaliserCb cb)+{+ return reinterpret_cast<HsQMLObjFinaliserHandle*>(+ new HsQMLObjectFinaliser::Ref(new HsQMLObjectFinaliser(cb)));+}++extern void hsqml_finalise_obj_finaliser(+ HsQMLObjFinaliserHandle* hndl)+{+ HsQMLObjectFinaliser::Ref* fp =+ reinterpret_cast<HsQMLObjectFinaliser::Ref*>(hndl);+ delete fp;+}++extern void hsqml_object_add_finaliser(+ HsQMLObjectHandle* hndl, HsQMLObjFinaliserHandle* fhndl)+{+ HsQMLObjectProxy* proxy = reinterpret_cast<HsQMLObjectProxy*>(hndl);+ proxy->addFinaliser(*reinterpret_cast<HsQMLObjectFinaliser::Ref*>(fhndl)); }
cbits/Object.h view
@@ -4,12 +4,33 @@ #include <QtCore/QObject> #include <QtCore/QAtomicInt> #include <QtCore/QEvent>+#include <QtCore/QExplicitlySharedDataPointer>+#include <QtCore/QMutex>+#include <QtCore/QVarLengthArray> #include <QtQml/QJSValue> +#include "hsqml.h"+ class HsQMLEngine; class HsQMLClass; class HsQMLObject;+class HsQMLObjectProxy; +class HsQMLObjectFinaliser : public QSharedData+{+public:+ typedef QExplicitlySharedDataPointer<HsQMLObjectFinaliser> Ref;++ HsQMLObjectFinaliser(HsQMLObjFinaliserCb);+ ~HsQMLObjectFinaliser();+ void finalise(HsQMLObjectProxy*) const;++private:+ Q_DISABLE_COPY(HsQMLObjectFinaliser);++ HsQMLObjFinaliserCb mFinaliseCb;+};+ class HsQMLObjectProxy { public:@@ -21,18 +42,25 @@ void clearObject(); void tryGCLock(); void removeGCLock();+ void addFinaliser(const HsQMLObjectFinaliser::Ref&);+ void runFinalisers(); HsQMLEngine* engine() const;- enum RefSrc {Handle, Object, Event, Variant};+ enum RefSrc {Handle, WeakHandle, Object, Event, Variant}; void ref(RefSrc); void deref(RefSrc); private:+ Q_DISABLE_COPY(HsQMLObjectProxy);+ HsStablePtr mHaskell; HsQMLClass* mKlass; int mSerial; HsQMLObject* volatile mObject; QAtomicInt mRefCount; QAtomicInt mHndlCount;+ QMutex mFinaliseMutex;+ typedef QVarLengthArray<HsQMLObjectFinaliser::Ref, 1> Finalisers;+ Finalisers mFinalisers; }; class HsQMLObjectEvent : public QEvent@@ -62,6 +90,8 @@ HsQMLEngine* engine() const; private:+ Q_DISABLE_COPY(HsQMLObject);+ HsQMLObjectProxy* mProxy; HsStablePtr mHaskell; HsQMLClass* mKlass;
cbits/hsqml.h view
@@ -162,17 +162,79 @@ extern HsQMLObjectHandle* hsqml_get_object_from_jval( HsQMLJValHandle*); +extern void hsqml_object_reference_handle(+ HsQMLObjectHandle*, int);+ extern void hsqml_finalise_object_handle( HsQMLObjectHandle*); +extern void hsqml_finalise_object_weak_handle(+ HsQMLObjectHandle*);+ extern void hsqml_fire_signal( HsQMLObjectHandle*, int, void**); +/* Object Finaliser */+typedef char HsQMLObjFinaliserHandle;++typedef void (*HsQMLObjFinaliserCb)(HsQMLObjectHandle*);++extern HsQMLObjFinaliserHandle* hsqml_create_obj_finaliser(+ HsQMLObjFinaliserCb);++extern void hsqml_finalise_obj_finaliser(+ HsQMLObjFinaliserHandle*);++extern void hsqml_object_add_finaliser(+ HsQMLObjectHandle*, HsQMLObjFinaliserHandle*);+ /* Engine */ extern void hsqml_create_engine( HsQMLObjectHandle*, HsQMLStringHandle*, HsQMLTrivialCb stopCb);++/* Canvas */+typedef char HsQMLGLDelegateHandle;++typedef enum {+ HSQML_GL_DESKTOP,+ HSQML_GL_ES+} HsQMLGLCanvasType;++typedef void (*HsQMLGLSetupCb)(+ HsQMLGLCanvasType, int, int);++typedef void (*HsQMLGLCleanupCb)();++typedef int (*HsQMLGLSyncCb)(+ HsQMLJValHandle*);++typedef void (*HsQMLGLPaintCb)(+ float*, float, float);++typedef void (*HsQMLGLMakeCallbacksCb)(+ HsQMLGLSetupCb*,+ HsQMLGLCleanupCb*,+ HsQMLGLSyncCb*,+ HsQMLGLPaintCb*);++extern HsQMLGLDelegateHandle* hsqml_create_gldelegate();++extern void hsqml_finalise_gldelegate_handle(+ HsQMLGLDelegateHandle*);++extern void hsqml_gldelegate_setup(+ HsQMLGLDelegateHandle*,+ HsQMLGLMakeCallbacksCb);++extern void hsqml_gldelegate_to_jval(+ HsQMLGLDelegateHandle*,+ HsQMLJValHandle*);++extern void hsqml_gldelegate_from_jval(+ HsQMLGLDelegateHandle*,+ HsQMLJValHandle*); #ifdef __cplusplus }
hsqml.cabal view
@@ -1,5 +1,5 @@ Name: hsqml-Version: 0.3.1.1+Version: 0.3.2.0 Cabal-version: >= 1.14 Build-type: Custom License: BSD3@@ -41,25 +41,30 @@ base == 4.*, containers >= 0.4 && < 0.6, filepath == 1.3.*,- text >= 0.11 && < 1.2,+ text >= 0.11 && < 1.3, tagged >= 0.4 && < 0.8, transformers >= 0.2 && < 0.5 Exposed-modules: Graphics.QML Graphics.QML.Debug+ Graphics.QML.Canvas Graphics.QML.Engine Graphics.QML.Marshal Graphics.QML.Objects+ Graphics.QML.Objects.Weak Other-modules: Graphics.QML.Internal.BindPrim+ Graphics.QML.Internal.BindCanvas Graphics.QML.Internal.BindObj Graphics.QML.Internal.BindCore Graphics.QML.Internal.JobQueue Graphics.QML.Internal.Marshal Graphics.QML.Internal.MetaObj+ Graphics.QML.Internal.Objects Graphics.QML.Internal.Types Hs-source-dirs: src C-sources:+ cbits/Canvas.cpp cbits/Class.cpp cbits/Engine.cpp cbits/Intrinsics.cpp@@ -67,6 +72,7 @@ cbits/Object.cpp Include-dirs: cbits X-moc-headers:+ cbits/Canvas.h cbits/Engine.h cbits/Manager.h X-separate-cbits: True@@ -105,7 +111,7 @@ base == 4.*, containers >= 0.4 && < 0.6, directory >= 1.1 && < 1.3,- text >= 0.11 && < 1.2,+ text >= 0.11 && < 1.3, tagged >= 0.4 && < 0.8, QuickCheck >= 2.4 && < 2.8, hsqml == 0.3.*
src/Graphics/QML.hs view
@@ -19,9 +19,11 @@ -- * Graphics.QML module Graphics.QML.Engine, module Graphics.QML.Marshal,- module Graphics.QML.Objects+ module Graphics.QML.Objects,+ module Graphics.QML.Objects.Weak ) where import Graphics.QML.Engine import Graphics.QML.Marshal import Graphics.QML.Objects+import Graphics.QML.Objects.Weak
+ src/Graphics/QML/Canvas.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE+ ScopedTypeVariables,+ TypeFamilies,+ FlexibleInstances+ #-}++{-| Facility for drawing OpenGL graphics into the QML scenegraph.++To use this facility, you must place a @HaskellCanvas@ item into your+QML scene. This item can be imported from the @HsQML.Canvas 1.0@ namespace+using the @import@ statement in your QML script. It has several properties+which can be set from QML:++[@displayMode@] Specifies how the canvas is rendered with respect to the+rest of the scene. Possible values are:++ [@HaskellCanvas.Above@] The canvas shares a buffer with the scenegraph+ and is painted top of other items.+ [@HaskellCanvas.Inline@] The canvas has its own framebuffer object and the+ contents of this buffer are painted inline with other items (default).+ [@HaskellCanvas.Below@] The canvas shares a buffer with the scenegraph+ and is painted underneath other items.++[@canvasWidth@] Width of the framebuffer object in pixels. Defaults to the+item width.+[@canvasHeight@] Height of the framebuffer object in pixels. Defaults to the+item height.+[@delegate@] A marshalled 'OpenGLDelegate' value which specifies the Haskell+functions used to render the canvas.+[@model@] A value passed to delegate's paint function. The canvas is+repainted whenever this value changes.+[@status@] Either @HaskellCanvas.Okay@ or an error code (read only).+-}+module Graphics.QML.Canvas (+ OpenGLDelegate,+ newOpenGLDelegate,+ OpenGLType (+ OpenGLDesktop,+ OpenGLES),+ OpenGLSetup,+ openGLType,+ openGLMajor,+ openGLMinor,+ OpenGLPaint,+ OpenGLPaint',+ setupData,+ modelData,+ matrixPtr,+ itemWidth,+ itemHeight+) where++import Graphics.QML.Internal.BindCanvas+import Graphics.QML.Internal.BindPrim+import Graphics.QML.Internal.Marshal+import Graphics.QML.Marshal++import Data.IORef+import Data.Maybe+import Data.Tagged+import Control.Monad.Trans.Maybe+import Foreign.Ptr (Ptr)+import Foreign.C.Types (CFloat)++-- | Delegate for painting OpenGL graphics.+newtype OpenGLDelegate = OpenGLDelegate HsQMLGLDelegateHandle++instance Marshal OpenGLDelegate where+ type MarshalMode OpenGLDelegate c d = ModeBidi c+ marshaller = Marshaller {+ mTypeCVal_ = Tagged tyJSValue,+ mFromCVal_ = jvalFromCVal,+ mToCVal_ = jvalToCVal,+ mWithCVal_ = jvalWithCVal,+ mFromJVal_ = \ptr -> MaybeT $ do+ hndl <- hsqmlCreateGldelegate+ hsqmlGldelegateFromJval hndl ptr+ return $ Just $ OpenGLDelegate hndl,+ mWithJVal_ = \(OpenGLDelegate hndl) f ->+ withJVal (flip hsqmlGldelegateToJval) hndl f,+ mFromHndl_ = unimplFromHndl,+ mToHndl_ = unimplToHndl}++-- | Represents the type of an OpenGL context.+data OpenGLType+ -- | Desktop OpenGL context.+ = OpenGLDesktop+ -- | OpenGL ES context.+ | OpenGLES+ deriving (Eq, Show)++mapGLType :: HsQMLGLCanvasType -> OpenGLType+mapGLType HsqmlGlDesktop = OpenGLDesktop+mapGLType HsqmlGlEs = OpenGLES++-- | Encapsulates parameters for OpenGL setup.+data OpenGLSetup = OpenGLSetup {+ -- | Type of OpenGL context.+ openGLType :: OpenGLType,+ -- | Major version number of OpenGL context.+ openGLMajor :: Int,+ -- | Minor version number of OpenGL context.+ openGLMinor :: Int+}++-- | Encapsulates parameters for OpenGL paint.+data OpenGLPaint s m = OpenGLPaint {+ -- | Gets the setup state.+ setupData :: s,+ -- | Gets the active model.+ modelData :: m,+ -- | Pointer to a 4 by 4 matrix which transform coordinates in the range+ -- (-1, -1) to (1, 1) on to the target rectangle in the scene.+ matrixPtr :: Ptr CFloat,+ -- | Width of the canvas item in its local coordinate system.+ itemWidth :: Float,+ -- | Height of the canvas item in its local coordinate system.+ itemHeight :: Float+}++-- | Specialised version of `OpenGLPaint` with no model.+type OpenGLPaint' s = OpenGLPaint s Ignored++newOpenGLCallbacks :: (Marshal m, CanGetFrom m ~ Yes) =>+ (OpenGLSetup -> IO i) -> (OpenGLPaint i m -> IO ()) -> (i -> IO ()) ->+ CallbacksFactory+newOpenGLCallbacks setupFn paintFn cleanupFn = do+ iRef <- newIORef Nothing+ mRef <- newIORef Nothing+ let setupCb ctype major minor = do+ iVal <- setupFn $ OpenGLSetup+ (mapGLType $ cIntToEnum ctype)+ (fromIntegral major) (fromIntegral minor)+ writeIORef iRef $ Just iVal+ cleanupCb = do+ iVal <- readIORef iRef+ cleanupFn $ fromJust iVal+ syncCb ptr = do+ mVal <- runMaybeT $ mFromJVal ptr+ writeIORef mRef mVal+ return $ if isJust mVal then 1 else 0+ paintCb mPtr w h = do+ iVal <- readIORef iRef+ mVal <- readIORef mRef+ paintFn $ OpenGLPaint+ (fromJust iVal) (fromJust mVal)+ mPtr (realToFrac w) (realToFrac h)+ return (setupCb, cleanupCb, syncCb, paintCb)++-- | Creates a new 'OpenGLDelegate' from setup, paint, and cleanup functions.+newOpenGLDelegate :: (Marshal m, CanGetFrom m ~ Yes) =>+ (OpenGLSetup -> IO i) -> (OpenGLPaint i m -> IO ()) -> (i -> IO ()) ->+ IO OpenGLDelegate+newOpenGLDelegate setupFn paintFn cleanupFn = do+ hndl <- hsqmlCreateGldelegate+ hsqmlGldelegateSetup hndl (newOpenGLCallbacks setupFn paintFn cleanupFn)+ return $ OpenGLDelegate hndl
src/Graphics/QML/Engine.hs view
@@ -34,18 +34,16 @@ import Graphics.QML.Internal.Marshal import Graphics.QML.Internal.BindPrim import Graphics.QML.Internal.BindCore-import Graphics.QML.Marshal+import Graphics.QML.Marshal () import Graphics.QML.Objects import Control.Applicative import Control.Concurrent-import Control.Concurrent.MVar import Control.Exception import Control.Monad import Control.Monad.IO.Class import qualified Data.Text as T import Data.List-import Data.Maybe import Data.Traversable import Data.Typeable import Foreign.Ptr@@ -75,8 +73,8 @@ hsqmlInit let obj = contextObject config DocumentPath res = initialDocument config- hndl <- sequenceA $ fmap mToHndl $ obj- mWithCVal (T.pack res) $ \resPtr -> do+ hndl <- sequenceA $ fmap mToHndl obj+ mWithCVal (T.pack res) $ \resPtr -> hsqmlCreateEngine hndl (HsQMLStringHandle $ castPtr resPtr) stopCb return Engine @@ -101,8 +99,7 @@ -- | Starts a new QML engine using the supplied configuration and returns -- immediately without blocking. runEngineAsync :: EngineConfig -> RunQML Engine-runEngineAsync config = RunQML $ do- runEngineImpl config (return ())+runEngineAsync config = RunQML $ runEngineImpl config (return ()) -- | Conveniance function that both runs the event loop and starts a new QML -- engine. It blocks keeping the event loop running until the engine has@@ -116,7 +113,7 @@ newtype RunQML a = RunQML (IO a) deriving (Functor, Applicative, Monad) instance MonadIO RunQML where- liftIO io = RunQML io+ liftIO = RunQML -- | This function enters the Qt event loop and executes the supplied function -- in the 'RunQML' monad on a new unbound thread. The event loop will continue@@ -138,7 +135,7 @@ ret <- try runFn case ret of Left ex -> putMVar finishVar $ throwIO (ex :: SomeException)- Right ret -> putMVar finishVar $ return ret+ Right ret' -> putMVar finishVar $ return ret' hsqmlEvloopRelease yieldCb = if rtsSupportsBoundThreads then Nothing@@ -151,7 +148,7 @@ finFn tryRunInBoundThread :: IO a -> IO a-tryRunInBoundThread action = do+tryRunInBoundThread action = if rtsSupportsBoundThreads then runInBoundThread action else action@@ -195,15 +192,15 @@ fileDocument :: FilePath -> DocumentPath fileDocument fp = let ds = splitDirectories fp- abs = isAbsolute fp+ isAbs = isAbsolute fp fixHead = (\cs -> if null cs then [] else '/':cs) .- takeWhile (\c -> not $ c `elem` pathSeparators)+ takeWhile (`notElem` pathSeparators) mapHead _ [] = [] mapHead f (x:xs) = f x : xs afp = intercalate "/" $ mapHead fixHead ds rfp = intercalate "/" ds- in DocumentPath $ if abs then "file://" ++ afp else rfp+ in DocumentPath $ if isAbs then "file://" ++ afp else rfp -- | Converts a URI string into a 'DocumentPath'. uriDocument :: String -> DocumentPath
+ src/Graphics/QML/Internal/BindCanvas.chs view
@@ -0,0 +1,88 @@+{-# LANGUAGE+ ForeignFunctionInterface+ #-}++module Graphics.QML.Internal.BindCanvas where++{#import Graphics.QML.Internal.BindPrim #}++import Foreign.C.Types+import Foreign.Ptr+import Foreign.ForeignPtr.Safe+import Foreign.Storable++#include "hsqml.h"++--+-- GL Delegate+--++{#pointer *HsQMLGLDelegateHandle as ^ foreign newtype #}++type SetupCb = CInt -> CInt -> CInt -> IO ()+type CleanupCb = IO ()+type SyncCb = HsQMLJValHandle -> IO CInt+type PaintCb = Ptr CFloat -> CFloat -> CFloat -> IO ()+type MakeCb = Ptr (FunPtr SetupCb) -> Ptr (FunPtr CleanupCb) ->+ Ptr (FunPtr SyncCb) -> Ptr (FunPtr PaintCb) -> IO ()+type CallbacksFactory = IO (SetupCb, CleanupCb, SyncCb, PaintCb)++{#enum HsQMLGLCanvasType as ^ {underscoreToCase} #}++foreign import ccall "wrapper"+ marshalSetupCb :: SetupCb -> IO (FunPtr SetupCb)++foreign import ccall "wrapper"+ marshalCleanupCb :: CleanupCb -> IO (FunPtr CleanupCb)++foreign import ccall "wrapper" + marshalSyncCb :: SyncCb -> IO (FunPtr SyncCb)++foreign import ccall "wrapper" + marshalPaintCb :: PaintCb -> IO (FunPtr PaintCb)++foreign import ccall "wrapper"+ marshalMakeCb :: MakeCb -> IO (FunPtr MakeCb)++withCallbacksFactory :: CallbacksFactory -> (FunPtr MakeCb -> IO a) -> IO a+withCallbacksFactory factory with = do+ let makeFn setupPtrFPtr cleanupPtrFPtr syncPtrFPtr paintPtrFPtr = do+ (setupFn, cleanupFn, syncFn, paintFn) <- factory+ setupFPtr <- marshalSetupCb setupFn+ poke setupPtrFPtr setupFPtr+ cleanupFPtr <- marshalCleanupCb cleanupFn+ poke cleanupPtrFPtr cleanupFPtr+ syncFPtr <- marshalSyncCb syncFn+ poke syncPtrFPtr syncFPtr+ paintFPtr <- marshalPaintCb paintFn+ poke paintPtrFPtr paintFPtr+ makeFPtr <- marshalMakeCb makeFn+ with makeFPtr++foreign import ccall "hsqml.h &hsqml_finalise_gldelegate_handle"+ hsqmlFinaliseGldelegateHandlePtr ::+ FunPtr (Ptr HsQMLGLDelegateHandle -> IO ())++newGLDelegateHandle :: Ptr HsQMLGLDelegateHandle -> IO HsQMLGLDelegateHandle+newGLDelegateHandle p = do+ fp <- newForeignPtr hsqmlFinaliseGldelegateHandlePtr p+ return $ HsQMLGLDelegateHandle fp++{#fun unsafe hsqml_create_gldelegate as ^+ {} ->+ `HsQMLGLDelegateHandle' newGLDelegateHandle* #}++{#fun unsafe hsqml_gldelegate_setup as ^+ {withHsQMLGLDelegateHandle* `HsQMLGLDelegateHandle',+ withCallbacksFactory* `CallbacksFactory'} ->+ `()' #}++{#fun unsafe hsqml_gldelegate_to_jval as ^+ {withHsQMLGLDelegateHandle* `HsQMLGLDelegateHandle',+ id `HsQMLJValHandle'} ->+ `()' #}++{#fun unsafe hsqml_gldelegate_from_jval as ^+ {withHsQMLGLDelegateHandle* `HsQMLGLDelegateHandle',+ id `HsQMLJValHandle'} ->+ `()' #}
src/Graphics/QML/Internal/BindCore.chs view
@@ -9,10 +9,7 @@ {#import Graphics.QML.Internal.BindObj #} import Foreign.C.Types-import Foreign.C.String-import Foreign.Marshal.Utils import Foreign.Ptr-import Foreign.StablePtr #include <HsFFI.h> @@ -47,9 +44,6 @@ withMaybeTrivialCb Nothing = \cont -> cont nullFunPtr {#enum HsQMLEventLoopStatus as ^ {underscoreToCase} #}--cIntToEnum :: Enum a => CInt -> a-cIntToEnum = toEnum . fromIntegral {#fun hsqml_evloop_run as ^ {withTrivialCb* `TrivialCb',
src/Graphics/QML/Internal/BindObj.chs view
@@ -10,7 +10,7 @@ import Control.Exception (bracket) import Control.Monad (void) import Foreign.C.Types-import Foreign.Marshal.Utils (toBool)+import Foreign.Marshal.Utils (fromBool, toBool) import Foreign.Ptr import Foreign.ForeignPtr.Safe import Foreign.ForeignPtr.Unsafe@@ -67,6 +67,9 @@ foreign import ccall "hsqml.h &hsqml_finalise_object_handle" hsqmlFinaliseObjectHandlePtr :: FunPtr (Ptr (HsQMLObjectHandle) -> IO ()) +foreign import ccall "hsqml.h &hsqml_finalise_object_weak_handle"+ hsqmlFinaliseObjectWeakHandlePtr :: FunPtr (Ptr (HsQMLObjectHandle) -> IO ())+ newObjectHandle :: Ptr HsQMLObjectHandle -> IO HsQMLObjectHandle newObjectHandle p = do fp <- newForeignPtr hsqmlFinaliseObjectHandlePtr p@@ -83,7 +86,7 @@ {#fun unsafe hsqml_object_set_active as ^ {withMaybeHsQMLObjectHandle* `Maybe HsQMLObjectHandle'} ->- `Bool' toBool #}+ `Bool' toBool #} withActiveObject :: HsQMLObjectHandle -> IO () -> IO () withActiveObject hndl action =@@ -116,8 +119,49 @@ {id `HsQMLJValHandle'} -> `HsQMLObjectHandle' newObjectHandle* #} +{#fun unsafe hsqml_object_reference_handle as ^+ {id `Ptr HsQMLObjectHandle',+ fromBool `Bool'} ->+ `()' #}++copyObjectHandle :: HsQMLObjectHandle -> Bool -> IO HsQMLObjectHandle+copyObjectHandle (HsQMLObjectHandle fp) weak = do+ withForeignPtr fp $ \p -> do+ hsqmlObjectReferenceHandle p weak+ fp' <- newForeignPtr final p+ return $ HsQMLObjectHandle fp'+ where final = if weak+ then hsqmlFinaliseObjectWeakHandlePtr+ else hsqmlFinaliseObjectHandlePtr+ {#fun hsqml_fire_signal as ^ {withHsQMLObjectHandle* `HsQMLObjectHandle', `Int', id `Ptr (Ptr ())'} -> `()' #}++{#pointer *HsQMLObjFinaliserHandle as ^ foreign newtype #}++foreign import ccall "hsqml.h &hsqml_finalise_obj_finaliser"+ hsqmlFinaliseObjFinaliserPtr :: FunPtr (Ptr HsQMLObjFinaliserHandle -> IO ())++type ObjFinaliserFunc = Ptr HsQMLObjectHandle -> IO ()++foreign import ccall "wrapper" + marshalObjFinaliser :: ObjFinaliserFunc -> IO (FunPtr ObjFinaliserFunc)++newObjFinaliserHandle ::+ Ptr HsQMLObjFinaliserHandle -> IO HsQMLObjFinaliserHandle+newObjFinaliserHandle p = do+ fp <- newForeignPtr hsqmlFinaliseObjFinaliserPtr p+ return $ HsQMLObjFinaliserHandle fp++{#fun unsafe hsqml_create_obj_finaliser as ^+ {id `FunPtr ObjFinaliserFunc'} ->+ `HsQMLObjFinaliserHandle' newObjFinaliserHandle* #}++{#fun unsafe hsqml_object_add_finaliser as ^+ {withHsQMLObjectHandle* `HsQMLObjectHandle',+ withHsQMLObjFinaliserHandle* `HsQMLObjFinaliserHandle'} ->+ `()' #}+
src/Graphics/QML/Internal/BindPrim.chs view
@@ -12,8 +12,8 @@ #include "hsqml.h" -cIntConv :: (Integral a, Integral b) => a -> b-cIntConv = fromIntegral+cIntToEnum :: Enum a => CInt -> a+cIntToEnum = toEnum . fromIntegral -- -- String
src/Graphics/QML/Internal/Marshal.hs view
@@ -34,7 +34,7 @@ tyString = TypeId 10 tyObject = TypeId 39 tyVoid = TypeId 43-tyJSValue = TypeId $ hsqmlJValTypeId+tyJSValue = TypeId hsqmlJValTypeId type MTypeCValFunc t = Tagged t TypeId type MFromCValFunc t = Ptr () -> ErrIO t@@ -83,6 +83,14 @@ type instance ModeFrom IIsObjType = No type instance ModeFrom IGetObjType = No +-- | 'MarshalMode' for non-object types with to-only marshalling.+type family ModeTo c+type instance ModeTo ICanGetFrom = No+type instance ModeTo ICanPassTo = Yes+type instance ModeTo ICanReturnTo = Yes+type instance ModeTo IIsObjType = No+type instance ModeTo IGetObjType = No+ -- | 'MarshalMode' for void in method returns. type family ModeRetVoid c type instance ModeRetVoid ICanGetFrom = No@@ -107,6 +115,14 @@ type instance ModeObjFrom a IIsObjType = Yes type instance ModeObjFrom a IGetObjType = a +-- | 'MarshalMode' for object types with to-only marshalling.+type family ModeObjTo a c+type instance ModeObjTo a ICanGetFrom = No+type instance ModeObjTo a ICanPassTo = Yes+type instance ModeObjTo a ICanReturnTo = Yes+type instance ModeObjTo a IIsObjType = Yes+type instance ModeObjTo a IGetObjType = a+ -- | Type value indicating a capability is supported. data Yes @@ -191,35 +207,35 @@ mToHndl = mToHndl_ (marshaller :: MarshallerFor t) unimplFromCVal :: MFromCValFunc t-unimplFromCVal = \_ -> error "Type does not support mFromCVal."+unimplFromCVal _ = error "Type does not support mFromCVal." unimplToCVal :: MToCValFunc t-unimplToCVal = \_ _ -> error "Type does not support mToCVal."+unimplToCVal _ _ = error "Type does not support mToCVal." unimplWithCVal :: MWithCValFunc t-unimplWithCVal = \_ _ -> error "Type does not support mWithCVal."+unimplWithCVal _ _ = error "Type does not support mWithCVal." unimplFromJVal :: MFromJValFunc t-unimplFromJVal = \_ -> error "Type does not support mFromJVal."+unimplFromJVal _ = error "Type does not support mFromJVal." unimplWithJVal :: MWithJValFunc t-unimplWithJVal = \_ _ -> error "Type does not support mWithJVal."+unimplWithJVal _ _ = error "Type does not support mWithJVal." unimplFromHndl :: MFromHndlFunc t-unimplFromHndl = \_ -> error "Type does not support mFromHndl."+unimplFromHndl _ = error "Type does not support mFromHndl." unimplToHndl :: MToHndlFunc t-unimplToHndl = \_ -> error "Type does not support mToHndl."+unimplToHndl _ = error "Type does not support mToHndl." jvalFromCVal :: (Marshal t) => MFromCValFunc t jvalFromCVal = mFromJVal . HsQMLJValHandle . castPtr jvalToCVal :: (Marshal t) => MToCValFunc t-jvalToCVal = \val ptr -> mWithJVal val $ \jval ->+jvalToCVal val ptr = mWithJVal val $ \jval -> hsqmlSetJval (HsQMLJValHandle $ castPtr ptr) jval jvalWithCVal :: (Marshal t) => MWithCValFunc t-jvalWithCVal = \val f -> mWithJVal val $ \(HsQMLJValHandle ptr) ->+jvalWithCVal val f = mWithJVal val $ \(HsQMLJValHandle ptr) -> f $ castPtr ptr instance Marshal () where
src/Graphics/QML/Internal/MetaObj.hs view
@@ -1,11 +1,9 @@ module Graphics.QML.Internal.MetaObj where -import Graphics.QML.Internal.BindObj-import Graphics.QML.Internal.Marshal import Graphics.QML.Internal.Types import Control.Monad-import Control.Monad.Trans.State+import Control.Monad.Trans.State (State, execState, get, put) import Data.Bits import Data.Map (Map) import qualified Data.Map as Map@@ -37,8 +35,8 @@ crlAppend :: CRList a -> [a] -> CRList a crlAppend (CRList n xs) ys = CRList n' xs' where (xs', n') = rev ys xs n- rev [] vs n = (vs, n)- rev (u:us) vs n = rev us (u:vs) (n+1)+ rev [] vs m = (vs, m)+ rev (u:us) vs m = rev us (u:vs) (m+1) crlToNewArray :: (Storable b) => (a -> IO b) -> CRList a -> IO (Ptr b) crlToNewArray f (CRList len lst) = do@@ -124,8 +122,7 @@ in enc filterMembers :: MemberKind -> [Member tt] -> [Member tt]-filterMembers k ms =- filter (\m -> k == memberKind m) ms+filterMembers k = filter (\m -> k == memberKind m) newMOCState :: MOCState -> MOCState newMOCState enc = MOCState@@ -149,12 +146,12 @@ let msChr = mStrChar state msInf = mStrInfo state msMap = mStrMap state- case (Map.lookup str msMap) of+ case Map.lookup str msMap of Just idx -> writeInt idx Nothing -> do let idx = crlLen msInf - 1- msChr' = msChr `crlAppend` (map castCharToCChar str) `crlAppend1` 0- msInf' = msInf `crlAppend1` (fromIntegral $ crlLen msChr')+ msChr' = msChr `crlAppend` map castCharToCChar str `crlAppend1` 0+ msInf' = msInf `crlAppend1` fromIntegral (crlLen msChr') msMap' = Map.insert str (fromIntegral idx) msMap put $ state { mStrChar = msChr',@@ -168,15 +165,15 @@ let types = memberTypes m datal = mData state mpMap = mParamMap state- case (Map.lookup types mpMap) of- Just idx -> return ()+ case Map.lookup types mpMap of+ Just _ -> return () Nothing -> do let idx = crlLen datal mpMap' = Map.insert types (fromIntegral idx) mpMap put $ state { mParamMap = mpMap'}- mapM_ writeInt $ map typeId types- mapM_ writeString $ replicate (length $ memberParams m) ""+ mapM_ (writeInt . typeId) types+ replicateM_ (length $ memberParams m) $ writeString "" writeMethod :: Member tt -> State MOCState () writeMethod m = do@@ -193,8 +190,8 @@ state <- get put $ state { mDataMethodsIdx = mplus (mDataMethodsIdx state) (Just idx),- mMethodCount = mc + (mMethodCount state),- mSignalCount = sc + (mSignalCount state),+ mMethodCount = mc + mMethodCount state,+ mSignalCount = sc + mSignalCount state, mSigMap = maybe (mSigMap state) (\k -> Map.insert k (fromIntegral $ mSignalCount state) (mSigMap state)) $ memberKey m,@@ -207,13 +204,13 @@ writeString $ memberName p writeInt $ typeId $ memberType p writeInt (pfReadable .|. pfScriptable .|.- (if (ConstPropertyMember == memberKind p) then pfConstant else 0) .|.- (if (isJust $ memberFunAux p) then pfWritable else 0) .|.- (if (isJust $ memberKey p) then pfNotify else 0))+ (if ConstPropertyMember == memberKind p then pfConstant else 0) .|.+ (if isJust (memberFunAux p) then pfWritable else 0) .|.+ (if isJust (memberKey p) then pfNotify else 0)) state <- get put $ state { mDataPropsIdx = mplus (mDataPropsIdx state) (Just idx),- mPropertyCount = 1 + (mPropertyCount state),+ mPropertyCount = 1 + mPropertyCount state, mFuncProperties = mFuncProperties state `crlAppend1` (Just $ memberFun p) `crlAppend1` memberFunAux p }
+ src/Graphics/QML/Internal/Objects.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE+ ScopedTypeVariables,+ TypeFamilies+ #-}++module Graphics.QML.Internal.Objects where++import Graphics.QML.Internal.BindObj+import Graphics.QML.Internal.Marshal+import Graphics.QML.Internal.Types++import Control.Monad.Trans.Maybe+import Data.Tagged+import Data.Typeable+import Foreign.ForeignPtr++-- | Represents an instance of the QML class which wraps the type @tt@.+newtype ObjRef tt = ObjRef HsQMLObjectHandle++instance (Typeable tt) => Marshal (ObjRef tt) where+ type MarshalMode (ObjRef tt) c d = ModeObjBidi tt c+ marshaller = Marshaller {+ mTypeCVal_ = retag (mTypeCVal :: Tagged AnyObjRef TypeId),+ mFromCVal_ = \ptr -> do+ anyObj <- mFromCVal ptr+ MaybeT $ fromAnyObjRefIO anyObj,+ mToCVal_ = \(ObjRef hndl) ptr ->+ mToCVal (AnyObjRef hndl) ptr,+ mWithCVal_ = \(ObjRef hndl) f ->+ mWithCVal (AnyObjRef hndl) f,+ mFromJVal_ = \ptr -> do+ anyObj <- mFromJVal ptr+ MaybeT $ fromAnyObjRefIO anyObj,+ mWithJVal_ = \(ObjRef hndl) f ->+ mWithJVal (AnyObjRef hndl) f,+ mFromHndl_ =+ return . ObjRef,+ mToHndl_ = \(ObjRef hndl) ->+ return hndl}++fromObjRefIO :: ObjRef tt -> IO tt+fromObjRefIO (ObjRef hndl) = hsqmlObjectGetHsValue hndl++-- | Represents an instance of a QML class which wraps an arbitrary Haskell+-- type. Unlike 'ObjRef', an 'AnyObjRef' only carries the type of its Haskell+-- value dynamically and does not encode it into the static type.+newtype AnyObjRef = AnyObjRef HsQMLObjectHandle++instance Marshal AnyObjRef where+ type MarshalMode AnyObjRef c d = ModeObjBidi No c+ marshaller = Marshaller {+ mTypeCVal_ = Tagged tyJSValue,+ mFromCVal_ = jvalFromCVal,+ mToCVal_ = jvalToCVal,+ mWithCVal_ = jvalWithCVal,+ mFromJVal_ = \ptr -> MaybeT $ do+ hndl <- hsqmlGetObjectFromJval ptr+ return $ if isNullObjectHandle hndl+ then Nothing else Just $ AnyObjRef hndl,+ mWithJVal_ = \(AnyObjRef hndl@(HsQMLObjectHandle ptr)) f -> do+ jval <- hsqmlObjectGetJval hndl+ ret <- f jval+ touchForeignPtr ptr+ return ret,+ mFromHndl_ =+ return . AnyObjRef,+ mToHndl_ = \(AnyObjRef hndl) ->+ return hndl}++fromAnyObjRefIO :: forall tt. (Typeable tt) =>+ AnyObjRef -> IO (Maybe (ObjRef tt))+fromAnyObjRefIO (AnyObjRef hndl) = do+ info <- hsqmlObjectGetHsTyperep hndl+ let srcRep = typeOf (undefined :: tt)+ dstRep = cinfoObjType info+ return $ if srcRep == dstRep+ then Just $ ObjRef hndl+ else Nothing
src/Graphics/QML/Internal/Types.hs view
@@ -1,7 +1,6 @@ module Graphics.QML.Internal.Types where import Data.Map (Map)-import qualified Data.Map as Map import Data.Typeable import Data.Unique import Foreign.Ptr
src/Graphics/QML/Marshal.hs view
@@ -12,9 +12,11 @@ marshaller), ModeBidi, ModeFrom,+ ModeTo, ModeRetVoid, ModeObjBidi, ModeObjFrom,+ ModeObjTo, Yes, CanGetFrom, ICanGetFrom,@@ -28,11 +30,17 @@ IGetObjType, Marshaller, + -- * Data Types+ Ignored (+ Ignored),+ -- * Custom Marshallers bidiMarshallerIO, bidiMarshaller, fromMarshallerIO, fromMarshaller,+ toMarshallerIO,+ toMarshaller ) where import Graphics.QML.Internal.BindPrim@@ -41,14 +49,11 @@ import Control.Monad import Control.Monad.Trans.Maybe-import Data.Maybe import Data.Tagged import Data.Int import Data.Text (Text)-import qualified Data.Text as T import qualified Data.Text.Foreign as T import Foreign.C.Types-import Foreign.C.String import Foreign.Marshal.Alloc import Foreign.Ptr import Foreign.Storable@@ -217,6 +222,26 @@ mFromHndl_ = unimplFromHndl, mToHndl_ = unimplToHndl} +--+-- Ignored+--++-- | Represents an argument whose value is ignored.++data Ignored = Ignored++instance Marshal Ignored where+ type MarshalMode Ignored c d = ModeFrom c+ marshaller = Marshaller {+ mTypeCVal_ = Tagged tyJSValue,+ mFromCVal_ = jvalFromCVal,+ mToCVal_ = unimplToCVal,+ mWithCVal_ = unimplWithCVal,+ mFromJVal_ = \_ -> MaybeT . return $ Just Ignored,+ mWithJVal_ = unimplWithJVal,+ mFromHndl_ = unimplFromHndl,+ mToHndl_ = unimplToHndl}+ type BidiMarshaller a b = Marshaller b (MarshalMode a ICanGetFrom ()) (MarshalMode a ICanPassTo ())@@ -279,3 +304,34 @@ forall a b. (Marshal a, CanGetFrom a ~ Yes) => (a -> b) -> FromMarshaller a b fromMarshaller fromFn = fromMarshallerIO (return . fromFn)++type ToMarshaller a b = Marshaller b+ No+ (MarshalMode a ICanPassTo ())+ (MarshalMode a ICanReturnTo ())+ (MarshalMode a IIsObjType ())+ (MarshalMode a IGetObjType ())++-- | Provides a "to" 'Marshaller' which allows you to define an instance of+-- 'Marshal' for your own type @b@ in terms of another marshallable type @a@.+-- Type @b@ should have a 'MarshalMode' of 'ModeObjTo' or 'ModeTo'+-- depending on whether @a@ was an object type or not.+toMarshallerIO ::+ forall a b. (Marshal a, CanPassTo a ~ Yes) =>+ (b -> IO a) -> ToMarshaller a b+toMarshallerIO toFn = Marshaller {+ mTypeCVal_ = retag (mTypeCVal :: Tagged a TypeId),+ mFromCVal_ = unimplFromCVal,+ mToCVal_ = \val ptr -> flip mToCVal ptr =<< toFn val,+ mWithCVal_ = \val f -> flip mWithCVal f =<< toFn val,+ mFromJVal_ = unimplFromJVal,+ mWithJVal_ = \val f -> flip mWithJVal f =<< toFn val,+ mFromHndl_ = unimplFromHndl,+ mToHndl_ = \val -> mToHndl =<< toFn val}++-- | Variant of 'toMarshallerIO' where the conversion function between types+-- @a@ and @b@ does not live in the IO monad.+toMarshaller ::+ forall a b. (Marshal a, CanPassTo a ~ Yes) =>+ (b -> a) -> ToMarshaller a b+toMarshaller toFn = toMarshallerIO (return . toFn)
src/Graphics/QML/Objects.hs view
@@ -54,20 +54,17 @@ defPropertySigRW' ) where -import System.IO- import Graphics.QML.Internal.BindCore import Graphics.QML.Internal.BindObj import Graphics.QML.Internal.JobQueue import Graphics.QML.Internal.Marshal import Graphics.QML.Internal.MetaObj+import Graphics.QML.Internal.Objects import Graphics.QML.Internal.Types import Control.Concurrent.MVar-import Control.Monad.Trans.Maybe import Data.Map (Map) import qualified Data.Map as Map-import Data.Set (Set) import qualified Data.Set as Set import Data.Maybe import Data.Proxy@@ -76,9 +73,7 @@ import Data.IORef import Data.Unique import Foreign.Ptr-import Foreign.ForeignPtr import Foreign.Storable-import Foreign.Marshal.Alloc import Foreign.Marshal.Array import System.IO.Unsafe import Numeric@@ -87,32 +82,6 @@ -- ObjRef -- --- | Represents an instance of the QML class which wraps the type @tt@.-data ObjRef tt = ObjRef {- objHndl :: HsQMLObjectHandle-}--instance (Typeable tt) => Marshal (ObjRef tt) where- type MarshalMode (ObjRef tt) c d = ModeObjBidi tt c- marshaller = Marshaller {- mTypeCVal_ = retag (mTypeCVal :: Tagged AnyObjRef TypeId),- mFromCVal_ = \ptr -> do- any <- mFromCVal ptr- MaybeT $ return $ fromAnyObjRef any,- mToCVal_ = \obj ptr ->- mToCVal (AnyObjRef $ objHndl obj) ptr,- mWithCVal_ = \obj f ->- mWithCVal (AnyObjRef $ objHndl obj) f,- mFromJVal_ = \ptr -> do- any <- mFromJVal ptr- MaybeT $ return $ fromAnyObjRef any,- mWithJVal_ = \obj f ->- mWithJVal (AnyObjRef $ objHndl obj) f,- mFromHndl_ = \hndl ->- return $ ObjRef hndl,- mToHndl_ = \obj ->- return $ objHndl obj}- -- | Creates a QML object given a 'Class' and a Haskell value of type @tt@. newObject :: forall tt. Class tt -> tt -> IO (ObjRef tt) newObject (Class cHndl) obj =@@ -130,37 +99,6 @@ fromObjRef :: ObjRef tt -> tt fromObjRef = unsafeDupablePerformIO . fromObjRefIO -fromObjRefIO :: ObjRef tt -> IO tt-fromObjRefIO = hsqmlObjectGetHsValue . objHndl---- | Represents an instance of a QML class which wraps an arbitrary Haskell--- type. Unlike 'ObjRef', an 'AnyObjRef' only carries the type of its Haskell--- value dynamically and does not encode it into the static type.-data AnyObjRef = AnyObjRef {- anyObjHndl :: HsQMLObjectHandle-}--instance Marshal AnyObjRef where- type MarshalMode AnyObjRef c d = ModeObjBidi No c- marshaller = Marshaller {- mTypeCVal_ = Tagged tyJSValue,- mFromCVal_ = jvalFromCVal,- mToCVal_ = jvalToCVal,- mWithCVal_ = jvalWithCVal,- mFromJVal_ = \ptr -> MaybeT $ do- hndl <- hsqmlGetObjectFromJval ptr- return $ if isNullObjectHandle hndl- then Nothing else Just $ AnyObjRef hndl,- mWithJVal_ = \(AnyObjRef hndl@(HsQMLObjectHandle ptr)) f -> do- jval <- hsqmlObjectGetJval hndl- ret <- f jval- touchForeignPtr ptr- return ret,- mFromHndl_ = \hndl ->- return $ AnyObjRef hndl,- mToHndl_ = \obj ->- return $ anyObjHndl obj}- -- | Upcasts an 'ObjRef' into an 'AnyObjRef'. anyObjRef :: ObjRef tt -> AnyObjRef anyObjRef (ObjRef hndl) = AnyObjRef hndl@@ -170,24 +108,12 @@ fromAnyObjRef :: (Typeable tt) => AnyObjRef -> Maybe (ObjRef tt) fromAnyObjRef = unsafeDupablePerformIO . fromAnyObjRefIO -fromAnyObjRefIO :: forall tt. (Typeable tt) =>- AnyObjRef -> IO (Maybe (ObjRef tt))-fromAnyObjRefIO (AnyObjRef hndl) = do- info <- hsqmlObjectGetHsTyperep hndl- let srcRep = typeOf (undefined :: tt)- dstRep = cinfoObjType info- if srcRep == dstRep- then return $ Just $ ObjRef hndl- else return Nothing- -- -- Class -- -- | Represents a QML class which wraps the type @tt@.-data Class tt = Class {- classHndl :: HsQMLClassHandle-}+newtype Class tt = Class HsQMLClassHandle -- | Creates a new QML class for the type @tt@. newClass :: forall tt. (Typeable tt) => [Member tt] -> IO (Class tt)@@ -230,7 +156,7 @@ (\_ _ -> return ()) Nothing (Just k)- in map (uncurry impMember) $ zip [0..] impKeys+ in map (uncurry impMember) $ zip [(0::Int)..] impKeys -- -- Default Class@@ -328,7 +254,7 @@ newtype VoidIO = VoidIO {runVoidIO :: (IO ())} instance MethodSuffix VoidIO where- mkMethodFunc _ f pv = errIO $ runVoidIO f+ mkMethodFunc _ f _ = errIO $ runVoidIO f mkMethodTypes = Tagged $ MethodTypeInfo [] tyVoid class IsVoidIO a
+ src/Graphics/QML/Objects/Weak.hs view
@@ -0,0 +1,120 @@+-- | Facilities for working with weak references, finalisers, and factory+-- pools.+module Graphics.QML.Objects.Weak (+ -- * Weak Object References+ WeakObjRef,+ toWeakObjRef,+ fromWeakObjRef,++ -- * Object Finalisers+ ObjFinaliser,+ newObjFinaliser,+ addObjFinaliser,++ -- * Factory Pools+ FactoryPool,+ newFactoryPool,+ getPoolObject+) where++import Graphics.QML.Internal.BindObj+import Graphics.QML.Internal.Objects++import Control.Concurrent.MVar+import qualified Data.Map as Map+import Data.Map (Map)++-- | Represents a weak reference to a QML object which wraps the type @tt@.+--+-- Unlike ordinary strong references, a weak reference does not prevent the+-- QML garbage collector from collecting the underlying object. Weak references+-- can be used to monitor the life cycles of QML objects.+newtype WeakObjRef tt = WeakObjRef HsQMLObjectHandle++-- | Converts a strong 'ObjRef' into a 'WeakObjRef'. +toWeakObjRef :: ObjRef tt -> IO (WeakObjRef tt)+toWeakObjRef (ObjRef hndl) = do+ hndl' <- copyObjectHandle hndl True+ return $ WeakObjRef hndl'++-- | Converts a 'WeakObjRef' into a strong 'ObjRef'.+--+-- If the underlying QML object has already been collected then the resulting+-- 'ObjRef' can be used to reincarnate it.+fromWeakObjRef :: WeakObjRef tt -> IO (ObjRef tt)+fromWeakObjRef (WeakObjRef hndl) = do+ hndl' <- copyObjectHandle hndl False+ return $ ObjRef hndl'++-- | Represents an object finaliser function for QML objects which wrap the+-- type @tt@.+data ObjFinaliser tt = ObjFinaliser HsQMLObjFinaliserHandle++-- | Create a new object finaliser from a finaliser function.+--+-- Note that at the time the finaliser is called the runtime will have already+-- comitted to collecting the underlying QML object. The 'ObjRef' passed into+-- the finaliser can be used to reincarnate the object, but this QML object+-- will have a distinct identity to the original.+newObjFinaliser :: (ObjRef tt -> IO ()) -> IO (ObjFinaliser tt)+newObjFinaliser f = do+ fPtr <- marshalObjFinaliser $ \hPtr -> do+ hndl <- newObjectHandle hPtr+ f $ ObjRef hndl+ final <- hsqmlCreateObjFinaliser fPtr+ return $ ObjFinaliser final++-- | Adds an object finaliser to an QML object.+--+-- The finaliser will be called no more than once for each time it was added to+-- an object. The timing of finaliser execution is subject to the combined+-- behaviour of the Haskell and QML garbage collectors. All outstanding+-- finalisers will be run when the QML engine is terminated provided that the+-- program does not prematurely exit.+addObjFinaliser :: ObjFinaliser tt -> ObjRef tt -> IO ()+addObjFinaliser (ObjFinaliser final) (ObjRef hndl) =+ hsqmlObjectAddFinaliser hndl final++-- | Represents an object factory which maintains a one-to-one mapping between+-- values of type @tt@ and QML object instances.+--+-- 'ObjRef's manufactured by the pool are cached using the wrapped type @tt@ as+-- the lookup key in an ordered map. The pool uses weak references to+-- automatically purge objects which no longer have any strong references+-- leading to them from either Haskell or QML code.++-- Hence, the pool guarantees that if QML code is using a pool object (e.g. as+-- a source for data binding) then the same object instance can be obtained+-- again from the pool. Conversely, if an object instance is no longer being+-- used then pool will not prevent it from being garbage collected.+data FactoryPool tt = FactoryPool {+ factory_ :: tt -> IO (ObjRef tt),+ pool_ :: MVar (Map tt (WeakObjRef tt)),+ finaliser_ :: ObjFinaliser tt+}++-- | Creates a new 'FactoryPool' using the supplied factory function.+newFactoryPool :: (Ord tt) =>+ (tt -> IO (ObjRef tt)) -> IO (FactoryPool tt)+newFactoryPool factory = do+ pool <- newMVar Map.empty+ finaliser <- newObjFinaliser $ \obj -> do+ value <- fromObjRefIO obj+ modifyMVar_ pool (return . Map.delete value)+ return $ FactoryPool factory pool finaliser++-- | Return the pool's canonical QML object for a value of @tt@, either by+-- creating it or looking it up in the pool's cache of objects.+getPoolObject :: (Ord tt) =>+ FactoryPool tt -> tt -> IO (ObjRef tt)+getPoolObject (FactoryPool factory pool finaliser) value =+ modifyMVar pool $ \pmap ->+ case Map.lookup value pmap of+ Just wkObj -> do+ obj <- fromWeakObjRef wkObj+ return (pmap, obj)+ Nothing -> do+ obj <- factory value+ addObjFinaliser finaliser obj+ wkObj <- toWeakObjRef obj+ return (Map.insert value wkObj pmap, obj)