From b5cb69f8a4a310fb3f18e79bcf5c7ed41d635a48 Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Tue, 31 Dec 2024 12:16:25 -0600 Subject: [PATCH] fix(tui): handle key events for arrow and function keys (#31804) Arrow and function keys do not use CSI u with the kitty keyboard protocol. For example, the Up arrow key uses CSI A, and the function keys use a variety of different CSI sequences. Until now, termkey only parsed subparams used by key events for CSI u sequences. The result being that any key which did not use CSI u (e.g. arrow and function keys) was being emitted twice by termkey since it was not recognizing the separate press and release events. This commit makes termkey also parse subparams for other key sequences so that the release key events do not send duplicate keys. --- src/nvim/tui/termkey/driver-csi.c | 50 +++++++++++++++++++++-------- src/nvim/tui/termkey/termkey_defs.h | 1 + 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/nvim/tui/termkey/driver-csi.c b/src/nvim/tui/termkey/driver-csi.c index 3f54c874d1..9741ec72b9 100644 --- a/src/nvim/tui/termkey/driver-csi.c +++ b/src/nvim/tui/termkey/driver-csi.c @@ -31,11 +31,20 @@ static TermKeyResult handle_csi_ss3_full(TermKey *tk, TermKeyKey *key, int cmd, if (nparams > 1 && params[1].param != NULL) { int arg = 0; - result = termkey_interpret_csi_param(params[1], &arg, NULL, NULL); + int subparam = 0; + size_t nsubparams = 1; + result = termkey_interpret_csi_param(params[1], &arg, &subparam, &nsubparams); if (result != TERMKEY_RES_KEY) { return result; } + if (nsubparams > 0) { + key->event = parse_key_event(subparam); + if (key->event == TERMKEY_EVENT_UNKNOWN) { + return TERMKEY_RES_NONE; + } + } + key->modifiers = arg - 1; } else { key->modifiers = 0; @@ -103,11 +112,20 @@ static TermKeyResult handle_csifunc(TermKey *tk, TermKeyKey *key, int cmd, TermK int args[3]; if (nparams > 1 && params[1].param != NULL) { - result = termkey_interpret_csi_param(params[1], &args[1], NULL, NULL); + int subparam = 0; + size_t nsubparams = 1; + result = termkey_interpret_csi_param(params[1], &args[1], &subparam, &nsubparams); if (result != TERMKEY_RES_KEY) { return result; } + if (nsubparams > 0) { + key->event = parse_key_event(subparam); + if (key->event == TERMKEY_EVENT_UNKNOWN) { + return TERMKEY_RES_NONE; + } + } + key->modifiers = args[1] - 1; } else { key->modifiers = 0; @@ -178,18 +196,8 @@ static TermKeyResult handle_csi_u(TermKey *tk, TermKeyKey *key, int cmd, TermKey } if (nsubparams > 0) { - switch (subparam) { - case 1: - key->event = TERMKEY_EVENT_PRESS; - break; - case 2: - key->event = TERMKEY_EVENT_REPEAT; - break; - case 3: - key->event = TERMKEY_EVENT_RELEASE; - break; - default: - // Invalid event + key->event = parse_key_event(subparam); + if (key->event == TERMKEY_EVENT_UNKNOWN) { return TERMKEY_RES_NONE; } } @@ -430,6 +438,20 @@ TermKeyResult termkey_interpret_modereport(TermKey *tk, const TermKeyKey *key, i #define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) +static TermKeyEvent parse_key_event(int n) +{ + switch (n) { + case 1: + return TERMKEY_EVENT_PRESS; + case 2: + return TERMKEY_EVENT_REPEAT; + case 3: + return TERMKEY_EVENT_RELEASE; + default: + return TERMKEY_EVENT_UNKNOWN; + } +} + static TermKeyResult parse_csi(TermKey *tk, size_t introlen, size_t *csi_len, TermKeyCsiParam params[], size_t *nargs, unsigned *commandp) { diff --git a/src/nvim/tui/termkey/termkey_defs.h b/src/nvim/tui/termkey/termkey_defs.h index 09d3a5615a..87d3f63447 100644 --- a/src/nvim/tui/termkey/termkey_defs.h +++ b/src/nvim/tui/termkey/termkey_defs.h @@ -124,6 +124,7 @@ typedef enum { } TermKeyMouseEvent; typedef enum { + TERMKEY_EVENT_UNKNOWN, TERMKEY_EVENT_PRESS, TERMKEY_EVENT_REPEAT, TERMKEY_EVENT_RELEASE,