spice-0.5.0.0: src/FRP/Spice/Internal/Engine.hs
{-|
This module contains all of the functions to create and start the engine. The
only exposed function is @'startEngine'@ seeing as every other function is to
be used solely internally within the module.
-}
module FRP.Spice.Internal.Engine ( startEngine
, startEngineDefault
) where
--------------------
-- Global Imports --
import Graphics.Rendering.OpenGL
import Graphics.UI.GLFW as GLFW
import FRP.Elerea.Param
import Data.Default
import Data.IORef
-------------------
-- Local Imports --
import FRP.Spice.Internal.Assets
import FRP.Spice.Internal.Input
import FRP.Spice.Internal.Types
----------
-- Code --
{-|
The callback for the window closing.
-}
closeCallback :: IORef Bool -> WindowCloseCallback
closeCallback closedRef = do
writeIORef closedRef True
return True
{-|
The callback for the window resizing.
-}
resizeCallback :: WindowSizeCallback
resizeCallback size@(Size w' h') = do
let (Vector w h) = Vector (fromIntegral w' / 640) (fromIntegral h' / 480)
matrixMode $= Projection
loadIdentity
ortho (-w) ( w)
(-h) ( h)
(-1) ( 1)
matrixMode $= Modelview 0
viewport $= (Position 0 0, size)
{-|
Creating the elerea network to be used in @'driveNetwork'@.
-}
makeNetwork :: Game a => a -> Signal Input -> IO (Float -> IO a)
makeNetwork game inputSignal = start $ transfer game update inputSignal
{-|
Either sending back a 'should close window' (in the form of a Nothing) signal
or the @'DeltaTime'@ (type synonym for @'Float'@) since the last update.
-}
runInput :: IORef Bool -> IO (Maybe DeltaTime)
runInput closedRef = do
pollEvents
closed <- readIORef closedRef
t <- get time
time $= 0
return $ if closed
then Nothing
else Just $ realToFrac t
{-|
Driving the network created from @'makeNetwork'@ with the input created from
@'runInput'@ to run the game loop.
-}
driveNetwork :: Game a => Assets -> (Float -> IO a) -> IO (Maybe Float) -> IO ()
driveNetwork assets network iomdriver = do
mdriver <- iomdriver
case mdriver of
Nothing -> return ()
Just driver -> do
game <- network driver
renderWrapper $ render assets game
driveNetwork assets network iomdriver
where renderWrapper :: Scene -> IO ()
renderWrapper scene = do
clear [ColorBuffer]
scene
swapBuffers
{-|
Starting the engine with window parameters described within the provided
@'WindowConfig'@.
-}
startEngine :: Game a => WindowConfig -> a -> IO ()
startEngine wc game = do
-- Opening the window
initialize
openWindow (Size (fromIntegral $ getWindowWidth wc)
(fromIntegral $ getWindowHeight wc))
[DisplayRGBBits 8 8 8, DisplayAlphaBits 8, DisplayDepthBits 24]
(case getWindowFullscreen wc of
True -> FullScreen
False -> Window)
-- An IORef to keep track of when the window is closed
closedRef <- newIORef False
-- Setting window parameters
windowTitle $= getWindowTitle wc
windowCloseCallback $= closeCallback closedRef
windowSizeCallback $= resizeCallback
-- Loading the assets
assets <- performAssetLoads $ loadAssets game
-- Creating the input container
ic <- makeInputContainer
-- Setting the input callbacks
mousePosCallback $= makeMousePosCallback ic
mouseButtonCallback $= makeMouseButtonCallback ic
keyCallback $= makeKeyCallback ic
-- Creating the network
network <- makeNetwork game $ getInput ic
-- Starting the network
time $= 0
driveNetwork assets network $ runInput closedRef
-- Closing the window, after all is said and done
closeWindow
{-|
Starting the engine with default window parameter.
-}
startEngineDefault :: Game a => a -> IO ()
startEngineDefault = startEngine def