Repository navigation
fix(recording): keep the countdown's last digit out of frame 0 on Linux - #1267
Conversation
On Linux nothing excludes the countdown overlay from a capture, so the only protection is ordering. The hide sent the overlay its cleared value and hid the window in the same tick, so the renderer never painted the empty state: the compositor's last buffer still showed the "1", GNOME Shell faded that buffer out over ~150 ms, and the capture armed right after the hide recorded the fade in its first frames. On Linux the hide now clears the digit, waits for the overlay renderer to report that empty frames have been painted (bounded by a 500 ms fallback), and only then hides the window, so whatever the compositor animates afterwards is transparent. A countdown that re-shows the overlay during that wait keeps it up. Windows and macOS keep the immediate hide.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
Summary
On Linux the countdown overlay's last digit ends up in the first frames of a full-screen recording. This PR makes the Linux hide wait until the overlay has really painted its empty state before the window is hidden, so the capture can no longer pick up the "1". It's a minimal bugfix meant to be cherry-picked onto
release/v2.1.0. Windows and macOS keep the immediate hide.Observed
Found during the manual e2e pass of the installed v2.1.0-rc.1
.debon Ubuntu 24.04, GNOME Shell 46, native Wayland, Electron 41.2.1, 1920×1080 @ 100%.Flow: HUD record → GNOME portal picker (answered) → 3-2-1 countdown overlay → capture.
[capture-linux] portal stream started→capture started {"width":1920,"height":1080,"fps":60}, with nothing about the overlay.Root cause
On Linux nothing keeps the overlay out of a capture (
setContentProtectiondoes nothing there), so the only protection is ordering. The capture side is fine: a prepared portal session holds the grant without connecting apw_stream, so no frames exist beforearm(). The hide is what goes wrong:countdown-overlay-hidesent the overlaynulland calledhide()in the same tick. Chromium stops producing frames for a hidden window, so the renderer never painted its empty state, and the last buffer the compositor held still showed the "1".start-native-linux-recording→arm()→ first frame). That chain finishes well inside the animation, so the first frames record the fading "1".Fix
electron/countdownOverlay.ts(new, small, testable without Electron):hideCountdownOverlay(win, runId, { platform, isSuperseded }).webContents.ipcforcountdown-overlay-cleared, sendsnull, waits for that report for the samerunId, and only then callshide(). Whatever the compositor animates after that is transparent, however long the animation runs.handlers.ts), the hide is skipped so the new countdown stays on screen.nulland callshide()synchronously, exactly as before.CountdownOverlay.tsx: commits each value withflushSync, so the DOM is already empty before any frame is counted. Onnullit waits 3 animation frames (one for Chromium to produce the empty frame, one for it to reach the compositor, one of margin), then callsnotifyCountdownOverlayCleared(runId).preload.ts/electron-env.d.ts:onCountdownOverlayValuenow also passesrunId(backward compatible). AddsnotifyCountdownOverlayCleared.handlers.ts: the hide handler nowawaitshideCountdownOverlay. The renderer already awaited it (safeHideCountdownOverlay) beforestartRecording, souseScreenRecorderneeds no change.On Linux the take starts about 50 ms later at 60 Hz, after a 3 s countdown.
Follow-up, not in this PR: the overlay is not centred on Wayland
A Wayland client cannot position its own toplevel, so the
x/ythatcreateCountdownOverlayWindowcomputes are ignored and mutter places the window itself: top-left (≈210,160) on one take, lower-middle on another. The obvious fix would make the overlay window cover the whole work area and centre the circle in CSS. That isn't small or safe for a release cherry-pick:setIgnoreMouseEvents) is not reliable on Linux. The HUD does not use it there.Better handled in a separate PR.
Related issue
No issue filed. Found during the manual e2e pass of v2.1.0-rc.1 on Ubuntu 24.04 / GNOME 46 Wayland.
Type of change
Release impact
Desktop impact
Screenshots / video
Frame 0 of the affected takes (before the fix): the "1" in a dark circle, top-left in one take and faint, lower-middle, in the other. The overlay fades out over frames 0–3.
Testing
Unit tests (platform pinned through the
platformargument, notprocess.platform):electron/countdownOverlay.test.ts, for the ordering on Linux:win32anddarwinkeep the synchronous hide.src/components/launch/CountdownOverlay.test.tsx(jsdom):flushSyncis replaced with a batchedsetValue);CLEARED_AFTER_FRAMESframes, with the rightrunId;flushSync, the component test fails.Commands:
npx vitest --run electron/countdownOverlay.test.ts src/components/launch/CountdownOverlay.test.tsx src/hooks/useScreenRecorder electron/windows.test.ts electron/lingeringOverlay.test.ts: 47/47 pass.npx vitest --run --changed origin/main: pass.npx tsc --noEmitandnpx tsc -p tsconfig.test.json --noEmit: no errors outsideelectron/mcp/*. Those errors come from@modelcontextprotocol/sdkmissing in my localnode_modules, are unrelated to this change, and CI installs it.npm run lint: 0 errors. The warnings were all there before this change.Manual verification on GNOME Wayland (not run here; needs the real desktop)
🤖 Generated with Claude Code
Summary by CodeRabbit