fix(gui): close-path deadlock, slow window close, null-panel click NPE
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Sun, 20 Sep 2026 14:12:36 +0000 (17:12 +0300)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Sun, 20 Sep 2026 14:12:36 +0000 (17:12 +0300)
commit2be827c3d54a6848a5d7e5fa74ea23162092ac54
treefbcbd80aa3ca8ad3bddde3b8d48f4692c017b839
parent8eeecdb82b8947a6f96c2026606ad7ca5dacc31f
fix(gui): close-path deadlock, slow window close, null-panel click NPE

Two live-session defects fixed (diagnosed from a frozen launcher
process via jstack + telemetry log):

- ViewPanel.stop(): interrupt the render thread BEFORE
  transformExecutor.shutdownNow() and clear pendingPaints only after
  the join. shutdownNow() cancelled queued paint tasks whose finally
  countDown() then never ran, parking the render thread forever on
  paintLatch.await() and hanging the EDT in renderThread.join() —
  window unclosable, SIGKILL required.
- Slow close (~2.8 s on every demo): device poll threads sleep 2 s
  and stop() did a bare join(1000), burning the full timeout on the
  EDT. HeadTrackingManager/SpaceMouseManager now interrupt before
  join. SpaceNavigatorHid polls (250 ms) before read() — Linux close()
  does not wake a read blocked on a silent-at-rest device — and stop()
  joins before closing the fd. HeadTracker.stop() closes before join.
- ViewPanel.stop() now stops GlobalIllumination: GI worker threads
  used to leak across demos in the launcher JVM (~3 cores stolen from
  the next demo). colorOf() instanceof-guards the SolidPolygon cast —
  textured triangles in a GI scene caused a ClassCastException storm.
- Clicking a text editor threw NPE (this.viewPanel is null): demos
  build scenes with a null ViewPanel for the headless golden harness,
  but GuiComponent.mouseClicked dereferenced the field. Focus never
  engaged (no red border) and the exception aborted the frame being
  painted (black screen until camera move). MouseInteractionController
  gains a 4-arg mouseClicked carrying the dispatching view's
  KeyboardFocusStack; RenderingContext/ViewPanel forward it;
  GuiComponent focuses via the stack (legacy 1-arg null-guarded).

Verified: 48/48 engine tests, demos ALL GOLDENS PASS; repro harness
closes in 64 ms where the old code hung or took 2.8 s; live user
check of editor focus.
src/main/java/eu/svjatoslav/aukio/e3d/gui/GuiComponent.java
src/main/java/eu/svjatoslav/aukio/e3d/gui/ViewPanel.java
src/main/java/eu/svjatoslav/aukio/e3d/gui/headtrack/HeadTracker.java
src/main/java/eu/svjatoslav/aukio/e3d/gui/headtrack/HeadTrackingManager.java
src/main/java/eu/svjatoslav/aukio/e3d/gui/humaninput/MouseInteractionController.java
src/main/java/eu/svjatoslav/aukio/e3d/gui/spacemouse/SpaceMouseManager.java
src/main/java/eu/svjatoslav/aukio/e3d/gui/spacemouse/SpaceNavigatorHid.java
src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/RenderingContext.java
src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/gi/GlobalIllumination.java