[Bug] Minecraft (wasm-voxel) leaks global mouse/keyboard listeners that break input app-wide after leaving the game #228

Closed
opened 2026-06-16 05:17:49 +00:00 by spikerj · 1 comment
Owner

Summary

Follow-up to the game keyboard-capture fix (spikersoft-angular PR #61, issues #182/#157/#181/#193). The Minecraft (wasm-voxel) engine leaks all of its global window/document listeners when you leave the game, and most are registered with preventDefault() enabled. The leaked document mousedown/mouseup/mousemove + contextmenu listeners then preventDefault() those events everywhere, which blocks click-to-focus on inputs (so form fields look completely inert) and disables the right-click menu across the whole app.

Root cause

GameWindow.registerListener(parent, event, listener, preventDefaults = true) wraps the handler in an anonymous function and calls parent.addEventListener(...) but stores no reference, so nothing can ever be removed. Minecraft.stop() (called from wasm-voxel.component.ngOnDestroy) resets renderers but never removes these listeners. Keyboard.create() similarly adds static window keydown/keyup listeners (the keyup one calls preventDefault()) that are never removed. block-editor.component adds an anonymous document keydown (Escape handler) that is also never removed.

Because registerListener defaults preventDefaults to true, the leaked document mousedown handler calls event.preventDefault() on every mouse press anywhere — and preventDefault on mousedown suppresses the default focus, so clicking into a text field no longer focuses it. This matches the "fields render but are completely unresponsive / can't even click in" reports (#181 calendar, #193 fundraiser modal) when the user had visited Minecraft earlier in the session.

Proposed fix

  1. GameWindow: track every registered { parent, event, handler } and add removeListeners() that removes them all.
  2. Keyboard: store the keydown/keyup handlers in static fields and add destroy() to remove them (and guard create() against double-registration).
  3. Minecraft.stop(): call this.window.removeListeners() and Keyboard.destroy().
  4. block-editor.component: store the keydown handler and remove it in ngOnDestroy.

Acceptance criteria

  • After opening Minecraft and navigating away, document no longer has lingering mousedown/mousemove/mouseup/contextmenu/keydown/keyup listeners from the game.
  • Clicking into and typing in the Calendar event form / Fundraiser modal / sandbox works after a Minecraft session.
  • Right-click context menu works again after leaving Minecraft.
  • In-game input still works while Minecraft is open.
## Summary Follow-up to the game keyboard-capture fix (spikersoft-angular PR #61, issues #182/#157/#181/#193). The Minecraft (`wasm-voxel`) engine leaks **all** of its global `window`/`document` listeners when you leave the game, and most are registered with `preventDefault()` enabled. The leaked `document` `mousedown`/`mouseup`/`mousemove` + `contextmenu` listeners then `preventDefault()` those events everywhere, which blocks click-to-focus on inputs (so form fields look completely inert) and disables the right-click menu across the whole app. ## Root cause `GameWindow.registerListener(parent, event, listener, preventDefaults = true)` wraps the handler in an anonymous function and calls `parent.addEventListener(...)` but **stores no reference**, so nothing can ever be removed. `Minecraft.stop()` (called from `wasm-voxel.component.ngOnDestroy`) resets renderers but never removes these listeners. `Keyboard.create()` similarly adds static `window` `keydown`/`keyup` listeners (the `keyup` one calls `preventDefault()`) that are never removed. `block-editor.component` adds an anonymous `document` `keydown` (Escape handler) that is also never removed. Because `registerListener` defaults `preventDefaults` to `true`, the leaked `document` `mousedown` handler calls `event.preventDefault()` on every mouse press anywhere — and `preventDefault` on `mousedown` suppresses the default focus, so clicking into a text field no longer focuses it. This matches the "fields render but are completely unresponsive / can't even click in" reports (#181 calendar, #193 fundraiser modal) when the user had visited Minecraft earlier in the session. ## Proposed fix 1. `GameWindow`: track every registered `{ parent, event, handler }` and add `removeListeners()` that removes them all. 2. `Keyboard`: store the keydown/keyup handlers in static fields and add `destroy()` to remove them (and guard `create()` against double-registration). 3. `Minecraft.stop()`: call `this.window.removeListeners()` and `Keyboard.destroy()`. 4. `block-editor.component`: store the keydown handler and remove it in `ngOnDestroy`. ## Acceptance criteria - [ ] After opening Minecraft and navigating away, `document` no longer has lingering `mousedown`/`mousemove`/`mouseup`/`contextmenu`/`keydown`/`keyup` listeners from the game. - [ ] Clicking into and typing in the Calendar event form / Fundraiser modal / sandbox works after a Minecraft session. - [ ] Right-click context menu works again after leaving Minecraft. - [ ] In-game input still works while Minecraft is open.
Author
Owner

Resolved in spikersoft-angular PR #62 (merged to master). GameWindow now tracks and removes every global window/document listener it registers (removeListeners()), Keyboard.destroy() removes its keydown/keyup handlers (and create() guards against double-registration), Minecraft.stop() calls both, and block-editor removes its keydown listener in ngOnDestroy. This eliminates the leaked global mousedown/mouseup/mousemove/contextmenu/keyup handlers that previously preventDefault()-ed input across the whole app after leaving Minecraft. Closing.

Resolved in spikersoft-angular PR #62 (merged to `master`). `GameWindow` now tracks and removes every global window/document listener it registers (`removeListeners()`), `Keyboard.destroy()` removes its keydown/keyup handlers (and `create()` guards against double-registration), `Minecraft.stop()` calls both, and `block-editor` removes its keydown listener in `ngOnDestroy`. This eliminates the leaked global `mousedown`/`mouseup`/`mousemove`/`contextmenu`/`keyup` handlers that previously `preventDefault()`-ed input across the whole app after leaving Minecraft. Closing.
Sign in to join this conversation.