6

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?

9

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.

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
    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 '15 at 10:15
  • This is an excellent writeup, thank you for answering a messy question in detail. <3 – ELLIOTTCABLE May 23 '17 at 15:04
5

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.

  • 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 '15 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. – Thomas Dickey Nov 25 '15 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 '15 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. – Thomas Dickey Nov 25 '15 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 '15 at 0:00
3

Based on the well-thought-out answers by Thomas Dickey and Gilles I have culled together this script in github.

The crux is to use xterm-compatible color querying, and based on the RGB color values see if the background is dark. 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).

Special care though is to identify what constitutes the boundary between when something is light versus dark. In xterm maximum red, green and blue values are 0xffff while in xterm-256color it is 0xff.

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.

  • 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 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 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 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 at 13:50
  • I assume that most users use the official xterm, not some xterm-256 fork (in particular, Debian provides xterm only), thus the normal way to get 256 colors is to set TERM=xterm-256color. This is also what gnome-terminal uses. Thus your script would be wrong with both the official xterm and gnome-terminal. – vinc17 May 17 at 10:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.