[PATCH v2 1/3] libkeymap: add support for KT_CSI keysym type

Nicolas Pitre <[email protected]> Tue, 23 Jun 2026 18:48:56 -0400
Newsgroups dev.linux.lists.kbd
Message-ID <[email protected]>
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).

Signed-off-by: Nicolas Pitre <[email protected]>
---
 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(-)

diff --git a/docs/man/man5/keymaps.5 b/docs/man/man5/keymaps.5
index 072afe3..bfa75aa 100644
--- a/docs/man/man5/keymaps.5
+++ b/docs/man/man5/keymaps.5
@@ -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
diff --git a/src/libkeymap/ksyms.c b/src/libkeymap/ksyms.c
index 219e5f2..98d1cf3 100644
--- a/src/libkeymap/ksyms.c
+++ b/src/libkeymap/ksyms.c
@@ -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
diff --git a/src/libkeymap/summary.c b/src/libkeymap/summary.c
index 2003a3f..fafcc06 100644
--- a/src/libkeymap/summary.c
+++ b/src/libkeymap/summary.c
@@ -78,7 +78,7 @@ int lk_get_kmapinfo(struct lk_ctx *ctx, struct kmapinfo *res)
 	return 0;
 }
 
-#define NR_TYPES 15
+#define NR_TYPES 16
 
 void lk_dump_summary(struct lk_ctx *ctx, FILE *fd, int console)
 {
diff --git a/src/libkeymap/syms.ktyp.h b/src/libkeymap/syms.ktyp.h
index 504d67e..aca0fc6 100644
--- a/src/libkeymap/syms.ktyp.h
+++ b/src/libkeymap/syms.ktyp.h
@@ -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 ~ */
+};
diff --git a/tests/data/keymap0-summary.txt b/tests/data/keymap0-summary.txt
index 0b66c06..6f757fd 100644
--- a/tests/data/keymap0-summary.txt
+++ b/tests/data/keymap0-summary.txt
@@ -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:
 
-- 
2.54.0