* [PATCH 1/3] vt: add modifier support to cursor keys
2026-02-03 4:52 [PATCH 0/3] vt: add modifier support to cursor and navigation keys Nicolas Pitre
@ 2026-02-03 4:52 ` Nicolas Pitre
2026-02-03 4:52 ` [PATCH 2/3] vt: add KT_CSI keysym type for modifier-aware CSI sequences Nicolas Pitre
` (2 subsequent siblings)
3 siblings, 0 replies; 13+ messages in thread
From: Nicolas Pitre @ 2026-02-03 4:52 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Alexey Gladkov
Cc: Nicolas Pitre, linux-serial, linux-kernel
From: Nicolas Pitre <npitre@baylibre.com>
Generate xterm-style CSI sequences with modifier parameters for arrow
keys when Shift, Alt, or Ctrl are held. For example, Shift+Up produces
ESC [ 1 ; 2 A instead of plain ESC [ A.
The modifier encoding follows the standard xterm convention:
mod = 1 + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0)
When no modifiers are pressed, the original behavior is preserved.
Explicit keymap bindings for modified cursor keys (e.g., "shift keycode
103 = Find") take precedence over this automatic modifier encoding.
Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
---
drivers/tty/vt/keyboard.c | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index d65fc60dd7be..dacc2267de6b 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -765,14 +765,39 @@ static void k_fn(struct vc_data *vc, unsigned char value, char up_flag)
pr_err("k_fn called with value=%d\n", value);
}
+/*
+ * Compute xterm-style modifier parameter for CSI sequences.
+ * Returns 1 + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0)
+ */
+static int csi_modifier_param(void)
+{
+ int mod = 1;
+
+ if (shift_state & (BIT(KG_SHIFT) | BIT(KG_SHIFTL) | BIT(KG_SHIFTR)))
+ mod += 1;
+ if (shift_state & (BIT(KG_ALT) | BIT(KG_ALTGR)))
+ mod += 2;
+ if (shift_state & (BIT(KG_CTRL) | BIT(KG_CTRLL) | BIT(KG_CTRLR)))
+ mod += 4;
+ return mod;
+}
+
static void k_cur(struct vc_data *vc, unsigned char value, char up_flag)
{
static const char cur_chars[] = "BDCA";
+ int mod;
if (up_flag)
return;
- applkey(vc, cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE));
+ mod = csi_modifier_param();
+ if (mod > 1) {
+ char buf[] = { 0x1b, '[', '1', ';', '0' + mod, cur_chars[value], 0x00 };
+
+ puts_queue(vc, buf);
+ } else {
+ applkey(vc, cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE));
+ }
}
static void k_pad(struct vc_data *vc, unsigned char value, char up_flag)
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 2/3] vt: add KT_CSI keysym type for modifier-aware CSI sequences
2026-02-03 4:52 [PATCH 0/3] vt: add modifier support to cursor and navigation keys Nicolas Pitre
2026-02-03 4:52 ` [PATCH 1/3] vt: add modifier support to cursor keys Nicolas Pitre
@ 2026-02-03 4:52 ` Nicolas Pitre
2026-02-03 4:52 ` [PATCH 3/3] vt: add fallback to plain map for modifier-aware key types Nicolas Pitre
2026-02-08 15:28 ` [PATCH 0/3] vt: add modifier support to cursor and navigation keys Greg Kroah-Hartman
3 siblings, 0 replies; 13+ messages in thread
From: Nicolas Pitre @ 2026-02-03 4:52 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Alexey Gladkov
Cc: Nicolas Pitre, linux-serial, linux-kernel
From: Nicolas Pitre <npitre@baylibre.com>
Add a new keysym type KT_CSI that generates CSI tilde sequences with
automatic modifier encoding. The keysym value encodes the CSI parameter
number, producing sequences like ESC [ <value> ~ or ESC [ <value> ; <mod> ~
when Shift, Alt, or Ctrl modifiers are held.
This allows navigation keys (Home, End, Insert, Delete, PgUp, PgDn) and
function keys to generate modifier-aware escape sequences without
consuming string table entries for each modifier combination.
Define key symbols for navigation keys (K_CSI_HOME, K_CSI_END, etc.)
and function keys (K_CSI_F1 through K_CSI_F20) using standard xterm
CSI parameter values.
The modifier encoding follows the xterm convention:
mod = 1 + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0)
Allowed CSI parameter values range from 0 to 99.
Note: The Linux console historically uses a non-standard double-bracket
format for F1-F5 (ESC [ [ A through ESC [ [ E) rather than the xterm
tilde format (ESC [ 11 ~ through ESC [ 15 ~). The K_CSI_F1 through
K_CSI_F5 definitions use the xterm format. Converting F1-F5 to KT_CSI
would require updating the "linux" terminfo entry to match. Navigation
keys and F6-F20 already use the tilde format and are fully compatible.
Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
---
drivers/tty/vt/keyboard.c | 38 ++++++++++++++++++++++++++++++-----
include/uapi/linux/keyboard.h | 29 ++++++++++++++++++++++++++
2 files changed, 62 insertions(+), 5 deletions(-)
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index dacc2267de6b..0b323cefc647 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -74,7 +74,7 @@ static inline int kbd_defleds(void)
k_self, k_fn, k_spec, k_pad,\
k_dead, k_cons, k_cur, k_shift,\
k_meta, k_ascii, k_lock, k_lowercase,\
- k_slock, k_dead2, k_brl, k_ignore
+ k_slock, k_dead2, k_brl, k_csi
typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value,
char up_flag);
@@ -127,6 +127,7 @@ static const unsigned char max_vals[] = {
[ KT_SLOCK ] = NR_LOCK - 1,
[ KT_DEAD2 ] = 255,
[ KT_BRL ] = NR_BRL - 1,
+ [ KT_CSI ] = 99,
};
static const int NR_TYPES = ARRAY_SIZE(max_vals);
@@ -644,10 +645,6 @@ static void fn_null(struct vc_data *vc)
/*
* Special key handlers
*/
-static void k_ignore(struct vc_data *vc, unsigned char value, char up_flag)
-{
-}
-
static void k_spec(struct vc_data *vc, unsigned char value, char up_flag)
{
if (up_flag)
@@ -1029,6 +1026,37 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag)
}
}
+/*
+ * Handle KT_CSI keysym type: generate CSI tilde sequences with modifier
+ * support. The value encodes the CSI parameter number, producing sequences
+ * like ESC [ <value> ~ or ESC [ <value> ; <mod> ~ when modifiers are held.
+ */
+static void k_csi(struct vc_data *vc, unsigned char value, char up_flag)
+{
+ char buf[10];
+ int i = 0;
+ int mod;
+
+ if (up_flag)
+ return;
+
+ mod = csi_modifier_param();
+
+ buf[i++] = 0x1b;
+ buf[i++] = '[';
+ if (value >= 10)
+ buf[i++] = '0' + value / 10;
+ buf[i++] = '0' + value % 10;
+ if (mod > 1) {
+ buf[i++] = ';';
+ buf[i++] = '0' + mod;
+ }
+ buf[i++] = '~';
+ buf[i] = 0x00;
+
+ puts_queue(vc, buf);
+}
+
#if IS_ENABLED(CONFIG_INPUT_LEDS) && IS_ENABLED(CONFIG_LEDS_TRIGGERS)
struct kbd_led_trigger {
diff --git a/include/uapi/linux/keyboard.h b/include/uapi/linux/keyboard.h
index 36d230cedf12..48ecb0cefb45 100644
--- a/include/uapi/linux/keyboard.h
+++ b/include/uapi/linux/keyboard.h
@@ -41,6 +41,7 @@
#define KT_SLOCK 12
#define KT_DEAD2 13
#define KT_BRL 14
+#define KT_CSI 15 /* CSI sequences with modifier support */
#define K(t,v) (((t)<<8)|(v))
#define KTYP(x) ((x) >> 8)
@@ -461,5 +462,33 @@
#define NR_BRL 11
+/* KT_CSI keys: value is the CSI parameter number for ESC [ <value> ~ */
+#define K_CSI_HOME K(KT_CSI, 1) /* ESC [ 1 ~ */
+#define K_CSI_INSERT K(KT_CSI, 2) /* ESC [ 2 ~ */
+#define K_CSI_DELETE K(KT_CSI, 3) /* ESC [ 3 ~ */
+#define K_CSI_END K(KT_CSI, 4) /* ESC [ 4 ~ */
+#define K_CSI_PGUP K(KT_CSI, 5) /* ESC [ 5 ~ */
+#define K_CSI_PGDN K(KT_CSI, 6) /* ESC [ 6 ~ */
+#define K_CSI_F1 K(KT_CSI, 11) /* ESC [ 11 ~ */
+#define K_CSI_F2 K(KT_CSI, 12) /* ESC [ 12 ~ */
+#define K_CSI_F3 K(KT_CSI, 13) /* ESC [ 13 ~ */
+#define K_CSI_F4 K(KT_CSI, 14) /* ESC [ 14 ~ */
+#define K_CSI_F5 K(KT_CSI, 15) /* ESC [ 15 ~ */
+#define K_CSI_F6 K(KT_CSI, 17) /* ESC [ 17 ~ */
+#define K_CSI_F7 K(KT_CSI, 18) /* ESC [ 18 ~ */
+#define K_CSI_F8 K(KT_CSI, 19) /* ESC [ 19 ~ */
+#define K_CSI_F9 K(KT_CSI, 20) /* ESC [ 20 ~ */
+#define K_CSI_F10 K(KT_CSI, 21) /* ESC [ 21 ~ */
+#define K_CSI_F11 K(KT_CSI, 23) /* ESC [ 23 ~ */
+#define K_CSI_F12 K(KT_CSI, 24) /* ESC [ 24 ~ */
+#define K_CSI_F13 K(KT_CSI, 25) /* ESC [ 25 ~ */
+#define K_CSI_F14 K(KT_CSI, 26) /* ESC [ 26 ~ */
+#define K_CSI_F15 K(KT_CSI, 28) /* ESC [ 28 ~ */
+#define K_CSI_F16 K(KT_CSI, 29) /* ESC [ 29 ~ */
+#define K_CSI_F17 K(KT_CSI, 31) /* ESC [ 31 ~ */
+#define K_CSI_F18 K(KT_CSI, 32) /* ESC [ 32 ~ */
+#define K_CSI_F19 K(KT_CSI, 33) /* ESC [ 33 ~ */
+#define K_CSI_F20 K(KT_CSI, 34) /* ESC [ 34 ~ */
+
#define MAX_DIACR 256
#endif /* _UAPI__LINUX_KEYBOARD_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 3/3] vt: add fallback to plain map for modifier-aware key types
2026-02-03 4:52 [PATCH 0/3] vt: add modifier support to cursor and navigation keys Nicolas Pitre
2026-02-03 4:52 ` [PATCH 1/3] vt: add modifier support to cursor keys Nicolas Pitre
2026-02-03 4:52 ` [PATCH 2/3] vt: add KT_CSI keysym type for modifier-aware CSI sequences Nicolas Pitre
@ 2026-02-03 4:52 ` Nicolas Pitre
2026-02-08 15:28 ` [PATCH 0/3] vt: add modifier support to cursor and navigation keys Greg Kroah-Hartman
3 siblings, 0 replies; 13+ messages in thread
From: Nicolas Pitre @ 2026-02-03 4:52 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Alexey Gladkov
Cc: Nicolas Pitre, linux-serial, linux-kernel
From: Nicolas Pitre <npitre@baylibre.com>
When a key is pressed with modifiers (Shift, Ctrl, Alt, etc.) and the
modifier-specific keymap has no binding (K_HOLE) or doesn't exist, fall
back to the plain keymap if the plain entry is a modifier-aware type
(KT_CUR or KT_CSI).
This allows arrow keys and CSI navigation keys to automatically handle
all modifier combinations with just a single plain map entry. The key
handlers (k_cur and k_csi) read the modifier state at runtime and encode
it into the output sequence.
For example, with just:
keycode 103 = Up
keycode 104 = Csi_Home
All these combinations now work automatically:
Up -> ESC [ A
Shift+Up -> ESC [ 1 ; 2 A
Ctrl+Up -> ESC [ 1 ; 5 A
Home -> ESC [ 1 ~
Shift+Home -> ESC [ 1 ; 2 ~
Ctrl+Home -> ESC [ 1 ; 5 ~
Previously, each modifier combination required an explicit keymap entry,
which was tedious and consumed keymap slots.
Explicit modifier bindings still take precedence - the fallback only
triggers when the modifier-specific entry is empty.
Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
---
drivers/tty/vt/keyboard.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 0b323cefc647..a145048e4da3 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -1498,6 +1498,21 @@ static void kbd_keycode(unsigned int keycode, int down, bool hw_raw)
param.ledstate = kbd->ledflagstate;
key_map = key_maps[shift_final];
+ /*
+ * Fall back to the plain map if modifiers are active, the modifier-
+ * specific map is missing or has no entry, and the plain map has a
+ * modifier-aware key type (KT_CUR or KT_CSI). These handlers encode
+ * the modifier state into the emitted escape sequence.
+ */
+ if (shift_final && keycode < NR_KEYS &&
+ (!key_map || key_map[keycode] == K_HOLE) && key_maps[0]) {
+ unsigned short plain = key_maps[0][keycode];
+ unsigned char type = KTYP(plain);
+
+ if (type >= 0xf0 && (type - 0xf0 == KT_CUR || type - 0xf0 == KT_CSI))
+ key_map = key_maps[0];
+ }
+
rc = atomic_notifier_call_chain(&keyboard_notifier_list,
KBD_KEYCODE, ¶m);
if (rc == NOTIFY_STOP || !key_map) {
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH 0/3] vt: add modifier support to cursor and navigation keys
2026-02-03 4:52 [PATCH 0/3] vt: add modifier support to cursor and navigation keys Nicolas Pitre
` (2 preceding siblings ...)
2026-02-03 4:52 ` [PATCH 3/3] vt: add fallback to plain map for modifier-aware key types Nicolas Pitre
@ 2026-02-08 15:28 ` Greg Kroah-Hartman
2026-02-08 16:22 ` Nicolas Pitre
3 siblings, 1 reply; 13+ messages in thread
From: Greg Kroah-Hartman @ 2026-02-08 15:28 UTC (permalink / raw)
To: Nicolas Pitre
Cc: Jiri Slaby, Alexey Gladkov, Nicolas Pitre, linux-serial,
linux-kernel
On Mon, Feb 02, 2026 at 11:52:45PM -0500, Nicolas Pitre wrote:
> This series adds xterm-style modifier encoding to cursor keys and navigation
> keys on the Linux console.
>
> Modern terminal applications (shells, editors, TUI programs) rely on
> modifier+key combinations like Ctrl+Left, Shift+Home, or Alt+Delete for
> navigation and selection. The xterm protocol encodes these as CSI sequences
> with a modifier parameter (e.g., ESC [ 1 ; 5 D for Ctrl+Left).
>
> While the existing func string table mechanism could technically support
> these sequences, each modifier combination would require a separate entry,
> quickly exhausting the limited string table space. This series instead
> generates the sequences programmatically, providing full modifier support
> without consuming string table entries.
>
> This series addresses that in three patches:
>
> 1. Add modifier encoding to cursor keys (Up/Down/Left/Right). When
> Shift, Alt, or Ctrl are held, the arrow keys now emit sequences like
> ESC [ 1 ; 2 A instead of plain ESC [ A.
>
> 2. Add a new KT_CSI keysym type for navigation keys (Home, End, Insert,
> Delete, PgUp, PgDn) and function keys. These generate CSI tilde
> sequences (ESC [ n ~) with automatic modifier encoding.
>
> 3. Add automatic fallback to the plain keymap for modifier-aware key
> types. This eliminates the need for explicit bindings for each
> modifier combination - a single plain keymap entry handles all
> modifier variants.
>
> The modifier encoding follows the standard xterm convention:
> mod = 1 + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0)
>
> Explicit keymap bindings take precedence, preserving backward
> compatibility with existing configurations.
>
> Corresponding patches for the kbd package (loadkeys/dumpkeys) are ready
> and will be submitted once this kernel support is available.
>
> diffstat:
> drivers/tty/vt/keyboard.c | 80 ++++++++++++++++++++++++++++++++--
> include/uapi/linux/keyboard.h | 29 +++++++++++++
> 2 files changed, 103 insertions(+), 6 deletions(-)
>
>
Argh, sorry, I saw this patch series too late for this merge window.
I'll review it after -rc1 is out.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 13+ messages in thread