Skip to content

# Fix: special characters not input on non‑US keyboard layouts - #13

Open
visitorise wants to merge 1 commit into
Blankeos:mainfrom
visitorise:feature/remove-REMPORT_ALL_KEYS_AS_ESCAPE_CODES-flag
Open

# Fix: special characters not input on non‑US keyboard layouts#13
visitorise wants to merge 1 commit into
Blankeos:mainfrom
visitorise:feature/remove-REMPORT_ALL_KEYS_AS_ESCAPE_CODES-flag

Conversation

@visitorise

@visitorise visitorise commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Fix: special characters not input on non‑US keyboard layouts

1. Symptoms

On non‑US keyboard layouts (e.g. JIS, German, French, Nordic), the following special characters cannot be typed on the main keyboard area:

=, ~, |, `, {, }, +, *

These characters work correctly when typed on the numeric keypad, and standard ASCII letters (az, 09) are unaffected. The issue manifests in Ghostty and other terminals that fully support the Kitty keyboard protocol.

2. Root Cause

The Kitty keyboard protocol's Report all keys as escape codes progressive enhancement (flag 0b1000, value 8) instructs the terminal to encode every keystroke — including plain text-producing keys — as CSI‑u escape sequences instead of raw UTF‑8 bytes.

On non‑US keyboard layouts, special character keys occupy physical positions that differ from the US layout and may interact with layout‑specific modifier keys defined by xkb (e.g. Kana / Kanji on JIS, Alt‑Gr on D and Y, Option on Mac). When the terminal wraps these keystrokes in CSI‑u sequences (CSI 61 ; modifiers u), the OS‑level keyboard‑layout translation can attach unexpected modifier bits (e.g. SUPER, META). Crossterm then fails to decode the resulting escape sequence back into KeyCode::Char(…), causing the character to be silently dropped.

The numeric keypad uses distinct hardware scancodes that bypass this code path, which is why those keys are unaffected.

3. Fix

Remove REPORT_ALL_KEYS_AS_ESCAPE_CODES from the PushKeyboardEnhancementFlags invocation while keeping the other two flags.

Two files changed:

  • src/main.rs — main event loop terminal setup
  • src/remote/mod.rs — remote TUI terminal mode guard

Before:

PushKeyboardEnhancementFlags(
    KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
        | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
        | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES,
),

After:

PushKeyboardEnhancementFlags(
    KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
        | KeyboardEnhancementFlags::REPORT_EVENT_TYPES,
),

4. Impact Analysis

4.1 Why this is safe

The DISAMBIGUATE_ESCAPE_CODES and REPORT_EVENT_TYPES flags are retained, so all of the following continue to work:

  • Resolving ESC / Ctrl+C ambiguity. The DISAMBIGUATE_ESCAPE_CODES flag alone is sufficient to distinguish a lone Escape press from the start of an escape sequence, and to receive Ctrl+C as a CSI‑u sequence instead of a kernel SIGINT.
  • Key event type distinction. The REPORT_EVENT_TYPES flag continues to deliver Press, Repeat, and — for non‑text‑producing keys — Release events.
  • Dialog protection against stray Enter releases. The current handle_keys guard at app.rs:3035 returns early on KeyEventKind::Release. After this change, plain-text keys including Enter no longer produce Release events at all (per the Kitty spec: "Enter, Tab and Backspace keys will not have release events unless Report all keys as escape codes is also set"). The guard becomes a permanent no‑op for Enter, which is strictly safer: a spurious Enter Release can never reach a freshly‑opened dialog.

4.2 What gets better after the change

  • Enter Release protection becomes structural, not just runtime.

Currently, the only defence against a spurious Enter key‑release submitting a
freshly‑opened dialog (app.rs:3035) is a runtime check
(if key.kind == KeyEventKind::Release { return; }).

After this change the Enter Release event is no longer reported at all on
Kitty‑protocol terminals, so the dialog can never receive it. This turns a
runtime guard into a structural guarantee, eliminating the class of timing bugs
where a pending Enter Release races ahead of a dialog opening.

  • Input now matches terminal baseline behaviour.

Disabling REPORT_ALL_KEYS_AS_ESCAPE_CODES restores the legacy convention that
plain text keys are delivered as raw UTF‑8 bytes. This is the path every TUI,
shell, and editor has relied on for decades, including in terminals that do
not support the Kitty protocol. Users switching between Ghostty (Kitty),
macOS Terminal.app (legacy), and tmux will now experience identical input
semantics for text characters, reducing “works only in my terminal” issues.

  • Reduced key‑event volume on fully‑capable terminals.

REPORT_ALL_KEYS_AS_ESCAPE_CODES causes the terminal to send CSI‑u sequences
for every character, including modifier info that tui‑textarea does not use for
plain text insertion. Removing it eliminates this overhead for the dominant
text‑entry path, slightly reducing input latency on high‑frequency keystroke
streams.

4.3 Behaviour shift per terminal category

Fully‑capable terminals (Kitty, Ghostty, foot, Alacritty ≥0.13, iTerm2, Contour, Rio, VS Code terminal)
The terminal parser's implementation of CSI‑u escape sequences is no longer mandatory for all keys. The OS delivers special character keys as raw UTF‑8, which maps to the correct KeyCode::Char(…) path.

Terminals with partial keyboard protocol support (WezTerm when keyboard enhancement is enabled, tmux extended-keys, Zellij)
The supports_keyboard_enhancement() query relies on the Kitty push/pop/query stack. Since these terminals either default to off or lack the stack, the query fails and keyboard_enhancement is false. The PushKeyboardEnhancementFlags call is never issued — this change is invisible to them.

Terminals that support modifyOtherKeys but not the Kitty stack (xterm)
Same reasoning: supports_keyboard_enhancement() returns false because CSI ? u goes unanswered or the response is ambiguous. No flags are pushed, so the change is invisible.

Unsupported terminals (macOS Terminal.app, PuTTY, GNOME Terminal, Konsole, mosh, GNU Screen)
keyboard_enhancement is always false — the flags are never sent. Zero impact.

4.4 Build verification

  • cargo check passes without warnings or errors.

5. Testing

  • Manual keyboard input verification. All seven affected special characters (=, ~, |, `, {, }, +, *) were typed on a JIS keyboard layout and confirmed to insert correctly into the prompt.
  • Existing dialog behaviour. /models and other slash‑command dialogs were exercised. Dialog open, navigation, selection, and close behave identically to before the change. No spurious Enter‑submit regressions were observed.
  • Existing test suite. All existing tests pass; none assert the removed flag value.

Remove REPORT_ALL_KEYS_AS_ESCAPE_CODES from Kitty keyboard protocol
enhancement flags. On non-US layouts, special character keys that
interact with layout-specific modifiers (JIS Kana/Kanji, Alt-Gr, etc.)
were encoded as CSI-u escape sequences with unexpected modifier bits,
causing crossterm to fail decoding them as KeyCode::Char(..).

Keeping DISAMBIGUATE_ESCAPE_CODES and REPORT_EVENT_TYPES ensures
ESC/Ctrl-C disambiguation and key event type reporting remain functional.
Numeric keypad input is unaffected as it uses a separate scancode path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant