@@ -1,4 +1,3 @@
1−{-# LANGUAGE RecordWildCards #-}
21{-# LANGUAGE FlexibleInstances #-}
32{-# LANGUAGE OverloadedStrings #-}
43{-# LANGUAGE CPP #-}
@@ -15,7 +14,7 @@ module Graphics.Vty.Output.TerminfoBased
1514where
1615
1716import Control.Monad (when)
18−import Data.Bits (shiftL)
17+import Data.Bits (shiftL, (.&.))
1918import qualified Data.ByteString as BS
2019import Data.ByteString.Internal (toForeignPtr)
2120import Data.Terminfo.Parse
@@ -26,9 +25,8 @@ import Graphics.Vty.Image (DisplayRegion)
2625import Graphics.Vty.DisplayAttributes
2726import Graphics.Vty.Output.Interface
2827
29−import Blaze.ByteString.Builder (Write, writeToByteString, writeStorable)
28+import Blaze.ByteString.Builder (Write, writeToByteString, writeStorable, writeWord8)
3029
31−import Data.Bits ((.&.))
3230import Data.IORef
3331import Data.Maybe (isJust, isNothing, fromJust)
3432import Data.Word
@@ -51,7 +49,6 @@ data TerminfoCaps = TerminfoCaps
5149 , cup :: CapExpression
5250 , cnorm :: Maybe CapExpression
5351 , civis :: Maybe CapExpression
54− , supportsNoColors :: Bool
5552 , useAltColorMap :: Bool
5653 , setForeColor :: CapExpression
5754 , setBackColor :: CapExpression
@@ -68,6 +65,8 @@ data DisplayAttrCaps = DisplayAttrCaps
6865 , exitStandout :: Maybe CapExpression
6966 , enterItalic :: Maybe CapExpression
7067 , exitItalic :: Maybe CapExpression
68+ , enterStrikethrough :: Maybe CapExpression
69+ , exitStrikethrough :: Maybe CapExpression
7170 , enterUnderline :: Maybe CapExpression
7271 , exitUnderline :: Maybe CapExpression
7372 , enterReverseVideo :: Maybe CapExpression
@@ -104,28 +103,28 @@ sendCapToTerminal t cap capParams = do
104103--
105104-- * Providing independent string capabilities for all display
106105-- attributes.
107−reserveTerminal :: String -> Fd -> IO Output
108−reserveTerminal termName outFd = do
106+reserveTerminal :: String -> Fd -> ColorMode -> IO Output
107+reserveTerminal termName outFd colorMode = do
109108 ti <- Terminfo.setupTerm termName
110109 -- assumes set foreground always implies set background exists.
111110 -- if set foreground is not set then all color changing style
112111 -- attributes are filtered.
113112 msetaf <- probeCap ti "setaf"
114113 msetf <- probeCap ti "setf"
115− let (noColors, useAlt, setForeCap)
114+ let (useAlt, setForeCap)
116115 = case msetaf of
117− Just setaf -> (False, False, setaf)
116+ Just setaf -> (False, setaf)
118117 Nothing -> case msetf of
119− Just setf -> (False, True, setf)
120− Nothing -> (True, True, error $ "no fore color support for terminal " ++ termName)
118+ Just setf -> (True, setf)
119+ Nothing -> (True, error $ "no fore color support for terminal " ++ termName)
121120 msetab <- probeCap ti "setab"
122121 msetb <- probeCap ti "setb"
123− let set_back_cap
122+ let setBackCap
124123 = case msetab of
124+ Just setab -> setab
125125 Nothing -> case msetb of
126126 Just setb -> setb
127127 Nothing -> error $ "no back color support for terminal " ++ termName
128− Just setab -> setab
129128
130129 hyperlinkModeStatus <- newIORef False
131130 newAssumedStateRef <- newIORef initialAssumedState
@@ -151,10 +150,9 @@ reserveTerminal termName outFd = do
151150 <*> requireCap ti "cup"
152151 <*> probeCap ti "cnorm"
153152 <*> probeCap ti "civis"
154− <*> pure noColors
155153 <*> pure useAlt
156154 <*> pure setForeCap
157− <*> pure set_back_cap
155+ <*> pure setBackCap
158156 <*> requireCap ti "sgr0"
159157 <*> requireCap ti "clear"
160158 <*> requireCap ti "el"
@@ -166,6 +164,10 @@ reserveTerminal termName outFd = do
166164 sendCap setDefaultAttr []
167165 maybeSendCap cnorm []
168166 , supportsBell = return $ isJust $ ringBellAudio terminfoCaps
167+ , supportsItalics = return $ (isJust $ enterItalic (displayAttrCaps terminfoCaps)) &&
168+ (isJust $ exitItalic (displayAttrCaps terminfoCaps))
169+ , supportsStrikethrough = return $ (isJust $ enterStrikethrough (displayAttrCaps terminfoCaps)) &&
170+ (isJust $ exitStrikethrough (displayAttrCaps terminfoCaps))
169171 , ringTerminalBell = maybeSendCap ringBellAudio []
170172 , reserveDisplay = do
171173 -- If there is no support for smcup: Clear the screen
@@ -190,20 +192,15 @@ reserveTerminal termName outFd = do
190192 when (toEnum len /= actualLen) $ fail $ "Graphics.Vty.Output: outputByteBuffer "
191193 ++ "length mismatch. " ++ show len ++ " /= " ++ show actualLen
192194 ++ " Please report this bug to vty project."
193− , contextColorCount
194− = case supportsNoColors terminfoCaps of
195− False -> case Terminfo.getCapability ti (Terminfo.tiGetNum "colors" ) of
196− Nothing -> 8
197− Just v -> toEnum v
198− True -> 1
199195 , supportsCursorVisibility = isJust $ civis terminfoCaps
200196 , supportsMode = terminfoModeSupported
201197 , setMode = terminfoSetMode
202198 , getModeStatus = terminfoModeStatus
203199 , assumedStateRef = newAssumedStateRef
200+ , outputColorMode = colorMode
204201 -- I think fix would help assure tActual is the only
205202 -- reference. I was having issues tho.
206− , mkDisplayContext = \tActual -> terminfoDisplayContext tActual terminfoCaps
203+ , mkDisplayContext = (`terminfoDisplayContext` terminfoCaps)
207204 }
208205 sendCap s = sendCapToTerminal t (s terminfoCaps)
209206 maybeSendCap s = when (isJust $ s terminfoCaps) . sendCap (fromJust . s)
@@ -235,6 +232,8 @@ currentDisplayAttrCaps ti
235232 <*> probeCap ti "rmso"
236233 <*> probeCap ti "sitm"
237234 <*> probeCap ti "ritm"
235+ <*> probeCap ti "smxx"
236+ <*> probeCap ti "rmxx"
238237 <*> probeCap ti "smul"
239238 <*> probeCap ti "rmul"
240239 <*> probeCap ti "rev"
@@ -270,7 +269,11 @@ terminfoDisplayContext tActual terminfoCaps r = return dc
270269 , writeSetAttr = terminfoWriteSetAttr dc terminfoCaps
271270 , writeDefaultAttr = \urlsEnabled ->
272271 writeCapExpr (setDefaultAttr terminfoCaps) [] `mappend`
273− (if urlsEnabled then writeURLEscapes EndLink else mempty)
272+ (if urlsEnabled then writeURLEscapes EndLink else mempty) `mappend`
273+ (case exitStrikethrough $ displayAttrCaps terminfoCaps of
274+ Just cap -> writeCapExpr cap []
275+ Nothing -> mempty
276+ )
274277 , writeRowEnd = writeCapExpr (clearEol terminfoCaps) []
275278 , inlineHack = return ()
276279 }
@@ -292,7 +295,7 @@ writeURLEscapes NoLinkChange =
292295-- | Portably setting the display attributes is a giant pain in the ass.
293296--
294297-- If the terminal supports the sgr capability (which sets the on/off
295−-- state of each style directly ; and, for no good reason, resets the
298+-- state of each style directly; and, for no good reason, resets the
296299-- colors to the default) this procedure is used:
297300--
298301-- 0. set the style attributes. This resets the fore and back color.
@@ -331,9 +334,9 @@ terminfoWriteSetAttr dc terminfoCaps urlsEnabled prevAttr reqAttr diffs =
331334 -- The only way to reset either color, portably, to the default
332335 -- is to use either the set state capability or the set default
333336 -- capability.
334− True -> do
337+ True -> do
335338 case reqDisplayCapSeqFor (displayAttrCaps terminfoCaps)
336− (fixedStyle attr )
339+ (fixedStyle attr)
337340 (styleToApplySeq $ fixedStyle attr) of
338341 -- only way to reset a color to the defaults
339342 EnterExitSeq caps -> writeDefaultAttr dc urlsEnabled
@@ -343,11 +346,11 @@ terminfoWriteSetAttr dc terminfoCaps urlsEnabled prevAttr reqAttr diffs =
343346 setColors
344347 -- implicitly resets the colors to the defaults
345348 SetState state -> writeCapExpr (fromJust $ setAttrStates
346− $ displayAttrCaps
347− $ terminfoCaps
349+ $ displayAttrCaps terminfoCaps
348350 )
349351 (sgrArgsForState state)
350352 `mappend` setItalics
353+ `mappend` setStrikethrough
351354 `mappend` setColors
352355 -- Otherwise the display colors are not changing or changing
353356 -- between two non-default points.
@@ -366,15 +369,16 @@ terminfoWriteSetAttr dc terminfoCaps urlsEnabled prevAttr reqAttr diffs =
366369 -- applied states.
367370 EnterExitSeq caps -> foldMap (\cap -> writeCapExpr cap []) caps
368371 `mappend`
369− writeColorDiff setForeColor (foreColorDiff diffs)
372+ writeColorDiff Foreground (foreColorDiff diffs)
370373 `mappend`
371− writeColorDiff setBackColor (backColorDiff diffs)
374+ writeColorDiff Background (backColorDiff diffs)
372375 -- implicitly resets the colors to the defaults
373376 SetState state -> writeCapExpr (fromJust $ setAttrStates
374377 $ displayAttrCaps terminfoCaps
375378 )
376379 (sgrArgsForState state)
377380 `mappend` setItalics
381+ `mappend` setStrikethrough
378382 `mappend` setColors
379383 where
380384 urlAttrs True = writeURLEscapes (urlDiff diffs)
@@ -392,31 +396,72 @@ terminfoWriteSetAttr dc terminfoCaps urlsEnabled prevAttr reqAttr diffs =
392396 , Just sitm <- enterItalic (displayAttrCaps terminfoCaps)
393397 = writeCapExpr sitm []
394398 | otherwise = mempty
399+ setStrikethrough
400+ | hasStyle (fixedStyle attr) strikethrough
401+ , Just smxx <- enterStrikethrough (displayAttrCaps terminfoCaps)
402+ = writeCapExpr smxx []
403+ | otherwise = mempty
395404 setColors =
396405 (case fixedForeColor attr of
397− Just c -> writeCapExpr (setForeColor terminfoCaps)
398− [toEnum $ colorMap c]
406+ Just c -> writeColor Foreground c
399407 Nothing -> mempty)
400408 `mappend`
401409 (case fixedBackColor attr of
402− Just c -> writeCapExpr (setBackColor terminfoCaps)
403− [toEnum $ colorMap c]
410+ Just c -> writeColor Background c
404411 Nothing -> mempty)
405− writeColorDiff _f NoColorChange
412+ writeColorDiff _side NoColorChange
406413 = mempty
407− writeColorDiff _f ColorToDefault
414+ writeColorDiff _side ColorToDefault
408415 = error "ColorToDefault is not a possible case for applyColorDiffs"
409− writeColorDiff f (SetColor c)
410− = writeCapExpr (f terminfoCaps) [toEnum $ colorMap c]
416+ writeColorDiff side (SetColor c)
417+ = writeColor side c
418+
419+ writeColor side (RGBColor r g b) =
420+ case outputColorMode (contextDevice dc) of
421+ FullColor ->
422+ hardcodeColor side (r, g, b)
423+ _ ->
424+ error "clampColor should remove rgb colors in standard mode"
425+ writeColor side c =
426+ writeCapExpr (setSideColor side terminfoCaps) [toEnum $ colorMap c]
427+
428+-- a color can either be in the foreground or the background
429+data ColorSide = Foreground | Background
430+
431+-- get the capability for drawing a color on a specific side
432+setSideColor :: ColorSide -> TerminfoCaps -> CapExpression
433+setSideColor Foreground = setForeColor
434+setSideColor Background = setBackColor
435+
436+hardcodeColor :: ColorSide -> (Word8, Word8, Word8) -> Write
437+hardcodeColor side (r, g, b) =
438+ -- hardcoded color codes are formatted as "\x1b[{side};2;{r};{g};{b}m"
439+ mconcat [ writeStr "\x1b[", sideCode, delimiter, writeChar '2', delimiter
440+ , writeColor r, delimiter, writeColor g, delimiter, writeColor b
441+ , writeChar 'm']
442+ where
443+ writeChar = writeWord8 . fromIntegral . fromEnum
444+ writeStr = mconcat . map writeChar
445+ writeColor = writeStr . show
446+ delimiter = writeChar ';'
447+ -- 38/48 are used to set whether we should write to the
448+ -- foreground/background. I really don't want to know why.
449+ sideCode = case side of
450+ Foreground -> writeStr "38"
451+ Background -> writeStr "48"
411452
412453-- | The color table used by a terminal is a 16 color set followed by a
413454-- 240 color set that might not be supported by the terminal.
414455--
415−-- This takes a Color which clearly identifies which pallete to use and
416−-- computes the index into the full 256 color pallete.
456+-- This takes a Color which clearly identifies which palette to use and
457+-- computes the index into the full 256 color palette.
417458ansiColorIndex :: Color -> Int
418459ansiColorIndex (ISOColor v) = fromEnum v
419460ansiColorIndex (Color240 v) = 16 + fromEnum v
461+ansiColorIndex (RGBColor _ _ _) =
462+ error $ unlines [ "Attempted to create color index from rgb color."
463+ , "This is currently unsupported, and shouldn't ever happen"
464+ ]
420465
421466-- | For terminals without setaf/setab
422467--
@@ -433,6 +478,10 @@ altColorIndex (ISOColor 6) = 3
433478altColorIndex (ISOColor 7) = 7
434479altColorIndex (ISOColor v) = fromEnum v
435480altColorIndex (Color240 v) = 16 + fromEnum v
481+altColorIndex (RGBColor _ _ _) =
482+ error $ unlines [ "Attempted to create color index from rgb color."
483+ , "This is currently unsupported, and shouldn't ever happen"
484+ ]
436485
437486{- | The sequence of terminfo caps to apply a given style are determined
438487 - according to these rules.
@@ -460,6 +509,7 @@ data DisplayAttrState = DisplayAttrState
460509 { applyStandout :: Bool
461510 , applyUnderline :: Bool
462511 , applyItalic :: Bool
512+ , applyStrikethrough :: Bool
463513 , applyReverseVideo :: Bool
464514 , applyBlink :: Bool
465515 , applyDim :: Bool
@@ -496,6 +546,8 @@ reqDisplayCapSeqFor caps s diffs
496546 -- set state cap then just use the set state cap.
497547 ( True, True ) -> SetState $ stateForStyle s
498548 where
549+ noEnterExitCap ApplyStrikethrough = isNothing $ enterStrikethrough caps
550+ noEnterExitCap RemoveStrikethrough = isNothing $ exitStrikethrough caps
499551 noEnterExitCap ApplyItalic = isNothing $ enterItalic caps
500552 noEnterExitCap RemoveItalic = isNothing $ exitItalic caps
501553 noEnterExitCap ApplyStandout = isNothing $ enterStandout caps
@@ -510,6 +562,8 @@ reqDisplayCapSeqFor caps s diffs
510562 noEnterExitCap RemoveDim = True
511563 noEnterExitCap ApplyBold = isNothing $ enterBoldMode caps
512564 noEnterExitCap RemoveBold = True
565+ enterExitCap ApplyStrikethrough = fromJust $ enterStrikethrough caps
566+ enterExitCap RemoveStrikethrough = fromJust $ exitStrikethrough caps
513567 enterExitCap ApplyItalic = fromJust $ enterItalic caps
514568 enterExitCap RemoveItalic = fromJust $ exitItalic caps
515569 enterExitCap ApplyStandout = fromJust $ enterStandout caps
@@ -526,6 +580,7 @@ stateForStyle s = DisplayAttrState
526580 { applyStandout = isStyleSet standout
527581 , applyUnderline = isStyleSet underline
528582 , applyItalic = isStyleSet italic
583+ , applyStrikethrough = isStyleSet strikethrough
529584 , applyReverseVideo = isStyleSet reverseVideo
530585 , applyBlink = isStyleSet blink
531586 , applyDim = isStyleSet dim
@@ -538,6 +593,7 @@ styleToApplySeq s = concat
538593 [ applyIfRequired ApplyStandout standout
539594 , applyIfRequired ApplyUnderline underline
540595 , applyIfRequired ApplyItalic italic
596+ , applyIfRequired ApplyStrikethrough strikethrough
541597 , applyIfRequired ApplyReverseVideo reverseVideo
542598 , applyIfRequired ApplyBlink blink
543599 , applyIfRequired ApplyDim dim