]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1736: Cannot detect <F3> using kitty protocol v9.1.1736
authorChristian Brabandt <cb@256bit.org>
Sat, 6 Sep 2025 08:11:39 +0000 (10:11 +0200)
committerChristian Brabandt <cb@256bit.org>
Sat, 6 Sep 2025 08:11:39 +0000 (10:11 +0200)
Problem:  Cannot detect <F3> using kitty protocol
Solution: Handle and detect Kitty keys when using the trailing "~" byte

fixes: #18100
closes: #18126

Signed-off-by: Christian Brabandt <cb@256bit.org>
src/term.c
src/testdir/keycode_check.json
src/testdir/keycode_check.vim
src/testdir/test_termcodes.vim
src/testdir/util/view_util.vim
src/version.c

index d1917203d20d3fe989bace76a198622ae6a2b871..2de1508f14010f92c8732a86fcacc01a2f37f97a 100644 (file)
@@ -5293,6 +5293,37 @@ put_key_modifiers_in_typebuf(
     return new_slen - csi_len + offset;
 }
 
+/*
+ * Parse the number from a CSI numbered sequence for an F1-F12 key:
+ *     ESC [ {number} ~
+ * Returns the key
+ */
+    static int
+parse_csi_f_keys(int arg)
+{
+    char_u key_name[2] = "";
+
+    switch (arg)
+    {
+       case 11: key_name[0] = 'k'; key_name[1] = '1'; break;  // K_F1
+       case 12: key_name[0] = 'k'; key_name[1] = '2'; break;  // K_F2
+       case 13: key_name[0] = 'k'; key_name[1] = '3'; break;  // K_F3
+       case 14: key_name[0] = 'k'; key_name[1] = '4'; break;  // K_F4
+       case 15: key_name[0] = 'k'; key_name[1] = '5'; break;  // K_F5
+       case 17: key_name[0] = 'k'; key_name[1] = '6'; break;  // K_F6
+       case 18: key_name[0] = 'k'; key_name[1] = '7'; break;  // K_F7
+       case 19: key_name[0] = 'k'; key_name[1] = '8'; break;  // K_F8
+       case 20: key_name[0] = 'k'; key_name[1] = '9'; break;  // K_F9
+       case 21: key_name[0] = 'F'; key_name[1] = ';'; break;  // K_F10
+       case 23: key_name[0] = 'F'; key_name[1] = '1'; break;  // K_F11
+       case 24: key_name[0] = 'F'; key_name[1] = '2'; break;  // K_F12
+    }
+    if (key_name[0])
+       return TERMCAP2KEY(key_name[0], key_name[1]);
+    // shouldn't happen
+    return arg;
+}
+
 /*
  * Handle a sequence with key and modifier, one of:
  *     {lead}27;{modifier};{key}~
@@ -5302,12 +5333,12 @@ put_key_modifiers_in_typebuf(
     static int
 handle_key_with_modifier(
        int     *arg,
-       int     trail,
        int     csi_len,
        int     offset,
        char_u  *buf,
        int     bufsize,
-       int     *buflen)
+       int     *buflen,
+       int     iskitty)
 {
     // Only set seenModifyOtherKeys for the "{lead}27;" code to avoid setting
     // it for terminals using the kitty keyboard protocol.  Xterm sends
@@ -5320,7 +5351,7 @@ handle_key_with_modifier(
     //
     // Do not set seenModifyOtherKeys for kitty, it does send some sequences
     // like this but does not have the modifyOtherKeys feature.
-    if (trail != 'u'
+    if (!iskitty
            && (kitty_protocol_state == KKPS_INITIAL
                || kitty_protocol_state == KKPS_OFF
                || kitty_protocol_state == KKPS_AFTER_T_TE)
@@ -5332,7 +5363,7 @@ handle_key_with_modifier(
        seenModifyOtherKeys = TRUE;
     }
 
-    int key = trail == 'u' ? arg[0] : arg[2];
+    int key = iskitty ? arg[0] : arg[2];
     int modifiers = decode_modifiers(arg[1]);
 
     // Some terminals do not apply the Shift modifier to the key.  To make
@@ -5345,6 +5376,9 @@ handle_key_with_modifier(
     if (key == ESC)
        key = K_ESC;
 
+    else if (arg[0] >= 11 && arg[0] <= 24)
+       key = parse_csi_f_keys(arg[0]);
+
     return put_key_modifiers_in_typebuf(key, modifiers,
                                        csi_len, offset, buf, bufsize, buflen);
 }
@@ -5352,6 +5386,7 @@ handle_key_with_modifier(
 /*
  * Handle a sequence with key without a modifier:
  *     {lead}{key}u
+ *     {lead}{key}~
  * Returns the difference in length.
  */
     static int
