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.

Reply via email to