CMATRIX · Volume 2
Curses, terminfo and the Library That Draws It
A terminal program cannot simply decide to put a character in the middle of the screen. The terminal is, historically and architecturally, a stream device: bytes go in one end and appear at the cursor. Everything else — moving the cursor, choosing a colour, clearing a region, turning a character bold — happens by sending escape sequences the terminal has agreed to interpret. Those sequences differ by terminal type, and there have been hundreds of terminal types. cmatrix is a program that does nothing but place characters at chosen positions in chosen colours, so almost all of its real work is delegated to the library that knows those sequences: ncurses.

TERM values vt100 and xterm descend from that lineage. — DEC VT100 terminal at the Living Computer Museum, photograph by Jason Scott. CC BY 2.0, via Wikimedia Commons.2.1 The problem curses exists to solve
The original solution to terminal diversity was a database: a file describing, for each terminal type, which byte sequence performs which operation. The BSD lineage called this termcap, and programs consulted it through a small library; AT&T’s System V replaced it with terminfo, a compiled form that was faster to look up. A program that wants to move the cursor asks the library, the library consults the database entry named by the TERM environment variable, and the right bytes go out.
Above that sits curses: a library that presents the screen as an addressable array of cells rather than a stream, tracks what is currently displayed, and — the part that matters for performance — sends only the sequences needed to turn the current screen into the requested one. An application writes into a virtual screen and calls refresh(); the library computes the difference and emits the minimum.
ncurses is the free implementation of that idea, and the one nearly every Linux system actually uses.
2.2 Where ncurses came from
The lineage is documented in the library’s own FAQ and history files, and is worth stating precisely because it is frequently garbled.
- The name traces back to pcurses, written by Pavel Curtis in 1982 on a 4.3BSD system at Cornell. Pieces of that work survive in the modern library, including the
Capscapability database and the awk scripts that build the terminfo compiler. - Zeyd Ben-Halim developed ncurses from pcurses; the first widely used release, 1.8.1, appeared in November 1993.
- Eric S. Raymond took up development from late 1993 or early 1994.
- Juergen Pfeifer wrote most of the forms and menus libraries, merged in August 1995.
- Thomas E. Dickey has done most of the configuration work and has carried the ongoing maintenance since March 1995 — by a wide margin the longest tenure on the project.
Two further points are commonly misstated. The first is the relationship to AT&T: ncurses began as a freely distributable clone of System V Release 4 curses and later tracked the X/Open Curses specification, gaining wide-character support through a separate ncursesw build. The second is the licence: in early 1998 the principal developers assigned copyright to the Free Software Foundation under an MIT-style licence, not the GPL. ncurses is one of the FSF’s own packages that is deliberately not copyleft, which is precisely why a GPL program like cmatrix and a proprietary one can both link against it.

make menuconfig — the most widely seen ncurses application there is. Boxes, highlighted hotkeys, a status line and a button bar, all produced by the same cell-addressing library that draws cmatrix. — File:Linux 4.4.2 ncurses configuration.png by Davod. GPL, via Wikimedia Commons.2.3 What cmatrix asks of it
The dependency is visible from the outside: the Ubuntu package declares libncurses6 and libtinfo6. The second is the terminfo half — the part that reads the database and knows what TERM means. A program this small pulling in both makes the division of labour unusually legible.
From inside the source, the calls cmatrix makes are a compact tour of the library’s basics:
initscr(); /* set up the screen, read terminfo for $TERM */
savetty(); /* remember the terminal's current mode ... */
nonl(); /* ... then change it: no newline translation */
cbreak(); /* deliver keys immediately, no line buffering */
noecho(); /* do not echo typed characters */
timeout(0); /* wgetch() returns ERR instead of blocking */
leaveok(stdscr, TRUE);
curs_set(0); /* hide the cursor */
Each line removes one piece of ordinary terminal behaviour that would spoil the effect. Without noecho() a keystroke would appear in the falling glyphs; without cbreak() the program would not see a key until the user pressed Return; without timeout(0) the animation would stop dead waiting for input, because cmatrix polls for keys inside the same loop that draws frames. curs_set(0) hides the blinking cursor that would otherwise sit in the middle of the rain.
Colour is handled the curses way, through numbered pairs rather than direct attributes:
if (has_colors()) {
start_color();
if (use_default_colors() != ERR) {
init_pair(COLOR_GREEN, COLOR_GREEN, -1); /* -1: keep the terminal's own background */
...
The use_default_colors() branch is the difference between a program that works in a transparent or themed terminal and one that stamps a black rectangle over it. Where the extension is available, -1 means “whatever background the terminal already has”; where it is not, the code falls back to an explicit COLOR_BLACK background. The README credits a contributor specifically for transparent-terminal support, and this is where it lives.
Three more library facilities carry real weight:
napms(update * 10)— the frame delay, in milliseconds. The defaultupdateof 4 gives a 40 ms pause, so roughly twenty-five frames a second before drawing time is counted.resizeterm()/wresize()— called after the program catchesSIGWINCH, reads the new size with theTIOCGWINSZioctl, and reallocates its buffers. Resizing a terminal runningcmatrixreflows the rain rather than corrupting it.addwstr()— the wide-character output path, used becauseaddch()cannot carry a multi-byte character. This is what makes the kana of-cmode and theλof-mmode possible, and it is why the source begins with#define NCURSES_WIDECHAR 1and callssetlocale(LC_ALL, "")before anything else.
2.4 The parts that are not portable
Three of the flags reach past ncurses to the operating system, and they mark the boundary of what the library can abstract.
-l (Linux console mode) and -x (X-window font mode) switch to an alternate glyph set on the assumption that a special font has been loaded — hence the separate cmatrix-xfont package and the kbd recommendation. The program does this by shelling out to consolechars or setfont, chosen at build time:
if (va_system("setfont matrix") != 0)
c_die(" There was an error running setfont ...");
Running an external program to change a font is a Linux-console idiom with no equivalent in a terminal emulator, and it is why these modes fail politely rather than working everywhere.
-t <tty> uses newterm() and set_term() to drive a different terminal than the one the program was launched from — a genuinely useful curses facility for painting one screen while keeping control on another.
The screensaver mode -s, which exits on the first keystroke, optionally pushes the key that woke it back into the input queue using the TIOCSTI ioctl, so the keystroke is not lost. That path is compiled in only when USE_TIOCSTI is defined, and for good reason: TIOCSTI injects characters into a terminal’s input as though they had been typed, which is a long-documented local privilege-escalation primitive — Debian bug reports about tty hijacking through it in su go back more than a decade. Linux 6.2 added a dev.tty.legacy_tiocsti sysctl that can make the call require CAP_SYS_ADMIN, with its default set at build time by CONFIG_LEGACY_TIOCSTI. Which distributions ship that default off could not be established for this writeup and varies. The safe reading is that -s exits on a keypress everywhere, and that whether the waking keystroke is replayed into the shell afterwards depends on the kernel configuration and on how the binary was compiled.
2.5 Why this is the interesting half
The falling-glyph algorithm in the next volume is perhaps sixty lines of arithmetic. Everything that makes those sixty lines appear on a screen at all — cursor addressing, colour pairs, bold attributes, wide characters, non-blocking input, resize handling, restoring the terminal on exit so the shell is usable afterwards — is ncurses, resting on a terminal-description database that has been maintained continuously since the era of the hardware in the photograph above. The toy is thirty years younger than its own foundation.
Comments (0)