packages feed

ansi-terminal 0.5.5.1 → 1.1.5

raw patch · 28 files changed

Files

+ CHANGELOG.md view
@@ -0,0 +1,304 @@+Changes
+=======
+
+Version 1.1.5
+-------------
+
+* On Windows, smothers a C compiler's warnings about `macro-undefined`,
+  `missing-definitions` and `pragma-pack`, which can be triggered by Windows
+  C headers.
+
+Version 1.1.4
+-------------
+
+* Fixes `hGetCursorPosition` and `hGetLayerColor` on Windows, allowing time for
+  reported information to reach the standard input channel.
+* Improvements to Haddock documentation.
+* The executable `ansi-terminal-example` allows individual examples to be
+  selected.
+
+Version 1.1.3
+-------------
+
+* Module `System.Console.ANSI.Codes` spun out to dependency package
+  `ansi-terminal-types-1.1.3`.
+
+Version 1.1.2
+-------------
+
+* Add `enableLineWrap` and `disableLineWrap`, and support for enabling and
+ disabling automatic line wrapping.
+
+Version 1.1.1
+-------------
+
+* Use of C function `setenv()` in one thread can cause other threads calling C
+  functions that query environment variables to crash. On Unix-like operating
+  systems, where such functions are used, the implementations of
+  `hSupportsANSI` and `hSupportsANSIColor` now avoid unnecessary calls of
+  `System.Environment.lookupEnv`.
+
+* Improvements to Haddock documentation.
+
+Version 1.1
+-----------
+
+* Add support for console layer `Underlining` and underlining styles
+  `CurlyUnderline`, `DottedUnderline` and `DashedUnderline`.
+* Add type synonyms `Parameter`, `SubParam`, and `ParamWithSubs` to represent
+  SGR parameter values with and without following parameter substrings comprised
+  of one or more parameter elements (including empty elements).
+* Add `csi'` and `sgrToCode'`, corresponding to `csi` and `sgrToCode` but
+  capable of handling a parameter value followed by a parameter substring.
+
+Version 1.0.2
+-------------
+
+* On Windows, fix linker error about a duplicate symbol `castUINTPtrToPtr`.
+
+Version 1.0.1
+-------------
+
+* On Windows, the processing of \'ANSI\' control characters in output is enabled
+  by default in Windows Terminal but is not enabled by default in ConHost
+  terminals. Additions have been made to allow support of users of ConHost
+  terminals.
+* Add `hNowSupportsANSI`. On Unix, the function is equivalent to
+  `hSupportsANSI`. On Windows, in Windows Terminal and ConHost terminals, the
+  action can try to enable the processing of \'ANSI\' control characters in
+  output.
+* In Windows Terminal and ConHost terminals, `hSupportsANSI` will yield `False`
+  if the processing of \'ANSI\' control characters in output is not enabled.
+* Deprecated `hSupportsANSIWithoutEmulation` is now consistent with
+  `hNowSupportsANSI`.
+* Improvements to Haddock documentation.
+Version 1.0
+-----------
+
+* On Windows, drop support for legacy Windows requiring emulation. The package
+  assumes Windows Terminal has replaced ConHost terminals on supported versions
+  of Windows. Functions that yield actions no longer enable (re-enable) the
+  processing of \'ANSI\' control characters in output.
+* On Windows, the package no longer depends (directly or indirectly) on the
+  `Win32`, `array`,`containers`, `deepseq`, `filepath`, `ghc-boot-th`, `mintty`,
+  `pretty` or `template-haskell` packages.
+* `hSupportsANSI` no longer assumes that the given handle is writeable.
+* `hSupportsANSIWithoutEmulation` is deprecated.
+
+Version 0.11.5
+--------------
+
+* Module `System.Console.ANSI.Types` spun out to new dependency package
+  `ansi-terminal-types-0.11.5`.
+* Drop support for GHC versions before GHC 7.10.1 (released March 2015).
+* Improvements to Haddock documentation.
+
+Version 0.11.4
+--------------
+
+* Add `reportLayerColor`, `getReportedLayerColor` and `getLayerColor` for
+  querying the layer color on terminals that support the functionality.
+* Add `useAlternateScreenBuffer` and `useNormalScreenBuffer`, and support for
+  switching between the Alternate and Normal Screen Buffers.
+* When the argument is `0`, `cursorUpCode`, `cursorDownCode`,
+  `cursorForwardCode`, `cursorBackwardCode`,`scrollPageUpCode` and
+  `scrollPageDownCode` now yield `""`, and `cursorUpLineCode` and
+  `cursorDownLineCode` now yield the equivalent of `setCursorColumnCode 0`. This
+  is because, on some terminals, a `0` parameter for the underlying 'ANSI' code
+  specifies a default parameter of `1`.
+* Add `osc` as a utility function, for OSC sequences.
+* `setTitle` now uses the recommended STRING TERMINATOR (ST) of `\ESC\\`, rather
+  than the legacy `\BEL` (`\007`), and filters the title of all non-printable
+  characters, not just `\BEL`.
+* Improvements to Haddock documentation.
+
+Version 0.11.3
+--------------
+
+* Add `hyperlink`, `hyperlinkWithId` and `hyperlinkWithParams`, and support for
+  clickable hyperlinks.
+
+Version 0.11.2
+--------------
+
+* On Windows, fix compatability with the Windows I/O Manager (WinIO) when
+  GHC >= 9.0.1 but `Win32` < 2.9.0.0.
+* Improvements to Haddock documentation.
+
+Version 0.11.1
+--------------
+
+* On Windows, fix compatability with the Windows I/O Manager (WinIO) introduced
+  in GHC 9.0.1, by incorporating changes made in package `Win32-2.13.2.0` in
+  that regard.
+* Improvements to Haddock documentation.
+
+Version 0.11
+------------
+
+* Remove deprecated `getCursorPosition0`. (Use `getCursorPosition` instead.)
+* On Unix-like operating systems, the temporary turning off of echoing is moved
+  from `getReportedCursorPosition` to `hGetCursorPositon`.
+* On Unix-like operating systems, fix a bug in `getCursorPosition` and
+  `hGetCursorPosition`, where the console input stream was was not always
+  clear before the cursor position was emitted into it.
+
+Version 0.10.3
+--------------
+
+* Add `getCursorPosition` as a synonym of `getCursorPosition0` and deprecate the
+  latter.
+
+Version 0.10.2
+--------------
+
+* `hGetTerminalSize` now assumes a terminal is no bigger than 9,999 by 9,999
+  (previously, no bigger than 999 by 999).
+* On Windows, fix a bug where emulated cursor movement functions differed from
+  Windows 10 (movement bounded by the current viewport).
+
+Version 0.10.1
+--------------
+
+* Add `hGetCursorPosition` and `hGetTerminalSize`.
+* On Unix-like operating systems, fix a bug where `getReportedCursorPosition`
+  could block indefinitely if no information was forthcoming on the console
+  input stream.
+* Improvements to Haddock documentation.
+
+Version 0.10
+------------
+
+* Add support for setting the default color with new `SetDefaultColor`
+  constructor of the `SGR` type.
+* `getTerminalSize` now flushes the `stdout` channel, to ensure the cursor
+  position is unaffected.
+
+Version 0.9.1
+-------------
+
+* Flag modules with GHC's 'Safe Haskell' language extensions (from GHC 7.2.1).
+* Improvements and corrections to Haddock documentation.
+
+Version 0.9
+-----------
+
+* Add support for 256-color palettes with new `SetPaletteColor` constructor of
+  the `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
+-------------
+
+* Add `getCursorPosition0` and deprecate `getCursorPosition`. Any position
+  provided by the latter is 1-based. Any position provided by the former is
+  0-based, consistent with `setCursorColumn` and `setCursorPosition`.
+* Improvements to Haddock documentation in respect of 0-based and 1-based
+  cursor positions.
+
+Version 0.8.1
+-------------
+
+* Add `hSupportsANSIWithoutEmulation`. On Windows 10, if the handle is
+  identified as connected to a native terminal ('Command Prompt' or
+  'PowerShell'), the processing of 'ANSI' control characters will be enabled.
+
+Version 0.8.0.4
+---------------
+
+* On Windows, `hSupportsANSI` now recognises if the handle is connected to a
+  'mintty' terminal.
+* Drop support for GHC versions before GHC 7.0.1 (released November 2010)
+
+Version 0.8.0.3
+---------------
+
+* On Windows, try to enable ANSI on ConHost terminals even if a TERM environment
+  variable exits (such as with the Hyper 2 terminal)
+* Minor improvements to Haddock documentation
+
+Version 0.8.0.2
+---------------
+
+* Improve README and Haddock documentation
+* On Windows, fix compatability with earlier GHC versions
+* Drop support for GHC versions before 6.12.1 (released December 2009)
+
+Version 0.8.0.1
+---------------
+
+* On Windows, if the standard output channel is valid but not a ConHost
+  terminal, assume it is ANSI-enabled rather than failing
+* On Windows, output the improved error message to the standard error channel
+  rather than the standard output channel
+
+Version 0.8
+-----------
+
+* Make the fields of `SGR` strict
+* Make compatible with GHC 8.2.2
+* Improve the error message on Windows when not ANSI-capable or ConHost
+* Recognise Appveyor build environment as ANSI-enabled
+
+Version 0.7.1.1
+---------------
+
+`getReportedCursorPosition`: don't let the cursor reporting code be echo'd
+
+Version 0.7.1
+-------------
+
+* Allow saving, restoring, and querying the current cursor position
+* Fix a couple of issues with the Reset emulation on Windows
+
+Version 0.7
+-----------
+
+Add 24-bit RGB color support
+
+Version 0.6.3.1
+---------------
+
+Fix Windows + ghc 7.8 compatibility
+
+Version 0.6.3
+-------------
+
+* Add ANSI support for Windows
+* Add compatibility with Win32-2.5.0.0 and above
+
+Version 0.6.2.3
+---------------
+
+Add an example to the haddocks
+
+Version 0.6.2.2
+---------------
+
+Fix a GHC 7.10 warning
+
+Version 0.6.2.1
+---------------
+
+Restore compatibility with GHC 7.4 and older
+
+Version 0.6.2
+-------------
+
+* Add `hSupportsANSI`
+* Drop support for `base < 4`
+
+Version 0.6.1.1
+---------------
+
+Fix to build with GHC 7.8 on Windows
+
+Version 0.6.1
+-------------
+
+* `BoldIntensity` no longer changes background color on Windows
+* `setSGR []` was not equivalent to `setSGR [Reset]` on Windows, even though it
+  should be according to the documentation. This is now fixed.
LICENSE view
@@ -1,22 +1,25 @@-Copyright (c) 2008, Maximilian Bolingbroke-All rights reserved.--Redistribution and use in source and binary forms, with or without modification, are permitted-provided that the following conditions are met:--    * Redistributions of source code must retain the above copyright notice, this list of-      conditions and the following disclaimer.-    * Redistributions in binary form must reproduce the above copyright notice, this list of-      conditions and the following disclaimer in the documentation and/or other materials-      provided with the distribution.-    * Neither the name of Maximilian Bolingbroke nor the names of other contributors may be used to-      endorse or promote products derived from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND-FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR-CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER-IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+Copyright (c) 2008, Maximilian Bolingbroke
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimer in the documentation and/or
+  other materials provided with the distribution.
+* Neither the name of Maximilian Bolingbroke nor the names of other contributors
+  may be used to endorse or promote products derived from this software without
+  specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,110 @@+ansi-terminal [![GitHub CI](https://github.com/UnkindPartition/ansi-terminal/workflows/CI/badge.svg)](https://github.com/UnkindPartition/ansi-terminal/actions)+=============++A Haskell package providing support for 'ANSI' control character sequences for+terminals on Unix-like operating systems and Windows++Description+-----------++['ANSI' terminal escape code](http://en.wikipedia.org/wiki/ANSI_escape_sequences)+support for Haskell, which allows:+-   Colored text output, with control over foreground, background and (where+    supported) underlining colors+-   Clearing parts of a line or the screen+-   Hiding or showing the cursor+-   Moving the cursor around+-   Reporting the position of the cursor+-   Enabling and disabling automatic line wrapping+-   Scrolling the screen up or down+-   Switching between the Alternate and Normal Screen Buffers+-   Clickable hyperlinks to URIs+-   Changing the title of the terminal++If you like this, you may be interested in+[ansi-wl-pprint](http://github.com/batterseapower/ansi-wl-pprint), which+provides a pretty-printer that can construct strings containing 'ANSI'+colorisation.++Not all 'ANSI' escape codes are suported by this library but most (if not+all) of the popular ones that are well-supported by terminal software are,+including:+-   Select Graphic Rendition mode (colors and other attributes): `setSGR`+-   Clearing parts of the screen: `clearFromCursorToScreenEnd`,+    `clearFromCursorToScreenBeginning`, `clearScreen`,+    `clearFromCursorToLineEnd`, `clearFromCursorToLineBeginning` and+    `clearLine`+-   Cursor visibility changes: `hideCursor` and `showCursor`+-   Cursor movement by character: `cursorUp`, `cursorDown`, `cursorForward` and+    `cursorBackward`+-   Cursor movement by line: `cursorUpLine` and `cursorDownLine`+-   Directly changing cursor position: `setCursorColumn` and `setCursorPosition`+-   Saving, restoring and reporting cursor position: `saveCursor`,+    `restoreCursor` and `reportCursorPosition`+-   Automatic line wrapping: `enableLineWrap` and `disableLineWrap`+-   Scrolling the screen: `scrollPageUp` and `scrollPageDown`+-   Changing the title: `setTitle`++Each supported escape code or family of codes has a corresponding+function that comes in three variants:++-   A straight `IO` variant that doesn't take a `Handle` and just applies the+    escape code to `stdout` and any terminal attached to it+-   An `IO` variant similar to above, but which takes a `Handle` to which the+    escape code should be applied+-   A `String` variant that returns a literal string that should be+    included to get the effect of the code.++Example+-------++A full example is+[available](https://github.com/UnkindPartition/ansi-terminal/blob/master/ansi-terminal/app/Example.hs),+but for a taste of how the library works try the following code:++``` haskell+import System.Console.ANSI+import System.IO (stdout)++main :: IO ()+main = do+  stdoutSupportsANSI <- hNowSupportsANSI stdout+  if stdoutSupportsANSI+    then do+      setCursorPosition 5 0+      setTitle "ANSI Terminal Short Example"++      setSGR [ SetConsoleIntensity BoldIntensity+             , SetColor Foreground Vivid Red+             ]+      putStr "Hello"++      setSGR [ SetConsoleIntensity NormalIntensity+             , SetColor Foreground Vivid White+             , SetColor Background Dull Blue+             ]+      putStrLn "World!"+    else+      putStrLn "Standard output does not support 'ANSI' escape codes."+```++![](https://raw.githubusercontent.com/feuerbach/ansi-terminal/master/example.png)++Documentation+-------------++Haddock documentation is [available at+Hackage](http://hackage.haskell.org/packages/archive/ansi-terminal/latest/doc/html/System-Console-ANSI.html).++Credits+-------++The library is originally written by [Max Bolingbroke](https://github.com/batterseapower)++Maintainers+-----------++[Mike Pilgrem](https://github.com/mpilgrem) and [Roman Cheplyaka](https://github.com/UnkindPartition) are the primary maintainers.++[Oliver Charles](https://github.com/ocharles) is the backup maintainer. Please+get in touch with him if the primary maintainers cannot be reached.
− README.textile
@@ -1,3 +0,0 @@-h1. ANSI Terminal--For all information on this package, please consult the "homepage":http://batterseapower.github.com/ansi-terminal
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple
+main = defaultMain
− Setup.lhs
@@ -1,4 +0,0 @@-#! /usr/bin/env runhaskell--> import Distribution.Simple-> main = defaultMain
− System/Console/ANSI.hs
@@ -1,48 +0,0 @@--- | Provides ANSI terminal support for Windows and ANSI terminal software running on a Unix-like operating system.------ The ANSI escape codes are described at <http://en.wikipedia.org/wiki/ANSI_escape_code> and provide a rich range of--- functionality for terminal control, which includes:------  * Colored text output, with control over both foreground and background colors------  * Hiding or showing the cursor------  * Moving the cursor around------  * Clearing parts of the screen------ The most frequently used parts of this ANSI command set are exposed with a platform independent interface by--- this module.  Every function exported comes in three flavours:------  * Vanilla: has an @IO ()@ type and doesn't take a @Handle@.  This just outputs the ANSI command directly on---    to the terminal corresponding to stdout.  Commands issued like this should work as you expect on both Windows---    and Unix.------  * Chocolate: has an @IO ()@ type but takes a @Handle@.  This outputs the ANSI command on the terminal corresponding---    to the supplied handle.  Commands issued like this should also work as your expect on both Windows and Unix.------  * Strawberry: has a @String@ type and just consists of an escape code which can be added to any other bit of text---    before being output.  This version of the API is often convenient to use, but due to fundamental limitations in---    Windows ANSI terminal support will only work on Unix.  On Windows these codes will always be the empty string,---    so it is possible to use them portably for e.g. coloring console output on the understanding that you will only---    see colors if you are running on a Unix-like operating system.-#if defined(WINDOWS)-module System.Console.ANSI (-        module System.Console.ANSI.Windows-    ) where--import System.Console.ANSI.Windows--#elif defined(UNIX)--module System.Console.ANSI (-        module System.Console.ANSI.Unix-    ) where--import System.Console.ANSI.Unix--#else--#error Unsupported platform for the ansi-terminal package--#endif
− System/Console/ANSI/Common.hs
@@ -1,48 +0,0 @@-module System.Console.ANSI.Common where----- | ANSI colors: come in various intensities, which are controlled by 'ColorIntensity'-data Color = Black-           | Red-           | Green-           | Yellow-           | Blue-           | Magenta-           | Cyan-           | White-           deriving (Bounded, Enum, Show)---- | ANSI colors come in two intensities-data ColorIntensity = Dull-                    | Vivid-                    deriving (Enum, Show)---- | ANSI colors can be set on two different layers-data ConsoleLayer = Foreground-                  | Background-                  deriving (Enum, Show)---- | ANSI blink speeds: values other than 'NoBlink' are not widely supported-data BlinkSpeed = SlowBlink -- ^ Less than 150 blinks per minute-                | RapidBlink -- ^ More than 150 blinks per minute-                | NoBlink---- | ANSI text underlining-data Underlining = SingleUnderline-                 | DoubleUnderline -- ^ Not widely supported-                 | NoUnderline---- | 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---- | 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
− System/Console/ANSI/Example.hs
@@ -1,267 +0,0 @@-module Main (-        main-    ) where--import System.Console.ANSI--import System.IO--import Control.Concurrent-import Control.Monad---examples :: [IO ()]-examples = [ cursorMovementExample-           , lineChangeExample-           , setCursorPositionExample-           , clearExample-           , scrollExample-           , sgrExample-           , cursorVisibilityExample-           , titleExample-           ]--main :: IO ()-main = mapM_ (\example -> resetScreen >> example) examples--resetScreen :: IO ()-resetScreen = clearScreen >> setSGR [Reset] >> setCursorPosition 0 0--pause :: IO ()-pause = do-    hFlush stdout-    -- 1 second pause-    threadDelay 1000000--cursorMovementExample :: IO ()-cursorMovementExample = do-    putStrLn "Line One"-    putStr "Line Two"-    pause-    -- Line One-    -- Line Two-    -    cursorUp 1-    putStr " - Extras"-    pause-    -- Line One - Extras-    -- Line Two-    -    cursorBackward 2-    putStr "zz"-    pause-    -- Line One - Extrzz-    -- Line Two-    -    cursorForward 2-    putStr "- And More"-    pause-    -- Line One - Extrzz  - And More-    -- Line Two-    -    cursorDown 1-    putStr "Disconnected"-    pause-    -- Line One - Extrzz  - And More-    -- Line Two                     Disconnected--lineChangeExample :: IO ()-lineChangeExample = do-    putStrLn "Line One"-    putStr "Line Two"-    pause-    -- Line One-    -- Line Two-    -    cursorUpLine 1-    putStr "New Line One"-    pause-    -- New Line One-    -- Line Two-    -    cursorDownLine 1-    putStr "New Line Two"-    pause-    -- New Line One-    -- New Line Two--setCursorPositionExample :: IO ()-setCursorPositionExample = do-    putStrLn "Line One"-    putStrLn "Line Two"-    pause-    -- Line One-    -- Line Two-    -    setCursorPosition 0 5-    putStr "Foo"-    pause-    -- Line Foo-    -- Line Two-    -    setCursorPosition 1 5-    putStr "Bar"-    pause-    -- Line Foo-    -- Line Bar-    -    setCursorColumn 1-    putStr "oaf"-    pause-    -- Line Foo-    -- Loaf Bar--clearExample :: IO ()-clearExample = do-    putStrLn "Line One"-    putStrLn "Line Two"-    pause-    -- Line One-    -- Line Two-    -    setCursorPosition 0 4-    clearFromCursorToScreenEnd-    pause-    -- Line-    -    -    resetScreen-    putStrLn "Line One"-    putStrLn "Line Two"-    pause-    -- Line One-    -- Line Two-    -    setCursorPosition 1 4-    clearFromCursorToScreenBeginning-    pause-    ---    --     Two-    -    -    resetScreen-    putStrLn "Line One"-    putStrLn "Line Two"-    pause-    -- Line One-    -- Line Two-    -    setCursorPosition 0 4-    clearFromCursorToLineEnd-    pause-    -- Line-    -- Line Two-    -    setCursorPosition 1 4-    clearFromCursorToLineBeginning-    pause-    -- Line-    --      Two-    -    clearLine-    pause-    -- Line-    -    clearScreen-    pause-    ----scrollExample :: IO ()-scrollExample = do-    putStrLn "Line One"-    putStrLn "Line Two"-    putStrLn "Line Three"-    pause-    -- Line One-    -- Line Two-    -- Line Three-    -    scrollPageDown 2-    pause-    ---    ---    -- Line One-    -- Line Two-    -- Line Three-    -    scrollPageUp 3-    pause-    -- Line Two-    -- Line Three--sgrExample :: IO ()-sgrExample = do-    let colors = enumFromTo minBound maxBound :: [Color]-    forM_ [Foreground, Background] $ \layer ->  do-        forM_ [Dull, Vivid] $ \intensity -> do-            resetScreen-            forM_ colors $ \color -> do-                setSGR [Reset]-                setSGR [SetColor layer intensity color]-                putStrLn (show color)-            pause-    -- All the colors, 4 times in sequence-    -    let named_styles = [ (SetConsoleIntensity BoldIntensity, "Bold")-                       , (SetConsoleIntensity FaintIntensity, "Faint")-                       , (SetConsoleIntensity NormalIntensity, "Normal")-                       , (SetItalicized True, "Italic")-                       , (SetItalicized False, "No Italics")-                       , (SetUnderlining SingleUnderline, "Single Underline")-                       , (SetUnderlining DoubleUnderline, "Double Underline")-                       , (SetUnderlining NoUnderline, "No Underline")-                       , (SetBlinkSpeed SlowBlink, "Slow Blink")-                       , (SetBlinkSpeed RapidBlink, "Rapid Blink")-                       , (SetBlinkSpeed NoBlink, "No Blink")-                       , (SetVisible False, "Conceal")-                       , (SetVisible True, "Reveal")-                       ]-    forM_ named_styles $ \(style, name) -> do-              resetScreen-              setSGR [style]-              putStrLn name-              pause-    -- Text describing a style displayed in that style in sequence-    -    setSGR [SetColor Foreground Vivid Red]-    setSGR [SetColor Background Vivid Blue]-    -    clearScreen >> setCursorPosition 0 0-    setSGR [SetSwapForegroundBackground False]-    putStr "Red-On-Blue"-    pause-    -- Red-On-Blue-    -    clearScreen >> setCursorPosition 0 0-    setSGR [SetSwapForegroundBackground True]-    putStr "Blue-On-Red"-    pause-    -- Blue-On-Red--cursorVisibilityExample :: IO ()-cursorVisibilityExample = do-    putStr "Cursor Demo"-    pause-    -- Cursor Demo|-    -    hideCursor-    pause-    -- Cursor Demo-    -    showCursor-    pause-    -- Cursor Demo|--titleExample :: IO ()-titleExample = do-    putStr "Title Demo"-    pause-    -- ~/foo/ - ansi-terminal-ex - 83x70-    -------------------------------------    -- Title Demo-    -    setTitle "Yup, I'm a new title!"-    pause-    -- Yup, I'm a new title! - ansi-terminal-ex - 83x70-    ----------------------------------------------------    -- Title Demo
− System/Console/ANSI/Unix.hs
@@ -1,125 +0,0 @@-{-# OPTIONS_HADDOCK hide #-}-module System.Console.ANSI.Unix (-#include "Exports-Include.hs"-    ) where--import System.Console.ANSI.Common--import System.IO--import Data.List---#include "Common-Include.hs"----- | The reference I used for the ANSI escape characters in this module was <http://en.wikipedia.org/wiki/ANSI_escape_sequences>.-csi :: [Int] -> String -> String-csi args code = "\ESC[" ++ concat (intersperse ";" (map show args)) ++ code--colorToCode :: Color -> Int-colorToCode color = case color of-    Black   -> 0-    Red     -> 1-    Green   -> 2-    Yellow  -> 3-    Blue    -> 4-    Magenta -> 5-    Cyan    -> 6-    White   -> 7--sgrToCode :: SGR -> Int-sgrToCode sgr = case sgr of-    Reset -> 0-    SetConsoleIntensity intensity -> case intensity of-        BoldIntensity   -> 1-        FaintIntensity  -> 2-        NormalIntensity -> 22-    SetItalicized True  -> 3-    SetItalicized False -> 23-    SetUnderlining underlining -> case underlining of-        SingleUnderline -> 4-        DoubleUnderline -> 21-        NoUnderline     -> 24-    SetBlinkSpeed blink_speed -> case blink_speed of-        SlowBlink   -> 5-        RapidBlink  -> 6-        NoBlink     -> 25-    SetVisible False -> 8-    SetVisible True  -> 28-    SetSwapForegroundBackground True  -> 7-    SetSwapForegroundBackground False -> 27-    SetColor Foreground Dull color  -> 30 + colorToCode color-    SetColor Foreground Vivid color -> 90 + colorToCode color-    SetColor Background Dull color  -> 40 + colorToCode color-    SetColor Background Vivid color -> 100 + colorToCode color---cursorUpCode n = csi [n] "A"-cursorDownCode n = csi [n] "B"-cursorForwardCode n = csi [n] "C"-cursorBackwardCode n = csi [n] "D"--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---cursorDownLineCode n = csi [n] "E"-cursorUpLineCode n = csi [n] "F"--hCursorDownLine h n = hPutStr h $ cursorDownLineCode n-hCursorUpLine h n = hPutStr h $ cursorUpLineCode n---setCursorColumnCode n = csi [n + 1] "G"-setCursorPositionCode n m = csi [n + 1, m + 1] "H"--hSetCursorColumn h n = hPutStr h $ setCursorColumnCode n-hSetCursorPosition h n m = hPutStr h $ setCursorPositionCode n m---clearFromCursorToScreenEndCode = csi [0] "J"-clearFromCursorToScreenBeginningCode = csi [1] "J"-clearScreenCode = csi [2] "J"--hClearFromCursorToScreenEnd h = hPutStr h clearFromCursorToScreenEndCode-hClearFromCursorToScreenBeginning h = hPutStr h clearFromCursorToScreenBeginningCode-hClearScreen h = hPutStr h clearScreenCode---clearFromCursorToLineEndCode = csi [0] "K"-clearFromCursorToLineBeginningCode = csi [1] "K"-clearLineCode = csi [2] "K"--hClearFromCursorToLineEnd h = hPutStr h clearFromCursorToLineEndCode-hClearFromCursorToLineBeginning h = hPutStr h clearFromCursorToLineBeginningCode-hClearLine h = hPutStr h clearLineCode---scrollPageUpCode n = csi [n] "S"-scrollPageDownCode n = csi [n] "T"--hScrollPageUp h n = hPutStr h $ scrollPageUpCode n-hScrollPageDown h n = hPutStr h $ scrollPageDownCode n---setSGRCode sgrs = csi (map sgrToCode sgrs) "m"--hSetSGR h sgrs = hPutStr h $ setSGRCode sgrs---hideCursorCode = csi [] "?25l"-showCursorCode = csi [] "?25h"--hHideCursor h = hPutStr h hideCursorCode-hShowCursor h = hPutStr h showCursorCode----- | Thanks to Brandon S. Allbery and Curt Sampson for pointing me in the right direction on xterm title setting on haskell-cafe.--- The "0" signifies that both the title and "icon" text should be set: i.e. the text for the window in the Start bar (or similar)--- as well as that in the actual window title.  This is chosen for consistent behaviour between Unixes and Windows.-setTitleCode title = "\ESC]0;" ++ filter (/= '\007') title ++ "\007"--hSetTitle h title = hPutStr h $ setTitleCode title
− System/Console/ANSI/Windows.hs
@@ -1,7 +0,0 @@-{-# OPTIONS_HADDOCK hide #-}-module System.Console.ANSI.Windows (-#include "Exports-Include.hs"-    ) where--import System.Console.ANSI.Common-import System.Console.ANSI.Windows.Emulator
− System/Console/ANSI/Windows/Emulator.hs
@@ -1,254 +0,0 @@-module System.Console.ANSI.Windows.Emulator (-#include "Exports-Include.hs"-    ) where--import System.Console.ANSI.Common-import qualified System.Console.ANSI.Unix as Unix-import System.Console.ANSI.Windows.Foreign--import System.IO--import Control.Exception (SomeException, catchJust)-import Control.Monad (guard)--import Data.Bits-import Data.Char (toLower)-import Data.List---#include "Common-Include.hs"---withHandle :: Handle -> (HANDLE -> IO a) -> IO a-withHandle handle action = do-    -- It's VERY IMPORTANT that we flush before issuing any sort of Windows API call to change the console-    -- because on Windows the arrival of API-initiated state changes is not necessarily synchronised with that-    -- of the text they are attempting to modify.-    hFlush handle-    withHandleToHANDLE handle action----- Unfortunately, the emulator is not perfect. In particular, it has a tendency to die with exceptions about--- invalid handles when it is used with certain Windows consoles (e.g. mintty, terminator, or cygwin sshd).------ This happens because in those environments the stdout family of handles are not actually associated with--- a real console.------ My observation is that every time I've seen this in practice, the handle we have instead of the actual console--- handle is there so that the terminal supports ANSI escape codes. So 99% of the time, the correct thing to do is--- just to fall back on the Unix module to output the ANSI codes and hope for the best.-emulatorFallback :: IO a -> IO a -> IO a-emulatorFallback fallback first_try = catchJust (\e -> guard (isHandleIsInvalidException e) >> return ()) first_try (\() -> fallback)-  where-    -- NB: this is a pretty hacked-up way to find out if we have the right sort of exception, but System.Win32.Types.fail* call into-    -- the fail :: String -> IO a function, and so we don't get any nice exception object we can extract information from.-    isHandleIsInvalidException :: SomeException -> Bool-    isHandleIsInvalidException e = "the handle is invalid" `isInfixOf` e_string || "invalid handle" `isInfixOf` e_string-      where e_string = map toLower (show e)---adjustCursorPosition :: HANDLE -> (SHORT -> SHORT -> SHORT) -> (SHORT -> SHORT -> SHORT) -> IO ()-adjustCursorPosition handle change_x change_y = do-    screen_buffer_info <- getConsoleScreenBufferInfo handle-    let window = csbi_window screen_buffer_info-        (COORD x y) = csbi_cursor_position screen_buffer_info-        cursor_pos' = COORD (change_x (rect_left window) x) (change_y (rect_top window) y)-    setConsoleCursorPosition handle cursor_pos'--hCursorUp h n       = emulatorFallback (Unix.hCursorUp h n)       $ withHandle h $ \handle -> adjustCursorPosition handle (\_ x -> x) (\_ y -> y - fromIntegral n)-hCursorDown h n     = emulatorFallback (Unix.hCursorDown h n)     $ withHandle h $ \handle -> adjustCursorPosition handle (\_ x -> x) (\_ y -> y + fromIntegral n)-hCursorForward h n  = emulatorFallback (Unix.hCursorForward h n)  $ withHandle h $ \handle -> adjustCursorPosition handle (\_ x -> x + fromIntegral n) (\_ y -> y)-hCursorBackward h n = emulatorFallback (Unix.hCursorBackward h n) $ withHandle h $ \handle -> adjustCursorPosition handle (\_ x -> x - fromIntegral n) (\_ y -> y)--cursorUpCode _       = ""-cursorDownCode _     = ""-cursorForwardCode _  = ""-cursorBackwardCode _ = ""---adjustLine :: HANDLE -> (SHORT -> SHORT -> SHORT) -> IO ()-adjustLine handle change_y = adjustCursorPosition handle (\window_left _ -> window_left) change_y--hCursorDownLine h n = emulatorFallback (Unix.hCursorDownLine h n) $ withHandle h $ \handle -> adjustLine handle (\_ y -> y + fromIntegral n)-hCursorUpLine h n   = emulatorFallback (Unix.hCursorUpLine h n)   $ withHandle h $ \handle -> adjustLine handle (\_ y -> y - fromIntegral n)--cursorDownLineCode _   = ""-cursorUpLineCode _ = ""---hSetCursorColumn h x = emulatorFallback (Unix.hSetCursorColumn h x) $ withHandle h $ \handle -> adjustCursorPosition handle (\window_left _ -> window_left + fromIntegral x) (\_ y -> y)--setCursorColumnCode _ = ""---hSetCursorPosition h y x = emulatorFallback (Unix.hSetCursorPosition h y x) $ withHandle h $ \handle -> adjustCursorPosition handle (\window_left _ -> window_left + fromIntegral x) (\window_top _ -> window_top + fromIntegral y)--setCursorPositionCode _ _ = ""---clearChar :: WCHAR-clearChar = charToWCHAR ' '--clearAttribute :: WORD-clearAttribute = 0--hClearScreenFraction :: HANDLE -> (SMALL_RECT -> COORD -> (DWORD, COORD)) -> IO ()-hClearScreenFraction handle fraction_finder = do-    screen_buffer_info <- getConsoleScreenBufferInfo handle-    -    let window = csbi_window screen_buffer_info-        cursor_pos = csbi_cursor_position screen_buffer_info-        (fill_length, fill_cursor_pos) = fraction_finder window cursor_pos-    -    fillConsoleOutputCharacter handle clearChar fill_length fill_cursor_pos-    fillConsoleOutputAttribute handle clearAttribute fill_length fill_cursor_pos-    return ()--hClearFromCursorToScreenEnd h = emulatorFallback (Unix.hClearFromCursorToScreenEnd h) $ withHandle h $ \handle -> hClearScreenFraction handle go-  where-    go window cursor_pos = (fromIntegral fill_length, cursor_pos)-      where-        size_x = rect_width window-        size_y = rect_bottom window - coord_y cursor_pos-        line_remainder = size_x - coord_x cursor_pos-        fill_length = size_x * size_y + line_remainder--hClearFromCursorToScreenBeginning h = emulatorFallback (Unix.hClearFromCursorToScreenBeginning h) $ withHandle h $ \handle -> hClearScreenFraction handle go-  where-    go window cursor_pos = (fromIntegral fill_length, rect_top_left window)-      where-        size_x = rect_width window-        size_y = coord_y cursor_pos - rect_top window-        line_remainder = coord_x cursor_pos-        fill_length = size_x * size_y + line_remainder--hClearScreen h = emulatorFallback (Unix.hClearScreen h) $ withHandle h $ \handle -> hClearScreenFraction handle go-  where-    go window _ = (fromIntegral fill_length, rect_top_left window)-      where-        size_x = rect_width window-        size_y = rect_height window-        fill_length = size_x * size_y--hClearFromCursorToLineEnd h = emulatorFallback (Unix.hClearFromCursorToLineEnd h) $ withHandle h $ \handle -> hClearScreenFraction handle go-  where-    go window cursor_pos = (fromIntegral (rect_right window - coord_x cursor_pos), cursor_pos)--hClearFromCursorToLineBeginning h = emulatorFallback (Unix.hClearFromCursorToLineBeginning h) $ withHandle h $ \handle -> hClearScreenFraction handle go-  where-    go window cursor_pos = (fromIntegral (coord_x cursor_pos), cursor_pos { coord_x = rect_left window })--hClearLine h = emulatorFallback (Unix.hClearLine h) $ withHandle h $ \handle -> hClearScreenFraction handle go-  where-    go window cursor_pos = (fromIntegral (rect_width window), cursor_pos { coord_x = rect_left window })--clearFromCursorToScreenEndCode       = ""-clearFromCursorToScreenBeginningCode = ""-clearScreenCode                      = ""-clearFromCursorToLineEndCode         = ""-clearFromCursorToLineBeginningCode   = ""-clearLineCode                        = ""---hScrollPage :: HANDLE -> Int -> IO ()-hScrollPage handle new_origin_y = do-    screen_buffer_info <- getConsoleScreenBufferInfo handle-    let fill = CHAR_INFO clearChar clearAttribute-        window = csbi_window screen_buffer_info-        origin = COORD (rect_left window) (rect_top window + fromIntegral new_origin_y)-    scrollConsoleScreenBuffer handle window Nothing origin fill--hScrollPageUp   h n = emulatorFallback (Unix.hScrollPageUp   h n) $ withHandle h $ \handle -> hScrollPage handle (negate n)-hScrollPageDown h n = emulatorFallback (Unix.hScrollPageDown h n) $ withHandle h $ \handle -> hScrollPage handle n--scrollPageUpCode _   = ""-scrollPageDownCode _ = ""---{-# INLINE applyANSIColorToAttribute #-}-applyANSIColorToAttribute :: WORD -> WORD -> WORD -> Color -> WORD -> WORD-applyANSIColorToAttribute rED gREEN bLUE color attribute = case color of-    Black   -> attribute'-    Red     -> attribute' .|. rED-    Green   -> attribute' .|. gREEN-    Yellow  -> attribute' .|. rED .|. gREEN-    Blue    -> attribute' .|. bLUE-    Magenta -> attribute' .|. rED .|. bLUE-    Cyan    -> attribute' .|. gREEN .|. bLUE-    White   -> attribute' .|. wHITE-  where-    wHITE = rED .|. gREEN .|. bLUE-    attribute' = attribute .&. (complement wHITE)--applyForegroundANSIColorToAttribute, applyBackgroundANSIColorToAttribute :: Color -> WORD -> WORD-applyForegroundANSIColorToAttribute = applyANSIColorToAttribute fOREGROUND_RED fOREGROUND_GREEN fOREGROUND_BLUE-applyBackgroundANSIColorToAttribute = applyANSIColorToAttribute bACKGROUND_RED bACKGROUND_GREEN bACKGROUND_BLUE--swapForegroundBackgroundColors :: WORD -> WORD-swapForegroundBackgroundColors attribute = clean_attribute .|. foreground_attribute' .|. background_attribute'-  where-    foreground_attribute = attribute .&. fOREGROUND_INTENSE_WHITE-    background_attribute = attribute .&. bACKGROUND_INTENSE_WHITE-    clean_attribute = attribute .&. complement (fOREGROUND_INTENSE_WHITE .|. bACKGROUND_INTENSE_WHITE)-    foreground_attribute' = background_attribute `shiftR` 4-    background_attribute' = foreground_attribute `shiftL` 4--applyANSISGRToAttribute :: SGR -> WORD -> WORD-applyANSISGRToAttribute sgr attribute = case sgr of-    Reset -> fOREGROUND_WHITE-    SetConsoleIntensity intensity -> case intensity of-        BoldIntensity   -> attribute .|. iNTENSITY-        FaintIntensity  -> attribute .&. (complement iNTENSITY) -- Not supported-        NormalIntensity -> attribute .&. (complement iNTENSITY)-    SetItalicized _ -> attribute -- Not supported-    SetUnderlining underlining -> case underlining of-        NoUnderline -> attribute .&. (complement cOMMON_LVB_UNDERSCORE)-        _           -> attribute .|. cOMMON_LVB_UNDERSCORE -- Not supported, since cOMMON_LVB_UNDERSCORE seems to have no effect-    SetBlinkSpeed _ -> attribute -- Not supported-    SetVisible _    -> attribute -- Not supported-    -- The cOMMON_LVB_REVERSE_VIDEO doesn't actually appear to have any affect on the colors being displayed, so the emulator-    -- just uses it to carry information and implements the color-swapping behaviour itself. Bit of a hack, I guess :-)-    SetSwapForegroundBackground True ->-        -- Check if the color-swapping flag is already set-        if attribute .&. cOMMON_LVB_REVERSE_VIDEO /= 0-         then attribute-         else swapForegroundBackgroundColors attribute .|. cOMMON_LVB_REVERSE_VIDEO-    SetSwapForegroundBackground False ->-        -- Check if the color-swapping flag is already not set-        if attribute .&. cOMMON_LVB_REVERSE_VIDEO == 0-         then attribute-         else swapForegroundBackgroundColors attribute .&. (complement cOMMON_LVB_REVERSE_VIDEO)-    SetColor Foreground Dull color  -> applyForegroundANSIColorToAttribute color (attribute .&. (complement fOREGROUND_INTENSITY))-    SetColor Foreground Vivid color -> applyForegroundANSIColorToAttribute color (attribute .|. fOREGROUND_INTENSITY)-    SetColor Background Dull color  -> applyBackgroundANSIColorToAttribute color (attribute .&. (complement bACKGROUND_INTENSITY))-    SetColor Background Vivid color -> applyBackgroundANSIColorToAttribute color (attribute .|. bACKGROUND_INTENSITY)-  where-    iNTENSITY = fOREGROUND_INTENSITY .|. bACKGROUND_INTENSITY--hSetSGR h sgr = emulatorFallback (Unix.hSetSGR h sgr) $ withHandle h $ \handle -> do-    screen_buffer_info <- getConsoleScreenBufferInfo handle-    let attribute = csbi_attributes screen_buffer_info-        attribute' = foldl' (flip applyANSISGRToAttribute) attribute sgr-    setConsoleTextAttribute handle attribute'--setSGRCode _ = ""---hChangeCursorVisibility :: HANDLE -> Bool -> IO ()-hChangeCursorVisibility handle cursor_visible = do-    cursor_info <- getConsoleCursorInfo handle-    setConsoleCursorInfo handle (cursor_info { cci_cursor_visible = cursor_visible })--hHideCursor h = emulatorFallback (Unix.hHideCursor h) $ withHandle h $ \handle -> hChangeCursorVisibility handle False-hShowCursor h = emulatorFallback (Unix.hShowCursor h) $ withHandle h $ \handle -> hChangeCursorVisibility handle True--hideCursorCode = ""-showCursorCode = ""----- Windows only supports setting the terminal title on a process-wide basis, so for now we will--- assume that that is what the user intended. This will fail if they are sending the command--- over e.g. a network link... but that's not really what I'm designing for.-hSetTitle h title = emulatorFallback (Unix.hSetTitle h title) $ withTString title $ setConsoleTitle--setTitleCode _ = ""
− System/Console/ANSI/Windows/Foreign.hs
@@ -1,323 +0,0 @@--- | "System.Win32.Console" is really very impoverished, so I have had to do all the FFI myself.-module System.Console.ANSI.Windows.Foreign (-        -- Re-exports from Win32.Types-        BOOL, WORD, DWORD, WCHAR, HANDLE, SHORT,-        -        charToWCHAR,-        -        COORD(..), SMALL_RECT(..), rect_top, rect_bottom, rect_left, rect_right, rect_width, rect_height,-        CONSOLE_CURSOR_INFO(..), CONSOLE_SCREEN_BUFFER_INFO(..), CHAR_INFO(..),-        -        sTD_INPUT_HANDLE, sTD_OUTPUT_HANDLE, sTD_ERROR_HANDLE,-        -        fOREGROUND_BLUE, fOREGROUND_GREEN, fOREGROUND_RED, fOREGROUND_INTENSITY, fOREGROUND_WHITE, fOREGROUND_INTENSE_WHITE,-        bACKGROUND_BLUE, bACKGROUND_GREEN, bACKGROUND_RED, bACKGROUND_INTENSITY, bACKGROUND_WHITE, bACKGROUND_INTENSE_WHITE,-        cOMMON_LVB_REVERSE_VIDEO, cOMMON_LVB_UNDERSCORE,-        -        getStdHandle,-        getConsoleScreenBufferInfo,-        getConsoleCursorInfo,-        -        setConsoleTextAttribute,-        setConsoleCursorPosition,-        setConsoleCursorInfo,-        setConsoleTitle,-        -        fillConsoleOutputAttribute,-        fillConsoleOutputCharacter,-        scrollConsoleScreenBuffer,-        -        withTString, withHandleToHANDLE-    ) where--import Foreign.C.Types-import Foreign.Marshal-import Foreign.Ptr-import Foreign.Storable--import Data.Bits-import Data.Char--import System.Win32.Types--import Control.Concurrent.MVar-import Control.Exception (bracket)--import Foreign.StablePtr--import GHC.IOBase (Handle(..), Handle__(..))-import qualified GHC.IOBase as IOBase (FD) -- Just an Int32--#if __GLASGOW_HASKELL__ >= 612-import GHC.IO.FD (FD(..)) -- A wrapper around an Int32-import Data.Typeable-#endif----- Some Windows types missing from System.Win32-type SHORT = CShort-type WCHAR = CWchar--charToWCHAR :: Char -> WCHAR-charToWCHAR char = fromIntegral (ord char)----- This is a FFI hack. Some of the API calls take a Coord, but that isn't a built-in FFI type so I can't--- use it directly. Instead, I use UNPACKED_COORD and marshal COORDs into this manually. Note that we CAN'T--- just use two SHORTs directly because they get expanded to 4 bytes each instead of just boing 2 lots of 2--- bytes by the stdcall convention, so linking fails.-type UNPACKED_COORD = CInt---- Field packing order determined experimentally: I couldn't immediately find a specification for Windows--- struct layout anywhere.-unpackCOORD :: COORD -> UNPACKED_COORD-unpackCOORD (COORD x y) = (fromIntegral y) `shiftL` (sizeOf x * 8) .|. (fromIntegral x)---peekAndOffset :: Storable a => Ptr a -> IO (a, Ptr b)-peekAndOffset ptr = do-    item <- peek ptr-    return (item, ptr `plusPtr` sizeOf item)--pokeAndOffset :: Storable a => Ptr a -> a -> IO (Ptr b)-pokeAndOffset ptr item = do-    poke ptr item-    return (ptr `plusPtr` sizeOf item)---data COORD = COORD {-        coord_x :: SHORT,-        coord_y :: SHORT-    }--instance Show COORD where-    show (COORD x y) = "(" ++ show x ++ ", " ++ show y ++ ")"--instance Storable COORD where-    sizeOf ~(COORD x y) = sizeOf x + sizeOf y-    alignment ~(COORD x _) = alignment x-    peek ptr = do-        let ptr' = castPtr ptr :: Ptr SHORT-        x <- peekElemOff ptr' 0-        y <- peekElemOff ptr' 1-        return (COORD x y)-    poke ptr (COORD x y) = do-        let ptr' = castPtr ptr :: Ptr SHORT-        pokeElemOff ptr' 0 x-        pokeElemOff ptr' 1 y---data SMALL_RECT = SMALL_RECT {-        rect_top_left :: COORD,-        rect_bottom_right :: COORD-    }--rect_top, rect_left, rect_bottom, rect_right :: SMALL_RECT -> SHORT-rect_top = coord_y . rect_top_left-rect_left = coord_x . rect_top_left-rect_bottom = coord_y . rect_bottom_right-rect_right = coord_x . rect_bottom_right--rect_width, rect_height :: SMALL_RECT -> SHORT-rect_width rect = rect_right rect - rect_left rect + 1-rect_height rect = rect_bottom rect - rect_top rect + 1--instance Show SMALL_RECT where-    show (SMALL_RECT tl br) = show tl ++ "-" ++ show br--instance Storable SMALL_RECT where-    sizeOf ~(SMALL_RECT tl br) = sizeOf tl + sizeOf br-    alignment ~(SMALL_RECT tl _) = alignment tl-    peek ptr = do-        let ptr' = castPtr ptr :: Ptr COORD-        tl <- peekElemOff ptr' 0-        br <- peekElemOff ptr' 1-        return (SMALL_RECT tl br)-    poke ptr (SMALL_RECT tl br) = do-        let ptr' = castPtr ptr :: Ptr COORD-        pokeElemOff ptr' 0 tl-        pokeElemOff ptr' 1 br---data CONSOLE_CURSOR_INFO = CONSOLE_CURSOR_INFO {-        cci_cursor_size :: DWORD,-        cci_cursor_visible :: BOOL-    }-    deriving (Show)--instance Storable CONSOLE_CURSOR_INFO where-    sizeOf ~(CONSOLE_CURSOR_INFO size visible) = sizeOf size + sizeOf visible-    alignment ~(CONSOLE_CURSOR_INFO size _) = alignment size-    peek ptr = do-        (size, ptr') <- peekAndOffset (castPtr ptr)-        visible <- peek ptr'-        return (CONSOLE_CURSOR_INFO size visible)-    poke ptr (CONSOLE_CURSOR_INFO size visible) = do-        ptr' <- pokeAndOffset (castPtr ptr) size-        poke ptr' visible---data CONSOLE_SCREEN_BUFFER_INFO = CONSOLE_SCREEN_BUFFER_INFO {-        csbi_size :: COORD,-        csbi_cursor_position :: COORD,-        csbi_attributes :: WORD,-        csbi_window :: SMALL_RECT,-        csbi_maximum_window_size :: COORD-    }-    deriving (Show)--instance Storable CONSOLE_SCREEN_BUFFER_INFO where-    sizeOf ~(CONSOLE_SCREEN_BUFFER_INFO size cursor_position attributes window maximum_window_size)-      = sizeOf size + sizeOf cursor_position + sizeOf attributes + sizeOf window + sizeOf maximum_window_size-    alignment ~(CONSOLE_SCREEN_BUFFER_INFO size _ _ _ _) = alignment size-    peek ptr = do-        (size, ptr1) <- peekAndOffset (castPtr ptr)-        (cursor_position, ptr2) <- peekAndOffset ptr1-        (attributes, ptr3) <- peekAndOffset ptr2-        (window, ptr4) <- peekAndOffset ptr3-        maximum_window_size <- peek ptr4-        return (CONSOLE_SCREEN_BUFFER_INFO size cursor_position attributes window maximum_window_size)-    poke ptr (CONSOLE_SCREEN_BUFFER_INFO size cursor_position attributes window maximum_window_size) = do-        ptr1 <- pokeAndOffset (castPtr ptr) size-        ptr2 <- pokeAndOffset ptr1 cursor_position-        ptr3 <- pokeAndOffset ptr2 attributes-        ptr4 <- pokeAndOffset ptr3 window-        poke ptr4 maximum_window_size---data CHAR_INFO = CHAR_INFO {-        ci_char :: WCHAR,-        ci_attributes :: WORD-    }-    deriving (Show)--instance Storable CHAR_INFO where-    sizeOf ~(CHAR_INFO char attributes) = sizeOf char + sizeOf attributes-    alignment ~(CHAR_INFO char _) = alignment char-    peek ptr = do-        (char, ptr') <- peekAndOffset (castPtr ptr)-        attributes <- peek ptr'-        return (CHAR_INFO char attributes)-    poke ptr (CHAR_INFO char attributes) = do-        ptr' <- pokeAndOffset (castPtr ptr) char-        poke ptr' attributes---sTD_INPUT_HANDLE, sTD_OUTPUT_HANDLE, sTD_ERROR_HANDLE :: DWORD-sTD_INPUT_HANDLE = -10-sTD_OUTPUT_HANDLE = -11-sTD_ERROR_HANDLE = -12--fOREGROUND_BLUE, fOREGROUND_GREEN, fOREGROUND_RED, fOREGROUND_INTENSITY,-  bACKGROUND_BLUE, bACKGROUND_GREEN, bACKGROUND_RED, bACKGROUND_INTENSITY,-  cOMMON_LVB_REVERSE_VIDEO, cOMMON_LVB_UNDERSCORE :: WORD-fOREGROUND_BLUE = 0x1-fOREGROUND_GREEN = 0x2-fOREGROUND_RED = 0x4-fOREGROUND_INTENSITY = 0x8-bACKGROUND_BLUE = 0x10-bACKGROUND_GREEN = 0x20-bACKGROUND_RED= 0x40-bACKGROUND_INTENSITY = 0x80-cOMMON_LVB_REVERSE_VIDEO = 0x4000-cOMMON_LVB_UNDERSCORE = 0x8000--fOREGROUND_WHITE, bACKGROUND_WHITE, fOREGROUND_INTENSE_WHITE, bACKGROUND_INTENSE_WHITE :: WORD-fOREGROUND_WHITE = fOREGROUND_RED .|. fOREGROUND_GREEN .|. fOREGROUND_BLUE-bACKGROUND_WHITE = bACKGROUND_RED .|. bACKGROUND_GREEN .|. bACKGROUND_BLUE-fOREGROUND_INTENSE_WHITE = fOREGROUND_WHITE .|. fOREGROUND_INTENSITY-bACKGROUND_INTENSE_WHITE = bACKGROUND_WHITE .|. bACKGROUND_INTENSITY---foreign import stdcall unsafe "windows.h GetStdHandle" getStdHandle :: DWORD -> IO HANDLE-foreign import stdcall unsafe "windows.h GetConsoleScreenBufferInfo" cGetConsoleScreenBufferInfo :: HANDLE -> Ptr CONSOLE_SCREEN_BUFFER_INFO -> IO BOOL-foreign import stdcall unsafe "windows.h GetConsoleCursorInfo" cGetConsoleCursorInfo :: HANDLE -> Ptr CONSOLE_CURSOR_INFO -> IO BOOL--foreign import stdcall unsafe "windows.h SetConsoleTextAttribute" cSetConsoleTextAttribute :: HANDLE -> WORD -> IO BOOL-foreign import stdcall unsafe "windows.h SetConsoleCursorPosition" cSetConsoleCursorPosition :: HANDLE -> UNPACKED_COORD -> IO BOOL-foreign import stdcall unsafe "windows.h SetConsoleCursorInfo" cSetConsoleCursorInfo :: HANDLE -> Ptr CONSOLE_CURSOR_INFO -> IO BOOL-foreign import stdcall unsafe "windows.h SetConsoleTitleW" cSetConsoleTitle :: LPCTSTR -> IO BOOL--foreign import stdcall unsafe "windows.h FillConsoleOutputAttribute" cFillConsoleOutputAttribute :: HANDLE -> WORD -> DWORD -> UNPACKED_COORD -> Ptr DWORD -> IO BOOL-foreign import stdcall unsafe "windows.h FillConsoleOutputCharacterW" cFillConsoleOutputCharacter :: HANDLE -> TCHAR -> DWORD -> UNPACKED_COORD -> Ptr DWORD -> IO BOOL-foreign import stdcall unsafe "windows.h ScrollConsoleScreenBufferW" cScrollConsoleScreenBuffer :: HANDLE -> Ptr SMALL_RECT -> Ptr SMALL_RECT -> UNPACKED_COORD -> Ptr CHAR_INFO -> IO BOOL---getConsoleScreenBufferInfo :: HANDLE -> IO CONSOLE_SCREEN_BUFFER_INFO-getConsoleScreenBufferInfo handle = alloca $ \ptr_console_screen_buffer_info -> do-    failIfFalse_ "getConsoleScreenBufferInfo" $ cGetConsoleScreenBufferInfo handle ptr_console_screen_buffer_info-    peek ptr_console_screen_buffer_info--getConsoleCursorInfo :: HANDLE -> IO CONSOLE_CURSOR_INFO-getConsoleCursorInfo handle = alloca $ \ptr_console_cursor_info -> do-    failIfFalse_ "getConsoleCursorInfo" $ cGetConsoleCursorInfo handle ptr_console_cursor_info-    peek ptr_console_cursor_info---setConsoleTextAttribute :: HANDLE -> WORD -> IO ()-setConsoleTextAttribute handle attributes = failIfFalse_ "setConsoleTextAttribute" $ cSetConsoleTextAttribute handle attributes--setConsoleCursorPosition :: HANDLE -> COORD -> IO ()-setConsoleCursorPosition handle cursor_position = failIfFalse_ "setConsoleCursorPosition" $ cSetConsoleCursorPosition handle (unpackCOORD cursor_position)--setConsoleCursorInfo :: HANDLE -> CONSOLE_CURSOR_INFO -> IO ()-setConsoleCursorInfo handle console_cursor_info = with console_cursor_info $ \ptr_console_cursor_info -> do-    failIfFalse_ "setConsoleCursorInfo" $ cSetConsoleCursorInfo handle ptr_console_cursor_info--setConsoleTitle :: LPCTSTR -> IO ()-setConsoleTitle title = failIfFalse_ "setConsoleTitle" $ cSetConsoleTitle title---fillConsoleOutputAttribute :: HANDLE -> WORD -> DWORD -> COORD -> IO DWORD-fillConsoleOutputAttribute handle attribute fill_length write_origin = alloca $ \ptr_chars_written -> do-    failIfFalse_ "fillConsoleOutputAttribute" $ cFillConsoleOutputAttribute handle attribute fill_length (unpackCOORD write_origin) ptr_chars_written-    peek ptr_chars_written--fillConsoleOutputCharacter :: HANDLE -> TCHAR -> DWORD -> COORD -> IO DWORD-fillConsoleOutputCharacter handle char fill_length write_origin = alloca $ \ptr_chars_written -> do-    failIfFalse_ "fillConsoleOutputCharacter" $ cFillConsoleOutputCharacter handle char fill_length (unpackCOORD write_origin) ptr_chars_written-    peek ptr_chars_written--scrollConsoleScreenBuffer :: HANDLE -> SMALL_RECT -> Maybe SMALL_RECT -> COORD -> CHAR_INFO -> IO ()-scrollConsoleScreenBuffer handle scroll_rectangle mb_clip_rectangle destination_origin fill -  = with scroll_rectangle $ \ptr_scroll_rectangle ->-    maybeWith with mb_clip_rectangle $ \ptr_clip_rectangle ->-    with fill $ \ptr_fill ->-    failIfFalse_ "scrollConsoleScreenBuffer" $ cScrollConsoleScreenBuffer handle ptr_scroll_rectangle ptr_clip_rectangle (unpackCOORD destination_origin) ptr_fill----- This essential function comes from the C runtime system. It is certainly provided by msvcrt, and also seems to be provided by the mingw C library - hurrah!-foreign import ccall unsafe "_get_osfhandle" cget_osfhandle :: IOBase.FD -> IO HANDLE---- | This bit is all highly dubious.  The problem is that we want to output ANSI to arbitrary Handles rather than forcing--- people to use stdout.  However, the Windows ANSI emulator needs a Windows HANDLE to work it's magic, so we need to be able--- to extract one of those from the Haskell Handle.------ This code accomplishes this, albeit at the cost of only being compatible with GHC.-withHandleToHANDLE :: Handle -> (HANDLE -> IO a) -> IO a-withHandleToHANDLE haskell_handle action = -    -- Create a stable pointer to the Handle. This prevents the garbage collector-    -- getting to it while we are doing horrible manipulations with it, and hence-    -- stops it being finalized (and closed).-    withStablePtr haskell_handle $ const $ do-        -- Grab the write handle variable from the Handle-        let write_handle_mvar = case haskell_handle of-                FileHandle _ handle_mvar     -> handle_mvar-                DuplexHandle _ _ handle_mvar -> handle_mvar -- This is "write" MVar, we could also take the "read" one-        -        -- Get the FD from the algebraic data type-#if __GLASGOW_HASKELL__ < 612-        fd <- fmap haFD $ readMVar write_handle_mvar-#else-        --readMVar write_handle_mvar >>= \(Handle__ { haDevice = dev }) -> print (typeOf dev)-        Just fd <- fmap (\(Handle__ { haDevice = dev }) -> fmap fdFD (cast dev)) $ readMVar write_handle_mvar-#endif--        -- Finally, turn that (C-land) FD into a HANDLE using msvcrt-        windows_handle <- cget_osfhandle fd-        -        -- Do what the user originally wanted-        action windows_handle--withStablePtr :: a -> (StablePtr a -> IO b) -> IO b-withStablePtr value = bracket (newStablePtr value) freeStablePtr
ansi-terminal.cabal view
@@ -1,88 +1,79 @@-Name:                ansi-terminal-Version:             0.5.5.1-Cabal-Version:       >= 1.2-Category:            User Interfaces-Synopsis:            Simple ANSI terminal support, with Windows compatibility-Description:         ANSI terminal support for Haskell: allows cursor movement, screen clearing, color output showing or hiding the cursor, and-                     changing the title. Compatible with Windows and those Unixes with ANSI terminals, but only GHC is supported as a compiler.-License:             BSD3-License-File:        LICENSE-Extra-Source-Files:  README.textile-Author:              Max Bolingbroke-Maintainer:          batterseapower@hotmail.com-Homepage:            http://batterseapower.github.com/ansi-terminal-Build-Type:          Simple--Extra-Source-Files:     includes/Common-Include.hs-                        includes/Exports-Include.hs--Flag SplitBase-        Description:    Choose the new smaller, split-up base package-        Default:        True--Flag Example-        Description:    Build the example application-        Default:        False--Library-        Exposed-Modules:        System.Console.ANSI-        -        Other-Modules:          System.Console.ANSI.Common-        -        Include-Dirs:           includes-        -        if os(windows)-                Build-Depends:          Win32 >= 2.0-                Cpp-Options:            -DWINDOWS-                Extra-Libraries:        "kernel32"-                Other-Modules:          System.Console.ANSI.Windows-                                        System.Console.ANSI.Windows.Foreign-                                        System.Console.ANSI.Windows.Emulator-                                        -- NB: used for fallback by the emulator-                                        System.Console.ANSI.Unix-        else-                -- We assume any non-Windows platform is Unix-                Build-Depends:          unix >= 2.3.0.0-                Cpp-Options:            -DUNIX-                Other-Modules:          System.Console.ANSI.Unix-        -        if flag(splitBase)-                Build-Depends:          base >= 3 && < 5-        else-                Build-Depends:          base < 3-        -        Extensions:             CPP-                                ForeignFunctionInterface-        -        Ghc-Options:                    -Wall--Executable ansi-terminal-example-        Main-Is:                System/Console/ANSI/Example.hs-        -        Cpp-Options:            -Iincludes-        -        if os(windows)-                Build-Depends:          Win32 >= 2.0-                Cpp-Options:            -DWINDOWS-                Extra-Libraries:        "kernel32"-                Other-Modules:          System.Console.ANSI.Windows-                                        System.Console.ANSI.Windows.Foreign-                                        System.Console.ANSI.Windows.Emulator-        else-                -- We assume any non-Windows platform is Unix-                Build-Depends:          unix >= 2.3.0.0-                Cpp-Options:            -DUNIX-                Other-Modules:          System.Console.ANSI.Unix-        -        if flag(splitBase)-                Build-Depends:          base >= 3 && < 5-        else-                Build-Depends:          base < 3-        -        Extensions:             CPP-                                ForeignFunctionInterface-        -        Ghc-Options:            -Wall-        -        if !flag(example)-                Buildable:              False+Cabal-Version:       1.22
+Name:                ansi-terminal
+Version:             1.1.5
+Category:            User Interfaces
+Synopsis:            Simple ANSI terminal support
+Description:         ANSI terminal support for Haskell: allows cursor movement,
+                     screen clearing, color output, showing or hiding the
+                     cursor, and changing the title. Works on UNIX and Windows.
+License:             BSD3
+License-File:        LICENSE
+Author:              Max Bolingbroke
+Maintainer:          Mike Pilgrem <public@pilgrem.com>, Roman Cheplyaka <roma@ro-che.info>
+Homepage:            https://github.com/UnkindPartition/ansi-terminal
+Build-Type:          Simple
+
+Extra-Source-Files:     CHANGELOG.md
+                        README.md
+                        win/include/errors.h
+                        win/include/HsWin32.h
+                        win/include/winternl_compat.h
+
+Source-repository head
+  type:     git
+  location: git://github.com/UnkindPartition/ansi-terminal.git
+
+Flag example
+        Description:    Build the example application
+        Default:        False
+
+Library
+        Hs-Source-Dirs:         src
+        Exposed-Modules:        System.Console.ANSI
+
+        -- We re-export all of ansi-terminal-types to aid compatibility for
+        -- downstream users.
+        Reexported-Modules:     System.Console.ANSI.Codes
+                              , System.Console.ANSI.Types
+
+        Other-Modules:          System.Console.ANSI.Internal
+
+        Build-Depends:          base >= 4.8.0.0 && < 5
+                              , ansi-terminal-types == 1.1.3
+                              , colour >= 2.1.0
+        if os(windows)
+            Hs-Source-Dirs:     win
+            Other-Modules:      System.Console.ANSI.Windows.Foreign
+                                System.Console.ANSI.Windows.Win32.Types
+                                System.Console.ANSI.Windows.Win32.MinTTY
+            Include-Dirs:       win/include
+            Install-Includes:   HsWin32.h
+            C-Sources:          win/c-source/errors.c
+                                win/c-source/HsWin32.c
+            -- The MSYS2 UCRT64 environment is stricter than the MINGW64
+            -- environment about these C compiler diagnostics:
+            -- (1) macro-redefined, (2) missing-declarations, and
+            -- (3) pragma-pack. Windows C headers are not warning-clean under
+            -- -Wmacro-redefined, -Wmissing-declarations and -Wpragma-pack. We
+            -- smother those warnings:
+            Cc-Options:         -Wno-macro-redefined
+                                -Wno-missing-declarations
+                                -Wno-pragma-pack
+        else
+            Hs-Source-Dirs:     unix
+
+        Default-Extensions:     CPP
+
+        Ghc-Options:            -Wall
+        Default-Language:       Haskell2010
+
+Executable ansi-terminal-example
+        Hs-Source-Dirs:         app
+        Main-Is:                Example.hs
+        Build-Depends:          base >= 4.8.0.0 && < 5
+                              , ansi-terminal
+                              , colour
+        Ghc-Options:            -Wall
+        if !flag(example)
+            Buildable:          False
+        Default-Language:       Haskell2010
+ app/Example.hs view
@@ -0,0 +1,525 @@+module Main
+  (
+    main
+  ) where
+
+import Control.Concurrent (threadDelay)
+import Control.Monad (forM_, replicateM_)
+import System.IO (hFlush, stdout)
+import Text.Printf(printf)
+
+import Data.Colour.SRGB (sRGB24)
+
+import System.Console.ANSI
+import System.Environment ( getArgs )
+
+examples :: [(String, IO ())]
+examples =
+  [ ( "Cursor movement", cursorMovementExample )
+  , ( "Line change", lineChangeExample )
+  , ( "Set cursor position", setCursorPositionExample )
+  , ( "Save restore cursor", saveRestoreCursorExample )
+  , ( "Clear", clearExample )
+  , ( "Line wrap", lineWrapExample )
+  , ( "Scroll", scrollExample )
+  , ( "Screen buffers", screenBuffersExample )
+  , ( "SGR color", sgrColorExample )
+  , ( "SGR other", sgrOtherExample )
+  , ( "Cursor visibility", cursorVisibilityExample )
+  , ( "Hyperlink", hyperlinkExample )
+  , ( "Title", titleExample )
+  , ( "Get cursor position", getCursorPositionExample )
+  , ( "Get terminal size", getTerminalSizeExample )
+  , ( "Get layer color", getLayerColorExample )
+  ]
+
+main :: IO ()
+main = do
+  args <- getArgs
+  let numExamples = zip [1 ..] examples
+  if wantHelp args
+    then
+      listExamples numExamples
+    else do
+      let wantedNumExamples = filter (isExample args) numExamples
+          wantedExamples = map getExample wantedNumExamples
+      stdoutSupportsANSI <- hNowSupportsANSI stdout
+      if stdoutSupportsANSI
+        then
+          mapM_ (resetScreen >>) wantedExamples
+        else
+          putStrLn "Standard output does not support 'ANSI' escape codes."
+ where
+  wantHelp :: [String] -> Bool
+  wantHelp args = "-h" `elem` args || "--help" `elem` args
+
+  listExamples :: [(Int, (String, IO ()))] -> IO ()
+  listExamples = mapM_ (\(n, (d, _)) -> putStrLn $ show n ++ ": " ++ d)
+
+  isExample :: [String] -> (Int, (String, IO ())) -> Bool
+  isExample args (n, _) = show n `elem` args
+
+  getExample :: (Int, (String, IO ())) -> IO ()
+  getExample (_, (_, example)) = example
+
+-- Annex D to Standard ECMA-48 (5th Ed, 1991) identifies that the representation
+-- of an erased state is implementation-dependent. There may or may not be a
+-- distinction between a character position in the erased state and one imaging
+-- SPACE. Consequently, to reset the screen, the default graphic rendition must
+-- be selected (setSGR [Reset]) before all character positions are put into the
+-- erased state (clearScreen).
+resetScreen :: IO ()
+resetScreen = setSGR [Reset] >> clearScreen >> setCursorPosition 0 0
+
+pause :: IO ()
+pause = do
+  hFlush stdout
+  -- 1 second pause
+  threadDelay 1000000
+
+cursorMovementExample :: IO ()
+cursorMovementExample = do
+  putStrLn "Line One"
+  putStr "Line Two"
+  pause
+  -- Line One
+  -- Line Two
+
+  cursorUp 1
+  putStr " - Extras"
+  pause
+  -- Line One - Extras
+  -- Line Two
+
+  cursorBackward 2
+  putStr "zz"
+  pause
+  -- Line One - Extrzz
+  -- Line Two
+
+  cursorForward 2
+  putStr "- And More"
+  pause
+  -- Line One - Extrzz  - And More
+  -- Line Two
+
+  cursorDown 1
+  putStr "Disconnected"
+  pause
+  -- Line One - Extrzz  - And More
+  -- Line Two                     Disconnected
+
+lineChangeExample :: IO ()
+lineChangeExample = do
+  putStrLn "Line One"
+  putStr "Line Two"
+  pause
+  -- Line One
+  -- Line Two
+
+  cursorUpLine 1
+  putStr "New Line One"
+  pause
+  -- New Line One
+  -- Line Two
+
+  cursorDownLine 1
+  putStr "New Line Two"
+  pause
+  -- New Line One
+  -- New Line Two
+
+setCursorPositionExample :: IO ()
+setCursorPositionExample = do
+  putStrLn "Line One"
+  putStrLn "Line Two"
+  pause
+  -- Line One
+  -- Line Two
+
+  setCursorPosition 0 5
+  putStr "Foo"
+  pause
+  -- Line Foo
+  -- Line Two
+
+  setCursorPosition 1 5
+  putStr "Bar"
+  pause
+  -- Line Foo
+  -- Line Bar
+
+  setCursorColumn 1
+  putStr "oaf"
+  pause
+  -- Line Foo
+  -- Loaf Bar
+
+saveRestoreCursorExample :: IO ()
+saveRestoreCursorExample = do
+  putStr "Start sentence ..."
+  pause
+  -- Start sentence ...
+
+  saveCursor
+  setCursorPosition 2 3
+  putStr "SPLASH!"
+  pause
+  -- Start sentence ...
+  --
+  --    SPLASH!
+
+  restoreCursor
+  putStr " end sentence, uninterrupted."
+  pause
+  -- Start sentence ... end sentence, uninterrupted
+  --
+  --    SPLASH!
+
+clearExample :: IO ()
+clearExample = do
+  putStrLn "Line One"
+  putStrLn "Line Two"
+  pause
+  -- Line One
+  -- Line Two
+
+  setCursorPosition 0 4
+  clearFromCursorToScreenEnd
+  pause
+  -- Line
+
+  resetScreen
+  putStrLn "Line One"
+  putStrLn "Line Two"
+  pause
+  -- Line One
+  -- Line Two
+
+  setCursorPosition 1 4
+  clearFromCursorToScreenBeginning
+  pause
+  --
+  --     Two
+
+  resetScreen
+  putStrLn "Line One"
+  putStrLn "Line Two"
+  pause
+  -- Line One
+  -- Line Two
+
+  setCursorPosition 0 4
+  clearFromCursorToLineEnd
+  pause
+  -- Line
+  -- Line Two
+
+  setCursorPosition 1 4
+  clearFromCursorToLineBeginning
+  pause
+  -- Line
+  --      Two
+
+  clearLine
+  pause
+  -- Line
+
+  clearScreen
+  pause
+  --
+
+lineWrapExample :: IO ()
+lineWrapExample = do
+  putStrLn $ take 240 $ cycle "Default-line-wrap|"
+  pause
+  -- Default-line-wrap|Default-line-wrap|Default-line-wrap|Default-line-wrap|Def
+  -- ault-line-wrap|Default-line-wrap|Default-line-wrap|Default-line-wrap|Defaul
+  -- t-line-wrap|Default-line-wrap|Default-line-wrap|Default-line-wrap|Default-l
+  disableLineWrap
+  putStrLn $ take 240 $ cycle "Disable-line-wrap|"
+  pause
+  -- Disable-line-wrap|Disable-line-wrap|Disable-line-wrap|Disable-line-wrap|Dis
+  enableLineWrap
+  putStrLn $ take 240 $ cycle "Enable-line-wrap|"
+  pause
+  -- Enable-line-wrap|Enable-line-wrap|Enable-line-wrap|Enable-line-wrap|Enable-
+  -- line-wrap|Enable-line-wrap|Enable-line-wrap|Enable-line-wrap|Enable-line-wr
+  -- ap|Enable-line-wrap|Enable-line-wrap|Enable-line-wrap|Enable-line-wrap|Enab
+
+scrollExample :: IO ()
+scrollExample = do
+  putStrLn "Line One"
+  putStrLn "Line Two"
+  putStrLn "Line Three"
+  pause
+  -- Line One
+  -- Line Two
+  -- Line Three
+
+  scrollPageDown 2
+  pause
+  --
+  --
+  -- Line One
+  -- Line Two
+  -- Line Three
+
+  scrollPageUp 3
+  pause
+  -- Line Two
+  -- Line Three
+
+screenBuffersExample :: IO ()
+screenBuffersExample = do
+  replicateM_ 5 $ putStrLn "This message is on the Normal Screen Bufffer"
+  replicateM_ 5 pause
+  useAlternateScreenBuffer
+  replicateM_ 5 $ putStrLn "This message is on the Alternate Screen Bufffer"
+  replicateM_ 5 pause
+  useNormalScreenBuffer
+  replicateM_ 5 $ putStrLn "This message is continuing where we left off"
+  replicateM_ 5 pause
+
+sgrColorExample :: IO ()
+sgrColorExample = do
+  let colors = enumFromTo minBound maxBound :: [Color]
+  forM_ [Foreground, Background, Underlining] $ \layer ->  do
+    forM_ [Dull, Vivid] $ \intensity -> do
+      resetScreen
+      forM_ colors $ \color -> do
+        setSGR [Reset]
+        setSGR [SetColor layer intensity color]
+        setSGR [SetUnderlining SingleUnderline]
+        print color
+      pause
+  -- The ANSI eight standard colors, 6 times in sequence (three 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
+
+  resetScreen
+  setSGR [Reset]
+  setSGR [SetUnderlining SingleUnderline]
+  forM_
+    (zip "Underlining: True color (24 bit color depth)" (cycle [0, 10 .. 255])) $
+    \(c, i) -> do
+      setSGR [SetRGBColor Underlining $ sRGB24 i 0 0]
+      putChar c
+  putChar '\n'
+  putChar '\n'
+  setSGR [Reset]
+  setSGR [SetUnderlining SingleUnderline]
+  forM_ (zip "Underlining: A 256-color palette" (cycle [0 .. 5])) $
+    \(c, i) -> do
+      setSGR [SetPaletteColor Underlining $ xterm6LevelRGB i 0 0]
+      putChar c
+  putChar '\n'
+  replicateM_ 5 pause
+
+sgrOtherExample :: IO ()
+sgrOtherExample = do
+  let named_styles = [ (SetConsoleIntensity BoldIntensity, "Bold")
+                     , (SetConsoleIntensity FaintIntensity, "Faint")
+                     , (SetConsoleIntensity NormalIntensity, "Normal")
+                     , (SetItalicized True, "Italic")
+                     , (SetItalicized False, "No Italics")
+                     , (SetUnderlining SingleUnderline, "Single Underline")
+                     , (SetUnderlining DoubleUnderline, "Double Underline")
+                     , (SetUnderlining CurlyUnderline, "Curly Underline")
+                     , (SetUnderlining DottedUnderline, "Dotted Underline")
+                     , (SetUnderlining DashedUnderline, "Dashed Underline")
+                     , (SetUnderlining NoUnderline, "No Underline")
+                     , (SetBlinkSpeed SlowBlink, "Slow Blink")
+                     , (SetBlinkSpeed RapidBlink, "Rapid Blink")
+                     , (SetBlinkSpeed NoBlink, "No Blink")
+                     , (SetVisible False, "Conceal")
+                     , (SetVisible True, "Reveal")
+                     ]
+  forM_ named_styles $ \(style, name) -> do
+    resetScreen
+    setSGR [style]
+    putStrLn name
+    pause
+  -- Text describing a style displayed in that style in sequence
+
+  setSGR [SetColor Foreground Vivid Red]
+  setSGR [SetColor Background Vivid Blue]
+
+  clearScreen >> setCursorPosition 0 0
+  setSGR [SetSwapForegroundBackground False]
+  putStr "Red-On-Blue"
+  pause
+  -- Red-On-Blue
+
+  clearScreen >> setCursorPosition 0 0
+  setSGR [SetSwapForegroundBackground True]
+  putStr "Blue-On-Red"
+  pause
+  -- Blue-On-Red
+
+cursorVisibilityExample :: IO ()
+cursorVisibilityExample = do
+  putStr "Cursor Demo"
+  pause
+  -- Cursor Demo|
+
+  hideCursor
+  pause
+  -- Cursor Demo
+
+  showCursor
+  pause
+  -- Cursor Demo|
+
+hyperlinkExample :: IO ()
+hyperlinkExample = do
+  putStr "Hyperlink demo: "
+  hyperlink "https://example.com" "Example hyperlink\n"
+  putStrLn ""
+  putStrLn "Linked hyperlinks demo:"
+  hyperlinkWithId "ref" "https://example.com" "Example linked hyperlink one\n"
+  hyperlinkWithId "ref" "https://example.com" "Example linked hyperlink two\n"
+
+  replicateM_ 5 pause
+  -- Hyperlink demo: Example hyperlink
+  --
+  -- Linked hyperlinks demo:
+  -- Example linked hyperlink one
+  -- Example linked hyperlink two
+
+titleExample :: IO ()
+titleExample = do
+  putStr "Title Demo"
+  pause
+  -- ~/foo/ - ansi-terminal-ex - 83x70
+  ------------------------------------
+  -- Title Demo
+
+  setTitle "Yup, I'm a new title!"
+  pause
+  -- Yup, I'm a new title! - ansi-terminal-ex - 83x70
+  ---------------------------------------------------
+  -- Title Demo
+
+getCursorPositionExample :: IO ()
+getCursorPositionExample = do
+  putStrLn "         11111111112222222222"
+  putStrLn "12345678901234567890123456789"
+  putStr   "Report cursor position here:"
+  pause
+  --          11111111112222222222
+  -- 12345678901234567890123456789
+  -- Report cursor position here:|
+  result <- getCursorPosition
+  putStrLn " (3rd row, 29th column) to stdin, as CSI 3 ; 29 R.\n"
+  case result of
+    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"
+  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.
+
+getLayerColorExample :: IO ()
+getLayerColorExample = do
+  fgResult <- getLayerColor Foreground
+  case fgResult of
+    Just fgCol -> putStrLn $ "The reported foreground color is:\n" ++
+      show fgCol ++ "\n"
+    Nothing -> putStrLn "Error: unable to get the foreground color\n"
+  bgResult <- getLayerColor Background
+  case bgResult of
+    Just bgCol -> putStrLn $ "The reported background color is:\n" ++
+      show bgCol ++ "\n"
+    Nothing -> putStrLn "Error: unable to get the background color\n"
− includes/Common-Include.hs
@@ -1,127 +0,0 @@-hCursorUp, hCursorDown, hCursorForward, hCursorBackward :: Handle-                                                        -> Int -- ^ Number of lines or characters to move-                                                        -> IO ()-cursorUp, cursorDown, cursorForward, cursorBackward :: Int -- ^ Number of lines or characters to move-                                                    -> IO ()-cursorUpCode, cursorDownCode, cursorForwardCode, cursorBackwardCode :: Int -- ^ Number of lines or characters to move-                                                                    -> String--cursorUp = hCursorUp stdout-cursorDown = hCursorDown stdout-cursorForward = hCursorForward stdout-cursorBackward = hCursorBackward stdout---hCursorDownLine, hCursorUpLine :: Handle-                               -> Int -- ^ Number of lines to move-                               -> IO ()-cursorDownLine, cursorUpLine :: Int -- ^ Number of lines to move-                             -> IO ()-cursorDownLineCode, cursorUpLineCode :: Int -- ^ Number of lines to move-                                     -> String--cursorDownLine = hCursorDownLine stdout-cursorUpLine = hCursorUpLine stdout---hSetCursorColumn :: Handle-                 -> Int -- ^ 0-based column to move to-                 -> IO ()-setCursorColumn :: Int -- ^ 0-based column to move to-                -> IO ()-setCursorColumnCode :: Int -- ^ 0-based column to move to-                    -> String--setCursorColumn = hSetCursorColumn stdout---hSetCursorPosition :: Handle-                   -> Int -- ^ 0-based row to move to-                   -> Int -- ^ 0-based column to move to-                   -> IO ()-setCursorPosition :: Int -- ^ 0-based row to move to-                  -> Int -- ^ 0-based column to move to-                  -> IO ()-setCursorPositionCode :: Int -- ^ 0-based row to move to-                      -> Int -- ^ 0-based column to move to-                      -> String--setCursorPosition = hSetCursorPosition stdout---hClearFromCursorToScreenEnd, hClearFromCursorToScreenBeginning, hClearScreen :: Handle-                                                                             -> IO ()-clearFromCursorToScreenEnd, clearFromCursorToScreenBeginning, clearScreen :: IO ()-clearFromCursorToScreenEndCode, clearFromCursorToScreenBeginningCode, clearScreenCode :: String--clearFromCursorToScreenEnd = hClearFromCursorToScreenEnd stdout-clearFromCursorToScreenBeginning = hClearFromCursorToScreenBeginning stdout-clearScreen = hClearScreen stdout---hClearFromCursorToLineEnd, hClearFromCursorToLineBeginning, hClearLine :: Handle-                                                                       -> IO ()-clearFromCursorToLineEnd, clearFromCursorToLineBeginning, clearLine :: IO ()-clearFromCursorToLineEndCode, clearFromCursorToLineBeginningCode, clearLineCode :: String--clearFromCursorToLineEnd = hClearFromCursorToLineEnd stdout-clearFromCursorToLineBeginning = hClearFromCursorToLineBeginning stdout-clearLine = hClearLine stdout----- | Scroll the displayed information up or down the terminal: not widely supported-hScrollPageUp, hScrollPageDown :: Handle-                               -> Int -- ^ Number of lines to scroll by-                               -> IO ()--- | Scroll the displayed information up or down the terminal: not widely supported-scrollPageUp, scrollPageDown :: Int -- ^ Number of lines to scroll by-                             -> IO ()--- | Scroll the displayed information up or down the terminal: not widely supported-scrollPageUpCode, scrollPageDownCode :: Int -- ^ Number of lines to scroll by-                                     -> String--scrollPageUp = hScrollPageUp stdout-scrollPageDown = hScrollPageDown stdout----- | Set the Select Graphic Rendition mode-hSetSGR :: Handle-        -> [SGR] -- ^ Commands: these will typically be applied on top of the current console SGR mode.-                 -- An empty list of commands is equivalent to the list @[Reset]@. Commands are applied-                 -- left to right.-        -> IO ()--- | Set the Select Graphic Rendition mode-setSGR :: [SGR] -- ^ Commands: these will typically be applied on top of the current console SGR mode.-                -- An empty list of commands is equivalent to the list @[Reset]@. Commands are applied-                -- left to right.-       -> IO ()--- | Set the Select Graphic Rendition mode-setSGRCode :: [SGR] -- ^ Commands: these will typically be applied on top of the current console SGR mode.-                    -- An empty list of commands is equivalent to the list @[Reset]@. Commands are applied-                    -- left to right.-           -> String--setSGR = hSetSGR stdout---hHideCursor, hShowCursor :: Handle-                         -> IO ()-hideCursor, showCursor :: IO ()-hideCursorCode, showCursorCode :: String--hideCursor = hHideCursor stdout-showCursor = hShowCursor stdout----- | Set the terminal window title-hSetTitle :: Handle-          -> String -- ^ New title-          -> IO ()--- | Set the terminal window title-setTitle :: String -- ^ New title-         -> IO ()--- | Set the terminal window title-setTitleCode :: String -- ^ New title-             -> String--setTitle = hSetTitle stdout
− includes/Exports-Include.hs
@@ -1,50 +0,0 @@--- * Basic data types-module System.Console.ANSI.Common,---- * Cursor movement by character-cursorUp, cursorDown, cursorForward, cursorBackward,-hCursorUp, hCursorDown, hCursorForward, hCursorBackward,-cursorUpCode, cursorDownCode, cursorForwardCode, cursorBackwardCode,---- * Cursor movement by line-cursorUpLine, cursorDownLine,-hCursorUpLine, hCursorDownLine,-cursorUpLineCode, cursorDownLineCode,---- * Directly changing cursor position-setCursorColumn,-hSetCursorColumn,-setCursorColumnCode,--setCursorPosition,-hSetCursorPosition,-setCursorPositionCode,---- * Clearing parts of the screen-clearFromCursorToScreenEnd, clearFromCursorToScreenBeginning, clearScreen,-hClearFromCursorToScreenEnd, hClearFromCursorToScreenBeginning, hClearScreen,-clearFromCursorToScreenEndCode, clearFromCursorToScreenBeginningCode, clearScreenCode,--clearFromCursorToLineEnd, clearFromCursorToLineBeginning, clearLine,-hClearFromCursorToLineEnd, hClearFromCursorToLineBeginning, hClearLine,-clearFromCursorToLineEndCode, clearFromCursorToLineBeginningCode, clearLineCode,---- * Scrolling the screen-scrollPageUp, scrollPageDown,-hScrollPageUp, hScrollPageDown,-scrollPageUpCode, scrollPageDownCode,---- * Select Graphic Rendition mode: colors and other whizzy stuff-setSGR,-hSetSGR,-setSGRCode,---- * Cursor visibilty changes-hideCursor, showCursor,-hHideCursor, hShowCursor,-hideCursorCode, showCursorCode,---- * Changing the title-setTitle,-hSetTitle,-setTitleCode
+ src/System/Console/ANSI.hs view
@@ -0,0 +1,1095 @@+{-# LANGUAGE Safe #-}
+
+{-| == Introduction
+Through this module, this library provides platform-independent support for
+control character sequences following the \'ANSI\' standards (see further below)
+for terminal software that supports those sequences, running on a Unix-like
+operating system or on Windows (see further below).
+
+The sequences of control characters (also referred to as \'escape\' sequences or
+codes) provide a rich range of functionality for terminal control, which
+includes:
+
+ * Colored text output, with control over foreground, background and (where
+   supported) underlining colors
+
+ * Clearing parts of a line or the screen
+
+ * Hiding or showing the cursor
+
+ * Moving the cursor around
+
+ * Reporting the position of the cursor
+
+ * Enabling or disabling automatic line wrapping
+
+ * Scrolling the screen up or down
+
+ * Switching between the Alternate and Normal Screen Buffers
+
+ * Clickable hyperlinks to URIs
+
+ * Changing the title of the terminal
+
+A terminal that supports control character sequences acts on them when they
+are flushed from the output buffer (with a newline character @\"\\n\"@ or, for
+the standard output channel, @hFlush stdout@).
+
+== \'ANSI\' standards
+The \'ANSI\' standards refer to (1) standard ECMA-48 \`Control Functions for
+Coded Character Sets\' (5th edition, 1991); (2) extensions in ITU-T
+Recommendation (previously CCITT Recommendation) T.416 (03/93) \'Information
+Technology – Open Document Architecture (ODA) and Interchange Format: Character
+Content Architectures\` (also published as ISO/IEC International Standard
+8613-6); and (3) further extensions used by \'XTerm\', a terminal emulator for
+the X Window System. The escape codes are described in a
+  [Wikipedia article](http://en.wikipedia.org/wiki/ANSI_escape_code) and those
+codes supported on current versions of Windows are descibed in
+  [Microsoft's documentation](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences).
+
+The whole of the \'ANSI\' standards are not supported by this library but most
+(if not all) of the parts that are popular and well-supported by terminal
+software are supported (see further below).
+
+== Cursor positions
+The functions moving the cursor to an absolute position are 0-based (the
+top-left corner is considered to be at row 0 column 0) (see 'setCursorPosition')
+and so is 'getCursorPosition'. The \'ANSI\' standards themselves are 1-based
+(that is, the top-left corner is considered to be at row 1 column 1) and some
+functions reporting the position of the cursor are too (see
+'reportCursorPosition').
+
+== Windows and control character sequences
+The native terminal software on Windows has developed over time. Before
+Windows 10 version 1511 (known as the \'November [2015] Update\' or
+\'Threshold 2\') that software did not support control character sequences. From
+2018, Microsoft introduced the Windows Pseudo Console (\'ConPTY\') API and then
+Windows Terminal, with the objective of replacing most of the Windows Console
+API with the use of control character sequences and retiring the historical
+user-interface role of Windows Console Host (\'ConHost\').
+
+Windows Terminal is supported on Windows 10 version 19041.0 or higher and
+provided with Windows 11. It can be downloaded from the Microsoft Store. Windows
+Terminal can be set as the default terminal application on Windows 10 (from
+the 22H2 update) and is the default application on Windows 11 (from the 22H2
+update).
+
+Despite the above developments, some Windows users may continue to use ConHost.
+ConHost does not enable the processing of \'ANSI\' control characters in output
+by default. See 'hNowSupportsANSI' for a function that can try to enable such
+processing.
+
+Terminal software other than the native software exists for Windows. One example
+is the \'mintty\' terminal emulator for \'Cygwin\', \'MSYS\' or \'MSYS2\', and
+dervied projects, and for \'WSL\' (Windows Subsystem for Linux).
+
+GHC's management of input and output (IO) on Windows has also developed over
+time. If they are supported by the terminal software, some control character
+sequences cause data to be emitted into the console input stream. For GHC's
+historical and default IO manager, the function 'System.IO.hGetBufNonBlocking'
+in module "System.IO" does not work on Windows. This has been attributed to the
+lack of non-blocking primatives in the operating system (see
+  [GHC bug report #806](https://ghc.haskell.org/trac/ghc/ticket/806). GHC's
+native IO manager on Windows (\'WinIO\'), introduced as a preview in
+  [GHC 9.0.1](https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/9.0.1-notes.html#highlights),
+has not yet provided a solution. On Windows, this library uses emulation based
+on the Windows Console API to try to read data emitted into the console input
+stream. Functions that use that emulation are not supported on consoles, such
+as mintty, that are not based on that API.
+
+== Function variants provided
+Every function exported by this module comes in three variants, namely:
+
+ * A variant that has an @IO ()@ type and doesn't take a @Handle@ (for example,
+   @clearScreen :: IO ()@). This variant just outputs the \`ANSI\` command
+   directly to the standard output channel ('stdout') and any terminal
+   corresponding to it. Commands issued like this should work as you expect on
+   both Unix-like operating systems and Windows (unless exceptions on Windows
+   are stated).
+
+ * An \'@h@...\' variant that has an @IO ()@ type but takes a @Handle@ (for
+   example, @hClearScreen :: Handle -> IO ()@). This variant outputs the
+   \`ANSI\` command to the supplied handle and any terminal corresponding to it.
+   Commands issued like this should also work as you expect on both Unix-like
+   operating systems and Windows (unless exceptions on Windows are stated).
+
+ * A \'...@Code@\' variant that has a @String@ type (for example,
+   @clearScreenCode :: String@). This variant outputs the sequence of control
+   characters as a 'String', which can be added to any other bit of text before
+   being output. If a high degree of backwards compatability is rewuired, the
+   use of these codes is discouraged because they will not work on legacy
+   versions of Windows where the terminal in use is not ANSI-enabled (see
+   further above). On Windows, where emulation has been necessary, these
+   variants will always output the empty string. That is done so that it is
+   possible to use them portably; for example, coloring console output on the
+   understanding that you will see colors only if you are running on a Unix-like
+   operating system or a version of Windows where emulation has not been
+   necessary. If the control characters are always required, see module
+   "System.Console.ANSI.Codes".
+
+== Examples of use
+
+A simple example is below:
+
+> module Main where
+>
+> import System.Console.ANSI
+> import System.IO (stdout)
+>
+> -- Set colors and write some text in those colors.
+> main :: IO ()
+> main = do
+>   stdoutSupportsANSI <- hNowSupportsANSI stdout
+>   if stdoutSupportsANSI
+>     then do
+>       setSGR [SetColor Foreground Vivid Red]
+>       setSGR [SetColor Background Vivid Blue]
+>       putStrLn "Red-On-Blue"
+>       setSGR [Reset]  -- Reset to default colour scheme
+>       putStrLn "Default colors."
+>     else
+>       putStrLn "Standard output does not support 'ANSI' escape codes."
+
+Another example is below:
+
+> module Main where
+>
+> import System.IO (hFlush, stdout)
+> import System.Console.ANSI
+>
+> main :: IO ()
+> main = do
+>   stdoutSupportsANSI <- hNowSupportsANSI stdout
+>   if stdoutSupportsANSI
+>     then do
+>       setSGR [SetColor Foreground Dull Blue]
+>       putStr "Enter your name: "
+>       setSGR [SetColor Foreground Dull Yellow]
+>       hFlush stdout  -- flush the output buffer before getLine
+>       name <- getLine
+>       setSGR [SetColor Foreground Dull Blue]
+>       putStrLn $ "Hello, " ++ name ++ "!"
+>       setSGR [Reset]  -- reset to default colour scheme
+>     else
+>       putStrLn "Standard output does not support 'ANSI' escape codes."
+
+For many more examples, see the project's extensive
+<https://github.com/UnkindPartition/ansi-terminal/blob/master/app/Example.hs Example.hs> file.
+-}
+
+module System.Console.ANSI
+  (
+    -- * Basic data types
+    module System.Console.ANSI.Types
+
+    -- * Cursor movement by character
+  , cursorUp
+  , cursorDown
+  , cursorForward
+  , cursorBackward
+    -- ** \'h...\' variants
+  , hCursorUp
+  , hCursorDown
+  , hCursorForward
+  , hCursorBackward
+    -- ** \'...Code\' variants
+  , cursorUpCode
+  , cursorDownCode
+  , cursorForwardCode
+  , cursorBackwardCode
+
+    -- * Cursor movement by line
+    --
+    -- | The difference between movements \"by character\" and \"by line\" is
+    -- that @*Line@ functions additionally move the cursor to the start of the
+    -- line, while functions like @cursorUp@ and @cursorDown@ keep the column
+    -- the same.
+  , cursorUpLine
+  , cursorDownLine
+    -- ** \'h...\' variants
+  , hCursorUpLine
+  , hCursorDownLine
+    -- ** \'...Code\' variants
+  , cursorUpLineCode
+  , cursorDownLineCode
+
+    -- * Directly changing cursor position
+  , setCursorColumn
+  , setCursorPosition
+    -- ** \'h...\' variants
+  , hSetCursorColumn
+  , hSetCursorPosition
+    -- ** \'...Code\' variants
+  , setCursorColumnCode
+  , setCursorPositionCode
+
+    -- * Saving, restoring and reporting cursor position
+    --
+    -- | These code sequences are not part of ECMA-48 standard; they are popular,
+    -- but non-portable extensions. E. g., Terminal.app on MacOS
+    -- <https://stackoverflow.com/questions/25879183 does not support them>.
+    -- A more portable way would be to query @terminfo@ database
+    -- for @rc@ and @sc@ capabilities.
+    --
+    -- Cursor positions
+    -- <https://unix.stackexchange.com/questions/565597 are relative to the viewport, not to its content>.
+    --
+  , saveCursor
+  , restoreCursor
+  , reportCursorPosition
+    -- ** \'h...\' variants
+  , hSaveCursor
+  , hRestoreCursor
+  , hReportCursorPosition
+    -- ** \'...Code\' variants
+  , saveCursorCode
+  , restoreCursorCode
+  , reportCursorPositionCode
+
+    -- * Clearing parts of the screen
+    --
+    -- | Note that these functions only clear parts of the screen. They do not
+    -- move the cursor. Some functions are based on the whole screen and others
+    -- are based on the line in which the cursor is located.
+  , clearFromCursorToScreenEnd
+  , clearFromCursorToScreenBeginning
+  , clearScreen
+  , clearFromCursorToLineEnd
+  , clearFromCursorToLineBeginning
+  , clearLine
+    -- ** \'h...\' variants
+  , hClearFromCursorToScreenEnd
+  , hClearFromCursorToScreenBeginning
+  , hClearScreen
+  , hClearFromCursorToLineEnd
+  , hClearFromCursorToLineBeginning
+  , hClearLine
+    -- ** \'...Code\' variants
+  , clearFromCursorToScreenEndCode
+  , clearFromCursorToScreenBeginningCode
+  , clearScreenCode
+  , clearFromCursorToLineEndCode
+  , clearFromCursorToLineBeginningCode
+  , clearLineCode
+
+    -- * Enabling and disabling automatic line wrapping
+    --
+    -- | These functions control whether or not characters automatically wrap to
+    -- the next line when the cursor reaches the right border.
+  , enableLineWrap
+  , disableLineWrap
+    -- ** \'h...\' variants
+  , hEnableLineWrap
+  , hDisableLineWrap
+    -- ** \'...Code\' variants
+  , enableLineWrapCode
+  , disableLineWrapCode
+
+    -- * Scrolling the screen
+  , scrollPageUp
+  , scrollPageDown
+    -- ** \'h...\' variants
+  , hScrollPageUp
+  , hScrollPageDown
+    -- ** \'...Code\' variants
+  , scrollPageUpCode
+  , scrollPageDownCode
+
+    -- * Using screen buffers
+    --
+    -- | These code sequences are not part of ECMA-48 standard; they are popular,
+    -- but non-portable extensions, corresponding to @smcup@ and @rmcup@ capabilities
+    -- in @terminfo@ database.
+    -- On Windows, if emulation is required, switching between alternate and
+    -- normal screen buffers is not emulated.
+  , useAlternateScreenBuffer
+  , useNormalScreenBuffer
+    -- ** \'h...\' variants
+  , hUseAlternateScreenBuffer
+  , hUseNormalScreenBuffer
+    -- ** \'...Code\' variants
+  , useAlternateScreenBufferCode
+  , useNormalScreenBufferCode
+
+    -- * Reporting the background or foreground colors
+  , reportLayerColor
+  , hReportLayerColor
+  , reportLayerColorCode
+
+    -- * Select Graphic Rendition mode: colors and other whizzy stuff
+  , setSGR
+  , hSetSGR
+  , setSGRCode
+
+    -- * Cursor visibilty changes
+    --
+    -- | Strictly speaking, these code sequences are not part of ECMA-48 standard;
+    -- they are popular, but non-portable extensions. However, in practice they seem
+    -- to work pretty much everywhere.
+  , hideCursor
+  , showCursor
+    -- ** \'h...\' variants
+  , hHideCursor
+  , hShowCursor
+    -- ** \'...Code\' variants
+  , hideCursorCode
+  , showCursorCode
+
+    -- * Hyperlinks
+    --
+    -- | These code sequences are not part of ECMA-48 standard and not even an
+    -- @xterm@ extension. Nevertheless
+    -- <https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda many terminals>
+    -- support them. On Windows, if emulation is required,
+    -- hyperlinks are not emulated.
+  , hyperlink
+  , hyperlinkWithId
+  , hyperlinkWithParams
+    -- ** \'h...\' variants
+  , hHyperlink
+  , hHyperlinkWithId
+  , hHyperlinkWithParams
+    -- ** \'...Code\' variants
+  , hyperlinkCode
+  , hyperlinkWithIdCode
+  , hyperlinkWithParamsCode
+
+    -- * Changing the title
+  , setTitle
+  , hSetTitle
+  , setTitleCode
+
+    -- * Checking if handle supports ANSI (not portable: GHC only)
+  , hSupportsANSI
+  , hNowSupportsANSI
+  , hSupportsANSIColor
+
+    -- * Getting the cursor position
+  , getCursorPosition
+  , hGetCursorPosition
+  , getReportedCursorPosition
+  , cursorPosition
+
+    -- * Getting the terminal size
+  , getTerminalSize
+  , hGetTerminalSize
+
+    -- * Getting the background or foreground colors
+  , getLayerColor
+  , hGetLayerColor
+  , getReportedLayerColor
+  , layerColor
+
+    -- * Deprecated
+  , hSupportsANSIWithoutEmulation
+  ) where
+
+import Control.Concurrent (threadDelay)
+import Control.Exception.Base ( bracket )
+import Control.Monad ( when, void )
+import Data.Char ( digitToInt, isDigit, isHexDigit )
+import Data.Colour.SRGB ( RGB (..) )
+import Data.Word ( Word16 )
+import System.IO
+         ( BufferMode (..), Handle, hFlush, hGetBuffering, hGetEcho, hPutStr
+         , hReady, hSetBuffering, hSetEcho, stdin, stdout
+         )
+import Text.ParserCombinators.ReadP
+         ( ReadP, (<++), char, many1, readP_to_S, satisfy, string )
+
+import System.Console.ANSI.Codes
+import qualified System.Console.ANSI.Internal as Internal
+import System.Console.ANSI.Types
+
+hCursorUp, hCursorDown, hCursorForward, hCursorBackward ::
+     Handle
+  -> Int -- Number of lines or characters to move
+  -> IO ()
+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
+
+cursorUp, cursorDown, cursorForward, cursorBackward ::
+     Int -- ^ Number of lines or characters to move
+  -> IO ()
+cursorUp = hCursorUp stdout
+cursorDown = hCursorDown stdout
+cursorForward = hCursorForward stdout
+cursorBackward = hCursorBackward stdout
+
+hCursorDownLine, hCursorUpLine ::
+     Handle
+  -> Int -- Number of lines to move
+  -> IO ()
+hCursorDownLine h n = hPutStr h $ cursorDownLineCode n
+hCursorUpLine h n = hPutStr h $ cursorUpLineCode n
+
+cursorDownLine, cursorUpLine ::
+     Int -- ^ Number of lines to move
+  -> IO ()
+cursorDownLine = hCursorDownLine stdout
+cursorUpLine = hCursorUpLine stdout
+
+hSetCursorColumn ::
+     Handle
+  -> Int -- 0-based column to move to
+  -> IO ()
+hSetCursorColumn h n = hPutStr h $ setCursorColumnCode n
+
+-- | Move the cursor to the specified column. The column numbering is 0-based
+-- (that is, the left-most column is numbered 0).
+setCursorColumn ::
+     Int -- ^ 0-based column to move to
+  -> IO ()
+setCursorColumn = hSetCursorColumn stdout
+
+hSetCursorPosition ::
+     Handle
+  -> Int -- 0-based row to move to
+  -> Int -- 0-based column to move to
+  -> IO ()
+hSetCursorPosition h n m = hPutStr h $ setCursorPositionCode n m
+
+-- | Move the cursor to the specified position (row and column). The position is
+-- 0-based (that is, the top-left corner is at row 0 column 0).
+setCursorPosition ::
+     Int -- ^ 0-based row to move to
+  -> Int -- ^ 0-based column to move to
+  -> IO ()
+setCursorPosition = hSetCursorPosition stdout
+
+hSaveCursor, hRestoreCursor, hReportCursorPosition :: Handle -> IO ()
+hSaveCursor h = hPutStr h saveCursorCode
+hRestoreCursor h = hPutStr h restoreCursorCode
+hReportCursorPosition h = hPutStr h reportCursorPositionCode
+
+-- | Save the cursor position in memory. The only way to access the saved value
+-- is with the 'restoreCursor' command.
+--
+-- @since 0.7.1
+saveCursor :: IO ()
+saveCursor = hSaveCursor stdout
+
+-- | Restore the cursor position from memory. There will be no value saved in
+-- memory until the first use of the 'saveCursor' command.
+--
+-- @since 0.7.1
+restoreCursor :: IO ()
+restoreCursor = hRestoreCursor stdout
+
+-- | Looking for a way to get the cursors position? See
+-- 'getCursorPosition'.
+--
+-- Emit the cursor position into the console input stream, immediately after
+-- being recognised on the output stream, as:
+-- @ESC [ \<cursor row> ; \<cursor column> R@
+--
+-- Note that the information that is emitted is 1-based (the top-left corner is
+-- at row 1 column 1) but 'setCursorColumn' and 'setCursorPosition' are
+-- 0-based.
+--
+-- In isolation of 'getReportedCursorPosition' or 'getCursorPosition', this
+-- function may be of limited use on Windows operating systems because of
+-- difficulties in obtaining the data emitted into the console input stream.
+--
+-- @since 0.7.1
+reportCursorPosition :: IO ()
+reportCursorPosition = hReportCursorPosition stdout
+
+hHideCursor, hShowCursor :: Handle -> IO ()
+hHideCursor h = hPutStr h hideCursorCode
+hShowCursor h = hPutStr h showCursorCode
+
+hideCursor, showCursor :: IO ()
+hideCursor = hHideCursor stdout
+showCursor = hShowCursor stdout
+
+hUseAlternateScreenBuffer :: Handle -> IO ()
+hUseAlternateScreenBuffer h = hPutStr h useAlternateScreenBufferCode
+
+hUseNormalScreenBuffer :: Handle -> IO ()
+hUseNormalScreenBuffer h = hPutStr h useNormalScreenBufferCode
+
+-- | Use the Alternate Screen Buffer. If currently using the Normal Screen
+-- Buffer, it will save the cursor position and switch to the Alternate Screen
+-- Buffer. It will always clear the Alternate Screen Buffer. The Alternate
+-- Screen Buffer has no scroll back facility.
+--
+-- It is an application's responsibility to ensure that it switches back to the
+-- Normal Screen Buffer if an exception is raised while the Alternate Screen
+-- Buffer is being used. For example, by using 'Control.Exception.bracket_':
+--
+-- > bracket_ useAlternateScreenBuffer useNormalScreenBuffer action
+--
+-- @since 0.11.4
+useAlternateScreenBuffer :: IO ()
+useAlternateScreenBuffer = hUseAlternateScreenBuffer stdout
+
+-- | Use the Normal Screen Buffer. If currently using the Alternate Screen
+-- Buffer, it will clear the Alternate Screen Buffer, and switch to the Normal
+-- Screen Buffer. It will always restore the saved cursor position.
+--
+-- @since 0.11.4
+useNormalScreenBuffer :: IO ()
+useNormalScreenBuffer = hUseNormalScreenBuffer stdout
+
+-- Introduce a hyperlink with (key, value) parameters. Some terminals support
+-- an @id@ parameter key, so that hyperlinks with the same @id@ value are
+-- treated as connected.
+--
+-- @since 0.11.3
+hHyperlinkWithParams::
+     Handle
+  -> [(String, String)]  -- Parameters
+  -> String              -- URI
+  -> String              -- Link text
+  -> IO ()
+hHyperlinkWithParams h params uri link =
+  hPutStr h $ hyperlinkWithParamsCode params uri link
+
+-- | Introduce a hyperlink with (key, value) parameters. Some terminals support
+-- an @id@ parameter key, so that hyperlinks with the same @id@ value are
+-- treated as connected.
+--
+-- @since 0.11.3
+hyperlinkWithParams ::
+     [(String, String)]  -- ^ Parameters
+  -> String              -- ^ URI
+  -> String              -- ^ Link text
+  -> IO ()
+hyperlinkWithParams = hHyperlinkWithParams stdout
+
+-- Introduce a hyperlink.
+--
+-- @since 0.11.3
+hHyperlink ::
+     Handle
+  -> String  -- URI
+  -> String  -- Link text
+  -> IO ()
+hHyperlink h = hHyperlinkWithParams h []
+
+-- | Introduce a hyperlink.
+--
+-- @since 0.11.3
+hyperlink ::
+     String  -- ^ URI
+  -> String  -- ^ Link text
+  -> IO ()
+hyperlink = hHyperlink stdout
+
+-- Introduce a hyperlink with an identifier for the link. Some terminals
+-- support an identifier, so that hyperlinks with the same identifier are
+-- treated as connected.
+--
+-- @since 0.11.3
+hHyperlinkWithId ::
+     Handle
+  -> String  -- Identifier for the link
+  -> String  -- URI
+  -> String  -- Link text
+  -> IO ()
+hHyperlinkWithId h linkId = hHyperlinkWithParams h [("id", linkId)]
+
+-- | Introduce a hyperlink with an identifier for the link. Some terminals
+-- support an identifier, so that hyperlinks with the same identifier are
+-- treated as connected.
+--
+-- @since 0.11.3
+hyperlinkWithId ::
+     String  -- ^ Identifier for the link
+  -> String  -- ^ URI
+  -> String  -- ^ Link text
+  -> IO ()
+hyperlinkWithId = hHyperlinkWithId stdout
+
+-- Set the terminal window title and icon name (that is, the text for the
+-- window in the Start bar, or similar).
+hSetTitle ::
+     Handle
+  -> String -- New window title and icon name
+  -> IO ()
+hSetTitle h title = hPutStr h $ setTitleCode title
+
+-- | Set the terminal window title and icon name (that is, the text for the
+-- window in the Start bar, or similar).
+setTitle :: String -- ^ New window title and icon name
+         -> IO ()
+setTitle = hSetTitle stdout
+
+-- | Use heuristics to determine whether the functions defined in this package
+-- will work with a given handle.
+--
+-- If the handle is not writable (that is, it cannot manage output - see
+-- 'System.IO.hIsWritable'), then @pure False@ is returned.
+--
+-- For Unix-like operating systems, the current implementation checks
+-- that: (1) the handle is a terminal; and (2) a @TERM@ environment variable is
+-- not set to @dumb@ (which is what the GNU Emacs text editor sets for its
+-- integrated terminal).
+--
+-- For Windows, the current implementation checks: first that (1) the handle is
+-- a terminal, (2) a @TERM@ environment variable is not set to @dumb@, and (3)
+-- the processing of \'ANSI\' control characters in output is enabled; and
+-- second, as an alternative, whether the handle is connected to a \'mintty\'
+-- terminal. (That is because the function 'System.IO.hIsTerminalDevice' is used
+-- to check if the handle is a terminal. However, where a non-native Windows
+-- terminal (such as \'mintty\') is implemented using redirection, that function
+-- will not identify a handle to the terminal as a terminal.) If it is not
+-- already enabled, this function does *not* enable the processing of \'ANSI\'
+-- control characters in output (see 'hNowSupportsANSI').
+--
+-- @since 0.6.2
+hSupportsANSI :: Handle -> IO Bool
+hSupportsANSI = Internal.hSupportsANSI
+
+-- | With one exception, equivalent to 'hSupportsANSI'. The exception is that,
+-- on Windows only, if a @TERM@ environment variable is not set to @dumb@ and
+-- the processing of \'ANSI\' control characters in output is not enabled, this
+-- function first tries to enable such processing.
+--
+-- @since 1.0.1
+hNowSupportsANSI :: Handle -> IO Bool
+hNowSupportsANSI = Internal.hNowSupportsANSI
+
+-- | 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.
+--
+-- @since 0.9
+hSupportsANSIColor :: Handle -> IO Bool
+hSupportsANSIColor = Internal.hSupportsANSIColor
+
+-- | Use heuristics to determine whether a given handle will support \'ANSI\'
+-- control characters in output. The function is consistent with
+-- 'hNowSupportsANSI'.
+--
+-- This function is deprecated as, from version 1.0, the package no longer
+-- supports legacy versions of Windows that required emulation.
+--
+-- @since 0.8.1
+{-# DEPRECATED hSupportsANSIWithoutEmulation "See Haddock documentation and hNowSupportsANSI." #-}
+hSupportsANSIWithoutEmulation :: Handle -> IO (Maybe Bool)
+hSupportsANSIWithoutEmulation h = Just <$> hNowSupportsANSI h
+
+-- | Parses the characters emitted by 'reportCursorPosition' into the console
+-- input stream. Returns the cursor row and column as a tuple.
+--
+-- For example, if the characters emitted by 'reportCursorPosition' are in
+-- 'String' @input@ then the parser could be applied like this:
+--
+-- > let result = readP_to_S cursorPosition input
+-- > case result of
+-- >     [] -> putStrLn $ "Error: could not parse " ++ show input
+-- >     [((row, column), _)] -> putStrLn $ "The cursor was at row " ++ show row
+-- >         ++ " and column" ++ show column ++ "."
+-- >     (_:_) -> putStrLn $ "Error: parse not unique"
+--
+-- @since 0.7.1
+cursorPosition :: ReadP (Int, Int)
+cursorPosition = do
+  void $ char '\ESC'
+  void $ char '['
+  row <- decimal -- A non-negative whole decimal number
+  void $ char ';'
+  col <- decimal -- A non-negative whole decimal number
+  void $ char 'R'
+  pure (read row, read col)
+ where
+  digit = satisfy isDigit
+  decimal = many1 digit
+
+-- | Attempts to get the reported cursor position data from the console input
+-- stream. The function is intended to be called immediately after
+-- 'reportCursorPosition' (or related functions) have caused characters to be
+-- emitted into the stream.
+--
+-- For example, on a Unix-like operating system:
+--
+-- > -- set no buffering (if 'no buffering' is not already set, the contents of
+-- > -- the buffer will be discarded, so this needs to be done before the cursor
+-- > -- positon is emitted)
+-- > hSetBuffering stdin NoBuffering
+-- > -- ensure that echoing is off
+-- > input <- bracket (hGetEcho stdin) (hSetEcho stdin) $ \_ -> do
+-- >   hSetEcho stdin False
+-- >   reportCursorPosition
+-- >   hFlush stdout -- ensure the report cursor position code is sent to the
+-- >                 -- operating system
+-- >   getReportedCursorPosition
+--
+-- On Windows operating systems, the function is not supported on consoles, such
+-- as mintty, that are not based on the Windows' Console API. (Command Prompt
+-- and PowerShell are based on the Console API.)
+--
+-- @since 0.7.1
+getReportedCursorPosition :: IO String
+getReportedCursorPosition = Internal.getReportedCursorPosition
+
+-- | Attempts to get the reported cursor position, combining the functions
+-- 'reportCursorPosition', 'getReportedCursorPosition' and 'cursorPosition'. Any
+-- position @(row, column)@ is translated to be 0-based (that is, the top-left
+-- corner is at @(0, 0)@), consistent with `setCursorColumn` and
+-- `setCursorPosition`. (Note that the information emitted into the console
+-- input stream by 'reportCursorPosition' is 1-based.) Returns 'Nothing' if any
+-- data emitted by 'reportCursorPosition', obtained by
+-- 'getReportedCursorPosition', cannot be parsed by 'cursorPosition'. Uses
+-- 'stdout'. If 'stdout' will be redirected, see 'hGetCursorPosition' for a more
+-- general function.
+--
+-- On Windows operating systems, the function is not supported on consoles, such
+-- as mintty, that are not based on the Windows' Console API. (Command Prompt
+-- and PowerShell are based on the Console API.)
+--
+-- This operation may fail with an error if 'stdin' has been redirected.
+--
+-- @since 0.10.3
+getCursorPosition :: IO (Maybe (Int, Int))
+getCursorPosition = hGetCursorPosition stdout
+
+-- | Attempts to get the reported cursor position, combining the functions
+-- 'hReportCursorPosition' (with the specified handle),
+-- 'getReportedCursorPosition' and 'cursorPosition'. Any position
+-- @(row, column)@ is translated to be 0-based (that is, the top-left corner is
+-- at @(0, 0)@), consistent with 'hSetCursorColumn' and 'hSetCursorPosition'.
+-- (Note that the information emitted into the console input stream by
+-- 'hReportCursorPosition' is 1-based.) Returns 'Nothing' if any data emitted by
+-- 'hReportCursorPosition', obtained by 'getReportedCursorPosition', cannot be
+-- parsed by 'cursorPosition'.
+--
+-- On Windows operating systems, the function is not supported on consoles, such
+-- as mintty, that are not based on the Windows' Console API. (Command Prompt
+-- and PowerShell are based on the Console API.)
+--
+-- This operation may fail with an error if 'stdin' has been redirected.
+--
+-- @since 0.10.1
+hGetCursorPosition :: Handle -> IO (Maybe (Int, Int))
+hGetCursorPosition h = fmap to0base <$> getCursorPosition'
+ where
+  to0base (row, col) = (row - 1, col - 1)
+  getCursorPosition' = do
+    input <- bracket (hGetBuffering stdin) (hSetBuffering stdin) $ \_ -> do
+      -- set no buffering (if 'no buffering' is not already set, the contents of
+      -- the buffer will be discarded, so this needs to be done before the
+      -- cursor positon is emitted)
+      hSetBuffering stdin NoBuffering
+      -- ensure that echoing is off
+      bracket (hGetEcho stdin) (hSetEcho stdin) $ \_ -> do
+        hSetEcho stdin False
+        clearStdin
+        hReportCursorPosition h
+        -- Ensure the report cursor position code is sent to the operating
+        -- system:
+        hFlush h
+        -- On Windows and in Windows Terminal v1.22.12111.0 (at least), it seems
+        -- that a moment's delay is required in order to allow sufficient time
+        -- for the reported information to reach the standard input channel:
+        threadDelay 1
+        getReportedCursorPosition
+    case readP_to_S cursorPosition input of
+      [] -> pure Nothing
+      [((row, col),_)] -> pure $ Just (row, col)
+      (_:_) -> pure Nothing
+  clearStdin = do
+    isReady <- hReady stdin
+    when isReady $ do
+      _ <-getChar
+      clearStdin
+
+-- | Looking for a way to get layer colors? See 'getLayerColor'.
+--
+-- Emit the layerColor into the console input stream, immediately after
+-- being recognised on the output stream, as:
+-- @ESC ] \<Ps> ; rgb: \<red> ; \<green> ; \<blue> \<ST>@
+-- where @\<Ps>@ is @10@ for 'Foreground' and @11@ for 'Background'; @\<red>@,
+-- @\<green>@ and @\<blue>@ are the color channel values in hexadecimal (4, 8,
+-- 12 and 16 bit values are possible, although 16 bit values are most common);
+-- and @\<ST>@ is the STRING TERMINATOR (ST). ST depends on the terminal
+-- software and may be the @BEL@ character or @ESC \\@ characters.
+--
+-- This function may be of limited, or no, use on Windows operating systems
+-- because (1) the function is not supported on native terminals and is
+-- emulated, but the emulation does not work on Windows Terminal and (2) of
+-- difficulties in obtaining the data emitted into the console input stream.
+--
+-- Underlining is not supported.
+--
+-- @since 0.11.4
+reportLayerColor :: ConsoleLayer -> IO ()
+reportLayerColor = hReportLayerColor stdout
+
+-- @since 0.11.4
+hReportLayerColor :: Handle -> ConsoleLayer -> IO ()
+hReportLayerColor h layer = hPutStr h $ reportLayerColorCode layer
+
+-- | Attempts to get the reported layer color data from the console input
+-- stream. The function is intended to be called immediately after
+-- 'reportLayerColor' (or related functions) have caused characters to be
+-- emitted into the stream.
+--
+-- For example, on a Unix-like operating system:
+--
+-- > -- set no buffering (if 'no buffering' is not already set, the contents of
+-- > -- the buffer will be discarded, so this needs to be done before the cursor
+-- > -- positon is emitted)
+-- > hSetBuffering stdin NoBuffering
+-- > -- ensure that echoing is off
+-- > input <- bracket (hGetEcho stdin) (hSetEcho stdin) $ \_ -> do
+-- >   hSetEcho stdin False
+-- >   reportLayerColor Foreground
+-- >   hFlush stdout -- ensure the report cursor position code is sent to the
+-- >                 -- operating system
+-- >   getReportedLayerColor Foreground
+--
+-- On Windows operating systems, the function is not supported on consoles, such
+-- as mintty, that are not based on the Windows' Console API. (Command Prompt
+-- and PowerShell are based on the Console API.)
+--
+-- Underlining is not supported.
+--
+-- @since 0.11.4
+getReportedLayerColor :: ConsoleLayer -> IO String
+getReportedLayerColor = Internal.getReportedLayerColor
+
+-- | Attempts to get the reported layer color, combining the functions
+-- 'reportLayerColor', 'getReportedLayerColor' and 'layerColor'. Any RGB color
+-- is scaled to be 16 bits per channel, the most common format reported by
+-- terminal software. Returns 'Nothing' if any data emitted by
+-- 'reportLayerColor', obtained by 'getReportedLayerColor', cannot be parsed by
+-- 'layerColor'. Uses 'stdout'. If 'stdout' will be redirected, see
+-- 'hGetLayerColor' for a more general function.
+--
+-- On Windows operating systems, the function is not supported on consoles, such
+-- as mintty, that are not based on the Windows' Console API. (Command Prompt
+-- and PowerShell are based on the Console API.) This function also relies on
+-- emulation that does not work on Windows Terminal.
+--
+-- Underlining is not supported.
+--
+-- This operation may fail with an error if 'stdin' has been redirected.
+--
+-- @since 0.11.4
+getLayerColor :: ConsoleLayer -> IO (Maybe(RGB Word16))
+getLayerColor = hGetLayerColor stdout
+
+-- | Attempts to get the reported layer color, combining the functions
+-- 'hReportLayerColor', 'getReportedLayerColor' and 'layerColor'. Any RGB color
+-- is scaled to be 16 bits per channel, the most common format reported by
+-- terminal software. Returns 'Nothing' if any data emitted by
+-- 'hReportLayerColor', obtained by 'getReportedLayerColor', cannot be parsed by
+-- 'layerColor'.
+--
+-- On Windows operating systems, the function is not supported on consoles, such
+-- as mintty, that are not based on the Windows' Console API. (Command Prompt
+-- and PowerShell are based on the Console API.) This function also relies on
+-- emulation that does not work on Windows Terminal.
+--
+-- Underlining is not supported.
+--
+-- This operation may fail with an error if 'stdin' has been redirected.
+--
+-- @since 0.11.4
+hGetLayerColor :: Handle -> ConsoleLayer -> IO (Maybe (RGB Word16))
+hGetLayerColor h layer = do
+  input <- bracket (hGetBuffering stdin) (hSetBuffering stdin) $ \_ -> do
+    -- set no buffering (if 'no buffering' is not already set, the contents of
+    -- the buffer will be discarded, so this needs to be done before the
+    -- cursor positon is emitted)
+    hSetBuffering stdin NoBuffering
+    -- ensure that echoing is off
+    bracket (hGetEcho stdin) (hSetEcho stdin) $ \_ -> do
+      hSetEcho stdin False
+      clearStdin
+      hReportLayerColor h layer
+      -- Ensure the report cursor position code is sent to the operating system:
+      hFlush h
+      -- On Windows and in Windows Terminal v1.22.12111.0 (at least), it seems
+      -- that a moment's delay is required in order to allow sufficient time for
+      -- the reported information to reach the standard input channel:
+      threadDelay 1
+      getReportedLayerColor layer
+  case readP_to_S (layerColor layer) input of
+      [] -> pure Nothing
+      [(col, _)] -> pure $ Just col
+      (_:_) -> pure Nothing
+ where
+  clearStdin = do
+    isReady <- hReady stdin
+    when isReady $ do
+      _ <-getChar
+      clearStdin
+
+-- | Parses the characters emitted by 'reportLayerColor' into the console input
+-- stream.
+--
+-- For example, if the characters emitted by 'reportLayerColor' are in 'String'
+-- @input@ then the parser could be applied like this:
+--
+-- > let result = readP_to_S (layerColor layer) input
+-- > case result of
+-- >     [] -> putStrLn $ "Error: could not parse " ++ show input
+-- >     [(col, _)] -> putStrLn $ "The color was " ++ show col ++ "."
+-- >     (_:_) -> putStrLn $ "Error: parse not unique"
+--
+-- Underlining is not supported.
+--
+-- @since 0.11.4
+layerColor :: ConsoleLayer -> ReadP (RGB Word16)
+layerColor layer = do
+  void $ string "\ESC]"
+  void $ string $ case layer of
+    Foreground -> "10"
+    Background -> "11"
+    Underlining -> fail "reportLayerColor does not support underlining"
+  void $ string ";rgb:"
+  redHex <- hexadecimal -- A non-negative whole hexadecimal number
+  void $ char '/'
+  greenHex <- hexadecimal -- A non-negative whole hexadecimal number
+  void $ char '/'
+  blueHex <- hexadecimal -- A non-negative whole hexadecimal number
+  void $ string "\BEL" <++ string "\ESC\\"
+  let lenRed = length redHex
+      lenGreen = length greenHex
+      lenBlue = length blueHex
+  if lenRed == lenGreen && lenGreen == lenBlue
+    then
+      if lenRed == 0 || lenRed > 4
+        then fail "Color format not recognised"
+        else
+          let m = 16 ^ (4 - lenRed)
+              r = fromIntegral $ m * hexToInt redHex
+              g = fromIntegral $ m * hexToInt greenHex
+              b = fromIntegral $ m * hexToInt blueHex
+          in  pure $ RGB r g b
+    else fail "Color format not recognised"
+ where
+  hexDigit = satisfy isHexDigit
+  hexadecimal = many1 hexDigit
+  hexToInt hex = foldl (\d a -> d * 16 + a) 0 (map digitToInt hex)
+
+-- | Attempts to get the current terminal size (height in rows, width in
+-- columns).
+--
+-- There is no \'ANSI\' control character sequence that reports the terminal
+-- size. So, it attempts to set the cursor position beyond the bottom right
+-- corner of the terminal and then use 'getCursorPosition' to query the console
+-- input stream. It works only on terminals that support each step and if data
+-- can be emitted to 'stdin'. (Use 'System.IO.hIsTerminalDevice' to test if
+-- 'stdin' is connected to a terminal.) Uses 'stdout'. If 'stdout' will be
+-- redirected, see 'System.IO.hGetTerminalSize' for a more general function.
+--
+-- On Windows operating systems, the function is not supported on consoles, such
+-- as mintty, that are not based on Windows' Console API. (Command Prompt and
+-- PowerShell are based on the Console API.)
+--
+-- This operation may fail with an error if 'stdin' has been redirected.
+--
+-- For a different approach, one that does not use control character sequences
+-- and works when 'stdin' is redirected, see the
+-- <https://hackage.haskell.org/package/terminal-size terminal-size> package.
+--
+-- @since 0.9
+getTerminalSize :: IO (Maybe (Int, Int))
+getTerminalSize = hGetTerminalSize stdout
+
+-- | Attempts to get the current terminal size (height in rows, width in
+-- columns), by writing control character sequences to the specified handle
+-- (which will typically be 'stdout' or 'System.IO.stderr').
+--
+-- There is no \'ANSI\' control character sequence that reports the terminal
+-- size. So, it attempts to set the cursor position beyond the bottom right
+-- corner of the terminal and then use 'hGetCursorPosition' to query the console
+-- input stream. It works only on terminals that support each step and if data
+-- can be emitted to 'stdin'. (Use 'System.IO.hIsTerminalDevice' to test if
+-- 'stdin' is connected to a terminal.)
+--
+-- On Windows operating systems, the function is not supported on consoles, such
+-- as mintty, that are not based on the Windows' Console API. (Command Prompt
+-- and PowerShell are based on the Console API.)
+--
+-- This operation may fail with an error if 'stdin' has been redirected.
+--
+-- For a different approach, one that does not use control character sequences
+-- and works when 'stdin' is redirected, see the
+-- <https://hackage.haskell.org/package/terminal-size terminal-size> package.
+--
+-- @since 0.10.1
+hGetTerminalSize :: Handle -> IO (Maybe (Int, Int))
+hGetTerminalSize h = do
+  hSaveCursor h
+  hSetCursorPosition h 9999 9999  -- Attempt to set the cursor position beyond
+                                  -- the bottom right corner of the terminal.
+  mPos <- hGetCursorPosition h
+  hRestoreCursor h
+  hFlush h -- ensure the restore cursor position code is sent to the
+           -- operating system
+  pure $ fmap (\(r, c) -> (r + 1, c + 1)) mPos
+
+-- Set the Select Graphic Rendition mode
+hSetSGR ::
+     Handle
+  -> [SGR] -- Commands: these will typically be applied on top of the
+           -- current console SGR mode. An empty list of commands is
+           -- equivalent to the list @[Reset]@. Commands are applied left to
+           -- right.
+  -> IO ()
+hSetSGR h sgrs = hPutStr h $ setSGRCode sgrs
+
+-- | Set the Select Graphic Rendition mode
+setSGR ::
+     [SGR] -- ^ Commands: these will typically be applied on top of the
+           -- current console SGR mode. An empty list of commands is
+           -- equivalent to the list @[Reset]@. Commands are applied left to
+           -- right.
+  -> IO ()
+setSGR = hSetSGR stdout
+
+hClearFromCursorToScreenEnd, hClearFromCursorToScreenBeginning, hClearScreen ::
+     Handle
+  -> IO ()
+hClearFromCursorToScreenEnd h = hPutStr h clearFromCursorToScreenEndCode
+hClearFromCursorToScreenBeginning h
+    = hPutStr h clearFromCursorToScreenBeginningCode
+hClearScreen h = hPutStr h clearScreenCode
+
+clearFromCursorToScreenEnd, clearFromCursorToScreenBeginning, clearScreen :: IO ()
+clearFromCursorToScreenEnd = hClearFromCursorToScreenEnd stdout
+clearFromCursorToScreenBeginning = hClearFromCursorToScreenBeginning stdout
+clearScreen = hClearScreen stdout
+
+hClearFromCursorToLineEnd, hClearFromCursorToLineBeginning, hClearLine ::
+     Handle
+  -> IO ()
+hClearFromCursorToLineEnd h = hPutStr h clearFromCursorToLineEndCode
+hClearFromCursorToLineBeginning h = hPutStr h clearFromCursorToLineBeginningCode
+hClearLine h = hPutStr h clearLineCode
+
+clearFromCursorToLineEnd, clearFromCursorToLineBeginning, clearLine :: IO ()
+clearFromCursorToLineEnd = hClearFromCursorToLineEnd stdout
+clearFromCursorToLineBeginning = hClearFromCursorToLineBeginning stdout
+clearLine = hClearLine stdout
+
+hEnableLineWrap, hDisableLineWrap ::
+     Handle
+  -> IO ()
+hEnableLineWrap h = hPutStr h enableLineWrapCode
+hDisableLineWrap h = hPutStr h disableLineWrapCode
+
+enableLineWrap, disableLineWrap :: IO ()
+enableLineWrap = hEnableLineWrap stdout
+disableLineWrap = hDisableLineWrap stdout
+
+hScrollPageUp, hScrollPageDown ::
+     Handle
+  -> Int -- Number of lines to scroll by
+  -> IO ()
+hScrollPageUp h n = hPutStr h $ scrollPageUpCode n
+hScrollPageDown h n = hPutStr h $ scrollPageDownCode n
+
+scrollPageUp, scrollPageDown ::
+     Int -- ^ Number of lines to scroll by
+  -> IO ()
+scrollPageUp = hScrollPageUp stdout
+scrollPageDown = hScrollPageDown stdout
+ unix/System/Console/ANSI/Internal.hs view
@@ -0,0 +1,109 @@+{-# LANGUAGE Trustworthy #-}
+
+module System.Console.ANSI.Internal
+  ( getReportedCursorPosition
+  , getReportedLayerColor
+  , hSupportsANSI
+  , hNowSupportsANSI
+  , hSupportsANSIColor
+  ) where
+
+import Data.List ( uncons )
+import Data.Maybe ( fromMaybe, isJust, mapMaybe )
+import System.Environment ( lookupEnv )
+import System.IO ( Handle, hIsTerminalDevice, hIsWritable )
+import System.IO.Unsafe ( unsafePerformIO )
+import System.Timeout ( timeout )
+
+import System.Console.ANSI.Types ( ConsoleLayer (..) )
+
+getReportedCursorPosition :: IO String
+getReportedCursorPosition = getReport "\ESC[" ["R"]
+
+getReportedLayerColor :: ConsoleLayer -> IO String
+getReportedLayerColor Underlining =
+  error "getReportedLayerColor does not support underlining."
+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
+  -- If, unexpectedly, no data is available on the console input stream then
+  -- the timeout will prevent the getChar blocking. For consistency with the
+  -- Windows equivalent, returns "" if the expected information is unavailable.
+  fromMaybe "" <$> timeout 500000 (getStart startChars "") -- 500 milliseconds
+ where
+  endChars' = mapMaybe uncons endChars
+
+  -- The list is built in reverse order, in order to avoid O(n^2) complexity.
+  -- So, getReport yields the reversed built list.
+
+  getStart :: String -> String -> IO String
+  getStart "" r = getRest r
+  getStart (h:hs) r = do
+    c <- getChar
+    if c == h
+      then getStart hs (c:r) -- Try to get the rest of the start characters
+      else pure $ reverse (c:r) -- If the first character(s) are not the
+                                  -- expected start then give up. This provides
+                                  -- a modicom of protection against unexpected
+                                  -- data in the input stream.
+  getRest :: String -> IO String
+  getRest r = do
+    c <- getChar
+    case lookup c endChars' of
+      Nothing -> getRest (c:r) -- Continue building the list, until the first of
+                               -- the end characters is obtained.
+      Just es -> getEnd es (c:r) -- Try to get the rest of the end characters.
+
+  getEnd :: String -> String -> IO String
+  getEnd "" r = pure $ reverse r
+  getEnd (e:es) r = do
+    c <- getChar
+    if c /= e
+      then getRest (c:r) -- Continue building the list, with the original end
+                         -- characters.
+      else getEnd es (c:r) -- Continue building the list, checking against the
+                           -- remaining end characters.
+
+hSupportsANSI :: Handle -> IO Bool
+-- Borrowed from an HSpec patch by Simon Hengel
+-- (https://github.com/hspec/hspec/commit/d932f03317e0e2bd08c85b23903fb8616ae642bd)
+hSupportsANSI h = (&&) <$> hIsWritable h <*> hSupportsANSI'
+ where
+  hSupportsANSI' = (&& isNotDumb) <$> hIsTerminalDevice h
+
+hNowSupportsANSI :: Handle -> IO Bool
+hNowSupportsANSI = hSupportsANSI
+
+hSupportsANSIColor :: Handle -> IO Bool
+hSupportsANSIColor h = (|| isEmacsTerm) <$> hSupportsANSI h
+ where
+  isEmacsTerm = insideEmacs && isDumb
+  isDumb = not isNotDumb
+
+-- | This function assumes that once it is first established whether or not the
+-- TERM environment variable exists with contents dumb, that will not change.
+-- This approach is taken because the use of C function setenv() in one thread
+-- can cause other threads calling C function getenv() to crash. On Unix-like
+-- operating systems, System.Environment.lookupEnv is implemented using C
+-- function getenv().
+isNotDumb :: Bool
+isNotDumb = unsafePerformIO (lookupEnv "TERM") /= Just "dumb"
+
+{-# NOINLINE isNotDumb #-}
+
+-- | This function assumes that once it is first established whether or not the
+-- INSIDE_EMACS environment variable exists, that will not change. This approach
+-- is taken because the use of C function setenv() in one thread can cause other
+-- threads calling C function getenv() to crash. On Unix-like operating systems,
+-- System.Environment.lookupEnv is implemented using C function getenv().
+insideEmacs :: Bool
+insideEmacs = isJust $ unsafePerformIO (lookupEnv "INSIDE_EMACS")
+
+{-# NOINLINE insideEmacs #-}
+ win/System/Console/ANSI/Internal.hs view
@@ -0,0 +1,113 @@+{-# LANGUAGE Safe #-}
+
+module System.Console.ANSI.Internal
+  ( getReportedCursorPosition
+  , getReportedLayerColor
+  , hNowSupportsANSI
+  , hSupportsANSI
+  , hSupportsANSIColor
+  ) where
+
+import Control.Exception ( IOException, SomeException, catch, try )
+import Data.Bits ( (.&.), (.|.) )
+import Data.Maybe ( mapMaybe )
+import System.Environment ( getEnvironment, 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, eNABLE_VIRTUAL_TERMINAL_PROCESSING
+         , getConsoleMode, getNumberOfConsoleInputEvents, iNVALID_HANDLE_VALUE
+         , nullHANDLE, readConsoleInput, setConsoleMode, unicodeAsciiChar
+         )
+import System.Console.ANSI.Windows.Win32.MinTTY ( isMinTTYHandle )
+import System.Console.ANSI.Windows.Win32.Types
+         ( DWORD, HANDLE, 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 = hSupportsANSI' False
+
+hNowSupportsANSI :: Handle -> IO Bool
+hNowSupportsANSI = hSupportsANSI' True
+
+hSupportsANSI' :: Bool -> Handle -> IO Bool
+hSupportsANSI' tryToEnable handle = do
+  isWritable <- hIsWritable handle
+  if isWritable
+    then withHandleToHANDLE handle $ withHANDLE
+      (pure False) -- Invalid handle or no handle
+      ( \h -> do
+          tryMode <- try (getConsoleMode h) :: IO (Either SomeException DWORD)
+          case tryMode of
+            Left _ -> isMinTTYHandle h -- No ConHost mode
+            Right mode -> do
+              let isVTEnabled = mode .&. eNABLE_VIRTUAL_TERMINAL_PROCESSING /= 0
+                  isNotDumb = (/= Just "dumb") <$> lookupEnv "TERM"
+              isTDNotDumb <- (&&) <$> hIsTerminalDevice handle <*> isNotDumb
+              if isTDNotDumb && not isVTEnabled && tryToEnable
+                then do
+                  let mode' = mode .|. eNABLE_VIRTUAL_TERMINAL_PROCESSING
+                  trySetMode <- try (setConsoleMode h mode')
+                    :: IO (Either SomeException ())
+                  case trySetMode of
+                    Left _ -> pure False -- Can't enable VT processing
+                    Right () -> pure True -- VT processing enabled
+                else pure $ isTDNotDumb && isVTEnabled
+      )
+    else pure False
+
+-- | This function applies another to the Windows handle, if the handle is
+-- valid. If it is invalid, the specified default action is returned.
+withHANDLE :: IO a -> (HANDLE -> IO a) -> HANDLE -> IO a
+withHANDLE invalid action h =
+  if h == iNVALID_HANDLE_VALUE || h == nullHANDLE
+    then invalid  -- Invalid handle or no handle
+    else action h
+
+hSupportsANSIColor :: Handle -> IO Bool
+hSupportsANSIColor h = (||) <$> hSupportsANSI h <*> isEmacsTerm
+  where
+    isEmacsTerm = (\env -> insideEmacs env && isDumb env) <$> getEnvironment
+    insideEmacs = any (\(k, _) -> k == "INSIDE_EMACS")
+    isDumb env = Just "dumb" == lookup "TERM" env
+ win/System/Console/ANSI/Windows/Foreign.hs view
@@ -0,0 +1,494 @@+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE RankNTypes         #-}
+{-# LANGUAGE Trustworthy        #-}
+
+module System.Console.ANSI.Windows.Foreign
+  (
+    INPUT_RECORD (..)
+  , INPUT_RECORD_EVENT (..)
+  , KEY_EVENT_RECORD (..)
+  , getNumberOfConsoleInputEvents
+  , readConsoleInput
+  , cWcharsToChars
+  , unicodeAsciiChar
+  , eNABLE_VIRTUAL_TERMINAL_PROCESSING
+  , iNVALID_HANDLE_VALUE
+  , nullHANDLE
+  , getConsoleMode
+  , setConsoleMode
+  ) where
+
+import Control.Exception ( Exception )
+import Data.Char ( chr )
+import Data.Typeable ( Typeable )
+import Data.Word ( Word32 )
+import Foreign.C.Types ( CWchar (..) )
+import Foreign.Marshal.Alloc ( alloca )
+import Foreign.Marshal.Array ( allocaArray, peekArray, pokeArray )
+import Foreign.Ptr ( Ptr, castPtr, plusPtr, nullPtr )
+import Foreign.Storable ( Storable (..) )
+import System.Console.ANSI.Windows.Win32.Types
+         ( BOOL, DWORD, ErrCode, HANDLE, LPDWORD, SHORT, UINT, UINT_PTR, ULONG
+         , WCHAR, WORD, failIfFalse_
+         )
+
+peekAndOffset :: Storable a => Ptr a -> IO (a, Ptr b)
+peekAndOffset ptr = do
+  item <- peek ptr
+  pure (item, ptr `plusPtr` sizeOf item)
+
+pokeAndOffset :: Storable a => Ptr a -> a -> IO (Ptr b)
+pokeAndOffset ptr item = do
+  poke ptr item
+  pure (ptr `plusPtr` sizeOf item)
+
+data COORD = COORD
+  { coord_x :: SHORT
+  , coord_y :: SHORT
+  } deriving (Read, Eq)
+
+instance Show COORD where
+  show (COORD x y) = "(" ++ show x ++ ", " ++ show y ++ ")"
+
+instance Storable COORD where
+  sizeOf ~(COORD x y) = sizeOf x + sizeOf y
+  alignment ~(COORD x _) = alignment x
+  peek ptr = do
+    let ptr' = castPtr ptr :: Ptr SHORT
+    x <- peekElemOff ptr' 0
+    y <- peekElemOff ptr' 1
+    pure (COORD x y)
+  poke ptr (COORD x y) = do
+    let ptr' = castPtr ptr :: Ptr SHORT
+    pokeElemOff ptr' 0 x
+    pokeElemOff ptr' 1 y
+
+data SMALL_RECT = SMALL_RECT COORD COORD
+
+instance Show SMALL_RECT where
+  show (SMALL_RECT tl br) = show tl ++ "-" ++ show br
+
+instance Storable SMALL_RECT where
+  sizeOf ~(SMALL_RECT tl br) = sizeOf tl + sizeOf br
+  alignment ~(SMALL_RECT tl _) = alignment tl
+  peek ptr = do
+    let ptr' = castPtr ptr :: Ptr COORD
+    tl <- peekElemOff ptr' 0
+    br <- peekElemOff ptr' 1
+    pure (SMALL_RECT tl br)
+  poke ptr (SMALL_RECT tl br) = do
+    let ptr' = castPtr ptr :: Ptr COORD
+    pokeElemOff ptr' 0 tl
+    pokeElemOff ptr' 1 br
+
+data CONSOLE_CURSOR_INFO = CONSOLE_CURSOR_INFO
+  { cci_cursor_size    :: DWORD
+  , cci_cursor_visible :: BOOL
+  } deriving (Show)
+
+instance Storable CONSOLE_CURSOR_INFO where
+  sizeOf ~(CONSOLE_CURSOR_INFO size visible) = sizeOf size + sizeOf visible
+  alignment ~(CONSOLE_CURSOR_INFO size _) = alignment size
+  peek ptr = do
+    (size, ptr') <- peekAndOffset (castPtr ptr)
+    visible <- peek ptr'
+    pure (CONSOLE_CURSOR_INFO size visible)
+  poke ptr (CONSOLE_CURSOR_INFO size visible) = do
+    ptr' <- pokeAndOffset (castPtr ptr) size
+    poke ptr' visible
+
+data CONSOLE_SCREEN_BUFFER_INFO = CONSOLE_SCREEN_BUFFER_INFO
+  { csbi_size                :: COORD
+  , csbi_cursor_position     :: COORD
+  , csbi_attributes          :: WORD
+  , csbi_window              :: SMALL_RECT
+  , csbi_maximum_window_size :: COORD
+  } deriving (Show)
+
+instance Storable CONSOLE_SCREEN_BUFFER_INFO where
+  sizeOf ~(CONSOLE_SCREEN_BUFFER_INFO
+    size cursor_position attributes window maximum_window_size)
+    = sizeOf size + sizeOf cursor_position + sizeOf attributes + sizeOf window
+        + sizeOf maximum_window_size
+  alignment ~(CONSOLE_SCREEN_BUFFER_INFO size _ _ _ _) = alignment size
+  peek ptr = do
+    (size, ptr1) <- peekAndOffset (castPtr ptr)
+    (cursor_position, ptr2) <- peekAndOffset ptr1
+    (attributes, ptr3) <- peekAndOffset ptr2
+    (window, ptr4) <- peekAndOffset ptr3
+    maximum_window_size <- peek ptr4
+    pure (CONSOLE_SCREEN_BUFFER_INFO
+      size cursor_position attributes window maximum_window_size)
+  poke ptr (CONSOLE_SCREEN_BUFFER_INFO
+    size cursor_position attributes window maximum_window_size)
+    = do
+      ptr1 <- pokeAndOffset (castPtr ptr) size
+      ptr2 <- pokeAndOffset ptr1 cursor_position
+      ptr3 <- pokeAndOffset ptr2 attributes
+      ptr4 <- pokeAndOffset ptr3 window
+      poke ptr4 maximum_window_size
+
+data CONSOLE_SCREEN_BUFFER_INFOEX = CONSOLE_SCREEN_BUFFER_INFOEX
+  { csbix_size                 :: COORD
+  , csbix_cursor_position      :: COORD
+  , csbix_attributes           :: WORD
+  , csbix_window               :: SMALL_RECT
+  , csbix_maximum_window_size  :: COORD
+  , csbix_popup_attributes     :: WORD
+  , csbix_fullscreen_supported :: BOOL
+  , csbix_color_table          :: [COLORREF]
+  } deriving (Show)
+
+-- When specifying an explicit RGB color, the COLORREF value has the following
+-- hexadecimal form:
+-- 0x00bbggrr
+-- The low-order byte contains a value for the relative intensity of red; the
+-- second byte contains a value for green; and the third byte contains a value
+-- for blue. The high-order byte must be zero. The maximum value for a single
+-- byte is 0xFF.
+type COLORREF = Word32
+
+instance Storable CONSOLE_SCREEN_BUFFER_INFOEX where
+  sizeOf ~(CONSOLE_SCREEN_BUFFER_INFOEX
+    size cursor_position attributes window maximum_window_size popup_attributes
+      fullscreen_supported _)
+    = sizeOf sizeCsbix + sizeOf size + sizeOf cursor_position + sizeOf attributes + sizeOf window
+        + sizeOf maximum_window_size + sizeOf popup_attributes
+        + sizeOf fullscreen_supported + 16 * sizeOf (undefined :: COLORREF)
+  alignment ~(CONSOLE_SCREEN_BUFFER_INFOEX _ _ _ _ _ _ _ _) = alignment sizeCsbix
+  peek ptr = do
+    let ptr0 = castPtr ptr `plusPtr` sizeOf sizeCsbix
+    (size, ptr1) <- peekAndOffset ptr0
+    (cursor_position, ptr2) <- peekAndOffset ptr1
+    (attributes, ptr3) <- peekAndOffset ptr2
+    (window, ptr4) <- peekAndOffset ptr3
+    (maximum_window_size, ptr5) <- peekAndOffset ptr4
+    (popup_attributes, ptr6) <- peekAndOffset ptr5
+    (fullscreen_supported, ptr7) <- peekAndOffset ptr6
+    color_table <- peekArray 16 ptr7
+    pure (CONSOLE_SCREEN_BUFFER_INFOEX
+      size cursor_position attributes window maximum_window_size
+      popup_attributes fullscreen_supported color_table)
+  poke ptr (CONSOLE_SCREEN_BUFFER_INFOEX
+    size cursor_position attributes window maximum_window_size popup_attributes
+    fullscreen_supported color_table)
+    = do
+      ptr0 <- pokeAndOffset (castPtr ptr) sizeCsbix
+      ptr1 <- pokeAndOffset ptr0 size
+      ptr2 <- pokeAndOffset ptr1 cursor_position
+      ptr3 <- pokeAndOffset ptr2 attributes
+      ptr4 <- pokeAndOffset ptr3 window
+      ptr5 <- pokeAndOffset ptr4 maximum_window_size
+      ptr6 <- pokeAndOffset ptr5 popup_attributes
+      ptr7 <- pokeAndOffset ptr6 fullscreen_supported
+      pokeArray ptr7 color_table'
+   where
+    color_table' = take 16 $ color_table ++ repeat 0
+
+sizeCsbix :: ULONG
+sizeCsbix = fromIntegral $
+  sizeOf (undefined :: CONSOLE_SCREEN_BUFFER_INFOEX)
+
+data CHAR_INFO = CHAR_INFO
+  { ci_char       :: WCHAR
+  , ci_attributes :: WORD
+  } deriving (Show)
+
+instance Storable CHAR_INFO where
+  sizeOf ~(CHAR_INFO char attributes) = sizeOf char + sizeOf attributes
+  alignment ~(CHAR_INFO char _) = alignment char
+  peek ptr = do
+    (char, ptr') <- peekAndOffset (castPtr ptr)
+    attributes <- peek ptr'
+    pure (CHAR_INFO char attributes)
+  poke ptr (CHAR_INFO char attributes) = do
+    ptr' <- pokeAndOffset (castPtr ptr) char
+    poke ptr' attributes
+
+kEY_EVENT, mOUSE_EVENT, wINDOW_BUFFER_SIZE_EVENT, mENU_EVENT,
+  fOCUS_EVENT :: WORD
+kEY_EVENT                =  1
+mOUSE_EVENT              =  2
+wINDOW_BUFFER_SIZE_EVENT =  4
+mENU_EVENT               =  8
+fOCUS_EVENT              = 16
+
+foreign import ccall unsafe "windows.h GetNumberOfConsoleInputEvents"
+  cGetNumberOfConsoleInputEvents :: HANDLE -> Ptr DWORD -> IO BOOL
+foreign import ccall unsafe "windows.h ReadConsoleInputW"
+  cReadConsoleInput :: HANDLE
+                    -> Ptr INPUT_RECORD
+                    -> DWORD
+                    -> LPDWORD
+                    -> IO BOOL
+
+data ConsoleException = ConsoleException !ErrCode deriving (Eq, Typeable)
+
+instance Show ConsoleException where
+  show (ConsoleException 6) =
+    "A fatal error has occurred.\n\n" ++
+    "An attempt has been made to send console virtual terminal sequences\n" ++
+    "(ANSI codes) to an output that has not been recognised as an\n" ++
+    "ANSI-capable terminal and also cannot be emulated as an ANSI-enabled\n" ++
+    "terminal (emulation needs a ConHost-based terminal, such as Command\n" ++
+    "Prompt or PowerShell). That may occur, for example, if output has\n" ++
+    "been redirected to a file.\n\n" ++
+    "If that is unexpected, please post an issue at:\n" ++
+    "https://github.com/UnkindPartition/ansi-terminal/issues\n"
+  show (ConsoleException errCode) = "ConsoleException " ++ show errCode
+
+instance Exception ConsoleException
+
+returnWith_ :: Storable a => (Ptr a -> IO b) -> IO a
+returnWith_ act = alloca $ \ptr -> act ptr >> peek ptr
+
+{-
+typedef union _UNICODE_ASCII_CHAR {
+    WCHAR UnicodeChar;
+    CHAR  AsciiChar;
+} UNICODE_ASCII_CHAR;
+-}
+newtype UNICODE_ASCII_CHAR = UnicodeAsciiChar
+  { unicodeAsciiChar :: WCHAR
+  } deriving (Show, Read, Eq)
+
+instance Storable UNICODE_ASCII_CHAR where
+  sizeOf _    = 2
+  alignment _ = 2
+  peek ptr = UnicodeAsciiChar <$> (`peekByteOff` 0) ptr
+  poke ptr val = case val of
+    UnicodeAsciiChar c -> (`pokeByteOff` 0) ptr c
+
+{-
+typedef struct _KEY_EVENT_RECORD {
+	BOOL bKeyDown;
+	WORD wRepeatCount;
+	WORD wVirtualKeyCode;
+	WORD wVirtualScanCode;
+	union {
+		WCHAR UnicodeChar;
+		CHAR AsciiChar;
+	} uChar;
+	DWORD dwControlKeyState;
+}
+#ifdef __GNUC__
+/* gcc's alignment is not what win32 expects */
+ PACKED
+#endif
+KEY_EVENT_RECORD;
+-}
+data KEY_EVENT_RECORD = KEY_EVENT_RECORD
+  { keyEventKeyDown         :: BOOL
+  , keyEventRepeatCount     :: WORD
+  , keyEventVirtualKeyCode  :: WORD
+  , keyEventVirtualScanCode :: WORD
+  , keyEventChar            :: UNICODE_ASCII_CHAR
+  , keyEventControlKeystate :: DWORD
+  } deriving (Show, Read, Eq)
+
+instance Storable KEY_EVENT_RECORD where
+  sizeOf _    = 16
+  alignment _ =  4
+  peek ptr = KEY_EVENT_RECORD <$> (`peekByteOff`  0) ptr
+                              <*> (`peekByteOff`  4) ptr
+                              <*> (`peekByteOff`  6) ptr
+                              <*> (`peekByteOff`  8) ptr
+                              <*> (`peekByteOff` 10) ptr
+                              <*> (`peekByteOff` 12) ptr
+  poke ptr val = do
+    (`pokeByteOff`  0) ptr $ keyEventKeyDown val
+    (`pokeByteOff`  4) ptr $ keyEventRepeatCount val
+    (`pokeByteOff`  6) ptr $ keyEventVirtualKeyCode val
+    (`pokeByteOff`  8) ptr $ keyEventVirtualScanCode val
+    (`pokeByteOff` 10) ptr $ keyEventChar val
+    (`pokeByteOff` 12) ptr $ keyEventControlKeystate val
+
+{-
+typedef struct _MOUSE_EVENT_RECORD {
+	COORD dwMousePosition;
+	DWORD dwButtonState;
+	DWORD dwControlKeyState;
+	DWORD dwEventFlags;
+} MOUSE_EVENT_RECORD;
+-}
+data MOUSE_EVENT_RECORD = MOUSE_EVENT_RECORD
+  { mousePosition        :: COORD
+  , mouseButtonState     :: DWORD
+  , mouseControlKeyState :: DWORD
+  , mouseEventFlags      :: DWORD
+  } deriving (Show, Read, Eq)
+
+instance Storable MOUSE_EVENT_RECORD where
+  sizeOf _    = 16
+  alignment _ =  4
+  peek ptr = MOUSE_EVENT_RECORD <$> (`peekByteOff`  0) ptr
+                                <*> (`peekByteOff`  4) ptr
+                                <*> (`peekByteOff`  8) ptr
+                                <*> (`peekByteOff` 12) ptr
+  poke ptr val = do
+    (`pokeByteOff`  0) ptr $ mousePosition val
+    (`pokeByteOff`  4) ptr $ mouseButtonState val
+    (`pokeByteOff`  8) ptr $ mouseControlKeyState val
+    (`pokeByteOff` 12) ptr $ mouseEventFlags val
+
+{-
+typedef struct _WINDOW_BUFFER_SIZE_RECORD {
+    COORD dwSize;
+} WINDOW_BUFFER_SIZE_RECORD;
+-}
+data WINDOW_BUFFER_SIZE_RECORD = WINDOW_BUFFER_SIZE_RECORD
+  { bufSizeNew :: COORD
+  } deriving (Show, Read, Eq)
+
+instance Storable WINDOW_BUFFER_SIZE_RECORD where
+  sizeOf _    = 4
+  alignment _ = 4
+  peek ptr = WINDOW_BUFFER_SIZE_RECORD <$> (`peekByteOff` 0) ptr
+  poke ptr val = (`pokeByteOff` 0) ptr $ bufSizeNew val
+
+{-
+typedef struct _MENU_EVENT_RECORD {
+    UINT dwCommandId;
+} MENU_EVENT_RECORD,*PMENU_EVENT_RECORD;
+-}
+data MENU_EVENT_RECORD = MENU_EVENT_RECORD
+  { menuCommandId :: UINT
+  } deriving (Show, Read, Eq)
+
+instance Storable MENU_EVENT_RECORD where
+  sizeOf _    = 4
+  alignment _ = 4
+  peek ptr = MENU_EVENT_RECORD <$> (`peekByteOff` 0) ptr
+  poke ptr val = (`pokeByteOff` 0) ptr $ menuCommandId val
+
+{-
+typedef struct _FOCUS_EVENT_RECORD { BOOL bSetFocus; } FOCUS_EVENT_RECORD;
+-}
+data FOCUS_EVENT_RECORD = FOCUS_EVENT_RECORD
+  { focusSetFocus :: BOOL
+  } deriving (Show, Read, Eq)
+
+instance Storable FOCUS_EVENT_RECORD where
+  sizeOf _    = 4
+  alignment _ = 4
+  peek ptr = FOCUS_EVENT_RECORD <$> (`peekByteOff` 0) ptr
+  poke ptr val = (`pokeByteOff` 0) ptr $ focusSetFocus val
+
+data INPUT_RECORD_EVENT
+  = InputKeyEvent KEY_EVENT_RECORD
+  | InputMouseEvent MOUSE_EVENT_RECORD
+  | InputWindowBufferSizeEvent WINDOW_BUFFER_SIZE_RECORD
+  | InputMenuEvent MENU_EVENT_RECORD
+  | InputFocusEvent FOCUS_EVENT_RECORD
+  deriving (Show, Read, Eq)
+
+{-
+typedef struct _INPUT_RECORD {
+	WORD EventType;
+	union {
+		KEY_EVENT_RECORD KeyEvent;
+		MOUSE_EVENT_RECORD MouseEvent;
+		WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
+		MENU_EVENT_RECORD MenuEvent;
+		FOCUS_EVENT_RECORD FocusEvent;
+	} Event;
+} INPUT_RECORD,*PINPUT_RECORD;
+-}
+data INPUT_RECORD = INPUT_RECORD
+  { inputEventType :: WORD
+  , inputEvent     :: INPUT_RECORD_EVENT
+  } deriving (Show, Read, Eq)
+
+instance Storable INPUT_RECORD where
+  sizeOf _    = 20
+  alignment _ =  4
+  peek ptr = do
+    evType <- (`peekByteOff` 0) ptr
+    event <- case evType of
+      _ | evType == kEY_EVENT
+          -> InputKeyEvent              <$> (`peekByteOff` 4) ptr
+      _ | evType == mOUSE_EVENT
+          -> InputMouseEvent            <$> (`peekByteOff` 4) ptr
+      _ | evType == wINDOW_BUFFER_SIZE_EVENT
+          -> InputWindowBufferSizeEvent <$> (`peekByteOff` 4) ptr
+      _ | evType == mENU_EVENT
+          -> InputMenuEvent             <$> (`peekByteOff` 4) ptr
+      _ | evType == fOCUS_EVENT
+          -> InputFocusEvent            <$> (`peekByteOff` 4) ptr
+      _ -> error $ "peek (INPUT_RECORD): Unknown event type " ++
+             show evType
+    pure $ INPUT_RECORD evType event
+  poke ptr val = do
+    (`pokeByteOff` 0) ptr $ inputEventType val
+    case inputEvent val of
+      InputKeyEvent              ev -> (`pokeByteOff` 4) ptr ev
+      InputMouseEvent            ev -> (`pokeByteOff` 4) ptr ev
+      InputWindowBufferSizeEvent ev -> (`pokeByteOff` 4) ptr ev
+      InputMenuEvent             ev -> (`pokeByteOff` 4) ptr ev
+      InputFocusEvent            ev -> (`pokeByteOff` 4) ptr ev
+
+-- The following is based on module System.Win32.Console.Extra from package
+-- Win32-console.
+
+getNumberOfConsoleInputEvents :: HANDLE -> IO DWORD
+getNumberOfConsoleInputEvents hdl =
+  returnWith_ $ \ptrN ->
+    failIfFalse_ "GetNumberOfConsoleInputEvents" $
+      cGetNumberOfConsoleInputEvents hdl ptrN
+
+-- The following is based on module System.Win32.Console.Extra from package
+-- Win32-console, cut down for the WCHAR version of readConsoleInput.
+
+readConsoleInput :: HANDLE -> DWORD -> IO [INPUT_RECORD]
+readConsoleInput hdl len
+  = readConsoleInputWith hdl len $ \(ptr, n) -> peekArray (fromEnum n) ptr
+
+readConsoleInputWith :: HANDLE
+                     -> DWORD
+                     -> OutputHandler (Ptr INPUT_RECORD, DWORD)
+readConsoleInputWith hdl len handler =
+  allocaArray (fromEnum len) $ \ptrBuf ->
+    alloca $ \ptrN -> do
+      failIfFalse_ "ReadConsoleInputW" $
+        cReadConsoleInput hdl ptrBuf len ptrN
+      n <- peek ptrN
+      handler (ptrBuf, n)
+
+type OutputHandler o = forall a. (o -> IO a) -> IO a
+
+-- Replicated from module Foreign.C.String in package base because that module
+-- does not export the function.
+cWcharsToChars :: [CWchar] -> [Char]
+cWcharsToChars = map chr . fromUTF16 . map fromIntegral
+ where
+  fromUTF16 (c1:c2:wcs)
+    | 0xd800 <= c1 && c1 <= 0xdbff && 0xdc00 <= c2 && c2 <= 0xdfff =
+      ((c1 - 0xd800)*0x400 + (c2 - 0xdc00) + 0x10000) : fromUTF16 wcs
+  fromUTF16 (c:wcs) = c : fromUTF16 wcs
+  fromUTF16 [] = []
+
+eNABLE_VIRTUAL_TERMINAL_PROCESSING :: DWORD
+eNABLE_VIRTUAL_TERMINAL_PROCESSING = 4
+
+iNVALID_HANDLE_VALUE :: HANDLE
+iNVALID_HANDLE_VALUE = castUINTPtrToPtr maxBound
+
+nullHANDLE :: HANDLE
+nullHANDLE = nullPtr
+
+foreign import ccall unsafe "HsWin32.h _ansi_terminal_castUINTPtrToPtr"
+  castUINTPtrToPtr :: UINT_PTR -> Ptr a
+
+foreign import ccall unsafe "windows.h GetConsoleMode"
+  c_GetConsoleMode :: HANDLE -> LPDWORD -> IO BOOL
+
+foreign import ccall unsafe "windows.h SetConsoleMode"
+  c_SetConsoleMode :: HANDLE -> DWORD -> IO BOOL
+
+getConsoleMode :: HANDLE -> IO DWORD
+getConsoleMode h = alloca $ \ptr -> do
+  failIfFalse_ "GetConsoleMode" $ c_GetConsoleMode h ptr
+  peek ptr
+
+setConsoleMode :: HANDLE -> DWORD -> IO ()
+setConsoleMode h mode = failIfFalse_ "SetConsoleMode" $ c_SetConsoleMode h mode
+ win/System/Console/ANSI/Windows/Win32/MinTTY.hsc view
@@ -0,0 +1,256 @@+{-# LANGUAGE Safe                #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+{-| This module is based on the corresponding code in the mintty package and the
+Win32 package, in order to avoid a dependency on those packages.
+-}
+module System.Console.ANSI.Windows.Win32.MinTTY
+  ( isMinTTYHandle
+  ) where
+
+import Control.Exception ( catch )
+import Data.Int ( Int32 )
+import Data.List ( isInfixOf )
+import Data.Word ( Word8 )
+import Foreign.C.String
+         ( peekCWStringLen, withCAString, withCWString, withCWStringLen )
+import Foreign.C.Types ( CInt (..) )
+import Foreign.Marshal.Alloc ( alloca, allocaBytes )
+import Foreign.Marshal.Array ( advancePtr, copyArray )
+import Foreign.Marshal.Utils ( maybeWith )
+import Foreign.Ptr ( FunPtr, Ptr, castPtr, castPtrToFunPtr, plusPtr )
+import Foreign.Storable ( Storable (..) )
+
+-- Provided by the ansi-terminal package
+import System.Console.ANSI.Windows.Win32.Types
+         ( Addr, BOOL, DWORD, FileType, HANDLE, HMODULE, LPCSTR, LPCTSTR, LPTSTR
+         , TCHAR, ULONG, USHORT, failIfFalse_, failIfNeg, failIfNull
+         )
+
+-- The headers that are shipped with GHC's copy of MinGW-w64 assume Windows XP.
+-- Since we need some structs that are only available with Vista or later,
+-- we must manually set WINVER/_WIN32_WINNT accordingly.
+#undef WINVER
+#define WINVER 0x0600
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#include <windows.h>
+#include "winternl_compat.h"
+
+#if __GLASGOW_HASKELL__ < 800
+#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__)
+#endif
+
+type F_NtQueryObject =
+     HANDLE
+  -> CInt
+  -> Ptr OBJECT_NAME_INFORMATION
+  -> ULONG
+  -> Ptr ULONG
+  -> IO NTSTATUS
+type F_GetFileInformationByHandleEx =
+  HANDLE -> CInt -> Ptr FILE_NAME_INFO -> DWORD -> IO BOOL
+type NTSTATUS = #type NTSTATUS
+
+data FILE_NAME_INFO = FILE_NAME_INFO
+  { fniFileNameLength :: DWORD
+  , fniFileName :: String
+  }
+
+instance Storable FILE_NAME_INFO where
+  sizeOf _ = #size FILE_NAME_INFO
+  alignment _ = #alignment FILE_NAME_INFO
+  poke buf fni = withTStringLen (fniFileName fni) $ \(str, len) -> do
+    let len' = (min mAX_PATH len) * sizeOfTCHAR
+        start = advancePtr (castPtr buf) (#offset FILE_NAME_INFO, FileName)
+        end = advancePtr start len'
+    (#poke FILE_NAME_INFO, FileNameLength) buf len'
+    copyArray start (castPtr str :: Ptr Word8) len'
+    poke (castPtr end) (0 :: TCHAR)
+  peek buf = do
+    vfniFileNameLength <- (#peek FILE_NAME_INFO, FileNameLength) buf
+    let len = fromIntegral vfniFileNameLength `div` sizeOfTCHAR
+    vfniFileName <-
+      peekTStringLen (plusPtr buf (#offset FILE_NAME_INFO, FileName), len)
+    pure $ FILE_NAME_INFO
+      { fniFileNameLength = vfniFileNameLength
+      , fniFileName = vfniFileName
+      }
+
+newtype OBJECT_NAME_INFORMATION = OBJECT_NAME_INFORMATION
+  { oniName :: UNICODE_STRING }
+
+instance Storable OBJECT_NAME_INFORMATION where
+  sizeOf _ = #size OBJECT_NAME_INFORMATION
+  alignment _ = #alignment OBJECT_NAME_INFORMATION
+  poke buf oni = (#poke OBJECT_NAME_INFORMATION, Name) buf (oniName oni)
+  peek buf =
+    fmap OBJECT_NAME_INFORMATION $ (#peek OBJECT_NAME_INFORMATION, Name) buf
+
+data UNICODE_STRING = UNICODE_STRING
+  { usLength :: USHORT
+  , usMaximumLength :: USHORT
+  , usBuffer :: String
+  }
+
+instance Storable UNICODE_STRING where
+  sizeOf _ = #size UNICODE_STRING
+  alignment _ = #alignment UNICODE_STRING
+  poke buf us = withTStringLen (usBuffer us) $ \(str, len) -> do
+    let len' = (min mAX_PATH len) * sizeOfTCHAR
+        start = advancePtr (castPtr buf) (#size UNICODE_STRING)
+        end = advancePtr start len'
+    (#poke UNICODE_STRING, Length) buf len'
+    (#poke UNICODE_STRING, MaximumLength) buf (len' + sizeOfTCHAR)
+    (#poke UNICODE_STRING, Buffer) buf start
+    copyArray start (castPtr str :: Ptr Word8) len'
+    poke (castPtr end) (0 :: TCHAR)
+  peek buf = do
+    vusLength <- (#peek UNICODE_STRING, Length) buf
+    vusMaximumLength <- (#peek UNICODE_STRING, MaximumLength) buf
+    vusBufferPtr <- (#peek UNICODE_STRING, Buffer) buf
+    let len = fromIntegral vusLength `div` sizeOfTCHAR
+    vusBuffer <- peekTStringLen (vusBufferPtr, len)
+    pure $ UNICODE_STRING
+      { usLength = vusLength
+      , usMaximumLength = vusMaximumLength
+      , usBuffer = vusBuffer
+      }
+
+-- | Returns 'True' is the given handle is attached to a MinTTY console
+-- (e.g., Cygwin or MSYS). Returns 'False' otherwise.
+isMinTTYHandle :: HANDLE -> IO Bool
+isMinTTYHandle h = do
+  fileType <- getFileType h
+  if fileType /= fILE_TYPE_PIPE
+    then pure False
+    else isMinTTYVista h `catch` \(_ :: IOError) -> isMinTTYCompat h
+    -- GetFileNameByHandleEx is only available on Vista and later (hence
+    -- the name isMinTTYVista). If we're on an older version of Windows,
+    -- getProcAddress will throw an IOException when it fails to find
+    -- GetFileNameByHandleEx, and thus we will default to using
+    -- NtQueryObject (isMinTTYCompat).
+
+isMinTTYVista :: HANDLE -> IO Bool
+isMinTTYVista h = do
+    fn <- getFileNameByHandle h
+    pure $ cygwinMSYSCheck fn
+  `catch` \(_ :: IOError) -> pure False
+
+cygwinMSYSCheck :: String -> Bool
+cygwinMSYSCheck fn =
+     ("cygwin-" `isInfixOf` fn || "msys-" `isInfixOf` fn)
+  && "-pty" `isInfixOf` fn
+-- Note that GetFileInformationByHandleEx might return a filepath like:
+--
+--    \msys-dd50a72ab4668b33-pty1-to-master
+--
+-- But NtQueryObject might return something like:
+--
+--    \Device\NamedPipe\msys-dd50a72ab4668b33-pty1-to-master
+--
+-- This means we can't rely on "\cygwin-" or "\msys-" being at the very start
+-- of the filepath. As a result, we use `isPrefixOf` to check for "cygwin" and
+-- "msys".
+--
+-- It's unclear if "-master" will always appear in the filepath name. Recent
+-- versions of MinTTY have been known to give filepaths like this (#186):
+--
+--    \msys-dd50a72ab4668b33-pty0-to-master-nat
+--
+-- Just in case MinTTY ever changes this convention, we don't bother checking
+-- for the presence of "-master" in the filepath name at all.
+
+isMinTTYCompat :: HANDLE -> IO Bool
+isMinTTYCompat h = do
+    fn <- ntQueryObjectNameInformation h
+    pure $ cygwinMSYSCheck fn
+  `catch` \(_ :: IOError) -> pure False
+
+fILE_TYPE_PIPE :: FileType
+fILE_TYPE_PIPE = 3
+
+ntQueryObjectNameInformation :: HANDLE -> IO String
+ntQueryObjectNameInformation h = do
+  let sizeOfONI = sizeOf (undefined :: OBJECT_NAME_INFORMATION)
+      bufSize   = sizeOfONI + mAX_PATH * sizeOfTCHAR
+  allocaBytes bufSize $ \buf ->
+    alloca $ \p_len -> do
+      hwnd <- getModuleHandle (Just "ntdll.exe")
+      addr <- getProcAddress hwnd "NtQueryObject"
+      let c_NtQueryObject = mk_NtQueryObject (castPtrToFunPtr addr)
+      _ <- failIfNeg "NtQueryObject" $ c_NtQueryObject
+             h objectNameInformation buf (fromIntegral bufSize) p_len
+      oni <- peek buf
+      pure $ usBuffer $ oniName oni
+
+sizeOfTCHAR :: Int
+sizeOfTCHAR = sizeOf (undefined :: TCHAR)
+
+getFileNameByHandle :: HANDLE -> IO String
+getFileNameByHandle h = do
+  let sizeOfDWORD = sizeOf (undefined :: DWORD)
+      -- note: implicitly assuming that DWORD has stronger alignment than wchar_t
+      bufSize     = sizeOfDWORD + mAX_PATH * sizeOfTCHAR
+  allocaBytes bufSize $ \buf -> do
+    getFileInformationByHandleEx h fileNameInfo buf (fromIntegral bufSize)
+    fni <- peek buf
+    pure $ fniFileName fni
+
+getFileInformationByHandleEx ::
+     HANDLE
+  -> CInt
+  -> Ptr FILE_NAME_INFO
+  -> DWORD
+  -> IO ()
+getFileInformationByHandleEx h cls buf bufSize = do
+  lib <- getModuleHandle (Just "kernel32.dll")
+  ptr <- getProcAddress lib "GetFileInformationByHandleEx"
+  let c_GetFileInformationByHandleEx =
+        mk_GetFileInformationByHandleEx (castPtrToFunPtr ptr)
+  failIfFalse_ "getFileInformationByHandleEx"
+    (c_GetFileInformationByHandleEx h cls buf bufSize)
+
+getModuleHandle :: Maybe String -> IO HMODULE
+getModuleHandle mb_name =
+  maybeWith withTString mb_name $ \ c_name ->
+  failIfNull "GetModuleHandle" $ c_GetModuleHandle c_name
+
+getProcAddress :: HMODULE -> String -> IO Addr
+getProcAddress hmod procname =
+  withCAString procname $ \ c_procname ->
+  failIfNull "GetProcAddress" $ c_GetProcAddress hmod c_procname
+
+peekTStringLen :: (LPCTSTR, Int) -> IO String
+peekTStringLen = peekCWStringLen
+
+withTString :: String -> (LPTSTR -> IO a) -> IO a
+withTString = withCWString
+
+withTStringLen :: String -> ((LPTSTR, Int) -> IO a) -> IO a
+withTStringLen = withCWStringLen
+
+fileNameInfo :: CInt
+fileNameInfo = #const FileNameInfo
+
+mAX_PATH :: Num a => a
+mAX_PATH = #const MAX_PATH
+
+objectNameInformation :: CInt
+objectNameInformation = #const ObjectNameInformation
+
+foreign import ccall "dynamic"
+  mk_GetFileInformationByHandleEx ::
+    FunPtr F_GetFileInformationByHandleEx -> F_GetFileInformationByHandleEx
+
+foreign import ccall unsafe "windows.h GetFileType"
+  getFileType :: HANDLE -> IO FileType
+
+foreign import ccall unsafe "windows.h GetProcAddress"
+  c_GetProcAddress :: HMODULE -> LPCSTR -> IO Addr
+
+foreign import ccall "dynamic"
+  mk_NtQueryObject :: FunPtr F_NtQueryObject -> F_NtQueryObject
+
+foreign import ccall unsafe "windows.h GetModuleHandleW"
+  c_GetModuleHandle :: LPCTSTR -> IO HMODULE
+ win/System/Console/ANSI/Windows/Win32/Types.hs view
@@ -0,0 +1,195 @@+{-# LANGUAGE Safe #-}
+
+{-| This module is based on the corresponding code in the Win32 package, in
+order to avoid a dependency on that package. Some of that code had its origins
+in earlier versions of this package.
+-}
+module System.Console.ANSI.Windows.Win32.Types
+  ( Addr
+  , BOOL
+  , DWORD
+  , ErrCode
+  , FileType
+  , HANDLE
+  , HMODULE
+  , LPCSTR
+  , LPCTSTR
+  , LPDWORD
+  , LPTSTR
+  , SHORT
+  , TCHAR
+  , UINT
+  , UINT_PTR
+  , ULONG
+  , USHORT
+  , WCHAR
+  , WORD
+  , failIfFalse_
+  , failIfNeg
+  , failIfNull
+  , withHandleToHANDLE
+  ) where
+
+import Control.Concurrent.MVar ( readMVar )
+import Control.Exception ( bracket, throwIO )
+import Control.Monad ( when )
+import Data.Char ( isSpace )
+import Data.Typeable ( cast )
+import Data.Word ( Word16, Word32 )
+import Foreign.C.Error ( Errno (..), errnoToIOError )
+import Foreign.C.String ( peekCWString )
+import Foreign.C.Types ( CChar, CInt (..), CShort (..), CWchar )
+import Foreign.Ptr ( Ptr, nullPtr )
+import Foreign.StablePtr ( StablePtr, freeStablePtr, newStablePtr )
+import GHC.IO.Handle.Types ( Handle (..), Handle__ (..) )
+import GHC.IO.FD ( FD(..) ) -- A wrapper around an Int32
+import Numeric ( showHex )
+import System.IO.Error ( ioeSetErrorString )
+
+#if defined(__IO_MANAGER_WINIO__)
+import GHC.IO.Exception
+         ( IOErrorType (InappropriateType), IOException (IOError), ioException )
+import GHC.IO.SubSystem ( (<!>) )
+import GHC.IO.Windows.Handle ( ConsoleHandle, Io, NativeHandle, toHANDLE )
+#endif
+
+type Addr = Ptr ()
+type BOOL = Bool
+type DWORD = Word32
+type ErrCode = DWORD
+type FileType = DWORD
+type HANDLE = Ptr ()
+type HMODULE = Ptr ()
+type LPCSTR = LPSTR
+type LPCTSTR = LPTSTR
+type LPDWORD = Ptr DWORD
+type LPSTR = Ptr CChar
+type LPTSTR = Ptr TCHAR
+type LPWSTR = Ptr CWchar
+type SHORT = CShort
+type TCHAR = CWchar
+type UINT = Word32
+type UINT_PTR = Word
+type ULONG = Word32
+type USHORT = Word16
+type WCHAR = CWchar
+type WORD = Word16
+
+withStablePtr :: a -> (StablePtr a -> IO b) -> IO b
+withStablePtr value = bracket (newStablePtr value) freeStablePtr
+
+#if defined(__IO_MANAGER_WINIO__)
+
+withHandleToHANDLE :: Handle -> (HANDLE -> IO a) -> IO a
+withHandleToHANDLE = withHandleToHANDLEPosix <!> withHandleToHANDLENative
+
+withHandleToHANDLENative :: Handle -> (HANDLE -> IO a) -> IO a
+withHandleToHANDLENative haskell_handle action =
+  withStablePtr haskell_handle $ const $ do
+    let write_handle_mvar = case haskell_handle of
+            FileHandle _ handle_mvar     -> handle_mvar
+            DuplexHandle _ _ handle_mvar -> handle_mvar
+    windows_handle <- readMVar write_handle_mvar >>= handle_ToHANDLE
+    action windows_handle
+ where
+  handle_ToHANDLE :: Handle__ -> IO HANDLE
+  handle_ToHANDLE (Handle__{haDevice = dev}) =
+    case ( cast dev :: Maybe (Io NativeHandle)
+         , cast dev :: Maybe (Io ConsoleHandle)) of
+      (Just hwnd, Nothing) -> pure $ toHANDLE hwnd
+      (Nothing, Just hwnd) -> pure $ toHANDLE hwnd
+      _                    -> throwErr "not a known HANDLE"
+
+  throwErr msg = ioException $ IOError (Just haskell_handle)
+    InappropriateType "withHandleToHANDLENative" msg Nothing Nothing
+
+#else
+
+withHandleToHANDLE :: Handle -> (HANDLE -> IO a) -> IO a
+withHandleToHANDLE = withHandleToHANDLEPosix
+
+#endif
+
+withHandleToHANDLEPosix :: Handle -> (HANDLE -> IO a) -> IO a
+withHandleToHANDLEPosix haskell_handle action =
+  -- Create a stable pointer to the Handle. This prevents the garbage collector
+  -- getting to it while we are doing horrible manipulations with it, and hence
+  -- stops it being finalized (and closed).
+  withStablePtr haskell_handle $ const $ do
+    -- Grab the write handle variable from the Handle
+    let write_handle_mvar = case haskell_handle of
+          FileHandle _ handle_mvar     -> handle_mvar
+          DuplexHandle _ _ handle_mvar -> handle_mvar
+          -- This is "write" MVar, we could also take the "read" one
+
+    -- Get the FD from the algebraic data type
+    Just fd <- (\(Handle__ { haDevice = dev }) -> fmap fdFD (cast dev))
+             <$> readMVar write_handle_mvar
+
+    -- Finally, turn that (C-land) FD into a HANDLE using msvcrt
+    windows_handle <- c_get_osfhandle fd
+    -- Do what the user originally wanted
+    action windows_handle
+
+-- This essential function comes from the C runtime system. It is certainly
+-- provided by msvcrt, and also seems to be provided by the mingw C library -
+-- hurrah!
+foreign import ccall unsafe "_get_osfhandle"
+  c_get_osfhandle :: CInt -> IO HANDLE
+
+failIfNeg :: (Num a, Ord a) => String -> IO a -> IO a
+failIfNeg = failIf (< 0)
+
+failIfNull :: String -> IO (Ptr a) -> IO (Ptr a)
+failIfNull = failIf (== nullPtr)
+
+failIf :: (a -> Bool) -> String -> IO a -> IO a
+failIf p wh act = do
+  v <- act
+  if p v then errorWin wh else return v
+
+failIfFalse_ :: String -> IO Bool -> IO ()
+failIfFalse_ = failIf_ not
+
+failIf_ :: (a -> Bool) -> String -> IO a -> IO ()
+failIf_ p wh act = do
+  v <- act
+  when (p v) $ errorWin wh
+
+errorWin :: String -> IO a
+errorWin fn_name = do
+  err_code <- getLastError
+  failWith fn_name err_code
+
+failWith :: String -> ErrCode -> IO a
+failWith fn_name err_code = do
+  c_msg <- getErrorMessage err_code
+  msg <- if c_msg == nullPtr
+    then return $ "Error 0x" ++ Numeric.showHex err_code ""
+    else do
+      msg <- peekTString c_msg
+      -- We ignore failure of freeing c_msg, given we're already failing
+      _ <- localFree c_msg
+      return msg
+  -- turn GetLastError() into errno, which errnoToIOError knows how to convert
+  -- to an IOException we can throw.
+  errno <- c_maperrno_func err_code
+  let msg' = reverse $ dropWhile isSpace $ reverse msg -- drop trailing \n
+      ioerror = errnoToIOError fn_name errno Nothing Nothing
+                  `ioeSetErrorString` msg'
+  throwIO ioerror
+
+peekTString :: LPCTSTR -> IO String
+peekTString = peekCWString
+
+foreign import ccall unsafe "maperrno_func" -- in base/cbits/Win32Utils.c
+   c_maperrno_func :: ErrCode -> IO Errno
+
+foreign import ccall unsafe "errors.h _ansi_terminal_getErrorMessage"
+  getErrorMessage :: DWORD -> IO LPWSTR
+
+foreign import ccall unsafe "windows.h GetLastError"
+  getLastError :: IO ErrCode
+
+foreign import ccall unsafe "windows.h LocalFree"
+  localFree :: Ptr a -> IO (Ptr a)
+ win/c-source/HsWin32.c view
@@ -0,0 +1,3 @@+// Out-of-line versions of all the inline functions from HsWin32.h
+#define INLINE  /* nothing */
+#include "HsWin32.h"
+ win/c-source/errors.c view
@@ -0,0 +1,39 @@+#define UNICODE
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+#include "errors.h"
+
+/* Copied from the Win32-2.13.4.0 package, but renamed `getErrorMessage` to
+ * `_ansi_terminal_getErrorMessage`, in order to avoid problems with duplicate
+ * symbols in GHC's object files. See:
+ * https://gitlab.haskell.org/ghc/ghc/-/issues/23365.
+ */
+
+/* There's two ways we can generate error messages - with different tradeoffs:
+ * If we do a function call, we have to use a static buffer.
+ * If we use a macro and ANSI C's string splicing, we have to use constant
+ * strings - and accept a certain amount of overhead from inserting the
+ * boilerplate text.
+ *
+ * Why the concern about performance? Error messages are only generated
+ * in exceptional situations    -- sof 9/98
+ *
+ * sof 9/98 : Removed use of non-standard (and wimpy :-) snprintf().
+ */
+
+LPTSTR _ansi_terminal_getErrorMessage(DWORD err)
+{
+    LPTSTR what;
+
+    FormatMessage(
+	(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER) ,
+	NULL,
+	err,
+	MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
+	(LPTSTR) &what,
+	0,
+	NULL
+	);
+    return what;
+}
+ win/include/HsWin32.h view
@@ -0,0 +1,23 @@+#ifndef _ANSI_TERMINAL_HSWIN32_H
+#define _ANSI_TERMINAL_HSWIN32_H
+
+#define UNICODE
+#include <windows.h>
+
+/* Copied from the Win32-2.13.4.0 package, but renamed `castUINTPtrToPtr` to
+ * `_ansi_terminal_castUINTPtrToPtr`, in order to avoid problems with duplicate
+ * symbols in GHC's object files. See:
+ * https://gitlab.haskell.org/ghc/ghc/-/issues/23365.
+ */
+
+#ifndef INLINE
+# if defined(_MSC_VER)
+#  define INLINE extern __inline
+# else
+#  define INLINE extern inline
+# endif
+#endif
+
+INLINE void *_ansi_terminal_castUINTPtrToPtr(UINT_PTR n) { return (void *)n; }
+
+#endif /* _ANSI_TERMINAL_HSWIN32_H */
+ win/include/errors.h view
@@ -0,0 +1,22 @@+#ifndef _ANSI_TERMINAL_ERRORS_H
+#define _ANSI_TERMINAL_ERRORS_H
+
+#include <windows.h>
+
+/* Copied from the Win32-2.13.4.0 package, but renamed `getErrorMessage` to
+ * `_ansi_terminal_getErrorMessage`, in order to avoid problems with duplicate
+ * symbols in GHC's object files. See:
+ * https://gitlab.haskell.org/ghc/ghc/-/issues/23365.
+ */
+
+/* There's two ways we can generate error messages - with different tradeoffs:
+ * If we do a function call, we have to use a static buffer.
+ * If we use a macro and ANSI C's string splicing, we have to use constant
+ * strings - and accept a certain amount of overhead from inserting the
+ * boilerplate text.
+ */
+
+/* result should be freed using LocalFree */
+extern LPTSTR _ansi_terminal_getErrorMessage(DWORD err);
+
+#endif /* _ANSI_TERMINAL_ERRORS_H */
+ win/include/winternl_compat.h view
@@ -0,0 +1,31 @@+#ifndef WINTERNL_COMPAT_H
+#define WINTERNL_COMPAT_H
+
+/*
+ * winternl.h is not included in MinGW, which was shipped with the 32-bit
+ * Windows version of GHC prior to the 7.10.3 release.
+ */
+#if defined(x86_64_HOST_ARCH) || \
+   __GLASGOW_HASKELL__ >= 711 || \
+   (__GLASGOW_HASKELL__ == 710 && \
+    defined(__GLASGOW_HASKELL_PATCHLEVEL1__) && \
+    __GLASGOW_HASKELL_PATCHLEVEL1__ >= 2)
+# include <winternl.h>
+#else
+// Some declarations from winternl.h that we need
+# include <windows.h>
+
+typedef LONG NTSTATUS;
+
+typedef struct _UNICODE_STRING {
+  USHORT Length;
+  USHORT MaximumLength;
+  PWSTR  Buffer;
+} UNICODE_STRING;
+
+typedef struct _OBJECT_NAME_INFORMATION {
+  UNICODE_STRING Name;
+} OBJECT_NAME_INFORMATION;
+#endif
+
+#endif /* WINTERNL_COMPAT_H */