netrik hacker's manual
>========================<

[This file describes handling of terminal input/output, and color handling. See hacking.txt or hacking.html for an overview of the manual.] <a name="color" id="color">

Color Handling

Color Representation

For storing color values internally (in the String structure in the layout, see hacking-layout.html) and for passing to the color setting functions (see screen.c below), a one-byte color representation is used much like the one of CGA/EGA/VGA cards:

+-------+-------+-------+-------+-------+-------+-------+-------+
|bright :                       |bright :                       |
|back-  :   background color    |fore-  :   foreground color    |
|ground :                       |ground :                       |
+-------+-------+-------+-------+-------+-------+-------+-------+
    7       6       5       4       3       2       1       0

Text Colors

Background colors are presently only used for link highlighting, so setting the original text color boils down to determining the foreground color.

The text colors of the text on the layouted page are determined when storing the text while Parsing Structure during the layouting process (see hacking-layout.*).

The right color for each text part is determined by a facility named text modes: For every kind of text on the page (normal text, emphasized text, links, layout marks etc.) one constant exists in "enum Text_mode", and some fixed color value corresponds to each one.

The assignement of color values to the text modes is done with the "color_map[]" array, which is declared and initialized in colors.c. (See Colors Schemes below.)

For the various kinds of layout/form/image markers the color is directly taken from the map entry of the appropriate text type and passed to add_string() (see hacking-layout.*). Text modes that affect normal text (content) added during parsing (like links, emphasize), set the "state.text_mode" variable instead; any normal text will be added in the mode currently stored in this variable.

Bold text (strong emphasize) is presently handled in a different manner: The "state.bold" flag is set, causing all text added inside the section to be added with a negated brightness flag. (i.e. normal colors will become bright/bold, while colors for text modes already having "bright" set in the color map will be non-bright instead.)

Color Schemes

Netrik allows using different color schemes, according to one's preferences and terminal colors. However, the color schemes aren't fully customizable for now; there are only two color schemes (defined in colors-dark.c and colors-bright.c) that can be switched by "cfg.bright_background" (using --bright-background or --dark-background).

This is chiefly because our preffered colors only look good on black background, but are unusable on bright background, so different color schemes have to be provided for those situations.

The handling is actually quite simple: Depending on the setting of "cfg.bright_background", either "color_map_bright" (from colors-bright.c) or "color_map_dark" (from colors-dark.c) is assigned to "color_map". This is done in load_color_map(), which is called after reading the command line/config file options in main().

Note that, unless explicitely overridden by --no-force-colors, "cfg.force_colors" is automatically set if "cfg.bright_background" is not set, so a black background will always be used with the dark color scheme, even on a terminal normally having a bright background. This isn't done with the bright color scheme; here, the terminal's default colors are kept.

Monochrome Mode

Netrik now features an extremly crude monochrome mode, which is activated by passing the --bw command line option, or automatically in init_curses(), if the terminal definition lacks color switching capabilities.

The monochrome mode presently completely disables all color/attribute switching.

This is achieved by skipping all color initializations and making all color setting functions nops. Thus, it needs handling only in screen.c (s.b.), and is otherwise completely transparant.

screen.c

screen.c contains a couple of helper functions for handling curses, both in raw and in full screen mode.

init_curses() is responsible for initializing curses in raw mode, and getting some control sequences for setting colors etc. If cfg.inverse_bold was not specified by the user, this is also done here: If the terminal name contains "xterm", cfg.inverse_bold is set, otherwise reset.

start_curses() initializes curses in full screen mode, and sets the foreground/background color pairs. (This is necessary to change colors using curses.) It also initializes several curses settings.

All 63 modifiable color pairs are initialized (pair 0 is hard-wired in curses), in such a manner that every combination of the eight foreground and eight background colors is covered. (Brightness is controled by extra flags, and not coded in the color pairs.) The upper three bits of the six-bit pair number specify the background, the lower 3 the foreground. While the background colors are mapped directly, some magic is necessary for the foreground, to ensure that color pair 0 (which is hard-wired to white on black) will actually correspond to the right color in the color scheme. This is achieved by rotating the foreground color space by one, so color 0 gets white, color 1 gets black etc., instead of color 0 being black and color 7 being white.

For color pairs having black background or white foreground, the respective parts aren't set; the terminal default is used instead. (Unless --force-colors is used.) This is to produce the expected results in most xterms (or other terminals with bright background and black text).

set_color() sets the text attributes in full screen mode. The foreground color is set, and the "bold" attribute if it is a light color.

If the background color is bright, the "blink" attribute is set, which works on most terminals. (Sadly not all...)

As this doesn't work in xterm, another approach has to be used for those: If cfg.inverse_bold was set (see above), the "blink" attribute is used in conjuction with reverse video instead. Of course we have to swap foreground and background colors to get the actually desired ones. (Note that this trick doesn't work on linux console, so we can use it *only* when in an xterm!)

The drawback is that the foreground is always bold but dark in this mode -- we can neiter prevent it getting bold nor get it bright to distinguish between bold and normal foreground as soon as a bright background is set...

set_color_raw() does the same in raw mode. As raw mode has no color pairs to be set during initialization, handling of default colors for white foreground/black background has to be done here. The respective parts simply aren't set at all, thus keeping the terminal default colors.

reset_colors_raw() resets all text attributes to their default values.

SIGWINCH Handling

Handling of the "winch" singnal (which is raised by the terminal each time the text area changes, e.g. when resizing an xterm window) itself is quite simple, as we use the default handler provided by ncurses. On SIGWINCH, ncurses will adjust its own data structures, and also store the pseudo-key "KEY_RESIZE" in the input queue, so we know there was a change.

Only thing we have to care of here is ensuring that ncurses won't resize its data structures while we are making some changes to the screen contents, as this might cause failure when we are outside the screen unexpectedly. This is achieved by the two functions hold_winch() and enable_winch() from winch.c. hold_winch() stops immediate delivery of the winch signal, bud does not descard it; instead, it is put on hold. enable_winch() reenables delivery of the singal. If there is a pending signal generated during the hold phase, it is also delivered now.

To ensure that SIGWINCH arrives only in controlled circumstances, it is put on hold before entering fullscreen mode at the beginning of display() (see hacking-pager.*), and released only after switching to scroll mode again. (Inside end_fullscreen(), which is located in screen.c.) Only for the time of the getch() call (waiting for a keypress) it is enabled, as the resulting KEY_RESIZE will be handled immediately, thus no problems with unexpected changes can arise.

Of course, resizing the pager window is not enough; the page contents also needs to be adapted to the new screen width. This is done by returning to main() (see hacking.*) with RET_WINCH, where ressize() (described in hacking-layout.*) is called before returning to the pager.