Skip to content

Commit 58c5a77

Browse files
committed
patch 9.1.1736: Cannot detect <F3> using kitty protocol
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>
1 parent ecdd89f commit 58c5a77

6 files changed

Lines changed: 249 additions & 11 deletions

File tree

src/term.c

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5293,6 +5293,37 @@ put_key_modifiers_in_typebuf(
52935293
return new_slen - csi_len + offset;
52945294
}
52955295

5296+
/*
5297+
* Parse the number from a CSI numbered sequence for an F1-F12 key:
5298+
* ESC [ {number} ~
5299+
* Returns the key
5300+
*/
5301+
static int
5302+
parse_csi_f_keys(int arg)
5303+
{
5304+
char_u key_name[2] = "";
5305+
5306+
switch (arg)
5307+
{
5308+
case 11: key_name[0] = 'k'; key_name[1] = '1'; break; // K_F1
5309+
case 12: key_name[0] = 'k'; key_name[1] = '2'; break; // K_F2
5310+
case 13: key_name[0] = 'k'; key_name[1] = '3'; break; // K_F3
5311+
case 14: key_name[0] = 'k'; key_name[1] = '4'; break; // K_F4
5312+
case 15: key_name[0] = 'k'; key_name[1] = '5'; break; // K_F5
5313+
case 17: key_name[0] = 'k'; key_name[1] = '6'; break; // K_F6
5314+
case 18: key_name[0] = 'k'; key_name[1] = '7'; break; // K_F7
5315+
case 19: key_name[0] = 'k'; key_name[1] = '8'; break; // K_F8
5316+
case 20: key_name[0] = 'k'; key_name[1] = '9'; break; // K_F9
5317+
case 21: key_name[0] = 'F'; key_name[1] = ';'; break; // K_F10
5318+
case 23: key_name[0] = 'F'; key_name[1] = '1'; break; // K_F11
5319+
case 24: key_name[0] = 'F'; key_name[1] = '2'; break; // K_F12
5320+
}
5321+
if (key_name[0])
5322+
return TERMCAP2KEY(key_name[0], key_name[1]);
5323+
// shouldn't happen
5324+
return arg;
5325+
}
5326+
52965327
/*
52975328
* Handle a sequence with key and modifier, one of:
52985329
* {lead}27;{modifier};{key}~
@@ -5302,12 +5333,12 @@ put_key_modifiers_in_typebuf(
53025333
static int
53035334
handle_key_with_modifier(
53045335
int *arg,
5305-
int trail,
53065336
int csi_len,
53075337
int offset,
53085338
char_u *buf,
53095339
int bufsize,
5310-
int *buflen)
5340+
int *buflen,
5341+
int iskitty)
53115342
{
53125343
// Only set seenModifyOtherKeys for the "{lead}27;" code to avoid setting
53135344
// it for terminals using the kitty keyboard protocol. Xterm sends
@@ -5320,7 +5351,7 @@ handle_key_with_modifier(
53205351
//
53215352
// Do not set seenModifyOtherKeys for kitty, it does send some sequences
53225353
// like this but does not have the modifyOtherKeys feature.
5323-
if (trail != 'u'
5354+
if (!iskitty
53245355
&& (kitty_protocol_state == KKPS_INITIAL
53255356
|| kitty_protocol_state == KKPS_OFF
53265357
|| kitty_protocol_state == KKPS_AFTER_T_TE)
@@ -5332,7 +5363,7 @@ handle_key_with_modifier(
53325363
seenModifyOtherKeys = TRUE;
53335364
}
53345365

5335-
int key = trail == 'u' ? arg[0] : arg[2];
5366+
int key = iskitty ? arg[0] : arg[2];
53365367
int modifiers = decode_modifiers(arg[1]);
53375368

53385369
// Some terminals do not apply the Shift modifier to the key. To make
@@ -5345,13 +5376,17 @@ handle_key_with_modifier(
53455376
if (key == ESC)
53465377
key = K_ESC;
53475378

5379+
else if (arg[0] >= 11 && arg[0] <= 24)
5380+
key = parse_csi_f_keys(arg[0]);
5381+
53485382
return put_key_modifiers_in_typebuf(key, modifiers,
53495383
csi_len, offset, buf, bufsize, buflen);
53505384
}
53515385

53525386
/*
53535387
* Handle a sequence with key without a modifier:
53545388
* {lead}{key}u
5389+
* {lead}{key}~
53555390
* Returns the difference in length.
53565391
*/
53575392
static int
@@ -5375,6 +5410,14 @@ handle_key_without_modifier(
53755410
string[2] = KE_ESC;
53765411
new_slen = 3;
53775412
}
5413+
else if (arg[0] >= 11 && arg[0] <= 24)
5414+
{
5415+
int key = parse_csi_f_keys(arg[0]);
5416+
string[0] = K_SPECIAL;
5417+
string[1] = KEY2TERMCAP0(key);
5418+
string[2] = KEY2TERMCAP1(key);
5419+
new_slen = 3;
5420+
}
53785421
else
53795422
new_slen = add_key_to_buf(arg[0], string);
53805423

