WL#12598: portable TTY and VT100 abstraction

Affects: Server-8.0   —   Status: Complete   —   Priority: Medium

Motivation

In console applications like "mysqlrouter --bootstrap" the user gets presented with lots of information with different importance.

Highlighting

To make it easier for the user to spot the important parts, the output should be able to make use of features like Bold, Underline, Colors, ... if the terminal supports it.

Example: googletest

With 100s of tests run by googletest and 1 failing it helps a lot to find the failing test by scrolling through the output on the console:

[ RUN      ] testname
[       OK ] testname
[ RUN      ] testname
[       OK ] testname
[ RUN      ] testname
[  FAILED  ] testname
[ RUN      ] testname
[       OK ] testname

By using coloring "FAILED" tests in red in a long list of green "OK" tests, the failing testings are easier to spot:

[ RUN      ] testname
[       OK ] testname
[ RUN      ] testname
[       OK ] testname
[ RUN      ] testname
[  FAILED  ] testname
[ RUN      ] testname
[       OK ] testname

Filtering output

In case the output goes to something else than a console (a pipe, a file, ...), the formatting should not be sent out to allow interaction with tools like "grep" to search the output.

User Stories

  1. As a user I want to see be able to easily spot an ERROR in a long output.
  2. As a user I want to filter the output of a console app easily with tools like grep.

Limitations

Platform Support

Unix System
most terminal emulators support the ANSI ESC sequences (vt100) and extensions of it
Windows
Since 2016 Windows 10 supports VT100 sequences in its console if enabled.

Goal

Goal is to provide

  1. a VT100 ESC Sequence abstraction (names instead of raw esc sequences)
  2. a facility to detect if a output-stream is a TTY
  3. a facility to detect if a TTY is supports VT100
  4. a facility to degrade output nicely for non-VT100-capable terminals

Background

ESC sequences

ESC sequences are defined in ANSI X3.64 and later in ISO 6429 in the form of:

seq = ESC one_byte_cmd|other_cmd $
one_byte_cmd = 0x40–0x49;
other_cmd = osc | csi | ...;
osc = ']' ...;
csi = '[' csi_param* csi_intermediate* csi_final;
csi_param = 0x30–0x3F;
csi_intermediate = 0x20–0x2F;
csi_final = 0x40–0x7E;

E.g. to set text in red:

ESC[31m

Implementation

Abstractions

To make working with ESC sequences easier, an abstraction is added to:

Vt100::render(Vt100::Render::Foreground::Red)

which generates:

ESC[31m

Details

The constants are generated as:

Vt100::Color::Red = 1
Vt100::Color::BrightRed = 60 + Vt100::Color::Red
...
Vt100::Render::ForegroundRed = 30 + Vt100::Color::Red
...
Vt100::Render::BackgroundRed = 40 + Vt100::Color::Red
...
Vt100::Render::ForegroundBrightRed = 30 + Vt100::Color::BrightRed
...

high-level abstractions

Instead of calling the low-level Render sequences, several high-level functions are provided to work with color directly:

Vt100::foreground(Vt100::Color::Red)

Colors are defined by their "name":

Vt100::Color::Red = 1
Vt100::Color::BrightRed = 60 + Vt100::Color::Red

Or as index into 88/256 palette:

Vt100::foreground(155)  // lighter red

Or as RBG triple:

Vt100::foreground(std::make_tuple(255, 0, 0))  // full red

Filtering VT100 sequences

To make it easier for application and readability of the code, it is assumed that applications also generated ESC sequences when writing to output-streams (std::ostream).

If the TTY the stream writes to in the end doesn't support ESC sequences it a filter is applied that removes the ESC sequences on the fly.