summaryrefslogtreecommitdiff
path: root/compiler/main/SysTools/Terminal.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/main/SysTools/Terminal.hs')
-rw-r--r--compiler/main/SysTools/Terminal.hs150
1 files changed, 150 insertions, 0 deletions
diff --git a/compiler/main/SysTools/Terminal.hs b/compiler/main/SysTools/Terminal.hs
new file mode 100644
index 0000000000..b3bf6e651d
--- /dev/null
+++ b/compiler/main/SysTools/Terminal.hs
@@ -0,0 +1,150 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module SysTools.Terminal (stderrSupportsAnsiColors) where
+#if defined MIN_VERSION_terminfo
+import Control.Exception (catch)
+import Data.Maybe (fromMaybe)
+import System.Console.Terminfo (SetupTermError, Terminal, getCapability,
+ setupTermFromEnv, termColors)
+import System.Posix (queryTerminal, stdError)
+#elif defined mingw32_HOST_OS
+import Control.Exception (catch, try)
+import Data.Bits ((.|.), (.&.))
+import Data.List (isInfixOf, isPrefixOf, isSuffixOf)
+import Foreign (FunPtr, Ptr, allocaBytes, castPtrToFunPtr,
+ peek, plusPtr, sizeOf, with)
+import Foreign.C (CInt(..), CWchar, peekCWStringLen)
+import qualified Graphics.Win32 as Win32
+import qualified System.Win32 as Win32
+#endif
+
+#if defined mingw32_HOST_OS && !defined WINAPI
+# if defined i386_HOST_ARCH
+# define WINAPI stdcall
+# elif defined x86_64_HOST_ARCH
+# define WINAPI ccall
+# else
+# error unknown architecture
+# endif
+#endif
+
+-- | Check if ANSI escape sequences can be used to control color in stderr.
+stderrSupportsAnsiColors :: IO Bool
+stderrSupportsAnsiColors = do
+#if defined MIN_VERSION_terminfo
+ queryTerminal stdError `andM` do
+ (termSupportsColors <$> setupTermFromEnv)
+ `catch` \ (_ :: SetupTermError) ->
+ pure False
+
+ where
+
+ andM :: Monad m => m Bool -> m Bool -> m Bool
+ andM mx my = do
+ x <- mx
+ if x
+ then my
+ else pure x
+
+ termSupportsColors :: Terminal -> Bool
+ termSupportsColors term = fromMaybe 0 (getCapability term termColors) > 0
+
+#elif defined mingw32_HOST_OS
+ h <- Win32.getStdHandle Win32.sTD_ERROR_HANDLE
+ `catch` \ (_ :: IOError) ->
+ pure Win32.nullHANDLE
+ if h == Win32.nullHANDLE
+ then pure False
+ else do
+ eMode <- try (getConsoleMode h)
+ case eMode of
+ Left (_ :: IOError) -> queryCygwinTerminal h
+ Right mode
+ | modeHasVTP mode -> pure True
+ | otherwise -> enableVTP h mode
+
+ where
+
+ queryCygwinTerminal :: Win32.HANDLE -> IO Bool
+ queryCygwinTerminal h = do
+ fileType <- Win32.getFileType h
+ if fileType /= Win32.fILE_TYPE_PIPE
+ then pure False
+ else do
+ fn <- getFileNameByHandle h
+ pure (("\\cygwin-" `isPrefixOf` fn || "\\msys-" `isPrefixOf` fn) &&
+ "-pty" `isInfixOf` fn &&
+ "-master" `isSuffixOf` fn)
+ `catch` \ (_ :: IOError) ->
+ pure False
+
+ enableVTP :: Win32.HANDLE -> Win32.DWORD -> IO Bool
+ enableVTP h mode = do
+ setConsoleMode h (modeAddVTP mode)
+ modeHasVTP <$> getConsoleMode h
+ `catch` \ (_ :: IOError) ->
+ pure False
+
+ modeHasVTP :: Win32.DWORD -> Bool
+ modeHasVTP mode = mode .&. eNABLE_VIRTUAL_TERMINAL_PROCESSING /= 0
+
+ modeAddVTP :: Win32.DWORD -> Win32.DWORD
+ modeAddVTP mode = mode .|. eNABLE_VIRTUAL_TERMINAL_PROCESSING
+
+eNABLE_VIRTUAL_TERMINAL_PROCESSING :: Win32.DWORD
+eNABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
+
+getConsoleMode :: Win32.HANDLE -> IO Win32.DWORD
+getConsoleMode h = with 64 $ \ mode -> do
+ Win32.failIfFalse_ "GetConsoleMode" (c_GetConsoleMode h mode)
+ peek mode
+
+setConsoleMode :: Win32.HANDLE -> Win32.DWORD -> IO ()
+setConsoleMode h mode = do
+ Win32.failIfFalse_ "SetConsoleMode" (c_SetConsoleMode h mode)
+
+foreign import WINAPI unsafe "windows.h GetConsoleMode" c_GetConsoleMode
+ :: Win32.HANDLE -> Ptr Win32.DWORD -> IO Win32.BOOL
+
+foreign import WINAPI unsafe "windows.h SetConsoleMode" c_SetConsoleMode
+ :: Win32.HANDLE -> Win32.DWORD -> IO Win32.BOOL
+
+fileNameInfo :: CInt
+fileNameInfo = 2
+
+mAX_PATH :: Num a => a
+mAX_PATH = 260
+
+getFileNameByHandle :: Win32.HANDLE -> IO String
+getFileNameByHandle h = do
+ let sizeOfDWORD = sizeOf (undefined :: Win32.DWORD)
+ let sizeOfWchar = sizeOf (undefined :: CWchar)
+ -- note: implicitly assuming that DWORD has stronger alignment than wchar_t
+ let bufSize = sizeOfDWORD + mAX_PATH * sizeOfWchar
+ allocaBytes bufSize $ \ buf -> do
+ getFileInformationByHandleEx h fileNameInfo buf (fromIntegral bufSize)
+ len :: Win32.DWORD <- peek buf
+ let len' = fromIntegral len `div` sizeOfWchar
+ peekCWStringLen (buf `plusPtr` sizeOfDWORD, min len' mAX_PATH)
+
+getFileInformationByHandleEx
+ :: Win32.HANDLE -> CInt -> Ptr a -> Win32.DWORD -> IO ()
+getFileInformationByHandleEx h cls buf bufSize = do
+ lib <- Win32.getModuleHandle (Just "kernel32.dll")
+ ptr <- Win32.getProcAddress lib "GetFileInformationByHandleEx"
+ let c_GetFileInformationByHandleEx =
+ mk_GetFileInformationByHandleEx (castPtrToFunPtr ptr)
+ Win32.failIfFalse_ "getFileInformationByHandleEx"
+ (c_GetFileInformationByHandleEx h cls buf bufSize)
+
+type F_GetFileInformationByHandleEx a =
+ Win32.HANDLE -> CInt -> Ptr a -> Win32.DWORD -> IO Win32.BOOL
+
+foreign import WINAPI "dynamic"
+ mk_GetFileInformationByHandleEx
+ :: FunPtr (F_GetFileInformationByHandleEx a)
+ -> F_GetFileInformationByHandleEx a
+
+#else
+ pure False
+#endif