module Graphics.QML.Internal.MetaObj where import Graphics.QML.Internal.Types import Control.Monad import Control.Monad.Trans.State (State, execState, get, put) import Data.Bits import Data.Map (Map) import qualified Data.Map as Map import Data.Maybe import Foreign.C.Types import Foreign.C.String import Foreign.Ptr import Foreign.Storable import Foreign.Marshal.Array -- -- Counted Reverse List -- data CRList a = CRList { crlLen :: !Int, crlList :: [a] } crlEmpty :: CRList a crlEmpty = CRList 0 [] crlSingle :: a -> CRList a crlSingle x = CRList 1 [x] crlAppend1 :: CRList a -> a -> CRList a crlAppend1 (CRList n xs) x = CRList (n+1) (x:xs) crlAppend :: CRList a -> [a] -> CRList a crlAppend (CRList n xs) ys = CRList n' xs' where (xs', n') = rev ys xs n 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 ptr <- mallocArray len pokeRev ptr lst len return ptr where pokeRev _ [] _ = return () pokeRev p (x:xs) n = do let n' = n-1 x' <- f x pokeElemOff p n' x' pokeRev p xs n' crlToList :: CRList a -> [a] crlToList (CRList _ lst) = reverse lst -- -- Meta Object Compiler -- data MemberKind = MethodMember | ConstPropertyMember | PropertyMember | SignalMember deriving Eq -- | Represents a named member of the QML class which wraps type @tt@. data Member tt = Member { memberKind :: MemberKind, memberName :: String, memberType :: TypeId, memberParams :: [(String, TypeId)], memberFun :: UniformFunc, memberFunAux :: Maybe UniformFunc, memberKey :: Maybe MemberKey } data MOCState = MOCState { mData :: CRList CUInt, mDataMethodsIdx :: Maybe Int, mDataPropsIdx :: Maybe Int, mStrChar :: CRList CChar, mStrInfo :: CRList CUInt, mStrMap :: Map String CUInt, mParamMap :: Map [TypeId] CUInt, mSigMap :: Map MemberKey CUInt, mFuncMethods :: CRList (Maybe UniformFunc), mFuncProperties :: CRList (Maybe UniformFunc), mMethodCount :: Int, mSignalCount :: Int, mPropertyCount :: Int } -- | Generate MOC meta-data from a class name and member list. compileClass :: String -> [Member tt] -> MOCState compileClass name ms = let enc = flip execState (newMOCState enc) $ do writeInt 7 -- Revision writeString name -- Class name writeInt 0 >> writeInt 0 -- Class info writeIntegral $ mMethodCount enc + mSignalCount enc -- Methods writeIntegral $ fromMaybe 0 $ mDataMethodsIdx enc -- Methods (data index) writeIntegral $ mPropertyCount enc -- Properties writeIntegral $ fromMaybe 0 $ mDataPropsIdx enc -- Properties (data index) writeInt 0 >> writeInt 0 -- Enums writeInt 0 >> writeInt 0 -- Constructors writeInt 0 -- Flags writeIntegral $ mSignalCount enc -- Signals let mms = filterMembers SignalMember ms ++ filterMembers MethodMember ms mapM_ writeMethodParams mms mapM_ writeMethod mms let pms = filterMembers ConstPropertyMember ms ++ filterMembers PropertyMember ms mapM_ writeProperty pms mapM_ writePropertySig pms writeInt 0 in enc filterMembers :: MemberKind -> [Member tt] -> [Member tt] filterMembers k = filter (\m -> k == memberKind m) newMOCState :: MOCState -> MOCState newMOCState enc = MOCState crlEmpty Nothing Nothing crlEmpty (crlSingle strCount) Map.empty Map.empty Map.empty crlEmpty crlEmpty 0 0 0 where strCount = fromIntegral $ Map.size $ mStrMap enc writeInt :: CUInt -> State MOCState () writeInt int = do state <- get put $ state {mData = mData state `crlAppend1` int} return () writeIntegral :: (Integral a) => a -> State MOCState () writeIntegral int = writeInt (fromIntegral int) writeString :: String -> State MOCState () writeString str = do state <- get let msChr = mStrChar state msInf = mStrInfo state msMap = mStrMap state 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') msMap' = Map.insert str (fromIntegral idx) msMap put $ state { mStrChar = msChr', mStrInfo = msInf', mStrMap = msMap'} writeIntegral idx writeMethodParams :: Member tt -> State MOCState () writeMethodParams m = do state <- get let types = memberTypes m datal = mData state mpMap = mParamMap state 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 . typeId) types mapM_ (writeString . fst) $ memberParams m writeMethod :: Member tt -> State MOCState () writeMethod m = do idx <- get >>= return . crlLen . mData paramMap <- get >>= return . mParamMap writeString $ memberName m writeIntegral $ length $ memberParams m writeInt $ fromMaybe 0 $ flip Map.lookup paramMap $ memberTypes m writeString "" let (mc,sc,flags) = case memberKind m of SignalMember -> (0,1,mfMethodSignal) _ -> (1,0,mfMethodMethod) writeInt (mfAccessPublic .|. mfMethodScriptable .|. flags) state <- get put $ state { mDataMethodsIdx = mplus (mDataMethodsIdx state) (Just idx), mMethodCount = mc + mMethodCount state, mSignalCount = sc + mSignalCount state, mSigMap = maybe (mSigMap state) (\k -> Map.insert k (fromIntegral $ mSignalCount state) (mSigMap state)) $ memberKey m, mFuncMethods = mFuncMethods state `crlAppend1` (Just $ memberFun m)} return () writeProperty :: Member tt -> State MOCState () writeProperty p = do idx <- get >>= return . crlLen . mData 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)) state <- get put $ state { mDataPropsIdx = mplus (mDataPropsIdx state) (Just idx), mPropertyCount = 1 + mPropertyCount state, mFuncProperties = mFuncProperties state `crlAppend1` (Just $ memberFun p) `crlAppend1` memberFunAux p } return () writePropertySig :: Member tt -> State MOCState () writePropertySig p = do state <- get writeInt $ fromMaybe 0 $ maybe Nothing (flip Map.lookup $ mSigMap state) $ memberKey p memberTypes :: Member tt -> [TypeId] memberTypes m = memberType m : (map snd $ memberParams m) typeId :: TypeId -> CUInt typeId (TypeId tyid) = fromIntegral tyid -- -- Constants -- 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