From 3a2eb80d83d59d194a3d07da227431c634892ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 10 Jan 2026 07:36:17 +0100 Subject: [PATCH] input: ignore release events after a keyboard shortcut was triggered This fixes an issue with the kitty keyboard protocol, where 'release' events associated with a shortcut was sent to the client application. Example: user triggers "scroll up". We scroll up. No key event(s) are sent to the client application. Then the user releases the keys. we don't do any shortcut handling on release events, and so we continue with the normal input processing. If the kitty keyboard protocol has been enabled (and specifically, release event reporting has been enabled), then we'll emit a 'release' escape sequence. This in itself is wrong, since the client application never saw the corresponding press event. But we _also_ reset the viewport. The effect (in this example), is that it's impossible to scroll up in the scrollback history. Note that we don't ignore _any_ release event, only the release event for the (final) symbol that triggered the shortcut. This should allow e.g. modifier keys release events to be processed normally, if released before the shortcut key. This is somewhat important, since the client application will have received press events for the modifier keys leading up to the shortcut (if modifier press/release events have been enabled in the kitty keyboard protocol - _Report all keys as escape codes_). Closes #2257 --- CHANGELOG.md | 3 +++ input.c | 24 ++++++++++++++++++++++++ wayland.h | 2 ++ 3 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0462666c..a69f58b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -122,8 +122,11 @@ * Crash when reverse-scrolling (terminfo capability `rin`) such that the current viewport ends up outside the scrollback ([#2232][2232]). * Regression: visual glitches in rare circumstances. +* Key release events for shortcuts being sent to the client + application (kitty keyboard protocol only) ([#2257][2257]). [2232]: https://codeberg.org/dnkl/foot/issues/2232 +[2257]: https://codeberg.org/dnkl/foot/issues/2257 ### Security diff --git a/input.c b/input.c index e8e84fb7..aa6b7f1d 100644 --- a/input.c +++ b/input.c @@ -1605,6 +1605,9 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, if (released) stop_repeater(seat, key); + if (pressed) + seat->kbd.last_shortcut_sym = XKB_KEYSYM_MAX + 1; + bool should_repeat = pressed && xkb_keymap_key_repeats(seat->kbd.xkb_keymap, key); @@ -1706,6 +1709,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, if (bind->k.sym == raw_syms[i] && execute_binding(seat, term, bind, serial, 1)) { + seat->kbd.last_shortcut_sym = sym; goto maybe_repeat; } } @@ -1719,6 +1723,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, bind->mods == (mods & ~consumed) && execute_binding(seat, term, bind, serial, 1)) { + seat->kbd.last_shortcut_sym = sym; goto maybe_repeat; } } @@ -1734,12 +1739,31 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, if (code->item == key && execute_binding(seat, term, bind, serial, 1)) { + seat->kbd.last_shortcut_sym = sym; goto maybe_repeat; } } } } + if (released && seat->kbd.last_shortcut_sym == sym) { + /* + * Don't process a release event, if it corresponds to a + * triggered shortcut. + * + * 1. If we consumed a key (press) event, we shouldn't emit an + * escape for its release event. + * 2. Ignoring the incorrectness of doing so; this also caused + * us to reset the viewport. + * + * Background: if the kitty keyboard protocol was enabled, + * then the viewport was instantly reset to the bottom, after + * scrolling up. + */ + //seat->kbd.last_shortcut_sym = XKB_KEYSYM_MAX + 1; + goto maybe_repeat; + } + /* * Keys generating escape sequences */ diff --git a/wayland.h b/wayland.h index 1b1c1f4c..6247875a 100644 --- a/wayland.h +++ b/wayland.h @@ -151,6 +151,8 @@ struct seat { bool alt; bool ctrl; bool super; + + xkb_keysym_t last_shortcut_sym; } kbd; /* Pointer state */