ansi-terminal icon indicating copy to clipboard operation
ansi-terminal copied to clipboard

Retrieve current foreground and background color

Open ad-si opened this issue 3 years ago • 6 comments

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?

ad-si avatar Dec 27 '21 10:12 ad-si

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.

ad-si avatar Dec 27 '21 11:12 ad-si

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

UnkindPartition avatar Dec 28 '21 08:12 UnkindPartition

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.

mpilgrem avatar Dec 29 '21 14:12 mpilgrem

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.

mpilgrem avatar May 03 '22 21:05 mpilgrem

@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.

mpilgrem avatar May 04 '22 20:05 mpilgrem

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).

mpilgrem avatar May 07 '22 12:05 mpilgrem

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 avatar Jan 04 '23 15:01 ad-si

@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)

mpilgrem avatar Jan 04 '23 18:01 mpilgrem

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 avatar Jan 04 '23 19:01 ad-si

@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.

mpilgrem avatar Jan 04 '23 19:01 mpilgrem