Skip to content

Latest commit

 

History

History
357 lines (290 loc) · 21.9 KB

README.md

File metadata and controls

357 lines (290 loc) · 21.9 KB

Terminal Colors

(Previously published and discussed at https://gist.github.com/XVilka/8346728.)

There exists common confusion about terminal colors. This is what we have right now:

  • Plain ASCII
  • ANSI escape codes: 16 color codes with bold/italic and background
  • 256 color palette: 216 colors + 16 ANSI + 24 gray (colors are 24-bit)
  • 24-bit truecolor: "888" colors (aka 16 million)
printf "\x1b[${bg};2;${red};${green};${blue}m\n"

The 256-color palette is configured at start and is a 666-cube of colors, each of them defined as a 24-bit (888 RGB) color.

This means that current support can only display 256 different colors in the terminal while "truecolor" means that you can display 16 million different colors at the same time.

Truecolor escape codes do not use a color palette. They just specify the color directly.

For a quick check of your terminal, run:

printf "\x1b[38;2;255;100;0mTRUECOLOR\x1b[0m\n"

which will print TRUECOLOR in brown if it understands Xterm-style true-color escapes.

For a more thorough test, run:

awk 'BEGIN{
    s="/\\/\\/\\/\\/\\"; s=s s s s s s s s;
    for (colnum = 0; colnum<77; colnum++) {
        r = 255-(colnum*255/76);
        g = (colnum*510/76);
        b = (colnum*255/76);
        if (g>255) g = 510-g;
        printf "\033[48;2;%d;%d;%dm", r,g,b;
        printf "\033[38;2;%d;%d;%dm", 255-r,255-g,255-b;
        printf "%s\033[0m", substr(s,colnum+1,1);
    }
    printf "\n";
}'

Some other tests:

You can download these scripts and inspect them before running them, by:
# make a sandbox
mkdir tmp$$
cd tmp$$

# downloads scripts
wget https://github.com/robertknight/konsole/raw/master/tests/color-spaces.pl \
     https://gist.github.com/lilydjwg/fdeaf79e921c2f413f44b6f613f6ad53/raw/94d8b2be62657e96488038b0e547e3009ed87d40/colors.py \
     https://github.com/JohnMorales/dotfiles/raw/master/colors/24-bit-color.sh \
     https://gitlab.gnome.org/GNOME/vte/-/raw/master/perf/img.sh

# read the scripts with your editor
$EDITOR *

Stop! Only if you're satisfied that the scripts are trustworthy should you proceed:

# if you trust them, run them
perl color-spaces.pl
python colors.py
bash 24-bit-color.sh
bash img.sh

Keep in mind that it is possible to use both ';' and ':' as Control Sequence delimiters.

According to Wikipedia[1], this behavior is only supported by xterm and konsole.

[1] https://en.wikipedia.org/wiki/ANSI_color

Truecolor Detection

Checking for COLORTERM

VTE, Konsole and iTerm2 all advertise truecolor support by placing COLORTERM=truecolor in the environment of the shell user's shell. This has been in VTE for a while, but is relatively new in Konsole and iTerm2 and has to be enabled at compile time (most packages do not, so you have to compile them yourself from the git source repo).

The S-Lang library has a check that $COLORTERM contains either "truecolor" or "24bit" (case sensitive).

Terminfo has supported the 24-bit TrueColor capability since ncurses-6.0-20180121, under the name "RGB". You need to use the "setaf" and "setab" commands to set the foreground and background respectively.

Having an extra environment variable (separate from TERM) is not ideal: by default it is not forwarded via sudo, ssh, etc, and so it may still be unreliable even where support is available in programs. (It does however err on the side of safety: it does not advertise support when it is not actually supported, and the programs should fall back to using 8-bit color.)

These issues can be ameliorated by adding COLORTERM to:

  • the SendEnv list in /etc/ssh/ssh_config on ssh clients;
  • the AcceptEnv list in /etc/ssh/sshd_config on ssh servers; and
  • the env_keep list in /etc/sudoers.

Despite these problems, it's currently the best option, so checking $COLORTERM is recommended since it will lead to a more seamless desktop experience where only one variable needs to be set.

App developers can freely choose to check for this variable, or introduce their own method (e.g. an option in their config file). They should use whichever method best matches the overall design of their app.

Ideally any terminal that really supports truecolor would set this variable; but as a work-around you might need to put a check in /etc/profile to set COLORTERM=truecolor when $TERM matches any terminal type known to have working truecolor.

case $TERM in
  iterm            |\
  linux-truecolor  |\
  screen-truecolor |\
  tmux-truecolor   |\
  xterm-truecolor  )    export COLORTERM=truecolor ;;
  vte*)
esac

Querying The Terminal

In an interactive program that can read terminal responses, a more reliable method is available, that is transparent to sudo & ssh.

Simply try sending a truecolor value to the terminal, followed by a query to ask what color it currently has. If the response indicates the same color as was just set, then truecolor is supported.

If the response indicates an 8-bit color, or does not indicate a color, or if no response is forthcoming within a few centiseconds, then assume that truecolor is not supported.

$ ( printf '\e[48:2:1:2:3m\eP$qm\e\\' ; xxd -g1 )

^[P1$r48:2:1:2:3m^[\
00000000: 1b 50 31 24 72 34 38 3a 32 3a 31 3a 32 3a 33 6d  .P1$r48:2:1:2:3m

Here we set the background color to RGB(1,2,3) - an unlikely default choice - and request the value that we just set. The response comes back that the request was understood (1), and that the color is indeed 48:2:1:2:3. This tells us also that the terminal supports the colon delimiter. If instead, the terminal did not support truecolor we might see a response like

^[P1$r40m^[\
00000000: 1b 50 31 24 72 34 30 6d 1b 5c 0a              .P1$r40m.\.

This terminal replied that the color is 40 - it has not accepted our request to set 48:2:1:2:3.

^[P0$r^[\
00000000: 1b 50 30 24 72 1b 5c 0a                      .P0$r.\.

This terminal did not even understand the DECRQSS request - its response was CSI+0$r. This does not indicate whether it set the color, but since it doesn't understand how to reply to our request it is unlikely to support truecolor either.

Truecolor Support in Output Devices

Fully Supporting

Terminal Emulators

There are a bunch of libvte-based terminals for GTK2, so they are listed in the another section.

Multiplexers

  • dvtm - not yet supporting truecolor martanne/dvtm#10
  • pymux - tmux clone in pure Python (to enable truecolor run pymux with --truecolor option)
  • screen - has support in 'master' branch, need to be enabled (see 'truecolor' option)
  • tmux - starting from version 2.2 (support since 427b820...)

Re-players

Partial Support

These terminal emulators parse ANSI color sequences, but approximate the true color using a palette or limit number of true colors that can be used at the same time. A 256-color (8-bit) palette is used unless specified.

Note about color differences

Human eyes are sensitive to the primary colors in such a way that the simple Gaussian distance √(R²+G²+B²) gives poor results when trying to find the "nearest" available color as perceived by most humans.

The CIEDE2000 formula provides much better perceptual matching, but it is considerably more complex and may perform very slowly if used blindly [2].

[2] neovim/neovim#793 (comment)

Not Supporting Truecolor

Console Programs + Truecolor

Console Programs Supporting Truecolor

Console Programs Not Supporting Truecolor