ansi-terminal-1.0: win/System/Console/ANSI/Internal.hs
{-# LANGUAGE Safe #-}
module System.Console.ANSI.Internal
( getReportedCursorPosition
, getReportedLayerColor
, hSupportsANSI
) where
import Control.Exception ( IOException, catch )
import Data.Maybe ( mapMaybe )
import System.Environment ( lookupEnv )
import System.IO ( Handle, hIsTerminalDevice, hIsWritable, stdin )
import System.Console.ANSI.Types ( ConsoleLayer )
-- Provided by the ansi-terminal package
import System.Console.ANSI.Windows.Foreign
( INPUT_RECORD (..), INPUT_RECORD_EVENT (..), KEY_EVENT_RECORD (..)
, cWcharsToChars, getNumberOfConsoleInputEvents, readConsoleInput
, unicodeAsciiChar
)
import System.Console.ANSI.Windows.Win32.MinTTY ( isMinTTYHandle )
import System.Console.ANSI.Windows.Win32.Types ( withHandleToHANDLE )
getReportedCursorPosition :: IO String
getReportedCursorPosition = getReported
getReportedLayerColor :: ConsoleLayer -> IO String
getReportedLayerColor _ = getReported
getReported :: IO String
getReported = catch getReported' getReportedExceptionHandler
where
getReported' = withHandleToHANDLE stdin action
where
action hdl = do
n <- getNumberOfConsoleInputEvents hdl
if n == 0
then pure ""
else do
es <- readConsoleInput hdl n
pure $ stringFromInputEvents es
stringFromInputEvents = cWcharsToChars . wCharsFromInputEvents
wCharsFromInputEvents = mapMaybe wCharFromInputEvent
wCharFromInputEvent e = if isKeyEvent && isKeyDown
then Just (unicodeAsciiChar $ keyEventChar keyEventRecord)
else Nothing
where
eventType = inputEventType e
eventRecord = inputEvent e
isKeyEvent = eventType == 1
keyEventRecord = case eventRecord of
InputKeyEvent keyEventRecord' -> keyEventRecord'
_ -> error "Unexpected input event, given input event type."
isKeyDown = keyEventKeyDown keyEventRecord
getReportedExceptionHandler :: IOException -> IO a
getReportedExceptionHandler e = error msg
where
msg = "Error: " ++ show e ++ "\nThis error may be avoided by using a " ++
"console based on the Windows' Console API, such as Command Prompt " ++
"or PowerShell."
hSupportsANSI :: Handle -> IO Bool
hSupportsANSI h = (&&) <$> hIsWritable h <*> hSupportsANSI'
where
hSupportsANSI' = (||) <$> isTDNotDumb <*> isMinTTY
-- Borrowed from an HSpec patch by Simon Hengel
-- (https://github.com/hspec/hspec/commit/d932f03317e0e2bd08c85b23903fb8616ae642bd)
isTDNotDumb = (&&) <$> hIsTerminalDevice h <*> isNotDumb
isNotDumb = (/= Just "dumb") <$> lookupEnv "TERM"
isMinTTY = withHandleToHANDLE h isMinTTYHandle