@@ -5672,19 +5715,22 @@ handle_csi(
56725715
// Key with modifier:
56735716
// {lead}27;{modifier};{key}~
56745717
// {lead}{key};{modifier}u
5718+
// {lead}{key};{modifier}~
56755719
// Even though we only handle four modifiers and the {modifier} value
56765720
// should be 16 or lower, we accept all modifier values to avoid the raw
56775721
// sequence to be passed through.
56785722
else if ((arg[0] == 27 && argc == 3 && trail == '~')
5679-
|| (argc == 2 && trail == 'u'))
5723+
|| (argc == 2 && (trail == 'u' || trail == '~')))
56805724
{
5681-
return len + handle_key_with_modifier(arg, trail,
5682-
csi_len, offset, buf, bufsize, buflen);
5725+
int iskitty = argc == 2 && (trail == 'u' || trail == '~');
5726+
return len + handle_key_with_modifier(arg, csi_len, offset, buf,
5727+
bufsize, buflen, iskitty);
56835728
}
56845729

5685-
// Key without modifier (Kitty sends this for Esc):
5730+
// Key without modifier (Kitty sends this for Esc or F3):
56865731
// {lead}{key}u
5687-
else if (argc == 1 && trail == 'u')
5732+
// {lead}{key}~
5733+
else if (argc == 1 && (trail == 'u' || trail == '~'))
56885734
{
56895735
return len + handle_key_without_modifier(arg,
56905736
csi_len, offset, buf, bufsize, buflen);

src/testdir/keycode_check.json

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,153 @@
1-
{"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"}}
1+
{
2+
"31kitty": {
3+
"Space": "20",
4+
"modkeys": "",
5+
"version": "1b5b3e313b343030303b323163",
6+
"C-Tab": "",
7+
"A-Esc": "1b5b32373b313175",
8+
"C-Space": "1b5b33323b3575",
9+
"S-C-I": "1b5b3130353b3675",
10+
"C-I": "1b5b3130353b3575",
11+
"S-Tab": "1b5b393b3275",
12+
"Tab": "09",
13+
"resource": "",
14+
"A-Tab": "1b5b393b313175",
15+
"S-Space": "20",
16+
"C-Esc": "1b5b32373b3575",
17+
"kitty": "1b5b3f3175",
18+
"protocol": "kitty",
19+
"A-Space": "1b5b33323b313175",
20+
"S-Esc": "1b5b32373b3275",
21+
"Esc": "1b5b323775",
22+
"F3": "1b5b31337e"
23+
},
24+
"32libvterm": {
25+
"Space": "20",
26+
"modkeys": "",
27+
"version": "1b5b3e303b3130303b3063",
28+
"C-Tab": "",
29+
"A-Esc": "1b5b32373b3375",
30+
"C-Space": "1b5b33323b3575",
31+
"S-C-I": "1b5b3130353b3675",
32+
"C-I": "1b5b3130353b3575",
33+
"S-Tab": "1b5b393b3275",
34+
"Tab": "09",
35+
"resource": "",
36+
"A-Tab": "1b5b393b3375",
37+
"S-Space": "20",
38+
"C-Esc": "1b5b32373b3575",
39+
"kitty": "1b5b3f3175",
40+
"protocol": "kitty",
41+
"A-Space": "1b5b33323b3375",
42+
"S-Esc": "1b5b32373b3275",
43+
"Esc": "1b5b323775",
44+
"F3": "1b4f52"
45+
},
46+
"22libvterm": {
47+
"Space": "20",
48+
"modkeys": "\u001b[>4;2m",
49+
"version": "1b5b3e303b3130303b3063",
50+
"C-Tab": "1b5b32373b353b397e",
51+
"A-Esc": "1b5b32373b333b32377e",
52+
"C-Space": "1b5b32373b353b33327e",
53+
"S-C-I": "1b5b32373b363b37337e",
54+
"C-I": "1b5b32373b353b3130357e",
55+
"S-Tab": "1b5b5a",
56+
"Tab": "09",
57+
"resource": "",
58+
"A-Tab": "1b5b32373b333b397e",
59+
"S-Space": "1b5b32373b323b33327e",
60+
"C-Esc": "1b5b32373b353b32377e",
61+
"kitty": "",
62+
"protocol": "mok2",
63+
"A-Space": "1b5b32373b333b33327e",
64+
"S-Esc": "1b5b32373b323b32377e",
65+
"Esc": "1b",
66+
"F3": "1b4f52"
67+
},
68+
"13kitty": {
69+
"Space": "20",
70+
"modkeys": "",
71+
"version": "1b5b3e313b343030303b323163",
72+
"C-Tab": "",
73+
"A-Esc": "1b1b",
74+
"S-C-I": "1b5b3130353b3675",
75+
"C-I": "09",
76+
"S-Tab": "1b5b5a",
77+
"Tab": "09",
78+
"S-Space": "20",
79+
"A-Tab": "1b09",
80+
"resource": "",
81+
"C-Esc": "1b",
82+
"kitty": "1b5b3f3075",
83+
"protocol": "none",
84+
"A-Space": "1b5b33323b313175",
85+
"S-Esc": "1b",
86+
"Esc": "1b",
87+
"F3": "1b4f52"
88+
},
89+
"21xterm": {
90+
"Space": "20",
91+
"modkeys": "\u001b[>4;2m",
92+
"version": "1b5b3e34313b3337373b3063",
93+
"C-Tab": "1b5b32373b353b397e",
94+
"A-Esc": "1b5b32373b333b32377e",
95+
"C-Space": "1b5b32373b353b33327e",
96+
"S-C-I": "1b5b32373b363b37337e",
97+
"C-I": "1b5b32373b353b3130357e",
98+
"S-Tab": "1b5b5a",
99+
"Tab": "09",
100+
"resource": "=30",
101+
"A-Tab": "1b5b32373b333b397e",
102+
"S-Space": "1b5b32373b323b33327e",
103+
"C-Esc": "1b5b32373b353b32377e",
104+
"kitty": "",
105+
"protocol": "mok2",
106+
"A-Space": "1b5b32373b333b33327e",
107+
"S-Esc": "1b5b32373b323b32377e",
108+
"Esc": "1b",
109+
"F3": "1b4f52"
110+
},
111+
"12libvterm": {
112+
"Space": "20",
113+
"modkeys": "\u001b[>4;0m",
114+
"version": "1b5b3e303b3130303b3063",
115+
"C-Tab": "1b5b393b3575",
116+
"A-Esc": "9b00",
117+
"S-C-I": "1b5b5a",
118+
"C-I": "09",
119+
"S-Tab": "1b5b5a",
120+
"Tab": "09",
121+
"resource": "",
122+
"A-Tab": "8900",
123+
"S-Space": "1b5b33323b3275",
124+
"C-Esc": "1b5b32373b3575",
125+
"kitty": "1b5b3f3075",
126+
"protocol": "none",
127+
"A-Space": "a000",
128+
"S-Esc": "1b5b32373b3275",
129+
"Esc": "1b",
130+
"F3": "1b4f52"
131+
},
132+
"11xterm": {
133+
"Space": "20",
134+
"modkeys": "\u001b[>4;0m",
135+
"version": "1b5b3e34313b3337373b3063",
136+
"C-Tab": "09",
137+
"A-Esc": "9b00",
138+
"S-C-I": "09",
139+
"C-I": "09",
140+
"S-Tab": "1b5b5a",
141+
"Tab": "09",
142+
"S-Space": "20",
143+
"A-Tab": "8900",
144+
"resource": "",
145+
"C-Esc": "1b",
146+
"kitty": "",
147+
"protocol": "none",
148+
"A-Space": "a000",
149+
"S-Esc": "1b",
150+
"Esc": "1b",
151+
"F3": "1b4f52"
152+
}
153+
}

