packages feed

ansi-terminal 0.8.2 → 0.9

raw patch · 10 files changed

+327/−79 lines, 10 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- System.Console.ANSI: getCursorPosition :: IO (Maybe (Int, Int))
+ System.Console.ANSI: getTerminalSize :: IO (Maybe (Int, Int))
+ System.Console.ANSI: hSupportsANSIColor :: Handle -> IO Bool
+ System.Console.ANSI.Types: SetPaletteColor :: !ConsoleLayer -> !Word8 -> SGR
+ System.Console.ANSI.Types: xterm24LevelGray :: Int -> Word8
+ System.Console.ANSI.Types: xterm6LevelRGB :: Int -> Int -> Int -> Word8
+ System.Console.ANSI.Types: xtermSystem :: ColorIntensity -> Color -> Word8
- System.Console.ANSI.Types: SetRGBColor :: !ConsoleLayer -> !(Colour Float) -> SGR
+ System.Console.ANSI.Types: SetRGBColor :: !ConsoleLayer -> !Colour Float -> SGR

Files

CHANGELOG.md view
@@ -1,6 +1,16 @@ Changes
 =======
 
+Version 0.9
+-----------
+
+* Add support for 256-color palettes with new `SetPaletteColor` constructor of
+  `SGR` type, and `xterm6LevelRGB`, `xterm24LevelGray` and `xtermSystem`.
+* Remove deprecated `getCursorPosition`. (Use `getCursorPosition0` instead.)
+* Add `hSupportsANSIColor`.
+* Add `getTerminalSize`.
+* Improvements to Haddock documentation.
+
 Version 0.8.2
 -------------
 
ansi-terminal.cabal view
@@ -1,5 +1,5 @@ Name:                ansi-terminal
-Version:             0.8.2
+Version:             0.9
 Cabal-Version:       >= 1.8
 Category:            User Interfaces
 Synopsis:            Simple ANSI terminal support, with Windows compatibility
@@ -66,6 +66,7 @@         Main-Is:                Example.hs
         Build-Depends:          base >= 4.3.0.0 && < 5
                               , ansi-terminal
+                              , colour
         Ghc-Options:            -Wall
         if !flag(example)
                 Buildable:              False
app/Example.hs view
@@ -4,9 +4,12 @@   ) where
 
 import Control.Concurrent (threadDelay)
-import Control.Monad (forM_)
+import Control.Monad (forM_, replicateM_)
 import System.IO (hFlush, stdout)
+import Text.Printf(printf)
 
+import Data.Colour.SRGB (sRGB24)
+
 import System.Console.ANSI
 
 examples :: [IO ()]
@@ -16,10 +19,12 @@            , saveRestoreCursorExample
            , clearExample
            , scrollExample
-           , sgrExample
+           , sgrColorExample
+           , sgrOtherExample
            , cursorVisibilityExample
            , titleExample
            , getCursorPositionExample
+           , getTerminalSizeExample
            ]
 
 main :: IO ()
@@ -215,8 +220,8 @@   -- Line Two
   -- Line Three
 
-sgrExample :: IO ()
-sgrExample = do
+sgrColorExample :: IO ()
+sgrColorExample = do
   let colors = enumFromTo minBound maxBound :: [Color]
   forM_ [Foreground, Background] $ \layer ->  do
     forM_ [Dull, Vivid] $ \intensity -> do
@@ -226,8 +231,90 @@         setSGR [SetColor layer intensity color]
         putStrLn (show color)
       pause
-  -- All the colors, 4 times in sequence
+  -- The ANSI eight standard colors, 4 times in sequence (two layers and two
+  -- intensities)
 
