hayland-0.1.0.1: Graphics/Wayland/Scanner/Marshaller.chs
{-# LANGUAGE TemplateHaskell, DeriveDataTypeable #-}
module Graphics.Wayland.Scanner.Marshaller (
argTypeToCType, argTypeToHaskType, argTypeToWeirdInterfaceCType,
argTypeMarshaller, argTypeUnmarshaller,
funcSize, funcAlign
) where
import Control.Exception.Base (bracket)
import Data.Functor
import Data.Fixed (Fixed(..))
import Foreign
import Foreign.C.Types
import Foreign.C.String
import System.Process
import System.IO
import System.Posix.Types
import Language.Haskell.TH
import Graphics.Wayland.Internal.Util (Fixed256, Time, millisecondsToTime, timeToMilliseconds)
import Graphics.Wayland.Scanner.Protocol
import Graphics.Wayland.Scanner.Names
import Graphics.Wayland.Scanner.Types
#include <wayland-server.h>
{#context prefix="wl"#}
wlFixedToFixed256 :: CInt -> Fixed256
wlFixedToFixed256 = MkFixed . fromIntegral
fixed256ToWlFixed :: Fixed256 -> CInt
fixed256ToWlFixed (MkFixed a) = fromIntegral a
-- {#pointer * array as WLArray nocode#}
argTypeToCType :: Argument -> TypeQ
argTypeToCType (_,IntArg,_) = [t| {#type int32_t#} |]
argTypeToCType (_,UIntArg,_) = [t| {#type uint32_t#} |]
argTypeToCType (_,FixedArg,_) = [t|{#type fixed_t#}|]
argTypeToCType (_,StringArg,_) = [t| Ptr CChar |]
argTypeToCType (_,(ObjectArg iname),_) = return $ ConT iname
argTypeToCType (_,(NewIdArg iname _),_) = return $ ConT iname
argTypeToCType (_,ArrayArg,_) = [t|WLArray|]
argTypeToCType (_,FdArg,_) = [t| {#type int32_t#} |]
argTypeToHaskType :: Argument -> TypeQ
argTypeToHaskType (_,IntArg,_) = [t|Int|]
argTypeToHaskType (name,UIntArg,_)
| name == "time" = [t|Time|]
| otherwise = [t|Word|]
argTypeToHaskType (_,FixedArg,_) = [t|Fixed256|]
argTypeToHaskType (_,StringArg,False) = [t|String|]
argTypeToHaskType (_,(ObjectArg iname),False) = return $ ConT iname
argTypeToHaskType (_,(NewIdArg iname _),False) = return $ ConT iname
argTypeToHaskType (_,StringArg,True) = [t|Maybe String|]
argTypeToHaskType (_,(ObjectArg iname),True) = [t|Maybe $(conT iname) |]
argTypeToHaskType (_,(NewIdArg iname _),True) = [t|Maybe $(conT iname) |]
argTypeToHaskType (_,ArrayArg,True) = [t|Maybe (Int, Ptr ())|] -- size_t size and void*
argTypeToHaskType (_,ArrayArg,False) = [t|(Int, Ptr ())|] -- size_t size and void*
argTypeToHaskType (_,FdArg,_) = [t|Fd|]
argTypeToWeirdInterfaceCType :: Argument -> TypeQ
argTypeToWeirdInterfaceCType (_,(NewIdArg iname _),_) = [t|{#type uint32_t#}|]
argTypeToWeirdInterfaceCType x = argTypeToCType x
marshallerVar :: Argument -> Name
marshallerVar (name, _, _) = mkName name
-- | Allows a C function to receive Haskell data
argTypeMarshaller :: [Argument] -> ExpQ -> ([Pat], ExpQ)
argTypeMarshaller args fun =
let vars = map marshallerVar args
mk = return . VarE . marshallerVar
applyMarshaller :: [Argument] -> ExpQ -> ExpQ
applyMarshaller (arg@(_, IntArg, _):as) fun = [|$(applyMarshaller as [|$fun (fromIntegral ($(mk arg) :: Int) )|])|]
applyMarshaller (arg@(name, UIntArg, _):as) fun
| name == "time" = [|$(applyMarshaller as [|$fun (timeToMilliseconds ($(mk arg) :: Time))|]) |]
| otherwise = [|$(applyMarshaller as [|$fun (fromIntegral ($(mk arg) :: Word))|]) |]
applyMarshaller (arg@(_, FixedArg, _):as) fun = [|$(applyMarshaller as [|$fun (fixed256ToWlFixed $(mk arg))|]) |]
applyMarshaller (arg@(_, StringArg, False):as) fun = [|withCString $(mk arg) (\cstr -> $(applyMarshaller as [|$fun cstr|]))|]
applyMarshaller (arg@(_, (ObjectArg iname), False):as) fun = [|$(applyMarshaller as [|$fun $(mk arg)|]) |]
applyMarshaller (arg@(_, (NewIdArg iname _), False):as) fun = [|$(applyMarshaller as [|$fun $(mk arg) |])|]
applyMarshaller (arg@(_, StringArg, True):as) fun = [|
case $(mk arg) of
Nothing -> $(applyMarshaller as [|$fun nullPtr|])
Just str -> withCString str (\cstr -> $(applyMarshaller as [|$fun cstr|]))
|]
applyMarshaller (arg@(_, (ObjectArg iname), True):as) fun = [|
case $(mk arg) of
Nothing -> $(applyMarshaller as [|$fun ($(conE iname) nullPtr)|])
Just obj -> $(applyMarshaller as [|$fun obj|])
|]
applyMarshaller (arg@(_, (NewIdArg iname _), True):as) fun = [|
case $(mk arg) of
Nothing -> $(applyMarshaller as [|$fun ($(conE iname) nullPtr)|])
Just obj -> $(applyMarshaller as [|$fun obj|])
|]
applyMarshaller (arg@(_, ArrayArg, True):as) fun = [|
case $(mk arg) of
Nothing -> $(applyMarshaller as [|$fun nullPtr|])
Just (size, dat) -> bracket
(mallocBytes (2*{#sizeof size_t#}+ 4)) -- FIXME prettify / make portable. is this even right?
(free)
(\arrayPtr -> do
{#set array.size#} arrayPtr size
{#set array.alloc#} arrayPtr size -- FIXME or should we force powers of 2?
{#set array.data#} arrayPtr dat
$(applyMarshaller as [|$fun arrayPtr|])
)
|]
applyMarshaller (arg@(_, ArrayArg, False):as) fun = [|
do
let (size, dat) = $(mk arg)
bracket
(mallocBytes (2*{#sizeof size_t#}+ 4)) -- FIXME prettify / make portable. is this even right?
(free)
(\arrayPtr -> do
{#set array.size#} arrayPtr size
{#set array.alloc#} arrayPtr size -- FIXME or should we force powers of 2?
{#set array.data#} arrayPtr dat
$(applyMarshaller as [|$fun arrayPtr|])
)
|]
applyMarshaller (arg@(_, FdArg, _):as) fun = [|$(applyMarshaller as [|$fun (unFd ($(mk arg)))|]) |]
applyMarshaller [] fun = fun
in (map VarP vars, applyMarshaller args fun)
unFd (Fd k) = k
-- | Opposite of argTypeMarshaller: allows a Haskell function to receive C data.
argTypeUnmarshaller :: [Argument] -> ExpQ -> ([Pat], ExpQ)
argTypeUnmarshaller args fun =
let vars = map marshallerVar args
mk = return . VarE . marshallerVar
applyUnmarshaller :: [Argument] -> ExpQ -> ExpQ
applyUnmarshaller (arg@(_, IntArg, _):as) fun = [|$(applyUnmarshaller as [|$fun (fromIntegral ($(mk arg) :: CInt) )|])|]
applyUnmarshaller (arg@(name, UIntArg, _):as) fun
| name == "time" = [|$(applyUnmarshaller as [|$fun (millisecondsToTime ($(mk arg) :: CUInt))|]) |]
| otherwise = [|$(applyUnmarshaller as [|$fun (fromIntegral ($(mk arg) :: CUInt))|]) |]
applyUnmarshaller (arg@(_, FixedArg, _):as) fun = [|$(applyUnmarshaller as [|$fun (wlFixedToFixed256 $(mk arg))|]) |]
applyUnmarshaller (arg@(_, StringArg, False):as) fun = [|do str <- peekCString $(mk arg); $(applyUnmarshaller as [|$fun str|])|]
applyUnmarshaller (arg@(_, (ObjectArg iname), False):as) fun = [|$(applyUnmarshaller as [|$fun $(mk arg)|]) |]
applyUnmarshaller (arg@(_, (NewIdArg iname _), False):as) fun = [|$(applyUnmarshaller as [|$fun $(mk arg) |])|]
applyUnmarshaller (arg@(_, StringArg, True):as) fun = [|do
str <- if $(mk arg) == nullPtr
then return Nothing
else Just <$> peekCString $(mk arg)
$(applyUnmarshaller as [|$fun str|])
|]
applyUnmarshaller (arg@(_, (ObjectArg iname), True):as) fun = [|$(applyUnmarshaller as [|$fun $
let $(conP iname ([varP $ mkName "ptr___"])) = $(mk arg)
in if $(varE $ mkName "ptr___") == nullPtr
then Nothing
else Just $(mk arg)|]) |]
applyUnmarshaller (arg@(_, (NewIdArg iname _), True):as) fun = [|$(applyUnmarshaller as [|$fun $
if $(mk arg) == nullPtr
then Nothing
else Just $ $(conE iname) $(mk arg)|]) |]
applyUnmarshaller (arg@(_, ArrayArg, True):as) fun = [|
if $(mk arg) == nullPtr
then $(applyUnmarshaller as [|$fun Nothing|])
else do
size <- fromIntegral <$> {#get array->size#} ($(mk arg) :: WLArray)
dat <- {#get array->data#} ($(mk arg) :: WLArray)
$(applyUnmarshaller as [|$fun (Just (size, dat)) |])
|]
applyUnmarshaller (arg@(_, ArrayArg, False):as) fun = [|do
size <- fromIntegral <$> {#get array->size#} ($(mk arg) :: WLArray)
dat <- {#get array->data#} ($(mk arg) :: WLArray)
$(applyUnmarshaller as [|$fun (size, dat) |])
|]
applyUnmarshaller (arg@(_, FdArg, _):as) fun = [|$(applyUnmarshaller as [|$fun (Fd ($(mk arg)))|]) |]
applyUnmarshaller [] fun = fun
in (map VarP vars, applyUnmarshaller args fun)
-- compute FunPtr size and alignment based on some dummy C type
funcSize = {#sizeof notify_func_t#} :: Integer
funcAlign = {#alignof notify_func_t#} :: Integer