fix(hud): stop being born click-through, wait for the renderer to ask - #279
Conversation
The HUD asked to be input-transparent twice: once here, at construction, and once from the renderer's mount effect. The first one is the whole bug in #266 — the app running, the bar painted, and every click, drag and button dead, forever, from the very first launch. On Windows `setIgnoreMouseEvents(true, { forward: true })` is a global WH_MOUSE_LL hook, and that hook is the only route back: Chromium delivers no pointermove to a window it has made input-transparent, so the renderer cannot ask to leave the state it is stuck in. Electron latches the install behind `forwarding_mouse_messages_` and only retries after a setIgnoreMouseEvents(false) — the call the dead hook prevents. So one hook that is refused, or that Windows revokes for overrunning the 300 ms LowLevelHooksTimeout, bricks the UI with no way out. Construction time is the worst possible moment to ask for it: that hook callback runs on the main thread, which is still booting the app. The renderer asks a frame or two later, over IPC, on a thread that is provably pumping messages — and if that ask never comes, the bar stays clickable instead of turning into a ghost. Costs an invisible rectangle that can swallow one desktop click in the two frames between show and mount. Refs #266.
Two halves, because both are load-bearing after #266: nothing may call setIgnoreMouseEvents while the HUD window is being constructed, and the renderer must still ask for it once it has mounted. The window is recreated through the app's own path (second-instance → showMainWindow → createHudOverlayWindow) with the native call taped, and the tape is read back synchronously — no await between arming it and snapshotting, so no renderer IPC can slip into what is meant to be construction only. The source selector is opened first purely to keep the window list non-empty while the HUD is destroyed: emptying it fires window-all-closed, which quits the app under the test. Ablated: restoring the deleted line turns duringConstruction into [[true, {forward: true}]] and the first assertion fails, so the test does cover the regression it claims to.
"A frame or two" was an assumption. Timing the real app from ready-to-show to the renderer's first hud-overlay-ignore-mouse-events puts it at 83 and 90 ms over two clean runs — roughly forty times what the comment claimed, and the number a reviewer should be weighing against #266. (A third run read -403 ms: the tape catches setIgnoreMouseEvents on the prototype, so an IPC still in flight from the destroyed HUD's renderer lands on the new window and dates the ask before the window exists. The probe was throwaway; the two clean runs are the ones quoted.)
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Local Windows e2e re-run on this branch is blocked, not skipped, and not passing.
That path resolves outside the worktree, into the main checkout, whose I did get a green run out of the guard test right after that failure, and it is worthless: the build had exited 1, so it exercised the previous bundle. Not counting it. What stands instead:
|
|
Local Windows e2e now run for real — the blocker in my previous comment was an unrelated conflicted Identical to the pre-merge run on #269. The two failures are #130 — Disregard the "worthless green" note above: that run is superseded by this one. |
Cherry-pick of #269 (merged to
mainas 6eb5bbb, 7e14d17, 7049644) onto the open RC branch, per AGENTS.md § Release branches.Refs #266 — the HUD painted on screen and every click dead from the first launch, reported on 1.8.0.
The fix:
createHudOverlayWindow()asked for click-through at construction and from the renderer's mount effect. Only the second is safe. On Windows theforwardoption is a globalWH_MOUSE_LLhook, and that hook is the only route back out — Chromium sends nopointermoveto a window it has made input-transparent, so the renderer can never ask to leave the state, and Electron latches the install behindforwarding_mouse_messages_. One refused or revoked hook and the UI is inert with no way back. The construction-time call goes; the renderer's ask stays.Full mechanism, measurements and platform analysis in #269.
Verification on this branch: the diff is byte-identical to what landed on
main(diffof both ranges is empty). Windows e2e re-run on the cherry-pick — result in a comment below.