fix(windows): prevent DirectComposition ghost artifacts on recording start/stop#1971
Merged
richiemcilroy merged 1 commit intoJul 2, 2026
Conversation
…position ghost artifacts
Comment on lines
+91
to
+94
| #[cfg(windows)] | ||
| return window.minimize(); | ||
| #[cfg(not(windows))] | ||
| window.hide() |
There was a problem hiding this comment.
Small thing: you can avoid the early return here so this match arm stays expression-based (makes it harder to accidentally skip future logic added after the match).
Suggested change
| #[cfg(windows)] | |
| return window.minimize(); | |
| #[cfg(not(windows))] | |
| window.hide() | |
| #[cfg(windows)] | |
| window.minimize() | |
| #[cfg(not(windows))] | |
| window.hide() |
Comment on lines
+330
to
+331
| #[cfg(windows)] | ||
| let _ = window.close(); |
There was a problem hiding this comment.
Since this is a Windows-only compositor issue, it might be worth logging close() failures so we have signal if this ever regresses (same pattern appears in handle_recording_end).
Suggested change
| #[cfg(windows)] | |
| let _ = window.close(); | |
| #[cfg(windows)] | |
| if let Err(err) = window.close() { | |
| debug!(?err, "Failed to close target-select overlay window"); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On Windows, window.hide() doesn't release the DirectComposition surface, leaving a ghost composited on screen.
Fixes the transparent overlay ghost and white blank box reported on Windows.
Greptile Summary
This PR fixes DirectComposition ghost artifacts on Windows by replacing
hide()calls withclose()for overlay windows andminimize()for the main window, preventing the DComp surface from persisting on screen during recording start/stop.general_settings.rs:Self::Closenow callsminimize()on Windows so the main window disappears from view without leaving a DComp surface; the existingunminimize()at recording end restores it correctly.recording.rs/target_select_overlay.rs:TargetSelectOverlaywindows are now fully destroyed (close()) on Windows instead of just hidden, releasing the transparent DComp surface that was compositing as a ghost overlay.Confidence Score: 4/5
The fix is safe to merge and correctly addresses the reported ghost artifact on Windows recording start/stop.
The three changed sites correctly use close()/minimize() on Windows, and the camera-cleanup side-effect triggered by the new Destroyed event path is guarded against harm by existing checks. One overlay hide_overlay() call in the display-disconnect path of target_select_overlay.rs is left unfixed, so the DComp artifact can still appear in that less common scenario.
apps/desktop/src-tauri/src/target_select_overlay.rs — the hide_overlay() call around line 101 (stale-display cleanup inside open_target_select_overlay_windows) was not updated with the Windows fix.
Important Files Changed
Comments Outside Diff (2)
apps/desktop/src-tauri/src/target_select_overlay.rs, line 93-103 (link)This
hide_overlay()call (reached when an overlay's display disappears while the overlay is open) skips the Windowsclose()fix applied in the other three sites. On Windows, hiding instead of closing leaves the ghost DComp surface on screen for the stale display's overlay. The fix should mirror the pattern used inclose_target_select_overlay_windowsbelow: callwindow.close()on Windows andhide_overlay()elsewhere.Prompt To Fix With AI
apps/desktop/src-tauri/src/recording.rs, line 3020-3026 (link)close()triggers theDestroyedevent handler, causing a redundantfm.destroy()and an unintendedcleanup_camera_after_overlay_closespawnThe
Destroyedevent handler inlib.rs(~line 5373) fires whenwindow.close()is called on Windows, which both callsfocus_manager.destroy()again (redundant but idempotent) and spawnscleanup_camera_after_overlay_close. That function wasn't designed to run during recording-end cleanup: it checksis_recording_active_or_pending()(false at this point) and then proceeds unless a camera window is still present. The camera window exists-but-hidden at this point (line 3031 hides it), soCapWindowId::Camera.get(&app).is_some()returnstrueand prevents actual harm — but this is a fragile guard that may not hold if the camera-window lifecycle changes later. The explicitfm.destroy()call afterwindow.close()is also now redundant: the Destroyed event handler already handles it.Prompt To Fix With AI
Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "fix(windows): use close/minimize instead..." | Re-trigger Greptile