diff --git a/src/term.c b/src/term.c index d1917203d2..2de1508f14 100644 --- a/src/term.c +++ b/src/term.c @@ -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); diff --git a/src/testdir/keycode_check.json b/src/testdir/keycode_check.json index b06e3c52bd..6e3ce5263b 100644 --- a/src/testdir/keycode_check.json +++ b/src/testdir/keycode_check.json @@ -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" + } +} diff --git a/src/testdir/keycode_check.vim b/src/testdir/keycode_check.vim index 8320341d2f..33eb6ceeb7 100644 --- a/src/testdir/keycode_check.vim +++ b/src/testdir/keycode_check.vim @@ -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 diff --git a/src/testdir/test_termcodes.vim b/src/testdir/test_termcodes.vim index 99710fada5..f37f618c81 100644 --- a/src/testdir/test_termcodes.vim +++ b/src/testdir/test_termcodes.vim @@ -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 = [ + \ ['', '13', 0], + \ ['', '13', 2], + \ ['', '13', 5], + \ ['', '13', 6], + \ + \ ['', '15', 0], + \ ['', '15', 2], + \ ['', '15', 5], + \ ['', '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 diff --git a/src/testdir/util/view_util.vim b/src/testdir/util/view_util.vim index 161c8b20cd..5c5bfa81de 100644 --- a/src/testdir/util/view_util.vim +++ b/src/testdir/util/view_util.vim @@ -108,6 +108,18 @@ func GetEscCodeFunckey(key, modifier) return "\[1;".. mod .. a:key endfunc +" Return the kitty keyboard protocol encoding for a function key: +" CSI {number}; {modifiier} ~ +func GetEscCodeFunckey2(key, modifier) + let key = "\[" .. 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) diff --git a/src/version.c b/src/version.c index e6b9f9dfd6..cf2f991016 100644 --- a/src/version.c +++ b/src/version.c @@ -724,6 +724,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1736, /**/ 1735, /**/