+  resetScreen
+  putStrLn "True color (24 bit color depth)"
+  putStrLn "-------------------------------"
+  putStrLn ""
+  setSGR [SetRGBColor Foreground $ sRGB24 0 0 0]
+  forM_ [0 .. 23] $ \row -> do
+    forM_ [0 .. 47] $ \col -> do
+      let r = row * 11
+          g = 255 - r
+          b = col * 5
+      setSGR [SetRGBColor Background $ sRGB24 r g b]
+      putStr "-"
+    putStrLn ""
+  replicateM_ 5 pause
+  -- True colors, a swatch of 24 rows and 48 columns
+
+  resetScreen
+  putStrLn "A 256-color palette"
+  putStrLn "-------------------"
+  putStrLn ""
+
+  -- First 16 colors ('system' colors in xterm protocol), in a row
+  --
+  -- 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 A A B B C C D D E E F F
+  forM_ [Dull .. Vivid] $ \intensity -> do
+    forM_ [Black .. White] $ \color -> do
+    let i = fromEnum intensity * 8 + fromEnum color
+        eol = i == 15
+    setSGR [SetPaletteColor Background $ xtermSystem intensity color]
+    setSGR [SetPaletteColor Foreground $ xtermSystem Dull Black]
+    printf "%X " i
+    setSGR [SetPaletteColor Foreground $ xtermSystem Vivid White]
+    printf "%X" i
+    if eol
+      then putStrLn ""
+      else do
+        setSGR [Reset]
+        putStr " "
+  putStrLn ""
+
+  -- Next 216 colors (6 level RGB in xterm protocol), in 12 rows of 18
+  --
+  -- 000 001 002 003 004 005 010 011 012 013 014 015 020 021 022 023 024 025
+  -- 030 031 032 033 034 035 040 041 042 043 044 045 050 051 052 053 054 055
+  -- 100 101 102 103 104 105 110 111 112 113 114 115 120 121 122 123 124 125
+  -- ... and so on ...
+  forM_ [0 .. 5] $ \r -> do
+    forM_ [0 .. 5] $ \g -> do
+      forM_ [0 .. 5] $ \b -> do
+        let i = 16 + b + g * 6 + r * 36
+            eol = i `mod` 18 == 15
+            r' = (r + 3) `mod` 6
+            g' = (g + 3) `mod` 6
+            b' = (b + 3) `mod` 6
+        setSGR [SetPaletteColor Foreground $ xterm6LevelRGB r' g' b']
+        setSGR [SetPaletteColor Background $ xterm6LevelRGB r g b]
+        putStr $ show r ++ show g ++ show b
+        if eol
+          then putStrLn ""
+          else do
+            setSGR [Reset]
+            putStr " "
+  putStrLn ""
+
+  -- Final 24 colors (24 levels of gray in xterm protocol), in two rows
+  --
+  --   0   1   2   3   4   5   6   7   8   9  10  11
+  --  12  13  14  15  16  17  18  19  20  21  22  23
+  forM_ [0 .. 23] $ \y -> do
+    setSGR [SetPaletteColor Foreground $ xterm24LevelGray $ (y + 12) `mod` 24]
+    setSGR [SetPaletteColor Background $ xterm24LevelGray y]
+    printf "%3d" y
+    if y == 11
+      then putStrLn ""
+      else do
+        setSGR [Reset]
+        putStr " "
+  replicateM_ 5 pause
+
+sgrOtherExample :: IO ()
+sgrOtherExample = do
   let named_styles = [ (SetConsoleIntensity BoldIntensity, "Bold")
                      , (SetConsoleIntensity FaintIntensity, "Faint")
                      , (SetConsoleIntensity NormalIntensity, "Normal")
@@ -307,9 +394,19 @@     Just (row, col) -> putStrLn $ "The cursor was at row number " ++
       show (row + 1) ++ " and column number " ++ show (col + 1) ++ ".\n"
     Nothing -> putStrLn "Error: unable to get the cursor position\n"
-  pause
+  replicateM_ 3 pause
   --          11111111112222222222
   -- 12345678901234567890123456789
   -- Report cursor position here: (3rd row, 29th column) to stdin, as CSI 3 ; 29 R.
   --
   -- The cursor was at row number 3 and column number 29.
+
+getTerminalSizeExample :: IO ()
+getTerminalSizeExample = do
+  result <- getTerminalSize
+  case result of
+    Just (h, w) -> putStrLn $ "The size of the terminal is " ++ show h ++
+      " rows by " ++ show w ++ " columns.\n"
+    Nothing -> putStrLn "Error: unable to get the terminal size\n"
+  pause
+  -- The size of the terminal is 25 rows by 80 columns.
src/System/Console/ANSI/Codes.hs view
@@ -75,7 +75,7 @@ csi args code = "\ESC[" ++ concat (intersperse ";" (map show args)) ++ code
 
 -- | 'colorToCode' @color@ returns the 0-based index of the color (one of the
--- eight colors in the standard).
+-- eight colors in the ANSI standard).
 colorToCode :: Color -> Int
 colorToCode color = case color of
   Black   -> 0
@@ -115,6 +115,8 @@   SetColor Foreground Vivid color -> [90 + colorToCode color]
   SetColor Background Dull color  -> [40 + colorToCode color]
   SetColor Background Vivid color -> [100 + colorToCode color]
+  SetPaletteColor Foreground index -> [38, 5, fromIntegral index]
+  SetPaletteColor Background index -> [48, 5, fromIntegral index]
   SetRGBColor Foreground color -> [38, 2] ++ toRGB color
   SetRGBColor Background color -> [48, 2] ++ toRGB color
  where
src/System/Console/ANSI/Types.hs view
@@ -1,6 +1,16 @@--- | Types used to represent SELECT GRAPHIC RENDITION (SGR) aspects.
+-- | The \'ANSI\' standards refer to the visual style of displaying characters
+-- as their \'graphic rendition\'. The style includes the color of a character
+-- or its background, the intensity (bold, normal or faint) of a character, or
+-- whether the character is italic or underlined (single or double), blinking
+-- (slowly or rapidly) or visible or not. The \'ANSI\' codes to establish the
+-- graphic rendition for subsequent text are referred to as SELECT GRAPHIC
+-- RENDITION (SGR).
+--
+-- This module exports types and functions used to represent SGR aspects. See
+-- also 'System.Console.ANSI.setSGR' and related functions.
 module System.Console.ANSI.Types
   (
+  -- * Types used to represent SGR aspects
     SGR (..)
   , ConsoleLayer (..)
   , Color (..)
@@ -8,14 +18,22 @@   , ConsoleIntensity (..)
   , Underlining (..)
   , BlinkSpeed (..)
+  -- * Constructors of xterm 256-color palette indices
+  , xterm6LevelRGB
+  , xterm24LevelGray
+  , xtermSystem
   ) where
 
 import Data.Ix (Ix)
+import Data.Word (Word8)
 
 import Data.Colour (Colour)
 
--- | ANSI colors: come in various intensities, which are controlled by
--- 'ColorIntensity'
+-- | ANSI's eight standard colors. They come in two intensities, which are
+-- controlled by 'ColorIntensity'. Many terminals allow the colors of the
+-- standard palette to be customised, so that, for example,
+-- @setSGR [ SetColor Foreground Vivid Green ]@ may not result in bright green
+-- characters.
 data Color = Black
            | Red
            | Green
@@ -26,7 +44,7 @@            | White
            deriving (Eq, Ord, Bounded, Enum, Show, Read, Ix)
 
--- | ANSI colors come in two intensities
+-- | ANSI's standard colors come in two intensities
 data ColorIntensity = Dull
                     | Vivid
                     deriving (Eq, Ord, Bounded, Enum, Show, Read, Ix)
@@ -43,29 +61,122 @@                 deriving (Eq, Ord, Bounded, Enum, Show, Read, Ix)
 
 -- | ANSI text underlining
-data Underlining = SingleUnderline
-                 | DoubleUnderline -- ^ Not widely supported
-                 | NoUnderline
-                 deriving (Eq, Ord, Bounded ,Enum, Show, Read, Ix)
+data Underlining
+  = SingleUnderline
+  -- | Not widely supported. Not supported natively on Windows 10
+  | DoubleUnderline
+  | NoUnderline
+  deriving (Eq, Ord, Bounded ,Enum, Show, Read, Ix)
 
 -- | ANSI general console intensity: usually treated as setting the font style
 -- (e.g. 'BoldIntensity' causes text to be bold)
-data ConsoleIntensity = BoldIntensity
-                      | FaintIntensity -- ^ Not widely supported: sometimes
-                                       -- treated as concealing text
-                      | NormalIntensity
-                      deriving (Eq, Ord, Bounded, Enum, Show, Read, Ix)
+data ConsoleIntensity
+  = BoldIntensity
+  -- | Not widely supported: sometimes treated as concealing text. Not supported
+  -- natively on Windows 10
+  | FaintIntensity
+  | NormalIntensity
+  deriving (Eq, Ord, Bounded, Enum, Show, Read, Ix)
 
--- | ANSI Select Graphic Rendition command
-data SGR = Reset
-         | SetConsoleIntensity !ConsoleIntensity
-         | SetItalicized !Bool -- ^ Not widely supported: sometimes treated as
-                               -- swapping foreground and background
-         | SetUnderlining !Underlining
-         | SetBlinkSpeed !BlinkSpeed
-         | SetVisible !Bool -- ^ Not widely supported
-         | SetSwapForegroundBackground !Bool
-         | SetColor !ConsoleLayer !ColorIntensity !Color
-         | SetRGBColor !ConsoleLayer !(Colour Float) -- ^ Supported from Windows 10
-                                                     -- Creators Update
-         deriving (Eq, Show, Read)
+-- | ANSI Select Graphic Rendition (SGR) command
+--
+-- In respect of colors, there are three alternative commands:
+--
+-- (1) the \'ANSI\' standards allow for eight standard colors (with two
+-- intensities). Windows and many other terminals (including xterm) allow the
+-- user to redefine the standard colors (so, for example 'Vivid' 'Green' may not
+-- correspond to bright green;
+--
+-- (2) an extension of the standard that allows true colors (24 bit color depth)
+-- in RGB space. This is usually the best alternative for more colors; and
+--
+-- (3) another extension that allows a palette of 256 colors, each color
+-- specified by an index. Xterm provides a protocol for a palette of 256 colors
+-- that many other terminals, including Windows 10, follow. Some terminals
+-- (including xterm) allow the user to redefine some or all of the palette
+-- colors.
+data SGR
+  -- | Default rendition, cancels the effect of any preceding occurrence of SGR
+  -- (implementation-defined)
+  = Reset
+  -- | Set the character intensity. Partially supported natively on Windows 10
+  | SetConsoleIntensity !ConsoleIntensity
+  -- | Set italicized. Not widely supported: sometimes treated as swapping
+  -- foreground and background. Not supported natively on Windows 10
+  | SetItalicized !Bool
+  -- | Set or clear underlining. Partially supported natively on Windows 10
+  | SetUnderlining !Underlining
+  -- | Set or clear character blinking. Not supported natively on Windows 10
+  | SetBlinkSpeed !BlinkSpeed
+  -- | Set revealed or concealed. Not widely supported. Not supported natively
+  -- on Windows 10
+  | SetVisible !Bool
+  -- | Set negative or positive image. Supported natively on Windows 10
+  | SetSwapForegroundBackground !Bool
+  -- | Set a color from the standard palette of 16 colors (8 colors by 2
+  -- color intensities). Many terminals allow the palette colors to be
+  -- customised
+  | SetColor !ConsoleLayer !ColorIntensity !Color
+  -- | Set a true color (24 bit color depth). Supported natively on Windows 10
+  -- from the Creators Update (April 2017)
+  --
+  -- @since 0.7
+  | SetRGBColor !ConsoleLayer !(Colour Float)
+  -- | Set a color from a palette of 256 colors using a numerical index
+  -- (0-based). Supported natively on Windows 10 from the Creators Update (April
+  -- 2017) but not on legacy Windows native terminals. See 'xtermSystem',
+  -- 'xterm6LevelRGB' and 'xterm24LevelGray' to construct indices based on
+  -- xterm's standard protocol for a 256-color palette.
+  --
+  -- @since 0.9
+  | SetPaletteColor !ConsoleLayer !Word8
+  deriving (Eq, Show, Read)
+
+-- | Given xterm's standard protocol for a 256-color palette, returns the index
+-- to that part of the palette which is a 6 level (6x6x6) color cube of 216 RGB
+-- colors. Throws an error if any of the red, green or blue channels is outside
+-- the range 0 to 5. An example of use is:
+--
+-- >>> setSGR [ SetRGBColor $ xterm6LevelRGB 5 2 0 ] -- Dark Orange
+--
+-- @since 0.9
+xterm6LevelRGB :: Int -> Int -> Int -> Word8
+xterm6LevelRGB r g b
+  -- RGB colors are represented by index:
+  -- 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
+  | r >= 0 && r < 6 && g >= 0 && g < 6 && b >= 0 && b < 6
+  =  fromIntegral $ 16 + 36 * r + 6 * g + b
+  | otherwise
+  = error $ show r ++ " " ++ show g ++ " " ++ show b ++ " (r g b) is " ++
+            "outside of a 6 level (6x6x6) color cube."
+
+-- | Given xterm's standard protocol for a 256-color palette, returns the index
+-- to that part of the palette which is a spectrum of 24 grays, from dark
+-- gray (0) to near white (23) (black and white are themselves excluded). Throws
+-- an error if the gray is outside of the range 0 to 23. An example of use is:
+--
+-- >>> setSGR [ SetRGBColor $ xterm24LevelGray 12 ] -- Gray50
+--
+-- @since 0.9
+xterm24LevelGray :: Int -> Word8
+xterm24LevelGray y
+  -- Grayscale colors are represented by index:
+  -- 232 + g (0 ≤ g ≤ 23)
+  | y >= 0 && y < 24 = fromIntegral $ 232 + y
+  | otherwise
+  = error $ show y ++ " (gray) is outside of the range 0 to 23."
+
+-- | Given xterm's standard protocol for a 256-color palette, returns the index
+-- to that part of the palette which corresponds to the \'ANSI\' standards' 16
+-- standard, or \'system\', colors (eight colors in two intensities). An example
+-- of use is:
+--
+-- >>> setSGR [ SetRGBColor $ xtermSystem Vivid Green ]
+--
+-- @since 0.9
+xtermSystem :: ColorIntensity -> Color -> Word8
+xtermSystem intensity color
+  | intensity == Dull  = index
+  | otherwise          = index + 8
+ where
+  index = fromIntegral $ fromEnum color
src/System/Console/ANSI/Unix.hs view
@@ -9,7 +9,6 @@   ) where
 
 import Control.Exception.Base (bracket)
