{-# LANGUAGE OverloadedStrings #-} -- An example hslua program that demonstrates providing Haskell callbacks -- to Lua and getting Lua callbacks from Haskell. import qualified Data.ByteString.Char8 as BC import Data.IORef import Foreign.C.Types (CInt) import Scripting.Lua as Lua main :: IO () main = do callbacks <- newIORef [] l <- newstate openlibs l registerrawhsfunction l "addLuaCallbacks" (addLuaCallbacks callbacks) registerrawhsfunction l "callLuaCallbacks" (callLuaCallbacks callbacks) registerrawhsfunction l "resetLuaCallbacks" (resetLuaCallbacks callbacks) loadfile l "examples/callbacks/callbacks.lua" call l 0 0 close l type LuaFunRef = Int -- | Get Lua callbacks as argument to later call them in order. -- Successive calls to this function without calling `resetLuaCallbacks` -- adds more callbacks to the queue. -- (I know lists are not the best functional queue implementations ...) addLuaCallbacks :: IORef [LuaFunRef] -> LuaState -> IO CInt addLuaCallbacks cs l = do -- number of arguments passed to this function args <- gettop l -- make sure arguments are functions as <- checkArgs args case as of Nothing -> do -- arguments are functions, add them to callback queue and return -- nothing addCallbacks 1 args return 0 Just errArg -> do -- error: argument at `errArg` is not a function, return error -- string pushstring l $ BC.pack $ "argument " ++ show errArg ++ " is not a function" return 1 where -- | Check if all arguments are functions, return `Just argIdx` if -- argument at `argIdx` is not a function and `Nothing` otherwise. checkArgs :: Int -> IO (Maybe Int) checkArgs 0 = return Nothing checkArgs n = do ty <- ltype l n if ty == TFUNCTION then checkArgs (n-1) else return $ Just n addCallbacks n max | n > max = return () | otherwise = do -- move nth argument to top of the stack pushvalue l n -- add function reference to registry refId <- ref l registryindex -- add registry index to IORef modifyIORef cs (++ [refId]) -- continue adding other arguments addCallbacks (n+1) max -- | Call Lua callbacks collected with `addLuaCallbacks`. callLuaCallbacks :: IORef [LuaFunRef] -> LuaState -> IO CInt callLuaCallbacks cs l = do cs' <- readIORef cs -- push new array to the stack createtable l (length cs') 0 -- call callbacks and fill array with return values iter cs' return 1 where iter [] = return () iter (c : rest) = do getglobal2 l "table.insert" pushvalue l (-2) pushinteger l (fromIntegral c) gettable l registryindex -- call the callback pcall l 0 1 0 -- call table.insert pcall l 2 0 0 iter rest -- | Reset callback queue and remove Lua functions from registry to enable -- garbage collection. resetLuaCallbacks :: IORef [LuaFunRef] -> LuaState -> IO CInt resetLuaCallbacks cs l = do cs' <- readIORef cs mapM_ (unref l registryindex) cs' writeIORef cs [] return 0