packages feed

h-raylib-5.1.1.0: src/Raylib/Internal/Web/Native.hs

{-# 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