Skip to content

fix(renderer): disable mouse tracking before raw mode in destroy path#905

Open
agutmanstein-scale wants to merge 1 commit intoanomalyco:mainfrom
agutmanstein-scale:fix/mouse-garble-on-destroy
Open

fix(renderer): disable mouse tracking before raw mode in destroy path#905
agutmanstein-scale wants to merge 1 commit intoanomalyco:mainfrom
agutmanstein-scale:fix/mouse-garble-on-destroy

Conversation

@agutmanstein-scale
Copy link
Copy Markdown

Issue for this PR

Closes #904

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

When a renderer is destroyed via destroy()finalizeDestroy()cleanupBeforeDestroy(), mouse escape sequences leak into the terminal as garbled text.

Root cause: cleanupBeforeDestroy() calls stdin.setRawMode(false) (re-enabling terminal ECHO) before mouse tracking is disabled. Between these two events, any mouse movement generates escape sequences that get echoed as raw text like 35;89;19M35;84;20M35;76;22M35....

The correct pattern already exists in suspend() (line ~2052-2077):

  1. disableMouse() — mouse off while raw mode is still on (no ECHO)
  2. suspendRenderer() — native cleanup
  3. setRawMode(false) — raw mode off last (safe, mouse already disabled)

This PR applies the same ordering to cleanupBeforeDestroy():

  1. Call disableMouse() while raw mode is still on
  2. Drain buffered mouse events from stdin
  3. Then disable raw mode

The _useMouse guard ensures disableMouse() is only called when mouse tracking was enabled. The stdin drain pattern matches resume().

How did you verify your code works?

  • Code review: compared cleanupBeforeDestroy() ordering with suspend() ordering
  • The fix is a minimal reordering of existing operations — no new logic

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

🤖 Generated with Claude Code

…eDestroy

When a renderer is destroyed, cleanupBeforeDestroy() calls
stdin.setRawMode(false) before mouse tracking is disabled. This
re-enables terminal ECHO while mouse events are still being sent,
causing them to appear as garbled escape sequences in the terminal.

The correct ordering already exists in suspend(): disableMouse() is
called before setRawMode(false). This commit applies the same pattern
to cleanupBeforeDestroy():

1. Call disableMouse() while raw mode is still on (no ECHO)
2. Drain buffered mouse events from stdin
3. Then disable raw mode

Fixes anomalyco#904

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
agutmanstein-scale added a commit to agutmanstein-scale/opencode that referenced this pull request Apr 1, 2026
…ragmented input

Two bugs cause SGR mouse escape sequences to appear as garbled text:

1. **Post-exit garbling** (new fix): cleanupBeforeDestroy() calls
   setRawMode(false) before mouse tracking is disabled, creating a
   window where mouse events echo as raw bytes. Fixed by adding
   disableMouse() + stdin drain before setRawMode(false), matching
   the correct ordering already used in suspend().

2. **In-session garbling** (ported from anomalyco#19520): StdinParser timeout
   fires mid-mouse-sequence during heavy event loop pressure, leaking
   individual bytes as KEY events. Fixed by patching three timeout
   paths with recovery flags and deferred processing.

Also bumps DEFAULT_TIMEOUT_MS from 20 to 25ms for defense-in-depth.

Patch targets @opentui/core@0.1.95 (current dev dependency).
Upstream fix: anomalyco/opentui#905

Closes anomalyco#20458

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
18vijayb pushed a commit to 18vijayb/opencode that referenced this pull request Apr 2, 2026
…ragmented input

Two bugs cause SGR mouse escape sequences to appear as garbled text:

1. **Post-exit garbling** (new fix): cleanupBeforeDestroy() calls
   setRawMode(false) before mouse tracking is disabled, creating a
   window where mouse events echo as raw bytes. Fixed by adding
   disableMouse() + stdin drain before setRawMode(false), matching
   the correct ordering already used in suspend().

2. **In-session garbling** (ported from anomalyco#19520): StdinParser timeout
   fires mid-mouse-sequence during heavy event loop pressure, leaking
   individual bytes as KEY events. Fixed by patching three timeout
   paths with recovery flags and deferred processing.

Also bumps DEFAULT_TIMEOUT_MS from 20 to 25ms for defense-in-depth.

Patch targets @opentui/core@0.1.95 (current dev dependency).
Upstream fix: anomalyco/opentui#905

Closes anomalyco#20458

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
agutmanstein-scale added a commit to agutmanstein-scale/opencode that referenced this pull request Apr 2, 2026
…ragmented input

Two bugs cause SGR mouse escape sequences to appear as garbled text:

1. **Post-exit garbling** (new fix): cleanupBeforeDestroy() calls
   setRawMode(false) before mouse tracking is disabled, creating a
   window where mouse events echo as raw bytes. Fixed by adding
   disableMouse() + stdin drain before setRawMode(false), matching
   the correct ordering already used in suspend().

2. **In-session garbling** (ported from anomalyco#19520): StdinParser timeout
   fires mid-mouse-sequence during heavy event loop pressure, leaking
   individual bytes as KEY events. Fixed by patching three timeout
   paths with recovery flags and deferred processing.

Also bumps DEFAULT_TIMEOUT_MS from 20 to 25ms for defense-in-depth.

Patch targets @opentui/core@0.1.95 (current dev dependency).
Upstream fix: anomalyco/opentui#905

Closes anomalyco#20458

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

bug: mouse escape sequences garbled after renderer destroy — cleanupBeforeDestroy() disables raw mode before mouse tracking

1 participant