Skip to content

Keyboard input duplicates exponentially (1,2,4,8...) on native Wayland, fixed by forcing XWayland #1167

Description

@danmwallace

Summary

On native Wayland, every keystroke in Terax gets duplicated, and the duplication count doubles with each subsequent keystroke: the 1st keypress produces 1 character, the 2nd produces 2, the 3rd produces 4, the 4th produces 8, etc. This reproduces in both the terminal (PTY) and the separate AI chat text input, so it isn't scoped to the terminal/xterm.js input path specifically — it looks like a window/seat-level keyboard event handling bug.

Environment

  • Terax version: 0.8.6 (RPM build)
  • OS: Fedora, Wayland session (XDG_SESSION_TYPE=wayland)
  • autocompleteEnabled and terminalWebglEnabled both toggled off — doubling persists regardless, ruling those features out.

Reproduction

  1. Launch Terax normally on a Wayland session.
  2. Open a terminal tab (or the AI chat input).
  3. Press any key repeatedly, one keystroke at a time.
  4. Character count per keystroke: 1 → 2 → 4 → 8 → 16 ... (doubling, not just +1 per press).

Also confirmed present in the AI chat text box using the same repeated-keystroke test (see old session titles like "dadada", "hdhdd", "helllo" in a long-lived terax-ai-sessions.json — those were this bug being hit organically over past sessions).

Workaround (confirmed fixes it)

Forcing the app onto XWayland instead of native Wayland eliminates the bug entirely:

WAYLAND_DISPLAY= terax

This strongly suggests the bug is in Terax's native Wayland keyboard-binding code, not the X11 path.

Suspected root cause

strings on the terax binary shows direct references to wl_keyboard and wl_callback, indicating a hand-rolled Wayland client keyboard binding (rather than going through a mature toolkit's abstraction). A plausible mechanism: on a wl_seat::capabilities event (which can fire on focus changes, tab switches, etc.), the client calls get_keyboard() again without releasing the previously-bound wl_keyboard object first. This leaves multiple live keyboard listeners bound to the same seat — each subsequent keypress is then delivered once per listener. If handling a keystroke itself triggers another capabilities re-negotiation, the listener count doubles each time, producing the exact 1→2→4→8 pattern observed.

TERAX.md already documents a related class of bug (React 19 Strict Mode double-invoking useEffect, causing duplicate PTY spawns, mitigated with a SPAWN_LOCK mutex) — worth checking whether that mitigation covers the native keyboard-binding path, or whether this is a separate instance of the same underlying pattern (resource acquired again without releasing the prior instance).

Suggested fix direction

Ensure any existing wl_keyboard object is released (wl_keyboard::release) before binding a new one in response to wl_seat::capabilities, and/or guard against re-binding when a valid keyboard object is already held.

Happy to provide more diagnostics (journal logs, binary strings output) if useful.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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