21

I have written a number of debuggers that all can colorize source code text shown in a terminal session. They all understand that some terminals have a dark background and some have a light background and that of course the colors need to be different depending on the terminal scheme.

It is annoying to have to set to the other scheme when your terminal doesn't match the default background, so I'd like to find a way to figure this out automatically. Suggestions? (They all support options --highlight={light|dark|plain})

One simple mechanism would be to key off an environment variable. For my shell profiles I've been using DARK_BACKGROUND_COLOR, but If there is already some sort of default name like there is for PAGER, EDITOR, SHELL, HOME, etc. I'd like to use that.

Is there such a environment name convention? Other suggestions?

Edit: Based on the accepted answer and discussion, I have switched from using DARK_BACKGROUND_COLOR to COLORFGBG. Value 15;0 is for a dark background (technically white on black) and 0;15 (technically black on white) is for a light background.

2
  • 1
    It is interesting that no-one, questioner or answerers, has mentioned DEC Private Mode 5 ("light/dark background"), which adds a further twist to this, or the DECRQM control sequence.
    – JdeBP
    Feb 22, 2020 at 18:46
  • 1
    @JdeBP Ok. What terminal emulator programs make use of this and on which OSs? Feel free to put in a PR for making use of this in github.com/rocky/shell-term-background
    – rocky
    Feb 23, 2020 at 1:51

3 Answers 3

23

There is no such convention. Furthermore, an environment variable is not a good way to report information about a terminal, because the value can get stale if a program starts another terminal emulator which doesn't update this variable, or if a program connects to multiple terminals.

(The TERM environment variable doesn't suffer from these problems because it's universal: every terminal emulator sets it and every program is aware of it. The problems only arise when a variable is partially supported.)

