on master help sign in
Renamed IdImage to EmptyImage. Start of translation.
e0b2037coreyoconnor
docs/vty_4_development.txt+1
@@ -87,3 +87,4 @@ I used a very simple Makefile to manage the execution of tests. The usage follow
87 directory.
87 directory.
88Nothing fancy, but enough.
88Nothing fancy, but enough.
89
89
90+
src/Data/Terminfo/Eval.hs+3−4
@@ -1,5 +1,4 @@
1{-# LANGUAGE RecordWildCards #-}
1{-# LANGUAGE RecordWildCards #-}
2−{-# LANGUAGE TypeFamilies #-}
3{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE NoMonomorphismRestriction #-}
3{-# LANGUAGE NoMonomorphismRestriction #-}
5{-# LANGUAGE NamedFieldPuns #-}
4{-# LANGUAGE NamedFieldPuns #-}
@@ -27,18 +26,18 @@ import Data.Word
27type Eval a = ReaderT (CapExpression,[CapParam]) (State [CapParam]) a
26type Eval a = ReaderT (CapExpression,[CapParam]) (State [CapParam]) a
28type EvalIO a = ReaderT (CapExpression,[CapParam]) (StateT [CapParam] IO) a
27type EvalIO a = ReaderT (CapExpression,[CapParam]) (StateT [CapParam] IO) a
29
28
30−pop :: (StateType m ~ [CapParam], MonadState m) => m CapParam
29+pop :: MonadState [CapParam] m => m CapParam
31pop = do
30pop = do
32 v : stack <- get
31 v : stack <- get
33 put stack
32 put stack
34 return v
33 return v
35
34
36−read_param :: (EnvType m ~ (CapExpression, [CapParam]), MonadReader m) => Word -> m CapParam
35+read_param :: MonadReader (CapExpression, [CapParam]) m => Word -> m CapParam
37read_param pn = do
36read_param pn = do
38 (_,params) <- ask
37 (_,params) <- ask
39 return $ genericIndex params pn
38 return $ genericIndex params pn
40
39
41−push :: (StateType m ~ [CapParam], MonadState m) => CapParam -> m ()
40+push :: MonadState [CapParam] m => CapParam -> m ()
42push v = do
41push v = do
43 stack <-get
42 stack <-get
44 put (v : stack)
43 put (v : stack)
src/Data/Terminfo/Parse.hs+49−18
@@ -1,22 +1,19 @@
1+{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE NoMonomorphismRestriction #-}
3{-# LANGUAGE NoMonomorphismRestriction #-}
3{-# LANGUAGE NamedFieldPuns #-}
4{-# LANGUAGE NamedFieldPuns #-}
4module Data.Terminfo.Parse ( module Data.Terminfo.Parse
5module Data.Terminfo.Parse ( module Data.Terminfo.Parse
5− , Text.Parsec.ParseError
6+ , Text.ParserCombinators.Parsec.ParseError
6 )
7 )
7 where
8 where
8
9
9import Control.Monad ( liftM )
10import Control.Monad ( liftM )
10−import Control.Monad.ST.Strict
11
11
12import Data.Array.Unboxed
12import Data.Array.Unboxed
13−import Data.Array.ST
14import Data.Monoid
13import Data.Monoid
15import Data.Word
14import Data.Word
16
15
17−import Numeric
18−
19−import Text.Parsec
16+import Text.ParserCombinators.Parsec
20
17
21type BytesLength = Word8
18type BytesLength = Word8
22type BytesOffset = Word8
19type BytesOffset = Word8
@@ -46,6 +43,7 @@ data CapOp =
46 , conditional_parts :: [(CapOps, CapOps)]
43 , conditional_parts :: [(CapOps, CapOps)]
47 }
44 }
48 | BitwiseOr
45 | BitwiseOr
46+ | CompareEq
49 | LiteralInt Int
47 | LiteralInt Int
50 deriving ( Show )
48 deriving ( Show )
51
49
@@ -55,15 +53,15 @@ data ParamOp =
55 deriving ( Show )
53 deriving ( Show )
56
54
57parse_cap_expression :: String -> Either ParseError CapExpression
55parse_cap_expression :: String -> Either ParseError CapExpression
58−parse_cap_expression cap_string = runST $ do
56+parse_cap_expression cap_string =
59− v <- runParserT cap_expression_parser
57+ let v = runParser cap_expression_parser
60 initial_build_state
58 initial_build_state
61 "terminfo cap"
59 "terminfo cap"
62 cap_string
60 cap_string
63− case v of
61+ in case v of
64− Left error -> return $ Left error
62+ Left e -> Left e
65 Right build_results ->
63 Right build_results ->
66− return $ Right $ CapExpression
64+ Right $ CapExpression
67 { cap_ops = out_cap_ops build_results
65 { cap_ops = out_cap_ops build_results
68 -- The cap bytes are the lower 8 bits of the input string's characters.
66 -- The cap bytes are the lower 8 bits of the input string's characters.
69 -- \todo Verify the input string actually contains an 8bit byte per character.
67 -- \todo Verify the input string actually contains an 8bit byte per character.
@@ -74,21 +72,27 @@ parse_cap_expression cap_string = runST $ do
74 , param_ops = out_param_ops build_results
72 , param_ops = out_param_ops build_results
75 }
73 }
76
74
75+type CapParser a = GenParser Char BuildState a
76+
77+cap_expression_parser :: CapParser BuildResults
77cap_expression_parser = do
78cap_expression_parser = do
78 rs <- many $ param_escape_parser <|> bytes_op_parser
79 rs <- many $ param_escape_parser <|> bytes_op_parser
79 return $ mconcat rs
80 return $ mconcat rs
80
81
82+param_escape_parser :: CapParser BuildResults
81param_escape_parser = do
83param_escape_parser = do
82 char '%'
84 char '%'
83 inc_offset 1
85 inc_offset 1
84 literal_percent_parser <|> param_op_parser
86 literal_percent_parser <|> param_op_parser
85
87
88+literal_percent_parser :: CapParser BuildResults
86literal_percent_parser = do
89literal_percent_parser = do
87 char '%'
90 char '%'
88 start_offset <- getState >>= return . next_offset
91 start_offset <- getState >>= return . next_offset
89 inc_offset 1
92 inc_offset 1
90 return $ BuildResults 0 [Bytes (start_offset, 1)] []
93 return $ BuildResults 0 [Bytes (start_offset, 1)] []
91
94
95+param_op_parser :: CapParser BuildResults
92param_op_parser
96param_op_parser
93 = increment_op_parser
97 = increment_op_parser
94 <|> push_op_parser
98 <|> push_op_parser
@@ -96,23 +100,28 @@ param_op_parser
96 <|> conditional_op_parser
100 <|> conditional_op_parser
97 <|> bitwise_op_parser
101 <|> bitwise_op_parser
98 <|> literal_int_op_parser
102 <|> literal_int_op_parser
103+ <|> compare_op_parser
99
104
105+increment_op_parser :: CapParser BuildResults
100increment_op_parser = do
106increment_op_parser = do
101 char 'i'
107 char 'i'
102 inc_offset 1
108 inc_offset 1
103 return $ BuildResults 0 [] [ IncFirstTwo ]
109 return $ BuildResults 0 [] [ IncFirstTwo ]
104
110
111+push_op_parser :: CapParser BuildResults
105push_op_parser = do
112push_op_parser = do
106 char 'p'
113 char 'p'
107 param_n <- digit >>= return . (\d -> read [d])
114 param_n <- digit >>= return . (\d -> read [d])
108 inc_offset 2
115 inc_offset 2
109 return $ BuildResults param_n [ PushParam $ param_n - 1 ] []
116 return $ BuildResults param_n [ PushParam $ param_n - 1 ] []
110
117
118+dec_out_parser :: CapParser BuildResults
111dec_out_parser = do
119dec_out_parser = do
112 char 'd'
120 char 'd'
113 inc_offset 1
121 inc_offset 1
114 return $ BuildResults 0 [ DecOut ] []
122 return $ BuildResults 0 [ DecOut ] []
115
123
124+conditional_op_parser :: CapParser BuildResults
116conditional_op_parser = do
125conditional_op_parser = do
117 char '?'
126 char '?'
118 inc_offset 1
127 inc_offset 1
@@ -122,7 +131,9 @@ conditional_op_parser = do
122 true_part <- many_expr $ choice [ try $ lookAhead conditional_end_parser
131 true_part <- many_expr $ choice [ try $ lookAhead conditional_end_parser
123 , conditional_false_parser
132 , conditional_false_parser
124 ]
133 ]
125− false_part <- many_expr $ lookAhead conditional_end_parser
134+ false_part <- many_expr $ choice [ try $ lookAhead conditional_end_parser
135+ , conditional_true_parser
136+ ]
126 return ( true_part, false_part )
137 return ( true_part, false_part )
127 )
138 )
128 conditional_end_parser
139 conditional_end_parser
@@ -140,9 +151,9 @@ conditional_op_parser = do
140
151
141 let true_param_ops = mconcat $ map out_param_ops true_parts
152 let true_param_ops = mconcat $ map out_param_ops true_parts
142 false_param_ops = mconcat $ map out_param_ops false_parts
153 false_param_ops = mconcat $ map out_param_ops false_parts
143− param_ops = mconcat [cond_param_ops, true_param_ops, false_param_ops]
154+ p_ops = mconcat [cond_param_ops, true_param_ops, false_param_ops]
144
155
145− return $ BuildResults n'' [ Conditional cond cond_parts ] param_ops
156+ return $ BuildResults n'' [ Conditional cond cond_parts ] p_ops
146
157
147 where
158 where
148 many_p p end = choice
159 many_p p end = choice
@@ -153,26 +164,32 @@ conditional_op_parser = do
153 ]
164 ]
154 many_expr end = liftM mconcat $ many_p ( param_escape_parser <|> bytes_op_parser ) end
165 many_expr end = liftM mconcat $ many_p ( param_escape_parser <|> bytes_op_parser ) end
155
166
167+conditional_true_parser :: CapParser ()
156conditional_true_parser = do
168conditional_true_parser = do
157 string "%t"
169 string "%t"
158 inc_offset 2
170 inc_offset 2
159
171
172+conditional_false_parser :: CapParser ()
160conditional_false_parser = do
173conditional_false_parser = do
161 string "%e"
174 string "%e"
162 inc_offset 2
175 inc_offset 2
163
176
177+conditional_end_parser :: CapParser ()
164conditional_end_parser = do
178conditional_end_parser = do
165 string "%;"
179 string "%;"
166 inc_offset 2
180 inc_offset 2
167
181
182+bitwise_op_parser :: CapParser BuildResults
168bitwise_op_parser = do
183bitwise_op_parser = do
169 bitwise_or_parser
184 bitwise_or_parser
170
185
186+bitwise_or_parser :: CapParser BuildResults
171bitwise_or_parser = do
187bitwise_or_parser = do
172 char '|'
188 char '|'
173 inc_offset 1
189 inc_offset 1
174 return $ BuildResults 0 [ BitwiseOr ] [ ]
190 return $ BuildResults 0 [ BitwiseOr ] [ ]
175
191
192+literal_int_op_parser :: CapParser BuildResults
176literal_int_op_parser = do
193literal_int_op_parser = do
177 char '{'
194 char '{'
178 inc_offset 1
195 inc_offset 1
@@ -183,19 +200,33 @@ literal_int_op_parser = do
183 inc_offset 1
200 inc_offset 1
184 return $ BuildResults 0 [ LiteralInt n ] [ ]
201 return $ BuildResults 0 [ LiteralInt n ] [ ]
185
202
203+compare_op_parser :: CapParser BuildResults
204+compare_op_parser = do
205+ char '='
206+ inc_offset 1
207+ return $ BuildResults 0 [ CompareEq ] [ ]
208+
209+bytes_op_parser :: CapParser BuildResults
186bytes_op_parser = do
210bytes_op_parser = do
187 bytes <- many1 $ satisfy (/= '%')
211 bytes <- many1 $ satisfy (/= '%')
188 start_offset <- getState >>= return . next_offset
212 start_offset <- getState >>= return . next_offset
189− let !count = toEnum $ length bytes
213+ let !c = toEnum $ length bytes
214+ !s <- getState
190− modifyState $ \s -> s { next_offset = start_offset + count }
215+ let s' = s { next_offset = start_offset + c }
216+ setState s'
191− return $ BuildResults 0 [Bytes (start_offset, count)] []
217+ return $ BuildResults 0 [Bytes (start_offset, c)] []
192
218
193data BuildState = BuildState
219data BuildState = BuildState
194 { next_offset :: Word8
220 { next_offset :: Word8
195 }
221 }
196
222
223+inc_offset :: Word8 -> CapParser ()
224+inc_offset n = do
225+ s <- getState
197−inc_offset n = modifyState $ \s -> s { next_offset = next_offset s + n }
226+ let s' = s { next_offset = next_offset s + n }
227+ setState s'
198
228
229+initial_build_state :: BuildState
199initial_build_state = BuildState 0
230initial_build_state = BuildState 0
200
231
201data BuildResults = BuildResults
232data BuildResults = BuildResults
src/Graphics/Vty/Image.hs+26−20
@@ -20,6 +20,7 @@ module Graphics.Vty.Image ( Image(..)
20 , utf8_bytestring
20 , utf8_bytestring
21 , char_fill
21 , char_fill
22 , empty_image
22 , empty_image
23+ , translate
23 -- | The possible display attributes used in constructing an `Image`.
24 -- | The possible display attributes used in constructing an `Image`.
24 , module Graphics.Vty.Attributes
25 , module Graphics.Vty.Attributes
25 )
26 )
@@ -31,6 +32,7 @@ import Codec.Binary.UTF8.Width
31
32
32import Codec.Binary.UTF8.String ( decode )
33import Codec.Binary.UTF8.String ( decode )
33
34
35+import Data.AffineSpace
34import qualified Data.ByteString as BS
36import qualified Data.ByteString as BS
35import Data.Monoid
37import Data.Monoid
36import qualified Data.Sequence as Seq
38import qualified Data.Sequence as Seq
@@ -94,11 +96,12 @@ data Image =
94 , output_height :: !Word -- >= 1
96 , output_height :: !Word -- >= 1
95 }
97 }
96 -- The combining operators identity constant.
98 -- The combining operators identity constant.
97− -- IdImage <|> a = a
99+ -- EmptyImage <|> a = a
98− -- IdImage <-> a = a
100+ -- EmptyImage <-> a = a
99 --
101 --
100− -- Any image of zero size equals the identity image.
102+ -- Any image of zero size equals the empty image.
101− | IdImage
103+ | EmptyImage
104+ | Translation (Int, Int) Image
102 deriving Eq
105 deriving Eq
103
106
104instance Show Image where
107instance Show Image where
@@ -110,7 +113,7 @@ instance Show Image where
110 = "HorizJoin " ++ show c ++ " ( " ++ show l ++ " <|> " ++ show r ++ " )"
113 = "HorizJoin " ++ show c ++ " ( " ++ show l ++ " <|> " ++ show r ++ " )"
111 show ( VertJoin { part_top = t, part_bottom = b, output_width = c, output_height = r } )
114 show ( VertJoin { part_top = t, part_bottom = b, output_width = c, output_height = r } )
112 = "VertJoin (" ++ show c ++ ", " ++ show r ++ ") ( " ++ show t ++ " ) <-> ( " ++ show b ++ " )"
115 = "VertJoin (" ++ show c ++ ", " ++ show r ++ ") ( " ++ show t ++ " ) <-> ( " ++ show b ++ " )"
113− show ( IdImage ) = "IdImage"
116+ show ( EmptyImage ) = "EmptyImage"
114
117
115-- | Currently append in the Monoid instance is equivalent to <->. Future versions will just stack
118-- | Currently append in the Monoid instance is equivalent to <->. Future versions will just stack
116-- the images.
119-- the images.
@@ -118,18 +121,18 @@ instance Monoid Image where
118 mempty = empty_image
121 mempty = empty_image
119 mappend = (<->)
122 mappend = (<->)
120
123
121−-- A horizontal text image of 0 characters in width simplifies to the IdImage
124+-- A horizontal text image of 0 characters in width simplifies to the EmptyImage
122horiz_text :: Attr -> StringSeq -> Word -> Image
125horiz_text :: Attr -> StringSeq -> Word -> Image
123horiz_text a txt ow
126horiz_text a txt ow
124− | ow == 0 = IdImage
127+ | ow == 0 = EmptyImage
125 | otherwise = HorizText a txt (toEnum $ Seq.length txt) ow
128 | otherwise = HorizText a txt (toEnum $ Seq.length txt) ow
126
129
127horiz_join :: Image -> Image -> Word -> Word -> Image
130horiz_join :: Image -> Image -> Word -> Word -> Image
128horiz_join i_0 i_1 w h
131horiz_join i_0 i_1 w h
129− -- A horiz join of two 0 width images simplifies to the IdImage
132+ -- A horiz join of two 0 width images simplifies to the EmptyImage
130− | w == 0 = IdImage
133+ | w == 0 = EmptyImage
131 -- A horizontal join where either part is 0 columns in width simplifies to the other part.
134 -- A horizontal join where either part is 0 columns in width simplifies to the other part.
132− -- This covers the case where one part is the IdImage.
135+ -- This covers the case where one part is the EmptyImage.
133 | image_width i_0 == 0 = i_1
136 | image_width i_0 == 0 = i_1
134 | image_width i_1 == 0 = i_0
137 | image_width i_1 == 0 = i_0
135 -- If the images are of the same height then no BG padding is required
138 -- If the images are of the same height then no BG padding is required
@@ -157,10 +160,10 @@ horiz_join _ _ _ _ = error "horiz_join applied to undefined values."
157
160
158vert_join :: Image -> Image -> Word -> Word -> Image
161vert_join :: Image -> Image -> Word -> Word -> Image
159vert_join i_0 i_1 w h
162vert_join i_0 i_1 w h
160− -- A vertical join of two 0 height images simplifies to the IdImage
163+ -- A vertical join of two 0 height images simplifies to the EmptyImage
161− | h == 0 = IdImage
164+ | h == 0 = EmptyImage
162 -- A vertical join where either part is 0 rows in height simplifies to the other part.
165 -- A vertical join where either part is 0 rows in height simplifies to the other part.
163− -- This covers the case where one part is the IdImage
166+ -- This covers the case where one part is the EmptyImage
164 | image_height i_0 == 0 = i_1
167 | image_height i_0 == 0 = i_1
165 | image_height i_1 == 0 = i_0
168 | image_height i_1 == 0 = i_0
166 -- If the images are of the same height then no background padding is required
169 -- If the images are of the same height then no background padding is required
@@ -189,8 +192,8 @@ vert_join _ _ _ _ = error "vert_join applied to undefined values."
189-- | An area of the picture's bacground (See Background) of w columns and h rows.
192-- | An area of the picture's bacground (See Background) of w columns and h rows.
190background_fill :: Word -> Word -> Image
193background_fill :: Word -> Word -> Image
191background_fill w h
194background_fill w h
192− | w == 0 = IdImage
195+ | w == 0 = EmptyImage
193− | h == 0 = IdImage
196+ | h == 0 = EmptyImage
194 | otherwise = BGFill w h
197 | otherwise = BGFill w h
195
198
196-- | The width of an Image. This is the number display columns the image will occupy.
199-- | The width of an Image. This is the number display columns the image will occupy.
@@ -199,7 +202,7 @@ image_width HorizText { output_width = w } = w
199image_width HorizJoin { output_width = w } = w
202image_width HorizJoin { output_width = w } = w
200image_width VertJoin { output_width = w } = w
203image_width VertJoin { output_width = w } = w
201image_width BGFill { output_width = w } = w
204image_width BGFill { output_width = w } = w
202−image_width IdImage = 0
205+image_width EmptyImage = 0
203
206
204-- | The height of an Image. This is the number of display rows the image will occupy.
207-- | The height of an Image. This is the number of display rows the image will occupy.
205image_height :: Image -> Word
208image_height :: Image -> Word
@@ -207,7 +210,7 @@ image_height HorizText {} = 1
207image_height HorizJoin { output_height = r } = r
210image_height HorizJoin { output_height = r } = r
208image_height VertJoin { output_height = r } = r
211image_height VertJoin { output_height = r } = r
209image_height BGFill { output_height = r } = r
212image_height BGFill { output_height = r } = r
210−image_height IdImage = 0
213+image_height EmptyImage = 0
211
214
212-- | Combines two images side by side.
215-- | Combines two images side by side.
213--
216--
@@ -257,14 +260,14 @@ im_t <-> im_b
257
260
258-- | Compose any number of images horizontally.
261-- | Compose any number of images horizontally.
259horiz_cat :: [Image] -> Image
262horiz_cat :: [Image] -> Image
260−horiz_cat = foldr (<|>) IdImage
263+horiz_cat = foldr (<|>) EmptyImage
261
264
262horzcat :: [Image] -> Image
265horzcat :: [Image] -> Image
263horzcat = horiz_cat
266horzcat = horiz_cat
264
267
265-- | Compose any number of images vertically.
268-- | Compose any number of images vertically.
266vert_cat :: [Image] -> Image
269vert_cat :: [Image] -> Image
267−vert_cat = foldr (<->) IdImage
270+vert_cat = foldr (<->) EmptyImage
268
271
269vertcat :: [Image] -> Image
272vertcat :: [Image] -> Image
270vertcat = vert_cat
273vertcat = vert_cat
@@ -325,5 +328,8 @@ char_fill !a !c w h =
325-- | The empty image. Useful for fold combinators. These occupy no space nor define any display
328-- | The empty image. Useful for fold combinators. These occupy no space nor define any display
326-- attributes.
329-- attributes.
327empty_image :: Image
330empty_image :: Image
328−empty_image = IdImage
331+empty_image = EmptyImage
332+
333+translate :: Image -> (Int, Int) -> Image
334+translate i v = Translation v i
329
335
src/Graphics/Vty/Picture.hs−1
@@ -1,5 +1,4 @@
1-- Copyright 2009 Corey O'Connor
1-- Copyright 2009 Corey O'Connor
2−{-# LANGUAGE NamedFieldPuns #-}
3module Graphics.Vty.Picture ( module Graphics.Vty.Picture
2module Graphics.Vty.Picture ( module Graphics.Vty.Picture
4 , image_width
3 , image_width
5 , image_height
4 , image_height
src/Graphics/Vty/Span.hs+2−1
@@ -123,7 +123,7 @@ ops_for_row mrow_ops bg region image y remaining_columns
123 | remaining_columns == 0 = return ()
123 | remaining_columns == 0 = return ()
124 | y >= region_height region = return ()
124 | y >= region_height region = return ()
125 | otherwise = case image of
125 | otherwise = case image of
126− IdImage -> return ()
126+ EmptyImage -> return ()
127 -- The width provided is the number of columns this text span will occupy when displayed.
127 -- The width provided is the number of columns this text span will occupy when displayed.
128 -- if this is greater than the number of remaining columsn the output has to be produced a
128 -- if this is greater than the number of remaining columsn the output has to be produced a
129 -- character at a time.
129 -- character at a time.
@@ -147,6 +147,7 @@ ops_for_row mrow_ops bg region image y remaining_columns
147 then remaining_columns
147 then remaining_columns
148 else width
148 else width
149 forM_ [y .. y + actual_height - 1] $ \y' -> snoc_bg_fill mrow_ops bg actual_width y'
149 forM_ [y .. y + actual_height - 1] $ \y' -> snoc_bg_fill mrow_ops bg actual_width y'
150+ Translation v i -> return ()
150
151
151snoc_text_span :: (Foldable.Foldable t)
152snoc_text_span :: (Foldable.Foldable t)
152 => Attr
153 => Attr
vty.cabal+3−2
@@ -26,10 +26,11 @@ Description:
26Build-Depends: base >= 4 && < 5, bytestring, containers, unix, uvector
26Build-Depends: base >= 4 && < 5, bytestring, containers, unix, uvector
27Build-Depends: terminfo >= 0.2 && < 0.3
27Build-Depends: terminfo >= 0.2 && < 0.3
28Build-Depends: utf8-string >= 0.3 && < 0.4
28Build-Depends: utf8-string >= 0.3 && < 0.4
29−Build-Depends: transformers, monads-tf
29+Build-Depends: mtl >= 1.1.0.0 && < 1.2
30Build-Depends: ghc-prim
30Build-Depends: ghc-prim
31Build-Depends: array
31Build-Depends: array
32−Build-Depends: parsec >= 3 && < 4
32+Build-Depends: parsec >= 2 && < 4
33+Build-Depends: vector-space >= 0.5 && < 0.6
33Build-Type: Simple
34Build-Type: Simple
34Data-Files: README, TODO
35Data-Files: README, TODO
35Exposed-Modules: Graphics.Vty
36Exposed-Modules: Graphics.Vty