on 175c235 help sign in
vty/src/Graphics/Vty.hs
158 lines 5.4 kB history raw
1-- | Vty provides interfaces for both terminal input and terminal
2-- output.
3--
4-- - Input to the terminal is provided to the Vty application as a
5-- sequence of 'Event's.
6--
7-- - Output is provided to Vty by the application in the form of a
8-- 'Picture'. A 'Picture' is one or more layers of 'Image's.
9-- 'Image' values can be built by the various constructors in
10-- "Graphics.Vty.Image". Output can be syled using 'Attr' (attribute)
11-- values in the "Graphics.Vty.Attributes" module.
12--
13-- @
14-- import "Graphics.Vty"
15--
16-- main = do
17-- cfg <- 'standardIOConfig'
18-- vty <- 'mkVty' cfg
19-- let line0 = 'string' ('defAttr' ` 'withForeColor' ` 'green') \"first line\"
20-- line1 = 'string' ('defAttr' ` 'withBackColor' ` 'blue') \"second line\"
21-- img = line0 '<->' line1
22-- pic = 'picForImage' img
23-- 'update' vty pic
24-- e <- 'nextEvent' vty
25-- 'shutdown' vty
26-- 'print' (\"Last event was: \" '++' 'show' e)
27-- @
28module Graphics.Vty
29 ( Vty(..)
30 , mkVty
31 , Mode(..)
32 , module Graphics.Vty.Config
33 , module Graphics.Vty.Input
34 , module Graphics.Vty.Output
35 , module Graphics.Vty.Output.Interface
36 , module Graphics.Vty.Picture
37 , module Graphics.Vty.Image
38 , module Graphics.Vty.Attributes
39 )
40where
42import Graphics.Vty.Config
43import Graphics.Vty.Input
44import Graphics.Vty.Output
45import Graphics.Vty.Output.Interface
46import Graphics.Vty.Picture
47import Graphics.Vty.Image
48import Graphics.Vty.Attributes
50import Control.Concurrent.STM
52import Data.IORef
53import Data.Monoid
55-- | A Vty value represents a handle to the Vty library that the
56-- application must create in order to use Vty.
57--
58-- The use of Vty typically follows this process:
59--
60-- 1. Initialize vty
61--
62-- 2. Use 'update' to display a picture.
63--
64-- 3. Use 'nextEvent' to get the next input event.
65--
66-- 4. Depending on the event, go to 2 or 5.
67--
68-- 5. Shutdown vty.
69--
70-- Operations on Vty handles are not thread-safe.
71data Vty = Vty
72 { -- | Outputs the given 'Picture'.
73 update :: Picture -> IO ()
74 -- | Get one 'Event' object, blocking if none are available. This
75 -- will refresh the terminal if the event is a 'EvResize'.
76 , nextEvent :: IO Event
77 -- | The input interface. See 'Input'.
78 , inputIface :: Input
79 -- | The output interface. See 'Output'.
80 , outputIface :: Output
81 -- | Refresh the display. 'nextEvent' will refresh the display if
82 -- a resize occurs, but this can be used to refresh the display
83 -- explicitly. If other programs output to the terminal and mess
84 -- up the display then the application might want to force a
85 -- refresh using this function.
86 , refresh :: IO ()
87 -- | Clean up after vty. A call to this function is necessary to
88 -- cleanly restore the terminal state before application exit. The
89 -- above methods will throw an exception if executed after this is
90 -- executed.
91 , shutdown :: IO ()
92 }
94-- | Create a Vty handle. At most one handle should be created at a time
95-- for a given terminal device.
96--
97-- The specified configuration is added to the 'userConfig' with the
98-- 'userConfig' taking precedence. See "Graphics.Vty.Config".
99--
100-- For most applications @mkVty defaultConfig@ is sufficient.
101mkVty :: Config -> IO Vty
102mkVty appConfig = do
103 config <- (<> appConfig) <$> userConfig
104 input <- inputForConfig config
105 out <- outputForConfig config
106 intMkVty input out
108intMkVty :: Input -> Output -> IO Vty
109intMkVty input out = do
110 reserveDisplay out
111 let shutdownIo = do
112 shutdownInput input
113 releaseDisplay out
114 releaseTerminal out
115 lastPicRef <- newIORef Nothing
116 lastUpdateRef <- newIORef Nothing
118 let innerUpdate inPic = do
119 b <- displayBounds out
120 mlastUpdate <- readIORef lastUpdateRef
121 updateData <- case mlastUpdate of
122 Nothing -> do
123 dc <- displayContext out b
124 outputPicture dc inPic
125 return (b, dc)
126 Just (lastBounds, lastContext) -> do
127 if b /= lastBounds
128 then do
129 dc <- displayContext out b
130 outputPicture dc inPic
131 return (b, dc)
132 else do
133 outputPicture lastContext inPic
134 return (b, lastContext)
135 writeIORef lastUpdateRef $ Just updateData
136 writeIORef lastPicRef $ Just inPic
138 let innerRefresh = do
139 writeIORef lastUpdateRef Nothing
140 bounds <- displayBounds out
141 dc <- displayContext out bounds
142 writeIORef (assumedStateRef $ contextDevice dc) initialAssumedState
143 mPic <- readIORef lastPicRef
144 maybe (return ()) innerUpdate mPic
146 let gkey = do k <- atomically $ readTChan $ _eventChannel input
147 case k of
148 (EvResize _ _) -> displayBounds out
149 >>= return . (\(w,h)-> EvResize w h)
150 _ -> return k
152 return $ Vty { update = innerUpdate
153 , nextEvent = gkey
154 , inputIface = input
155 , outputIface = out
156 , refresh = innerRefresh
157 , shutdown = shutdownIo
158 }