-- | Initialize an OpenGL window using the GLFW-b library
module GL where
--------------------------------------------------------------------------------
import Data.Char hiding ( Space )
import Control.Monad
import Control.Concurrent
import Control.Concurrent.MVar
import Data.IORef
import System.Exit
import System.IO.Unsafe as Unsafe
import Graphics.UI.GLFW as GLFW
import Graphics.Rendering.OpenGL as GL
--------------------------------------------------------------------------------
{-# NOINLINE theExitFlag #-}
theExitFlag :: IORef Bool
theExitFlag = Unsafe.unsafePerformIO $ newIORef False
{-# NOINLINE theRefreshTrigger #-}
theRefreshTrigger :: MVar ()
theRefreshTrigger = Unsafe.unsafePerformIO $ newMVar () -- newEmptyMVar
{-# NOINLINE theWindowSize #-}
theWindowSize :: IORef (Int,Int)
theWindowSize = Unsafe.unsafePerformIO $ newIORef $ error "window size not set"
{-# NOINLINE theDisplayFunction #-}
theDisplayFunction :: IORef (Window -> Double -> IO ())
theDisplayFunction = Unsafe.unsafePerformIO $ newIORef nodisplay where
nodisplay _ _ = return ()
--------------------------------------------------------------------------------
triggerExit :: IO ()
triggerExit = writeIORef theExitFlag True
setWindowCoordSystem :: IO ()
setWindowCoordSystem = do
(w,h) <- readIORef theWindowSize
viewport $=! (Position 0 0 , Size (fromIntegral w) (fromIntegral h))
matrixMode $=! Projection
loadIdentity
GL.ortho 0 (fromIntegral w) (fromIntegral h) 0 (-1) (1::Double)
matrixMode $=! Modelview 0
loadIdentity
--------------------------------------------------------------------------------
myErrorCallback :: GLFW.Error -> String -> IO ()
myErrorCallback err msg = do
putStrLn $ msg ++ "(" ++ show err ++ ")"
triggerExit
myKeyCallback :: Window -> Key -> Int -> KeyState -> ModifierKeys -> IO ()
myKeyCallback _ key nrepeat keyState modif =
case key of
Key'Escape -> triggerExit
_ -> return ()
myCharCallback :: Window -> Char -> IO ()
myCharCallback _ char = do
return ()
myWinCloseCallback :: Window -> IO ()
myWinCloseCallback window = triggerExit
myFrBufSizeCallback :: Window -> Int -> Int -> IO ()
myFrBufSizeCallback window xsiz ysiz = do
-- writeIORef theWindowSize (xsiz,ysiz)
-- putStrLn $ "framebuffer resized to " ++ show (xsiz,ysiz)
return ()
myRefreshCallback :: Window -> IO ()
myRefreshCallback window = do
-- putStrLn "refresh callback"
_ <- tryTakeMVar theRefreshTrigger
putMVar theRefreshTrigger ()
return ()
--------------------------------------------------------------------------------
{-
frac :: Double -> Double
frac x = x - fromIntegral (floor x :: Int)
fmod :: Double -> Double -> Double
fmod x s = frac (x/s) * s
-}
--------------------------------------------------------------------------------
redraw :: Window -> IO ()
redraw window = do
Just time <- getTime
(xsiz,ysiz) <- getFramebufferSize window -- window resizing is broken in glfw
writeIORef theWindowSize (xsiz,ysiz) -- this helps a little.
display <- readIORef theDisplayFunction
display window time
swapBuffers window
renderLoop :: Window -> IO ()
renderLoop window = loop where
loop = do
_ <- takeMVar theRefreshTrigger
redraw window
exit <- readIORef theExitFlag
unless exit loop
eventLoop :: IO ()
eventLoop = do
threadDelay 1
waitEvents
exit <- readIORef theExitFlag
unless exit eventLoop
--------------------------------------------------------------------------------
initGL :: IO precalc -> (precalc -> Window -> Double -> IO ()) -> IO ()
initGL userPrecalc userDisplay = do
setErrorCallback (Just myErrorCallback)
GLFW.init
Just window <- createWindow 800 500 "window title" Nothing Nothing
(xsiz,ysiz) <- getFramebufferSize window
putStrLn $ "initial framebuffer size = " ++ show (xsiz,ysiz)
writeIORef theWindowSize (xsiz,ysiz)
major <- getWindowContextVersionMajor window
minor <- getWindowContextVersionMinor window
rev <- getWindowContextVersionRevision window
putStrLn $ "OpenGL context version = " ++ show major ++ "." ++ show minor ++ "." ++ show rev
setWindowCloseCallback window (Just myWinCloseCallback )
setKeyCallback window (Just myKeyCallback )
setCharCallback window (Just myCharCallback )
setFramebufferSizeCallback window (Just myFrBufSizeCallback)
setWindowRefreshCallback window (Just myRefreshCallback )
forkOS $ do
makeContextCurrent (Just window)
swapInterval 1
precalc <- userPrecalc
writeIORef theDisplayFunction (userDisplay precalc)
renderLoop window
eventLoop
putStrLn "calling terminate"
terminate
exitWith ExitSuccess
--------------------------------------------------------------------------------