module Dyna.Proc.Run( -- * Run the app runApp, Spec(..), -- * User interaction timeInterval, getClicks, mouse, mouseV, mouseA, relMouse, mouseRight, mouseLeft, mouseMiddle, mouseAdditional, mouseWheel, isDrag, drag, dragV, keyUp, keyDown, charUp, charDown, ) where import Control.Monad import Control.Monad.Reader import Control.Monad.IO.Class import Data.IORef import qualified Dyna as D import Dyna.Proc.Types import Graphics.Proc ( runProc, Proc(..), Default(..), Pio, Draw, P2, MouseButton(..), Key(..), Modifiers ) import qualified Graphics.Proc as P import Control.Concurrent.Chan.Unagi qualified as U -- | Initialisation of the application. -- It's processing setup procedure that is called -- once at the app start up. newtype Spec = Spec { spec'setup :: Pio () } runApp :: Spec -> Run (Dyn Draw) -> IO () runApp Spec{..} mDyn = do env <- newEnv ref <- runRun (D.runDyn . unDyn =<< mDyn) env runProc def { procSetup = spec'setup >> pure ref , procDraw = draw env , procUpdate = pure , procUpdateTime = \t -> through $ updateTime env t , procMousePressed = through $ mouseBy Down env , procMouseReleased = through $ mouseBy Up env , procKeyPressed = through $ keyBy Down env , procKeyReleased = through $ keyBy Up env } where through act st = act >> pure st draw env ref = join $ liftIO $ runRun (D.readDyn ref) env updateTime Env{..} t = do -- mouse input pos <- P.mouse relPos <- P.relMouse liftIO $ do prevPos <- readIORef env'mousePos prevDef1 <- readIORef env'mouseDif1 let dif1 = pos - prevPos writeIORef env'mousePos pos writeIORef env'mouseDif1 dif1 writeIORef env'mouseDif2 (dif1 - prevDef1) writeIORef env'relMousePos relPos writeIORef env'time t mouseBy keySt Env{..} = do mBut <- P.mouseButton forM_ mBut $ \but -> do mods <- P.modifiers pos <- P.mouse let ev = Click (Right but) keySt mods pos liftIO $ U.writeChan (fst env'clicks) ev keyBy keySt Env{..} = do k <- P.key mods <- P.modifiers pos <- P.mouse let ev = Click (Left k) keySt mods pos liftIO $ U.writeChan (fst env'clicks) ev ------------------------------------------------------------------------------------- -- user input -- | Time that has passed since previous step of simulation timeInterval :: Dyn Float timeInterval = Dyn $ D.constDyn (liftIO . readIORef =<< asks env'time) -- | Mouse position mouse :: Dyn P2 mouse = Dyn $ D.constDyn (liftIO . readIORef =<< asks env'mousePos) -- | Relative mouse position (if the whole screen is in the range [0, 1]) relMouse :: Dyn P2 relMouse = Dyn $ D.constDyn (liftIO . readIORef =<< asks env'relMousePos) -- | Mouse velocity mouseV :: Dyn P2 mouseV = Dyn $ D.constDyn (liftIO . readIORef =<< asks env'mouseDif1) -- | Mouse acceleration mouseA :: Dyn P2 mouseA = Dyn $ D.constDyn (liftIO . readIORef =<< asks env'mouseDif2) -- | Reads generic click events getClicks :: Evt Click getClicks = Evt $ D.Evt $ \go -> do clickChan <- asks env'clicks D.runEvt (D.uchanEvt $ fst clickChan) go -- | Generic mouse click event mouseButton :: MouseButton -> Evt P2 mouseButton btn' = Evt $ D.mapMay go $ unEvt getClicks where go = \case Click (Right btn) Down mods pos | btn == btn' -> Just pos _ -> Nothing -- | Event stream of clicks of the mouse right button mouseRight :: Evt P2 mouseRight = mouseButton RightButton -- | Event stream of clicks of the mouse left button mouseLeft :: Evt P2 mouseLeft = mouseButton LeftButton -- | Event stream of clicks of the mouse middle button mouseMiddle :: Evt P2 mouseMiddle = mouseButton MiddleButton -- | Event stream of clicks of the mouse additional button mouseAdditional :: Int -> Evt P2 mouseAdditional n = mouseButton (AdditionalButton n) -- | Mouse wheel displacement. -- If positive then it goes up, if negative then it goes down. mouseWheel :: MouseButton -> Evt Float mouseWheel btn' = Evt $ D.mapMay go $ unEvt getClicks where go = \case Click (Right WheelUp) Down _ _ -> Just 1 Click (Right WheelDown) Down _ _ -> Just (-1) _ -> Nothing -- | Event stream of key up actions keyUp :: Key -> Evt Modifiers keyUp = keyBy Up -- | Event stream of key down actions keyDown :: Key -> Evt Modifiers keyDown = keyBy Down -- | Event stream of char press up actions charUp :: Char -> Evt Modifiers charUp = keyUp . Char -- | Event stream of char press down actions charDown :: Char -> Evt Modifiers charDown = keyDown . Char keyBy :: KeyState -> Key -> Evt Modifiers keyBy st' key' = Evt $ D.mapMay go $ unEvt getClicks where go (Click (Left key) st mods _) | key == key' && st == st' = Just mods | otherwise = Nothing isDrag :: MouseButton -> Dyn Bool isDrag btn = Dyn $ D.scanD collect False $ D.mapMay go $ unEvt getClicks where go = \case Click (Right mbtn) st _ _ | mbtn == btn -> Just st _ -> Nothing collect a st = case a of Up -> False Down -> True -- | Displacement on drag, if no drag it becomes zero dragV :: MouseButton -> Dyn P2 dragV btn = (\x -> if x then id else const 0) <$> isDrag btn <*> mouseV -- | Position of the mouse during drag, if no drag it becomes zero drag :: MouseButton -> Dyn P2 drag btn = (\x -> if x then id else const 0) <$> isDrag btn <*> mouse