support for the kernel 7.1 modifier-aware KT_CSI keysym type
v2 cover support for the kernel 7.1 modifier-aware KT_CSI keysym type
Linux kernel 7.1 introduces modifier-aware escape sequence generation for the console keyboard:
4af70f151671 ("vt: add modifier support to cursor keys") 5cba06c71c71 ("vt: add KT_CSI keysym type for modifier-aware CSI sequences") c1d2deb6492f ("vt: add fallback to plain map for modifier-aware key types")
The new KT_CSI keysym type produces xterm-style CSI tilde sequences (ESC [ n ~, or ESC [ n ; mod ~ when Shift/Alt/Control are held) with the modifier state encoded at runtime. A single plain map entry covers all modifier combinations, instead of consuming a func_table string slot per combination.
This series adds the userspace counterpart:
Patch 1 teaches libkeymap the Csi_* keysym names (Csi_Home, Csi_End, Csi_Delete, Csi_Insert, Csi_PgUp, Csi_PgDn, Csi_F1..Csi_F20, matching the kernel UAPI values) and documents modifier-aware keys in keymaps(5).
Patch 2 adds a contrib script (with optional systemd unit) that reconciles the "linux" terminfo entry with the loaded keymap: F1-F5 in CSI form and Backtab as ESC [ Z are not what the stock terminfo entry expects.
Patch 3 keeps the default keymaps on their traditional bindings and adds an opt-in overlay (linux-keys-csi.inc) that switches the function and navigation keys to the Csi_* keysyms. The boot script applies it only when the running kernel supports KT_CSI, so nothing changes on older kernels.
Changes since v1 (addressing Alexey's review):
- Patch 1: bump NR_TYPES in summary.c so KT_CSI appears in "dumpkeys --long-info".
- Patch 3: reworked from an in-place conversion (which broke pre-7.1 systems) into the opt-in overlay described above.
v1: https://lore.kernel.org/kbd/[email protected]
Tested with make check, a staged "make install" (overlay installs and resolves by name), and the terminfo script exercised live on Fedora 43.
Nicolas Pitre (3):
libkeymap: add support for KT_CSI keysym type
Add kbd-terminfo-fixup script and systemd service
keymaps: add opt-in CSI overlay and load it when the kernel supports
it
.gitignore | 1 + Makefile.am | 16 + configure.ac | 18 ++ contrib/kbd-terminfo-fixup | 303 +++++++++++++++++++ contrib/kbd-terminfo-fixup.service.in | 13 + data/keymaps/i386/include/linux-keys-csi.inc | 83 +++++ docs/man/man5/keymaps.5 | 77 +++++ src/libkeymap/ksyms.c | 3 +- src/libkeymap/summary.c | 2 +- src/libkeymap/syms.ktyp.h | 39 +++ tests/data/keymap0-summary.txt | 26 ++ 11 files changed, 579 insertions(+), 2 deletions(-) create mode 100755 contrib/kbd-terminfo-fixup create mode 100644 contrib/kbd-terminfo-fixup.service.in create mode 100644 data/keymaps/i386/include/linux-keys-csi.inc
Signature · 1 line
2.54.0
PATCH v2 1/3 libkeymap: add support for KT_CSI keysym type
Add support for the new KT_CSI keysym type which provides modifier-aware CSI escape sequences for navigation and function keys. This eliminates the need to exhaust the limited func_table string entries for modifier combinations.
The new Csi_* keysyms (Csi_Home, Csi_End, Csi_Delete, Csi_Insert, Csi_PgUp, Csi_PgDn, Csi_F1-F20) generate tilde-format CSI sequences with automatic modifier encoding when used with a supporting kernel.
The kernel automatically falls back to the plain keymap entry when modifiers are held, encoding the modifier state into the output sequence, so only plain map entries need to be defined.
Also bump NR_TYPES in summary.c so that the new KT_CSI type is included in the action-code range listing of "dumpkeys --long-info", add documentation for modifier-aware keys to keymaps(5), and update the libkeymap test fixture for the extended symbol table.
Note: Requires Linux kernel 7.1 or later for KT_CSI support. On older kernels these keysyms load without effect on consoles in Unicode mode (the affected keys produce no output), and are rejected with EINVAL on non-Unicode consoles (loadkeys reports an error for each such entry but carries on).
--- docs/man/man5/keymaps.5 | 77 ++++++++++++++++++++++++++++++++++ src/libkeymap/ksyms.c | 3 +- src/libkeymap/summary.c | 2 +- src/libkeymap/syms.ktyp.h | 39 +++++++++++++++++ tests/data/keymap0-summary.txt | 26 ++++++++++++ 5 files changed, 145 insertions(+), 2 deletions(-)
docs/man/man5/keymaps.5 +77 -0
@@ -353,6 +353,83 @@ and describe how two bytes are combined to form a third one (when a dead accent or compose key is used). This is used to get accented letters and the like on a standard keyboard. +.SH "MODIFIER-AWARE KEYS" +Certain key types automatically encode the current modifier state into +the escape sequence they produce. This eliminates the need to define +separate keymap entries for each modifier combination. +.SS "Cursor Keys" +The cursor keys (Up, Down, Left, Right) automatically include modifier +information when Shift, Alt, or Control are held. With no modifiers, +they produce the standard sequences (e.g., ESC [ A for Up). With +modifiers, they produce extended sequences in the format ESC [ 1 ; \fImod\fP X, +where \fImod\fP encodes the modifier state and X is the direction letter. +.LP +The modifier value is calculated as: 1 + (Shift?1:0) + (Alt?2:0) + (Control?4:0). +AltGr counts as Alt for this purpose. +For example, Shift+Up produces ESC [ 1 ; 2 A, and Control+Alt+Up +produces ESC [ 1 ; 7 A. +.SS "CSI Keys" +The Csi_* keysyms provide similar modifier-aware behavior for navigation +and function keys, generating sequences in the CSI tilde format +(ESC [ \fIn\fP ~ or ESC [ \fIn\fP ; \fImod\fP ~ with modifiers). +.LP +Available CSI keysyms and their unmodified sequences: +.RS +.TP 16 +.B Csi_Home +ESC [ 1 ~ +.TP +.B Csi_Insert +ESC [ 2 ~ +.TP +.B Csi_Delete +ESC [ 3 ~ +.TP +.B Csi_End +ESC [ 4 ~ +.TP +.B Csi_PgUp +ESC [ 5 ~ +.TP +.B Csi_PgDn +ESC [ 6 ~ +.TP +.B Csi_F1 \fR...\fB Csi_F12 +ESC [ 11 ~ through ESC [ 24 ~ (skipping 16 and 22) +.TP +.B Csi_F13 \fR...\fB Csi_F20 +ESC [ 25 ~ through ESC [ 34 ~ (skipping 27 and 30) +.RE +.LP +Note: The Csi_F* sequences follow the xterm numbering scheme, including +its gaps in the parameter numbering, which differs from the traditional +Linux console F1-F5 sequences (ESC [ [ A through ESC [ [ E). +.SS "Fallback Behavior" +When a cursor or CSI key is pressed with modifiers, and no explicit binding +exists for that modifier combination, the kernel automatically falls back +to the plain (unmodified) keymap entry. The modifier state is still encoded +in the output sequence. +.LP +This means you only need to define the plain keymap entry: +.LP +.RS +.nf +keycode 102 = Csi_Home +.fi +.RE +.LP +and all modifier combinations (Shift+Home, Control+Home, etc.) will +automatically produce the correct modified sequences without additional +keymap entries. +.LP +.B Note: +Modifier-aware key behavior requires Linux kernel 7.1 or later. +On older kernels, cursor keys produce their traditional unmodified +sequences, and Csi_* keysyms have no effect: on a console in Unicode +mode the corresponding keys produce no output, while on a non-Unicode +console +.BR loadkeys (1) +reports an error for each Csi_* entry. .SH ABBREVIATIONS Various abbreviations can be used with kbd-0.96 and later. .TP
src/libkeymap/ksyms.c +2 -1
@@ -52,7 +52,8 @@ static const syms_entry syms[] = { { NULL, 0 }, /* KT_LETTER */E (sticky_syms),/* KT_SLOCK */ { NULL, 0 }, /* KT_DEAD2 */ - E(brl_syms) /* KT_BRL */ +E (brl_syms),/* KT_BRL */ + E(csi_syms) /* KT_CSI */ };#undef E
src/libkeymap/summary.c +1 -1
@@ -78,7 +78,7 @@ int lk_get_kmapinfo(struct lk_ctx *ctx, struct kmapinfo *res)return 0 ; } -#define NR_TYPES 15 +#define NR_TYPES 16void lk_dump_summary (struct lk_ctx *ctx,FILE *fd,int console) {
src/libkeymap/syms.ktyp.h +39 -0
@@ -588,3 +588,42 @@ static const char *const brl_syms[] = {"Brl_dot9" ,"Brl_dot10" }; + +/* +* Keysyms whose KTYP is KT_CSI. +* Index is the CSI parameter number for ESC [ <index> ~ sequences. +*/ +static const char *const csi_syms[] = { +"" ,/* 0: unused */ +"Csi_Home" ,/* 1: ESC [ 1 ~ */ +"Csi_Insert" ,/* 2: ESC [ 2 ~ */ +"Csi_Delete" ,/* 3: ESC [ 3 ~ */ +"Csi_End" ,/* 4: ESC [ 4 ~ */ +"Csi_PgUp" ,/* 5: ESC [ 5 ~ */ +"Csi_PgDn" ,/* 6: ESC [ 6 ~ */ +"" ,"" ,"" ,"" ,/* 7-10: unused */ +"Csi_F1" ,/* 11: ESC [ 11 ~ */ +"Csi_F2" ,/* 12: ESC [ 12 ~ */ +"Csi_F3" ,/* 13: ESC [ 13 ~ */ +"Csi_F4" ,/* 14: ESC [ 14 ~ */ +"Csi_F5" ,/* 15: ESC [ 15 ~ */ +"" ,/* 16: unused */ +"Csi_F6" ,/* 17: ESC [ 17 ~ */ +"Csi_F7" ,/* 18: ESC [ 18 ~ */ +"Csi_F8" ,/* 19: ESC [ 19 ~ */ +"Csi_F9" ,/* 20: ESC [ 20 ~ */ +"Csi_F10" ,/* 21: ESC [ 21 ~ */ +"" ,/* 22: unused */ +"Csi_F11" ,/* 23: ESC [ 23 ~ */ +"Csi_F12" ,/* 24: ESC [ 24 ~ */ +"Csi_F13" ,/* 25: ESC [ 25 ~ */ +"Csi_F14" ,/* 26: ESC [ 26 ~ */ +"" ,/* 27: unused */ +"Csi_F15" ,/* 28: ESC [ 28 ~ */ +"Csi_F16" ,/* 29: ESC [ 29 ~ */ +"" ,/* 30: unused */ +"Csi_F17" ,/* 31: ESC [ 31 ~ */ +"Csi_F18" ,/* 32: ESC [ 32 ~ */ +"Csi_F19" ,/* 33: ESC [ 33 ~ */ +"Csi_F20" ,/* 34: ESC [ 34 ~ */ +};
tests/data/keymap0-summary.txt +26 -0
@@ -810,6 +810,32 @@ nr of compose definitions in actual use: 0 0x0e08 Brl_dot8 0x0e09 Brl_dot9 0x0e0a Brl_dot10 +0x0f01 Csi_Home +0x0f02 Csi_Insert +0x0f03 Csi_Delete +0x0f04 Csi_End +0x0f05 Csi_PgUp +0x0f06 Csi_PgDn +0x0f0b Csi_F1 +0x0f0c Csi_F2 +0x0f0d Csi_F3 +0x0f0e Csi_F4 +0x0f0f Csi_F5 +0x0f11 Csi_F6 +0x0f12 Csi_F7 +0x0f13 Csi_F8 +0x0f14 Csi_F9 +0x0f15 Csi_F10 +0x0f17 Csi_F11 +0x0f18 Csi_F12 +0x0f19 Csi_F13 +0x0f1a Csi_F14 +0x0f1c Csi_F15 +0x0f1d Csi_F16 +0x0f1f Csi_F17 +0x0f20 Csi_F18 +0x0f21 Csi_F19 +0x0f22 Csi_F20 The following synonyms are recognized:
Signature · 1 line
2.54.0
PATCH v2 2/3 Add kbd-terminfo-fixup script and systemd service
In reply to Nicolas PitreThis script handles two terminfo mismatches with the linux terminal:
1. The Linux console has a long-standing discrepancy where F1-F5 use non-standard sequences (\e[[A through \e[[E) while F6 and above use standard CSI sequences (\e[17~ etc.). The new Csi_F1 through Csi_F5 keysyms (Linux kernel 7.1 and later) fix this by making F1-F5 use CSI sequences as well (\e[11~ through \e[15~). However, the terminfo entry for "linux" still expects the old sequences, causing applications to not recognize F1-F5.
2. The Backtab keysym produces the standard backtab sequence \e[Z, but the linux terminfo entry expects \e^I (ESC + Tab).
Some applications may recognize these sequences regardless of terminfo, but not all do. The kbd-terminfo-fixup script ensures consistent behavior across all terminfo-aware applications by detecting Csi_F* and Backtab keysyms in the current keymap (via dumpkeys) and installing an updated "linux" terminfo entry in /etc/terminfo/.
The override is always rebuilt from the distribution's pristine terminfo entry (looked up in /usr/share/terminfo, /lib/terminfo or /usr/lib/terminfo) rather than from the active search path, so the script never feeds on its own output and fixups that are no longer needed are automatically dropped when the keymap changes. Rewrites are skipped when the installed override is already up to date.
The generated entry is tagged with a "(kbd-terminfo-fixup)" marker in its terminfo description field. An /etc/terminfo/l/linux file without that marker is treated as the administrator's own and is never overwritten or removed.
A systemd service unit is provided to run this automatically at boot, after systemd-vconsole-setup.service loads the keymap. The service is only installed when systemd is detected. The unit file is generated at make time so that the libexec path is fully expanded.
Configure options:
--with-systemdsystemunitdir=DIR Set systemd unit directory
(auto-detected via pkg-config)
--- .gitignore | 1 + Makefile.am | 16 ++ configure.ac | 18 +++ contrib/kbd-terminfo-fixup | 222 ++++++++++++++++++++++++++ contrib/kbd-terminfo-fixup.service.in | 13 ++ 5 files changed, 270 insertions(+) create mode 100755 contrib/kbd-terminfo-fixup create mode 100644 contrib/kbd-terminfo-fixup.service.in
.gitignore +1 -0
@@ -105,3 +105,4 @@ tests/libkbdfile/libkbdfile-test[0-9][0-9] tests/libkeymap/libkeymap-test[0-9][0-9] tests/libkfont/libkfont-test[0-9][0-9] tests/testsuite +contrib/kbd-terminfo-fixup.service
Makefile.am +16 -0
@@ -33,6 +33,22 @@ EXTRA_DIST = \ CREDITS \ contrib docs +pkglibexec_SCRIPTS = contrib/kbd-terminfo-fixup + +if HAVE_SYSTEMD +systemdsystemunit_DATA = contrib/kbd-terminfo-fixup.service +endif + +# Substitute directory variables at make time so they are fully +# expanded (configure would leave ${exec_prefix} unexpanded). +contrib/kbd-terminfo-fixup.service: contrib/kbd-terminfo-fixup.service.in Makefile + $(AM_V_GEN)$(MKDIR_P) contrib && \ + $(SED) -e 's|@libexecdir[@]|$(libexecdir)|g' \ + -e 's|@PACKAGE[@]|$(PACKAGE)|g' \ + $(srcdir)/contrib/kbd-terminfo-fixup.service.in > $@ + +CLEANFILES = contrib/kbd-terminfo-fixup.service + SUBDIRS = src data po docs if BUILD_TESTS SUBDIRS += tests
configure.ac +18 -0
@@ -368,6 +368,22 @@ AS_IF([test "x$USE_XKB" != xno], [USE_XKB=no]) AM_CONDITIONAL(USE_XKB, test "$USE_XKB" = "yes") +AC_ARG_WITH([systemdsystemunitdir], + [AS_HELP_STRING([--with-systemdsystemunitdir=DIR], + [directory for systemd service files @<:@default=auto@:>@])], + [], + [with_systemdsystemunitdir=auto] +) +AS_IF([test "$with_systemdsystemunitdir" = "auto"], [ + PKG_CHECK_VAR([systemdsystemunitdir], [systemd], [systemdsystemunitdir], + [with_systemdsystemunitdir="$systemdsystemunitdir"], + [with_systemdsystemunitdir=no]) +]) +AS_IF([test "$with_systemdsystemunitdir" != "no"], [ + AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir]) +]) +AM_CONDITIONAL(HAVE_SYSTEMD, test "$with_systemdsystemunitdir" != "no") + AC_MSG_NOTICE([generation of Makefiles...]) # Remove -h (dereference) from am__tar
@@ -431,4 +447,6 @@ AC_MSG_RESULT([ standalone libkeymap: ${BUILD_LIBKEYMAP} standalone libkfont: ${BUILD_LIBKFONT} xkb support: ${USE_XKB} + + systemd unit dir: ${with_systemdsystemunitdir} ])
contrib/kbd-terminfo-fixup new file +222 -0
222 lines not on this page · raw message
contrib/kbd-terminfo-fixup.service.in new file +13 -0
@@ -0,0 +1,13 @@ +[Unit] +Description=Update terminfo for CSI function keys +Documentation=man:keymaps(5) +After=systemd-vconsole-setup.service +ConditionPathExists=/dev/tty0 + +[Service] +Type=oneshot +ExecStart=@libexecdir@/@PACKAGE@/kbd-terminfo-fixup --update +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target
Signature · 1 line
2.54.0
PATCH v2 3/3 keymaps: add opt-in CSI overlay and load it when the kernel supports it
In reply to Nicolas PitreThe KT_CSI keysyms only work on Linux 7.1 and later. Converting the shared include files (linux-keys-bare.inc and friends) in place would silently switch every keymap that includes them to CSI sequences, which regress the function and navigation keys on older kernels: on a Unicode console the keys produce nothing, and on a non-Unicode console loadkeys reports errors. Since kbd is updated independently of the kernel, this would break existing installs.
Instead, keep the default include files as the traditional (legacy) bindings and add an opt-in overlay, linux-keys-csi.inc, that rebinds the function keys (F1-F12) and navigation keys (Home, End, Insert, Delete, PageUp, PageDown) to the Csi_* keysyms. It carries no "keymaps" line so loadkeys merges it onto an existing keymap rather than clearing unrelated tables, and it only sets the specific (modifier, keycode) entries it
needs: the legacy F13-F36 shift/control bindings are overridden, while Alt/Control+Alt console switching and the Shift+PageUp/PageDown scroll bindings are left intact.
Two ways to use it:
- Include it after linux-keys-bare in a custom keymap:
include "linux-keys-bare"
include "linux-keys-csi"
- Let kbd-terminfo-fixup apply it at boot. The script now detects whether the running kernel supports KT_CSI (by version, since a runtime probe is unreliable on Unicode consoles) and, if so, loads the overlay before reconciling terminfo. For the same reason it only updates the terminfo F1-F5 entries when the kernel honors KT_CSI; Backtab works on any kernel and is always reconciled. KBD_FORCE_CSI=1/0 overrides the detection for backported kernels. If the script is not installed, or the kernel is older than 7.1, the legacy bindings remain in effect.
This makes the migration safe by construction: nothing changes for systems on pre-7.1 kernels, and the modifier-aware keys are enabled only where the kernel can honor them.
--- contrib/kbd-terminfo-fixup | 117 ++++++++++++++++--- contrib/kbd-terminfo-fixup.service.in | 2 +- data/keymaps/i386/include/linux-keys-csi.inc | 83 +++++++++++++ 3 files changed, 183 insertions(+), 19 deletions(-) create mode 100644 data/keymaps/i386/include/linux-keys-csi.inc
contrib/kbd-terminfo-fixup +99 -18
@@ -1,21 +1,35 @@ #!/bin/sh -# kbd-terminfo-fixup - Update terminfo when new kbd keysym bindings are used +# kbd-terminfo-fixup - Enable modifier-aware console keys and reconcile terminfo # -# This script handles two terminfo mismatches: +# This script does two things, in order: # -# 1. The Linux console has a long-standing discrepancy where F1-F5 use -# non-standard sequences (\e[[A through \e[[E) while F6 and above use -# standard CSI sequences. The Csi_F1 through Csi_F5 keysyms fix this. +# A. Keymap upgrade (kernel-aware migration). +# If the running kernel supports the KT_CSI keysym type (Linux 7.1 and +# later), it loads the linux-keys-csi.inc overlay, rebinding the +# function and navigation keys to modifier-aware CSI sequences. The +# default keymaps ship the traditional (legacy) bindings, so a system +# running an older kernel - or one where this script is not installed - +# keeps working unchanged. The CSI variant is enabled at runtime only +# where the kernel can honor it. Set KBD_FORCE_CSI=1 (or =0) to force +# the overlay on (or off) regardless of the kernel version. # -# 2. The Backtab keysym produces the standard \e[Z sequence, but the -# linux terminfo entry expects \e^I (ESC + Tab). +# B. Terminfo reconciliation. Two console sequences do not match the +# stock "linux" terminfo entry: # -# This script detects when these keysyms are in use and installs a -# modified "linux" terminfo entry in /etc/terminfo so applications can -# correctly recognize them. +# 1. The Linux console has a long-standing discrepancy where F1-F5 use +# non-standard sequences (\e[[A through \e[[E) while F6 and above +# use standard CSI sequences. The Csi_F1 through Csi_F5 keysyms fix +# this. # -# The override is always rebuilt from the distribution's pristine entry -# (never from the active search path, which may include a previous +# 2. The Backtab keysym produces the standard \e[Z sequence, but the +# linux terminfo entry expects \e^I (ESC + Tab). +# +# This script detects when these keysyms are in use (after the overlay +# above, if any) and installs a modified "linux" terminfo entry in +# /etc/terminfo so applications can correctly recognize them. +# +# The terminfo override is always rebuilt from the distribution's pristine +# entry (never from the active search path, which may include a previous # override), so fixups that are no longer needed are automatically # dropped when the keymap changes. The generated entry is tagged with a # "(kbd-terminfo-fixup)" marker in its description; an existing
@@ -29,7 +43,8 @@ # Usage: kbd-terminfo-fixup [--update | --remove | --check] # # Options: -# --update Create/update terminfo if CSI keysyms detected (default) +# --update Apply the CSI overlay if supported, then create/update +# terminfo if CSI keysyms are detected (default) # --remove Remove system terminfo override # --check Check if CSI keysyms are in use (exit 0 if yes)
@@ -38,6 +53,50 @@ set -e TERMINFO_DIR="${TERMINFO_DIR:-/etc/terminfo}" MARKER="kbd-terminfo-fixup" +# Keymap overlay loaded by name from the kbd keymap search path. The +# ".inc" is given explicitly because loadkeys only auto-appends .map/.kmap. +OVERLAY_KEYMAP="linux-keys-csi.inc" + +# Return success if the running kernel supports the KT_CSI keysym type. +# KT_CSI and modifier-aware cursor keys were added in Linux 7.1. A runtime +# probe is unreliable (an old kernel silently accepts unknown keysym types +# on a Unicode-mode console), so gate on the kernel version, with an +# override for backports. +kernel_supports_csi() { + case "${KBD_FORCE_CSI:-}" in + 1|yes|true) return 0 ;; + 0|no|false) return 1 ;; + esac + kver=$(uname -r) + kmaj=${kver%%.*} + krest=${kver#*.} + kmin=${krest%%.*} + # Keep only the leading digits (e.g. "1-rc8" -> "1") + kmaj=${kmaj%%[!0-9]*} + kmin=${kmin%%[!0-9]*} + if [ -z "$kmaj" ]; then return 1; fi + if [ -z "$kmin" ]; then kmin=0; fi + if [ "$kmaj" -gt 7 ]; then + return 0 + fi + if [ "$kmaj" -eq 7 ] && [ "$kmin" -ge 1 ]; then + return 0 + fi + return 1 +} + +# Apply the modifier-aware CSI keymap overlay if the kernel supports it. +# Best effort: never fail the boot if the overlay cannot be loaded. +apply_csi_overlay() { + kernel_supports_csi || return 0 + command -v loadkeys >/dev/null 2>&1 || return 0 + if loadkeys "$OVERLAY_KEYMAP" >/dev/null 2>&1; then + echo "Applied CSI keymap overlay ($OVERLAY_KEYMAP)" + else + echo "Warning: kernel supports KT_CSI but loading $OVERLAY_KEYMAP failed" >&2 + fi +} + # Check which Csi_F1-F5 keysyms are in use # (-w prevents e.g. Csi_F11 from matching as Csi_F1) detect_csi_keys() {
@@ -91,13 +150,26 @@ do_check() { do_update() { target="${TERMINFO_DIR}/l/linux" + # Step A: enable the CSI keymap where the kernel supports it. Done + # before detection so the terminfo update below sees the freshly + # bound Csi_* keysyms. This is independent of terminfo ownership. + apply_csi_overlay + # Never touch an override file we did not create if [ -f "$target" ] && ! override_is_ours; then echo "$target was not created by this script; leaving it alone" return 0 fi - csi_keys=$(detect_csi_keys) + # The Csi_F* terminfo fix only makes sense when the kernel honors + # KT_CSI; otherwise those keys emit nothing and kf1-5 must stay at the + # legacy sequences. Backtab is an ordinary string keysym that works on + # any kernel, so it is always considered. + if kernel_supports_csi; then + csi_keys=$(detect_csi_keys) + else + csi_keys= + fi backtab=$(detect_backtab) if [ -z "$csi_keys" ] && [ -z "$backtab" ]; then
@@ -176,15 +248,24 @@ show_usage() { cat <<EOF Usage: kbd-terminfo-fixup [--update | --remove | --check] -Update system terminfo when new kbd keysym bindings are used. +Enable modifier-aware console keys and reconcile the terminfo entry. Options: - --update Create/update terminfo if new keysyms detected, or remove - the override if not (default) + --update Apply the CSI keymap overlay if the running kernel supports + KT_CSI (Linux 7.1+), then create/update terminfo if new + keysyms are detected, or remove the override if not (default) --remove Remove system terminfo override --check Check if new keysyms are in use -This script handles two cases: +Environment: + KBD_FORCE_CSI Set to 1 to force the CSI keymap overlay on, or 0 to + force it off, regardless of the detected kernel version. + +The CSI keymap overlay (linux-keys-csi.inc) rebinds the function and +navigation keys to modifier-aware CSI sequences. The default keymaps keep +the traditional bindings, so older kernels are unaffected. + +This script also handles two terminfo cases: 1. The Linux console has a long-standing discrepancy where F1-F5 use non-standard sequences (\e[[A through \e[[E) while F6 and above use
contrib/kbd-terminfo-fixup.service.in +1 -1
@@ -1,5 +1,5 @@ [Unit] -Description=Update terminfo for CSI function keys +Description=Enable modifier-aware console keys and update terminfo Documentation=man:keymaps(5) After=systemd-vconsole-setup.service ConditionPathExists=/dev/tty0
data/keymaps/i386/include/linux-keys-csi.inc new file +83 -0
83 lines not on this page · raw message
Signature · 1 line
2.54.0
Quoted 7 lines · Nicolas Pitre
Linux kernel 7.1 introduces modifier-aware escape sequence generation for the console keyboard:
4af70f151671("vt: add modifier support to cursor keys")5cba06c71c71("vt: addKT_CSIkeysym type for modifier-aware CSI sequences")c1d2deb6492f("vt: add fallback to plain map for modifier-aware key types")
Quoted 5 lines
The new
KT_CSIkeysym type produces xterm-style CSI tilde sequences (ESC [ n ~, or ESC [ n ; mod ~ when Shift/Alt/Control are held) with the modifier state encoded at runtime. A single plain map entry covers all modifier combinations, instead of consuming afunc_tablestring slot per combination.
Here's what's bothering me. These patches disrupt the logic behind how keymaps work.
Previously, userspace determined which modifier key should generate which sequence. We had string F1 = "\033[[A" and use it in any position:alt keycode 16 = F1.
After your patches, modifier handling has been moved to the kernel. The escape sequence ESC[11;03~ will be generated only if Csi_F1 is set to alt keycode 16 = Csi_F1. So, for Csi_*, the modifier has taken on a special meaning. I can't bind the Alt+Csi_F1 sequence to any other modifier key.
Quoted 56 lines
This series adds the userspace counterpart:
Patch 1 teaches libkeymap the Csi_* keysym names (
Csi_Home,Csi_End,Csi_Delete,Csi_Insert,Csi_PgUp,Csi_PgDn, Csi_F1..Csi_F20, matching the kernel UAPI values) and documents modifier-aware keys in keymaps(5).Patch 2 adds a contrib script (with optional systemd unit) that reconciles the "linux" terminfo entry with the loaded keymap: F1-F5 in CSI form and Backtab as ESC [ Z are not what the stock terminfo entry expects.
Patch 3 keeps the default keymaps on their traditional bindings and adds an opt-in overlay (linux-keys-csi.inc) that switches the function and navigation keys to the Csi_* keysyms. The boot script applies it only when the running kernel supports
KT_CSI, so nothing changes on older kernels.Changes since v1 (addressing Alexey's review):
- Patch 1: bump
NR_TYPESinsummary.csoKT_CSIappears in "dumpkeys --long-info".- Patch 3: reworked from an in-place conversion (which broke pre-7.1 systems) into the opt-in overlay described above.
v1: https://lore.kernel.org/kbd/[email protected]
Tested with make check, a staged "make install" (overlay installs and resolves by name), and the terminfo script exercised live on Fedora 43.
Nicolas Pitre (3):
libkeymap: add support forKT_CSIkeysym type
Add kbd-terminfo-fixup script and systemd service
keymaps: add opt-in CSI overlay and load it when the kernel supports
it.gitignore | 1 + Makefile.am | 16 + configure.ac | 18 ++ contrib/kbd-terminfo-fixup | 303 +++++++++++++++++++ contrib/kbd-terminfo-fixup.service.in | 13 + data/keymaps/i386/include/linux-keys-csi.inc | 83 +++++ docs/man/man5/keymaps.5 | 77 +++++ src/libkeymap/ksyms.c | 3 +- src/libkeymap/summary.c | 2 +- src/libkeymap/syms.ktyp.h | 39 +++ tests/data/keymap0-summary.txt | 26 ++ 11 files changed, 579 insertions(+), 2 deletions(-) create mode 100755 contrib/kbd-terminfo-fixup create mode 100644 contrib/kbd-terminfo-fixup.service.in create mode 100644 data/keymaps/i386/include/linux-keys-csi.incSignature · 1 line
2.54.0
Signature · 1 line
Rgrds, legion
Quoted 14 lines · Alexey Gladkov
Quoted 7 lines · Nicolas Pitre
Linux kernel 7.1 introduces modifier-aware escape sequence generation for the console keyboard:
4af70f151671("vt: add modifier support to cursor keys")5cba06c71c71("vt: addKT_CSIkeysym type for modifier-aware CSI sequences")c1d2deb6492f("vt: add fallback to plain map for modifier-aware key types")Quoted 5 lines
The new
KT_CSIkeysym type produces xterm-style CSI tilde sequences (ESC [ n ~, or ESC [ n ; mod ~ when Shift/Alt/Control are held) with the modifier state encoded at runtime. A single plain map entry covers all modifier combinations, instead of consuming afunc_tablestring slot per combination.
Quoted 6 lines
Here's what's bothering me. These patches disrupt the logic behind how keymaps work.
Previously, userspace determined which modifier key should generate which sequence. We had
string F1 = "\033[[A"and use it in any position:alt keycode 16 = F1.
That mechanism is untouched. KT_FN strings behave exactly as before and can still be placed in any position. KT_CSI does not replace them; it is an additional keysym type alongside them.
Quoted 5 lines
After your patches, modifier handling has been moved to the kernel. The escape sequence
ESC[11;03~will be generated only ifCsi_F1is set toalt keycode 16 = Csi_F1. So, for Csi_*, the modifier has taken on a special meaning. I can't bind the Alt+Csi_F1 sequence to any other modifier key.
I think this conflates two separate things, and the one that matters isn't actually constrained.
1. Csi_F1 does not take over the modifier columns. The kernel only falls back to the plain entry when the modifier-specific slot is empty. From c1d2deb6492f ("vt: add fallback to plain map for modifier-aware key types"):
if (shift_final && keycode < NR_KEYS &&
(!key_map || key_map[keycode] == K_HOLE) && key_maps[0]) {
...
if (type - 0xf0 == KT_CUR || type - 0xf0 == KT_CSI)
key_map = key_maps[0];
} As soon as you bind, say, shift keycode 16 = Foo, that slot is no longer K_HOLE, the condition is false, and your explicit binding is used -- the automatic encoding never runs. The commit log says as
much: "Explicit modifier bindings still take precedence - the fallback only triggers when the modifier-specific entry is empty." So the keymap author keeps full control; Csi_F1 in the plain slot is merely the default for the modifier combinations you leave unbound.
2. What you genuinely cannot do is make the Csi_F1 keysym emit an encoding that disagrees with the keys physically held -- e.g. get the Alt form ESC[11;3~ out of a Control press. That is deliberate: k_csi reads the live modifier state, exactly like xterm, foot, kitty and every other terminal that speaks these sequences. But if you want a fixed, hand-picked sequence on a given modifier, that is precisely what KT_FN strings are for, and they still work in any column:
string AltF1 = "\033[11;3~"
control keycode 16 = AltF1
So nothing that was possible before is lost. KT_CSI is opt-in and purely additive: it trades manual per-column control of a CSI sequence's modifier field (which strings still give you) for not having to enumerate -- and exhaust func_table with -- one string per modifier combination. That is the whole reason the kernel side exists.
Nor is this a new departure for the keymap model: KT_CUR arrow keys already derive their output from runtime state (VC_CKMODE), as does KT_PAD (VC_APPLIC), and KT_CONS/KT_SHIFT/dead keys have never been userspace strings either. A keysym type with kernel-defined, state-dependent behavior is the norm, not the exception; KT_CSI simply joins that set.
Nicolas
Quoted 19 lines · Nicolas Pitre
Quoted 17 lines · Alexey Gladkov
Quoted 7 lines · Nicolas Pitre
Linux kernel 7.1 introduces modifier-aware escape sequence generation for the console keyboard:
4af70f151671("vt: add modifier support to cursor keys")5cba06c71c71("vt: addKT_CSIkeysym type for modifier-aware CSI sequences")c1d2deb6492f("vt: add fallback to plain map for modifier-aware key types")Quoted 5 lines
The new
KT_CSIkeysym type produces xterm-style CSI tilde sequences (ESC [ n ~, or ESC [ n ; mod ~ when Shift/Alt/Control are held) with the modifier state encoded at runtime. A single plain map entry covers all modifier combinations, instead of consuming afunc_tablestring slot per combination.Here's what's bothering me. These patches disrupt the logic behind how keymaps work.
Quoted 7 lines
Quoted 3 lines
Previously, userspace determined which modifier key should generate which sequence. We had
string F1 = "\033[[A"and use it in any position:alt keycode 16 = F1.That mechanism is untouched.
KT_FNstrings behave exactly as before and can still be placed in any position.KT_CSIdoes not replace them; it is an additional keysym type alongside them.
I guess I didn't mean it that way. I didn't say you broke KT_FN. You broke KT_CUR.
Quoted 47 lines
Quoted 5 lines
After your patches, modifier handling has been moved to the kernel. The escape sequence
ESC[11;03~will be generated only ifCsi_F1is set toalt keycode 16 = Csi_F1. So, for Csi_*, the modifier has taken on a special meaning. I can't bind the Alt+Csi_F1 sequence to any other modifier key.I think this conflates two separate things, and the one that matters isn't actually constrained.
1.
Csi_F1does not take over the modifier columns. The kernel only falls back to the plain entry when the modifier-specific slot is empty. Fromc1d2deb6492f("vt: add fallback to plain map for modifier-aware key types"):if (shift_final && keycode < NR_KEYS && (!key_map || key_map[keycode] == K_HOLE) && key_maps[0]) { ... if (type - 0xf0 == KT_CUR || type - 0xf0 == KT_CSI) key_map = key_maps[0]; }As soon as you bind, say, shift keycode 16 = Foo, that slot is no longer
K_HOLE, the condition is false, and your explicit binding is used -- the automatic encoding never runs. The commit log says as
much: "Explicit modifier bindings still take precedence - the fallback only triggers when the modifier-specific entry is empty." So the keymap author keeps full control;Csi_F1in the plain slot is merely the default for the modifier combinations you leave unbound.2. What you genuinely cannot do is make the
Csi_F1keysym emit an encoding that disagrees with the keys physically held -- e.g. get the Alt form ESC[11;3~ out of a Control press. That is deliberate:k_csireads the live modifier state, exactly like xterm, foot, kitty and every other terminal that speaks these sequences. But if you want a fixed, hand-picked sequence on a given modifier, that is precisely whatKT_FNstrings are for, and they still work in any column:string AltF1 = "\033[11;3~"
control keycode 16 = AltF1So nothing that was possible before is lost.
KT_CSIis opt-in and purely additive: it trades manual per-column control of a CSI sequence's modifier field (which strings still give you) for not having to enumerate -- and exhaustfunc_tablewith -- one string per modifier combination. That is the whole reason the kernel side exists.
Quoted 6 lines
Nor is this a new departure for the keymap model:
KT_CURarrow keys already derive their output from runtime state (VC_CKMODE), as doesKT_PAD(VC_APPLIC), and KT_CONS/KT_SHIFT/dead keys have never been userspace strings either. A keysym type with kernel-defined, state-dependent behavior is the norm, not the exception;KT_CSIsimply joins that set.
Yes, the old mechanism (KT_FN) still exists and can still be used.
What I'm trying to say is that Csi_* significantly changes the user experience. If now the user controls what will be in which position. However, Csi_F1 changes its output depending on the active modifiers.
KT_CONS is a bad example. KT_CONS is just a separate type. I can even assign it to Ctrl-X to switch to my especially favorite console. I can use Console_63 anywhere in the keymap, and it will always take me to console 63 and it will work regardless of any active modifiers.
It's the same story with KT_PAD/KT_DEAD. You can put them anywhere you like in the keymap and they will behave the same way.
That's not the case with KT_CSI.
Let me give you another example. In the last release, I implemented initial support for converting xkb keymaps to kernel keymaps. The conversion generates a keymap on the fly from xkbcommon. xkbsupport encodes layout/group using KG_SHIFTL and KG_SHIFTR. So a single combination cycles through several layouts:
- group 1: plain
- group 2: shiftl
- group 3: shiftr
- group 4: shiftl | shiftr
(this idea isn't new. It has been used in debian for a long time in the console-setup, and also in ubuntu and some others).
This makes it impossible to use Csi_* in it, since Csi_F1 in the second layout will be recognized by the kernel as "F1 with Shift".
And this seems to apply not only to KT_CSI, but also to the existingKT_CUR: k_cur() uses the same csi_modifier_param(). If the arrows end up in a layout group other than the first one via shiftl/shiftr, an extra Shift modifier will be generated in the escape sequence. And that's a real regression in userspace.
I hope I've made my concerns more clear.
Signature · 1 line
Rgrds, legion
Quoted 10 lines · Alexey Gladkov
This makes it impossible to use Csi_* in it, since
Csi_F1in the second layout will be recognized by the kernel as "F1 with Shift".And this seems to apply not only to
KT_CSI, but also to the existingKT_CUR:k_cur()uses the samecsi_modifier_param(). If the arrows end up in a layout group other than the first one via shiftl/shiftr, an extra Shift modifier will be generated in the escape sequence. And that's a real regression in userspace.I hope I've made my concerns more clear.
You're right, and thanks for the concrete example -- this one is a genuine bug, and it's in the kernel rather than in the keymaps.
I misunderstood the actual purpose of KG_SHIFT vs KG_SHIFTL/KG_SHIFTR.
I'll send a fix right away.
Nicolas