packages feed

hsqml (empty) → 0.1.0

raw patch · 25 files changed

+2533/−0 lines, 25 filesdep +basedep +containersdep +directorybuild-type:Customsetup-changed

Dependencies added: base, containers, directory, filepath, hsqml, network, tagged, text, transformers

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2010-2012, Robin KAY++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Robin KAY nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,224 @@+#!/usr/bin/runhaskell +module Main where++import Data.List+import Distribution.Simple+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.PackageIndex+import Distribution.Simple.Setup+import Distribution.Simple.Utils (rawSystemExit)+import qualified Distribution.InstalledPackageInfo as I+import Distribution.PackageDescription+import System.Directory+import System.FilePath++import Control.Monad+import Data.Char+import Data.Maybe+import qualified Distribution.ModuleName as ModuleName+import Distribution.Simple.BuildPaths+import Distribution.Simple.Program+import Distribution.Simple.Program.Ld+import Distribution.Simple.Register+import Distribution.Simple.Utils+import Distribution.Text+import Distribution.Version+import Distribution.Verbosity+import System.FilePath++main :: IO ()+main = defaultMainWithHooks simpleUserHooks {+  confHook = confWithQt, buildHook = buildWithQt,+  copyHook = copyWithQt, instHook = instWithQt,+  regHook = regWithQt}++confWithQt :: (GenericPackageDescription, HookedBuildInfo) -> ConfigFlags ->+  IO LocalBuildInfo+confWithQt (gpd,hbi) flags = do+  let verb = fromFlag $ configVerbosity flags+  mocPath <- findProgramLocation verb "moc"+  cppPath <- findProgramLocation verb "cpp"+  let condLib = fromJust $ condLibrary gpd+      fixCondLib lib = lib {+        libBuildInfo = substPaths mocPath cppPath $ libBuildInfo lib} +      condLib' = mapCondTree fixCondLib condLib+      gpd' = gpd {condLibrary = Just $ condLib'}+  lbi <- confHook simpleUserHooks (gpd',hbi) flags+  (_,_,db') <- requireProgramVersion verb+    mocProgram qtVersionRange (withPrograms lbi)+  return $ lbi {withPrograms = db'}++mapCondTree :: (a -> a) -> CondTree v c a -> CondTree v c a+mapCondTree f (CondNode val cnstr cs) =+  CondNode (f val) cnstr $ map updateChildren cs+  where updateChildren (cond,left,right) =+          (cond, mapCondTree f left, fmap (mapCondTree f) right)++substPaths :: Maybe FilePath -> Maybe FilePath -> BuildInfo -> BuildInfo+substPaths mocPath cppPath build =+  let toRoot = takeDirectory . takeDirectory . fromMaybe ""+      substPath = replacePrefix "/QT_ROOT" (toRoot mocPath) .+        replacePrefix "/SYS_ROOT" (toRoot cppPath)+  in build {extraLibDirs = map substPath $ extraLibDirs build,+            includeDirs = map substPath $ includeDirs build}++replacePrefix :: (Eq a) => [a] -> [a] -> [a] -> [a]+replacePrefix old new xs =+  case stripPrefix old xs of+    Just ys -> new ++ ys+    Nothing -> xs++buildWithQt ::+  PackageDescription -> LocalBuildInfo -> UserHooks -> BuildFlags -> IO ()+buildWithQt pkgDesc lbi hooks flags = do+    let verb = fromFlag $ buildVerbosity flags+    libs' <- maybeMapM (\lib -> fmap (\lib' ->+      lib {libBuildInfo = lib'}) $ fixQtBuild verb lbi $ libBuildInfo lib) $+      library pkgDesc+    exes' <- mapM (\exe -> fmap (\exe' ->+      exe {buildInfo = exe'}) $ fixQtBuild verb lbi $ buildInfo exe) $+      executables pkgDesc+    let pkgDesc' = pkgDesc {library = libs', executables = exes'}+        lbi' = if (needsGHCiFix pkgDesc lbi)+                 then lbi {withGHCiLib = False, splitObjs = False} else lbi+    buildHook simpleUserHooks pkgDesc' lbi' hooks flags+    case libs' of+      Just lib -> when (needsGHCiFix pkgDesc lbi) $+        buildGHCiFix verb pkgDesc lbi lib+      Nothing  -> return ()++fixQtBuild :: Verbosity -> LocalBuildInfo -> BuildInfo -> IO BuildInfo+fixQtBuild verb lbi build = do+  let moc  = fromJust $ lookupProgram mocProgram $ withPrograms lbi+      incs = words $ fromMaybe "" $ lookup "x-moc-headers" $+        customFieldsBI build+      bDir = buildDir lbi+      cpps = map (\inc ->+        bDir </> (takeDirectory inc) </>+        ("moc_" ++ (takeBaseName inc) ++ ".cpp")) incs+  mapM_ (\(i,o) -> do+      createDirectoryIfMissingVerbose verb True (takeDirectory o)+      runProgram verb moc [i,"-o",o]) $ zip incs cpps+  return build {cSources = cpps ++ cSources build,+                ccOptions = "-fPIC" : ccOptions build}++needsGHCiFix :: PackageDescription -> LocalBuildInfo -> Bool+needsGHCiFix pkgDesc lbi =+  (withGHCiLib lbi &&) $ fromMaybe False $ do+    lib <- library pkgDesc+    str <- lookup "x-separate-cbits" $ customFieldsBI $ libBuildInfo lib+    simpleParse str++mkGHCiFixLibPkgId :: PackageDescription -> PackageIdentifier+mkGHCiFixLibPkgId pkgDesc =+  let id = packageId pkgDesc+      (PackageName name) = pkgName id+  in id {pkgName = PackageName $ "cbits-" ++ name}++mkGHCiFixLibName :: PackageDescription -> String+mkGHCiFixLibName pkgDesc =+  ("lib" ++ display (mkGHCiFixLibPkgId pkgDesc)) <.> dllExtension++buildGHCiFix ::+  Verbosity -> PackageDescription -> LocalBuildInfo -> Library -> IO ()+buildGHCiFix verb pkgDesc lbi lib = do+  let bDir = buildDir lbi+      ms = map ModuleName.toFilePath $ libModules lib+      hsObjs = map ((bDir </>) . (<.> "o")) ms+  stubObjs <- fmap catMaybes $+    mapM (findFileWithExtension ["o"] [bDir]) $ map (++ "_stub") ms+  (ld,_) <- requireProgram verb ldProgram (withPrograms lbi)+  combineObjectFiles verb ld+    (bDir </> (("HS" ++) $ display $ packageId pkgDesc) <.> "o")+    (stubObjs ++ hsObjs)+  (ghc,_) <- requireProgram verb ghcProgram (withPrograms lbi)+  let bi = libBuildInfo lib+      bDir = buildDir lbi+  runProgram verb ghc (+    ["-shared","-o",bDir </> (mkGHCiFixLibName pkgDesc)] +++    (ldOptions bi) ++ (map ("-l" ++) $ extraLibs bi) +++    (map ("-L" ++) $ extraLibDirs bi) +++    (map ((bDir </>) . flip replaceExtension objExtension) $ cSources bi))+  return ()++mocProgram :: Program+mocProgram = Program {+  programName = "moc",+  programFindLocation = flip findProgramLocation "moc",+  programFindVersion = \verbosity path -> do+    (_,line,_) <- rawSystemStdInOut verbosity path ["-v"] Nothing False+    return $+      findSubseq (stripPrefix "(Qt ") line >>=+      Just . takeWhile (\c -> isDigit c || c == '.') >>=+      simpleParse,+  programPostConf = \_ _ -> return []+}++qtVersionRange :: VersionRange+qtVersionRange = orLaterVersion $ Version [4,7] []++copyWithQt ::+  PackageDescription -> LocalBuildInfo -> UserHooks -> CopyFlags -> IO ()+copyWithQt pkgDesc lbi hooks flags = do+  copyHook simpleUserHooks pkgDesc lbi hooks flags+  let verb = fromFlag $ copyVerbosity flags+      dest = fromFlag $ copyDest flags+      bDir = buildDir lbi+      libDir = libdir $ absoluteInstallDirs pkgDesc lbi dest+      file = mkGHCiFixLibName pkgDesc+  when (needsGHCiFix pkgDesc lbi) $+    installOrdinaryFile verb (bDir </> file) (libDir </> file)++regWithQt :: +  PackageDescription -> LocalBuildInfo -> UserHooks -> RegisterFlags -> IO ()+regWithQt pkg@PackageDescription { library       = Just lib  }+         lbi@LocalBuildInfo     { libraryConfig = Just clbi } hooks flags = do+  let verb    = fromFlag $ regVerbosity flags+      inplace = fromFlag $ regInPlace flags+      dist    = fromFlag $ regDistPref flags+      pkgDb   = withPackageDB lbi+  instPkgInfo <- generateRegistrationInfo+    verb pkg lib lbi clbi inplace dist+  let instPkgInfo' = if (needsGHCiFix pkg lbi)+        then instPkgInfo {I.extraGHCiLibraries =+          (display $ mkGHCiFixLibPkgId pkg) :+          I.extraGHCiLibraries instPkgInfo}+        else instPkgInfo+  case flagToMaybe $ regGenPkgConf flags of+    Just regFile -> do+      let regFile = fromMaybe (display (packageId pkg) <.> "conf")+            (fromFlag $ regGenPkgConf flags)+      writeUTF8File regFile $ I.showInstalledPackageInfo instPkgInfo'+    _ | fromFlag (regGenScript flags) ->+      die "Registration scripts are not implemented."+      | otherwise -> +      registerPackage verb instPkgInfo' pkg lbi inplace pkgDb+regWithQt pkgDesc _ _ flags =+  setupMessage (fromFlag $ regVerbosity flags) +    "Package contains no library to register:" (packageId pkgDesc)++instWithQt ::+  PackageDescription -> LocalBuildInfo -> UserHooks -> InstallFlags -> IO ()+instWithQt pkgDesc lbi hooks flags = do+  let copyFlags = defaultCopyFlags {+        copyDistPref   = installDistPref flags,+        copyVerbosity  = installVerbosity flags+      }+      regFlags = defaultRegisterFlags {+        regDistPref  = installDistPref flags,+        regInPlace   = installInPlace flags,+        regPackageDB = installPackageDB flags,+        regVerbosity = installVerbosity flags+      }+  copyWithQt pkgDesc lbi hooks copyFlags+  when (hasLibs pkgDesc) $ regWithQt pkgDesc lbi hooks regFlags++maybeMapM :: (Monad m) => (a -> m b) -> (Maybe a) -> m (Maybe b)+maybeMapM f = maybe (return Nothing) $ liftM Just . f++findSubseq :: ([a] -> Maybe b) -> [a] -> Maybe b+findSubseq f [] = f []+findSubseq f xs@(_:ys) =+  case f xs of+    Nothing -> findSubseq f ys+    Just r  -> Just r
+ cbits/HsQMLClass.cpp view
@@ -0,0 +1,113 @@+#include <cstdlib>+#include <HsFFI.h>+#include <QAtomicInt>+#include <QMetaObject>+#include <QString>+#include <QDebug>++#include "hsqml.h"+#include "HsQMLClass.h"+#include "HsQMLManager.h"++QAtomicInt gClassId;++HsQMLClass::HsQMLClass(+    unsigned int*  metaData,+    char*          metaStrData,+    HsQMLUniformFunc* methods,+    HsQMLUniformFunc* properties)+    : mRefCount(0)+    , mMetaData(metaData)+    , mMetaStrData(metaStrData)+    , mMethods(methods)+    , mProperties(properties)+    , mMethodCount(metaData[MD_METHOD_COUNT])+    , mPropertyCount(metaData[MD_PROPERTY_COUNT])+    , mMetaObject(QMetaObject {{+          &QObject::staticMetaObject,+          mMetaStrData,+          mMetaData,+          0}})+{+    ref(Handle);+}++HsQMLClass::~HsQMLClass()+{+    for (int i=0; i<mMethodCount; i++) {+        gManager->freeFun((HsFunPtr)mMethods[i]);+    }+    for (unsigned int i=0; i<2*mPropertyCount; i++) {+        if (mProperties[i]) {+            gManager->freeFun((HsFunPtr)mProperties[i]);+        }+    }+    std::free(mMetaData);+    std::free(mMetaStrData);+    std::free(mMethods);+    std::free(mProperties);+}++const char* HsQMLClass::name()+{+    return mMetaObject.className();+} ++const HsQMLUniformFunc* HsQMLClass::methods()+{+    return mMethods;+}++const HsQMLUniformFunc* HsQMLClass::properties()+{+    return mProperties;+}++void HsQMLClass::ref(RefSrc src)+{+    int count = mRefCount.fetchAndAddOrdered(1);++    if (gLogLevel >= (count == 0 ? 1 : 2)) {+        qDebug() << QString().sprintf(+            "HsQML: %s Class, name=%s, src=%d, count=%d.",+            count ? "Ref" : "New", name(), src, count+1);+    }+}++void HsQMLClass::deref(RefSrc src)+{+    int count = mRefCount.fetchAndAddOrdered(-1);++    if (gLogLevel >= (count == 1 ? 1 : 2)) {+        qDebug() << QString().sprintf(+            "HsQML: %s Class, name=%s, src=%d, count=%d.",+            count > 1 ? "Deref" : "Delete", name(), src, count);+    }++    if (count == 1) {+        delete this;+    }+}++extern "C" int hsqml_get_next_class_id()+{+    return gClassId.fetchAndAddRelaxed(1);+}++extern "C" HsQMLClassHandle* hsqml_create_class(+    unsigned int*  metaData,+    char*          metaStrData,+    HsQMLUniformFunc* methods,+    HsQMLUniformFunc* properties)+{+    HsQMLClass* klass = new HsQMLClass(+            metaData, metaStrData, methods, properties);+    return (HsQMLClassHandle*)klass;+}++extern "C" void hsqml_finalise_class_handle(+    HsQMLClassHandle* hndl)+{+    HsQMLClass* klass = (HsQMLClass*)hndl;+    klass->deref(HsQMLClass::Handle);+}
+ cbits/HsQMLClass.h view
@@ -0,0 +1,41 @@+#ifndef HSQML_CLASS_H+#define HSQML_CLASS_H++#include <QObject>+#include <QAtomicInt>++#include "hsqml.h"++enum MDFields {+    MD_CLASS_NAME     = 1,+    MD_METHOD_COUNT   = 4,+    MD_PROPERTY_COUNT = 6,+};++class HsQMLClass+{+public:+    HsQMLClass(+        unsigned int*, char*, HsQMLUniformFunc*, HsQMLUniformFunc*);+    ~HsQMLClass();+    const char* name();+    const HsQMLUniformFunc* methods();+    const HsQMLUniformFunc* properties();+    enum RefSrc {Handle, ObjProxy};+    void ref(RefSrc);+    void deref(RefSrc);++private:+    QAtomicInt mRefCount;+    unsigned int* mMetaData;+    char* mMetaStrData;+    HsQMLUniformFunc* mMethods;+    HsQMLUniformFunc* mProperties;++public:+    const int mMethodCount;+    const int mPropertyCount;+    const QMetaObject mMetaObject;+};++#endif /*HSQML_CLASS_H*/
+ cbits/HsQMLEngine.cpp view
@@ -0,0 +1,55 @@+#include "hsqml.h"+#include "HsQMLManager.h"+#include "HsQMLEngine.h"+#include "HsQMLObject.h"+#include "HsQMLWindow.h"++HsQMLEngine::HsQMLEngine(HsQMLEngineConfig& config)+{+    // Obtain, re-parent, and set QML global object+    if (config.contextObject) {+        QObject* contextObject = config.contextObject->object();+        contextObject->setParent(this);+        mEngine.rootContext()->setContextObject(contextObject);+    }++    // Create window+    HsQMLWindow* win = new HsQMLWindow(this);+    win->setSource(config.initialURL);+    win->setVisible(config.showWindow);+    if (config.setWindowTitle) {+        win->setTitle(config.windowTitle);+    }+    mWindows.insert(win);+}++HsQMLEngine::~HsQMLEngine()+{+}++QDeclarativeEngine* HsQMLEngine::engine()+{+    return &mEngine;+}++extern "C" void hsqml_create_engine(+    HsQMLObjectHandle* contextObject,+    HsQMLUrlHandle* initialURL,+    int showWindow,+    int setWindowTitle,+    HsQMLStringHandle* windowTitle)+{+    HsQMLEngineConfig config;+    config.contextObject = reinterpret_cast<HsQMLObjectProxy*>(contextObject);+    config.initialURL = *reinterpret_cast<QUrl*>(initialURL);+    config.showWindow = static_cast<bool>(showWindow);+    if (setWindowTitle) {+        config.setWindowTitle = true;+        config.windowTitle = *reinterpret_cast<QString*>(windowTitle);+    }++    Q_ASSERT (gManager);+    QMetaObject::invokeMethod(+        gManager, "createEngine", Qt::QueuedConnection,+        Q_ARG(HsQMLEngineConfig, config));+}
+ cbits/HsQMLEngine.h view
@@ -0,0 +1,41 @@+#ifndef HSQML_ENGINE_H+#define HSQML_ENGINE_H++#include <QSet>+#include <QString>+#include <QUrl>+#include <QDeclarativeEngine>++class HsQMLObjectProxy;+class HsQMLWindow;++struct HsQMLEngineConfig+{+    HsQMLEngineConfig()+        : contextObject(NULL)+        , showWindow(false)+        , setWindowTitle(false)+    {}++    HsQMLObjectProxy* contextObject;+    QUrl initialURL;+    bool showWindow;+    bool setWindowTitle;+    QString windowTitle;+};++class HsQMLEngine : public QObject+{+    Q_OBJECT++public:+    HsQMLEngine(HsQMLEngineConfig&);+    ~HsQMLEngine();+    QDeclarativeEngine* engine();++private:+    QDeclarativeEngine mEngine;+    QSet<HsQMLWindow*> mWindows;+};++#endif /*HSQML_ENGINE_H*/
+ cbits/HsQMLIntrinsics.cpp view
@@ -0,0 +1,77 @@+#include <cstdlib>+#include <cstring>++#include <QString>+#include <QUrl>++#include "hsqml.h"++/* String */+extern "C" size_t hsqml_get_string_size()+{+    return sizeof(QString);+}++extern "C" void hsqml_init_string(HsQMLStringHandle* hndl)+{+    new((void*)hndl) QString();+}++extern "C" void hsqml_deinit_string(HsQMLStringHandle* hndl)+{+    QString* string = (QString*)hndl;+    string->~QString();+}++extern "C" UTF16* hsqml_marshal_string(+    int bufLen, HsQMLStringHandle* hndl)+{+    QString* string = (QString*)hndl;+    string->resize(bufLen);+    return reinterpret_cast<UTF16*>(string->data());+}++extern "C" int hsqml_unmarshal_string(+    HsQMLStringHandle* hndl, UTF16** bufPtr)+{+    QString* string = (QString*)hndl;+    *bufPtr = reinterpret_cast<UTF16*>(string->data());+    return string->length();+}++/* URL */+extern "C" size_t hsqml_get_url_size()+{+    return sizeof(QUrl);+}++extern "C" void hsqml_init_url(HsQMLUrlHandle* hndl)+{+    new((void*)hndl) QUrl();+}++extern "C" void hsqml_deinit_url(HsQMLUrlHandle* hndl)+{+    QUrl* url = (QUrl*)hndl;+    url->~QUrl();+}++extern "C" void hsqml_marshal_url(+    char* buf, int bufLen, HsQMLUrlHandle* hndl)+{+    QUrl* url = (QUrl*)hndl;+    QByteArray cstr(buf, bufLen);+    url->setEncodedUrl(cstr, QUrl::StrictMode);+}++extern "C" int hsqml_unmarshal_url(+    HsQMLUrlHandle* hndl, char** bufPtr)+{+    QUrl* url = (QUrl*)hndl;+    QByteArray cstr = url->toEncoded();+    int bufLen = cstr.length();+    char* buf = reinterpret_cast<char*>(malloc(bufLen));+    memcpy(buf, cstr.data(), bufLen);+    *bufPtr = buf;+    return bufLen;+}
+ cbits/HsQMLManager.cpp view
@@ -0,0 +1,78 @@+#include <QMetaType>++#include "HsQMLManager.h"++QMutex gMutex;+HsQMLManager* gManager;+int gLogLevel;++HsQMLManager::HsQMLManager(+    int& argc,+    char** argv,+    void (*freeFun)(HsFunPtr),+    void (*freeStable)(HsStablePtr))+    : mApp(argc, argv)+    , mFreeFun(freeFun)+    , mFreeStable(freeStable)+{+}++HsQMLManager::~HsQMLManager()+{+}++void HsQMLManager::run()+{+    mApp.exec();++    // Delete engines once the event loop returns+    for (QVector<HsQMLEngine*>::iterator it = mEngines.begin();+            it != mEngines.end(); ++it) {+        delete *it;+    }+    mEngines.clear();+}++void HsQMLManager::freeFun(HsFunPtr funPtr)+{+    mFreeFun(funPtr);+}++void HsQMLManager::freeStable(HsStablePtr stablePtr)+{+    mFreeStable(stablePtr);+}++void HsQMLManager::createEngine(HsQMLEngineConfig config)+{+    mEngines.push_back(new HsQMLEngine(config));+}++extern "C" void hsqml_init(+    void (*free_fun)(HsFunPtr),+    void (*free_stable)(HsStablePtr))+{+    gMutex.lock();+    if (!gManager) {+        qRegisterMetaType<HsQMLEngineConfig>("HsQMLEngineConfig");++        int* argcp = new int[1];+        *argcp = 1;+        char** argv = new char*[1];+        argv[0] = new char[1];+        argv[0][0] = '\0';+        gManager = new HsQMLManager(*argcp, argv, free_fun, free_stable);+    }+    gMutex.unlock();+}++extern "C" void hsqml_run()+{+    Q_ASSERT (gManager);+    gManager->run();+}++extern "C" void hsqml_set_debug_loglevel(int level)+{+    gLogLevel = level;+}
+ cbits/HsQMLManager.h view
@@ -0,0 +1,42 @@+#ifndef HSQML_MANAGER_H+#define HSQML_MANAGER_H++#include <QApplication>+#include <QMutex>+#include <QVector>+#include <QUrl>++#include "hsqml.h"+#include "HsQMLEngine.h"++class HsQMLManager : public QObject+{+    Q_OBJECT++public:+    HsQMLManager(+        int& argc,+        char** argv,+        void (*)(HsFunPtr),+        void (*)(HsStablePtr));+    ~HsQMLManager();+    void run();+    void freeFun(HsFunPtr);+    void freeStable(HsStablePtr);+    Q_SLOT void createEngine(HsQMLEngineConfig);++private:+    HsQMLManager(const HsQMLManager&);+    HsQMLManager& operator=(const HsQMLManager&);++    QApplication mApp;+    QVector<HsQMLEngine*> mEngines;+    void (*mFreeFun)(HsFunPtr);+    void (*mFreeStable)(HsStablePtr);+};++extern QMutex gMutex;+extern HsQMLManager* gManager;+extern int gLogLevel;++#endif /*HSQML_MANAGER_H*/
+ cbits/HsQMLObject.cpp view
@@ -0,0 +1,199 @@+#include <HsFFI.h>+#include <QDeclarativeEngine>+#include <QString>+#include <QDebug>++#include "HsQMLObject.h"+#include "HsQMLClass.h"+#include "HsQMLManager.h"++HsQMLObjectProxy::HsQMLObjectProxy(HsStablePtr haskell, HsQMLClass* klass)+    : mHaskell(haskell)+    , mKlass(klass)+    , mObject(NULL)+    , mRefCount(0)+{+    ref(Handle);+    mKlass->ref(HsQMLClass::ObjProxy);+}++HsQMLObjectProxy::~HsQMLObjectProxy()+{+    mKlass->deref(HsQMLClass::ObjProxy);+}++HsStablePtr HsQMLObjectProxy::haskell() const+{+    return mHaskell;+}++HsQMLClass* HsQMLObjectProxy::klass() const+{+    return mKlass;+}++HsQMLObject* HsQMLObjectProxy::object()+{+    if (!mObject) {+        mObject = new HsQMLObject(this);+    }+    return mObject;+}++void HsQMLObjectProxy::clearObject()+{+    mObject = NULL;+}++void HsQMLObjectProxy::ref(RefSrc src)+{+    int count = mRefCount.fetchAndAddOrdered(1);++    if (gLogLevel >= (count == 0 ? 3 : 4)) {+        qDebug() << QString().sprintf(+            "HsQML: %s ObjProxy, class=%s, ptr=%p, src=%d, count=%d.",+            count ? "Ref" : "New", mKlass->name(), this, src, count+1);+    }+}++void HsQMLObjectProxy::deref(RefSrc src)+{+    int count = mRefCount.fetchAndAddOrdered(-1);++    if (gLogLevel >= (count == 1 ? 3 : 4)) {+        qDebug() << QString().sprintf(+            "HsQML: %s ObjProxy, class=%s, ptr=%p, src=%d, count=%d.",+            count > 1 ? "Deref" : "Delete", mKlass->name(), this, src, count);+    }++    if (count == 1) {+        delete this;+    }+}++HsQMLObject::HsQMLObject(HsQMLObjectProxy* proxy)+    : mProxy(proxy)+    , mHaskell(proxy->haskell())+    , mKlass(proxy->klass())+{+    QDeclarativeEngine::setObjectOwnership(+        this, QDeclarativeEngine::JavaScriptOwnership);+    mProxy->ref(HsQMLObjectProxy::Object);+}++HsQMLObject::~HsQMLObject()+{+    mProxy->clearObject();+    mProxy->deref(HsQMLObjectProxy::Object);+}++const QMetaObject* HsQMLObject::metaObject() const+{+    return QObject::d_ptr->metaObject ?+        QObject::d_ptr->metaObject : &mKlass->mMetaObject;+}++void* HsQMLObject::qt_metacast(const char* clname)+{+    if (!clname) {+        return 0;+    }+    if (!strcmp(clname,+            mKlass->mMetaObject.d.stringdata ++            mKlass->mMetaObject.d.data[MD_CLASS_NAME])) {+        return static_cast<void*>(const_cast<HsQMLObject*>(this));+    }+    return QObject::qt_metacast(clname);+}++int HsQMLObject::qt_metacall(QMetaObject::Call c, int id, void** a)+{+    id = QObject::qt_metacall(c, id, a);+    if (id < 0) {+        return id;+    }+    if (QMetaObject::InvokeMetaMethod == c) {+        mKlass->methods()[id](this, a);+        id -= mKlass->mMethodCount;+    }+    else if (QMetaObject::ReadProperty == c) {+        mKlass->properties()[2*id](this, a);+        id -= mKlass->mPropertyCount;+    }+    else if (QMetaObject::WriteProperty == c) {+        HsQMLUniformFunc uf = mKlass->properties()[2*id+1];+        if (uf) {+            uf(this, a);+        }+        id -= mKlass->mPropertyCount;+    }+    else if (QMetaObject::QueryPropertyDesignable == c ||+             QMetaObject::QueryPropertyScriptable == c ||+             QMetaObject::QueryPropertyStored == c ||+             QMetaObject::QueryPropertyEditable == c ||+             QMetaObject::QueryPropertyUser == c) {+        id -= mKlass->mPropertyCount;+    }+    return id;+}++HsQMLObjectProxy* HsQMLObject::proxy() const+{+    return mProxy;+}++extern "C" HsQMLObjectHandle* hsqml_create_object(+    HsStablePtr haskell, HsQMLClassHandle* kHndl)+{+    HsQMLObjectProxy* proxy = new HsQMLObjectProxy(haskell, (HsQMLClass*)kHndl);+    return (HsQMLObjectHandle*)proxy;+}++extern HsStablePtr hsqml_object_get_haskell(+    HsQMLObjectHandle* hndl)+{+    HsQMLObjectProxy* proxy = (HsQMLObjectProxy*)hndl;+    return proxy->haskell();+}++extern void* hsqml_object_get_pointer(+    HsQMLObjectHandle* hndl)+{+    HsQMLObjectProxy* proxy = (HsQMLObjectProxy*)hndl;+    return (void*)proxy->object();+}++extern HsQMLObjectHandle* hsqml_get_object_handle(+    void* ptr, HsQMLClassHandle* klassHndl)+{+    // Return NULL if the input pointer is NULL+    if (!ptr) {+        return NULL;+    }++    // Return NULL if the object type doesn't match any supplied class+    if (klassHndl) {+        HsQMLClass* klass = (HsQMLClass*)klassHndl;+        QObject* qObj = static_cast<QObject*>(ptr);+        if (0 != strcmp(+            qObj->metaObject()->className(),+            klass->mMetaObject.className())) {+            return NULL;+        }+    }++    // Return object proxy+    HsQMLObject* object = (HsQMLObject*)ptr;+    HsQMLObjectProxy* proxy = object->proxy();+    proxy->ref(HsQMLObjectProxy::Handle);+    return (HsQMLObjectHandle*)proxy;+}++extern void hsqml_finalise_object_handle(+    HsQMLObjectHandle* hndl)+{+    if (hndl) {+        HsQMLObjectProxy* proxy = (HsQMLObjectProxy*)hndl;+        proxy->deref(HsQMLObjectProxy::Handle);+    }+}
+ cbits/HsQMLObject.h view
@@ -0,0 +1,45 @@+#ifndef HSQML_OBJECT_H+#define HSQML_OBJECT_H++#include <QObject>++class HsQMLClass;+class HsQMLObject;++class HsQMLObjectProxy+{+public:+    HsQMLObjectProxy(HsStablePtr, HsQMLClass*);+    virtual ~HsQMLObjectProxy();+    HsStablePtr haskell() const;+    HsQMLClass* klass() const;+    HsQMLObject* object();+    void clearObject();+    enum RefSrc {Handle, Object};+    void ref(RefSrc);+    void deref(RefSrc);++private:+    HsStablePtr mHaskell;+    HsQMLClass* mKlass;+    HsQMLObject* mObject;+    QAtomicInt mRefCount;+};++class HsQMLObject : public QObject+{+public:+    HsQMLObject(HsQMLObjectProxy*);+    virtual ~HsQMLObject();+    virtual const QMetaObject* metaObject() const;+    virtual void* qt_metacast(const char*);+    virtual int qt_metacall(QMetaObject::Call, int, void**);+    HsQMLObjectProxy* proxy() const;++private:+    HsQMLObjectProxy* mProxy;+    HsStablePtr mHaskell;+    HsQMLClass* mKlass;+};++#endif /*HSQML_OBJECT_H*/
+ cbits/HsQMLWindow.cpp view
@@ -0,0 +1,104 @@+#include "hsqml.h"+#include "HsQMLManager.h"+#include "HsQMLWindow.h"++HsQMLWindow::HsQMLWindow(HsQMLEngine* engine)+    : QObject(engine)+    , mEngine(engine)+    , mContext(engine->engine())+    , mView(&mScene)+    , mComponent(NULL)+{+    // Setup context+    mContext.setContextProperty("window", this);++    // Don't delete on close+    mWindow.setAttribute(Qt::WA_DeleteOnClose, false);++    // Setup for QML performance+    mView.setOptimizationFlags(QGraphicsView::DontSavePainterState);+    mView.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);+    mScene.setItemIndexMethod(QGraphicsScene::NoIndex);++    // Setup for QML key handling+    mView.viewport()->setFocusPolicy(Qt::NoFocus);+    mView.setFocusPolicy(Qt::StrongFocus);+    mScene.setStickyFocus(true);++    mWindow.setCentralWidget(&mView);+}++HsQMLWindow::~HsQMLWindow()+{+}++QUrl HsQMLWindow::source() const+{+    return mSource;+}++void HsQMLWindow::setSource(const QUrl& url)+{+    mSource = url;+    if (mComponent) {+        delete mComponent;+        mComponent = NULL;+    }+    if (!mSource.isEmpty()) {+        mComponent = new QDeclarativeComponent(+            mEngine->engine(), mSource, this);+        if (mComponent->isLoading()) {+            QObject::connect(+                mComponent,+                SIGNAL(statusChanged(QDeclarativeComponent::Status)),+                this,+                SLOT(completeSetSource()));+        }+        else {+            completeSetSource();+        }+    }+}++void HsQMLWindow::completeSetSource()+{+    QObject::disconnect(+        mComponent, SIGNAL(statusChanged(QDeclarativeComponent::Status)),+        this, SLOT(completeSetSource()));+    QDeclarativeItem* item =+        qobject_cast<QDeclarativeItem*>(mComponent->create(&mContext));+    if (item) {+        mScene.addItem(item);+    }+}++QString HsQMLWindow::title() const+{+    return mWindow.windowTitle();+}++void HsQMLWindow::setTitle(const QString& title)+{+    mWindow.setWindowTitle(title);+}++bool HsQMLWindow::visible() const+{+    return mWindow.isVisible();+}++void HsQMLWindow::setVisible(bool visible)+{+    mWindow.setVisible(visible);+}++void HsQMLWindow::close()+{+    QMetaObject::invokeMethod(+        this, "completeClose", Qt::QueuedConnection);+}++void HsQMLWindow::completeClose()+{+    delete this;+}
+ cbits/HsQMLWindow.h view
@@ -0,0 +1,45 @@+#ifndef HSQML_WINDOW_H+#define HSQML_WINDOW_H++#include <QMainWindow>+#include <QDeclarativeContext>+#include <QDeclarativeItem>+#include <QGraphicsScene>+#include <QGraphicsView>+#include <QUrl>++#include "HsQMLManager.h"++class QDeclarativeComponent;++class HsQMLWindow : public QObject+{+    Q_OBJECT++public:+    HsQMLWindow(HsQMLEngine*);+    virtual ~HsQMLWindow();+    QUrl source() const;+    void setSource(const QUrl&);+    Q_PROPERTY(QUrl source READ source WRITE setSource);+    QString title() const;+    void setTitle(const QString&);+    Q_PROPERTY(QString title READ title WRITE setTitle);+    bool visible() const;+    void setVisible(bool);+    Q_PROPERTY(bool visible READ visible WRITE setVisible);+    Q_SCRIPTABLE void close();++private:+    Q_SLOT void completeSetSource();+    Q_SLOT void completeClose();+    HsQMLEngine* mEngine;+    QDeclarativeContext mContext;+    QMainWindow mWindow;+    QGraphicsScene mScene;+    QGraphicsView mView;+    QUrl mSource;+    QDeclarativeComponent* mComponent;+};++#endif /*HSQML_WINDOW_H*/
+ cbits/hsqml.h view
@@ -0,0 +1,99 @@+#ifndef HSQML_H+#define HSQML_H++#ifdef __cplusplus+extern "C" {+#endif++#include <stddef.h>+#include <HsFFI.h>++/* Manager */+extern void hsqml_init(+    void (*)(HsFunPtr),+    void (*)(HsStablePtr));++extern void hsqml_run();++extern void hsqml_set_debug_loglevel(int);++/* String */+typedef char HsQMLStringHandle;++typedef unsigned short UTF16;++extern size_t hsqml_get_string_size();++extern void hsqml_init_string(+    HsQMLStringHandle*);++extern void hsqml_deinit_string(+    HsQMLStringHandle*);++extern UTF16* hsqml_marshal_string(+    int, HsQMLStringHandle*);++extern int hsqml_unmarshal_string(+    HsQMLStringHandle*, UTF16**);++/* URL */+typedef char HsQMLUrlHandle;++extern size_t hsqml_get_url_size();++extern void hsqml_init_url(+    HsQMLUrlHandle*);++extern void hsqml_deinit_url(+    HsQMLUrlHandle*);++extern void hsqml_marshal_url(+    char*, int, HsQMLUrlHandle*);++extern int hsqml_unmarshal_url(+    HsQMLUrlHandle*, char**);++/* Class */+typedef char HsQMLClassHandle;++typedef void (*HsQMLUniformFunc)(void*, void**);++extern int hsqml_get_next_class_id();++extern HsQMLClassHandle* hsqml_create_class(+    unsigned int*, char*, HsQMLUniformFunc*, HsQMLUniformFunc*);++extern void hsqml_finalise_class_handle(+    HsQMLClassHandle* hndl);++/* Object */+typedef char HsQMLObjectHandle;++extern HsQMLObjectHandle* hsqml_create_object(+    HsStablePtr, HsQMLClassHandle*);++extern HsStablePtr hsqml_object_get_haskell(+    HsQMLObjectHandle*);++extern void* hsqml_object_get_pointer(+    HsQMLObjectHandle*);++extern HsQMLObjectHandle* hsqml_get_object_handle(+    void*, HsQMLClassHandle*);++extern void hsqml_finalise_object_handle(+    HsQMLObjectHandle*);++/* Engine */+extern void hsqml_create_engine(+    HsQMLObjectHandle*,+    HsQMLUrlHandle*,+    int,+    int,+    HsQMLStringHandle*);++#ifdef __cplusplus+}+#endif++#endif /*HSQML_H*/
+ hsqml.cabal view
@@ -0,0 +1,84 @@+Name:               hsqml+Version:            0.1.0+Cabal-version:      >= 1.10+Build-type:         Custom+License:            BSD3+License-file:       LICENSE+Copyright:          (c) 2010-2012 Robin KAY+Author:             Robin KAY+Maintainer:         komadori@gekkou.co.uk+Stability:          experimental+Homepage:           http://www.gekkou.co.uk/+Category:           Graphics+Synopsis:           Haskell binding for Qt Quick+Extra-source-files: cbits/*.cpp cbits/*.h+Description:+    A Haskell binding for Qt Quick.++    General documentation is present in the 'Graphics.QML' module.++Library+    Default-language: Haskell2010+    Build-depends:+        base         == 4.*,+        containers   == 0.4.*,+        filepath     == 1.3.*,+        network      == 2.3.*,+        text         == 0.11.*,+        tagged       == 0.4.*,+        transformers >= 0.2 && < 0.4+    Exposed-modules:+        Graphics.QML+        Graphics.QML.Debug+        Graphics.QML.Engine+        Graphics.QML.Marshal+        Graphics.QML.Objects+    Other-modules:+        Graphics.QML.Internal.Marshal+        Graphics.QML.Internal.Objects+        Graphics.QML.Internal.PrimValues+        Graphics.QML.Internal.Engine+    Hs-source-dirs: src+    Cc-options: -std=c++0x+    C-sources:+        cbits/HsQMLClass.cpp+        cbits/HsQMLEngine.cpp+        cbits/HsQMLIntrinsics.cpp+        cbits/HsQMLManager.cpp+        cbits/HsQMLObject.cpp+        cbits/HsQMLWindow.cpp+    Include-dirs: cbits+    X-moc-headers:+        cbits/HsQMLEngine.h+        cbits/HsQMLManager.h+        cbits/HsQMLWindow.h+    X-separate-cbits: True+    Build-tools: c2hs+    if os(windows)+        Include-dirs:+            /QT_ROOT/include+            /QT_ROOT/include/QtCore+            /QT_ROOT/include/QtGui+            /QT_ROOT/include/QtDeclarative+        Extra-libraries: QtCore4, QtGui4, QtDeclarative4, stdc+++        Extra-lib-dirs:+            /SYS_ROOT/bin+            /QT_ROOT/bin+    else+        Pkgconfig-depends:+            QtDeclarative >= 4.7++Test-Suite hsqml-test1+    Type: exitcode-stdio-1.0+    Default-language: Haskell2010+    Hs-source-dirs: test+    Main-is: Test1.hs+    Build-depends:+        base      == 4.*,+        directory == 1.1.*,+        network   == 2.3.*,+        hsqml     == 0.1.*++Source-repository head+    type:     darcs+    location: https://patch-tag.com/r/komadori/HsQML
+ src/Graphics/QML.hs view
@@ -0,0 +1,45 @@+-- | This module imports the entire package, except 'Graphics.QML.Debug'.+module Graphics.QML (+-- * Overview+{-|+HsQML layers a low to medium level Haskell API on top of the C++ Qt Quick+framework. It allows you to write graphical applications where the front-end is +written in Qt Quick's QML language (incorporating JavaScript) and the back-end+is written in Haskell. To this end, this library provides two pieces of+functionality:-++The 'Graphics.QML.Engine' module allows you to create windows which host QML+content. You can specify a custom global object to be made available to the+JavaScript running inside the content. In this way, the content can interface+with the Haskell program.++The 'Graphics.QML.Objects' module allows you to define your own custom object+types which can be marshalled between Haskell and JavaScript.+ -}+-- * Script-side APIs+{-|+The @window@ object provides the following methods and properties to QML+scripts.++/Properties/++[@source : url@] URL for the Window's QML document.++[@title : string@] Window title.++[@visible : bool@] Window visibility.++/Methods/++[@close()@] Closes the window.++ -}+-- * Graphics.QML+  module Graphics.QML.Engine,+  module Graphics.QML.Marshal,+  module Graphics.QML.Objects+) where++import Graphics.QML.Engine+import Graphics.QML.Marshal+import Graphics.QML.Objects
+ src/Graphics/QML/Debug.hs view
@@ -0,0 +1,11 @@+-- | Debug Options+module Graphics.QML.Debug (+    setDebugLogLevel+) where++import Graphics.QML.Internal.Engine++-- | Sets the global debug log level. At level zero, no logging information+-- will be printed. Higher levels will increase debug verbosity.+setDebugLogLevel :: Int -> IO ()+setDebugLogLevel = hsqmlSetDebugLoglevel
+ src/Graphics/QML/Engine.hs view
@@ -0,0 +1,111 @@+{-# LANGUAGE+    ExistentialQuantification,+    Rank2Types+  #-}++-- | Functions for starting QML engines, displaying content in a window.+module Graphics.QML.Engine (+  InitialWindowState(+    ShowWindow,+    ShowWindowWithTitle,+    HideWindow),+  EngineConfig(+    EngineConfig,+    initialURL,+    initialWindowState,+    contextObject),+  defaultEngineConfig,+  createEngine,+  runEngines,+  filePathToURI+) where++import Graphics.QML.Internal.Marshal+import Graphics.QML.Internal.Objects+import Graphics.QML.Internal.Engine+import Graphics.QML.Marshal+import Graphics.QML.Objects++import Data.List+import Data.Maybe+import Data.Typeable+import Foreign.Storable+import System.FilePath (isAbsolute, splitDirectories, pathSeparators)+import Network.URI (URI(URI), URIAuth(URIAuth), nullURI, uriPath)++-- | Specifies the intial state of the display window.+data InitialWindowState+  -- | A visible window should be created for the initial document with a+  -- default title.+  = ShowWindow+  -- | A visible window should be created for the initial document with the+  -- given title.+  | ShowWindowWithTitle String+  -- | A window should be created for the initial document, but it will remain+  -- hidden until made visible by the QML script.+  | HideWindow++-- | Holds parameters for configuring a QML runtime engine.+data EngineConfig a = EngineConfig {+  -- | URL for the first QML document to be loaded.+  initialURL         :: URI,+  -- | Window state for the initial QML document.+  initialWindowState :: InitialWindowState,+  -- | Context 'Object' made available to QML script code.+  contextObject      :: Maybe (ObjRef a)+}++-- | Default engine configuration. Loads @\"main.qml\"@ from the current+-- working directory into a visible window with no context object.+defaultEngineConfig :: EngineConfig a+defaultEngineConfig = EngineConfig {+  initialURL         = nullURI {uriPath = "main.qml"},+  initialWindowState = ShowWindow,+  contextObject      = Nothing+}++isWindowShown :: InitialWindowState -> Bool+isWindowShown ShowWindow = True+isWindowShown (ShowWindowWithTitle _) = True+isWindowShown HideWindow = False++getWindowTitle :: InitialWindowState -> Maybe String+getWindowTitle (ShowWindowWithTitle t) = Just t+getWindowTitle _ = Nothing++-- | Create a QML engine from a specification of its configuration.+createEngine :: (Object a) => EngineConfig a -> IO ()+createEngine config = do+  hsqmlInit+  let hndl = fmap (\(ObjRef h) -> h) $ contextObject config+      url = initialURL config+      state = initialWindowState config+      showWin = isWindowShown state+      maybeTitle = getWindowTitle state+      setTitle = isJust maybeTitle+      titleStr = fromMaybe "" maybeTitle+  mOutAlloc url $ \urlPtr -> do+    mOutAlloc titleStr $ \titlePtr -> do+      hsqmlCreateEngine hndl urlPtr showWin setTitle titlePtr++-- | Enters the Qt event loop and runs until all engines have terminated.+runEngines :: IO ()+runEngines = do+  hsqmlInit+  hsqmlRun++-- | Convenience function for converting local file paths into URIs.+filePathToURI :: FilePath -> URI+filePathToURI fp =+    let ds = splitDirectories fp+        abs = isAbsolute fp+        fixHead =+            (\cs -> if null cs then [] else '/':cs) .+            takeWhile (\c -> not $ c `elem` pathSeparators)+        mapHead _ [] = []+        mapHead f (x:xs) = f x : xs+        afp = intercalate "/" $ mapHead fixHead ds+        rfp = intercalate "/" ds+    in if abs+       then URI "file:" (Just $ URIAuth "" "" "") afp "" ""+       else URI "" Nothing rfp "" ""
+ src/Graphics/QML/Internal/Engine.chs view
@@ -0,0 +1,48 @@+{-# LANGUAGE+    ForeignFunctionInterface+  #-}+{-# OPTIONS_HADDOCK hide #-}++module Graphics.QML.Internal.Engine where++{#import Graphics.QML.Internal.Objects #}++import Foreign.C.Types+import Foreign.C.String+import Foreign.Marshal.Utils+import Foreign.Ptr+import Foreign.StablePtr++#include <HsFFI.h>++#include "hsqml.h"++type HsFreeFunPtr = FunPtr (FunPtr (IO ()) -> IO ())+foreign import ccall "HsFFI.h &hs_free_fun_ptr"+  hsFreeFunPtr :: HsFreeFunPtr++type HsFreeStablePtr = FunPtr (Ptr () -> IO ())+foreign import ccall "HsFFI.h &hs_free_stable_ptr"+  hsFreeStablePtr :: HsFreeStablePtr++{#fun unsafe hsqml_init as hsqmlInit_+  {id `HsFreeFunPtr',+   id `HsFreeStablePtr'} ->+  `()' #}++hsqmlInit :: IO ()+hsqmlInit = hsqmlInit_ hsFreeFunPtr hsFreeStablePtr++{#fun unsafe hsqml_create_engine as ^+  {withMaybeHsQMLObjectHandle* `Maybe HsQMLObjectHandle',+   castPtr `Ptr ()',+   fromBool `Bool',+   fromBool `Bool',+   castPtr `Ptr ()'} ->+  `()' #}++{#fun hsqml_run as ^ {} -> `()' #}++{#fun hsqml_set_debug_loglevel as ^+  {fromIntegral `Int'} -> `()'+  #}
+ src/Graphics/QML/Internal/Marshal.hs view
@@ -0,0 +1,63 @@+{-# LANGUAGE+    ScopedTypeVariables+  #-}++module Graphics.QML.Internal.Marshal where++import Control.Monad.Trans.Maybe+import Data.Maybe+import Data.Tagged+import Foreign.Ptr+import System.IO++-- | Represents a QML type name.+newtype TypeName = TypeName {+  typeName :: String+}++-- | The class 'MarshalIn' allows QML values to be converted into Haskell+-- values.+class MarshalIn a where+  mIn :: InMarshaller a++type ErrIO a = MaybeT IO a++runErrIO :: ErrIO a -> IO ()+runErrIO m = do+  r <- runMaybeT m+  if isNothing r+  then hPutStrLn stderr "Warning: Marshalling error."+  else return ()++errIO :: IO a -> ErrIO a+errIO = MaybeT . fmap Just++-- | Encapsulates the functionality to needed to implement an instance of+-- 'MarshalIn' so that such instances can be defined without access to+-- implementation details.+data InMarshaller a = InMarshaller {+  mInFuncFld :: Ptr () -> ErrIO a,+  mIOTypeFld :: Tagged a TypeName+}++mInFunc :: (MarshalIn a) => Ptr () -> ErrIO a+mInFunc = mInFuncFld mIn ++mIOType :: (MarshalIn a) => Tagged a TypeName+mIOType = mIOTypeFld mIn++-- | The class 'MarshalOut' allows Haskell values to be converted into QML+-- values.+class (MarshalIn a) => MarshalOut a where+  mOutFunc  :: Ptr () -> a -> IO ()+  mOutAlloc :: a -> (Ptr () -> IO b) -> IO b++instance MarshalOut () where+  mOutFunc _ _ = return ()+  mOutAlloc _ f = f nullPtr++instance MarshalIn () where+  mIn = InMarshaller {+    mInFuncFld = \_ -> return (),+    mIOTypeFld = Tagged $ TypeName ""+  }
+ src/Graphics/QML/Internal/Objects.chs view
@@ -0,0 +1,143 @@+{-# LANGUAGE+    ForeignFunctionInterface+  #-}++module Graphics.QML.Internal.Objects where++import Foreign.C.Types+import Foreign.Ptr+import Foreign.ForeignPtr.Safe+import Foreign.ForeignPtr.Unsafe+import Foreign.StablePtr++#include "hsqml.h"++{#fun unsafe hsqml_get_next_class_id as ^+  {} ->+  `CInt' id #}++type UniformFunc = Ptr () -> Ptr (Ptr ()) -> IO ()++foreign import ccall "wrapper"  +  marshalFunc :: UniformFunc -> IO (FunPtr UniformFunc)++{#pointer *HsQMLClassHandle as ^ foreign newtype #}++foreign import ccall "hsqml.h &hsqml_finalise_class_handle"+  hsqmlFinaliseClassHandlePtr :: FunPtr (Ptr (HsQMLClassHandle) -> IO ())++newClassHandle :: Ptr HsQMLClassHandle -> IO (Maybe HsQMLClassHandle)+newClassHandle p =+  if nullPtr == p+    then return Nothing+    else do+      fp <- newForeignPtr hsqmlFinaliseClassHandlePtr p+      return $ Just $ HsQMLClassHandle fp++{#fun unsafe hsqml_create_class as ^+  {id `Ptr CUInt',+   id `Ptr CChar',+   id `Ptr (FunPtr UniformFunc)',+   id `Ptr (FunPtr UniformFunc)'} ->+  `Maybe HsQMLClassHandle' newClassHandle* #}++withMaybeHsQMLClassHandle ::+    Maybe HsQMLClassHandle -> (Ptr HsQMLClassHandle -> IO b) -> IO b+withMaybeHsQMLClassHandle (Just (HsQMLClassHandle fp)) = withForeignPtr fp+withMaybeHsQMLClassHandle Nothing = \f -> f nullPtr++withMaybeHsQMLObjectHandle ::+    Maybe HsQMLObjectHandle -> (Ptr HsQMLObjectHandle -> IO b) -> IO b+withMaybeHsQMLObjectHandle (Just (HsQMLObjectHandle fp)) = withForeignPtr fp+withMaybeHsQMLObjectHandle Nothing = \f -> f nullPtr++{#pointer *HsQMLObjectHandle as ^ foreign newtype #}++foreign import ccall "hsqml.h &hsqml_finalise_object_handle"+  hsqmlFinaliseObjectHandlePtr :: FunPtr (Ptr (HsQMLObjectHandle) -> IO ())++newObjectHandle :: Ptr HsQMLObjectHandle -> IO HsQMLObjectHandle+newObjectHandle p = do+  fp <- newForeignPtr hsqmlFinaliseObjectHandlePtr p+  return $ HsQMLObjectHandle fp++isNullObjectHandle :: HsQMLObjectHandle -> Bool+isNullObjectHandle (HsQMLObjectHandle fp) =+  nullPtr == unsafeForeignPtrToPtr fp++-- | Represents an instance of the QML class which wraps the type @tt@.+data ObjRef tt = ObjRef {+  objHndl :: HsQMLObjectHandle+}++objToPtr :: a -> (Ptr () -> IO b) -> IO b+objToPtr obj f = do+  sPtr <- newStablePtr obj+  res <- f $ castStablePtrToPtr sPtr+  return res++{#fun unsafe hsqml_create_object as ^+  {objToPtr* `a',+   withHsQMLClassHandle* `HsQMLClassHandle'} ->+  `HsQMLObjectHandle' newObjectHandle* #}++ptrToObj :: Ptr () -> IO a+ptrToObj =+  deRefStablePtr . castPtrToStablePtr++{#fun unsafe hsqml_object_get_haskell as ^+  {withHsQMLObjectHandle* `HsQMLObjectHandle'} ->+  `a' ptrToObj* #}++{#fun unsafe hsqml_object_get_pointer as ^+  {withHsQMLObjectHandle* `HsQMLObjectHandle'} ->+  `Ptr ()' id #}++{#fun unsafe hsqml_get_object_handle as ^+  {id `Ptr ()',+   withMaybeHsQMLClassHandle* `Maybe HsQMLClassHandle'} ->+  `HsQMLObjectHandle' newObjectHandle* #}++ofDynamicMetaObject :: CUInt+ofDynamicMetaObject = 0x01++mfAccessPrivate, mfAccessProtected, mfAccessPublic, mfAccessMask,+  mfMethodMethod, mfMethodSignal, mfMethodSlot, mfMethodConstructor,+  mfMethodTypeMask, mfMethodCompatibility, mfMethodCloned, mfMethodScriptable+  :: CUInt+mfAccessPrivate   = 0x00+mfAccessProtected = 0x01+mfAccessPublic    = 0x02+mfAccessMask      = 0x03+mfMethodMethod      = 0x00+mfMethodSignal      = 0x04+mfMethodSlot        = 0x08+mfMethodConstructor = 0x0c+mfMethodTypeMask    = 0x0c+mfMethodCompatibility = 0x10+mfMethodCloned        = 0x20+mfMethodScriptable    = 0x40++pfInvalid, pfReadable, pfWritable, pfResettable, pfEnumOrFlag, pfStdCppSet,+  pfConstant, pfFinal, pfDesignable, pfResolveDesignable, pfScriptable,+  pfResolveScriptable, pfStored, pfResolveStored, pfEditable,+  pfResolveEditable, pfUser, pfResolveUser, pfNotify :: CUInt+pfInvalid           = 0x00000000+pfReadable          = 0x00000001+pfWritable          = 0x00000002+pfResettable        = 0x00000004+pfEnumOrFlag        = 0x00000008+pfStdCppSet         = 0x00000100+pfConstant          = 0x00000400+pfFinal             = 0x00000800+pfDesignable        = 0x00001000+pfResolveDesignable = 0x00002000+pfScriptable        = 0x00004000+pfResolveScriptable = 0x00008000+pfStored            = 0x00010000+pfResolveStored     = 0x00020000+pfEditable          = 0x00040000+pfResolveEditable   = 0x00080000+pfUser              = 0x00100000+pfResolveUser       = 0x00200000+pfNotify            = 0x00400000
+ src/Graphics/QML/Internal/PrimValues.chs view
@@ -0,0 +1,82 @@+{-# LANGUAGE+    ForeignFunctionInterface+  #-}++module Graphics.QML.Internal.PrimValues where++import Foreign.C.Types+import Foreign.C.String+import Foreign.Ptr+import Foreign.ForeignPtr+import Foreign.Storable+import Foreign.Marshal.Alloc+import Foreign.Marshal.Array+import System.IO.Unsafe++#include "hsqml.h"++cIntConv :: (Integral a, Integral b) => a -> b+cIntConv = fromIntegral++--+-- String+--++{#pointer *HsQMLStringHandle as ^ newtype #}++{#fun unsafe hsqml_get_string_size as ^+  {} ->+  `Int' fromIntegral #}++hsqmlStringSize :: Int+hsqmlStringSize = unsafePerformIO $ hsqmlGetStringSize++{#fun unsafe hsqml_init_string as ^+  {id `HsQMLStringHandle'} ->+  `()' #}++{#fun unsafe hsqml_deinit_string as ^+  {id `HsQMLStringHandle'} ->+  `()' #}++{#fun unsafe hsqml_marshal_string as ^+  {`Int',+   id `HsQMLStringHandle'} ->+  `Ptr CUShort' id #}++{#fun unsafe hsqml_unmarshal_string as ^+  {id `HsQMLStringHandle',+   id `Ptr (Ptr CUShort)'} ->+  `Int' #}++--+-- URL+--++{#pointer *HsQMLUrlHandle as ^ newtype #}++{#fun unsafe hsqml_get_url_size as ^+  {} ->+  `Int' fromIntegral #}++hsqmlUrlSize :: Int+hsqmlUrlSize = unsafePerformIO $ hsqmlGetUrlSize++{#fun unsafe hsqml_init_url as ^+  {id `HsQMLUrlHandle'} ->+  `()' #}++{#fun unsafe hsqml_deinit_url as ^+  {id `HsQMLUrlHandle'} ->+  `()' #}++{#fun unsafe hsqml_marshal_url as ^+  {id `Ptr CChar',+   `Int',+   id `HsQMLUrlHandle'} ->+  `()' #}++{#fun unsafe hsqml_unmarshal_url as ^+  {id `HsQMLUrlHandle',+   id `Ptr (Ptr CChar)'} ->+  `Int' #}
+ src/Graphics/QML/Marshal.hs view
@@ -0,0 +1,155 @@+{-# LANGUAGE+    ScopedTypeVariables,+    TypeSynonymInstances,+    FlexibleInstances+  #-}++-- | Type classs and instances for marshalling values between Haskell and QML.+module Graphics.QML.Marshal (+  MarshalIn (+    mIn),+  InMarshaller,+  MarshalOut+) where++import Graphics.QML.Internal.Marshal+import Graphics.QML.Internal.PrimValues++import Data.Maybe+import Data.Tagged+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+import Network.URI (+    URI (URI), URIAuth (URIAuth),+    parseURIReference, unEscapeString,+    uriToString, escapeURIString, nullURI,+    isUnescapedInURI)++--+-- Int/int built-in type+--++instance MarshalOut Int where+  mOutFunc ptr int =+    poke (castPtr ptr :: Ptr CInt) (fromIntegral int)+  mOutAlloc num f =+    alloca $ \(ptr :: Ptr CInt) ->+      mOutFunc (castPtr ptr) num >> f (castPtr ptr)++instance MarshalIn Int where+  mIn = InMarshaller {+    mInFuncFld = \ptr ->+      errIO $ peek (castPtr ptr :: Ptr CInt) >>= return . fromIntegral,+    mIOTypeFld = Tagged $ TypeName "int"+  }++--+-- Double/double built-in type+--++instance MarshalOut Double where+  mOutFunc ptr num =+    poke (castPtr ptr :: Ptr CDouble) (realToFrac num)+  mOutAlloc num f =+    alloca $ \(ptr :: Ptr CDouble) ->+      mOutFunc (castPtr ptr) num >> f (castPtr ptr)++instance MarshalIn Double where+  mIn = InMarshaller {+    mInFuncFld = \ptr ->+      errIO $ peek (castPtr ptr :: Ptr CDouble) >>= return . realToFrac,+    mIOTypeFld = Tagged $ TypeName "double"+  }++--+-- Text/QString built-in type+--++instance MarshalOut Text where+  mOutFunc ptr txt = do+    array <- hsqmlMarshalString+        (T.lengthWord16 txt) (HsQMLStringHandle $ castPtr ptr)+    T.unsafeCopyToPtr txt (castPtr array)+  mOutAlloc txt f =+    allocaBytes hsqmlStringSize $ \ptr -> do+      hsqmlInitString $ HsQMLStringHandle ptr+      mOutFunc (castPtr ptr) txt+      ret <- f (castPtr ptr)+      hsqmlDeinitString $ HsQMLStringHandle ptr+      return ret++instance MarshalIn Text where+  mIn = InMarshaller {+    mInFuncFld = \ptr -> errIO $ do+      pair <- alloca (\bufPtr -> do+        len <- hsqmlUnmarshalString (HsQMLStringHandle $ castPtr ptr) bufPtr+        buf <- peek bufPtr+        return (castPtr buf, fromIntegral len))+      uncurry T.fromPtr pair,+    mIOTypeFld = Tagged $ TypeName "QString"+  }++--+-- String/QString built-in type+--++instance MarshalOut String where+  mOutFunc ptr str = mOutFunc ptr $ T.pack str+  mOutAlloc txt f =+    allocaBytes hsqmlStringSize $ \ptr -> do+      hsqmlInitString $ HsQMLStringHandle ptr+      mOutFunc (castPtr ptr) txt+      ret <- f (castPtr ptr)+      hsqmlDeinitString $ HsQMLStringHandle ptr+      return ret++instance MarshalIn String where+  mIn = InMarshaller {+    mInFuncFld = fmap T.unpack . mInFuncFld mIn,+    mIOTypeFld = Tagged $ TypeName "QString"+  }++--+-- URI/QUrl built-in type+--++mapURIStrings :: (String -> String) -> URI -> URI+mapURIStrings f (URI scheme auth path query frag) =+    URI (f scheme) (mapAuth auth) (f path) (f query) (f frag)+    where mapAuth (Just (URIAuth user name port)) =+              Just $ URIAuth (f user) (f name) (f port)+          mapAuth Nothing = Nothing++instance MarshalOut URI where+  mOutFunc ptr uri =+    let str = uriToString id (mapURIStrings+                (escapeURIString isUnescapedInURI) uri) ""+    in withCStringLen str (\(buf, bufLen) ->+         hsqmlMarshalUrl buf bufLen (HsQMLUrlHandle $ castPtr ptr))+  mOutAlloc uri f =+    allocaBytes hsqmlUrlSize $ \ptr -> do+      hsqmlInitUrl $ HsQMLUrlHandle ptr+      mOutFunc (castPtr ptr) uri+      ret <- f (castPtr ptr)+      hsqmlDeinitUrl $ HsQMLUrlHandle ptr+      return ret++instance MarshalIn URI where+  mIn = InMarshaller {+    mInFuncFld = \ptr -> errIO $ do+      pair <- alloca (\bufPtr -> do+        len <- hsqmlUnmarshalUrl (HsQMLUrlHandle $ castPtr ptr) bufPtr+        buf <- peek bufPtr+        return (castPtr buf, fromIntegral len))+      str <- peekCStringLen pair+      free $ fst pair+      return $ mapURIStrings unEscapeString $+        fromMaybe nullURI $ parseURIReference str,+    mIOTypeFld = Tagged $ TypeName "QUrl"+  }
+ src/Graphics/QML/Objects.hs view
@@ -0,0 +1,450 @@+{-# LANGUAGE+    ScopedTypeVariables,+    TypeFamilies,+    FlexibleContexts+  #-}++-- | Facilities for defining new object types which can be marshalled between+-- Haskell and QML.+module Graphics.QML.Objects (+  -- * Class Definition+  Object (+    classDef),+  ClassDef,+  Member,+  defClass,++  -- * Methods+  defMethod,+  MethodSuffix,++  -- * Properties+  defPropertyRO,+  defPropertyRW,++  -- * Object References+  ObjRef,+  newObject,+  fromObjRef,++  -- * Marshalling Type-classes+  objectInMarshaller,+  MarshalThis (+    type ThisObj,+    mThis),+  objectThisMarshaller+) where++import Graphics.QML.Internal.Marshal+import Graphics.QML.Internal.Objects+import Graphics.QML.Internal.Engine++import Control.Monad+import Control.Monad.Trans.Maybe+import Control.Monad.Trans.State+import Data.Bits+import Data.Char+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Maybe+import Data.Tagged+import Data.Typeable+import Foreign.C.Types+import Foreign.C.String+import Foreign.Ptr+import Foreign.Storable+import Foreign.Marshal.Alloc+import Foreign.Marshal.Array+import System.IO.Unsafe+import Numeric++--+-- ObjRef+--++-- | The class 'MarshalThis' allows objects to be marshalled back into+-- Haskell as the \"this\" value for callbacks.+class (Object (ThisObj tt)) => MarshalThis tt where+  type ThisObj tt+  mThis :: ThisMarshaller tt++-- | Encapsulates the functionality to needed to implement an instance of+-- 'MarshalThis' so that such instances can be defined without access to+-- implementation details.+data ThisMarshaller tt = ThisMarshaller {+  mThisFuncFld :: Ptr () -> IO tt+}++mThisFunc :: (MarshalThis tt) => Ptr () -> IO tt+mThisFunc = mThisFuncFld mThis++instance (Object tt) => MarshalOut (ObjRef tt) where+  mOutFunc ptr obj = do+    objPtr <- hsqmlObjectGetPointer $ objHndl obj+    poke (castPtr ptr) objPtr+  mOutAlloc obj f =+    alloca $ \(ptr :: Ptr (Ptr ())) ->+      mOutFunc (castPtr ptr) obj >> f (castPtr ptr)++instance (Object tt) => MarshalIn (ObjRef tt) where+  mIn = InMarshaller {+    mInFuncFld = \ptr -> MaybeT $ do+      objPtr <- peek (castPtr ptr)+      hndl <- hsqmlGetObjectHandle objPtr $+        Just $ classHndl (classDefCAF :: ClassDef tt)+      return $ if isNullObjectHandle hndl+        then Nothing else Just $ ObjRef hndl,+    mIOTypeFld = Tagged $ TypeName "QObject*"+  }++instance (Object tt) => MarshalThis (ObjRef tt) where+  type ThisObj (ObjRef tt) = tt+  mThis = ThisMarshaller {+      mThisFuncFld = \ptr -> do+      hndl <- hsqmlGetObjectHandle ptr Nothing+      return $ ObjRef hndl+  }++retagType :: Tagged (ObjRef tt) TypeName -> Tagged tt TypeName+retagType = retag++-- | Provides an 'InMarshaller' which allows you to define instances of+-- 'MarshalIn' for custom object types. For example:+--+-- @+-- instance MarshalIn MyObjectType where+--     mIn = objectInMarshaller+-- @+--+-- This instance would allow @MyObjectType@ to be used as a parameter type+-- in callbacks. An instance is provided for @'ObjRef' MyObjectType@ by+-- default.+objectInMarshaller :: (Object tt) => InMarshaller tt+objectInMarshaller =+  InMarshaller {+    mInFuncFld = fmap fromObjRef . mInFunc,+    mIOTypeFld = retagType mIOType+  }++-- | Provides an 'ThisMarshaller' which allows you to define instances of+-- 'MarshalThis' for custom object types. For example:+--+-- @+-- instance MarshalThis MyObjectType where+--     type (ThisObj MyObjectType) = MyObjectType+--     mIn = objectInMarshaller+-- @+--+-- This instance would allow @MyObjectType@ to be used as the \"this\" type+-- for callbacks. An instance is provided for @'ObjRef' MyObjectType@ by+-- default.+objectThisMarshaller :: (Object tt, (ThisObj tt) ~ tt) => ThisMarshaller tt+objectThisMarshaller =+  ThisMarshaller {+    mThisFuncFld = fmap fromObjRef . mThisFunc+  }++-- | Creates an instance of a QML class given a value of the underlying Haskell +-- type @tt@.+newObject :: forall tt. (Object tt) => tt -> IO (ObjRef tt)+newObject obj = do+  hndl <- hsqmlCreateObject obj $ classHndl (classDefCAF :: ClassDef tt)+  return $ ObjRef hndl++-- | Returns the associated value of the underlying Haskell type @tt@ from an+-- instance of the QML class which wraps it.+fromObjRef :: ObjRef tt -> tt+fromObjRef =+    unsafePerformIO . hsqmlObjectGetHaskell . objHndl++--+-- Object+--++-- | The class 'Object' allows Haskell types to expose an object-oriented+-- interface to QML. +class (Typeable tt) => Object tt where+  classDef :: ClassDef tt++-- | Uninlinable version of classDef to try and ensure that class definitions+-- get stored as constant applicable forms.+{-# NOINLINE classDefCAF #-}+classDefCAF :: (Object tt) => ClassDef tt+classDefCAF = classDef++--+-- ClassDef+--++-- | Represents the API of the QML class which wraps the type @tt@.+data ClassDef tt = ClassDef {+  classType :: TypeName,+  classHndl :: HsQMLClassHandle+}++-- | Generates a 'ClassDef' from a list of 'Member's.+defClass :: forall tt. (Object tt) => [Member tt] -> ClassDef tt+defClass ms = unsafePerformIO $ do+  let typ  = typeOf (undefined :: tt)+      con  = typeRepTyCon typ+      name = showString (tyConModule con) $ showChar '.' $ tyConName con+  id <- hsqmlGetNextClassId+  createClass (showString name $ showChar '_' $ showInt id "") ms++createClass :: forall tt. (Object tt) =>+  String -> [Member tt] -> IO (ClassDef tt)+createClass name ms = do+  let methods = methodMembers ms+      properties = propertyMembers ms+      (MOCOutput metaData metaStrData) = compileClass name methods properties+  metaDataPtr <- newArray metaData+  metaStrDataPtr <- newArray metaStrData+  methodsPtr <- mapM (marshalFunc . methodFunc) methods >>= newArray+  pReads <- mapM (marshalFunc . propertyReadFunc) properties+  pWrites <- mapM (fromMaybe (return nullFunPtr) . fmap marshalFunc .+    propertyWriteFunc) properties+  propertiesPtr <- newArray $ interleave pReads pWrites+  hsqmlInit+  hndl <- hsqmlCreateClass metaDataPtr metaStrDataPtr methodsPtr propertiesPtr+  return $ case hndl of +    Just hndl' -> ClassDef (TypeName name) hndl'+    Nothing    -> error ("Failed to create QML class '"++name++"'.")++interleave :: [a] -> [a] -> [a]+interleave [] ys = ys+interleave (x:xs) ys = x : ys `interleave` xs ++--+-- Member+--++-- | Represents a named member of the QML class which wraps type @tt@.+data Member tt+  -- | Constructs a 'Member' from a 'Method'.+  = MethodMember (Method tt)+  -- | Constructs a 'Member' from a 'Property'.+  | PropertyMember (Property tt)++-- | Returns the methods in a list of members.+methodMembers :: [Member tt] -> [Method tt]+methodMembers = mapMaybe f+  where f (MethodMember m) = Just m+        f _ = Nothing++-- | Returns the properties in a list of members.+propertyMembers :: [Member tt] -> [Property tt]+propertyMembers = mapMaybe f+  where f (PropertyMember m) = Just m+        f _ = Nothing++--+-- Method+--++-- | Represents a named method which can be invoked from QML on an object of+-- type @tt@.+data Method tt = Method {+  -- | Gets the name of a 'Method'.+  methodName  :: String,+  -- | Gets the 'TypeName's which comprise the signature of a 'Method'.+  -- The head of the list is the return type and the tail the arguments.+  methodTypes :: [TypeName],+  methodFunc  :: UniformFunc+}++data CrudeMethodTypes = CrudeMethodTypes {+    methodParamTypes :: [TypeName],+    methodReturnType :: TypeName+  }++crudeTypesToList :: CrudeMethodTypes -> [TypeName]+crudeTypesToList (CrudeMethodTypes p r) = r:p++-- | Supports marshalling Haskell functions with an arbitrary number of+-- arguments.+class MethodSuffix a where+  mkMethodFunc  :: Int -> a -> Ptr (Ptr ()) -> ErrIO ()+  mkMethodTypes :: Tagged a CrudeMethodTypes++instance (MarshalIn a, MethodSuffix b) => MethodSuffix (a -> b) where+  mkMethodFunc n f pv = do+    ptr <- errIO $ peekElemOff pv n+    val <- mInFunc ptr+    mkMethodFunc (n+1) (f val) pv+    return ()+  mkMethodTypes =+    let (CrudeMethodTypes p r) =+          untag (mkMethodTypes :: Tagged b CrudeMethodTypes)+        ty = untag (mIOType :: Tagged a TypeName)+    in Tagged $ CrudeMethodTypes (ty:p) r++instance (MarshalOut a) => MethodSuffix (IO a) where+  mkMethodFunc _ f pv = errIO $ do+    ptr <- peekElemOff pv 0+    val <- f+    if nullPtr == ptr+    then return ()+    else mOutFunc ptr val+  mkMethodTypes =+    let ty = untag (mIOType :: Tagged a TypeName)+    in Tagged $ CrudeMethodTypes [] ty++mkUniformFunc :: forall tt ms. (MarshalThis tt, MethodSuffix ms) =>+  (tt -> ms) -> UniformFunc+mkUniformFunc f = \pt pv -> do+  this <- mThisFunc pt+  runErrIO $ mkMethodFunc 1 (f this) pv++-- | Defines a named method using a function @f@ in the IO monad.+--+-- The first argument to @f@ receives the \"this\" object and hence must match+-- the type of the class on which the method is being defined. Subsequently,+-- there may be zero or more parameter arguments followed by an optional return+-- argument in the IO monad. These argument types must be members of the+-- 'MarshalThis', 'MarshalIn', and 'MarshalOut' typeclasses respectively.++defMethod ::+  forall tt ms. (MarshalThis tt, MethodSuffix ms) =>+  String -> (tt -> ms) -> Member (ThisObj tt)+defMethod name f = MethodMember $ Method name+  (crudeTypesToList $ untag (mkMethodTypes :: Tagged ms CrudeMethodTypes))+  (mkUniformFunc f)++--+-- Property+--++-- | Represents a named property which can be accessed from QML on an object+-- of type @tt@.+data Property tt = Property {+  -- | Gets the name of a 'Property'.+  propertyName :: String,+  propertyType :: TypeName,+  propertyReadFunc :: UniformFunc,+  propertyWriteFunc :: Maybe UniformFunc+}++-- | Defines a named read-only property using an accessor function in the IO+-- monad.+defPropertyRO ::+  forall tt tr. (MarshalThis tt, MarshalOut tr) =>+  String -> (tt -> IO tr) -> Member (ThisObj tt)+defPropertyRO name g = PropertyMember $ Property name+  (untag (mIOType :: Tagged tr TypeName))+  (mkUniformFunc g)+  Nothing++-- | Defines a named read-write property using a pair of accessor and mutator+-- functions in the IO monad.+defPropertyRW ::+  forall tt tr. (MarshalThis tt, MarshalOut tr) =>+  String -> (tt -> IO tr) -> (tt -> tr -> IO ()) -> Member (ThisObj tt)+defPropertyRW name g s = PropertyMember $ Property name+  (untag (mIOType :: Tagged tr TypeName))+  (mkUniformFunc g)+  (Just $ mkUniformFunc s)++--+-- Meta Object Compiler+--++data MOCState = MOCState {+  mData            :: [CUInt],+  mDataLen         :: Int,+  mDataMethodsIdx  :: Maybe Int,+  mDataPropsIdx    :: Maybe Int,+  mStrData         :: [CChar],+  mStrDataLen      :: Int,+  mStrDataMap      :: Map String CUInt+} deriving Show++data MOCOutput = MOCOutput [CUInt] [CChar]++newMOCState :: MOCState+newMOCState = MOCState [] 0 Nothing Nothing [] 0 Map.empty++writeInt :: CUInt -> State MOCState ()+writeInt int = do+  state <- get+  let md    = mData state+      mdLen = mDataLen state+  put $ state {mData = int:md, mDataLen = mdLen+1}+  return ()++writeString :: String -> State MOCState ()+writeString str = do+  state <- get+  let msd    = mStrData state+      msdLen = mStrDataLen state+      msdMap = mStrDataMap state+  case (Map.lookup str msdMap) of+    Just idx -> writeInt idx+    Nothing  -> do+      let idx = fromIntegral msdLen+          msd' = 0 : (map castCharToCChar (reverse str) ++ msd)+          msdLen' = msdLen + length str + 1+          msdMap' = Map.insert str idx msdMap+      put $ state {+        mStrData = msd',+        mStrDataLen = msdLen',+        mStrDataMap = msdMap'}+      writeInt idx++writeMethod :: Method tt -> State MOCState ()+writeMethod m = do+  idx <- get >>= return . mDataLen+  writeString $ methodSignature m+  writeString $ methodParameters m+  writeString $ typeName $ head $ methodTypes m+  writeString ""+  writeInt (mfAccessPublic .|. mfMethodScriptable)+  state <- get+  put $ state {mDataMethodsIdx = mplus (mDataMethodsIdx state) (Just idx)}+  return ()++writeProperty :: Property tt -> State MOCState ()+writeProperty p = do+  idx <- get >>= return . mDataLen+  writeString $ propertyName p+  writeString $ typeName $ propertyType p+  writeInt (pfReadable .|. pfScriptable .|.+    if (isJust $ propertyWriteFunc p) then pfWritable else 0)+  state <- get+  put $ state {mDataPropsIdx = mplus (mDataPropsIdx state) (Just idx)}+  return ()++compileClass :: String -> [Method tt] -> [Property tt] -> MOCOutput+compileClass name ms ps = +  let enc = flip execState newMOCState $ do+        writeInt 5                           -- Revision+        writeString name                     -- Class name+        writeInt 0 >> writeInt 0             -- Class info+        writeInt $ fromIntegral $ length ms  -- Methods+        writeInt $ fromIntegral $+          fromMaybe 0 $ mDataMethodsIdx enc  -- Methods (data index)+        writeInt $ fromIntegral $ length ps  -- Properties+        writeInt $ fromIntegral $+          fromMaybe 0 $ mDataPropsIdx enc    -- Properties (data index)+        writeInt 0 >> writeInt 0             -- Enums+        writeInt 0 >> writeInt 0             -- Constructors+        writeInt 0                           -- Flags+        writeInt 0                           -- Signals        +        mapM_ writeMethod ms+        mapM_ writeProperty ps+        writeInt 0+  in MOCOutput (reverse $ mData enc) (reverse $ mStrData enc)++foldr0 :: (a -> a -> a) -> a -> [a] -> a+foldr0 _ x [] = x+foldr0 f _ xs = foldr1 f xs++methodSignature :: Method tt -> String+methodSignature method =+  let paramTypes = tail $ methodTypes method+  in (showString (methodName method) . showChar '(' .+       foldr0 (\l r -> l . showChar ',' . r) id+         (map (showString . typeName) paramTypes) . showChar ')') ""++methodParameters :: Method tt -> String+methodParameters method =+  replicate (flip (-) 2 $ length $ methodTypes method) ','
+ test/Test1.hs view
@@ -0,0 +1,148 @@+{-# LANGUAGE DeriveDataTypeable #-}++module Main where++import Graphics.QML+import Control.Monad+import Data.IORef+import Data.List+import Data.Maybe+import Data.Typeable+import Network.URI+import System.Exit+import System.IO+import System.Directory++data HarnessObject = HarnessObject {+    globalState :: IORef [String]+} deriving Typeable++markTaskComplete :: HarnessObject -> String -> IO ()+markTaskComplete go task = do+    let ref = globalState go+    list <- readIORef ref+    if elem task list+    then do+        putStrLn (+            showString "Completed task '" .+            showString task $ showString "'." [])+        writeIORef ref $ delete task list+    else do+        putStrLn (showString "Unexpected task '" .+            showString task $ showString "' completed." [])+        writeIORef ref $ ("unexpected_" ++ task) : list++testInt :: Int+testInt = 8888++testDouble :: Double+testDouble = 0.25++testString :: String+testString = "我係QuickBrownFox"++testURI :: URI+testURI =+    URI "http:" (Just $ URIAuth "" "www.example.com" "")+        "/with space" "" "#test"++data TestObject = TestObject deriving (Eq, Typeable)++instance Object TestObject where+    classDef = defClass []++instance MarshalIn TestObject where+    mIn = objectInMarshaller++instance Object HarnessObject where+    classDef = defClass [+        defMethod "getInt" (\go -> do+            markTaskComplete (fromObjRef go) "getInt"+            return testInt),+        defMethod "setInt" (\go i -> do+            markTaskComplete (fromObjRef go) "setInt"+            if (i == testInt)+            then markTaskComplete (fromObjRef go) "setIntCorrect"+            else return ()),+        defMethod "getDouble" (\go -> do+            markTaskComplete (fromObjRef go) "getDouble"+            return testDouble),+        defMethod "setDouble" (\go i -> do+            markTaskComplete (fromObjRef go) "setDouble"+            if (i == testDouble)+            then markTaskComplete (fromObjRef go) "setDoubleCorrect"+            else return ()),+        defMethod "getString" (\go -> do+            markTaskComplete (fromObjRef go) "getString"+            return testString),+        defMethod "setString" (\go i -> do+            markTaskComplete (fromObjRef go) "setString"+            if (i == testString)+            then markTaskComplete (fromObjRef go) "setStringCorrect"+            else return ()),+        defMethod "getURI" (\go -> do+            markTaskComplete (fromObjRef go) "getURI"+            return testURI),+        defMethod "setURI" (\go i -> do+            markTaskComplete (fromObjRef go) "setURI"+            if (i == testURI)+            then markTaskComplete (fromObjRef go) "setURICorrect"+            else return ()),+        defMethod "getObject" (\go -> do+            markTaskComplete (fromObjRef go) "getObject"+            newObject TestObject),+        defMethod "setObject" (\go i -> do+            markTaskComplete (fromObjRef go) "setObject"+            if (i == TestObject)+            then markTaskComplete (fromObjRef go) "setObjectCorrect"+            else return ())]++testScript :: String+testScript = unlines [+    "import Qt 4.7",+    "Rectangle {",+    "    id: page;",+    "    width: 100; height: 100;",+    "    color: 'green';",+    "    Component.onCompleted: {",+    "        var intVar = getInt();",+    "        setInt(intVar);",+    "        var dblVar = getDouble();",+    "        setDouble(dblVar);",+    "        var strVar = getString();",+    "        setString(strVar);",+    "        var uriVar = getURI();",+    "        setURI(uriVar);",+    "        var objVar = getObject();",+    "        setObject(objVar);",+    "        window.close();",+    "    }",+    "}"]++testTasks :: [String]+testTasks = [+    "getInt", "setInt", "setIntCorrect",+    "getDouble", "setDouble", "setDoubleCorrect",+    "getString", "setString", "setStringCorrect",+    "getURI", "setURI", "setURICorrect",+    "getObject", "setObject", "setObjectCorrect"]++main :: IO ()+main = do+    tmpDir <- getTemporaryDirectory+    (qmlPath, hndl) <- openTempFile tmpDir "test1-.qml"+    hPutStr hndl testScript+    hClose hndl+    list <- newIORef testTasks+    go <- newObject $ HarnessObject list+    createEngine defaultEngineConfig {+        initialURL = fromJust $ parseURIReference qmlPath,+        contextObject = Just go}+    runEngines+    removeFile qmlPath+    ts <- readIORef list+    forM_ ts (\t -> putStrLn (+        showString "Incomplete task '" . showString t $ showString "'." []))+    if null ts+    then exitSuccess+    else exitFailure