@@ -16,16 +16,17 @@ import Graphics.Vty.Span
1616
1717import Graphics.Vty.DisplayAttributes
1818
19−import Blaze.ByteString.Builder (Write)
19+import Blaze.ByteString.Builder (Write, writeToByteString)
2020import Blaze.ByteString.Builder.ByteString (writeByteString)
2121
2222import Control.Monad.Trans
2323
2424import qualified Data.ByteString as BS
25+import Data.IORef
26+import Data.Monoid (mempty, mappend)
2527import qualified Data.Text.Encoding as T
2628import qualified Data.Text.Lazy as TL
27−import Data.IORef
28−import qualified Data.Vector.Unboxed as Vector
29+import qualified Data.Vector as Vector
2930
3031data Output = Output
3132 { -- | Text identifier for the output device. Used for debugging.
@@ -105,7 +106,7 @@ data DisplayContext = DisplayContext
105106
106107-- | All terminals serialize UTF8 text to the terminal device exactly as serialized in memory.
107108write_utf8_text :: BS.ByteString -> Write
108−write_utf8_text str dest_ptr = writeByteString
109+write_utf8_text = writeByteString
109110
110111-- | Displays the given `Picture`.
111112--
@@ -125,9 +126,9 @@ output_picture :: MonadIO m => DisplayContext -> Picture -> m ()
125126output_picture dc pic = liftIO $ do
126127 as <- readIORef (assumed_state_ref $ context_device dc)
127128 let manip_cursor = supports_cursor_visibility (context_device dc)
128− let !r = context_region dc
129− !ops = display_ops_for_pic pic r
130− !initial_attr = FixedAttr default_style_mask Nothing Nothing
129+ r = context_region dc
130+ ops = display_ops_for_pic pic r
131+ initial_attr = FixedAttr default_style_mask Nothing Nothing
131132 -- Diff the previous output against the requested output. Differences are currently on a per-row
132133 -- basis.
133134 -- \todo handle resizes that crop the dominate directions better.
@@ -137,134 +138,60 @@ output_picture dc pic = liftIO $ do
137138 then replicate (display_ops_rows ops) True
138139 else zipWith (/=) (Vector.toList previous_ops)
139140 (Vector.toList ops)
140− -- determine the number of bytes required to completely serialize the output ops.
141− total = (if manip_cursor then hide_cursor_required_bytes dc else 0)
142− + default_attr_required_bytes dc
143− + required_bytes dc initial_attr diffs ops
144− + case pic_cursor pic of
145− _ | not manip_cursor -> 0
146− NoCursor -> 0
141+ -- build the Write corresponding to the output image
142+ out = write_default_attr dc
143+ `mappend` write_output_ops dc initial_attr diffs ops
144+ `mappend`
145+ (case pic_cursor pic of
146+ _ | not manip_cursor -> mempty
147+ NoCursor -> write_hide_cursor dc
147148 Cursor x y ->
148− let { m = cursor_output_map ops $ pic_cursor pic; (ox, oy) = char_to_output_pos m ( x, y ) }
149− in show_cursor_required_bytes dc + move_cursor_required_bytes dc ox oy
149+ let m = cursor_output_map ops $ pic_cursor pic
150+ (ox, oy) = char_to_output_pos m (x,y)
151+ in write_show_cursor dc `mappend` write_move_cursor dc ox oy
152+ )
150153 -- ... then serialize
151− allocaBytes (fromEnum total) $ \start_ptr -> do
152− ptr <- case manip_cursor of
153− True -> serialize_hide_cursor dc start_ptr
154− False -> return start_ptr
155− ptr' <- serialize_default_attr dc ptr
156− ptr'' <- serialize_output_ops dc ptr' initial_attr diffs ops
157− end_ptr <- case pic_cursor pic of
158− _ | not manip_cursor -> return ptr''
159− NoCursor -> return ptr''
160− Cursor x y -> do
161− let m = cursor_output_map ops $ pic_cursor pic
162− (ox, oy) = char_to_output_pos m (x,y)
163− serialize_show_cursor dc ptr'' >>= serialize_move_cursor dc ox oy
164− -- todo: How to handle exceptions?
165− -- probably set the prev_output_ops to Nothing
166− case end_ptr `minusPtr` start_ptr of
167− count | count < 0 -> fail "End pointer before start of buffer."
168− | toEnum count > total -> fail $ "End pointer past end of buffer by " ++ show (toEnum count - total)
169− | otherwise -> output_byte_buffer (context_device dc) start_ptr (toEnum count)
170− -- Cache the output spans.
171− let as' = as { prev_output_ops = Just ops }
172− writeIORef (assumed_state_ref $ context_device dc) as'
173−
174−required_bytes :: DisplayContext -> FixedAttr -> [Bool] -> DisplayOps -> Int
175−required_bytes dc in_fattr diffs ops =
176− let (_, n, _, _) = Vector.foldl' required_bytes' (0, 0, in_fattr, diffs) ops
177− in n
154+ output_byte_buffer (context_device dc) (writeToByteString out)
155+ -- Cache the output spans.
156+ let as' = as { prev_output_ops = Just ops }
157+ writeIORef (assumed_state_ref $ context_device dc) as'
158+
159+write_output_ops :: DisplayContext -> FixedAttr -> [Bool] -> DisplayOps -> Write
160+write_output_ops dc in_fattr diffs ops =
161+ let (_, out, _, _) = Vector.foldl' write_output_ops'
162+ (0, mempty, in_fattr, diffs)
163+ ops
164+ in out
178165 where
179− required_bytes' (y, current_sum, fattr, True : diffs') span_ops
180− = let (s, fattr') = span_ops_required_bytes dc y fattr span_ops
181− in ( y + 1, s + current_sum, fattr', diffs' )
182− required_bytes' (y, current_sum, fattr, False : diffs') _span_ops
183− = ( y + 1, current_sum, fattr, diffs' )
184− required_bytes' (_y, _current_sum, _fattr, [] ) _span_ops
185− = error "shouldn't be possible"
186−
187−span_ops_required_bytes :: DisplayContext -> Int -> FixedAttr -> SpanOps -> (Int, FixedAttr)
188−span_ops_required_bytes dc y in_fattr span_ops =
189− -- The first operation is to set the cursor to the start of the row
190− let header_required_bytes = move_cursor_required_bytes dc 0 y
191− -- then the span ops are serialized in the order specified
192− in Vector.foldl' ( \(current_sum, fattr) op -> let (c, fattr') = span_op_required_bytes dc fattr op
193− in (c + current_sum, fattr')
194− )
195− (header_required_bytes, in_fattr)
196− span_ops
197−
198−span_op_required_bytes :: DisplayContext -> FixedAttr -> SpanOp -> (Int, FixedAttr)
199−span_op_required_bytes dc fattr (TextSpan attr _ _ str) =
200− let attr' = limit_attr_for_display (context_device dc) attr
201− diffs = display_attr_diffs fattr fattr'
202− c = attr_required_bytes dc fattr attr' diffs
203− fattr' = fix_display_attr fattr attr'
204− in (c + utf8_text_required_bytes (T.encodeUtf8 $ TL.toStrict str), fattr')
205−span_op_required_bytes _dc _fattr (Skip _) = error "span_op_required_bytes for Skip."
206−span_op_required_bytes dc fattr (RowEnd _) = (row_end_required_bytes dc, fattr)
207−
208−serialize_output_ops :: DisplayContext
209− -> OutputBuffer
210− -> FixedAttr
211− -> [Bool]
212− -> DisplayOps
213− -> IO OutputBuffer
214−serialize_output_ops dc start_ptr in_fattr diffs ops = do
215− (_, end_ptr, _, _) <- Vector.foldM' serialize_output_ops'
216− ( 0, start_ptr, in_fattr, diffs )
217− ops
218− return end_ptr
219− where
220− serialize_output_ops' ( y, out_ptr, fattr, True : diffs' ) span_ops
221− = serialize_span_ops dc y out_ptr fattr span_ops
222− >>= return . ( \(out_ptr', fattr') -> ( y + 1, out_ptr', fattr', diffs' ) )
223− serialize_output_ops' ( y, out_ptr, fattr, False : diffs' ) _span_ops
224− = return ( y + 1, out_ptr, fattr, diffs' )
225− serialize_output_ops' (_y, _out_ptr, _fattr, [] ) _span_ops
166+ write_output_ops' (y, out, fattr, True : diffs') span_ops
167+ = let (span_out, fattr') = write_span_ops dc y fattr span_ops
168+ in (y+1, out `mappend` span_out, fattr', diffs')
169+ write_output_ops' (y, out, fattr, False : diffs') _span_ops
170+ = (y + 1, out, fattr, diffs')
171+ write_output_ops' (_y, _out, _fattr, []) _span_ops
226172 = error "vty - output spans without a corresponding diff."
227173
228−serialize_span_ops :: DisplayContext
229− -> Int
230− -> OutputBuffer
231− -> FixedAttr
232− -> SpanOps
233− -> IO (OutputBuffer, FixedAttr)
234−serialize_span_ops dc y out_ptr in_fattr span_ops = do
174+write_span_ops :: DisplayContext -> Int -> FixedAttr -> SpanOps -> (Write, FixedAttr)
175+write_span_ops dc y in_fattr span_ops =
235176 -- The first operation is to set the cursor to the start of the row
236− out_ptr' <- serialize_move_cursor dc 0 y out_ptr
177+ let start = write_move_cursor dc 0 y
237178 -- then the span ops are serialized in the order specified
238− Vector.foldM ( \(out_ptr'', fattr) op -> serialize_span_op dc op out_ptr'' fattr )
239− (out_ptr', in_fattr)
240− span_ops
179+ in Vector.foldl' (\(out, fattr) op -> case write_span_op dc op fattr of
180+ (op_out, fattr') -> (out `mappend` op_out, fattr')
181+ )
182+ (start, in_fattr)
183+ span_ops
241184
242−-- |
243−-- TODO: move this into the terminal implementation?
244−serialize_span_op :: DisplayContext
245− -> SpanOp
246− -> OutputBuffer
247− -> FixedAttr
248− -> IO (OutputBuffer, FixedAttr)
249−serialize_span_op dc (TextSpan attr _ _ str) out_ptr fattr = do
185+write_span_op :: DisplayContext -> SpanOp -> FixedAttr -> (Write, FixedAttr)
186+write_span_op dc (TextSpan attr _ _ str) fattr =
250187 let attr' = limit_attr_for_display (context_device dc) attr
251188 fattr' = fix_display_attr fattr attr'
252189 diffs = display_attr_diffs fattr fattr'
253− out_ptr' <- serialize_set_attr dc fattr attr' diffs out_ptr
254− out_ptr'' <- serialize_utf8_text (T.encodeUtf8 $ TL.toStrict str) out_ptr'
255− return (out_ptr'', fattr')
256−serialize_span_op _dc (Skip _) _out_ptr _fattr = error "serialize_span_op for Skip"
257−serialize_span_op dc (RowEnd _) out_ptr fattr = do
258− out_ptr' <- serialize_row_end dc out_ptr
259− return (out_ptr', fattr)
260−
261−send_to_terminal :: Output -> Int -> (Ptr Word8 -> IO (Ptr Word8)) -> IO ()
262−send_to_terminal t c f = allocaBytes (fromEnum c) $ \start_ptr -> do
263− end_ptr <- f start_ptr
264− case end_ptr `minusPtr` start_ptr of
265− count | count < 0 -> fail "End pointer before start pointer."
266− | toEnum count > c -> fail $ "End pointer past end of buffer by " ++ show (toEnum count - c)
267− | otherwise -> output_byte_buffer t start_ptr (toEnum count)
190+ out = write_set_attr dc fattr attr' diffs
191+ `mappend` write_utf8_text (T.encodeUtf8 $ TL.toStrict str)
192+ in (out, fattr')
193+write_span_op _dc (Skip _) _fattr = error "serialize_span_op for Skip"
194+write_span_op dc (RowEnd _) fattr = (write_row_end dc, fattr)
268195
269196-- | The cursor position is given in X,Y character offsets. Due to multi-column characters this
270197-- needs to be translated to column, row positions.