module Common(runWithSDL, FrameCallback(..), EventCallback(..))
where
import System.IO
import Control.Concurrent
import Control.Concurrent.STM
import qualified Graphics.UI.SDL as SDL
import Graphics.Ogre.Ogre
data FrameCallback a = FrameCallback { framecallback :: (a -> IO a) }
data EventCallback a = EventCallback { eventcallback :: (a -> [SDL.Event] -> IO a) }
pollAllSDLEvents :: IO [SDL.Event]
pollAllSDLEvents = go []
where go l = do
e <- SDL.pollEvent
if e == SDL.NoEvent
then return l
else do
es <- pollAllSDLEvents
return (e:es)
type Action = (Bool, -- right mouse button pressed
(Float, Float), -- rotation (yaw, pitch)
(Float, Float, Float), -- translation (x, y, z)
Bool) -- quit flag
eventToAction :: Action -> SDL.Event -> Action
eventToAction (bt, ro, t, _) SDL.Quit = (bt, ro, t, True)
eventToAction (bt, ro, t, _) (SDL.KeyDown (SDL.Keysym SDL.SDLK_ESCAPE _ _)) = (bt, ro, t, True)
eventToAction (bt, ro, t, _) (SDL.KeyDown (SDL.Keysym SDL.SDLK_q _ _)) = (bt, ro, t, True)
eventToAction (bt, ro, t@(x_, y_, z_), q) (SDL.KeyDown (SDL.Keysym k _ _)) = case k of
SDL.SDLK_UP -> (bt, ro, (x_, y_, z_ - 1.0), q)
SDL.SDLK_DOWN -> (bt, ro, (x_, y_, z_ + 1.0), q)
SDL.SDLK_RIGHT -> (bt, ro, (x_ + 1.0, y_, z_), q)
SDL.SDLK_LEFT -> (bt, ro, (x_ - 1.0, y_, z_), q)
SDL.SDLK_PAGEDOWN -> (bt, ro, (x_, y_ - 1.0, z_), q)
SDL.SDLK_PAGEUP -> (bt, ro, (x_, y_ + 1.0, z_), q)
_ -> (bt, ro, t, q)
eventToAction (bt, ro, t@(x_, y_, z_), q) (SDL.KeyUp (SDL.Keysym k _ _)) = case k of
SDL.SDLK_UP -> (bt, ro, (x_, y_, z_ + 1.0), q)
SDL.SDLK_DOWN -> (bt, ro, (x_, y_, z_ - 1.0), q)
SDL.SDLK_RIGHT -> (bt, ro, (x_ - 1.0, y_, z_), q)
SDL.SDLK_LEFT -> (bt, ro, (x_ + 1.0, y_, z_), q)
SDL.SDLK_PAGEDOWN -> (bt, ro, (x_, y_ + 1.0, z_), q)
SDL.SDLK_PAGEUP -> (bt, ro, (x_, y_ - 1.0, z_), q)
_ -> (bt, ro, t, q)
eventToAction (False, ro, t, q) (SDL.MouseMotion _ _ _ _) = (False, ro, t, q)
eventToAction (True, (ya, pit), t, q) (SDL.MouseMotion _ _ abs_x abs_y) = (True, (ya - (0.005 * fromIntegral abs_x), pit - (0.005 * fromIntegral abs_y)), t, q)
eventToAction (_, ro, t, q) (SDL.MouseButtonUp _ _ SDL.ButtonRight) = (False, ro, t, q)
eventToAction (_, ro, t, q) (SDL.MouseButtonDown _ _ SDL.ButtonRight) = (True, ro, t, q)
eventToAction (bt, ro, t, q) _ = (bt, ro, t, q)
resetRotation :: Action -> Action
resetRotation (bt, _, t, q) = (bt, (0, 0), t, q)
doAction :: (Float, Float) -> (Float, Float, Float) -> IO ()
doAction (ya, pit) (x_, y_, z_) = do
rotateCamera (YPR ya 0 0) World
rotateCamera (YPR 0 pit 0) Local
translateCamera (Vector3 x_ y_ z_)
shutdown :: IO ()
shutdown = do
putStrLn "Shutting down..."
cleanupOgre
runWithSDL :: IO () -> (a, EventCallback a, FrameCallback a) -> IO ()
runWithSDL initGame action = SDL.withInit [SDL.InitEverything] $ runThreadedNonblocking initGame action 20 20 >> return ()
nullAction :: Action
nullAction = (False, (0, 0), (0, 0, 0), False)
runThreadedNonblocking :: IO () -> (a, EventCallback a, FrameCallback a) -> Int -> Int -> IO ()
runThreadedNonblocking initGame (ival, ecb, fcb) renderinterval handleinterval = do
initGame
let ri = renderinterval * 1000
let si = handleinterval * 1000
box <- atomically $ newTMVar ival
rtid <- forkIO (renderLoop box fcb ri)
inputLoop si box ecb [rtid] nullAction
renderLoop :: TMVar a -> FrameCallback a -> Int -> IO ()
renderLoop box action ri = do
renderOgre
val <- atomically $ takeTMVar box
nval <- (framecallback action) val
atomically $ putTMVar box nval
threadDelay ri
renderLoop box action ri
fullCleanup :: [ThreadId] -> IO () -> IO ()
fullCleanup tids cf = mapM_ killThread tids >> cf
inputLoop :: Int -> TMVar a -> EventCallback a -> [ThreadId] -> Action -> IO ()
inputLoop si box action tids ac = do
i <- input ac box action
case i of
Nothing -> fullCleanup tids shutdown
Just nac -> threadDelay si >> inputLoop si box action tids (resetRotation nac)
input :: Action -> TMVar a -> EventCallback a -> IO (Maybe Action)
input ac box action = do
events <- pollAllSDLEvents
-- when (not (null events)) (print events >> (getCameraPosition >>= print))
let nac@(_, ro, t, q) = foldl eventToAction ac events
if q then return Nothing else do
val <- atomically $ takeTMVar box
nval <- (eventcallback action) val events
atomically $ putTMVar box nval
doAction ro t
return (Just nac)