Add User-Defined Capabilities to terminfo

A capability with no slot in the standard terminfo tables does not mean hacking the system database: tic -x registers it privately instead. You compile a small private terminfo entry, inspect it with infocmp -x, and leave the system terminal database completely untouched.

The examples use the installed ncurses tools from ncurses-bin 6.4+20240113-1ubuntu2.2, and the manpage describes ncurses 6.4 behaviour. Allow about fifteen minutes. You need a shell, a writable working directory, and a terminal description in source format. No command below needs sudo.

Checkpoint: This guide creates a throwaway database under /tmp. It does not replace a system entry, alter a service, or change your terminal emulator.

1. Confirm the Tool Versions and Options

Check which binaries your shell will actually run first. tic and infocmp can come from a different ncurses installation than the one your package manager reports:

$ command -v tic
/usr/bin/tic
$ tic -V
ncurses 6.4
$ command -v infocmp
/usr/bin/infocmp
$ infocmp -V
ncurses 6.4

Your paths and patch version may differ. The two options that matter here are tic -x, which stores user-defined capabilities, and infocmp -x, which prints them. Without -x, the extended fields are simply left out of the inspection output, which is the most common source of "it didn't work" confusion.

2. Write a Minimal Source Entry

Create a source file in a temporary directory. The first field is the terminal name, followed by a description and comma-separated capabilities:

$ work=$(mktemp -d /tmp/terminfo-usercaps.XXXXXX)
$ cat > "$work/demo.src" <<'EOF'
acme-usercaps|private ncurses capability example,
        cols#80, lines#24,
        clear=\E[H\E[2J,
        E3=\E[3J,
        RGB#1,
        U8#1,
        xx=demo-value,
EOF

In a terminfo source file, \E represents the escape character. E3 tells programs such as clear how to clear scrollback before clearing the screen. RGB#1 asserts an RGB colour convention, while a non-zero U8#1 tells ncurses to use Unicode line-drawing values in a UTF-8 locale. Only add a capability when the terminal and the application really agree about its meaning.

Safety boundary: Capability strings are terminal control sequences. A wrong value can alter terminal state, erase scrollback, or make an application misbehave. Do not copy a sequence from an untrusted source into a shared system entry.

3. Validate the Source Without Compiling It

Ask tic to parse the file and run its checks without producing a compiled entry:

$ tic -x -c "$work/demo.src"
$ printf 'validation status: %s\n' "$?"
validation status: 0

A successful check is deliberately quiet. It confirms syntax and references, but it does not prove that a terminal understands the sequences you supplied. Keep -x present if a capability name looks unknown: the manpage says validation of non-standard string expressions is limited when they are defined through -x, so application-level testing still matters.

Checkpoint: Stop here if the command returns non-zero. Read the diagnostic, fix one source line, and rerun the same check before compiling.

4. Compile into a Private Database

Make a database directory and compile into it explicitly. The -o option keeps the output away from the normal terminfo search locations:

$ mkdir "$work/db"
$ tic -x -o "$work/db" "$work/demo.src"
$ find "$work/db" -type f -maxdepth 2 -print
/tmp/terminfo-usercaps.abc123/db/a/acme-usercaps

The random directory component in the example is illustrative. A directory-tree database stores the entry below a name-derived subdirectory. If you omit -o, tic chooses its configured database location instead and may write to a user or system database, so do not omit it while experimenting.

5. Inspect the Stored Extensions

Point the reader at the private database for this shell, then ask for the extended fields:

$ export TERMINFO="$work/db"
$ infocmp -x -1 acme-usercaps | grep -E 'E3|RGB|U8|xx'
        RGB#1, U8#1,
        E3=\E[3J,
        xx=demo-value,

The exact ordering and surrounding fields can vary. What matters is that all four names are present. Now compare the normal view:

$ infocmp -1 acme-usercaps | grep -E 'E3|RGB|U8|xx' || echo 'extended fields hidden'
extended fields hidden

This is the most common way a working test looks like a failed one: the entry compiled correctly, but infocmp was run without -x. The same switch controls retrieval for this utility. In a curses application, the corresponding library control is use_extended_names.

6. Use the Capability Only When Its Meaning Is Established

Names alone do not make an application consume a capability. ncurses explicitly recognises several extensions, including AX, E3, NQ, RGB, U8 and XM. Other names can be stored for a program that deliberately looks them up, but they have no automatic effect.

Extended key definitions follow a separate naming convention. Names such as kUP2 and kRIT3 represent modified special keys, where suffix values identify combinations such as Shift or Alt. They are allocated at runtime rather than being part of the old fixed key table, so an application must request their strings and map them to key codes; merely placing one in a description does not make every curses program recognise it.

Do not use this demo entry as your real TERM. It describes only a small subset of a terminal. A program relying on cursor movement, colours or input keys may fail when the normal capabilities are absent. Build from a complete, known-good entry with use= when customising a real terminal, and test the result in the application that needs it.

7. Remove the Test State

The example changed only the temporary directory and the current shell's TERMINFO variable. Restore normal lookup by unsetting it:

$ unset TERMINFO
$ printf 'TERMINFO=%s\n' "${TERMINFO-unset}"
TERMINFO=unset

If you have finished inspecting the entry, remove the temporary directory after checking its value carefully:

$ printf 'removing: %s\n' "$work"
removing: /tmp/terminfo-usercaps.abc123
$ rm -r -- "$work"
$ test ! -e "$work" && echo 'temporary database removed'
temporary database removed

Warning: rm -r is irreversible. Do not substitute a broad path or an unset variable. If you need the compiled entry later, keep the directory and remove only the TERMINFO export from shell startup files. This guide did not edit those files.

Done means