Skip to content

CsiFunctionalDecoder misinterprets arrow keys as wheel events when DECCKM is not honored #279

@Aaronontheweb

Description

@Aaronontheweb

Summary

When PreferRawInput = true with ScrollInputMode.AlternateScroll, Termina sends CSI ?1h (DECCKM) to switch arrow keys to SS3 form (ESC O A/B/C/D), then uses the sequence form to disambiguate keyboard arrows (SS3) from mouse wheel (CSI). This breaks in any terminal that doesn't honor DECCKM — arrow keys are still sent as CSI form and CsiFunctionalDecoder.TryDecodeBareFinal() misroutes them:

  • CSI A (Up) → WheelUp instead of KeyPressed(UpArrow)
  • CSI B (Down) → WheelDown instead of KeyPressed(DownArrow)
  • CSI C/D (Left/Right) → silently dropped (result = null)

Repro

Any terminal or PTY wrapper that ignores DECCKM: VHS (charmbracelet/vhs), some configurations of screen, older tmux builds. All keyboard navigation via arrow keys stops working — lists don't scroll, cursor doesn't move.

Discovered via Netclaw's CI smoke harness (VHS-driven tapes): netclaw-dev/netclaw#1359.

Root cause

CsiFunctionalDecoder.TryDecodeBareFinal() (lines 33-39) unconditionally treats bare CSI A/B as wheel events when KittyReportAllKeysVisible is false. It assumes DECCKM is in effect and that real arrow keys will arrive as SS3, but DECCKM compliance cannot be assumed.

Suggested fix

When KittyReportAllKeysVisible is false, bare CSI A/B/C/D should fall back to arrow key events rather than wheel events. The alternate-scroll disambiguation needs a different signal — either detect DECCKM support (DA1 query), or use a heuristic (e.g., only treat CSI A/B as wheel when the alternate screen buffer is active AND the terminal confirmed DECCKM via response).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions