#include "Common-Safe-Haskell.hs"
{-# OPTIONS_HADDOCK hide #-}
module System.Console.ANSI.Unix
(
#include "Exports-Include.hs"
) where
import Control.Exception.Base (bracket)
import Control.Monad (when)
#if MIN_VERSION_base(4,8,0)
import Data.List (uncons)
#endif
import Data.Maybe (fromMaybe, mapMaybe)
import System.IO (BufferMode (..), Handle, hGetBuffering, hGetEcho,
hIsTerminalDevice, hIsWritable, hPutStr, hReady, hSetBuffering, hSetEcho,
stdin)
import System.Timeout (timeout)
import Text.ParserCombinators.ReadP (readP_to_S)
import System.Console.ANSI.Codes
#include "Common-Include.hs"
#include "Common-Include-Enabled.hs"
hCursorUp h n = hPutStr h $ cursorUpCode n
hCursorDown h n = hPutStr h $ cursorDownCode n
hCursorForward h n = hPutStr h $ cursorForwardCode n
hCursorBackward h n = hPutStr h $ cursorBackwardCode n
hCursorDownLine h n = hPutStr h $ cursorDownLineCode n
hCursorUpLine h n = hPutStr h $ cursorUpLineCode n
hSetCursorColumn h n = hPutStr h $ setCursorColumnCode n
hSetCursorPosition h n m = hPutStr h $ setCursorPositionCode n m
hSaveCursor h = hPutStr h saveCursorCode
hRestoreCursor h = hPutStr h restoreCursorCode
hReportCursorPosition h = hPutStr h reportCursorPositionCode
hClearFromCursorToScreenEnd h = hPutStr h clearFromCursorToScreenEndCode
hClearFromCursorToScreenBeginning h
= hPutStr h clearFromCursorToScreenBeginningCode
hClearScreen h = hPutStr h clearScreenCode
hClearFromCursorToLineEnd h = hPutStr h clearFromCursorToLineEndCode
hClearFromCursorToLineBeginning h = hPutStr h clearFromCursorToLineBeginningCode
hClearLine h = hPutStr h clearLineCode
hScrollPageUp h n = hPutStr h $ scrollPageUpCode n
hScrollPageDown h n = hPutStr h $ scrollPageDownCode n
hUseAlternateScreenBuffer h = hPutStr h useAlternateScreenBufferCode
hUseNormalScreenBuffer h = hPutStr h useNormalScreenBufferCode
hReportLayerColor h layer = hPutStr h $ reportLayerColorCode layer
hSetSGR h sgrs = hPutStr h $ setSGRCode sgrs
hHideCursor h = hPutStr h hideCursorCode
hShowCursor h = hPutStr h showCursorCode
hHyperlinkWithParams h params uri link =
hPutStr h $ hyperlinkWithParamsCode params uri link
hSetTitle h title = hPutStr h $ setTitleCode title
hSupportsANSI h = (&&) <$> hIsTerminalDevice h <*> isNotDumb
where
isNotDumb = (/= Just "dumb") . lookup "TERM" <$> getEnvironment
hSupportsANSIWithoutEmulation h =
Just <$> ((&&) <$> hIsWritable h <*> hSupportsANSI h)
getReportedCursorPosition = getReport "\ESC[" ["R"]
getReportedLayerColor layer =
getReport ("\ESC]" ++ pS ++ ";rgb:") ["\BEL", "\ESC\\"]
where
pS = case layer of
Foreground -> "10"
Background -> "11"
getReport :: String -> [String] -> IO String
getReport _ [] = error "getReport requires a list of terminating sequences."
getReport startChars endChars = do
fromMaybe "" <$> timeout 500000 (getStart startChars "")
where
endChars' = mapMaybe uncons endChars
#if !MIN_VERSION_base(4,8,0)
where
uncons :: [a] -> Maybe (a, [a])
uncons [] = Nothing
uncons (x:xs) = Just (x, xs)
#endif
getStart :: String -> String -> IO String
getStart "" r = getRest r
getStart (h:hs) r = do
c <- getChar
if c == h
then getStart hs (c:r)
else return $ reverse (c:r)
getRest :: String -> IO String
getRest r = do
c <- getChar
case lookup c endChars' of
Nothing -> getRest (c:r)
Just es -> getEnd es (c:r)
getEnd :: String -> String -> IO String
getEnd "" r = return $ reverse r
getEnd (e:es) r = do
c <- getChar
if c /= e
then getRest (c:r)
else getEnd es (c:r)
hGetCursorPosition h = fmap to0base <$> getCursorPosition'
where
to0base (row, col) = (row - 1, col - 1)
getCursorPosition' = do
input <- bracket (hGetBuffering stdin) (hSetBuffering stdin) $ \_ -> do
hSetBuffering stdin NoBuffering
bracket (hGetEcho stdin) (hSetEcho stdin) $ \_ -> do
hSetEcho stdin False
clearStdin
hReportCursorPosition h
hFlush h
getReportedCursorPosition
case readP_to_S cursorPosition input of
[] -> return Nothing
[((row, col),_)] -> return $ Just (row, col)
(_:_) -> return Nothing
clearStdin = do
isReady <- hReady stdin
when isReady $ do
_ <-getChar
clearStdin
hGetLayerColor h layer = do
input <- bracket (hGetBuffering stdin) (hSetBuffering stdin) $ \_ -> do
hSetBuffering stdin NoBuffering
bracket (hGetEcho stdin) (hSetEcho stdin) $ \_ -> do
hSetEcho stdin False
clearStdin
hReportLayerColor h layer
hFlush h
getReportedLayerColor layer
case readP_to_S (layerColor layer) input of
[] -> return Nothing
[(col, _)] -> return $ Just col
(_:_) -> return Nothing
where
clearStdin = do
isReady <- hReady stdin
when isReady $ do
_ <-getChar
clearStdin