WL#12598: portable TTY and VT100 abstraction
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
- As a user I want to see be able to easily spot an ERROR in a long output.
- 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
- a VT100 ESC Sequence abstraction (names instead of raw esc sequences)
- a facility to detect if a output-stream is a TTY
- a facility to detect if a TTY is supports VT100
- 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.