on master help sign in
perf: Use `ByteString` instead of `[Char]` for input parsing.
98b2a83iphydf
src/Graphics/Vty/Input/Classify.hs+27−24
@@ -1,5 +1,5 @@
1{-# OPTIONS_HADDOCK hide #-}
1{-# OPTIONS_HADDOCK hide #-}
2−-- This makes a kind of tri. Has space efficiency issues with large
2+-- This makes a kind of trie. Has space efficiency issues with large
3-- input blocks. Likely building a parser and just applying that would
3-- input blocks. Likely building a parser and just applying that would
4-- be better.
4-- be better.
5module Graphics.Vty.Input.Classify
5module Graphics.Vty.Input.Classify
@@ -16,25 +16,28 @@ import Graphics.Vty.Input.Classify.Types
16
16
17import Codec.Binary.UTF8.Generic (decode)
17import Codec.Binary.UTF8.Generic (decode)
18
18
19−import Data.List (inits)
19+import Control.Arrow (first)
20import qualified Data.Map as M( fromList, lookup )
20import qualified Data.Map as M( fromList, lookup )
21import Data.Maybe ( mapMaybe )
21import Data.Maybe ( mapMaybe )
22import qualified Data.Set as S( fromList, member )
22import qualified Data.Set as S( fromList, member )
23
23
24−import Data.Char
25import Data.Word
24import Data.Word
26
25
26+import qualified Data.ByteString as BS
27+import qualified Data.ByteString.Char8 as BS8
28+import Data.ByteString.Char8 (ByteString)
29+
27−compile :: ClassifyMap -> String -> KClass
30+compile :: ClassifyMap -> ByteString -> KClass
28compile table = cl' where
31compile table = cl' where
29 -- take all prefixes and create a set of these
32 -- take all prefixes and create a set of these
30− prefixSet = S.fromList $ concatMap (init . inits . fst) table
33+ prefixSet = S.fromList $ concatMap (init . BS.inits . BS8.pack . fst) table
31 maxValidInputLength = maximum (map (length . fst) table)
34 maxValidInputLength = maximum (map (length . fst) table)
32− eventForInput = M.fromList table
35+ eventForInput = M.fromList $ map (first BS8.pack) table
33− cl' [] = Prefix
36+ cl' inputBlock | BS8.null inputBlock = Prefix
34 cl' inputBlock = case M.lookup inputBlock eventForInput of
37 cl' inputBlock = case M.lookup inputBlock eventForInput of
35 -- if the inputBlock is exactly what is expected for an
38 -- if the inputBlock is exactly what is expected for an
36 -- event then consume the whole block and return the event
39 -- event then consume the whole block and return the event
37− Just e -> Valid e []
40+ Just e -> Valid e BS8.empty
38 Nothing -> case S.member inputBlock prefixSet of
41 Nothing -> case S.member inputBlock prefixSet of
39 True -> Prefix
42 True -> Prefix
40 -- look up progressively smaller tails of the input
43 -- look up progressively smaller tails of the input
@@ -45,40 +48,40 @@ compile table = cl' where
45 -- H: There will always be one match. The prefixSet
48 -- H: There will always be one match. The prefixSet
46 -- contains, by definition, all prefixes of an event.
49 -- contains, by definition, all prefixes of an event.
47 False ->
50 False ->
48− let inputPrefixes = reverse $ take maxValidInputLength $ tail $ inits inputBlock
51+ let inputPrefixes = reverse . take maxValidInputLength . tail . BS8.inits $ inputBlock
49 in case mapMaybe (\s -> (,) s `fmap` M.lookup s eventForInput) inputPrefixes of
52 in case mapMaybe (\s -> (,) s `fmap` M.lookup s eventForInput) inputPrefixes of
50− (s,e) : _ -> Valid e (drop (length s) inputBlock)
53+ (s,e) : _ -> Valid e (BS8.drop (BS8.length s) inputBlock)
51 -- neither a prefix or a full event.
54 -- neither a prefix or a full event.
52 [] -> Invalid
55 [] -> Invalid
53
56
54−classify :: ClassifyMap -> String -> KClass
57+classify :: ClassifyMap -> ByteString -> KClass
55classify table =
58classify table =
56 let standardClassifier = compile table
59 let standardClassifier = compile table
57− in \s -> case s of
60+ in \s -> case BS.uncons s of
58 _ | bracketedPasteStarted s ->
61 _ | bracketedPasteStarted s ->
59 if bracketedPasteFinished s
62 if bracketedPasteFinished s
60 then parseBracketedPaste s
63 then parseBracketedPaste s
61 else Prefix
64 else Prefix
62− _ | isMouseEvent s -> classifyMouseEvent s
65+ _ | isMouseEvent s -> classifyMouseEvent s
63− _ | isFocusEvent s -> classifyFocusEvent s
66+ _ | isFocusEvent s -> classifyFocusEvent s
64− c:cs | ord c >= 0xC2 -> classifyUtf8 c cs
67+ Just (c,cs) | c >= 0xC2 -> classifyUtf8 c cs
65− _ -> standardClassifier s
68+ _ -> standardClassifier s
66
69
67−classifyUtf8 :: Char -> String -> KClass
70+classifyUtf8 :: Word8 -> ByteString -> KClass
68classifyUtf8 c cs =
71classifyUtf8 c cs =
69− let n = utf8Length (ord c)
72+ let n = utf8Length c
70− (codepoint,rest) = splitAt n (c:cs)
73+ (codepoint,rest) = BS8.splitAt (n - 1) cs
71
74
72 codepoint8 :: [Word8]
75 codepoint8 :: [Word8]
73− codepoint8 = map (fromIntegral . ord) codepoint
76+ codepoint8 = c:BS.unpack codepoint
74
77
75 in case decode codepoint8 of
78 in case decode codepoint8 of
76− _ | n < length codepoint -> Prefix
79+ _ | n < BS.length codepoint + 1 -> Prefix
77− Just (unicodeChar, _) -> Valid (EvKey (KChar unicodeChar) []) rest
80+ Just (unicodeChar, _) -> Valid (EvKey (KChar unicodeChar) []) rest
78 -- something bad happened; just ignore and continue.
81 -- something bad happened; just ignore and continue.
79− Nothing -> Invalid
82+ Nothing -> Invalid
80
83
81−utf8Length :: (Num t, Ord a, Num a) => a -> t
84+utf8Length :: Word8 -> Int
82utf8Length c
85utf8Length c
83 | c < 0x80 = 1
86 | c < 0x80 = 1
84 | c < 0xE0 = 2
87 | c < 0xE0 = 2
src/Graphics/Vty/Input/Classify/Parse.hs+10−7
@@ -16,12 +16,15 @@ import Graphics.Vty.Input.Classify.Types
16import Control.Monad.Trans.Maybe
16import Control.Monad.Trans.Maybe
17import Control.Monad.State
17import Control.Monad.State
18
18
19+import qualified Data.ByteString.Char8 as BS8
20+import Data.ByteString.Char8 (ByteString)
21+
19−type Parser a = MaybeT (State String) a
22+type Parser a = MaybeT (State ByteString) a
20
23
21-- | Run a parser on a given input string. If the parser fails, return
24-- | Run a parser on a given input string. If the parser fails, return
22-- 'Invalid'. Otherwise return the valid event ('Valid') and the
25-- 'Invalid'. Otherwise return the valid event ('Valid') and the
23-- remaining unparsed characters.
26-- remaining unparsed characters.
24−runParser :: String -> Parser Event -> KClass
27+runParser :: ByteString -> Parser Event -> KClass
25runParser s parser =
28runParser s parser =
26 case runState (runMaybeT parser) s of
29 case runState (runMaybeT parser) s of
27 (Nothing, _) -> Invalid
30 (Nothing, _) -> Invalid
@@ -36,9 +39,9 @@ failParse = fail "invalid parse"
36-- return '123' and consume those characters.
39-- return '123' and consume those characters.
37readInt :: Parser Int
40readInt :: Parser Int
38readInt = do
41readInt = do
39− s <- get
42+ s <- BS8.unpack <$> get
40 case (reads :: ReadS Int) s of
43 case (reads :: ReadS Int) s of
41− [(i, rest)] -> put rest >> return i
44+ [(i, rest)] -> put (BS8.pack rest) >> return i
42 _ -> failParse
45 _ -> failParse
43
46
44-- | Read a character from the input stream. If one cannot be read (e.g.
47-- | Read a character from the input stream. If one cannot be read (e.g.
@@ -46,9 +49,9 @@ readInt = do
46readChar :: Parser Char
49readChar :: Parser Char
47readChar = do
50readChar = do
48 s <- get
51 s <- get
49− case s of
52+ case BS8.uncons s of
50− c:rest -> put rest >> return c
53+ Just (c,rest) -> put rest >> return c
51− _ -> failParse
54+ Nothing -> failParse
52
55
53-- | Read a character from the input stream and fail parsing if it is
56-- | Read a character from the input stream and fail parsing if it is
54-- not the specified character.
57-- not the specified character.
src/Graphics/Vty/Input/Classify/Types.hs+3−1
@@ -8,8 +8,10 @@ where
8
8
9import Graphics.Vty.Input.Events
9import Graphics.Vty.Input.Events
10
10
11+import Data.ByteString.Char8 (ByteString)
12+
11data KClass
13data KClass
12− = Valid Event String
14+ = Valid Event ByteString
13 -- ^ A valid event was parsed. Any unused characters from the input
15 -- ^ A valid event was parsed. Any unused characters from the input
14 -- stream are also provided.
16 -- stream are also provided.
15 | Invalid
17 | Invalid
src/Graphics/Vty/Input/Focus.hs+15−13
@@ -11,30 +11,32 @@ import Graphics.Vty.Input.Classify.Types
11import Graphics.Vty.Input.Classify.Parse
11import Graphics.Vty.Input.Classify.Parse
12
12
13import Control.Monad.State
13import Control.Monad.State
14−import Data.List (isPrefixOf)
14+
15+import qualified Data.ByteString.Char8 as BS8
16+import Data.ByteString.Char8 (ByteString)
15
17
16-- | These sequences set xterm-based terminals to send focus event
18-- | These sequences set xterm-based terminals to send focus event
17-- sequences.
19-- sequences.
18−requestFocusEvents :: String
20+requestFocusEvents :: ByteString
19−requestFocusEvents = "\ESC[?1004h"
21+requestFocusEvents = BS8.pack "\ESC[?1004h"
20
22
21-- | These sequences disable focus events.
23-- | These sequences disable focus events.
22−disableFocusEvents :: String
24+disableFocusEvents :: ByteString
23−disableFocusEvents = "\ESC[?1004l"
25+disableFocusEvents = BS8.pack "\ESC[?1004l"
24
26
25-- | Does the specified string begin with a focus event?
27-- | Does the specified string begin with a focus event?
26−isFocusEvent :: String -> Bool
28+isFocusEvent :: ByteString -> Bool
27−isFocusEvent s = isPrefixOf focusIn s ||
29+isFocusEvent s = BS8.isPrefixOf focusIn s ||
28− isPrefixOf focusOut s
30+ BS8.isPrefixOf focusOut s
29
31
30−focusIn :: String
32+focusIn :: ByteString
31−focusIn = "\ESC[I"
33+focusIn = BS8.pack "\ESC[I"
32
34
33−focusOut :: String
35+focusOut :: ByteString
34−focusOut = "\ESC[O"
36+focusOut = BS8.pack "\ESC[O"
35
37
36-- | Attempt to classify an input string as a focus event.
38-- | Attempt to classify an input string as a focus event.
37−classifyFocusEvent :: String -> KClass
39+classifyFocusEvent :: ByteString -> KClass
38classifyFocusEvent s = runParser s $ do
40classifyFocusEvent s = runParser s $ do
39 when (not $ isFocusEvent s) failParse
41 when (not $ isFocusEvent s) failParse
40
42
src/Graphics/Vty/Input/Loop.hs+18−13
@@ -33,12 +33,15 @@ import Control.Monad.Trans.State (StateT(..), evalStateT)
33import Control.Monad.State.Class (MonadState, modify)
33import Control.Monad.State.Class (MonadState, modify)
34import Control.Monad.Trans.Reader (ReaderT(..))
34import Control.Monad.Trans.Reader (ReaderT(..))
35
35
36−import Data.Char
36+import qualified Data.ByteString.Char8 as BS8
37+import qualified Data.ByteString as BS
38+import Data.ByteString.Char8 (ByteString)
37import Data.IORef
39import Data.IORef
38import Data.Word (Word8)
40import Data.Word (Word8)
39
41
40−import Foreign ( allocaArray, peekArray, Ptr )
42+import Foreign (allocaArray)
41import Foreign.C.Types (CInt(..))
43import Foreign.C.Types (CInt(..))
44+import Foreign.Ptr (Ptr, castPtr)
42
45
43import System.IO
46import System.IO
44import System.Posix.IO (fdReadBuf, setFdOption, FdOption(..))
47import System.Posix.IO (fdReadBuf, setFdOption, FdOption(..))
@@ -76,10 +79,10 @@ data InputBuffer = InputBuffer
76makeLenses ''InputBuffer
79makeLenses ''InputBuffer
77
80
78data InputState = InputState
81data InputState = InputState
79− { _unprocessedBytes :: String
82+ { _unprocessedBytes :: ByteString
80 , _appliedConfig :: Config
83 , _appliedConfig :: Config
81 , _inputBuffer :: InputBuffer
84 , _inputBuffer :: InputBuffer
82− , _classifier :: String -> KClass
85+ , _classifier :: ByteString -> KClass
83 }
86 }
84
87
85makeLenses ''InputState
88makeLenses ''InputState
@@ -104,7 +107,7 @@ loopInputProcessor = do
104 dropInvalid
107 dropInvalid
105 loopInputProcessor
108 loopInputProcessor
106
109
107−addBytesToProcess :: String -> InputM ()
110+addBytesToProcess :: ByteString -> InputM ()
108addBytesToProcess block = unprocessedBytes <>= block
111addBytesToProcess block = unprocessedBytes <>= block
109
112
110emit :: Event -> InputM ()
113emit :: Event -> InputM ()
@@ -117,7 +120,7 @@ emit event = do
117--
120--
118-- Precondition: Under the threaded runtime. Only current use is from a
121-- Precondition: Under the threaded runtime. Only current use is from a
119-- forkOS thread. That case satisfies precondition.
122-- forkOS thread. That case satisfies precondition.
120−readFromDevice :: InputM String
123+readFromDevice :: InputM ByteString
121readFromDevice = do
124readFromDevice = do
122 newConfig <- view configRef >>= liftIO . readIORef
125 newConfig <- view configRef >>= liftIO . readIORef
123 oldConfig <- use appliedConfig
126 oldConfig <- use appliedConfig
@@ -137,9 +140,10 @@ readFromDevice = do
137 threadWaitRead fd
140 threadWaitRead fd
138 bytesRead <- fdReadBuf fd bufferPtr (fromIntegral maxBytes)
141 bytesRead <- fdReadBuf fd bufferPtr (fromIntegral maxBytes)
139 if bytesRead > 0
142 if bytesRead > 0
140− then map (chr . fromIntegral) <$> peekArray (fromIntegral bytesRead) bufferPtr
143+ then BS.packCStringLen (castPtr bufferPtr, fromIntegral bytesRead)
141− else return []
144+ else return BS.empty
145+ when (not $ BS.null stringRep) $
142− when (not $ null stringRep) $ logMsg $ "input bytes: " ++ show stringRep
146+ logMsg $ "input bytes: " ++ show (BS8.unpack stringRep)
143 return stringRep
147 return stringRep
144
148
145applyConfig :: Fd -> Config -> IO ()
149applyConfig :: Fd -> Config -> IO ()
@@ -165,15 +169,16 @@ dropInvalid = do
165 b <- use unprocessedBytes
169 b <- use unprocessedBytes
166 when (c b == Invalid) $ do
170 when (c b == Invalid) $ do
167 logMsg "dropping input bytes"
171 logMsg "dropping input bytes"
168− unprocessedBytes .= []
172+ unprocessedBytes .= BS8.empty
169
173
170runInputProcessorLoop :: ClassifyMap -> Input -> IO ()
174runInputProcessorLoop :: ClassifyMap -> Input -> IO ()
171runInputProcessorLoop classifyTable input = do
175runInputProcessorLoop classifyTable input = do
172 let bufferSize = 1024
176 let bufferSize = 1024
173 allocaArray bufferSize $ \(bufferPtr :: Ptr Word8) -> do
177 allocaArray bufferSize $ \(bufferPtr :: Ptr Word8) -> do
178+ s0 <- InputState BS8.empty
174− s0 <- InputState [] <$> readIORef (_configRef input)
179+ <$> readIORef (_configRef input)
175− <*> pure (InputBuffer bufferPtr bufferSize)
180+ <*> pure (InputBuffer bufferPtr bufferSize)
176− <*> pure (classify classifyTable)
181+ <*> pure (classify classifyTable)
177 runReaderT (evalStateT loopInputProcessor s0) input
182 runReaderT (evalStateT loopInputProcessor s0) input
178
183
179-- | Construct two IO actions: one to configure the terminal for Vty and
184-- | Construct two IO actions: one to configure the terminal for Vty and
src/Graphics/Vty/Input/Mouse.hs+17−15
@@ -15,10 +15,12 @@ import Graphics.Vty.Input.Classify.Types
15import Graphics.Vty.Input.Classify.Parse
15import Graphics.Vty.Input.Classify.Parse
16
16
17import Control.Monad.State
17import Control.Monad.State
18−import Data.List (isPrefixOf)
19import Data.Maybe (catMaybes)
18import Data.Maybe (catMaybes)
20import Data.Bits ((.&.))
19import Data.Bits ((.&.))
21
20
21+import qualified Data.ByteString.Char8 as BS8
22+import Data.ByteString.Char8 (ByteString)
23+
22-- A mouse event in SGR extended mode is
24-- A mouse event in SGR extended mode is
23--
25--
24-- '\ESC' '[' '<' B ';' X ';' Y ';' ('M'|'m')
26-- '\ESC' '[' '<' B ';' X ';' Y ';' ('M'|'m')
@@ -32,28 +34,28 @@ import Data.Bits ((.&.))
32
34
33-- | These sequences set xterm-based terminals to send mouse event
35-- | These sequences set xterm-based terminals to send mouse event
34-- sequences.
36-- sequences.
35−requestMouseEvents :: String
37+requestMouseEvents :: ByteString
36−requestMouseEvents = "\ESC[?1000h\ESC[?1002h\ESC[?1006h"
38+requestMouseEvents = BS8.pack "\ESC[?1000h\ESC[?1002h\ESC[?1006h"
37
39
38-- | These sequences disable mouse events.
40-- | These sequences disable mouse events.
39−disableMouseEvents :: String
41+disableMouseEvents :: ByteString
40−disableMouseEvents = "\ESC[?1000l\ESC[?1002l\ESC[?1006l"
42+disableMouseEvents = BS8.pack "\ESC[?1000l\ESC[?1002l\ESC[?1006l"
41
43
42-- | Does the specified string begin with a mouse event?
44-- | Does the specified string begin with a mouse event?
43−isMouseEvent :: String -> Bool
45+isMouseEvent :: ByteString -> Bool
44isMouseEvent s = isSGREvent s || isNormalEvent s
46isMouseEvent s = isSGREvent s || isNormalEvent s
45
47
46−isSGREvent :: String -> Bool
48+isSGREvent :: ByteString -> Bool
47−isSGREvent = isPrefixOf sgrPrefix
49+isSGREvent = BS8.isPrefixOf sgrPrefix
48
50
49−sgrPrefix :: String
51+sgrPrefix :: ByteString
50−sgrPrefix = "\ESC[M"
52+sgrPrefix = BS8.pack "\ESC[M"
51
53
52−isNormalEvent :: String -> Bool
54+isNormalEvent :: ByteString -> Bool
53−isNormalEvent = isPrefixOf normalPrefix
55+isNormalEvent = BS8.isPrefixOf normalPrefix
54
56
55−normalPrefix :: String
57+normalPrefix :: ByteString
56−normalPrefix = "\ESC[<"
58+normalPrefix = BS8.pack "\ESC[<"
57
59
58-- Modifier bits:
60-- Modifier bits:
59shiftBit :: Int
61shiftBit :: Int
@@ -88,7 +90,7 @@ hasBitSet :: Int -> Int -> Bool
88hasBitSet val bit = val .&. bit > 0
90hasBitSet val bit = val .&. bit > 0
89
91
90-- | Attempt to lassify an input string as a mouse event.
92-- | Attempt to lassify an input string as a mouse event.
91−classifyMouseEvent :: String -> KClass
93+classifyMouseEvent :: ByteString -> KClass
92classifyMouseEvent s = runParser s $ do
94classifyMouseEvent s = runParser s $ do
93 when (not $ isMouseEvent s) failParse
95 when (not $ isMouseEvent s) failParse
94
96
src/Graphics/Vty/Input/Paste.hs+15−16
@@ -9,34 +9,33 @@ module Graphics.Vty.Input.Paste
9where
9where
10
10
11import qualified Data.ByteString.Char8 as BS8
11import qualified Data.ByteString.Char8 as BS8
12+import Data.ByteString.Char8 (ByteString)
12
13
13import Graphics.Vty.Input.Events
14import Graphics.Vty.Input.Events
14import Graphics.Vty.Input.Classify.Types
15import Graphics.Vty.Input.Classify.Types
15
16
16−import Data.List (isPrefixOf, isInfixOf)
17+bracketedPasteStart :: ByteString
18+bracketedPasteStart = BS8.pack "\ESC[200~"
17
19
18−bracketedPasteStart :: String
19−bracketedPasteStart = "\ESC[200~"
20−
21−bracketedPasteEnd :: String
20+bracketedPasteEnd :: ByteString
22−bracketedPasteEnd = "\ESC[201~"
21+bracketedPasteEnd = BS8.pack "\ESC[201~"
23
22
24-- | Does the input start a bracketed paste?
23-- | Does the input start a bracketed paste?
25−bracketedPasteStarted :: String -> Bool
24+bracketedPasteStarted :: ByteString -> Bool
26−bracketedPasteStarted = isPrefixOf bracketedPasteStart
25+bracketedPasteStarted = BS8.isPrefixOf bracketedPasteStart
27
26
28-- | Does the input contain a complete bracketed paste?
27-- | Does the input contain a complete bracketed paste?
29−bracketedPasteFinished :: String -> Bool
28+bracketedPasteFinished :: ByteString -> Bool
30−bracketedPasteFinished = isInfixOf bracketedPasteEnd
29+bracketedPasteFinished = BS8.isInfixOf bracketedPasteEnd
31
30
32-- | Parse a bracketed paste. This should only be called on a string if
31-- | Parse a bracketed paste. This should only be called on a string if
33-- both 'bracketedPasteStarted' and 'bracketedPasteFinished' return
32-- both 'bracketedPasteStarted' and 'bracketedPasteFinished' return
34-- 'True'.
33-- 'True'.
35−parseBracketedPaste :: String -> KClass
34+parseBracketedPaste :: ByteString -> KClass
36parseBracketedPaste s =
35parseBracketedPaste s =
37− Valid (EvPaste p) (BS8.unpack $ BS8.drop (BS8.length end) rest')
36+ Valid (EvPaste p) (BS8.drop endLen rest')
38 where
37 where
39− start = BS8.pack bracketedPasteStart
38+ startLen = BS8.length bracketedPasteStart
40− end = BS8.pack bracketedPasteEnd
39+ endLen = BS8.length bracketedPasteEnd
41− (_, rest ) = BS8.breakSubstring start . BS8.pack $ s
40+ (_, rest ) = BS8.breakSubstring bracketedPasteStart s
42− (p, rest') = BS8.breakSubstring end . BS8.drop (BS8.length start) $ rest
41+ (p, rest') = BS8.breakSubstring bracketedPasteEnd . BS8.drop startLen $ rest
src/Graphics/Vty/Output/XTermColor.hs+19−9
@@ -15,13 +15,17 @@ import qualified Graphics.Vty.Output.TerminfoBased as TerminfoBased
15import Blaze.ByteString.Builder (writeToByteString)
15import Blaze.ByteString.Builder (writeToByteString)
16import Blaze.ByteString.Builder.Word (writeWord8)
16import Blaze.ByteString.Builder.Word (writeWord8)
17
17
18+import qualified Data.ByteString.Char8 as BS8
19+import Data.ByteString.Char8 (ByteString)
20+import Foreign.Ptr (castPtr)
21+
18import Control.Monad (void, when)
22import Control.Monad (void, when)
19import Control.Monad.Trans
23import Control.Monad.Trans
20import Data.Char (toLower)
24import Data.Char (toLower)
21import Data.IORef
25import Data.IORef
22
26
23−import System.Posix.IO (fdWrite)
27+import System.Posix.IO (fdWriteBuf)
24−import System.Posix.Types (Fd)
28+import System.Posix.Types (ByteCount, Fd)
25import System.Posix.Env (getEnv)
29import System.Posix.Env (getEnv)
26
30
27import Data.List (isInfixOf)
31import Data.List (isInfixOf)
@@ -31,6 +35,12 @@ import Data.Maybe (catMaybes)
31import Data.Monoid ((<>))
35import Data.Monoid ((<>))
32#endif
36#endif
33
37
38+-- | Write a 'ByteString' to an 'Fd'.
39+fdWrite :: Fd -> ByteString -> IO ByteCount
40+fdWrite fd s =
41+ BS8.useAsCStringLen s $ \(buf,len) -> do
42+ fdWriteBuf fd (castPtr buf) (fromIntegral len)
43+
34-- | Construct an Xterm output driver. Initialize the display to UTF-8.
44-- | Construct an Xterm output driver. Initialize the display to UTF-8.
35reserveTerminal :: ( Applicative m, MonadIO m ) => String -> Fd -> m Output
45reserveTerminal :: ( Applicative m, MonadIO m ) => String -> Fd -> m Output
36reserveTerminal variant outFd = liftIO $ do
46reserveTerminal variant outFd = liftIO $ do
@@ -100,19 +110,19 @@ utf8Active = do
100
110
101-- | Enable bracketed paste mode:
111-- | Enable bracketed paste mode:
102-- http://cirw.in/blog/bracketed-paste
112-- http://cirw.in/blog/bracketed-paste
103−enableBracketedPastes :: String
113+enableBracketedPastes :: ByteString
104−enableBracketedPastes = "\ESC[?2004h"
114+enableBracketedPastes = BS8.pack "\ESC[?2004h"
105
115
106-- | Disable bracketed paste mode:
116-- | Disable bracketed paste mode:
107−disableBracketedPastes :: String
117+disableBracketedPastes :: ByteString
108−disableBracketedPastes = "\ESC[?2004l"
118+disableBracketedPastes = BS8.pack "\ESC[?2004l"
109
119
110-- | These sequences set xterm based terminals to UTF-8 output.
120-- | These sequences set xterm based terminals to UTF-8 output.
111--
121--
112-- There is no known terminfo capability equivalent to this.
122-- There is no known terminfo capability equivalent to this.
113−setUtf8CharSet, setDefaultCharSet :: String
123+setUtf8CharSet, setDefaultCharSet :: ByteString
114−setUtf8CharSet = "\ESC%G"
124+setUtf8CharSet = BS8.pack "\ESC%G"
115−setDefaultCharSet = "\ESC%@"
125+setDefaultCharSet = BS8.pack "\ESC%@"
116
126
117xtermInlineHack :: Output -> IO ()
127xtermInlineHack :: Output -> IO ()
118xtermInlineHack t = do
128xtermInlineHack t = do