-- | Vty provides interfaces for both terminal input and terminal -- output. -- -- - Input to the terminal is provided to the Vty application as a -- sequence of 'Event's. -- -- - Output is provided to Vty by the application in the form of a -- 'Picture'. A 'Picture' is one or more layers of 'Image's. -- 'Image' values can be built by the various constructors in -- "Graphics.Vty.Image". Output can be syled using 'Attr' (attribute) -- values in the "Graphics.Vty.Attributes" module. -- -- @ -- import "Graphics.Vty" -- -- main = do -- cfg <- 'standardIOConfig' -- vty <- 'mkVty' cfg -- let line0 = 'string' ('defAttr' ` 'withForeColor' ` 'green') \"first line\" -- line1 = 'string' ('defAttr' ` 'withBackColor' ` 'blue') \"second line\" -- img = line0 '<->' line1 -- pic = 'picForImage' img -- 'update' vty pic -- e <- 'nextEvent' vty -- 'shutdown' vty -- 'print' (\"Last event was: \" '++' 'show' e) -- @ module Graphics.Vty ( Vty(..) , mkVty , Mode(..) , module Graphics.Vty.Config , module Graphics.Vty.Input , module Graphics.Vty.Output , module Graphics.Vty.Output.Interface , module Graphics.Vty.Picture , module Graphics.Vty.Image , module Graphics.Vty.Attributes ) where import Graphics.Vty.Config import Graphics.Vty.Input import Graphics.Vty.Output import Graphics.Vty.Output.Interface import Graphics.Vty.Picture import Graphics.Vty.Image import Graphics.Vty.Attributes import Control.Concurrent.STM import Data.IORef import Data.Monoid -- | A Vty value represents a handle to the Vty library that the -- application must create in order to use Vty. -- -- The use of Vty typically follows this process: -- -- 1. Initialize vty -- -- 2. Use 'update' to display a picture. -- -- 3. Use 'nextEvent' to get the next input event. -- -- 4. Depending on the event, go to 2 or 5. -- -- 5. Shutdown vty. -- -- Operations on Vty handles are not thread-safe. data Vty = Vty { -- | Outputs the given 'Picture'. update :: Picture -> IO () -- | Get one 'Event' object, blocking if none are available. This -- will refresh the terminal if the event is a 'EvResize'. , nextEvent :: IO Event -- | The input interface. See 'Input'. , inputIface :: Input -- | The output interface. See 'Output'. , outputIface :: Output -- | Refresh the display. 'nextEvent' will refresh the display if -- a resize occurs, but this can be used to refresh the display -- explicitly. If other programs output to the terminal and mess -- up the display then the application might want to force a -- refresh using this function. , refresh :: IO () -- | Clean up after vty. A call to this function is necessary to -- cleanly restore the terminal state before application exit. The -- above methods will throw an exception if executed after this is -- executed. , shutdown :: IO () } -- | Create a Vty handle. At most one handle should be created at a time -- for a given terminal device. -- -- The specified configuration is added to the the configuration -- loaded by 'userConfig' with the 'userConfig' configuration taking -- precedence. See "Graphics.Vty.Config". -- -- For most applications @mkVty defaultConfig@ is sufficient. mkVty :: Config -> IO Vty mkVty appConfig = do config <- (<> appConfig) <$> userConfig input <- inputForConfig config out <- outputForConfig config intMkVty input out intMkVty :: Input -> Output -> IO Vty intMkVty input out = do reserveDisplay out let shutdownIo = do shutdownInput input releaseDisplay out releaseTerminal out lastPicRef <- newIORef Nothing lastUpdateRef <- newIORef Nothing let innerUpdate inPic = do b <- displayBounds out mlastUpdate <- readIORef lastUpdateRef updateData <- case mlastUpdate of Nothing -> do dc <- displayContext out b outputPicture dc inPic return (b, dc) Just (lastBounds, lastContext) -> do if b /= lastBounds then do dc <- displayContext out b outputPicture dc inPic return (b, dc) else do outputPicture lastContext inPic return (b, lastContext) writeIORef lastUpdateRef $ Just updateData writeIORef lastPicRef $ Just inPic let innerRefresh = do writeIORef lastUpdateRef Nothing bounds <- displayBounds out dc <- displayContext out bounds writeIORef (assumedStateRef $ contextDevice dc) initialAssumedState mPic <- readIORef lastPicRef maybe (return ()) innerUpdate mPic let gkey = do k <- atomically $ readTChan $ _eventChannel input case k of (EvResize _ _) -> displayBounds out >>= return . (\(w,h)-> EvResize w h) _ -> return k return $ Vty { update = innerUpdate , nextEvent = gkey , inputIface = input , outputIface = out , refresh = innerRefresh , shutdown = shutdownIo }