packages feed

gore-and-ash-glfw-1.1.0.0: src/Game/GoreAndAsh/GLFW/API.hs

{-# OPTIONS_GHC -fno-warn-orphans #-}
{-|
Module      : Game.GoreAndAsh.GLFW.API
Description : Monadic and arrow API for GLFW core module
Copyright   : (c) Anton Gushcha, 2015-2016
License     : BSD3
Maintainer  : ncrashed@gmail.com
Stability   : experimental
Portability : POSIX

The module contains monadic and arrow API of the core module.
-}
module Game.GoreAndAsh.GLFW.API(
    MonadGLFW(..)
  -- * Keyboard API
  , keyStatus
  , keyStatusDyn
  , keyPressed
  , keyPressedDyn
  , keyReleased
  , keyReleasedDyn
  , keyRepeating
  , keyRepeatingDyn
  , keyPressing
  , keyPressingDyn
  -- * Mouse buttons API
  , mouseButton
  , mouseButtonDyn
  , mouseButtonPressed
  , mouseButtonPressedDyn
  , mouseButtonReleased
  , mouseButtonReleasedDyn
  -- * Cursor position
  , mousePosition
  , mousePositionChange
  , mouseXChange
  , mouseYChange
  , mouseDelta
  , mouseDeltaChange
  , mouseDeltaXChange
  , mouseDeltaYChange
  -- * Mouse scroll
  , mouseScroll
  , mouseScrollX
  , mouseScrollY
  -- * Window API
  , windowSize
  , windowClosing
  -- * Reexports
  , Key(..)
  , KeyState(..)
  , MouseButton(..)
  , MouseButtonState(..)
  , ModifierKeys(..)
  ) where

import Prelude hiding (id, (.))
import Control.Wire 

import Control.Monad.State.Strict 
import Control.Wire.Unsafe.Event
import Graphics.UI.GLFW
import qualified Data.HashMap.Strict as M 

import Game.GoreAndAsh
import Game.GoreAndAsh.GLFW.State
import Game.GoreAndAsh.GLFW.Module 

-- | Module low-level API
class Monad m => MonadGLFW m where 
  -- | Returns state of given keyboard's key
  keyStatusM :: Key -> m (Maybe (KeyState, ModifierKeys))
  -- | Returns state of given mouse button
  mouseButtonM :: MouseButton -> m (Maybe (MouseButtonState, ModifierKeys))
  -- | Returns current position of mouse cursor
  mousePosM :: m (Double, Double)
  -- | Returns current scroll values of mouse
  mouseScrollM :: m [(Double, Double)]
  -- | Returns current size of window
  windowSizeM :: m (Maybe (Double, Double))
  -- | Returns True when close button is pushed
  windowClosingM :: m Bool 
  -- | Setups current window for input catch
  setCurrentWindowM :: Maybe Window -> m ()
  -- | Returns current window 
  getCurrentWindowM :: m (Maybe Window)
  -- | Setup maximum size of inner buffers for keys, mouse buttons
  setBufferSizeM :: Int -> m ()

instance {-# OVERLAPPING #-} Monad m => MonadGLFW (GLFWT s m) where 
  keyStatusM k = do 
    GLFWState{..} <- GLFWT get
    return $ M.lookup k glfwKeys

  mouseButtonM b = do 
    GLFWState{..} <- GLFWT get 
    return $ M.lookup b glfwMouseButtons

  mousePosM = GLFWT $ glfwMousePos <$> get 
  mouseScrollM = GLFWT $ glfwScroll <$> get 
  windowSizeM = GLFWT $ glfwWindowSize <$> get 
  windowClosingM = GLFWT $ glfwClose <$> get 

  setCurrentWindowM w = GLFWT $ do 
    s <- get 
    put $ s { 
        glfwWindow = w 
      , glfwPrevWindow = glfwWindow s 
      }

  getCurrentWindowM = GLFWT $ do 
    s <- get 
    return . glfwWindow $! s 

  setBufferSizeM i = GLFWT $ do 
    s <- get 
    put $ s {
        glfwBufferSize = i 
      }

instance {-# OVERLAPPABLE #-} (Monad (mt m), MonadGLFW m, MonadTrans mt) => MonadGLFW (mt m) where 
  keyStatusM = lift . keyStatusM
  mouseButtonM = lift . mouseButtonM
  mousePosM = lift mousePosM
  mouseScrollM = lift mouseScrollM
  windowSizeM = lift windowSizeM
  windowClosingM = lift windowClosingM
  setCurrentWindowM = lift . setCurrentWindowM
  getCurrentWindowM = lift getCurrentWindowM
  setBufferSizeM = lift . setBufferSizeM
  
-- | Produces event when key state changes
keyStatus :: MonadGLFW m => Key -> GameWire m a (Event (KeyState, ModifierKeys))
keyStatus k = liftGameMonad (maybe2Event <$> keyStatusM k)

-- | Produces event when key state changes, get key as arrow argument
keyStatusDyn :: MonadGLFW m => GameWire m Key (Event (KeyState, ModifierKeys))
keyStatusDyn = liftGameMonad1 $ \k -> do 
  ms <- keyStatusM k 
  return $ maybe2Event ms 

-- | Simple transform from maybe to event
maybe2Event :: Maybe a -> Event a 
maybe2Event Nothing = NoEvent 
maybe2Event (Just a) = Event a

keyStated :: MonadGLFW m => KeyState -> Key -> GameWire m a (Event ModifierKeys)
keyStated ks k = mapE snd . filterE (\(ks', _) -> ks' == ks) . keyStatus k

keyStatedDyn :: MonadGLFW m => KeyState -> GameWire m Key (Event ModifierKeys)
keyStatedDyn ks = mapE snd . filterE (\(ks', _) -> ks' == ks) . keyStatusDyn

-- | Fires when keyboard key is pressed
keyPressed :: MonadGLFW m => Key -> GameWire m a (Event ModifierKeys)
keyPressed = keyStated KeyState'Pressed

-- | Version of keyPressed that takes key at runtime
keyPressedDyn :: MonadGLFW m => GameWire m Key (Event ModifierKeys)
keyPressedDyn = keyStatedDyn KeyState'Pressed

-- | Fires when keyboard key is released
keyReleased :: MonadGLFW m => Key -> GameWire m a (Event ModifierKeys)
keyReleased = keyStated KeyState'Released

-- | Version of keyReleased that takes key at runtime
keyReleasedDyn :: MonadGLFW m => GameWire m Key (Event ModifierKeys)
keyReleasedDyn = keyStatedDyn KeyState'Released

-- | Fires when keyboard key is entered into repeating mode
keyRepeating :: MonadGLFW m => Key -> GameWire m a (Event ModifierKeys)
keyRepeating = keyStated KeyState'Repeating

-- | Version of keyRepeating that takes key at runtime
keyRepeatingDyn :: MonadGLFW m => GameWire m Key (Event ModifierKeys)
keyRepeatingDyn = keyStatedDyn KeyState'Repeating

-- | Fires event from moment of press until release of given key
keyPressing :: MonadGLFW m => Key -> GameWire m a (Event ModifierKeys)
keyPressing k = go NoEvent 
  where
    go !e = mkGen $ \_ _ -> do 
      !mks <- keyStatusM k
      return $! case mks of 
        Nothing -> (Right e, go e)
        Just (!ks, !mds) -> case ks of 
          KeyState'Pressed -> (Right $! Event mds, go $! Event mds)
          KeyState'Released -> (Right NoEvent, go NoEvent)
          _ -> (Right e, go e)

-- | Version of keyPressing that takes key at runtime
keyPressingDyn :: MonadGLFW m => GameWire m Key (Event ModifierKeys)
keyPressingDyn = go NoEvent 
  where
    go !e = mkGen $ \_ k -> do 
      !mks <- keyStatusM k
      return $! case mks of 
        Nothing -> (Right e, go e)
        Just (!ks, !mds) -> case ks of 
          KeyState'Pressed -> (Right $! Event mds, go $! Event mds)
          KeyState'Released -> (Right NoEvent, go NoEvent)
          _ -> (Right e, go e)

-- | Produces event when mouse button state changes
mouseButton :: MonadGLFW m => MouseButton -> GameWire m a (Event (MouseButtonState, ModifierKeys))
mouseButton k = liftGameMonad (maybe2Event <$> mouseButtonM k)

-- | Produces event when key state changes, get key as arrow argument
mouseButtonDyn :: MonadGLFW m => GameWire m MouseButton (Event (MouseButtonState, ModifierKeys))
mouseButtonDyn = liftGameMonad1 $ \k -> do 
  ms <- mouseButtonM k 
  return $ maybe2Event ms 

mouseButtonStated :: MonadGLFW m => MouseButtonState -> MouseButton -> GameWire m a (Event ModifierKeys)
mouseButtonStated bs b = mapE snd . filterE (\(bs', _) -> bs == bs') . mouseButton b

mouseButtonStatedDyn :: MonadGLFW m => MouseButtonState -> GameWire m MouseButton (Event ModifierKeys)
mouseButtonStatedDyn bs = mapE snd . filterE (\(bs', _) -> bs == bs') . mouseButtonDyn

-- | Fires when mouse button is pressed
mouseButtonPressed :: MonadGLFW m => MouseButton -> GameWire m a (Event ModifierKeys)
mouseButtonPressed = mouseButtonStated MouseButtonState'Pressed 

-- | Version of mouseButtonPressed that takes button at runtime
mouseButtonPressedDyn :: MonadGLFW m => GameWire m MouseButton (Event ModifierKeys)
mouseButtonPressedDyn = mouseButtonStatedDyn MouseButtonState'Pressed

-- | Fires when mouse button is released
mouseButtonReleased :: MonadGLFW m => MouseButton -> GameWire m a (Event ModifierKeys)
mouseButtonReleased = mouseButtonStated MouseButtonState'Released 

-- | Version of mouseButtonReleased that takes button at runtime
mouseButtonReleasedDyn :: MonadGLFW m => GameWire m MouseButton (Event ModifierKeys)
mouseButtonReleasedDyn = mouseButtonStatedDyn MouseButtonState'Released

-- | Returns current position of mouse
mousePosition :: MonadGLFW m => GameWire m a (Double, Double)
mousePosition = liftGameMonad mousePosM

-- | Fires event when mouse position changes
mousePositionChange :: MonadGLFW m => GameWire m a (Event (Double, Double))
mousePositionChange = go 0 0
  where
    go !x !y = mkGen $ \_ _-> do 
      (!x', !y') <- mousePosM
      return $ if x /= x' || y /= y' 
        then (Right $! Event (x', y'), go x' y')
        else (Right NoEvent, go x y)

-- | Fires event when mouse X axis changes
mouseXChange :: MonadGLFW m => GameWire m a (Event Double)
mouseXChange = go 0 
  where
    go !x = mkGen $ \_ _-> do 
      (!x', _) <- mousePosM
      return $ if x /= x'
        then (Right $! Event x', go x')
        else (Right NoEvent, go x)

-- | Fires event when mouse Y axis changes
mouseYChange :: MonadGLFW m => GameWire m a (Event Double)
mouseYChange = go 0 
  where
    go !y = mkGen $ \_ _-> do 
      (_, !y') <- mousePosM
      return $ if y /= y'
        then (Right $! Event y', go y')
        else (Right NoEvent, go y)

-- | Returns mouse delta moves
mouseDelta :: MonadGLFW m => GameWire m a (Double, Double)
mouseDelta = go 0 0
  where 
    go !x !y = mkGen $ \_ _ -> do 
      (!x', !y') <- mousePosM
      let dx = x' - x 
          dy = y' - y
          res = Right (dx, dy)
      return $ dx `seq` dy `seq` (res, go x' y')

-- | Fires when mouse moves, holds delta move
mouseDeltaChange :: MonadGLFW m => GameWire m a (Event (Double, Double))
mouseDeltaChange = go 0 0
  where 
    go !x !y = mkGen $ \_ _ -> do 
      (!x', !y') <- mousePosM
      let dx = x' - x 
          dy = y' - y
          res = Right $! Event (dx, dy)
      return $ if x /= x' || y /= y' 
        then dx `seq` dy `seq` (res, go x' y')
        else (Right NoEvent, go x y)

-- | Fires when mouse X axis changes, holds delta move
mouseDeltaXChange :: MonadGLFW m => GameWire m a (Event Double)
mouseDeltaXChange = go 0 
  where 
    go !x = mkGen $ \_ _ -> do 
      (!x', _) <- mousePosM
      let dx = x' - x 
          res = Right $! Event dx
      return $ if x /= x' 
        then dx `seq` (res, go x')
        else (Right NoEvent, go x)

-- | Fires when mouse Y axis changes, holds delta move
mouseDeltaYChange :: MonadGLFW m => GameWire m a (Event Double)
mouseDeltaYChange = go 0 
  where 
    go !y = mkGen $ \_ _ -> do 
      (_, !y') <- mousePosM
      let dy = y' - y 
          res = Right $! Event dy
      return $ if y /= y'
        then dy `seq` (res, go y')
        else (Right NoEvent, go y)

-- | Fires when windows size is changed
windowSize :: MonadGLFW m => GameWire m a (Event (Double, Double))
windowSize = go 0 0 
  where
    go !x !y = mkGen $ \_ _ -> do 
      ms <- windowSizeM
      return $! case ms of 
        Nothing -> (Right NoEvent, go x y)
        Just (!x', !y') -> if x /= x' || y /= y' 
          then x' `seq` y' `seq` (Right $! Event (x', y'), go x' y')
          else (Right NoEvent, go x y)

-- | Fires when user scrolls
mouseScroll :: MonadGLFW m => GameWire m a (Event (Double, Double))
mouseScroll = mkGen_ $ \_ -> do 
  ss <- mouseScrollM
  return . Right $! case ss of 
    [] -> NoEvent
    ((!x', !y'):_) -> Event (x', y')

-- | Fires when user scrolls X axis
mouseScrollX :: MonadGLFW m => GameWire m a (Event Double)
mouseScrollX = mapE fst . mouseScroll

-- | Fires when user scrolls Y axis
mouseScrollY :: MonadGLFW m => GameWire m a (Event Double)
mouseScrollY = mapE snd . mouseScroll 

-- | Fires when user hits close button of window 
windowClosing :: MonadGLFW m => GameWire m a (Event ())
windowClosing = liftGameMonad $ do 
  f <- windowClosingM 
  return $! if f then Event ()
    else NoEvent