The right way to obtain the information would be to query the terminal. In the unix world, this is done by writing an escape sequence which the terminal interprets as “send back some data that answers my query”. As Thomas Dickey explains, xterm has such a control sequence, OSC 11 ; ? BEL (set text parameters, parameter 11 = text background color, value ? means query instead of set). Write \e]11;?\a to the terminal (where \e is the escape character (^[) and \a is the bell character (^G)), and xterm replies with a string like \e]11;rgb:0000/0000/0000\a (that's a black background). Unfortunately, few other terminal emulators support this escape sequence. Even in xterm, this feature might be disabled (through the XTerm.VT100.allowColorOps resource) because it's a security risk: writing to a terminal can result in output to that terminal that's partially controlled by the text being written.

Rxvt sets the environment variable COLORFGBG to a string like 7;0 where 7 is the foreground color (7 is light gray) and 0 is the background color (black). Konsole also supports this.

Emacs attempts to detect whether the terminal has a light or dark background, in order to set the background-mode terminal parameter. As of Emacs 24.5, there are three methods to set the background mode automatically:

  • On xterm, Emacs uses the OSC 11 escape sequence as explained above.
  • On rxvt, Emacs uses the COLORFGBG environment variable as explained above.
  • On DOS and Windows consoles, Emacs uses OS-specific interfaces to obtain information about the terminal; these interfaces play the same role as the OSC 11 escape sequence.

This leaves out many terminals, however there is some progress: the vte library, which powers many terminal emulators such as gnome-terminal, guake, terminator, xfce4-terminal, …, implements OSC 11 reporting like xterm since version 0.35.2. You can detect VTE-based terminals by checking the environment variable VTE_VERSION; the value is a number, you want 3502 and above.

If you want to standardize on a way report the information to applications, then support on the terminal side might not matter: after all you know whether you prefer light or dark backgrounds. Then you might as well align with rxvt and use COLORFGBG, since it's the only interface that somebody is already using and that you can adopt independently of any terminal support. The COLORFGBG interface is limited: it was designed for a world with only 16 colors, and everybody agreeing on a mapping from color numbers to colors (at least approximately, exact hues differ). Konsole supports more than 16 colors, but it uses an approximation when reporting COLORFGBG: it approximates the foreground and background colors by one of the 16 standard colors. If all you care about is light vs dark, that's not a problem, just set COLORFGBG to 15;0 for light text on a dark background or 0;15 for dark text on a light background.

1
  • 1
    Given how complicated it is turning out to be to figure this simple question, I clearly don't want to duplicate the code in each one of the many debuggers. Instead I'd prefer to have a standalone program that I can query to give the information. Even with COLORFGBG, I'd have to add some sort of additional logic to get my question answered. So possibly a plan of attack: run this program that gives the information, failing, that consult DARK_BACKGROUND_COLOR which can easily be set. And finally I allow for command-line option or setup profile override which is currently in place.
    – rocky
    Nov 26, 2015 at 10:15
8

There is none. You are free to make your own convention.

A few programs (such as xterm) can provide an application with currently-set foreground and background colors (see "dynamic colors"). But aside from applications such as xtermcontrol, very few make use of it. Emacs queries the current background color to set its background-mode terminal parameter, which determines whether to apply the “dark” or “light” version of color theme. On the other hand, Vim, which may use the xterm control sequences for the number of colors and the strings sent by special keys (see "Request Termcap/Terminfo String" in XTerm Control Sequences), does not detect the sense of foreground/background colors.

5
  • Will wait to hear from others - you never know what's out there. If after a while there's no convention in place, I'll accept this and use DARK_BACKGROUND_COLOR.
    – rocky
    Nov 25, 2015 at 18:43
  • 1
    COLORFGBG is used by gnome-terminal and konsole, as well as mlterm and mrxvt. But of those, only konsole gives color information. If it had occurred to me, I would have mentioned that it is essentially a worthless means of getting color information due to things like that. Likewise, there is no solution for PuTTY. Nov 25, 2015 at 23:49
  • 2
    gnome-terminal does not set COLORFGBG, but it supports the necessary escape sequences for xtermcontrol to work. Please see bugzilla.gnome.org/show_bug.cgi?id=733423#c1 for rationale.
    – egmont
    Nov 25, 2015 at 23:53
  • Sorry: it sets COLORTERM (it's out of my way to check, since it doesn't display in a readable manner on my OSX desktop). Still, an environment variable has more limitations than an escape sequence for this use. Nov 25, 2015 at 23:56
  • Yup we agree that the escape sequences are the better choice. Just for the record (it's irrelevant for the original question): current gnome-terminal doesn't set COLORTERM either, it only sets TERM=xterm-256color. We might resurrect COLORTERM with a new value, see bugzilla.gnome.org/show_bug.cgi?id=754521.
    – egmont
    Nov 26, 2015 at 0:00
3

Based on the well-thought-out answers by Thomas Dickey and Gilles and what to do for OSX in the comment to this answer, I have culled together this script in github.

The crux is to use xterm-compatible color querying, and based on the RGB color values of background versus the foreground. If that doesn't work, then try to interpret COLORFGBG and failing that use preset defaults for various terminals. For example, xterm is a light background by default.

The code for the preset defaults and RGB dark/light interpolation was derived from GNU emacs code (as suggested by Gilles).

I haven't added any MS Windows specific code, but folks should feel free to submit pull requests to handle that or to add any other corrections.

7
  • 1
    Using macos Mojave 10.14.4 (uname -r -> 18.5.0), the read bash builtin does not accept float time values, at lines 91 and 92: IFS=: read -t 0.1 -d $'\a' x fg. I tried to replace 0.1 by 1but it "Can't decide"... Anyway, I could get the bg rgb with osascript -e 'tell application "Terminal" to get background color of current settings of selected tab of front window'
    – duthen
    May 9, 2019 at 13:05
  • I have added the above to github.com/rocky/bash-term-background/commit/… . If this has problems, please consider issuing a pull request against it.Thanks.
    – rocky
    May 9, 2019 at 17:23
  • 1
    It makes no sense to distinguish xterm and xterm-256color since any of them can be used as the $TERM value in Xterm, and they do not affect the output of the echo -ne '\e]10;?\a\e]11;?\a' command. For instance, I have TERM=xterm-256color in Xterm, but the maximum component value is 0xffff. FYI, in all the xterm-compatible terminals I've tested, the maximum value is 0xffff and for each output component, there are always 4 digits (e.g. 0000 for 0).
    – vinc17
    May 16, 2019 at 11:30
  • @vinc17 This code uses heuristics and conventions, and can easily be fooled if you are so disposed to doing so. Or stated in a positive way, the user can override the defaults if she chooses to. So you can set TERM or COLORFGBG or TERIMINAL_COLOR_MIDPOINT to whatever you want and that will influence the result. However the heuristic is that TERM=xterm-256color is based on the xterm-256code base which often uses 2**8-1 for the highest r/g/b value while XTERM=xterm is based on xterm and uses 2*16-1 for its hghest r/g/b value.
    – rocky
    May 16, 2019 at 13:50
  • 2
    There's no reason to pursue what the maximum of RGB is. Just compare it against the background vs. foreground, its sum of each 3-element of RGB. However, I made some enhancement on top of Rocky's script that would work better in xterm, gnome-terminal, but still not inside the gvim's terminal shell session (as it's linux). github.com/egberts/shell-term-background/blob/master/… Apr 10, 2020 at 22:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .