vty-5.35: src/Graphics/Vty/Output/XTermColor.hs
{-# Language CPP #-}
-- Copyright 2009-2010 Corey O'Connor
-- | Xterm output driver. This uses the Terminfo driver with some
-- extensions for Xterm.
module Graphics.Vty.Output.XTermColor
( reserveTerminal
)
where
import Graphics.Vty.Output.Interface
import Graphics.Vty.Input.Mouse
import Graphics.Vty.Input.Focus
import Graphics.Vty.Attributes.Color (ColorMode)
import qualified Graphics.Vty.Output.TerminfoBased as TerminfoBased
import Blaze.ByteString.Builder (writeToByteString)
import Blaze.ByteString.Builder.Word (writeWord8)
import qualified Data.ByteString.Char8 as BS8
import Data.ByteString.Char8 (ByteString)
import Foreign.Ptr (castPtr)
import Control.Monad (void, when)
import Control.Monad.Trans
import Data.Char (toLower)
import Data.IORef
import System.Posix.IO (fdWriteBuf)
import System.Posix.Types (ByteCount, Fd)
import System.Posix.Env (getEnv)
import Data.List (isInfixOf)
import Data.Maybe (catMaybes)
#if !MIN_VERSION_base(4,11,0)
import Data.Monoid ((<>))
#endif
-- | Write a 'ByteString' to an 'Fd'.
fdWrite :: Fd -> ByteString -> IO ByteCount
fdWrite fd s =
BS8.useAsCStringLen s $ \(buf,len) -> do
fdWriteBuf fd (castPtr buf) (fromIntegral len)
-- | Construct an Xterm output driver. Initialize the display to UTF-8.
reserveTerminal :: ( Applicative m, MonadIO m ) => String -> Fd -> ColorMode -> m Output
reserveTerminal variant outFd colorMode = liftIO $ do
let flushedPut = void . fdWrite outFd
-- If the terminal variant is xterm-color use xterm instead since,
-- more often than not, xterm-color is broken.
let variant' = if variant == "xterm-color" then "xterm" else variant
utf8a <- utf8Active
when (not utf8a) $ flushedPut setUtf8CharSet
t <- TerminfoBased.reserveTerminal variant' outFd colorMode
mouseModeStatus <- newIORef False
focusModeStatus <- newIORef False
pasteModeStatus <- newIORef False
let xtermSetMode t' m newStatus = do
curStatus <- getModeStatus t' m
when (newStatus /= curStatus) $
case m of
Focus -> liftIO $ do
case newStatus of
True -> flushedPut requestFocusEvents
False -> flushedPut disableFocusEvents
writeIORef focusModeStatus newStatus
Mouse -> liftIO $ do
case newStatus of
True -> flushedPut requestMouseEvents
False -> flushedPut disableMouseEvents
writeIORef mouseModeStatus newStatus
BracketedPaste -> liftIO $ do
case newStatus of
True -> flushedPut enableBracketedPastes
False -> flushedPut disableBracketedPastes
writeIORef pasteModeStatus newStatus
Hyperlink -> setMode t Hyperlink newStatus
xtermGetMode Mouse = liftIO $ readIORef mouseModeStatus
xtermGetMode Focus = liftIO $ readIORef focusModeStatus
xtermGetMode BracketedPaste = liftIO $ readIORef pasteModeStatus
xtermGetMode Hyperlink = getModeStatus t Hyperlink
let t' = t
{ terminalID = terminalID t ++ " (xterm-color)"
, releaseTerminal = do
when (not utf8a) $ liftIO $ flushedPut setDefaultCharSet
setMode t' BracketedPaste False
setMode t' Mouse False
setMode t' Focus False
releaseTerminal t
, mkDisplayContext = \tActual r -> do
dc <- mkDisplayContext t tActual r
return $ dc { inlineHack = xtermInlineHack t' }
, supportsMode = const True
, getModeStatus = xtermGetMode
, setMode = xtermSetMode t'
}
return t'
utf8Active :: IO Bool
utf8Active = do
let vars = ["LC_ALL", "LANG", "LC_CTYPE"]
results <- map (toLower <$>) . catMaybes <$> mapM getEnv vars
let matches = filter ("utf8" `isInfixOf`) results <>
filter ("utf-8" `isInfixOf`) results
return $ not $ null matches
-- | Enable bracketed paste mode:
-- http://cirw.in/blog/bracketed-paste
enableBracketedPastes :: ByteString
enableBracketedPastes = BS8.pack "\ESC[?2004h"
-- | Disable bracketed paste mode:
disableBracketedPastes :: ByteString
disableBracketedPastes = BS8.pack "\ESC[?2004l"
-- | These sequences set xterm based terminals to UTF-8 output.
--
-- There is no known terminfo capability equivalent to this.
setUtf8CharSet, setDefaultCharSet :: ByteString
setUtf8CharSet = BS8.pack "\ESC%G"
setDefaultCharSet = BS8.pack "\ESC%@"
xtermInlineHack :: Output -> IO ()
xtermInlineHack t = do
let writeReset = foldMap (writeWord8.toEnum.fromEnum) "\ESC[K"
outputByteBuffer t $ writeToByteString writeReset