module Reactive.Banana.SDL2 ( module Reactive.Banana.SDL2.Types , module Reactive.Banana.SDL2.Util , getSDLEventSource , runSDLPump , runCappedSDLPump ) where import Control.Monad import Data.Word import Reactive.Banana as R import Reactive.Banana.Frameworks (newAddHandler) import qualified SDL as SDL import qualified SDL.Raw as SDLR import Reactive.Banana.SDL2.Types import Reactive.Banana.SDL2.Util getSDLEventSource :: IO SDLEventSource getSDLEventSource = SDLEventSource <$> newAddHandler <*> newAddHandler -- | One step in the main event loop, returning False when it needs to stop. mainSDLPump :: SDLEventSource -> IO Bool mainSDLPump sdlEventSource = do let esdl = getSDLEvent sdlEventSource etick = getTickEvent sdlEventSource tick <- SDL.ticks mEvents <- collectEvents case mEvents of Nothing -> return False Just events -> do mapM (fire esdl) events fire etick tick return True -- | Collect SDL events. -- Evaluate to Nothing on quit, otherwise evaluates to the last event. collectEvents :: IO (Maybe [SDL.EventPayload]) collectEvents = do e <- SDL.pollEvent case fmap SDL.eventPayload e of Just SDL.QuitEvent -> return Nothing Nothing -> return (Just []) Just event -> liftM (liftM (event:)) collectEvents -- | The main event loop. runSDLPump :: SDLEventSource -> IO () runSDLPump sdlEventSource = whileM $ mainSDLPump sdlEventSource -- | The main event loop, capped at a given FPS. runCappedSDLPump :: Word16 -> SDLEventSource -> IO () runCappedSDLPump fpsCap sdlEventSource = do startTick <- SDL.ticks shouldContinue <- mainSDLPump sdlEventSource endTick <- SDL.ticks let ticks = fromIntegral $ endTick - startTick secsPerFrame = fromIntegral $ 1000 `div` fpsCap when (ticks < secsPerFrame) $ SDL.delay (secsPerFrame - ticks) when shouldContinue $ runCappedSDLPump fpsCap sdlEventSource