elerea-sdl-0.1: FRP/Elerea/SDL.hs
module FRP.Elerea.SDL (sdlLoop, Ticks) where
import Data.Word (Word32)
import System.IO.Unsafe
import Control.Concurrent (threadDelay)
import FRP.Elerea.Param
import qualified Graphics.UI.SDL as SDL
-- | SDL Ticks
type Ticks = Word32
-- | Main SDL event loop (with framerate)
--
-- Produces an "infinite" list of network samples
sdlLoop ::
Ticks
-- ^ Frame duration / Frame time
-> (SignalGen Ticks (Signal [SDL.Event]) -> SignalGen Ticks (Signal a))
-- ^ 'Signal' network, takes event 'SignalGen' as argument
-> IO [a]
sdlLoop frameTime network = do
(events, signalEvents) <- externalMulti
sampleNetwork <- start (network events)
now <- SDL.getTicks
sdlLoop' signalEvents sampleNetwork frameTime now
sdlLoop' ::
(SDL.Event -> IO ()) -- ^ IO action to signal an event
-> (Ticks -> IO a) -- ^ IO action to sample the network
-> Ticks -- ^ Frame duration / Frame time
-> Ticks -- ^ Current SDL time (getTicks)
-> IO [a]
sdlLoop' signalEvents sampleNetwork frameTime = loop frameTime
where
loop timeLeft time = do
(event, (left, nextTime)) <- waitEventTimeout (timeLeft, time)
case event of
(Just e) -> signalEvents e >> loop left nextTime
Nothing -> do
x <- sampleNetwork nextTime
xs <- unsafeInterleaveIO (loop frameTime nextTime)
return (x : xs)
-- | Turns out, SDL just does poll-and-wait internally anyway
-- This wait can time out, which is useful for drawing
waitEventTimeout :: (Ticks,Ticks) -> IO (Maybe SDL.Event, (Ticks,Ticks))
waitEventTimeout (initialLeft, lastTime) = do
SDL.pumpEvents
e <- SDL.pollEvent
case e of
SDL.NoEvent -> do
now <- SDL.getTicks
loop (initialLeft `sub` (now-lastTime)) now
_ -> return (Just e, (initialLeft, lastTime))
where
loop 0 _ = do
timeoutNow <- SDL.getTicks
return (Nothing, (0, timeoutNow))
loop _ now = do
SDL.pumpEvents
e <- SDL.pollEvent
case e of
SDL.NoEvent -> do
threadDelay 10000
eventNow <- SDL.getTicks
loop (initialLeft `sub` (eventNow - now)) now
_ -> do
eventNow <- SDL.getTicks
return (Just e, (initialLeft `sub` (eventNow - now), eventNow))
sub t n
| t > n = t - n
| otherwise = 0