-import System.Environment (getEnvironment)
 import System.IO (BufferMode (..), Handle, hFlush, hGetBuffering, hGetEcho,
   hIsTerminalDevice, hIsWritable, hPutStr, hSetBuffering, hSetEcho, stdin,
   stdout)
@@ -99,20 +98,22 @@                       -- in order to avoid O(n^2) complexity.
       else return $ reverse (c:s) -- Reverse the order of the built list.
 
--- getCursorPosition :: IO (Maybe (Int, Int))
+-- getCursorPosition0 :: IO (Maybe (Int, Int))
 -- (See Common-Include.hs for Haddock documentation)
-{-# DEPRECATED getCursorPosition "Use getCursorPosition0 instead." #-}
-getCursorPosition = do
-  input <- bracket (hGetBuffering stdin) (hSetBuffering stdin) $ \_ -> do
-    hSetBuffering stdin NoBuffering -- set no buffering (the contents of the
-                                    -- buffer will be discarded, so this needs
-                                    -- to be done before the cursor positon is
-                                    -- emitted)
-    reportCursorPosition
-    hFlush stdout -- ensure the report cursor position code is sent to the
-                  -- operating system
-    getReportedCursorPosition
-  case readP_to_S cursorPosition input of
-    [] -> return Nothing
-    [((row, col),_)] -> return $ Just (row, col)
-    (_:_) -> return Nothing
+getCursorPosition0 = fmap to0base <$> getCursorPosition
+ where
+  to0base (row, col) = (row - 1, col - 1)
+  getCursorPosition = do
+    input <- bracket (hGetBuffering stdin) (hSetBuffering stdin) $ \_ -> do
+      hSetBuffering stdin NoBuffering -- set no buffering (the contents of the
+                                      -- buffer will be discarded, so this needs
+                                      -- to be done before the cursor positon is
+                                      -- emitted)
+      reportCursorPosition
+      hFlush stdout -- ensure the report cursor position code is sent to the
+                    -- operating system
+      getReportedCursorPosition
+    case readP_to_S cursorPosition input of
+      [] -> return Nothing
+      [((row, col),_)] -> return $ Just (row, col)
+      (_:_) -> return Nothing
src/System/Console/ANSI/Windows.hs view
@@ -190,7 +190,6 @@ -- (See Common-Include.hs for Haddock documentation)
 getReportedCursorPosition = E.getReportedCursorPosition
 
--- getCursorPosition :: IO (Maybe (Int, Int))
+-- getCursorPosition0 :: IO (Maybe (Int, Int))
 -- (See Common-Include.hs for Haddock documentation)
-{-# DEPRECATED getCursorPosition "Use getCursorPosition0 instead." #-}
-getCursorPosition = E.getCursorPosition
+getCursorPosition0 = E.getCursorPosition0
src/System/Console/ANSI/Windows/Emulator.hs view
@@ -13,7 +13,6 @@ import Data.List (foldl', minimumBy)
 import Data.Maybe (mapMaybe)
 import qualified Data.Map.Strict as Map (Map, empty, insert, lookup)
-import System.Environment (getEnvironment)
 import System.IO (Handle, hFlush, hIsTerminalDevice, stdin, stdout)
 import System.IO.Unsafe (unsafePerformIO)
 import Text.ParserCombinators.ReadP (readP_to_S)
@@ -309,6 +308,7 @@           Dull  -> attribute .&. complement bACKGROUND_INTENSITY
           Vivid -> attribute .|. bACKGROUND_INTENSITY
     in applyBackgroundANSIColorToAttribute aNSIColor attribute'
+  SetPaletteColor _ _ -> attribute  -- Not supported
  where
   iNTENSITY = fOREGROUND_INTENSITY
 
@@ -470,24 +470,27 @@       isKeyDown = keyEventKeyDown keyEventRecord
       isKeyDownEvent = eventType == 1 && isKeyDown
 
--- getCursorPosition :: IO (Maybe (Int, Int))
+-- getCursorPosition0 :: IO (Maybe (Int, Int))
 -- (See Common-Include.hs for Haddock documentation)
-getCursorPosition = CE.catch getCursorPosition' getCPExceptionHandler
+getCursorPosition0 = fmap to0base <$> getCursorPosition
  where
-  getCursorPosition' = do
-    withHandleToHANDLE stdin flush -- Flush the console input buffer
-    reportCursorPosition
-    hFlush stdout -- ensure the report cursor position code is sent to the
-                  -- operating system
-    input <- getReportedCursorPosition
-    case readP_to_S cursorPosition input of
-      [] -> return Nothing
-      [((row, col),_)] -> return $ Just (row, col)
-      (_:_) -> return Nothing
+  to0base (row, col) = (row - 1, col - 1)
+  getCursorPosition = CE.catch getCursorPosition' getCPExceptionHandler
    where
-    flush hdl = do
-      n <- getNumberOfConsoleInputEvents hdl
-      unless (n == 0) (void $ readConsoleInput hdl n)
+    getCursorPosition' = do
+      withHandleToHANDLE stdin flush -- Flush the console input buffer
+      reportCursorPosition
+      hFlush stdout -- ensure the report cursor position code is sent to the
+                    -- operating system
+      input <- getReportedCursorPosition
+      case readP_to_S cursorPosition input of
+        [] -> return Nothing
+        [((row, col),_)] -> return $ Just (row, col)
+        (_:_) -> return Nothing
+     where
+      flush hdl = do
+        n <- getNumberOfConsoleInputEvents hdl
+        unless (n == 0) (void $ readConsoleInput hdl n)
 
 getCPExceptionHandler :: IOException -> IO a
 getCPExceptionHandler e = error msg
src/includes/Common-Include.hs view
@@ -10,6 +10,8 @@ 
 import Control.Monad (void)
 import Data.Char (isDigit)
+import Data.Functor ((<$>))
+import System.Environment (getEnvironment)
 import Text.ParserCombinators.ReadP (char, many1, ReadP, satisfy)
 
 hCursorUp, hCursorDown, hCursorForward, hCursorBackward
@@ -128,8 +130,20 @@ -- identified as connected to a native terminal, this function does /not/ enable
 -- the processing of \'ANSI\' control characters in output (see
 -- 'hSupportsANSIWithoutEmulation').
+--
+-- @since 0.6.2
 hSupportsANSI :: Handle -> IO Bool
 
+-- | Some terminals (e.g. Emacs) are not fully ANSI compliant but can support
+-- ANSI colors. This can be used in such cases, if colors are all that is
+-- needed.
+hSupportsANSIColor :: Handle -> IO Bool
+hSupportsANSIColor h = (||) <$> hSupportsANSI h <*> isEmacsTerm
+  where
+    isEmacsTerm = (\env -> (insideEmacs env) && (isDumb env)) <$> getEnvironment
+    insideEmacs env = any (\(k, _) -> k == "INSIDE_EMACS") env
+    isDumb env = Just "dumb" == lookup "TERM" env
+
 -- | Use heuristics to determine whether a given handle will support \'ANSI\'
 -- control characters in output. (On Windows versions before Windows 10, that
 -- means \'support without emulation\'.)
@@ -221,12 +235,21 @@ --
 -- @since 0.8.2
 getCursorPosition0 :: IO (Maybe (Int, Int))
-getCursorPosition0 = fmap to0base <$> getCursorPosition
- where
-  to0base (row, col) = (row - 1, col - 1)
 
--- | Similar to 'getCursorPosition0', but does not translate the 1-based data
--- emitted by 'reportCursorPosition' to be 0-based.
+-- | Attempts to get the current terminal size (height in rows, width in
+-- columns), by using `getCursorPosition0` after attempting to set the cursor
+-- position beyond the bottom right corner of the terminal.
 --
--- @since 0.7.1
-getCursorPosition :: IO (Maybe (Int, Int))
+-- On Windows operating systems, the function is not supported on consoles, such
+-- as mintty, that are not based on the Win32 console of the Windows API.
+-- (Command Prompt and PowerShell are based on the Win32 console.)
+--
+-- @since 0.9
+getTerminalSize :: IO (Maybe (Int, Int))
+getTerminalSize = do
+  saveCursor
+  setCursorPosition 999 999  -- Attempt to set the cursor position beyond the
+                             -- bottom right corner of the terminal.
+  mPos <- getCursorPosition0
+  restoreCursor
+  return $ fmap (\(r, c) -> (r + 1, c + 1)) mPos
src/includes/Exports-Include.hs view
@@ -107,6 +107,7 @@ 
     -- * Checking if handle supports ANSI (not portable: GHC only)
   , hSupportsANSI
+  , hSupportsANSIColor
   , hSupportsANSIWithoutEmulation
 
     -- * Getting the cursor position
@@ -114,5 +115,5 @@   , getReportedCursorPosition
   , cursorPosition
 
-    -- * Deprecated
-  , getCursorPosition
+    -- * Getting the terminal size
+  , getTerminalSize