dyna-brick-0.1.0.0: src/Dyna/Brick/Run.hs
-- | IO of thebrick application
module Dyna.Brick.Run(
Spec(..),
defSpec,
emptyAttrMap,
-- * Run application
Run,
runApp,
-- * Sensors
vtyEvents,
mouseUp,
mouseDown,
keyEvents,
onChar,
onKey,
readChars,
module X,
Key(..),
Modifier(..),
) where
import Control.Concurrent (forkIO, killThread)
import Control.Concurrent.MVar qualified as M
import Control.Monad.Reader
import Control.Monad.Base
import Control.Monad.Trans.Control (MonadBaseControl(..))
import Control.Concurrent.Chan.Unagi
import Control.Exception.Lifted
import Data.Default
import Data.Text (Text)
import Data.IORef
import Dyna qualified as D
import Brick as X
import Brick.Main (continueWithoutRedraw)
import Brick.BChan qualified as Brick
import qualified Graphics.Vty as Vty
import Graphics.Vty (Key(..), Modifier, Button)
import Dyna.Brick.Types
data Spec = Spec
{ spec'attrMap :: AttrMap
, spec'cursor :: [CursorLocation BoxId] -> Maybe (CursorLocation BoxId)
}
instance Default Spec where
def = defSpec emptyAttrMap
defSpec :: AttrMap -> Spec
defSpec attrs = Spec attrs (const Nothing)
emptyAttrMap :: AttrMap
emptyAttrMap = attrMap Vty.defAttr []
--------------------------------------------------------------------------------
-- run application
-- | Run application
runApp :: Spec -> Run Win -> IO ()
runApp Spec{..} dynActs = do
env <- newEnv
actChan <- Brick.newBChan 10
let app = App
{ appDraw = id
, appChooseCursor = const spec'cursor
, appHandleEvent = handleEvent env
, appStartEvent = pure
, appAttrMap = const spec'attrMap
}
let evs = (\(Win dyn acts) -> (UpdateWidgets <$> D.unhold (unDyn dyn)) <> (BrickAct <$> unEvt acts)) <$> dynActs
tid <- forkIO $ evalRun ((\e -> D.runEvt e (liftIO . Brick.writeBChan actChan)) =<< evs) env
runChanMain actChan app
`finally` killThread tid
where
handleEvent env@Env{..} st evt = case evt of
VtyEvent event -> do
liftIO $ writeChan (fst env'eventChan) event
continueWithoutRedraw st
AppEvent act -> case act of
UpdateWidgets ws -> continue ws
BrickAct act ->
case act of
Quit -> halt st
MouseDown n but mods loc -> do
liftIO $ writeChan (fst env'mouseDownChan) (MouseDownEvent n but mods loc)
continueWithoutRedraw st
MouseUp n mBut loc -> do
liftIO $ writeChan (fst env'mouseUpChan) (MouseUpEvent n mBut loc)
continueWithoutRedraw st
runChanMain chan app = do
let buildVty = Vty.mkVty Vty.defaultConfig
initialVty <- buildVty
void $ customMain initialVty buildVty (Just chan) app [emptyWidget]
--------------------------------------------------------------------------------
-- event sensors
vtyEvents :: Evt Vty.Event
vtyEvents = Evt $ D.Evt $ \go -> do
eventChan <- fst <$> asks env'eventChan
D.runEvt (D.uchanEvt eventChan) go
mouseDown :: Evt MouseDownEvent
mouseDown = Evt $ D.Evt $ \go -> do
mouseDownChan <- fst <$> asks env'mouseDownChan
D.runEvt (D.uchanEvt mouseDownChan) go
mouseUp :: Evt MouseUpEvent
mouseUp = Evt $ D.Evt $ \go -> do
mouseUpChan <- fst <$> asks env'mouseUpChan
D.runEvt (D.uchanEvt mouseUpChan) go
keyEvents :: Evt (Key, [Modifier])
keyEvents = Evt $ D.mapMay go (unEvt vtyEvents)
where
go = \case
Vty.EvKey key mods -> Just (key, mods)
_ -> Nothing
onChar :: Char -> Evt [Modifier]
onChar ch = onKey (KChar ch)
readChars :: Evt Char
readChars = Evt $ D.mapMay go (unEvt keyEvents)
where
go x = case fst x of
KChar ch -> Just ch
_ -> Nothing
onKey :: Key -> Evt [Modifier]
onKey k = Evt $
D.mapMay
(\(x, mods) -> if x == k then Just mods else Nothing)
(unEvt keyEvents)