on 00a6a4f help sign in
vty/src/Graphics/Vty.hs
156 lines 5.9 kB history raw
1-- | Vty supports input and output to terminal devices.
2--
3-- - The input is provides as a sequence of 'Event's.
4--
5-- - The output is defined by a 'Picture'. Which is one or more layers of 'Image's.
6-- The constructors in "Graphics.Vty.Image.Internal" should not be used directly. The module
7-- "Graphics.Vty.Image" provides a number of constructor equations that will build correct
8-- 'Image' values. See 'string', '<|>', and '<->' for starters.
9--
10-- - 'Image's can be styled using 'Attr'. See "Graphics.Vty.Attributes".
11--
12-- Good sources of documentation for terminal programming are:
13--
14-- - <https://github.com/b4winckler/vim/blob/master/src/term.c>
15--
16-- - <http://invisible-island.net/xterm/ctlseqs/ctlseqs.html>
17--
18-- - <http://ulisse.elettra.trieste.it/services/doc/serial/config.html>
19--
20-- - <http://www.leonerd.org.uk/hacks/hints/xterm-8bit.html>
21--
22-- - <http://www.unixwiz.net/techtips/termios-vmin-vtime.html>
23--
24-- - <http://vt100.net/docs/vt100-ug/chapter3.html vt100 control sequences>
25module Graphics.Vty ( Vty(..)
26 , mkVty
27 , module Graphics.Vty.Input
28 , module Graphics.Vty.Output
29 , module Graphics.Vty.Picture
30 , DisplayRegion
31 )
32 where
34import Graphics.Vty.Prelude
36import Graphics.Vty.Input
37import Graphics.Vty.Output
38import Graphics.Vty.Picture
40import Control.Concurrent
42import Data.Default
43import Data.IORef
45import qualified System.Console.Terminfo as Terminfo
47-- | The main object. At most one should be created.
48--
49-- The use of Vty typically follows this process:
50--
51-- 0. initialize vty
52--
53-- 1. use the update equation of Vty to display a picture
54--
55-- 2. repeat
56--
57-- 3. shutdown vty.
58--
59-- An alternative to tracking the Vty instance is to use 'withVty' in "Graphics.Vty.Inline.Unsafe".
60--
61-- This does not assure any thread safety. In theory, as long as an update action is not executed
62-- when another update action is already then it's safe to call this on multiple threads.
63--
64-- \todo Remove explicit `shutdown` requirement.
65data Vty = Vty
66 { -- | Outputs the given Picture. Equivalent to 'output_picture' applied to a display context
67 -- implicitly managed by Vty. The managed display context is reset on resize.
68 update :: Picture -> IO ()
69 -- | Get one Event object, blocking if necessary. This will refresh the terminal if the event
70 -- is a 'EvResize'.
71 , next_event :: IO Event
72 -- | The input interface. See 'Input'
73 , input_iface :: Input
74 -- | The output interface. See 'Output'
75 , output_iface :: Output
76 -- | Refresh the display. Normally the library takes care of refreshing. Nonetheless, some
77 -- other program might output to the terminal and mess up the display. In that case the
78 -- application might want to force a refresh.
79 , refresh :: IO ()
80 -- | Clean up after vty.
81 -- The above methods will throw an exception if executed after this is executed.
82 , shutdown :: IO ()
83 }
85-- | Set up the state object for using vty. At most one state object should be
86-- created at a time.
87mkVty :: Config -> IO Vty
88mkVty config = do
89 input <- input_for_current_terminal config
90 out <- output_for_current_terminal
91 intMkVty input out
93intMkVty :: Input -> Output -> IO Vty
94intMkVty input out = do
95 reserve_display out
96 let shutdown_io = do
97 shutdown_input input
98 release_display out
99 release_terminal out
100 last_pic_ref <- newIORef Nothing
101 last_update_ref <- newIORef Nothing
103 let inner_update in_pic = do
104 b@(w,h) <- display_bounds out
105 let cursor = pic_cursor in_pic
106 in_pic' = case cursor of
107 Cursor x y ->
108 let
109 x' = case x of
110 _ | x < 0 -> 0
111 | x >= w -> w - 1
112 | otherwise -> x
113 y' = case y of
114 _ | y < 0 -> 0
115 | y >= h -> h - 1
116 | otherwise -> y
117 in in_pic { pic_cursor = Cursor x' y' }
118 _ -> in_pic
119 mlast_update <- readIORef last_update_ref
120 update_data <- case mlast_update of
121 Nothing -> do
122 dc <- display_context out b
123 output_picture dc in_pic'
124 return (b, dc)
125 Just (last_bounds, last_context) -> do
126 if b /= last_bounds
127 then do
128 dc <- display_context out b
129 output_picture dc in_pic'
130 return (b, dc)
131 else do
132 output_picture last_context in_pic'
133 return (b, last_context)
134 writeIORef last_update_ref $ Just update_data
135 writeIORef last_pic_ref $ Just in_pic'
137 let inner_refresh
138 = writeIORef last_update_ref Nothing
139 >> readIORef last_pic_ref
140 >>= maybe ( return () ) ( \pic -> inner_update pic )
142 let gkey = do k <- readChan $ _event_channel input
143 case k of
144 (EvResize _ _) -> inner_refresh
145 >> display_bounds out
146 >>= return . (\(w,h)-> EvResize w h)
147 _ -> return k
149 return $ Vty { update = inner_update
150 , next_event = gkey
151 , input_iface = input
152 , output_iface = out
153 , refresh = inner_refresh
154 , shutdown = shutdown_io
155 }