src/testdir/keycode_check.vim

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ var key_entries = [
7474
['Shift-Space', 'S-Space'],
7575
['Ctrl-Space', 'C-Space'],
7676
['Alt-Space', 'A-Space'],
77+
['F3', 'F3'],
7778
]
7879

7980
# Given a terminal name and a item name, return the text to display.
@@ -464,7 +465,7 @@ while true
464465
ActionReplace()
465466
elseif action == 4
466467
ActionClear()
467-
elseif action == 5
468+
elseif action == 5 || action == 0
468469
ActionQuit()
469470
endif
470471
endwhile

src/testdir/test_termcodes.vim

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2517,6 +2517,31 @@ func Test_mapping_kitty_function_keys()
25172517
set timeoutlen&
25182518
endfunc
25192519

2520+
func Test_mapping_kitty_function_keys2()
2521+
" uses the CSI {number}; {modifiers} ~ form
2522+
new
2523+
set timeoutlen=10
2524+
2525+
let maps = [
2526+
\ ['<F3>', '13', 0],
2527+
\ ['<S-F3>', '13', 2],
2528+
\ ['<C-F3>', '13', 5],
2529+
\ ['<C-S-F3>', '13', 6],
2530+
\
2531+
\ ['<F5>', '15', 0],
2532+
\ ['<S-F5>', '15', 2],
2533+
\ ['<C-F5>', '15', 5],
2534+
\ ['<C-S-F5>', '15', 6],
2535+
\ ]
2536+
2537+
for map in maps
2538+
call RunTest_mapping_funckey(map[0], function('GetEscCodeFunckey2'), map[1], map[2])
2539+
endfor
2540+
2541+
bwipe!
2542+
set timeoutlen&
2543+
endfunc
2544+
25202545
func Test_insert_literal()
25212546
set timeoutlen=10
25222547

src/testdir/util/view_util.vim

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,18 @@ func GetEscCodeFunckey(key, modifier)
108108
return "\<Esc>[1;".. mod .. a:key
109109
endfunc
110110

111+
" Return the kitty keyboard protocol encoding for a function key:
112+
" CSI {number}; {modifiier} ~
113+
func GetEscCodeFunckey2(key, modifier)
114+
let key = "\<Esc>[" .. a:key
115+
if a:modifier == 0
116+
return key .. "~"
117+
endif
118+
119+
let mod = printf("%d", a:modifier)
120+
return key .. ';' .. mod .. '~'
121+
endfunc
122+
111123
" Return the kitty keyboard protocol encoding for "key" without a modifier.
112124
" Used for the Escape key.
113125
func GetEscCodeCSIuWithoutModifier(key)

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,8 @@ static char *(features[]) =
724724

725725
static int included_patches[] =
726726
{ /* Add new patch number below this line */
727+
/**/
728+
1736,
727729
/**/
728730
1735,
729731
/**/

0 commit comments

Comments
 (0)