@@ -5375,6 +5410,14 @@ handle_key_without_modifier(
        string[2] = KE_ESC;
        new_slen = 3;
     }
+    else if (arg[0] >= 11 && arg[0] <= 24)
+    {
+       int key = parse_csi_f_keys(arg[0]);
+       string[0] = K_SPECIAL;
+       string[1] = KEY2TERMCAP0(key);
+       string[2] = KEY2TERMCAP1(key);
+       new_slen = 3;
+    }
     else
        new_slen = add_key_to_buf(arg[0], string);
 
@@ -5672,19 +5715,22 @@ handle_csi(
     // Key with modifier:
     // {lead}27;{modifier};{key}~
     // {lead}{key};{modifier}u
+    // {lead}{key};{modifier}~
     // Even though we only handle four modifiers and the {modifier} value
     // should be 16 or lower, we accept all modifier values to avoid the raw
     // sequence to be passed through.
     else if ((arg[0] == 27 && argc == 3 && trail == '~')
-               || (argc == 2 && trail == 'u'))
+               || (argc == 2 && (trail == 'u' || trail == '~')))
     {
-       return len + handle_key_with_modifier(arg, trail,
-                                       csi_len, offset, buf, bufsize, buflen);
+       int iskitty = argc == 2 && (trail == 'u' || trail == '~');
+       return len + handle_key_with_modifier(arg, csi_len, offset, buf,
+               bufsize, buflen, iskitty);
     }
 
-    // Key without modifier (Kitty sends this for Esc):
+    // Key without modifier (Kitty sends this for Esc or F3):
     // {lead}{key}u
