on fe55cd4 help sign in
vty/src/Graphics/Vty.hs
133 lines 5.0 kB history raw
1-- Copyright 2009 Corey O'Connor
2{-# LANGUAGE ForeignFunctionInterface, BangPatterns, UnboxedTuples #-}
3{-# CFILES gwinsz.c #-}
5-- Good sources of documentation for terminal programming are:
6-- vt100 control sequences: http://vt100.net/docs/vt100-ug/chapter3.html#S3.3.3
7-- Xterm control sequences: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
9module Graphics.Vty ( Vty(..)
10 , mkVty
11 , mkVtyEscDelay
12 , module Graphics.Vty.Terminal
13 , module Graphics.Vty.Picture
14 , module Graphics.Vty.DisplayRegion
15 , Key(..)
16 , Modifier(..)
17 , Button(..)
18 , Event(..)
19 )
20 where
23import Graphics.Vty.Terminal
24import Graphics.Vty.Picture
25import Graphics.Vty.DisplayRegion
26import Graphics.Vty.LLInput
28import Data.IORef
29import Control.Concurrent
31import Data.Maybe ( maybe )
33import System.Console.Terminfo
34import System.IO
36-- | The main object. At most one should be created.
37-- An alternative is to use unsafePerformIO to automatically create a singleton Vty instance when
38-- required.
39--
40-- This does not assure any thread safety. In theory, as long as an update action is not executed
41-- when another update action is already then it's safe to call this on multiple threads.
42--
43-- todo: Once the Terminal interface encompasses input this interface will be deprecated.
44-- Currently, just using the Terminal interface there is no support for input events.
45data Vty = Vty
46 { -- | Outputs the given Picture. Equivalent to output_picture applied to a display context
47 -- implicitly managed by Vty.
48 update :: Picture -> IO ()
49 -- | Get one Event object, blocking if necessary.
50 , next_event :: IO Event
51 -- | Handle to the terminal interface. See `Terminal`
52 --
53 -- todo: provide a similar abstraction for input. Use haskeline's input backend for
54 -- implementation.
55 --
56 -- The use of Vty typically follows this process:
57 --
58 -- 0. initialize vty
59 --
60 -- 1. use the update equation of Vty to display a picture
61 --
62 -- 2. repeat
63 --
64 -- 3. shutdown vty. todo: remove? Automate release of resources as much as possible.
65 --
66 -- This version currently supports the same interface.
67 , terminal :: TerminalHandle
68 -- | Refresh the display. Normally the library takes care of refreshing.
69 -- Nonetheless, some other program might output to the terminal and mess the display.
70 -- In that case the user might want to force a refresh.
71 , refresh :: IO ()
72 -- | Clean up after vty.
73 , shutdown :: IO ()
74 }
76-- | Set up the state object for using vty. At most one state object should be
77-- created at a time.
78mkVty :: IO Vty
79mkVty = mkVtyEscDelay 0
81mkVtyEscDelay :: Int -> IO Vty
82mkVtyEscDelay escDelay = do
83 term_info <- setupTermFromEnv
84 t <- terminal_handle
85 reserve_display t
86 (kvar, endi) <- initTermInput escDelay term_info
87 intMkVty kvar ( endi >> release_display t >> release_terminal t ) t
89intMkVty :: IO Event -> IO () -> TerminalHandle -> IO Vty
90intMkVty kvar fend t = do
91 last_pic_ref <- newIORef undefined
92 last_update_ref <- newIORef Nothing
94 let inner_update in_pic = do
95 b <- display_bounds t
96 mlast_update <- readIORef last_update_ref
97 update_data <- case mlast_update of
98 Nothing -> do
99 d <- display_context t b
100 output_picture d in_pic
101 return (b, d)
102 Just (last_bounds, last_context) -> do
103 if b /= last_bounds
104 then do
105 d <- display_context t b
106 output_picture d in_pic
107 return (b, d)
108 else do
109 output_picture last_context in_pic
110 return (b, last_context)
111 writeIORef last_update_ref $ Just update_data
112 writeIORef last_pic_ref $ Just in_pic
114 let inner_refresh
115 = readIORef last_pic_ref
116 >>= maybe ( return () ) ( \pic -> inner_update pic )
118 let gkey = do k <- kvar
119 case k of
120 (EvResize _ _) -> inner_refresh
121 >> display_bounds t
122 >>= return . ( \(DisplayRegion w h)
123 -> EvResize (fromEnum w) (fromEnum h)
124 )
125 _ -> return k
127 return $ Vty { update = inner_update
128 , next_event = gkey
129 , terminal = t
130 , refresh = inner_refresh
131 , shutdown = fend
132 }