ansi-terminal
ansi-terminal copied to clipboard
Retrieve current foreground and background color
I want to implement a dark and light mode for a CLI tool, but therefore I need to figure out the current fore-/background color.
There seems to be a control sequence for it: https://stackoverflow.com/a/7767891/1850340 However, it's not clear if the response can be captured in a sensible way.
Are there any other ways to achieve this?
Here is another Answer with some more information: https://unix.stackexchange.com/a/245568/96815
It also mentions a $COLORFGBG
environment variable which might be better suited.
However, it's not clear if the response can be captured in a sensible way.
We already have a similar logic for querying the cursor position; perhaps it could be generalized to cover this case as well.
https://github.com/UnkindPartition/ansi-terminal/blob/5217d0856cd1377d044aa55def40b5bcf9872374/src/System/Console/ANSI/Unix.hs#L86-L105
On Windows 10/11 and with Windows Terminal (planned to be the default terminal on Windows 11 during 2022: https://devblogs.microsoft.com/commandline/windows-terminal-as-your-default-command-line-experience/), the implementation of the OSC 11
code appears to be a work in progress: https://github.com/microsoft/terminal/issues/3718. For now, if this were to be implemented in ansi-terminal
it would likely be only for Unix-like OSs.
I have been looking into this further, on macOS 10.15.5. I am running into inconsistencies with the STRING TERMINATOR (ST) in the output. I was hoping the output would use the same ST as the command.
macOS Terminal v2.10 (433) appears to recognise legacy "\BEL" and modern "\ESC\" but returns only "\BEL". iTerm2 build 3.4.15 appears to be the same as macOS Terminal. Hyper 3.2.3 (stable) appears to recognise "\BEL" and "\ESC\" but return only "\ESC\".
On Windows: mintty 3.6.0 appears to recognise "\BEL" and "\ESC\" and return the same ST as the command.
@ad-si, I think my proposed pull request #137 will add this to ansi-terminal
for Unix-like operating systems only. I have tested it on macOS 10.15.5 (Catalina) (with macOS Terminal, iTerm2 and Hyper) but my access to machines with other Unix-like operating systems is limited. If you were able to test that PR, I would be grateful.
My proposed API mimics the existing API for getCursorPosition :: IO (Maybe (Int, Int))
etc, with getLayerColor :: ConsoleLayer -> IO (Maybe (RGB Word16))
etc. As explained in the Haddock documentation, I chose RGB Word16
because 16 bit colour channels is the most common format used by terminal software to report colours.
I have updated my pull request to cover Windows, and I think it could be merged and a new version of ansi-terminal
released, but I going to wait a little while to see if the position as regards GHC's WinIO can be bottomed out (see below).
On Windows, things are a little complicated. As mentioned above, the native terminals still do not support the OSC code. So, I have emulated that. However, the emulation only works for ConHost and not for Windows Terminal (which no longer uses ConHost for the user-interface). Further, getting the data emitted into the input stream has to use the Windows Console API (GHC's WinIO does not yet provide a solution - see https://gitlab.haskell.org/ghc/ghc/-/issues/21488) and so will not work on mintty (for example).
Awesome, thanks a lot for implementing this @mpilgrem!
The docs say:
Uses stdout. If stdout will be redirected, see hGetLayerColor for a more general function.
But how would it work to use some other Handler? When I use it with stdout, it behaves very weirdly on redirection:
$ stack run open | head -c 40
Id []11;rgb:1999/1999/1999^[\⏎
$ ]11;rgb:1999/1999/1999\
How can I fix / circumvent this?
@ad-si, the way that this works is that the relevant 'ANSI' codes are detected in the (ANSI-capable) terminal's output stream and the information is injected into the terminal's input stream. If the stdout
output stream is redirected away from the terminal, then you need to use stderr
(assuming it is not also redirected). For example:
module Main (main) where
import System.Console.ANSI ( ConsoleLayer (..), hGetLayerColor )
import System.IO ( stderr )
main :: IO ()
main = do
mBC <- hGetLayerColor stderr Background
mFC <- hGetLayerColor stderr Foreground
print (mBC, mFC)
Yeah, stderr
is already a little better, but is there no way to create something like a "dummy output stream", which will never interfere with the normal usage of the CLI tool?
@ad-si, not as far as I am aware. An application's code would usually detect if its output was to a terminal or not (eg redirected) (https://hackage.haskell.org/package/ansi-terminal-0.11.4/docs/System-Console-ANSI.html#g:32), and then behave accordingly.