@@ -14,7 +14,7 @@ module Graphics.Vty.Output.TerminfoBased
1414where
1515
1616import Control.Monad (when)
17−import Data.Bits (shiftL)
17+import Data.Bits (shiftL, (.&.))
1818import qualified Data.ByteString as BS
1919import Data.ByteString.Internal (toForeignPtr)
2020import Data.Terminfo.Parse
@@ -25,9 +25,8 @@ import Graphics.Vty.Image (DisplayRegion)
2525import Graphics.Vty.DisplayAttributes
2626import Graphics.Vty.Output.Interface
2727
28−import Blaze.ByteString.Builder (Write, writeToByteString, writeStorable)
28+import Blaze.ByteString.Builder (Write, writeToByteString, writeStorable, writeWord8)
2929
30−import Data.Bits ((.&.))
3130import Data.IORef
3231import Data.Maybe (isJust, isNothing, fromJust)
3332import Data.Word
@@ -50,7 +49,6 @@ data TerminfoCaps = TerminfoCaps
5049 , cup :: CapExpression
5150 , cnorm :: Maybe CapExpression
5251 , civis :: Maybe CapExpression
53− , supportsNoColors :: Bool
5452 , useAltColorMap :: Bool
5553 , setForeColor :: CapExpression
5654 , setBackColor :: CapExpression
@@ -105,28 +103,28 @@ sendCapToTerminal t cap capParams = do
105103--
106104-- * Providing independent string capabilities for all display
107105-- attributes.
108−reserveTerminal :: String -> Fd -> IO Output
109−reserveTerminal termName outFd = do
106+reserveTerminal :: String -> Fd -> ColorMode -> IO Output
107+reserveTerminal termName outFd colorMode = do
110108 ti <- Terminfo.setupTerm termName
111109 -- assumes set foreground always implies set background exists.
112110 -- if set foreground is not set then all color changing style
113111 -- attributes are filtered.
114112 msetaf <- probeCap ti "setaf"
115113 msetf <- probeCap ti "setf"
116− let (noColors, useAlt, setForeCap)
114+ let (useAlt, setForeCap)
117115 = case msetaf of
118− Just setaf -> (False, False, setaf)
116+ Just setaf -> (False, setaf)
119117 Nothing -> case msetf of
120− Just setf -> (False, True, setf)
121− 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)
122120 msetab <- probeCap ti "setab"
123121 msetb <- probeCap ti "setb"
124− let set_back_cap
122+ let setBackCap
125123 = case msetab of
124+ Just setab -> setab
126125 Nothing -> case msetb of
127126 Just setb -> setb
128127 Nothing -> error $ "no back color support for terminal " ++ termName
129− Just setab -> setab
130128
131129 hyperlinkModeStatus <- newIORef False
132130 newAssumedStateRef <- newIORef initialAssumedState
@@ -152,10 +150,9 @@ reserveTerminal termName outFd = do
152150 <*> requireCap ti "cup"
153151 <*> probeCap ti "cnorm"
154152 <*> probeCap ti "civis"
155− <*> pure noColors
156153 <*> pure useAlt
157154 <*> pure setForeCap
158− <*> pure set_back_cap
155+ <*> pure setBackCap
159156 <*> requireCap ti "sgr0"
160157 <*> requireCap ti "clear"
161158 <*> requireCap ti "el"
@@ -195,17 +192,12 @@ reserveTerminal termName outFd = do
195192 when (toEnum len /= actualLen) $ fail $ "Graphics.Vty.Output: outputByteBuffer "
196193 ++ "length mismatch. " ++ show len ++ " /= " ++ show actualLen
197194 ++ " Please report this bug to vty project."
198− , contextColorCount
199− = case supportsNoColors terminfoCaps of
200− False -> case Terminfo.getCapability ti (Terminfo.tiGetNum "colors" ) of
201− Nothing -> 8
202− Just v -> toEnum v
203− True -> 1
204195 , supportsCursorVisibility = isJust $ civis terminfoCaps
205196 , supportsMode = terminfoModeSupported
206197 , setMode = terminfoSetMode
207198 , getModeStatus = terminfoModeStatus
208199 , assumedStateRef = newAssumedStateRef
200+ , outputColorMode = colorMode
209201 -- I think fix would help assure tActual is the only
210202 -- reference. I was having issues tho.
211203 , mkDisplayContext = (`terminfoDisplayContext` terminfoCaps)
@@ -303,7 +295,7 @@ writeURLEscapes NoLinkChange =
303295-- | Portably setting the display attributes is a giant pain in the ass.
304296--
305297-- If the terminal supports the sgr capability (which sets the on/off
306−-- 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
307299-- colors to the default) this procedure is used:
308300--
309301-- 0. set the style attributes. This resets the fore and back color.
@@ -342,9 +334,9 @@ terminfoWriteSetAttr dc terminfoCaps urlsEnabled prevAttr reqAttr diffs =
342334 -- The only way to reset either color, portably, to the default
343335 -- is to use either the set state capability or the set default
344336 -- capability.
345− True -> do
337+ True -> do
346338 case reqDisplayCapSeqFor (displayAttrCaps terminfoCaps)
347− (fixedStyle attr )
339+ (fixedStyle attr)
348340 (styleToApplySeq $ fixedStyle attr) of
349341 -- only way to reset a color to the defaults
350342 EnterExitSeq caps -> writeDefaultAttr dc urlsEnabled
@@ -354,8 +346,7 @@ terminfoWriteSetAttr dc terminfoCaps urlsEnabled prevAttr reqAttr diffs =
354346 setColors
355347 -- implicitly resets the colors to the defaults
356348 SetState state -> writeCapExpr (fromJust $ setAttrStates
357− $ displayAttrCaps
358− $ terminfoCaps
349+ $ displayAttrCaps terminfoCaps
359350 )
360351 (sgrArgsForState state)
361352 `mappend` setItalics
@@ -378,9 +369,9 @@ terminfoWriteSetAttr dc terminfoCaps urlsEnabled prevAttr reqAttr diffs =
378369 -- applied states.
379370 EnterExitSeq caps -> foldMap (\cap -> writeCapExpr cap []) caps
380371 `mappend`
381− writeColorDiff setForeColor (foreColorDiff diffs)
372+ writeColorDiff Foreground (foreColorDiff diffs)
382373 `mappend`
383− writeColorDiff setBackColor (backColorDiff diffs)
374+ writeColorDiff Background (backColorDiff diffs)
384375 -- implicitly resets the colors to the defaults
385376 SetState state -> writeCapExpr (fromJust $ setAttrStates
386377 $ displayAttrCaps terminfoCaps
@@ -412,29 +403,65 @@ terminfoWriteSetAttr dc terminfoCaps urlsEnabled prevAttr reqAttr diffs =
412403 | otherwise = mempty
413404 setColors =
414405 (case fixedForeColor attr of
415− Just c -> writeCapExpr (setForeColor terminfoCaps)
416− [toEnum $ colorMap c]
406+ Just c -> writeColor Foreground c
417407 Nothing -> mempty)
418408 `mappend`
419409 (case fixedBackColor attr of
420− Just c -> writeCapExpr (setBackColor terminfoCaps)
421− [toEnum $ colorMap c]
410+ Just c -> writeColor Background c
422411 Nothing -> mempty)
423− writeColorDiff _f NoColorChange
412+ writeColorDiff _side NoColorChange
424413 = mempty
425− writeColorDiff _f ColorToDefault
414+ writeColorDiff _side ColorToDefault
426415 = error "ColorToDefault is not a possible case for applyColorDiffs"
427− writeColorDiff f (SetColor c)
428− = 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"
429452
430453-- | The color table used by a terminal is a 16 color set followed by a
431454-- 240 color set that might not be supported by the terminal.
432455--
433−-- This takes a Color which clearly identifies which pallete to use and
434−-- 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.
435458ansiColorIndex :: Color -> Int
436459ansiColorIndex (ISOColor v) = fromEnum v
437460ansiColorIndex (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+ ]
438465
439466-- | For terminals without setaf/setab
440467--
@@ -451,6 +478,10 @@ altColorIndex (ISOColor 6) = 3
451478altColorIndex (ISOColor 7) = 7
452479altColorIndex (ISOColor v) = fromEnum v
453480altColorIndex (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+ ]
454485
455486{- | The sequence of terminfo caps to apply a given style are determined
456487 - according to these rules.