Re: [PATCH v2 0/3] support for the kernel 7.1 modifier-aware KT_CSI keysym type

Nicolas Pitre <[email protected]> Thu, 25 Jun 2026 12:28:44 -0400 (EDT)
Newsgroups dev.linux.lists.kbd
Message-ID <[email protected]>
On Wed, 24 Jun 2026, Alexey Gladkov wrote:

> On Tue, Jun 23, 2026 at 06:48:55PM -0400, Nicolas Pitre wrote:
> > 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.
> 
> 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.

> 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.

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