aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-01-01 11:39:25 -0800
committer3gg <3gg@shellblade.net>2025-01-01 11:39:25 -0800
commitacc954c9ac3a18e2d48e52839a7dc751597dfb15 (patch)
treee002438e1085cbda09a36ef81c4d661e0102a0d1
parent8984aede0162f6bdcfc2dc0a54f563a3b1ff5684 (diff)
Streamling the Game monad, use MonadIO for automatic lifting.
-rw-r--r--Demos/Pong/Main.hs62
-rw-r--r--Spear/App.hs135
-rw-r--r--Spear/Game.hs84
-rw-r--r--Spear/Render/Core/Buffer.hs5
-rw-r--r--Spear/Render/Core/Geometry.hs9
-rw-r--r--Spear/Render/Core/Pipeline.hs24
-rw-r--r--Spear/Render/Core/Shader.hs15
-rw-r--r--Spear/Render/Immediate.hs17
-rw-r--r--Spear/Scene/Loader.hs2
-rw-r--r--Spear/Sound/Sound.hs13
-rw-r--r--Spear/Sys/Timer.hsc21
-rw-r--r--Spear/Window.hs36
12 files changed, 246 insertions, 177 deletions
diff --git a/Demos/Pong/Main.hs b/Demos/Pong/Main.hs
index f77136f..eafa983 100644
--- a/Demos/Pong/Main.hs
+++ b/Demos/Pong/Main.hs
@@ -1,3 +1,5 @@
1{-# LANGUAGE MultiParamTypeClasses #-}
2
1module Main where 3module Main where
2 4
3import Pong 5import Pong
@@ -21,15 +23,16 @@ import Control.Monad (when)
21import Data.Maybe (mapMaybe) 23import Data.Maybe (mapMaybe)
22 24
23 25
24data GameState = GameState 26data Pong = Pong
25 { context :: AppContext 27 { immRenderState :: ImmRenderState
26 , renderCoreState :: RenderCoreState
27 , immRenderState :: ImmRenderState
28 , viewProjection :: Matrix4 28 , viewProjection :: Matrix4
29 , backgroundMusic :: SoundSource 29 , backgroundMusic :: SoundSource
30 , world :: [GameObject] 30 , world :: [GameObject]
31 } 31 }
32 32
33type GameState = AppState Pong
34
35
33options = defaultAppOptions { title = "Pong" } 36options = defaultAppOptions { title = "Pong" }
34 37
35app = App options initGame endGame step render resize 38app = App options initGame endGame step render resize
@@ -38,32 +41,38 @@ app = App options initGame endGame step render resize
38main :: IO () 41main :: IO ()
39main = runApp app 42main = runApp app
40 43
41initGame :: AppContext -> Game () GameState 44initGame :: Game AppContext Pong
42initGame context = do 45initGame = do
43 (immRenderState, renderCoreState) <- runSiblingGame newImmRenderer newRenderCoreState 46 renderCoreState <- contextRenderCoreState <$> get
44 (music, soundState') <- flip runSiblingGame (appSoundState context) $ do 47 (immRenderState, renderCoreState') <- runSiblingGame renderCoreState newImmRenderer
48 -- TODO: This can work if we use FlexibleContexts and change the function signatures.
49 --immRenderState <- newImmRenderer
50 music <- siblingGame $ do
45 musicBuffer <- loadAudioFile "/home/jeanne/Casual Tiki Party Main.wav" 51 musicBuffer <- loadAudioFile "/home/jeanne/Casual Tiki Party Main.wav"
46 music <- makeSoundSource 52 music <- makeSoundSource
47 liftIO $ do 53 -- TODO: setSoundSourceBuffer generates an AL error for some reason, though
48 setSoundSourceBuffer music musicBuffer 54 -- the music still plays.
49 setSoundLoopMode music Loop 55 -- "user error (runALUT: There was already an AL error on entry to an ALUT function)"
50 playSounds [music] 56 setSoundSourceBuffer music musicBuffer
57 setSoundLoopMode music Loop
58 playSounds [music]
51 return music 59 return music
52 let context' = context { appSoundState = soundState' } 60 return $ Pong immRenderState Matrix4.id music newWorld
53 return $ GameState context' renderCoreState immRenderState Matrix4.id music newWorld
54 61
55endGame :: Game GameState () 62endGame :: Game GameState ()
56endGame = do 63endGame = do
57 game <- get 64 renderCoreState <- appRenderCoreState <$> get
58 runSubGame' (deleteImmRenderer $ immRenderState game) (renderCoreState game) 65 game <- getGameState
66 exec' runSiblingGame renderCoreState (deleteImmRenderer $ immRenderState game)
59 67
60 68
61step :: Elapsed -> Dt -> [InputEvent] -> Game GameState Bool 69step :: Elapsed -> Dt -> [InputEvent] -> Game GameState Bool
62step elapsed dt inputEvents = do 70step elapsed dt inputEvents = do
63 gameState <- get 71 appState <- get
64 events <- processInput (appWindow . context $ gameState) 72 gameState <- getGameState
73 events <- processInput (appWindow appState)
65 --when (events /= []) $ liftIO . putStrLn $ show events 74 --when (events /= []) $ liftIO . putStrLn $ show events
66 modify $ \gameState -> gameState 75 modifyGameState $ \pong -> pong
67 { world = stepWorld (realToFrac elapsed) (realToFrac dt) events (world gameState) 76 { world = stepWorld (realToFrac elapsed) (realToFrac dt) events (world gameState)
68 } 77 }
69 return (not $ exitRequested inputEvents) 78 return (not $ exitRequested inputEvents)
@@ -79,18 +88,17 @@ exitRequested = elem (KeyDown KEY_ESC)
79 88
80render :: Game GameState () 89render :: Game GameState ()
81render = do 90render = do
82 gameState <- get 91 gameState <- getGameState
83 immRenderState' <- flip execSubGame (immRenderState gameState) $ do 92 immRenderState' <- exec runSiblingGame (immRenderState gameState) $ do
84 immStart 93 immStart
85 immSetViewProjectionMatrix (viewProjection gameState) 94 immSetViewProjectionMatrix (viewProjection gameState)
86 -- Clear the background to a different colour than the playable area to make 95 -- Clear the background to a different colour than the playable area to make
87 -- the latter distinguishable. 96 -- the latter distinguishable.
88 liftIO $ do 97 setClearColour (0.2, 0.2, 0.2, 0.0)
89 setClearColour (0.2, 0.2, 0.2, 0.0) 98 clearBuffers [ColourBuffer]
90 clearBuffers [ColourBuffer]
91 render' $ world gameState 99 render' $ world gameState
92 immEnd 100 immEnd
93 put $ gameState { immRenderState = immRenderState' } 101 putGameState $ gameState { immRenderState = immRenderState' }
94 102
95render' :: [GameObject] -> Game ImmRenderState () 103render' :: [GameObject] -> Game ImmRenderState ()
96render' world = do 104render' world = do
@@ -132,7 +140,7 @@ resize (ResizeEvent w h) =
132 bottom = if r > 1 then 0 else -pad 140 bottom = if r > 1 then 0 else -pad
133 top = if r > 1 then 1 else 1 + pad 141 top = if r > 1 then 1 else 1 + pad
134 in do 142 in do
135 liftIO $ setViewport 0 0 w h 143 setViewport 0 0 w h
136 modify $ \state -> state { 144 modifyGameState $ \pong -> pong {
137 viewProjection = Matrix4.ortho left right bottom top (-1) 1 145 viewProjection = Matrix4.ortho left right bottom top (-1) 1
138 } 146 }
diff --git a/Spear/App.hs b/Spear/App.hs
index 8c0371e..75bf6fa 100644
--- a/Spear/App.hs
+++ b/Spear/App.hs
@@ -1,26 +1,34 @@
1{-# LANGUAGE MultiParamTypeClasses #-}
2
1module Spear.App 3module Spear.App
2( App(..) 4( App(..)
3, AppOptions(..) 5, AppOptions(..)
4, AppContext(..) 6, AppContext(..)
7, AppState(..)
5, Elapsed 8, Elapsed
6, Dt 9, Dt
7, Step 10, Step
8, defaultAppOptions 11, defaultAppOptions
12, getGameState
13, putGameState
14, modifyGameState
9, runApp 15, runApp
10, loop 16, loop
11) 17)
12where 18where
13 19
14import Spear.Game 20import Spear.Game
21import Spear.Render.Core.State
15import Spear.Sound.Sound 22import Spear.Sound.Sound
16import Spear.Sound.State 23import Spear.Sound.State
17import Spear.Sys.Timer as Timer 24import Spear.Sys.Timer as Timer
18import Spear.Window 25import Spear.Window
19 26
20import Control.Monad 27import Control.Monad
21import Data.Fixed (mod') 28import Data.Fixed (mod')
22import GHC.Float 29import GHC.Float
23 30
31
24-- | Time elapsed. 32-- | Time elapsed.
25type Elapsed = Double 33type Elapsed = Double
26 34
@@ -31,6 +39,8 @@ type Dt = Double
31type Step s = Elapsed -> Dt -> [InputEvent] -> Game s Bool 39type Step s = Elapsed -> Dt -> [InputEvent] -> Game s Bool
32 40
33-- | Application options. 41-- | Application options.
42--
43-- Use `defaultOptions` for default options.
34data AppOptions = AppOptions 44data AppOptions = AppOptions
35 { title :: String 45 { title :: String
36 , windowWidth :: Int 46 , windowWidth :: Int
@@ -40,6 +50,7 @@ data AppOptions = AppOptions
40 , enableProfiling :: Bool 50 , enableProfiling :: Bool
41 } 51 }
42 52
53-- | Default application options.
43defaultAppOptions = AppOptions 54defaultAppOptions = AppOptions
44 { title = "Spear Application" 55 { title = "Spear Application"
45 , windowWidth = 1920 56 , windowWidth = 1920
@@ -49,55 +60,105 @@ defaultAppOptions = AppOptions
49 , enableProfiling = False 60 , enableProfiling = False
50 } 61 }
51 62
52-- | Application state. 63-- | Application descriptor.
53data App s = App 64data App s = App
54 { appOptions :: AppOptions 65 { appOptions :: AppOptions
55 , initApp :: AppContext -> Game () s 66 , initApp :: Game AppContext s
56 , endApp :: Game s () 67 , endApp :: Game (AppState s) ()
57 , stepApp :: Step s 68 , stepApp :: Step (AppState s)
58 , renderApp :: Game s () 69 , renderApp :: Game (AppState s) ()
59 , resizeApp :: WindowEvent -> Game s () 70 , resizeApp :: WindowEvent -> Game (AppState s) ()
60 } 71 }
61 72
62-- | Application context. 73-- | Application context.
74--
75-- The application context is the initial state from which the application's
76-- `AppState` is bootstrapped with `initApp`.
63data AppContext = AppContext 77data AppContext = AppContext
64 { appWindow :: Window 78 { contextWindow :: Window
65 , appSoundState :: SoundState 79 , contextRenderCoreState :: RenderCoreState
80 , contextSoundState :: SoundState
66 } 81 }
67 82
83instance HasState AppContext RenderCoreState where
84 getInnerState = contextRenderCoreState
85 setInnerState context renderCoreState = context { contextRenderCoreState = renderCoreState }
86
87instance HasState AppContext SoundState where
88 getInnerState = contextSoundState
89 setInnerState context soundState = context { contextSoundState = soundState }
90
91-- | Application state.
92data AppState s = AppState
93 { appWindow :: Window
94 , appRenderCoreState :: RenderCoreState
95 , appSoundState :: SoundState
96 , customState :: s
97 }
98
99instance HasState (AppState s) RenderCoreState where
100 getInnerState = appRenderCoreState
101 setInnerState appState renderCoreState = appState { appRenderCoreState = renderCoreState }
102
103instance HasState (AppState s) SoundState where
104 getInnerState = appSoundState
105 setInnerState appState soundState = appState { appSoundState = soundState }
106
107
108-- | Get the custom state in the app state.
109getGameState :: Game (AppState s) s
110getGameState = customState <$> get
111
112-- | Put the custom state in the app state.
113putGameState :: s -> Game (AppState s) ()
114putGameState custom = do
115 appState <- get
116 put $ appState { customState = custom }
117
118-- | Modify the custom state in the app state.
119modifyGameState :: (s -> s) -> Game (AppState s) ()
120modifyGameState f = modify $ \appState -> appState { customState = f (customState appState )}
121
68-- | Run the application. 122-- | Run the application.
69runApp :: App s -> IO () 123runApp :: App s -> IO ()
70runApp app = 124runApp app =
71 let ops = appOptions app 125 let ops = appOptions app
72 w = windowWidth ops 126 w = windowWidth ops
73 h = windowHeight ops 127 h = windowHeight ops
74 in withWindow (w, h) (title ops) $ \window -> 128 in -- Initialize subsystems.
75 withSoundContext $ flip evalGame () $ do 129 withWindow (w, h) (title ops) $ \window ->
76 soundState <- evalSiblingGame initSoundSystem () 130 withSoundContext $ eval runGame () $ do
77 let appContext = AppContext window soundState 131 -- Create initial context.
78 gameState <- initApp app appContext 132 initialSoundState <- eval runSiblingGame () initSoundSystem
79 (result, endGameState) <- runSubGame (loop app window) gameState 133 let context = AppContext window newRenderCoreState initialSoundState
80 runSubGame' (endApp app) endGameState 134 -- Create initial app state.
81 runSiblingGame' destroySoundSystem soundState 135 (gameState, context') <- runSiblingGame context (initApp app)
82 136 let appState = AppState {
83-- | Convert FPS to desired delta time. 137 appWindow = contextWindow context'
84fpsToDdt :: Int -> TimeDelta 138 , appRenderCoreState = contextRenderCoreState context'
85fpsToDdt fps = if fps > 0 then secToTimeDelta (1.0 / fromIntegral fps) else 0 139 , appSoundState = contextSoundState context'
140 , customState = gameState
141 }
142 -- Run app.
143 (result, endGameState) <- runSiblingGame appState (loop app window)
144 -- Shut down.
145 exec' runSiblingGame endGameState (endApp app)
146 exec' runSiblingGame (appSoundState appState) destroySoundSystem
86 147
87-- | Enter the main application loop. 148-- | Enter the main application loop.
88loop :: App s -> Window -> Game s () 149loop :: App s -> Window -> Game (AppState s) ()
89loop app window = do 150loop app window = do
90 -- For convenience, trigger an initial resize followed by a render of the 151 -- For convenience, trigger an initial resize followed by a render of the
91 -- application's initial state. 152 -- application's initial state.
92 (width, height) <- liftIO $ getWindowSize window 153 (width, height) <- getWindowSize window
93 resizeApp app (ResizeEvent width height) 154 resizeApp app (ResizeEvent width height)
94 renderApp app 155 renderApp app
95 156
96 let ddt = fpsToDdt . maxFPS . appOptions $ app -- Desired render time step. 157 let ddt = fpsToDdt . maxFPS . appOptions $ app -- Desired render time step.
97 let animationDdt = fpsToDdt . animationFPS . appOptions $ app -- Desired animation time step. 158 let animationDdt = fpsToDdt . animationFPS . appOptions $ app -- Desired animation time step.
98 159
99 timer <- liftIO newTimer 160 timer <- newTimer
100 liftIO $ Timer.start timer 161 Timer.start timer
101 let lastAnimationTime = lastTick timer 162 let lastAnimationTime = lastTick timer
102 loop' window ddt animationDdt lastAnimationTime timer app 163 loop' window ddt animationDdt lastAnimationTime timer app
103 164
@@ -108,18 +169,18 @@ loop' ::
108 TimePoint -> -- Time point of last animation update. 169 TimePoint -> -- Time point of last animation update.
109 Timer -> 170 Timer ->
110 App s -> 171 App s ->
111 Game s () 172 Game (AppState s) ()
112loop' window renderDdt animationDdt lastAnimationTime inputTimer app = do 173loop' window renderDdt animationDdt lastAnimationTime inputTimer app = do
113 timer <- liftIO $ tick inputTimer 174 timer <- tick inputTimer
114 windowEvents <- liftIO $ pollWindowEvents window 175 windowEvents <- pollWindowEvents window
115 close <- liftIO $ shouldWindowClose window 176 close <- shouldWindowClose window
116 177
117 (continue, lastAnimationTimeNextFrame) <- case animationDdt of 178 (continue, lastAnimationTimeNextFrame) <- case animationDdt of
118 0 -> do 179 0 -> do
119 -- Variable time step game animation. 180 -- Variable time step game animation.
120 let t = timeDeltaToSec $ runningTime timer 181 let t = timeDeltaToSec $ runningTime timer
121 let dt = timeDeltaToSec $ deltaTime timer 182 let dt = timeDeltaToSec $ deltaTime timer
122 inputEvents <- liftIO $ pollInputEvents window 183 inputEvents <- pollInputEvents window
123 continue <- stepApp app t dt inputEvents 184 continue <- stepApp app t dt inputEvents
124 return (continue, lastAnimationTime) 185 return (continue, lastAnimationTime)
125 186
@@ -139,7 +200,7 @@ loop' window renderDdt animationDdt lastAnimationTime inputTimer app = do
139 let lastAnimationTimeNextFrame = timeAdd lastAnimationTime (steps * ddt) 200 let lastAnimationTimeNextFrame = timeAdd lastAnimationTime (steps * ddt)
140 --liftIO . print $ "Steps: " ++ show steps ++ ", Budget: " ++ show timeBudgetThisFrame ++ ", ddt: " ++ show ddt 201 --liftIO . print $ "Steps: " ++ show steps ++ ", Budget: " ++ show timeBudgetThisFrame ++ ", ddt: " ++ show ddt
141 continue <- and <$> forM [1..steps] (\i -> do 202 continue <- and <$> forM [1..steps] (\i -> do
142 inputEvents <- liftIO $ pollInputEvents window 203 inputEvents <- pollInputEvents window
143 let t = timeDeltaToSec $ elapsed + i * ddt 204 let t = timeDeltaToSec $ elapsed + i * ddt
144 stepApp app t dt inputEvents) 205 stepApp app t dt inputEvents)
145 return (continue, lastAnimationTimeNextFrame) 206 return (continue, lastAnimationTimeNextFrame)
@@ -151,16 +212,20 @@ loop' window renderDdt animationDdt lastAnimationTime inputTimer app = do
151 -- For smoother resizing, render only while not resizing. 212 -- For smoother resizing, render only while not resizing.
152 unless resized $ do 213 unless resized $ do
153 renderApp app 214 renderApp app
154 liftIO $ swapBuffers window 215 swapBuffers window
155 216
156 -- Limit frame rate if so requested by the application. 217 -- Limit frame rate if so requested by the application.
157 -- This currently makes the rendering stutter and is not very desirable. 218 -- This currently makes the rendering stutter and is not very desirable.
158 when ((maxFPS . appOptions $ app) > 0) $ do 219 when ((maxFPS . appOptions $ app) > 0) $ do
159 frameEnd <- liftIO now 220 frameEnd <- now
160 let ddt = renderDdt 221 let ddt = renderDdt
161 let frameTime = timeDiff (lastTick timer) frameEnd 222 let frameTime = timeDiff (lastTick timer) frameEnd
162 when (frameTime < ddt) $ do 223 when (frameTime < ddt) $ do
163 liftIO $ Timer.sleep (ddt - frameTime) 224 Timer.sleep (ddt - frameTime)
164 225
165 when (continue && not close) $ do 226 when (continue && not close) $ do
166 loop' window renderDdt animationDdt lastAnimationTimeNextFrame timer app 227 loop' window renderDdt animationDdt lastAnimationTimeNextFrame timer app
228
229-- | Convert FPS to desired delta time.
230fpsToDdt :: Int -> TimeDelta
231fpsToDdt fps = if fps > 0 then secToTimeDelta (1.0 / fromIntegral fps) else 0
diff --git a/Spear/Game.hs b/Spear/Game.hs
index 92cc680..1af8e9b 100644
--- a/Spear/Game.hs
+++ b/Spear/Game.hs
@@ -1,8 +1,11 @@
1{-# LANGUAGE GeneralizedNewtypeDeriving #-} 1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2{-# LANGUAGE MultiParamTypeClasses #-}
3
2module Spear.Game 4module Spear.Game
3( Game 5( Game
4, GameException (..) 6, GameException(..)
5, ResourceClass (..) 7, HasState(..)
8, ResourceClass(..)
6, ReleaseKey 9, ReleaseKey
7 -- * Game state 10 -- * Game state
8, get 11, get
@@ -18,15 +21,12 @@ module Spear.Game
18, catch 21, catch
19 -- * Running and IO 22 -- * Running and IO
20, runGame 23, runGame
21, evalGame
22, runSubGame 24, runSubGame
23, runSubGame'
24, evalSubGame
25, execSubGame
26, runSiblingGame 25, runSiblingGame
27, runSiblingGame' 26, eval
28, evalSiblingGame 27, exec
29, execSiblingGame 28, exec'
29, siblingGame
30, liftIO 30, liftIO
31) 31)
32where 32where
@@ -44,7 +44,6 @@ import Control.Monad.Trans.Resource
44class ResourceClass a where 44class ResourceClass a where
45 getResource :: a -> ReleaseKey 45 getResource :: a -> ReleaseKey
46 46
47
48-- | A game exception. 47-- | A game exception.
49-- 48--
50-- This is mostly a convenient wrapper around `String` so that we can throw 49-- This is mostly a convenient wrapper around `String` so that we can throw
@@ -53,7 +52,6 @@ newtype GameException = GameException String deriving (Show)
53 52
54instance Exception GameException 53instance Exception GameException
55 54
56
57-- | The game monad. 55-- | The game monad.
58-- 56--
59-- The game monad performs three different roles: 57-- The game monad performs three different roles:
@@ -73,6 +71,14 @@ newtype Game s a = Game { getGame :: StateT s (ResourceT IO) a }
73 , MonadResource 71 , MonadResource
74 ) 72 )
75 73
74-- | A class used to define state hierarchies.
75--
76-- By declaring `HasState s t`, a `Game s` monad can then execute actions of a
77-- `Game t` monad more conveniently with `siblingGame`.
78class HasState s t where
79 getInnerState :: s -> t
80 setInnerState :: s -> t -> s
81
76 82
77-- | Release the given 'Resource'. 83-- | Release the given 'Resource'.
78release' :: ResourceClass a => a -> Game s () 84release' :: ResourceClass a => a -> Game s ()
@@ -91,49 +97,39 @@ assertMaybe (Just x) _ = return x
91-- result and its final state. 97-- result and its final state.
92-- 98--
93-- Any resources acquired by the given game are released when this returns. 99-- Any resources acquired by the given game are released when this returns.
94runGame :: Game s a -> s -> IO (a, s) 100runGame :: s -> Game s a -> IO (a, s)
95runGame game = runResourceT . runStateT (getGame game) 101runGame state game = runResourceT . runStateT (getGame game) $ state
96
97-- | Run the given game and return its result.
98evalGame :: Game s a -> s -> IO a
99evalGame g s = fst <$> runGame g s
100 102
101-- | Run the given sub-game, unrolling the full monad stack and returning the 103-- | Run the given sub-game, unrolling the full monad stack and returning the
102-- game's result and its final state. 104-- game's result and its final state.
103-- 105--
104-- Like `runGame`, this frees any resources that are acquired by the sub-game. 106-- Like `runGame`, this frees any resources that are acquired by the sub-game.
105-- If you want to keep acquired resources, see `runSiblingGame` instead. 107-- If you want to keep acquired resources, see `runSiblingGame` instead.
106runSubGame :: Game s a -> s -> Game t (a, s) 108runSubGame :: s -> Game s a -> Game t (a, s)
107runSubGame g s = liftIO $ runGame g s 109runSubGame state game = liftIO $ runGame state game
108
109-- | Run the given sub-game and return its result.
110evalSubGame :: Game s a -> s -> Game t a
111evalSubGame g s = fst <$> runSubGame g s
112
113-- | Like 'runSubGame', but discarding the result.
114runSubGame' :: Game s a -> s -> Game t ()
115runSubGame' g s = void $ runSubGame g s
116
117-- | Run the given sub-game and return its state.
118execSubGame :: Game s a -> s -> Game t s
119execSubGame g s = snd <$> runSubGame g s
120 110
121-- | Run the given sibling game, unrolling the state transformer but not the 111-- | Run the given sibling game, unrolling the state transformer but not the
122-- resource transformer. 112-- resource transformer.
123-- 113--
124-- Unlike `runSubGame`, any resources acquired by the sibling game are *not* 114-- Unlike `runSubGame`, any resources acquired by the sibling game are *not*
125-- released. 115-- released.
126runSiblingGame :: Game s a -> s -> Game t (a, s) 116runSiblingGame :: s -> Game s a -> Game t (a, s)
127runSiblingGame game = Game . lift . runStateT (getGame game) 117runSiblingGame state game = Game . lift $ runStateT (getGame game) state
128 118
129-- | Like 'runSiblingGame', but discarding the result. 119-- | Run the given game and return its result.
130runSiblingGame' :: Game s a -> s -> Game t () 120--eval :: (Monad m s, Monad n s) => (m s a -> s -> n (a, s)) -> m s a -> s -> m a
131runSiblingGame' g s = void $ runSiblingGame g s 121eval runner game state = fst <$> runner game state
132 122
133-- | Run the given sibling game and return its result. 123-- | Run the given game and return its final state.
134evalSiblingGame :: Game s a -> s -> Game t a 124exec runner game state = snd <$> runner game state
135evalSiblingGame g s = fst <$> runSiblingGame g s 125
136 126-- | Run the given game and ignore both its result and final state.
137-- | Run the given sibling game and return its state. 127exec' runner game state = void $ runner game state
138execSiblingGame :: Game s a -> s -> Game t s 128
139execSiblingGame g s = snd <$> runSiblingGame g s 129-- | Run a sibling game on nested state.
130siblingGame :: HasState s t => Game t a -> Game s a
131siblingGame tAction = do
132 tState <- getInnerState <$> get
133 (result, tState') <- runSiblingGame tState tAction
134 modify $ \outerState -> setInnerState outerState tState'
135 return result
diff --git a/Spear/Render/Core/Buffer.hs b/Spear/Render/Core/Buffer.hs
index eaff475..3003987 100644
--- a/Spear/Render/Core/Buffer.hs
+++ b/Spear/Render/Core/Buffer.hs
@@ -14,6 +14,7 @@ import Spear.Math.Vector
14import Spear.Render.Core.State 14import Spear.Render.Core.State
15 15
16import Control.Monad (unless, void) 16import Control.Monad (unless, void)
17import Control.Monad.IO.Class
17import qualified Data.HashMap as HashMap 18import qualified Data.HashMap as HashMap
18import Data.Word 19import Data.Word
19import Foreign.C.Types 20import Foreign.C.Types
@@ -68,8 +69,8 @@ deleteBuffer buffer = do
68 release' buffer 69 release' buffer
69 70
70-- TODO: use glBufferSubData for updates. 71-- TODO: use glBufferSubData for updates.
71updateBuffer :: Buffer -> BufferData -> IO () 72updateBuffer :: MonadIO io => Buffer -> BufferData -> io ()
72updateBuffer buffer bufferData = 73updateBuffer buffer bufferData = liftIO $
73 unless (bufferEmpty bufferData) $ do 74 unless (bufferEmpty bufferData) $ do
74 glBindBuffer GL_ARRAY_BUFFER (bufferHandle buffer) 75 glBindBuffer GL_ARRAY_BUFFER (bufferHandle buffer)
75 uploadData (bufferUsage buffer) bufferData 76 uploadData (bufferUsage buffer) bufferData
diff --git a/Spear/Render/Core/Geometry.hs b/Spear/Render/Core/Geometry.hs
index 10ff709..05c23ec 100644
--- a/Spear/Render/Core/Geometry.hs
+++ b/Spear/Render/Core/Geometry.hs
@@ -21,6 +21,7 @@ import Spear.Render.Core.Buffer
21import Spear.Render.Core.Constants 21import Spear.Render.Core.Constants
22import Spear.Render.Core.State 22import Spear.Render.Core.State
23 23
24import Control.Monad.IO.Class
24import Data.HashMap as HashMap 25import Data.HashMap as HashMap
25import Data.IORef 26import Data.IORef
26import Data.Maybe (fromJust) 27import Data.Maybe (fromJust)
@@ -105,8 +106,8 @@ deleteGeometry geometry = do
105 geometries = HashMap.delete (geometryVao geometry) (geometries state) }) 106 geometries = HashMap.delete (geometryVao geometry) (geometries state) })
106 release' geometry 107 release' geometry
107 108
108renderGeometry :: Geometry -> IO () 109renderGeometry :: MonadIO io => Geometry -> io ()
109renderGeometry geometry = do 110renderGeometry geometry = liftIO $ do
110 gdata <- readIORef (geometryData geometry) 111 gdata <- readIORef (geometryData geometry)
111 let mode = toGLPrimitiveType $ geometryPrimitiveType gdata 112 let mode = toGLPrimitiveType $ geometryPrimitiveType gdata
112 glBindVertexArray (geometryVao geometry) 113 glBindVertexArray (geometryVao geometry)
@@ -121,8 +122,8 @@ renderGeometry geometry = do
121 122
122-- Functions for updating dynamic geometry. 123-- Functions for updating dynamic geometry.
123 124
124setPositions :: Geometry -> [Vector3] -> IO () 125setPositions :: MonadIO io => Geometry -> [Vector3] -> io ()
125setPositions geometry vectors = do 126setPositions geometry vectors = liftIO $ do
126 gdata <- readIORef $ geometryData geometry 127 gdata <- readIORef $ geometryData geometry
127 case vertexPositions gdata of 128 case vertexPositions gdata of
128 VertexPositions3d view -> do 129 VertexPositions3d view -> do
diff --git a/Spear/Render/Core/Pipeline.hs b/Spear/Render/Core/Pipeline.hs
index 724b391..ee9c7d2 100644
--- a/Spear/Render/Core/Pipeline.hs
+++ b/Spear/Render/Core/Pipeline.hs
@@ -13,8 +13,9 @@ module Spear.Render.Core.Pipeline
13) 13)
14where 14where
15 15
16import Data.Bits ((.|.)) 16import Control.Monad.IO.Class
17import Data.List (foldl') 17import Data.Bits ((.|.))
18import Data.List (foldl')
18import Graphics.GL.Core46 19import Graphics.GL.Core46
19 20
20 21
@@ -24,7 +25,7 @@ data BufferTarget
24 | StencilBuffer 25 | StencilBuffer
25 26
26 27
27clearBuffers :: [BufferTarget] -> IO () 28clearBuffers :: MonadIO io => [BufferTarget] -> io ()
28clearBuffers = glClear . toBufferBitfield 29clearBuffers = glClear . toBufferBitfield
29 where toBufferBitfield = foldl' (.|.) 0 . (<$>) toGLEnum 30 where toBufferBitfield = foldl' (.|.) 0 . (<$>) toGLEnum
30 toGLEnum target = case target of 31 toGLEnum target = case target of
@@ -32,28 +33,28 @@ clearBuffers = glClear . toBufferBitfield
32 DepthBuffer -> GL_DEPTH_BUFFER_BIT 33 DepthBuffer -> GL_DEPTH_BUFFER_BIT
33 StencilBuffer -> GL_STENCIL_BUFFER_BIT 34 StencilBuffer -> GL_STENCIL_BUFFER_BIT
34 35
35setBlending :: Bool -> IO () 36setBlending :: MonadIO io => Bool -> io ()
36setBlending enable = 37setBlending enable =
37 if enable 38 if enable
38 then glEnable GL_BLEND >> glBlendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA 39 then glEnable GL_BLEND >> glBlendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
39 else glDisable GL_BLEND 40 else glDisable GL_BLEND
40 41
41setClearColour :: (Float, Float, Float, Float) -> IO () 42setClearColour :: MonadIO io => (Float, Float, Float, Float) -> io ()
42setClearColour (r,g,b,a) = glClearColor r g b a 43setClearColour (r,g,b,a) = glClearColor r g b a
43 44
44setClearDepth :: Double -> IO () 45setClearDepth :: MonadIO io => Double -> io ()
45setClearDepth = glClearDepth 46setClearDepth = glClearDepth
46 47
47setClearStencil :: Int -> IO () 48setClearStencil :: MonadIO io => Int -> io ()
48setClearStencil = glClearStencil . fromIntegral 49setClearStencil = glClearStencil . fromIntegral
49 50
50setCulling :: Bool -> IO () 51setCulling :: MonadIO io => Bool -> io ()
51setCulling enable = (if enable then glEnable else glDisable) GL_CULL_FACE 52setCulling enable = (if enable then glEnable else glDisable) GL_CULL_FACE
52 53
53setDepthMask :: Bool -> IO () 54setDepthMask :: MonadIO io => Bool -> io ()
54setDepthMask enable = glDepthMask (if enable then GL_TRUE else GL_FALSE) 55setDepthMask enable = glDepthMask (if enable then GL_TRUE else GL_FALSE)
55 56
56setPolygonOffset :: Float -> Float -> IO () 57setPolygonOffset :: MonadIO io => Float -> Float -> io ()
57setPolygonOffset scale bias = do 58setPolygonOffset scale bias = do
58 glPolygonOffset scale bias 59 glPolygonOffset scale bias
59 if scale /= 0 && bias /= 0 60 if scale /= 0 && bias /= 0
@@ -61,6 +62,7 @@ setPolygonOffset scale bias = do
61 else glDisable GL_POLYGON_OFFSET_FILL 62 else glDisable GL_POLYGON_OFFSET_FILL
62 63
63setViewport :: 64setViewport ::
65 MonadIO io =>
64 -- | x 66 -- | x
65 Int -> 67 Int ->
66 -- | y 68 -- | y
@@ -69,6 +71,6 @@ setViewport ::
69 Int -> 71 Int ->
70 -- | height 72 -- | height
71 Int -> 73 Int ->
72 IO () 74 io ()
73setViewport x y width height = 75setViewport x y width height =
74 glViewport (fromIntegral x) (fromIntegral y) (fromIntegral width) (fromIntegral height) 76 glViewport (fromIntegral x) (fromIntegral y) (fromIntegral width) (fromIntegral height)
diff --git a/Spear/Render/Core/Shader.hs b/Spear/Render/Core/Shader.hs
index 21db66f..32a3cb1 100644
--- a/Spear/Render/Core/Shader.hs
+++ b/Spear/Render/Core/Shader.hs
@@ -20,6 +20,7 @@ import Spear.Math.Vector
20import Spear.Render.Core.State 20import Spear.Render.Core.State
21 21
22import Control.Monad (mapM_) 22import Control.Monad (mapM_)
23import Control.Monad.IO.Class
23import Data.Bits 24import Data.Bits
24import Data.Hashable 25import Data.Hashable
25import Data.HashMap as HashMap 26import Data.HashMap as HashMap
@@ -146,22 +147,22 @@ deleteShaderProgram program = do
146 shaderPrograms = HashMap.delete (shaderProgramHash program) (shaderPrograms state)}) 147 shaderPrograms = HashMap.delete (shaderProgramHash program) (shaderPrograms state)})
147 release' program 148 release' program
148 149
149activateShaderProgram :: ShaderProgram -> IO () 150activateShaderProgram :: MonadIO io => ShaderProgram -> io ()
150activateShaderProgram program = do 151activateShaderProgram program = do
151 glUseProgram . shaderProgramHandle $ program 152 glUseProgram . shaderProgramHandle $ program
152 applyUniforms program 153 applyUniforms program
153 154
154deactivateShaderProgram :: ShaderProgram -> IO () 155deactivateShaderProgram :: MonadIO io => ShaderProgram -> io ()
155deactivateShaderProgram _ = glUseProgram 0 156deactivateShaderProgram _ = glUseProgram 0
156 157
157setUniform :: ShaderUniform -> ShaderProgram -> IO () 158setUniform :: MonadIO io => ShaderUniform -> ShaderProgram -> io ()
158setUniform uniform program = 159setUniform uniform program = liftIO $
159 modifyIORef (shaderProgramUniforms program) (setUniform' . removeUniform) 160 modifyIORef (shaderProgramUniforms program) (setUniform' . removeUniform)
160 where removeUniform = deleteBy matchesUniform uniform 161 where removeUniform = deleteBy matchesUniform uniform
161 matchesUniform uniform u = uniformName u == uniformName uniform 162 matchesUniform uniform u = uniformName u == uniformName uniform
162 setUniform' = (:) uniform 163 setUniform' = (:) uniform
163 164
164applyUniforms :: ShaderProgram -> IO () 165applyUniforms :: MonadIO io => ShaderProgram -> io ()
165applyUniforms program = 166applyUniforms program =
166 let update (FloatUniform name value) = 167 let update (FloatUniform name value) =
167 glGetUniformLocation' handle name >>= 168 glGetUniformLocation' handle name >>=
@@ -181,7 +182,7 @@ applyUniforms program =
181 \location -> withArray mat4s $ \ptrMat4s -> 182 \location -> withArray mat4s $ \ptrMat4s ->
182 glUniformMatrix4fv location (fromIntegral $ length mat4s) GL_FALSE (unsafeCoerce ptrMat4s) 183 glUniformMatrix4fv location (fromIntegral $ length mat4s) GL_FALSE (unsafeCoerce ptrMat4s)
183 handle = shaderProgramHandle program 184 handle = shaderProgramHandle program
184 in do 185 in liftIO $ do
185 uniforms <- readIORef (shaderProgramUniforms program) 186 uniforms <- readIORef (shaderProgramUniforms program)
186 mapM_ update uniforms 187 mapM_ update uniforms
187 writeIORef (shaderProgramUniforms program) [] 188 writeIORef (shaderProgramUniforms program) []
@@ -189,7 +190,7 @@ applyUniforms program =
189-- Private 190-- Private
190 191
191glGetUniformLocation' :: GLuint -> String -> IO GLint 192glGetUniformLocation' :: GLuint -> String -> IO GLint
192glGetUniformLocation' handle name = 193glGetUniformLocation' handle name = liftIO $
193 withCString name $ \nameCStr -> 194 withCString name $ \nameCStr ->
194 glGetUniformLocation (fromIntegral handle) (unsafeCoerce nameCStr) 195 glGetUniformLocation (fromIntegral handle) (unsafeCoerce nameCStr)
195 196
diff --git a/Spear/Render/Immediate.hs b/Spear/Render/Immediate.hs
index 26f6513..b3a8998 100644
--- a/Spear/Render/Immediate.hs
+++ b/Spear/Render/Immediate.hs
@@ -79,22 +79,21 @@ deleteImmRenderer immState = do
79immStart :: Game ImmRenderState () 79immStart :: Game ImmRenderState ()
80immStart = do 80immStart = do
81 state <- get 81 state <- get
82 liftIO $ activateShaderProgram (shader state) 82 activateShaderProgram (shader state)
83 83
84immEnd :: Game ImmRenderState () 84immEnd :: Game ImmRenderState ()
85immEnd = do 85immEnd = do
86 state <- get 86 state <- get
87 liftIO $ deactivateShaderProgram (shader state) 87 deactivateShaderProgram (shader state)
88 88
89immDrawTriangles :: [Vector3] -> Game ImmRenderState () 89immDrawTriangles :: [Vector3] -> Game ImmRenderState ()
90immDrawTriangles vertices = do 90immDrawTriangles vertices = do
91 unless (null vertices) $ do 91 unless (null vertices) $ do
92 loadMatrixStack 92 loadMatrixStack
93 state <- get 93 state <- get
94 liftIO $ do 94 setPositions (triangles state) vertices
95 setPositions (triangles state) vertices 95 applyUniforms (shader state)
96 applyUniforms (shader state) 96 renderGeometry (triangles state)
97 renderGeometry (triangles state)
98 97
99-- NOTE: consider using triangle strips for quads. This will require a separate 98-- NOTE: consider using triangle strips for quads. This will require a separate
100-- Geometry. Using Vector3 for everything currently makes this simple. 99-- Geometry. Using Vector3 for everything currently makes this simple.
@@ -137,17 +136,17 @@ immPreservingMatrix f = do
137immSetColour :: Vector4 -> Game ImmRenderState () 136immSetColour :: Vector4 -> Game ImmRenderState ()
138immSetColour colour = do 137immSetColour colour = do
139 state <- get 138 state <- get
140 liftIO $ setUniform (Vec4Uniform "Colour" colour) (shader state) 139 setUniform (Vec4Uniform "Colour" colour) (shader state)
141 140
142immSetModelMatrix :: Matrix4 -> Game ImmRenderState () 141immSetModelMatrix :: Matrix4 -> Game ImmRenderState ()
143immSetModelMatrix model = do 142immSetModelMatrix model = do
144 state <- get 143 state <- get
145 liftIO $ setUniform (Mat4Uniform "Model" model) (shader state) 144 setUniform (Mat4Uniform "Model" model) (shader state)
146 145
147immSetViewProjectionMatrix :: Matrix4 -> Game ImmRenderState () 146immSetViewProjectionMatrix :: Matrix4 -> Game ImmRenderState ()
148immSetViewProjectionMatrix viewProjection = do 147immSetViewProjectionMatrix viewProjection = do
149 state <- get 148 state <- get
150 liftIO $ setUniform (Mat4Uniform "ViewProjection" viewProjection) (shader state) 149 setUniform (Mat4Uniform "ViewProjection" viewProjection) (shader state)
151 150
152-- Private 151-- Private
153 152
diff --git a/Spear/Scene/Loader.hs b/Spear/Scene/Loader.hs
index 4bbbde0..5f96f8c 100644
--- a/Spear/Scene/Loader.hs
+++ b/Spear/Scene/Loader.hs
@@ -58,7 +58,7 @@ validate _ = Nothing
58 58
59-- | Load the scene described by the given 'SceneGraph'. 59-- | Load the scene described by the given 'SceneGraph'.
60resourceMap :: SceneGraph -> Game s SceneResources 60resourceMap :: SceneGraph -> Game s SceneResources
61resourceMap g = execSubGame (resourceMap' g) emptySceneResources 61resourceMap g = exec runSubGame emptySceneResources (resourceMap' g)
62 62
63resourceMap' :: SceneGraph -> Loader () 63resourceMap' :: SceneGraph -> Loader ()
64resourceMap' node@(SceneLeaf nid props) = do 64resourceMap' node@(SceneLeaf nid props) = do
diff --git a/Spear/Sound/Sound.hs b/Spear/Sound/Sound.hs
index 53a1a46..832ffb8 100644
--- a/Spear/Sound/Sound.hs
+++ b/Spear/Sound/Sound.hs
@@ -16,9 +16,10 @@ where
16import Spear.Game 16import Spear.Game
17import Spear.Sound.State 17import Spear.Sound.State
18 18
19import Data.Set as Set 19import Control.Monad.IO.Class
20import Data.StateVar (($=)) 20import Data.Set as Set
21import qualified Sound.ALUT as AL 21import Data.StateVar (($=))
22import qualified Sound.ALUT as AL
22 23
23 24
24data LoopMode 25data LoopMode
@@ -86,16 +87,16 @@ deleteSoundSource source = do
86 release' source 87 release' source
87 88
88-- | Set the sound that the sound source emits. 89-- | Set the sound that the sound source emits.
89setSoundSourceBuffer :: SoundSource -> SoundBuffer -> IO () 90setSoundSourceBuffer :: MonadIO io => SoundSource -> SoundBuffer -> io ()
90setSoundSourceBuffer source buffer = 91setSoundSourceBuffer source buffer =
91 AL.buffer (alSource source) $= Just (alBuffer buffer) 92 AL.buffer (alSource source) $= Just (alBuffer buffer)
92 93
93-- | Set the sound's loop mode. 94-- | Set the sound's loop mode.
94setSoundLoopMode :: SoundSource -> LoopMode -> IO () 95setSoundLoopMode :: MonadIO io => SoundSource -> LoopMode -> io ()
95setSoundLoopMode source mode = AL.loopingMode (alSource source) $= alMode mode 96setSoundLoopMode source mode = AL.loopingMode (alSource source) $= alMode mode
96 where alMode SingleShot = AL.OneShot 97 where alMode SingleShot = AL.OneShot
97 alMode Loop = AL.Looping 98 alMode Loop = AL.Looping
98 99
99-- | Play the sound sources. 100-- | Play the sound sources.
100playSounds :: [SoundSource] -> IO () 101playSounds :: MonadIO io => [SoundSource] -> io ()
101playSounds = AL.play . (alSource <$>) 102playSounds = AL.play . (alSource <$>)
diff --git a/Spear/Sys/Timer.hsc b/Spear/Sys/Timer.hsc
index fb18521..46a692d 100644
--- a/Spear/Sys/Timer.hsc
+++ b/Spear/Sys/Timer.hsc
@@ -25,6 +25,7 @@ import Foreign.Marshal.Alloc (alloca)
25import Foreign.Ptr 25import Foreign.Ptr
26import Foreign.Storable 26import Foreign.Storable
27import Control.Monad 27import Control.Monad
28import Control.Monad.IO.Class
28import System.IO.Unsafe 29import System.IO.Unsafe
29 30
30#include "Timer/timer.h" 31#include "Timer/timer.h"
@@ -134,22 +135,22 @@ withTimer' c_func timer = alloca $ \ptr -> do
134 peek ptr 135 peek ptr
135 136
136-- | Construct a new timer. 137-- | Construct a new timer.
137newTimer :: IO Timer 138newTimer :: MonadIO io => io Timer
138newTimer = alloca $ \ptr -> do 139newTimer = liftIO . alloca $ \ptr -> do
139 c_timer_make ptr 140 c_timer_make ptr
140 peek ptr 141 peek ptr
141 142
142-- | Start the timer. 143-- | Start the timer.
143start :: Timer -> IO () 144start :: MonadIO io => Timer -> io ()
144start = withTimer c_timer_start 145start = liftIO . withTimer c_timer_start
145 146
146-- | Update the timer. 147-- | Update the timer.
147tick :: Timer -> IO Timer 148tick :: MonadIO io => Timer -> io Timer
148tick = withTimer' c_timer_tick 149tick = liftIO . withTimer' c_timer_tick
149 150
150-- | Get the current time. 151-- | Get the current time.
151now :: IO TimePoint 152now :: MonadIO io => io TimePoint
152now = alloca $ \ptr -> do 153now = liftIO . alloca $ \ptr -> do
153 c_time_now ptr 154 c_time_now ptr
154 peek ptr 155 peek ptr
155 156
@@ -186,5 +187,5 @@ timeAdd t dt = unsafeDupablePerformIO $
186 peek ptr 187 peek ptr
187 188
188-- | Put the caller thread to sleep for the given amount of time. 189-- | Put the caller thread to sleep for the given amount of time.
189sleep :: TimeDelta -> IO () 190sleep :: MonadIO io => TimeDelta -> io ()
190sleep = c_time_sleep 191sleep = liftIO . c_time_sleep
diff --git a/Spear/Window.hs b/Spear/Window.hs
index 75a38f7..a873362 100644
--- a/Spear/Window.hs
+++ b/Spear/Window.hs
@@ -34,6 +34,7 @@ import Spear.Game
34import Control.Concurrent.MVar 34import Control.Concurrent.MVar
35import Control.Exception 35import Control.Exception
36import Control.Monad (foldM, unless, void, when) 36import Control.Monad (foldM, unless, void, when)
37import Control.Monad.IO.Class
37import Data.Functor ((<&>)) 38import Data.Functor ((<&>))
38import Data.Maybe (fromJust, fromMaybe, isJust) 39import Data.Maybe (fromJust, fromMaybe, isJust)
39import qualified Graphics.UI.GLFW as GLFW 40import qualified Graphics.UI.GLFW as GLFW
@@ -76,12 +77,8 @@ data Window = Window
76 } 77 }
77 78
78 79
79withWindow :: 80withWindow :: MonadIO io => Dimensions -> WindowTitle -> (Window -> IO a) -> io a
80 Dimensions -> 81withWindow dim@(w, h) windowTitle run = liftIO $ do
81 WindowTitle ->
82 (Window -> IO a) ->
83 IO a
84withWindow dim@(w, h) windowTitle run = do
85 window <- do 82 window <- do
86 success <- GLFW.init 83 success <- GLFW.init
87 unless success $ throw (WindowException "GLFW.initialize failed") 84 unless success $ throw (WindowException "GLFW.initialize failed")
@@ -91,11 +88,8 @@ withWindow dim@(w, h) windowTitle run = do
91 GLFW.terminate 88 GLFW.terminate
92 return result 89 return result
93 90
94setup :: 91setup :: MonadIO io => Dimensions -> WindowTitle -> io Window
95 Dimensions -> 92setup (w, h) windowTitle = liftIO $ do
96 WindowTitle ->
97 IO Window
98setup (w, h) windowTitle = do
99 closeRequest <- newEmptyMVar 93 closeRequest <- newEmptyMVar
100 windowEvents <- newEmptyMVar 94 windowEvents <- newEmptyMVar
101 inputEvents <- newEmptyMVar 95 inputEvents <- newEmptyMVar
@@ -125,14 +119,14 @@ setup (w, h) windowTitle = do
125 return $ Window window closeRequest inputEvents windowEvents 119 return $ Window window closeRequest inputEvents windowEvents
126 120
127-- | Poll for input events. 121-- | Poll for input events.
128pollInputEvents :: Window -> IO [InputEvent] 122pollInputEvents :: MonadIO io => Window -> io [InputEvent]
129pollInputEvents window = do 123pollInputEvents window = liftIO $ do
130 GLFW.pollEvents 124 GLFW.pollEvents
131 getEvents (inputEventsMVar window) 125 getEvents (inputEventsMVar window)
132 126
133-- | Poll for window events. 127-- | Poll for window events.
134pollWindowEvents :: Window -> IO [WindowEvent] 128pollWindowEvents :: MonadIO io => Window -> io [WindowEvent]
135pollWindowEvents window = do 129pollWindowEvents window = liftIO $ do
136 GLFW.pollEvents 130 GLFW.pollEvents
137 getEvents (windowEventsMVar window) 131 getEvents (windowEventsMVar window)
138 132
@@ -144,16 +138,16 @@ getEvents mvar = tryTakeMVar mvar >>= \xs -> do
144 Just events -> return events 138 Just events -> return events
145 139
146-- | Return true when the user requests to close the window. 140-- | Return true when the user requests to close the window.
147shouldWindowClose :: Window -> IO Bool 141shouldWindowClose :: MonadIO io => Window -> io Bool
148shouldWindowClose = getRequest . closeRequestMVar 142shouldWindowClose = liftIO . getRequest . closeRequestMVar
149 143
150-- | Swaps buffers. 144-- | Swaps buffers.
151swapBuffers :: Window -> IO () 145swapBuffers :: MonadIO io => Window -> io ()
152swapBuffers = GLFW.swapBuffers . glfwWindow 146swapBuffers = liftIO . GLFW.swapBuffers . glfwWindow
153 147
154-- | Get the window's size. 148-- | Get the window's size.
155getWindowSize :: Window -> IO (Width, Height) 149getWindowSize :: MonadIO io => Window -> io (Width, Height)
156getWindowSize = GLFW.getWindowSize . glfwWindow 150getWindowSize = liftIO . GLFW.getWindowSize . glfwWindow
157 151
158getRequest :: MVar Bool -> IO Bool 152getRequest :: MVar Bool -> IO Bool
159getRequest mvar = 153getRequest mvar =