https://bugs.kde.org/show_bug.cgi?id=524772
Vincenzo Di Massa <[email protected]> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|REPORTED |CONFIRMED CC| |[email protected] Ever confirmed|0 |1 --- Comment #2 from Vincenzo Di Massa <[email protected]> --- I can reproduce this on a different system, and I think I found the underlying issue. Environment: ``` CachyOS Konsole 26.08.0 Plasma 6.7.4 KDE Frameworks 6.29.0 Qt 6.11.2 Wayland Neovim 0.12.5 ``` I captured the raw bytes generated by Konsole after explicitly enabling individual Kitty keyboard protocol progressive-enhancement flags. With flag 1 (`disambiguate escape codes`): ``` CSI > 1 u Escape press: 0.906362s len=1 hex=1b repr=b'\x1b' ``` So Konsole still sends Escape as the legacy raw `0x1b`. With flag 2 (`report event types`) alone: ``` CSI > 2 u 1.155940s len=1 hex=1b 1.156715s len=9 hex=1b 5b 32 37 3b 31 3a 33 75 ``` i.e. ``` press: ESC release: CSI 27;1:3u ``` The release arrives about 0.775 ms after the press. Most importantly, with flags 1+2: ``` CSI > 3 u 0.655176s len=1 hex=1b 0.655901s len=9 hex=1b 5b 32 37 3b 31 3a 33 75 ``` Again: ``` press: ESC release: CSI 27;1:3u ``` The release arrives about 0.725 ms after the press. This seems to identify the actual bug. According to the Kitty keyboard protocol, progressive enhancement flag 1 specifically exists to disambiguate sequences such as Escape. When it is enabled, Escape should be reported using CSI-u rather than the legacy `0x1b`. The specification also explicitly says that the exceptions which retain legacy encoding are Enter, Tab and Backspace. Escape is not one of those exceptions. Therefore, with flag 1 I would expect: ``` press: CSI 27u ``` and with flags 1+2: ``` press: CSI 27u release: CSI 27;1:3u ``` Current Konsole instead emits: ``` press: ESC release: CSI 27;1:3u ``` This also explains why the problem depends on timing in Neovim. `nvim --clean` on this system has: ``` ttimeout ttimeoutlen=50 timeout timeoutlen=1000 ``` The standalone raw Escape is therefore ambiguous: it can either be a complete Escape key event or the beginning of the CSI-u release sequence which follows less than 1 ms later. Setting: ``` :set ttimeoutlen=0 ``` makes the problem disappear, but that is only a workaround. I also checked the current Konsole source. `Vt102Emulation::handleKittyKeyEvent()` currently contains this logic: ```cpp // For Backspace/Enter/Tab/Escape: check if we should use legacy encoding if (keyCode == 27 || keyCode == 13 || keyCode == 9 || keyCode == 127) { ... if (!hasMods && !(flags & 8)) { // No modifiers — send legacy byte return false; } } ``` This appears to be the cause: Escape (`27`) is grouped with Enter, Tab and Backspace, so flag 1 does not actually disambiguate an unmodified Escape press. It only switches away from legacy encoding when flag 8 is enabled. A possible minimal fix would be to handle Escape separately. Untested patch: ```diff - // For Backspace/Enter/Tab/Escape: check if we should use legacy encoding - if (keyCode == 27 || keyCode == 13 || keyCode == 9 || keyCode == 127) { + // Escape must use CSI-u when the "disambiguate escape codes" + // flag is enabled. Enter, Tab and Backspace retain their legacy + // encoding unless "report all keys" is enabled. + if (keyCode == 27) { + if (!(flags & 1) && !(flags & 8)) { + return false; + } + } else if (keyCode == 13 || keyCode == 9 || keyCode == 127) { bool hasMods = false; #ifdef HAVE_XKBCOMMON bool capsLock = xkb_state_mod_name_is_active(_xkbData.state_us, XKB_MOD_NAME_CAPS, XKB_STATE_MODS_EFFECTIVE); bool numLock = xkb_state_mod_name_is_active(_xkbData.state_us, XKB_MOD_NAME_NUM, XKB_STATE_MODS_EFFECTIVE); int modBits = kittyModifierBits(mods, capsLock, numLock); #else int modBits = kittyModifierBits(mods, false, false); #endif hasMods = (modBits != 1); if (!hasMods && !(flags & 8)) { // No modifiers — send legacy byte return false; } } ``` I have not built or tested this patch yet, but the raw input capture seems to make the source of the problem quite clear. PS: The unusually short interval between the Escape press and release is expected in this setup and is worth explaining, although it is not the cause of the Konsole bug. The Escape key on this Cheapino V2 is configured as a QMK Layer-Tap key (`LT(layer, KC_ESC)`). A Layer-Tap key has to distinguish between two meanings: when held it activates a layer, while when tapped it sends its tap keycode (`KC_ESC`). QMK therefore cannot simply expose the physical key press immediately as an Escape press: it first has to determine whether the physical action is going to become a tap or a hold. When the physical key is released and QMK classifies the action as a tap, the logical `KC_ESC` event is generated at that point. As a consequence, the host sees the synthesized Escape press and release almost back-to-back, even though the physical key itself was held for a normal human tap duration. This explains the sub-millisecond interval visible in the capture: ```text 0.655176s Escape press 0.655901s Escape release ~0.725 ms ``` So this timing is not evidence of a keyboard hardware or debounce problem. It is a natural consequence of using a dual-role Layer-Tap key. It does, however, make the Konsole issue very easy to reproduce: Konsole emits the tap as a legacy `ESC` byte and then, less than 1 ms later, emits the key-release event as a CSI-u sequence. An application receiving the stream therefore sees: ```text ESC CSI 27;1:3u ``` with virtually no temporal separation between the two. A regular dedicated Escape key would normally have a much larger interval between press and release, so the same protocol bug can be substantially harder to notice. -- You are receiving this mail because: You are watching all bug changes.
