never executed always true always false
1 {-# LANGUAGE CPP #-}
2 {-# LANGUAGE ScopedTypeVariables #-}
3 module GHC.SysTools.Terminal (stderrSupportsAnsiColors) where
4
5 import GHC.Prelude
6
7 #if defined(MIN_VERSION_terminfo)
8 import GHC.IO (catchException)
9 import Data.Maybe (fromMaybe)
10 import System.Console.Terminfo (SetupTermError, Terminal, getCapability,
11 setupTermFromEnv, termColors)
12 import System.Posix (queryTerminal, stdError)
13 #elif defined(mingw32_HOST_OS)
14 import GHC.IO (catchException)
15 import GHC.Utils.Exception (try)
16 -- import Data.Bits ((.|.), (.&.))
17 import Foreign (Ptr, peek, with)
18 import qualified Graphics.Win32 as Win32
19 import qualified System.Win32 as Win32
20 #endif
21
22 import System.IO.Unsafe
23
24 #if defined(mingw32_HOST_OS) && !defined(WINAPI)
25 # if defined(i386_HOST_ARCH)
26 # define WINAPI stdcall
27 # elif defined(x86_64_HOST_ARCH)
28 # define WINAPI ccall
29 # else
30 # error unknown architecture
31 # endif
32 #endif
33
34 -- | Does the controlling terminal support ANSI color sequences?
35 -- This memoized to avoid thread-safety issues in ncurses (see #17922).
36 stderrSupportsAnsiColors :: Bool
37 stderrSupportsAnsiColors = unsafePerformIO stderrSupportsAnsiColors'
38 {-# NOINLINE stderrSupportsAnsiColors #-}
39
40 -- | Check if ANSI escape sequences can be used to control color in stderr.
41 stderrSupportsAnsiColors' :: IO Bool
42 stderrSupportsAnsiColors' = do
43 #if defined(MIN_VERSION_terminfo)
44 stderr_available <- queryTerminal stdError
45 if stderr_available then
46 fmap termSupportsColors setupTermFromEnv
47 `catchException` \ (_ :: SetupTermError) -> pure False
48 else
49 pure False
50 where
51 termSupportsColors :: Terminal -> Bool
52 termSupportsColors term = fromMaybe 0 (getCapability term termColors) > 0
53
54 #elif defined(mingw32_HOST_OS)
55 h <- Win32.getStdHandle Win32.sTD_ERROR_HANDLE
56 `catchException` \ (_ :: IOError) ->
57 pure Win32.nullHANDLE
58 if h == Win32.nullHANDLE
59 then pure False
60 else do
61 eMode <- try (getConsoleMode h)
62 case eMode of
63 Left (_ :: IOError) -> Win32.isMinTTYHandle h
64 -- Check if the we're in a MinTTY terminal
65 -- (e.g., Cygwin or MSYS2)
66 Right mode
67 | modeHasVTP mode -> pure True
68 | otherwise -> enableVTP h mode
69
70 where
71
72 enableVTP :: Win32.HANDLE -> Win32.DWORD -> IO Bool
73 enableVTP h mode = do
74 setConsoleMode h (modeAddVTP mode)
75 modeHasVTP <$> getConsoleMode h
76 `catchException` \ (_ :: IOError) ->
77 pure False
78
79 modeHasVTP :: Win32.DWORD -> Bool
80 modeHasVTP mode = mode .&. eNABLE_VIRTUAL_TERMINAL_PROCESSING /= 0
81
82 modeAddVTP :: Win32.DWORD -> Win32.DWORD
83 modeAddVTP mode = mode .|. eNABLE_VIRTUAL_TERMINAL_PROCESSING
84
85 eNABLE_VIRTUAL_TERMINAL_PROCESSING :: Win32.DWORD
86 eNABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
87
88 getConsoleMode :: Win32.HANDLE -> IO Win32.DWORD
89 getConsoleMode h = with 64 $ \ mode -> do
90 Win32.failIfFalse_ "GetConsoleMode" (c_GetConsoleMode h mode)
91 peek mode
92
93 setConsoleMode :: Win32.HANDLE -> Win32.DWORD -> IO ()
94 setConsoleMode h mode = do
95 Win32.failIfFalse_ "SetConsoleMode" (c_SetConsoleMode h mode)
96
97 foreign import WINAPI unsafe "windows.h GetConsoleMode" c_GetConsoleMode
98 :: Win32.HANDLE -> Ptr Win32.DWORD -> IO Win32.BOOL
99
100 foreign import WINAPI unsafe "windows.h SetConsoleMode" c_SetConsoleMode
101 :: Win32.HANDLE -> Win32.DWORD -> IO Win32.BOOL
102
103 #else
104 pure False
105 #endif