-    else if (argc == 1 && trail == 'u')
+    // {lead}{key}~
+    else if (argc == 1 && (trail == 'u' || trail == '~'))
     {
        return len + handle_key_without_modifier(arg,
                            csi_len, offset, buf, bufsize, buflen);
index b06e3c52bd29bb371d26ebb6b27a15a19aba3ef7..6e3ce5263bdd3c0c19fccf59fc9a202175416871 100644 (file)
@@ -1 +1,153 @@
-{"31kitty":{"Space":"20","modkeys":"","version":"1b5b3e313b343030303b323163","C-Tab":"","A-Esc":"1b5b32373b313175","C-Space":"1b5b33323b3575","S-C-I":"1b5b3130353b3675","C-I":"1b5b3130353b3575","S-Tab":"1b5b393b3275","Tab":"09","resource":"","A-Tab":"1b5b393b313175","S-Space":"20","C-Esc":"1b5b32373b3575","kitty":"1b5b3f3175","protocol":"kitty","A-Space":"1b5b33323b313175","S-Esc":"1b5b32373b3275","Esc":"1b5b323775"},"32libvterm":{"Space":"20","modkeys":"","version":"1b5b3e303b3130303b3063","C-Tab":"","A-Esc":"1b5b32373b3375","C-Space":"1b5b33323b3575","S-C-I":"1b5b3130353b3675","C-I":"1b5b3130353b3575","S-Tab":"1b5b393b3275","Tab":"09","resource":"","A-Tab":"1b5b393b3375","S-Space":"20","C-Esc":"1b5b32373b3575","kitty":"1b5b3f3175","protocol":"kitty","A-Space":"1b5b33323b3375","S-Esc":"1b5b32373b3275","Esc":"1b5b323775"},"22libvterm":{"Space":"20","modkeys":"\u001b[>4;2m","version":"1b5b3e303b3130303b3063","C-Tab":"1b5b32373b353b397e","A-Esc":"1b5b32373b333b32377e","C-Space":"1b5b32373b353b33327e","S-C-I":"1b5b32373b363b37337e","C-I":"1b5b32373b353b3130357e","S-Tab":"1b5b5a","Tab":"09","resource":"","A-Tab":"1b5b32373b333b397e","S-Space":"1b5b32373b323b33327e","C-Esc":"1b5b32373b353b32377e","kitty":"","protocol":"mok2","A-Space":"1b5b32373b333b33327e","S-Esc":"1b5b32373b323b32377e","Esc":"1b"},"13kitty":{"Space":"20","modkeys":"","version":"1b5b3e313b343030303b323163","C-Tab":"","A-Esc":"1b1b","S-C-I":"1b5b3130353b3675","C-I":"09","S-Tab":"1b5b5a","Tab":"09","S-Space":"20","A-Tab":"1b09","resource":"","C-Esc":"1b","kitty":"1b5b3f3075","protocol":"none","A-Space":"1b5b33323b313175","S-Esc":"1b","Esc":"1b"},"21xterm":{"Space":"20","modkeys":"\u001b[>4;2m","version":"1b5b3e34313b3337373b3063","C-Tab":"1b5b32373b353b397e","A-Esc":"1b5b32373b333b32377e","C-Space":"1b5b32373b353b33327e","S-C-I":"1b5b32373b363b37337e","C-I":"1b5b32373b353b3130357e","S-Tab":"1b5b5a","Tab":"09","resource":"=30","A-Tab":"1b5b32373b333b397e","S-Space":"1b5b32373b323b33327e","C-Esc":"1b5b32373b353b32377e","kitty":"","protocol":"mok2","A-Space":"1b5b32373b333b33327e","S-Esc":"1b5b32373b323b32377e","Esc":"1b"},"12libvterm":{"Space":"20","modkeys":"\u001b[>4;0m","version":"1b5b3e303b3130303b3063","C-Tab":"1b5b393b3575","A-Esc":"9b00","S-C-I":"1b5b5a","C-I":"09","S-Tab":"1b5b5a","Tab":"09","resource":"","A-Tab":"8900","S-Space":"1b5b33323b3275","C-Esc":"1b5b32373b3575","kitty":"1b5b3f3075","protocol":"none","A-Space":"a000","S-Esc":"1b5b32373b3275","Esc":"1b"},"11xterm":{"Space":"20","modkeys":"\u001b[>4;0m","version":"1b5b3e34313b3337373b3063","C-Tab":"09","A-Esc":"9b00","S-C-I":"09","C-I":"09","S-Tab":"1b5b5a","Tab":"09","S-Space":"20","A-Tab":"8900","resource":"","C-Esc":"1b","kitty":"","protocol":"none","A-Space":"a000","S-Esc":"1b","Esc":"1b"}}
+{
+    "31kitty": {
+        "Space": "20",
+        "modkeys": "",
+        "version": "1b5b3e313b343030303b323163",
+        "C-Tab": "",
+        "A-Esc": "1b5b32373b313175",
+        "C-Space": "1b5b33323b3575",
+        "S-C-I": "1b5b3130353b3675",
+        "C-I": "1b5b3130353b3575",
+        "S-Tab": "1b5b393b3275",
+        "Tab": "09",
+        "resource": "",
+        "A-Tab": "1b5b393b313175",
+        "S-Space": "20",
+        "C-Esc": "1b5b32373b3575",
+        "kitty": "1b5b3f3175",
+        "protocol": "kitty",
+        "A-Space": "1b5b33323b313175",
+        "S-Esc": "1b5b32373b3275",
+        "Esc": "1b5b323775",
+        "F3":  "1b5b31337e"
+    },
+    "32libvterm": {
+        "Space": "20",
+        "modkeys": "",
+        "version": "1b5b3e303b3130303b3063",
+        "C-Tab": "",
+        "A-Esc": "1b5b32373b3375",
+        "C-Space": "1b5b33323b3575",
+        "S-C-I": "1b5b3130353b3675",
+        "C-I": "1b5b3130353b3575",
+        "S-Tab": "1b5b393b3275",
+        "Tab": "09",
+        "resource": "",
+        "A-Tab": "1b5b393b3375",
+        "S-Space": "20",
+        "C-Esc": "1b5b32373b3575",
+        "kitty": "1b5b3f3175",
+        "protocol": "kitty",
+        "A-Space": "1b5b33323b3375",
+        "S-Esc": "1b5b32373b3275",
+        "Esc": "1b5b323775",
+        "F3":  "1b4f52"
+    },
+    "22libvterm": {
+        "Space": "20",
+        "modkeys": "\u001b[>4;2m",
+        "version": "1b5b3e303b3130303b3063",
+        "C-Tab": "1b5b32373b353b397e",
+        "A-Esc": "1b5b32373b333b32377e",
+        "C-Space": "1b5b32373b353b33327e",
+        "S-C-I": "1b5b32373b363b37337e",
+        "C-I": "1b5b32373b353b3130357e",
+        "S-Tab": "1b5b5a",
+        "Tab": "09",
+        "resource": "",
+        "A-Tab": "1b5b32373b333b397e",
+        "S-Space": "1b5b32373b323b33327e",
+        "C-Esc": "1b5b32373b353b32377e",
+        "kitty": "",
+        "protocol": "mok2",
+        "A-Space": "1b5b32373b333b33327e",
+        "S-Esc": "1b5b32373b323b32377e",
+        "Esc": "1b",
+        "F3":  "1b4f52"
+    },
+    "13kitty": {
+        "Space": "20",
+        "modkeys": "",
+        "version": "1b5b3e313b343030303b323163",
+        "C-Tab": "",
+        "A-Esc": "1b1b",
+        "S-C-I": "1b5b3130353b3675",
+        "C-I": "09",
+        "S-Tab": "1b5b5a",
+        "Tab": "09",
+        "S-Space": "20",
+        "A-Tab": "1b09",
+        "resource": "",
+        "C-Esc": "1b",
+        "kitty": "1b5b3f3075",
+        "protocol": "none",
+        "A-Space": "1b5b33323b313175",
+        "S-Esc": "1b",
+        "Esc": "1b",
+        "F3":  "1b4f52"
+    },
+    "21xterm": {
+        "Space": "20",
+        "modkeys": "\u001b[>4;2m",
+        "version": "1b5b3e34313b3337373b3063",
+        "C-Tab": "1b5b32373b353b397e",
+        "A-Esc": "1b5b32373b333b32377e",
+        "C-Space": "1b5b32373b353b33327e",
+        "S-C-I": "1b5b32373b363b37337e",
+        "C-I": "1b5b32373b353b3130357e",
+        "S-Tab": "1b5b5a",
+        "Tab": "09",
+        "resource": "=30",
+        "A-Tab": "1b5b32373b333b397e",
+        "S-Space": "1b5b32373b323b33327e",
+        "C-Esc": "1b5b32373b353b32377e",
+        "kitty": "",
+        "protocol": "mok2",
+        "A-Space": "1b5b32373b333b33327e",
+        "S-Esc": "1b5b32373b323b32377e",
+        "Esc": "1b",
+        "F3":  "1b4f52"
+    },
+    "12libvterm": {
+        "Space": "20",
+        "modkeys": "\u001b[>4;0m",
+        "version": "1b5b3e303b3130303b3063",
+        "C-Tab": "1b5b393b3575",
+        "A-Esc": "9b00",
+        "S-C-I": "1b5b5a",
+        "C-I": "09",
+        "S-Tab": "1b5b5a",
+        "Tab": "09",
+        "resource": "",
+        "A-Tab": "8900",
+        "S-Space": "1b5b33323b3275",
+        "C-Esc": "1b5b32373b3575",
+        "kitty": "1b5b3f3075",
+        "protocol": "none",
+        "A-Space": "a000",
+        "S-Esc": "1b5b32373b3275",
+        "Esc": "1b",
+        "F3":  "1b4f52"
+    },
+    "11xterm": {
+        "Space": "20",
+        "modkeys": "\u001b[>4;0m",
+        "version": "1b5b3e34313b3337373b3063",
+        "C-Tab": "09",
+        "A-Esc": "9b00",
+        "S-C-I": "09",
+        "C-I": "09",
+        "S-Tab": "1b5b5a",
+        "Tab": "09",
+        "S-Space": "20",
+        "A-Tab": "8900",
+        "resource": "",
+        "C-Esc": "1b",
+        "kitty": "",
+        "protocol": "none",
+        "A-Space": "a000",
+        "S-Esc": "1b",
+        "Esc": "1b",
+        "F3":  "1b4f52"
+    }
+}
index 8320341d2f3c6eb47bac8e6e7e2fc6499871899d..33eb6ceeb792d5694162dc8c777ea1e530712220 100644 (file)
@@ -74,6 +74,7 @@ var key_entries = [
        ['Shift-Space', 'S-Space'],
        ['Ctrl-Space', 'C-Space'],
        ['Alt-Space', 'A-Space'],
+       ['F3', 'F3'],
       ]
 
 # Given a terminal name and a item name, return the text to display.
@@ -464,7 +465,7 @@ while true
     ActionReplace()
   elseif action == 4
     ActionClear()
-  elseif action == 5
+  elseif action == 5 || action == 0
     ActionQuit()
   endif
 endwhile
index 99710fada53f283612a5f95c7c2fe9d2c780702d..f37f618c8156a43ab3c3035dedf2b4972bfe9967 100644 (file)
@@ -2517,6 +2517,31 @@ func Test_mapping_kitty_function_keys()
   set timeoutlen&
 endfunc
 
+func Test_mapping_kitty_function_keys2()
+  " uses the CSI {number}; {modifiers} ~ form
+  new
+  set timeoutlen=10
+
+  let maps = [
+        \    ['<F3>', '13', 0],
+        \    ['<S-F3>', '13', 2],
+        \    ['<C-F3>', '13', 5],
+        \    ['<C-S-F3>', '13', 6],
+        \
+        \    ['<F5>', '15', 0],
+        \    ['<S-F5>', '15', 2],
+        \    ['<C-F5>', '15', 5],
+        \    ['<C-S-F5>', '15', 6],
+        \ ]
+
+  for map in maps
+    call RunTest_mapping_funckey(map[0], function('GetEscCodeFunckey2'), map[1], map[2])
+  endfor
+
+  bwipe!
+  set timeoutlen&
+endfunc
+
 func Test_insert_literal()
   set timeoutlen=10
 
index 161c8b20cdf3077f871576abd54a16ea8383633b..5c5bfa81de917b9280e2f0abfcb66ea11c8d666d 100644 (file)
@@ -108,6 +108,18 @@ func GetEscCodeFunckey(key, modifier)
   return "\<Esc>[1;".. mod .. a:key
 endfunc
 
+" Return the kitty keyboard protocol encoding for a function key:
+" CSI {number}; {modifiier} ~
+func GetEscCodeFunckey2(key, modifier)
+  let key = "\<Esc>[" .. a:key
+  if a:modifier == 0
+    return key .. "~"
+  endif
+
+  let mod = printf("%d", a:modifier)
+  return key .. ';' .. mod .. '~'
+endfunc
+
 " Return the kitty keyboard protocol encoding for "key" without a modifier.
 " Used for the Escape key.
 func GetEscCodeCSIuWithoutModifier(key)
index e6b9f9dfd6f5e1df057069d2e38efb798df1c041..cf2f9910168946e9506bc1b599aaef8ef1fc5c6c 100644 (file)
@@ -724,6 +724,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1736,
 /**/
     1735,
 /**/