on master help sign in
rename current_terminal to output_for_current_terminal etc. plus doc fixups
commite994b3b5dc9c1428a81e9a5931eab4a19d6aa9fb authorCorey O'Connor <corey@ooyala.com> dateSat 21 Dec 2013, 16:02 (13 years ago) parentbdc4be9
10 files changed+133−118
src/Graphics/Vty.hs+31−34 src/Graphics/Vty/Attributes.hs+48−26 src/Graphics/Vty/Inline.hs+11−12 src/Graphics/Vty/Inline/Unsafe.hs+2 src/Graphics/Vty/Input.hs+21−21 src/Graphics/Vty/Output.hs+14−19 src/Graphics/Vty/PictureToSpans.hs+2−3 src/Graphics/Vty/Span.hs+2−1 test/VerifyOutput.hs+1−1 test/VerifyUsingMockInput.hs+1−1
src/Graphics/Vty.hs+31−34
@@ -21,7 +21,7 @@
2121--
2222-- - <http://www.unixwiz.net/techtips/termios-vmin-vtime.html>
2323--
24−-- - <<http://vt100.net/docs/vt100-ug/chapter3.html vt100 control sequences>
24+-- - <http://vt100.net/docs/vt100-ug/chapter3.html vt100 control sequences>
2525module Graphics.Vty ( Vty(..)
2626 , mkVty
2727 , mkVtyEscDelay
@@ -46,34 +46,31 @@ import Data.IORef
4646import qualified System.Console.Terminfo as Terminfo
4747
4848-- | The main object. At most one should be created.
49−-- An alternative is to use unsafePerformIO to automatically create a singleton Vty instance when
50−-- required.
49+--
50+-- The use of Vty typically follows this process:
51+--
52+-- 0. initialize vty
53+--
54+-- 1. use the update equation of Vty to display a picture
55+--
56+-- 2. repeat
57+--
58+-- 3. shutdown vty.
59+--
60+-- An alternative to tracking the Vty instance is to use 'withVty' in "Graphics.Vty.Inline.Unsafe".
5161--
5262-- This does not assure any thread safety. In theory, as long as an update action is not executed
5363-- when another update action is already then it's safe to call this on multiple threads.
64+--
65+-- \todo Remove explicit `shutdown` requirement.
5466data Vty = Vty
5567 { -- | Outputs the given Picture. Equivalent to output_picture applied to a display context
5668 -- implicitly managed by Vty.
5769 update :: Picture -> IO ()
5870 -- | Get one Event object, blocking if necessary.
5971 , next_event :: IO Event
60− -- | The terminal device interface. See `Interface`
61− --
62− -- The use of Vty typically follows this process:
63− --
64− -- 0. initialize vty
65− --
66− -- 1. use the update equation of Vty to display a picture
67− --
68− -- 2. repeat
69− --
70− -- 3. shutdown vty.
71− --
72− -- todo: provide a similar abstraction to Graphics.Vty.Output for input. Use haskeline's
73− -- input backend for implementation.
74− --
75− -- todo: remove explicit `shutdown` requirement.
76− , terminal :: Output
72+ -- | The output interface. See `Output`
73+ , output_iface :: Output
7774 -- | Refresh the display. Normally the library takes care of refreshing. Nonetheless, some
7875 -- other program might output to the terminal and mess the display. In that case the user
7976 -- might want to force a refresh.
@@ -95,42 +92,42 @@ mkVty = mkVtyEscDelay defaultEscDelay
9592mkVtyEscDelay :: Int -> IO Vty
9693mkVtyEscDelay escDelay = do
9794 term_info <- Terminfo.setupTermFromEnv
98− t <- current_terminal
95+ t <- output_for_current_terminal
9996 reserve_display t
10097 (kvar, endi) <- initTermInput escDelay term_info
10198 intMkVty kvar ( endi >> release_display t >> release_terminal t ) t
10299
103100intMkVty :: IO Event -> IO () -> Output -> IO Vty
104−intMkVty kvar fend t = do
101+intMkVty kvar fend out = do
105102 last_pic_ref <- newIORef Nothing
106103 last_update_ref <- newIORef Nothing
107104
108105 let inner_update in_pic = do
109− b@(w,h) <- display_bounds t
106+ b@(w,h) <- display_bounds out
110107 let cursor = pic_cursor in_pic
111108 in_pic' = case cursor of
112109 Cursor x y ->
113110 let
114111 x' = case x of
115− _ | x >= 0x80000000 -> 0
116− | x >= w -> w - 1
117− | otherwise -> x
112+ _ | x < 0 -> 0
113+ | x >= w -> w - 1
114+ | otherwise -> x
118115 y' = case y of
119− _ | y >= 0x80000000 -> 0
120− | y >= h -> h - 1
121− | otherwise -> y
116+ _ | y < 0 -> 0
117+ | y >= h -> h - 1
118+ | otherwise -> y
122119 in in_pic { pic_cursor = Cursor x' y' }
123120 _ -> in_pic
124121 mlast_update <- readIORef last_update_ref
125122 update_data <- case mlast_update of
126123 Nothing -> do
127− dc <- display_context t b
124+ dc <- display_context out b
128125 output_picture dc in_pic'
129126 return (b, dc)
130127 Just (last_bounds, last_context) -> do
131128 if b /= last_bounds
132129 then do
133− dc <- display_context t b
130+ dc <- display_context out b
134131 output_picture dc in_pic'
135132 return (b, dc)
136133 else do
@@ -146,14 +143,14 @@ intMkVty kvar fend t = do
146143
147144 let gkey = do k <- kvar
148145 case k of
149− (EvResize _ _) -> inner_refresh
150− >> display_bounds t
146+ (EvResize _ _) -> inner_refresh
147+ >> display_bounds out
151148 >>= return . (\(w,h)-> EvResize w h)
152149 _ -> return k
153150
154151 return $ Vty { update = inner_update
155152 , next_event = gkey
156− , terminal = t
153+ , output_iface = out
157154 , refresh = inner_refresh
158155 , shutdown = fend
159156 }
src/Graphics/Vty/Attributes.hs+48−26
@@ -3,7 +3,54 @@
33{-# LANGUAGE RankNTypes #-}
44-- | Display attributes
55--
6−-- For efficiency, this could be encoded into a single 32 bit word. The 32 bit word is first divided
6+-- Typically the values 'def_attr' or 'current_attr' are modified to form attributes:
7+--
8+-- @
9+-- def_attr `with_fore_color` red
10+-- @
11+--
12+-- Is the attribute that will set the foreground color to red and the background color to the
13+-- default.
14+--
15+-- This can then be used to build an image wiht a red foreground like so:
16+--
17+-- @
18+-- string (def_attr `with_fore_color` red) "this text will be red"
19+-- @
20+--
21+-- The default attributes set by 'def_attr' have a presentation determined by the terminal. This is
22+-- not something VTY can control. The user is free to define the color scheme of the terminal as
23+-- they see fit. Up to the limits of the terminal anyways.
24+--
25+-- The value 'current_attr' will keep the attributes of whatever was output previously.
26+--
27+-- \todo This API is very verbose IMO. I'd like something more succinct.
28+module Graphics.Vty.Attributes ( module Graphics.Vty.Attributes
29+ , module Graphics.Vty.Attributes.Color
30+ , module Graphics.Vty.Attributes.Color240
31+ )
32+ where
33+
34+import Data.Bits
35+import Data.Monoid
36+import Data.Word
37+
38+import Graphics.Vty.Attributes.Color
39+import Graphics.Vty.Attributes.Color240
40+
41+-- | A display attribute defines the Color and Style of all the characters rendered after the
42+-- attribute is applied.
43+--
44+-- At most 256 colors, picked from a 240 and 16 color palette, are possible for the background and
45+-- foreground. The 240 colors and 16 colors are points in different palettes. See Color for more
46+-- information.
47+data Attr = Attr
48+ { attr_style :: !(MaybeDefault Style)
49+ , attr_fore_color :: !(MaybeDefault Color)
50+ , attr_back_color :: !(MaybeDefault Color)
51+ } deriving ( Eq, Show )
52+
53+-- This could be encoded into a single 32 bit word. The 32 bit word is first divided
754-- into 4 groups of 8 bits where: The first group codes what action should be taken with regards to
855-- the other groups.
956-- XXYYZZ__
@@ -33,31 +80,6 @@
3380--
3481-- Then the foreground color encoded into 8 bits.
3582-- Then the background color encoded into 8 bits.
36−--
37−module Graphics.Vty.Attributes ( module Graphics.Vty.Attributes
38− , module Graphics.Vty.Attributes.Color
39− , module Graphics.Vty.Attributes.Color240
40− )
41− where
42−
43−import Data.Bits
44−import Data.Monoid
45−import Data.Word
46−
47−import Graphics.Vty.Attributes.Color
48−import Graphics.Vty.Attributes.Color240
49−
50−-- | A display attribute defines the Color and Style of all the characters rendered after the
51−-- attribute is applied.
52−--
53−-- At most 256 colors, picked from a 240 and 16 color palette, are possible for the background and
54−-- foreground. The 240 colors and 16 colors are points in different palettes. See Color for more
55−-- information.
56−data Attr = Attr
57− { attr_style :: !(MaybeDefault Style)
58− , attr_fore_color :: !(MaybeDefault Color)
59− , attr_back_color :: !(MaybeDefault Color)
60− } deriving ( Eq, Show )
6183
6284instance Monoid Attr where
6385 mempty = Attr mempty mempty mempty
src/Graphics/Vty/Inline.hs+11−12
@@ -80,22 +80,22 @@ default_all = put def_attr
8080--
8181-- This does not flush the terminal.
8282put_attr_change :: ( Applicative m, MonadIO m ) => Output -> InlineM () -> m ()
83−put_attr_change t c = liftIO $ do
84− bounds <- display_bounds t
85− dc <- display_context t bounds
86− mfattr <- prev_fattr <$> readIORef (assumed_state_ref t)
83+put_attr_change out c = liftIO $ do
84+ bounds <- display_bounds out
85+ dc <- display_context out bounds
86+ mfattr <- prev_fattr <$> readIORef (assumed_state_ref out)
8787 fattr <- case mfattr of
8888 Nothing -> do
89− liftIO $ send_to_terminal t (default_attr_required_bytes dc) (serialize_default_attr dc)
89+ liftIO $ send_to_terminal out (default_attr_required_bytes dc) (serialize_default_attr dc)
9090 return $ FixedAttr default_style_mask Nothing Nothing
9191 Just v -> return v
9292 let attr = execState c current_attr
93− attr' = limit_attr_for_display t attr
93+ attr' = limit_attr_for_display out attr
9494 fattr' = fix_display_attr fattr attr'
9595 diffs = display_attr_diffs fattr fattr'
96− send_to_terminal t (attr_required_bytes dc fattr attr' diffs)
97− (serialize_set_attr dc fattr attr' diffs)
98− modifyIORef (assumed_state_ref t) $ \s -> s { prev_fattr = Just fattr' }
96+ send_to_terminal out (attr_required_bytes dc fattr attr' diffs)
97+ (serialize_set_attr dc fattr attr' diffs)
98+ modifyIORef (assumed_state_ref out) $ \s -> s { prev_fattr = Just fattr' }
9999 inline_hack dc
100100
101101-- | Apply the provided display attributes changes to the terminal that was current at the time this
@@ -104,8 +104,7 @@ put_attr_change t c = liftIO $ do
104104-- This will flush the terminal output.
105105put_attr_change_ :: ( Applicative m, MonadIO m ) => InlineM () -> m ()
106106put_attr_change_ c = liftIO $ do
107− t <- withVty $ return . terminal
107+ out <- withVty $ return . output_iface
108108 hFlush stdout
109− put_attr_change t c
109+ put_attr_change out c
110110 hFlush stdout
111−
src/Graphics/Vty/Inline/Unsafe.hs+2
@@ -10,6 +10,8 @@ global_vty :: IORef (Maybe Vty)
1010{-# NOINLINE global_vty #-}
1111global_vty = unsafePerformIO $ newIORef Nothing
1212
13+-- | This will create a Vty instance using 'mkVty' and execute an IO action provided that instance.
14+-- The created Vty instance will be stored to the unsafe 'IORef' 'global_vty'.
1315withVty :: (Vty -> IO b) -> IO b
1416withVty f = do
1517 mvty <- readIORef global_vty
src/Graphics/Vty/Input.hs+21−21
@@ -1,24 +1,5 @@
1−-- | Right, I'm mostly guessing on these details. So, as far as I can figure:
2−--
3−-- There are two input modes:
4−-- 1. 7 bit
5−-- 2. 8 bit
6−--
7−-- Which is better? Well, depends on if you want to enter Unicode as UTF-8 or not.
8−--
9−-- * 7 bit mode: UTF-8 can be input unambiguiously.
10−-- * 8 bit mode: UTF-8 cannot be input. Does not require an escape delay.
11−--
12−-- vty uses 7 bit mode.
13−--
14−-- To differentiate between ESC and control keys that contain ESC:
15−-- vty using the timing of the input to differentiate.
16−-- This, I best I can figure, is what VMIN and VTIME are supposed to be for.
17−-- For some reason, I don't yet know, vty implements the timing in the library.
18−-- I'm not sure if this is an advantage or not!
19−--
20−-- By default, the escape delay is 10000 microseconds. Which is assumed to be well above the
21−-- sampling rate required to detect a keyup for a person typing 200 wpm.
1+-- | The input layer for VTY. This provides methods for initializing an 'Input' structure which can
2+-- then be used to read 'Event's from the terminal.
223module Graphics.Vty.Input ( Key(..)
234 , Modifier(..)
245 , Button(..)
@@ -40,6 +21,25 @@ import System.Posix.Signals.Exts
4021import System.Posix.Terminal
4122import System.Posix.IO (stdInput)
4223
24+-- Right, I'm mostly guessing on these details. So, as far as I can figure:
25+--
26+-- There are two input modes:
27+-- 1. 7 bit
28+-- 2. 8 bit
29+--
30+-- Which is better? Well, depends on if you want to enter Unicode as UTF-8 or not.
31+--
32+-- * 7 bit mode: UTF-8 can be input unambiguiously.
33+-- * 8 bit mode: UTF-8 cannot be input. Does not require an escape delay.
34+--
35+-- vty uses 7 bit mode.
36+--
37+-- To differentiate between ESC and control keys that contain ESC:
38+-- vty using the timing of the input to differentiate.
39+-- This, I best I can figure, is what VMIN and VTIME are supposed to be for.
40+-- For some reason, I don't yet know, vty implements the timing in the library.
41+-- I'm not sure if this is an advantage or not!
42+
4343-- | By default, the escape delay is 10000 microseconds. Which is assumed to be well above the
4444-- sampling rate required to detect a keyup for a person typing 200 wpm.
4545defaultEscDelay :: Int
src/Graphics/Vty/Output.hs+14−19
@@ -43,13 +43,13 @@ import GHC.IO.Handle
4343import System.Environment
4444import System.IO
4545
46−-- | Returns a `Output` for the current terminal.
46+-- | Returns a `Output` for the current terminal as determined by TERM.
4747--
48−-- The specific Output implementation used is hidden from the API user. All terminal
49−-- implementations are assumed to perform more, or less, the same. Currently all implementations use
50−-- terminfo for at least some terminal specific information. This is why platforms without terminfo
51−-- are not supported. However, as mentioned before, any specifics about it being based on terminfo
52−-- are hidden from the API user. If a terminal implementation is developed for a terminal for a
48+-- The specific Output implementation used is hidden from the API user. All terminal implementations
49+-- are assumed to perform more, or less, the same. Currently all implementations use terminfo for at
50+-- least some terminal specific information. This is why platforms without terminfo are not
51+-- supported. However, as mentioned before, any specifics about it being based on terminfo are
52+-- hidden from the API user. If a terminal implementation is developed for a terminal for a
5353-- platform without terminfo support then Vty should work as expected on that terminal.
5454--
5555-- Selection of a terminal is done as follows:
@@ -61,11 +61,6 @@ import System.IO
6161--
6262-- * for any other TERM value TerminfoBased is used.
6363--
64−--
65−-- The terminal has to be determined dynamically at runtime. To satisfy this requirement all
66−-- terminals instances are lifted into an abstract terminal handle via existential qualification.
67−-- This implies that the only equations that can used are those in the terminal class.
68−--
6964-- To differentiate between Mac OS X terminals this uses the TERM_PROGRAM environment variable.
7065-- However, an xterm started by Terminal or iTerm *also* has TERM_PROGRAM defined since the
7166-- environment variable is not reset/cleared by xterm. However a Terminal.app or iTerm.app started
@@ -73,19 +68,19 @@ import System.IO
7368-- environment variables (I think?) this assumes that XTERM_VERSION will never be set for a true
7469-- Terminal.app or iTerm.app session.
7570--
71+-- The file descriptor used for output will a be a duplicate of the current stdout file descriptor.
7672--
77−-- The file descriptor used for output will a duplicate of the current stdout file descriptor.
78−--
79−-- todo: add an implementation for windows that does not depend on terminfo. Should be installable
73+-- \todo add an implementation for windows that does not depend on terminfo. Should be installable
8074-- with only what is provided in the haskell platform. Use ansi-terminal
81−current_terminal :: ( Applicative m, MonadIO m ) => m Output
82−current_terminal = do
75+output_for_current_terminal :: ( Applicative m, MonadIO m ) => m Output
76+output_for_current_terminal = do
8377 term_type <- liftIO $ getEnv "TERM"
8478 out_handle <- liftIO $ hDuplicate stdout
85− terminal_with_name_and_io term_type out_handle
79+ output_for_name_and_io term_type out_handle
8680
87−terminal_with_name_and_io :: (Applicative m, MonadIO m) => String -> Handle -> m Output
88−terminal_with_name_and_io term_type out_handle = do
81+-- | gives an output method structure for a terminal with the given name and the given 'Handle'.
82+output_for_name_and_io :: (Applicative m, MonadIO m) => String -> Handle -> m Output
83+output_for_name_and_io term_type out_handle = do
8984 t <- if "xterm" `isPrefixOf` term_type
9085 then do
9186 maybe_terminal_app <- get_env "TERM_PROGRAM"
src/Graphics/Vty/PictureToSpans.hs+2−3
@@ -177,7 +177,7 @@ swap_skips_for_char_span w c a = Vector.map f
177177--
178178-- Crops to the given display region.
179179--
180−-- TODO: I'm pretty sure there is an algorithm that does not require a mutable buffer.
180+-- \todo I'm pretty sure there is an algorithm that does not require a mutable buffer.
181181build_spans :: Image -> DisplayRegion -> ST s (MRowOps s)
182182build_spans image out_region = do
183183 -- First we create a mutable vector for each rows output operations.
@@ -228,7 +228,7 @@ is_out_of_bounds i s
228228-- implementations are odd. They pass the current tests but something seems terribly wrong about all
229229-- this.
230230--
231−-- TODO: prove this cannot be called in an out of bounds case.
231+-- \todo prove this cannot be called in an out of bounds case.
232232add_maybe_clipped :: forall s . Image -> BlitM s ()
233233add_maybe_clipped EmptyImage = return ()
234234add_maybe_clipped (HorizText a text_str ow _cw) = do
@@ -342,4 +342,3 @@ snoc_op !op !row = do
342342 when (span_ops_effected_columns ops' > region_width the_region)
343343 $ fail $ "row " ++ show row ++ " now exceeds region width"
344344 MVector.write the_mrow_ops row ops'
345−
src/Graphics/Vty/Span.hs+2−1
@@ -63,7 +63,8 @@ split_ops_at in_w in_ops = split_ops_at' in_w in_ops
6363 split_ops_at' 0 ops = (Vector.empty, ops)
6464 split_ops_at' remaining_columns ops = case Vector.head ops of
6565 t@(TextSpan {}) -> if remaining_columns >= text_span_output_width t
66− then let (pre,post) = split_ops_at' (remaining_columns - text_span_output_width t) (Vector.tail ops)
66+ then let (pre,post) = split_ops_at' (remaining_columns - text_span_output_width t)
67+ (Vector.tail ops)
6768 in (Vector.cons t pre, post)
6869 else let pre_txt = clip_text (text_span_text t) 0 remaining_columns
6970 pre_op = TextSpan { text_span_attr = text_span_attr t
test/VerifyOutput.hs+1−1
@@ -46,7 +46,7 @@ smoke_test_term_mac term_name i = liftIOResult $ do
4646smoke_test_term :: String -> Image -> IO Result
4747smoke_test_term term_name i = do
4848 null_out <- openFile "/dev/null" WriteMode
49− t <- terminal_with_name_and_io term_name null_out
49+ t <- output_for_name_and_io term_name null_out
5050 putStrLn $ "context color count: " ++ show (context_color_count t)
5151 reserve_display t
5252 dc <- display_context t (100,100)
test/VerifyUsingMockInput.hs+1−1
@@ -87,7 +87,7 @@ compare_events input_spec expected_events out_events = compare_events' expected_
8787 where
8888 compare_events' [] [] = return True
8989 compare_events' [] out_events' = do
90− printf "extra events %s\n" (show out_events) :: IO ()
90+ printf "extra events %s\n" (show out_events') :: IO ()
9191 return False
9292 compare_events' expected_events' [] = do
9393 printf "events %s were not produced for input %s\n" (show expected_events') (show input_spec) :: IO ()