{-# OPTIONS -Wall -Wno-unrecognised-pragmas #-} {-# LANGUAGE CPP #-} {-# LANGUAGE ScopedTypeVariables #-} {-# HLINT ignore "Redundant lambda" #-} -- | Internal functions for running in a browser -- -- /NOTE: This module is only used when building for the web/ module Raylib.Internal.Web.Native ( callRaylibFunction, jslog, jsfree, p'jsfree, CallRaylibType (..), ) where import Foreign (FunPtr, Ptr, Storable (peek, sizeOf), castPtr, free, mallocArray, pokeArray) import Foreign.C (CChar, CString, CUChar (..), CUInt (..), castCharToCChar, withCStringLen) import Raylib.Internal.Web.Processable ( Processable (processableType), ProcessedParam (..), processParam, ) -- | For \"varargs\" function calls, based on https://wiki.haskell.org/Varargs class CallRaylibType t where callRaylibFunction' :: String -> IO [ProcessedParam] -> t instance (Storable a, Processable a) => CallRaylibType (IO a) where callRaylibFunction' func params' = do params <- params' callRaylibFunctionRaw func params instance (Storable a, Processable a, CallRaylibType r) => CallRaylibType (a -> r) where callRaylibFunction' func params' = \x -> callRaylibFunction' func ( do params <- params' param <- processParam x return $ params ++ [param] ) callRaylibFunctionRaw :: forall a. (Storable a, Processable a) => String -> [ProcessedParam] -> IO a callRaylibFunctionRaw func params = do let l = length func p = length params namePtr <- mallocArray l pokeArray namePtr (map castCharToCChar func :: [CChar]) let nameLen = fromIntegral l :: CUInt ptrs = map (\(ProcessedParam ptr _ _) -> ptr) params sizes = map (\(ProcessedParam _ size _) -> fromIntegral size) params :: [CUInt] signs = map (\(ProcessedParam _ _ pType) -> fromIntegral pType) params :: [CUChar] numParams = fromIntegral p :: CUInt ptrsPtr <- mallocArray p pokeArray ptrsPtr ptrs sizesPtr <- mallocArray p pokeArray sizesPtr sizes typesPtr <- mallocArray p pokeArray typesPtr signs resPtr <- c'callRaylibFunction namePtr nameLen ptrsPtr sizesPtr typesPtr numParams (fromIntegral $ sizeOf (undefined :: a)) (fromIntegral $ fromEnum $ processableType (undefined :: a)) res <- peek (castPtr resPtr) jsfree resPtr free namePtr free ptrsPtr mapM_ free ptrs free sizesPtr free typesPtr return res jslog :: String -> IO () jslog str = withCStringLen str (\(s, len) -> c'jslog s (fromIntegral len)) -- | This is an interfacing function that allows Haskell to call raylib -- functions that have been compiled with emscripten. This has to be done in -- a roundabout way because we cannot directly call these functions through -- Haskell; we have to call a JS proxy function that calls the actual raylib -- functions. callRaylibFunction :: (CallRaylibType t) => String -> t callRaylibFunction func = callRaylibFunction' func (return []) #ifdef WEB_FFI foreign import ccall "main.h jslog" c'jslog :: CString -> CUInt -> IO () foreign import ccall "main.h jsfree" jsfree :: Ptr () -> IO () foreign import ccall "main.h &jsfree" p'jsfree :: FunPtr (Ptr a -> IO ()) foreign import ccall "main.h callRaylibFunction" c'callRaylibFunction :: CString -> CUInt -> Ptr (Ptr ()) -> Ptr CUInt -> Ptr CUChar -> CUInt -> CUInt -> CUChar -> IO (Ptr ()) #else c'jslog :: CString -> CUInt -> IO () c'jslog = error "(c'jslog) Not running in the web!" jsfree :: Ptr () -> IO () jsfree = error "(jsfree) Not running in the web!" p'jsfree :: FunPtr (Ptr a -> IO ()) p'jsfree = error "(p'jsfree) Not running in the web!" c'callRaylibFunction :: CString -> CUInt -> Ptr (Ptr ()) -> Ptr CUInt -> Ptr CUChar -> CUInt -> CUInt -> CUChar -> IO (Ptr ()) c'callRaylibFunction = error "(c'callRaylibFunction): Not running in the web!" #endif