From f5bb20620d6ab626153657ab6e1c1290d82546d7 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Sat, 11 Apr 2026 11:49:09 +0800 Subject: [PATCH 1/8] {"schema":"maestro/commit/1","summary":"allow rsnap windows to be externally capturable","authority":"XY-240"} --- packages/rsnap-overlay/src/overlay.rs | 67 +++++------- .../src/overlay/aux_window_runtime.rs | 2 + .../src/overlay/capture_window_runtime.rs | 101 +++++++++++++++++- .../src/overlay/tests/self_capture_runtime.rs | 10 +- .../src/overlay/window_runtime.rs | 58 ++++++++-- .../src/overlay/worker_runtime.rs | 48 ++++++++- 6 files changed, 227 insertions(+), 59 deletions(-) diff --git a/packages/rsnap-overlay/src/overlay.rs b/packages/rsnap-overlay/src/overlay.rs index b0ae6093..4a95d4be 100644 --- a/packages/rsnap-overlay/src/overlay.rs +++ b/packages/rsnap-overlay/src/overlay.rs @@ -1490,20 +1490,7 @@ impl OverlaySession { } #[cfg(target_os = "macos")] - fn try_latest_live_freeze_preview(&mut self, monitor: MonitorRect) -> Option { - if self.state.live_bg_monitor == Some(monitor) - && let Some(image) = self.state.live_bg_image.take() - { - return Some(image); - } - - self.live_sample_stream - .as_ref() - .and_then(|stream| stream.peek_latest_rgba_snapshot(monitor)) - .map(|snapshot| snapshot.image.as_ref().clone()) - } - - #[cfg(target_os = "macos")] + #[allow(dead_code)] fn commit_frozen_preview( &mut self, monitor: MonitorRect, @@ -1678,17 +1665,14 @@ impl OverlaySession { #[cfg(target_os = "macos")] { - if let Some(image) = self.try_latest_live_freeze_preview(monitor) { - self.state.live_bg_monitor = None; - self.state.live_bg_image = None; + let _ = cursor; - self.commit_frozen_preview(monitor, image, cursor); - self.force_apply_pending_toolbar_window_move(); - } else { - self.state.live_bg_monitor = None; - self.state.live_bg_image = None; - self.capture_windows_hidden = true; - } + self.state.live_bg_monitor = None; + self.state.live_bg_image = None; + self.capture_windows_hidden = true; + self.pending_freeze_capture_armed = true; + + self.hide_capture_windows(); } #[cfg(not(target_os = "macos"))] { @@ -4728,7 +4712,11 @@ impl OverlaySession { } self.state.finish_freeze(monitor, frozen_preview_image); + #[cfg(target_os = "macos")] + self.destroy_live_only_aux_windows(); self.restore_capture_windows_visibility(); + #[cfg(target_os = "macos")] + self.request_aux_window_creation_if_needed(); self.toolbar_state.needs_redraw = true; @@ -5242,6 +5230,13 @@ impl OverlaySession { return; }; + #[cfg(target_os = "macos")] + if self.loupe_window.is_none() { + self.request_aux_window_creation_if_needed(); + + return; + } + self.maybe_apply_pending_startup_aux_live_stream_filter_upgrade(monitor); let visible = self.update_loupe_window_position(monitor); @@ -6747,6 +6742,11 @@ impl OverlaySession { { self.toolbar_state.visible = !self.toolbar_state.visible; + #[cfg(target_os = "macos")] + if self.toolbar_state.visible { + self.request_aux_window_creation_if_needed(); + } + self.request_redraw_all(); OverlayControl::Continue @@ -7265,6 +7265,7 @@ impl OverlaySession { "Entered scroll-capture mode." ); + self.request_aux_window_creation_if_needed(); self.sync_frozen_toolbar_state(); self.refresh_scroll_preview_committed_image(); self.refresh_scroll_preview_display_image(); @@ -7993,19 +7994,9 @@ impl OverlaySession { .map_or(FreezeCaptureTarget::Monitor, |target| FreezeCaptureTarget::Window { window_id: target.window_id, }); - #[cfg(target_os = "macos")] - { - if worker.request_freeze_capture(overlay_monitor, freeze_target) { - self.pending_freeze_capture = None; - self.pending_freeze_capture_armed = false; - self.inflight_freeze_capture = Some(overlay_monitor); - self.inflight_window_freeze_capture = pending_window_target; - self.pending_window_freeze_capture = None; - } else { - self.request_redraw_for_monitor(overlay_monitor); - } - } + let _ = (&worker, &freeze_target, &pending_window_target, &overlay_monitor); + #[cfg(not(target_os = "macos"))] { // Capture must happen on a post-hide redraw so the HUD/loupe are not included. @@ -9071,8 +9062,6 @@ fn macos_configure_overlay_window_mouse_moved_events(window: &Window) { let _: () = objc::msg_send![ns_window, setOpaque: false]; let _: () = objc::msg_send![ns_window, setHasShadow: false]; - let sharing_type_none = 0_u64; - let _: () = objc::msg_send![ns_window, setSharingType: sharing_type_none]; let clear: *mut Object = objc::msg_send![objc::class!(NSColor), clearColor]; let _: () = objc::msg_send![ns_window, setBackgroundColor: clear]; let _: () = objc::msg_send![ns_window, setLevel: MACOS_OVERLAY_WINDOW_LEVEL]; @@ -9134,8 +9123,6 @@ fn macos_configure_hud_window( let _: () = objc::msg_send![ns_window, setHasShadow: false]; let _: () = objc::msg_send![ns_window, setAcceptsMouseMovedEvents: YES]; let _: () = objc::msg_send![ns_window, setLevel: MACOS_HUD_WINDOW_LEVEL]; - let sharing_type_none = 0_u64; - let _: () = objc::msg_send![ns_window, setSharingType: sharing_type_none]; let clear: *mut Object = objc::msg_send![objc::class!(NSColor), clearColor]; let _: () = objc::msg_send![ns_window, setBackgroundColor: clear]; let content_view: *mut Object = objc::msg_send![ns_window, contentView]; diff --git a/packages/rsnap-overlay/src/overlay/aux_window_runtime.rs b/packages/rsnap-overlay/src/overlay/aux_window_runtime.rs index 0d8c7869..28c8549f 100644 --- a/packages/rsnap-overlay/src/overlay/aux_window_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/aux_window_runtime.rs @@ -64,6 +64,8 @@ impl OverlaySession { self.maybe_log_event_loop_stall(now); self.mark_progress(OverlayEventLoopPhase::AboutToWait); self.maybe_clear_loupe_activation_after_focus_loss(); + #[cfg(target_os = "macos")] + self.maybe_dispatch_armed_freeze_capture(); self.maybe_request_keepalive_redraw(); self.maybe_keep_selection_flow_repaint(); self.maybe_keep_frozen_text_caret_repaint(); diff --git a/packages/rsnap-overlay/src/overlay/capture_window_runtime.rs b/packages/rsnap-overlay/src/overlay/capture_window_runtime.rs index f180c292..24aef8a2 100644 --- a/packages/rsnap-overlay/src/overlay/capture_window_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/capture_window_runtime.rs @@ -1,5 +1,5 @@ #[allow(unused_imports)] -use crate::overlay::{GlobalPoint, MonitorRect, OverlayMode, OverlaySession}; +use crate::overlay::{GlobalPoint, Instant, MonitorRect, OverlayMode, OverlaySession}; impl OverlaySession { pub(super) fn update_cursor_state(&mut self, monitor: MonitorRect, cursor: GlobalPoint) { @@ -7,6 +7,42 @@ impl OverlaySession { self.state.cursor = Some(cursor); } + #[cfg(target_os = "macos")] + pub(super) fn hide_capture_windows(&mut self) { + self.capture_windows_hidden = true; + + for overlay_window in self.windows.values() { + overlay_window.window.set_visible(false); + } + + if let Some(hud_window) = &self.hud_window { + hud_window.window.set_visible(false); + } + + self.hud_window_visible = false; + + if let Some(loupe_window) = &self.loupe_window { + loupe_window.window.set_visible(false); + } + + self.loupe_window_visible = false; + + self.reset_loupe_window_warmup_redraws(); + + if let Some(toolbar_window) = &self.toolbar_window { + toolbar_window.window.set_visible(false); + } + + self.toolbar_window_visible = false; + self.toolbar_window_warmup_redraws_remaining = 0; + + if let Some(preview_window) = &self.scroll_preview_window { + preview_window.window.set_visible(false); + } + + self.last_present_at = Instant::now(); + } + #[cfg(not(target_os = "macos"))] pub(super) fn hide_capture_windows(&mut self) { self.capture_windows_hidden = true; @@ -28,6 +64,58 @@ impl OverlaySession { } self.capture_windows_hidden = false; + #[cfg(target_os = "macos")] + { + for overlay_window in self.windows.values() { + overlay_window.window.set_visible(true); + overlay_window.window.request_redraw(); + } + + if matches!(self.state.mode, OverlayMode::Live) { + if let Some(hud_window) = &self.hud_window { + hud_window.window.set_visible(true); + hud_window.window.request_redraw(); + } + + self.hud_window_visible = self.hud_window.is_some(); + + if let Some(loupe_window) = &self.loupe_window { + loupe_window.window.set_visible(self.state.alt_held); + loupe_window.window.request_redraw(); + } + + self.loupe_window_visible = self.state.alt_held && self.loupe_window.is_some(); + + return; + } + + self.hud_window_visible = false; + self.loupe_window_visible = false; + + if let Some(toolbar_window) = &self.toolbar_window { + let show_toolbar = matches!(self.state.mode, OverlayMode::Frozen) + && self.toolbar_state.visible + && self.authoritative_frozen_capture_ready + && self.state.frozen_image.is_some(); + + toolbar_window.window.set_visible(show_toolbar); + + if show_toolbar { + toolbar_window.window.request_redraw(); + } + + self.toolbar_window_visible = show_toolbar; + } else { + self.toolbar_window_visible = false; + } + if let Some(preview_window) = &self.scroll_preview_window { + preview_window.window.set_visible(self.scroll_capture.active); + + if self.scroll_capture.active { + preview_window.window.request_redraw(); + } + } + } #[cfg(not(target_os = "macos"))] { if matches!(self.state.mode, OverlayMode::Live) { @@ -59,4 +147,15 @@ impl OverlaySession { loupe_window.window.focus_window(); } } + + #[cfg(target_os = "macos")] + pub(super) fn destroy_live_only_aux_windows(&mut self) { + self.loupe_window = None; + self.loupe_inner_size_points = None; + self.loupe_outer_pos = None; + self.pending_loupe_outer_pos = None; + self.loupe_window_visible = false; + + self.reset_loupe_window_warmup_redraws(); + } } diff --git a/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs b/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs index e12198e7..cc8fe543 100644 --- a/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs @@ -133,7 +133,7 @@ fn complete_startup_aux_window_creation_defers_live_stream_upgrade_until_aux_win #[cfg(target_os = "macos")] #[test] -fn showing_loupe_window_applies_pending_startup_live_stream_upgrade() { +fn showing_loupe_window_requests_lazy_creation_before_applying_stream_upgrade() { let monitor = tests::test_monitor(); let (mut session, original_worker_debug_id) = tests::configured_session_with_macos_worker(); let original_live_sample_stream = ptr::from_ref(session.live_sample_stream.as_ref().unwrap()); @@ -144,7 +144,8 @@ fn showing_loupe_window_applies_pending_startup_live_stream_upgrade() { session.set_alt_loupe_window_visible(Some(monitor), true); - assert!(!session.pending_startup_aux_live_stream_filter_upgrade); + assert!(session.pending_startup_aux_live_stream_filter_upgrade); + assert!(session.startup_aux_window_creation_pending); assert_eq!( ptr::from_ref(session.live_sample_stream.as_ref().unwrap()), original_live_sample_stream @@ -153,10 +154,7 @@ fn showing_loupe_window_applies_pending_startup_live_stream_upgrade() { ptr::from_ref(session.scroll_capture.live_stream.as_ref().unwrap()), original_scroll_live_stream ); - assert_eq!( - session.live_sample_stream.as_ref().unwrap().debug_last_request_kind(), - Some("upgrade_monitor_nonblocking") - ); + assert_eq!(session.live_sample_stream.as_ref().unwrap().debug_last_request_kind(), None); assert_eq!(session.worker.as_ref().unwrap().debug_id(), original_worker_debug_id); } diff --git a/packages/rsnap-overlay/src/overlay/window_runtime.rs b/packages/rsnap-overlay/src/overlay/window_runtime.rs index 600949cc..eac91f41 100644 --- a/packages/rsnap-overlay/src/overlay/window_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/window_runtime.rs @@ -210,7 +210,7 @@ impl OverlaySession { #[cfg(target_os = "macos")] { - self.startup_aux_window_creation_pending = true; + self.startup_aux_window_creation_pending = false; self.startup_aux_window_creation_scheduled = false; Ok(StartupWindowCreationMetrics { @@ -286,7 +286,7 @@ impl OverlaySession { &mut self, event_loop: &ActiveEventLoop, ) -> Result<(), String> { - if !self.startup_aux_window_creation_pending { + if !self.startup_aux_window_creation_pending && !self.aux_window_creation_needed() { return Ok(()); } @@ -294,17 +294,17 @@ impl OverlaySession { let mut created_aux_windows = false; - if self.loupe_window.is_none() { + if self.loupe_window.is_none() && self.loupe_window_needed() { self.create_loupe_window(event_loop)?; created_aux_windows = true; } - if self.toolbar_window.is_none() { + if self.toolbar_window.is_none() && self.toolbar_window_needed() { self.create_toolbar_window(event_loop)?; created_aux_windows = true; } - if self.scroll_preview_window.is_none() { + if self.scroll_preview_window.is_none() && self.scroll_preview_window_needed() { self.create_scroll_preview_window(event_loop)?; created_aux_windows = true; @@ -312,19 +312,61 @@ impl OverlaySession { self.complete_startup_aux_window_creation(created_aux_windows); - if self.state.alt_held && matches!(self.state.mode, OverlayMode::Live) { + if self.loupe_window_needed() { self.set_alt_loupe_window_visible(self.active_cursor_monitor(), true); } - if self.toolbar_state.visible { + if self.toolbar_window_needed() { self.request_redraw_toolbar_window(); } - if self.scroll_capture.active { + if self.scroll_preview_window_needed() { + if let Some(monitor) = self.scroll_capture.monitor { + self.position_scroll_preview_window(monitor); + } + self.request_redraw_scroll_preview_window(); } Ok(()) } + #[cfg(target_os = "macos")] + fn loupe_window_needed(&self) -> bool { + matches!(self.state.mode, OverlayMode::Live) + && self.state.alt_held + && !self.live_loupe_uses_hud_window() + } + + #[cfg(target_os = "macos")] + fn toolbar_window_needed(&self) -> bool { + matches!(self.state.mode, OverlayMode::Frozen) + && self.toolbar_state.visible + && self.authoritative_frozen_capture_ready + && self.state.frozen_image.is_some() + } + + #[cfg(target_os = "macos")] + fn scroll_preview_window_needed(&self) -> bool { + self.scroll_capture.active + } + + #[cfg(target_os = "macos")] + fn aux_window_creation_needed(&self) -> bool { + (self.loupe_window.is_none() && self.loupe_window_needed()) + || (self.toolbar_window.is_none() && self.toolbar_window_needed()) + || (self.scroll_preview_window.is_none() && self.scroll_preview_window_needed()) + } + + #[cfg(target_os = "macos")] + pub(super) fn request_aux_window_creation_if_needed(&mut self) { + if !self.aux_window_creation_needed() { + return; + } + + self.startup_aux_window_creation_pending = true; + + self.maybe_schedule_startup_aux_window_creation(); + } + #[cfg(target_os = "macos")] pub(super) fn complete_startup_aux_window_creation(&mut self, created_aux_windows: bool) { self.startup_aux_window_creation_pending = false; diff --git a/packages/rsnap-overlay/src/overlay/worker_runtime.rs b/packages/rsnap-overlay/src/overlay/worker_runtime.rs index 84cbe660..c1914727 100644 --- a/packages/rsnap-overlay/src/overlay/worker_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/worker_runtime.rs @@ -3,13 +3,53 @@ use crate::overlay::CursorSampleRequest; #[allow(unused_imports)] use crate::overlay::{ - Arc, CURSOR_POLL_INTERVAL_MIN, CapturedMonitorRegionResult, Duration, GlobalPoint, Instant, - LiveCursorSample, LiveSampleApplyResult, MonitorRect, MonitorRectPoints, OverlayControl, - OverlayMode, OverlaySession, WindowFreezeCaptureTarget, WindowHit, WindowListSnapshot, - WorkerErrorSource, WorkerRequestSendError, WorkerResponse, mem, + Arc, CURSOR_POLL_INTERVAL_MIN, CapturedMonitorRegionResult, Duration, FreezeCaptureTarget, + GlobalPoint, Instant, LiveCursorSample, LiveSampleApplyResult, MonitorRect, MonitorRectPoints, + OverlayControl, OverlayMode, OverlaySession, WindowFreezeCaptureTarget, WindowHit, + WindowListSnapshot, WorkerErrorSource, WorkerRequestSendError, WorkerResponse, mem, }; impl OverlaySession { + #[cfg(target_os = "macos")] + pub(super) fn maybe_dispatch_armed_freeze_capture(&mut self) { + if !self.pending_freeze_capture_armed { + return; + } + + let Some(overlay_monitor) = self.pending_freeze_capture else { + self.pending_freeze_capture_armed = false; + + return; + }; + + if !self.pending_freeze_capture_matches(overlay_monitor) { + self.pending_freeze_capture_armed = false; + + return; + } + + let Some(worker) = &self.worker else { + return; + }; + let pending_window_target = + self.pending_window_freeze_capture.filter(|target| target.monitor == overlay_monitor); + let freeze_target = pending_window_target.map_or(FreezeCaptureTarget::Monitor, |target| { + FreezeCaptureTarget::Window { window_id: target.window_id } + }); + + if worker.request_freeze_capture(overlay_monitor, freeze_target) { + self.pending_freeze_capture = None; + self.pending_freeze_capture_armed = false; + self.inflight_freeze_capture = Some(overlay_monitor); + self.inflight_window_freeze_capture = pending_window_target; + self.pending_window_freeze_capture = None; + } else { + self.schedule_egui_repaint_after( + self.repaint_interval_for_monitor(Some(overlay_monitor)), + ); + } + } + pub(super) fn drain_worker_responses(&mut self) -> OverlayControl { #[cfg(target_os = "macos")] if self.worker.is_none() && self.live_sample_worker.is_none() { From 9f6aecb0d7ec2306e3986f2699cb3949a230080f Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Sat, 11 Apr 2026 12:11:19 +0800 Subject: [PATCH 2/8] {"schema":"maestro/commit/1","summary":"refresh startup live stream after capture windows appear","authority":"XY-240"} --- .../src/overlay/tests/self_capture_runtime.rs | 27 +++++++++++++++++++ .../src/overlay/window_runtime.rs | 20 ++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs b/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs index cc8fe543..1c7f0f23 100644 --- a/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs @@ -131,6 +131,33 @@ fn complete_startup_aux_window_creation_defers_live_stream_upgrade_until_aux_win assert!(session.window_list_snapshot.is_some()); } +#[cfg(target_os = "macos")] +#[test] +fn refresh_startup_live_stream_after_window_creation_rebuilds_and_reprimes_stream() { + let monitor = tests::test_monitor(); + let (mut session, _original_worker_debug_id) = tests::configured_session_with_macos_worker(); + + assert!( + session + .live_sample_stream + .as_ref() + .unwrap() + .debug_self_capture_exception_window_ids() + .is_empty() + ); + + session.refresh_startup_live_stream_after_window_creation(Some(monitor)); + + assert_eq!( + session.live_sample_stream.as_ref().unwrap().debug_self_capture_exception_window_ids(), + &[17] + ); + assert_eq!( + session.live_sample_stream.as_ref().unwrap().debug_last_request_kind(), + Some("prime_monitor_nonblocking") + ); +} + #[cfg(target_os = "macos")] #[test] fn showing_loupe_window_requests_lazy_creation_before_applying_stream_upgrade() { diff --git a/packages/rsnap-overlay/src/overlay/window_runtime.rs b/packages/rsnap-overlay/src/overlay/window_runtime.rs index eac91f41..dd33a0b1 100644 --- a/packages/rsnap-overlay/src/overlay/window_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/window_runtime.rs @@ -58,7 +58,15 @@ impl OverlaySession { } let gpu_init_ms = gpu_init_started_at.elapsed().as_millis(); + #[cfg(target_os = "macos")] + let reused_prewarmed_windows = self.has_matching_prewarmed_startup_resources(&monitors); let window_creation = self.create_startup_windows(event_loop, &monitors)?; + + #[cfg(target_os = "macos")] + if !reused_prewarmed_windows { + self.refresh_startup_live_stream_after_window_creation(startup_monitor); + } + let prime_cursor_started_at = Instant::now(); self.prime_startup_cursor_context(startup_cursor, startup_monitor); @@ -280,6 +288,18 @@ impl OverlaySession { } } + #[cfg(target_os = "macos")] + pub(super) fn refresh_startup_live_stream_after_window_creation( + &mut self, + startup_monitor: Option, + ) { + self.live_sample_stream = Some(MacLiveFrameStream::with_self_capture_exception_window_ids( + self.config.self_capture_exception_window_ids.clone(), + )); + + self.prime_startup_live_stream_nonblocking(startup_monitor); + } + #[cfg(target_os = "macos")] /// Completes creation of non-critical auxiliary windows after the first overlay frame. pub fn finish_startup_aux_window_creation( From 57944ee02aa23e5533f81ee3cbe80b5b09faede7 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Sat, 11 Apr 2026 13:11:10 +0800 Subject: [PATCH 3/8] {"schema":"maestro/commit/1","summary":"recover freeze UI when dispatch stalls","authority":"XY-240"} --- packages/rsnap-overlay/src/overlay.rs | 34 ++++--- .../src/overlay/cursor_context_runtime.rs | 2 +- packages/rsnap-overlay/src/overlay/tests.rs | 2 +- .../src/overlay/tests/self_capture_runtime.rs | 57 +++++++++++ .../src/overlay/worker_runtime.rs | 95 +++++++++++++++---- packages/rsnap-overlay/src/worker.rs | 6 +- 6 files changed, 164 insertions(+), 32 deletions(-) diff --git a/packages/rsnap-overlay/src/overlay.rs b/packages/rsnap-overlay/src/overlay.rs index 4a95d4be..69bf6610 100644 --- a/packages/rsnap-overlay/src/overlay.rs +++ b/packages/rsnap-overlay/src/overlay.rs @@ -865,6 +865,7 @@ pub struct OverlaySession { window_list_refresh_interval: Duration, last_live_bg_request_at: Instant, live_bg_request_interval: Duration, + freeze_capture_send_full_count: u64, hit_test_send_full_count: u64, hit_test_send_disconnected_count: u64, hit_test_request_id: u64, @@ -1088,6 +1089,7 @@ impl OverlaySession { window_list_refresh_interval: Duration::ZERO, last_live_bg_request_at: Instant::now(), live_bg_request_interval: Duration::ZERO, + freeze_capture_send_full_count: 0, hit_test_send_full_count: 0, hit_test_send_disconnected_count: 0, hit_test_request_id: 0, @@ -1652,6 +1654,7 @@ impl OverlaySession { self.pending_freeze_capture_armed = false; self.inflight_freeze_capture = None; self.authoritative_frozen_capture_ready = false; + self.freeze_capture_send_full_count = 0; self.pending_window_freeze_capture = window_target; self.inflight_window_freeze_capture = None; self.frozen_window_image = None; @@ -7984,9 +7987,7 @@ impl OverlaySession { overlay_monitor: MonitorRect, draw_toolbar: bool, ) -> OverlayControl { - if self.should_dispatch_pending_freeze_capture(overlay_monitor) - && let Some(worker) = &self.worker - { + if self.should_dispatch_pending_freeze_capture(overlay_monitor) { let pending_window_target = self .pending_window_freeze_capture .filter(|target| target.monitor == overlay_monitor); @@ -7995,22 +7996,31 @@ impl OverlaySession { window_id: target.window_id, }); #[cfg(target_os = "macos")] - let _ = (&worker, &freeze_target, &pending_window_target, &overlay_monitor); + let _ = (&freeze_target, &pending_window_target, &overlay_monitor); #[cfg(not(target_os = "macos"))] { // Capture must happen on a post-hide redraw so the HUD/loupe are not included. if self.pending_freeze_capture_armed { - if worker.request_freeze_capture(overlay_monitor, freeze_target) { - self.pending_freeze_capture = None; - self.pending_freeze_capture_armed = false; - self.inflight_freeze_capture = Some(overlay_monitor); - self.inflight_window_freeze_capture = pending_window_target; - self.pending_window_freeze_capture = None; - } else { - self.request_redraw_for_monitor(overlay_monitor); + let Some(worker) = &self.worker else { + self.abort_pending_freeze_capture("Capture worker is unavailable."); + + return OverlayControl::Continue; + }; + + match worker.request_freeze_capture(overlay_monitor, freeze_target) { + Ok(()) => { + self.note_freeze_capture_request_started( + overlay_monitor, + pending_window_target, + ); + }, + Err(err) => { + self.handle_freeze_capture_request_send_error(overlay_monitor, err); + }, } } else { + self.freeze_capture_send_full_count = 0; self.pending_freeze_capture_armed = true; #[cfg(not(target_os = "macos"))] diff --git a/packages/rsnap-overlay/src/overlay/cursor_context_runtime.rs b/packages/rsnap-overlay/src/overlay/cursor_context_runtime.rs index 3570696c..f06d89f8 100644 --- a/packages/rsnap-overlay/src/overlay/cursor_context_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/cursor_context_runtime.rs @@ -168,7 +168,7 @@ impl OverlaySession { return; }; - if worker.request_freeze_capture(monitor, FreezeCaptureTarget::Monitor) { + if worker.request_freeze_capture(monitor, FreezeCaptureTarget::Monitor).is_ok() { self.last_live_bg_request_at = Instant::now(); } } diff --git a/packages/rsnap-overlay/src/overlay/tests.rs b/packages/rsnap-overlay/src/overlay/tests.rs index f7887dfb..dc5da3b6 100644 --- a/packages/rsnap-overlay/src/overlay/tests.rs +++ b/packages/rsnap-overlay/src/overlay/tests.rs @@ -86,7 +86,7 @@ use crate::state::{WindowListSnapshot, WindowRect}; #[cfg(target_os = "macos")] use crate::worker::OverlayWorker; #[cfg(target_os = "macos")] -use crate::worker::{WorkerErrorSource, WorkerResponse}; +use crate::worker::{WorkerErrorSource, WorkerRequestSendError, WorkerResponse}; #[cfg(target_os = "macos")] struct SequenceScrollCaptureBackend { diff --git a/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs b/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs index 1c7f0f23..2da22d81 100644 --- a/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs @@ -13,6 +13,11 @@ use crate::overlay::tests::{ GlobalPoint, Instant, OverlaySession, ScrollDirection, WorkerErrorSource, WorkerResponse, overlay, }; +#[cfg(target_os = "macos")] +use crate::overlay::worker_runtime::FREEZE_CAPTURE_SEND_FULL_RETRY_LIMIT; +#[cfg(target_os = "macos")] +#[allow(unused_imports)] +use crate::overlay::tests::WorkerRequestSendError; #[cfg(target_os = "macos")] #[test] @@ -158,6 +163,58 @@ fn refresh_startup_live_stream_after_window_creation_rebuilds_and_reprimes_strea ); } +#[cfg(target_os = "macos")] +#[test] +fn armed_freeze_capture_without_worker_restores_visibility_and_surfaces_error() { + let monitor = tests::test_monitor(); + let mut session = OverlaySession::new(); + + session.state.begin_freeze(monitor); + + session.pending_freeze_capture = Some(monitor); + session.pending_freeze_capture_armed = true; + session.capture_windows_hidden = true; + + session.maybe_dispatch_armed_freeze_capture(); + + assert!(session.pending_freeze_capture.is_none()); + assert!(session.inflight_freeze_capture.is_none()); + assert!(!session.pending_freeze_capture_armed); + assert!(!session.capture_windows_hidden); + assert_eq!(session.state.error_message.as_deref(), Some("Capture worker is unavailable.")); +} + +#[cfg(target_os = "macos")] +#[test] +fn repeated_freeze_capture_send_full_aborts_and_restores_hidden_windows() { + let monitor = tests::test_monitor(); + let mut session = OverlaySession::new(); + + session.state.begin_freeze(monitor); + + session.pending_freeze_capture = Some(monitor); + session.pending_freeze_capture_armed = true; + session.capture_windows_hidden = true; + + for _ in 0..FREEZE_CAPTURE_SEND_FULL_RETRY_LIMIT.saturating_sub(1) { + session.handle_freeze_capture_request_send_error(monitor, WorkerRequestSendError::Full); + + assert_eq!(session.pending_freeze_capture, Some(monitor)); + assert!(session.pending_freeze_capture_armed); + assert!(session.capture_windows_hidden); + assert!(session.state.error_message.is_none()); + } + + session.handle_freeze_capture_request_send_error(monitor, WorkerRequestSendError::Full); + + assert!(session.pending_freeze_capture.is_none()); + assert!(session.inflight_freeze_capture.is_none()); + assert!(!session.pending_freeze_capture_armed); + assert!(!session.capture_windows_hidden); + assert_eq!(session.freeze_capture_send_full_count, 0); + assert_eq!(session.state.error_message.as_deref(), Some("Capture worker is busy. Please try again.")); +} + #[cfg(target_os = "macos")] #[test] fn showing_loupe_window_requests_lazy_creation_before_applying_stream_upgrade() { diff --git a/packages/rsnap-overlay/src/overlay/worker_runtime.rs b/packages/rsnap-overlay/src/overlay/worker_runtime.rs index c1914727..19e60716 100644 --- a/packages/rsnap-overlay/src/overlay/worker_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/worker_runtime.rs @@ -9,7 +9,75 @@ use crate::overlay::{ WindowListSnapshot, WorkerErrorSource, WorkerRequestSendError, WorkerResponse, mem, }; +pub(super) const FREEZE_CAPTURE_SEND_FULL_RETRY_LIMIT: u64 = 8; + impl OverlaySession { + fn clear_freeze_capture_tracking(&mut self) { + self.pending_freeze_capture = None; + self.inflight_freeze_capture = None; + self.pending_freeze_capture_armed = false; + self.pending_window_freeze_capture = None; + self.inflight_window_freeze_capture = None; + self.freeze_capture_send_full_count = 0; + } + + pub(super) fn abort_pending_freeze_capture(&mut self, message: impl Into) { + let message = message.into(); + + self.clear_freeze_capture_tracking(); + self.restore_capture_windows_visibility(); + self.state.set_error(message); + self.request_redraw_all(); + } + + fn note_freeze_capture_request_started( + &mut self, + overlay_monitor: MonitorRect, + pending_window_target: Option, + ) { + self.pending_freeze_capture = None; + self.pending_freeze_capture_armed = false; + self.inflight_freeze_capture = Some(overlay_monitor); + self.inflight_window_freeze_capture = pending_window_target; + self.pending_window_freeze_capture = None; + self.freeze_capture_send_full_count = 0; + } + + pub(super) fn handle_freeze_capture_request_send_error( + &mut self, + overlay_monitor: MonitorRect, + err: WorkerRequestSendError, + ) { + match err { + WorkerRequestSendError::Full => { + self.freeze_capture_send_full_count = + self.freeze_capture_send_full_count.saturating_add(1); + + tracing::debug!( + monitor_id = overlay_monitor.id, + full_count = self.freeze_capture_send_full_count, + "Freeze capture request dropped: worker queue full." + ); + + if self.freeze_capture_send_full_count >= FREEZE_CAPTURE_SEND_FULL_RETRY_LIMIT { + self.abort_pending_freeze_capture("Capture worker is busy. Please try again."); + } else { + self.schedule_egui_repaint_after( + self.repaint_interval_for_monitor(Some(overlay_monitor)), + ); + } + }, + WorkerRequestSendError::Disconnected => { + tracing::warn!( + monitor_id = overlay_monitor.id, + "Freeze capture request failed: worker disconnected before capture could start." + ); + + self.abort_pending_freeze_capture("Capture worker is unavailable."); + }, + } + } + #[cfg(target_os = "macos")] pub(super) fn maybe_dispatch_armed_freeze_capture(&mut self) { if !self.pending_freeze_capture_armed { @@ -18,17 +86,21 @@ impl OverlaySession { let Some(overlay_monitor) = self.pending_freeze_capture else { self.pending_freeze_capture_armed = false; + self.freeze_capture_send_full_count = 0; return; }; if !self.pending_freeze_capture_matches(overlay_monitor) { self.pending_freeze_capture_armed = false; + self.freeze_capture_send_full_count = 0; return; } let Some(worker) = &self.worker else { + self.abort_pending_freeze_capture("Capture worker is unavailable."); + return; }; let pending_window_target = @@ -37,16 +109,11 @@ impl OverlaySession { FreezeCaptureTarget::Window { window_id: target.window_id } }); - if worker.request_freeze_capture(overlay_monitor, freeze_target) { - self.pending_freeze_capture = None; - self.pending_freeze_capture_armed = false; - self.inflight_freeze_capture = Some(overlay_monitor); - self.inflight_window_freeze_capture = pending_window_target; - self.pending_window_freeze_capture = None; - } else { - self.schedule_egui_repaint_after( - self.repaint_interval_for_monitor(Some(overlay_monitor)), - ); + match worker.request_freeze_capture(overlay_monitor, freeze_target) { + Ok(()) => { + self.note_freeze_capture_request_started(overlay_monitor, pending_window_target); + }, + Err(err) => self.handle_freeze_capture_request_send_error(overlay_monitor, err), } } @@ -557,13 +624,9 @@ impl OverlaySession { WorkerResponse::Error { source, message } => { match source { WorkerErrorSource::FreezeCapture => { - self.pending_freeze_capture = None; - self.inflight_freeze_capture = None; - self.pending_freeze_capture_armed = false; - self.pending_window_freeze_capture = None; - self.inflight_window_freeze_capture = None; + self.abort_pending_freeze_capture(message); - self.restore_capture_windows_visibility(); + return OverlayControl::Continue; }, WorkerErrorSource::RefreshWindowList => { #[cfg(target_os = "macos")] diff --git a/packages/rsnap-overlay/src/worker.rs b/packages/rsnap-overlay/src/worker.rs index 5274dc89..fd8e2d4b 100644 --- a/packages/rsnap-overlay/src/worker.rs +++ b/packages/rsnap-overlay/src/worker.rs @@ -400,8 +400,10 @@ impl OverlayWorker { &self, monitor: MonitorRect, target: FreezeCaptureTarget, - ) -> bool { - self.req_tx.try_send(WorkerRequest::FreezeCapture { monitor, target }).is_ok() + ) -> Result<(), WorkerRequestSendError> { + let request = WorkerRequest::FreezeCapture { monitor, target }; + + self.req_tx.try_send(request).map_err(Self::map_try_send_error) } pub(crate) fn request_hit_test_window( From ba58f5ea4fa1a28f932e7ebed5f28d736cb25dc5 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Sat, 11 Apr 2026 15:49:03 +0800 Subject: [PATCH 4/8] {"schema":"maestro/commit/1","summary":"preserve deferred worker refresh on freeze errors","authority":"XY-240"} --- .../src/overlay/tests/self_capture_runtime.rs | 20 +++++++++++++++++++ .../src/overlay/worker_runtime.rs | 18 ++++++++++------- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs b/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs index 2da22d81..5de72f99 100644 --- a/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs @@ -355,6 +355,26 @@ fn captured_freeze_response_applies_deferred_worker_refresh() { assert!(!session.pending_self_capture_exception_window_ids_worker_refresh); } +#[cfg(target_os = "macos")] +#[test] +fn freeze_error_response_applies_deferred_worker_refresh() { + let monitor = tests::test_monitor(); + let (mut session, original_worker_debug_id) = tests::configured_session_with_macos_worker(); + + session.inflight_freeze_capture = Some(monitor); + session.pending_self_capture_exception_window_ids_worker_refresh = true; + + let control = session.maybe_tick_worker_response_limiter(WorkerResponse::Error { + source: WorkerErrorSource::FreezeCapture, + message: String::from("freeze failed"), + }); + + assert!(matches!(control, super::OverlayControl::Continue)); + assert_ne!(session.worker.as_ref().unwrap().debug_id(), original_worker_debug_id); + assert!(!session.pending_self_capture_exception_window_ids_worker_refresh); + assert_eq!(session.state.error_message.as_deref(), Some("freeze failed")); +} + #[cfg(target_os = "macos")] #[test] fn hit_test_response_applies_deferred_worker_refresh() { diff --git a/packages/rsnap-overlay/src/overlay/worker_runtime.rs b/packages/rsnap-overlay/src/overlay/worker_runtime.rs index 19e60716..312302f3 100644 --- a/packages/rsnap-overlay/src/overlay/worker_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/worker_runtime.rs @@ -622,12 +622,14 @@ impl OverlaySession { OverlayControl::Continue }, WorkerResponse::Error { source, message } => { - match source { - WorkerErrorSource::FreezeCapture => { - self.abort_pending_freeze_capture(message); + let mut error_already_handled = false; - return OverlayControl::Continue; - }, + match source { + WorkerErrorSource::FreezeCapture => { + self.abort_pending_freeze_capture(message.as_str()); + + error_already_handled = true; + }, WorkerErrorSource::RefreshWindowList => { #[cfg(target_os = "macos")] { @@ -649,8 +651,10 @@ impl OverlaySession { }, } - self.state.set_error(message); - self.request_redraw_all(); + if !error_already_handled { + self.state.set_error(message); + self.request_redraw_all(); + } OverlayControl::Continue }, From 0eb4da2f347eb501bea7b0a3bc1dad55eda19063 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Sat, 11 Apr 2026 15:54:38 +0800 Subject: [PATCH 5/8] {"schema":"maestro/commit/1","summary":"fix linux visibility for freeze dispatch helper","authority":"XY-240"} --- packages/rsnap-overlay/src/overlay/worker_runtime.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rsnap-overlay/src/overlay/worker_runtime.rs b/packages/rsnap-overlay/src/overlay/worker_runtime.rs index 312302f3..f17c4574 100644 --- a/packages/rsnap-overlay/src/overlay/worker_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/worker_runtime.rs @@ -30,7 +30,7 @@ impl OverlaySession { self.request_redraw_all(); } - fn note_freeze_capture_request_started( + pub(super) fn note_freeze_capture_request_started( &mut self, overlay_monitor: MonitorRect, pending_window_target: Option, From cae4c3f1ec958566610eea1197f23d4be1f6195d Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Sat, 11 Apr 2026 16:00:52 +0800 Subject: [PATCH 6/8] {"schema":"maestro/commit/1","summary":"format freeze error follow-up","authority":"XY-240"} --- .../src/overlay/tests/self_capture_runtime.rs | 11 +++++++---- packages/rsnap-overlay/src/overlay/worker_runtime.rs | 10 +++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs b/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs index 5de72f99..b43df9f0 100644 --- a/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs @@ -1,6 +1,9 @@ #[cfg(target_os = "macos")] use std::ptr; +#[cfg(target_os = "macos")] +#[allow(unused_imports)] +use crate::overlay::tests::WorkerRequestSendError; #[cfg(target_os = "macos")] #[allow(unused_imports)] use crate::overlay::tests::{ @@ -15,9 +18,6 @@ use crate::overlay::tests::{ }; #[cfg(target_os = "macos")] use crate::overlay::worker_runtime::FREEZE_CAPTURE_SEND_FULL_RETRY_LIMIT; -#[cfg(target_os = "macos")] -#[allow(unused_imports)] -use crate::overlay::tests::WorkerRequestSendError; #[cfg(target_os = "macos")] #[test] @@ -212,7 +212,10 @@ fn repeated_freeze_capture_send_full_aborts_and_restores_hidden_windows() { assert!(!session.pending_freeze_capture_armed); assert!(!session.capture_windows_hidden); assert_eq!(session.freeze_capture_send_full_count, 0); - assert_eq!(session.state.error_message.as_deref(), Some("Capture worker is busy. Please try again.")); + assert_eq!( + session.state.error_message.as_deref(), + Some("Capture worker is busy. Please try again.") + ); } #[cfg(target_os = "macos")] diff --git a/packages/rsnap-overlay/src/overlay/worker_runtime.rs b/packages/rsnap-overlay/src/overlay/worker_runtime.rs index f17c4574..3cc317c6 100644 --- a/packages/rsnap-overlay/src/overlay/worker_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/worker_runtime.rs @@ -624,12 +624,12 @@ impl OverlaySession { WorkerResponse::Error { source, message } => { let mut error_already_handled = false; - match source { - WorkerErrorSource::FreezeCapture => { - self.abort_pending_freeze_capture(message.as_str()); + match source { + WorkerErrorSource::FreezeCapture => { + self.abort_pending_freeze_capture(message.as_str()); - error_already_handled = true; - }, + error_already_handled = true; + }, WorkerErrorSource::RefreshWindowList => { #[cfg(target_os = "macos")] { From 504cf77348662d6419f294c59c0db89018270117 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Sat, 11 Apr 2026 16:18:48 +0800 Subject: [PATCH 7/8] {"schema":"maestro/commit/1","summary":"rebuild scroll live stream after aux window creation","authority":"XY-240"} --- .../src/overlay/config_runtime.rs | 78 ++++++++++--------- .../src/overlay/tests/self_capture_runtime.rs | 43 ++++++++++ .../src/overlay/window_runtime.rs | 7 ++ 3 files changed, 91 insertions(+), 37 deletions(-) diff --git a/packages/rsnap-overlay/src/overlay/config_runtime.rs b/packages/rsnap-overlay/src/overlay/config_runtime.rs index 037c18a6..3445d85d 100644 --- a/packages/rsnap-overlay/src/overlay/config_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/config_runtime.rs @@ -65,45 +65,49 @@ impl OverlaySession { self.config.self_capture_exception_window_ids.clone(), )); - if self.scroll_capture.active { - self.scroll_capture.live_stream = if self.should_use_scroll_capture_worker_sampling() { - None - } else { - match ( - self.scroll_capture.capture_rect_points, - self.scroll_capture.capture_rect_pixels, - ) { - (Some(capture_rect_points), Some(capture_rect_pixels)) => { - Some(MacLiveFrameStream::with_scroll_capture_region_and_waker( - self.config.self_capture_exception_window_ids.clone(), - capture_rect_points, - capture_rect_pixels, - self.scroll_frame_waker.clone(), - )) - }, - _ => { - Some(MacLiveFrameStream::with_self_capture_exception_window_ids_and_waker( - self.config.self_capture_exception_window_ids.clone(), - self.scroll_frame_waker.clone(), - )) - }, - } - }; - - self.scroll_capture.live_stream_backlog.clear(); - - self.scroll_capture.last_stream_frame_seq = 0; - self.scroll_capture.last_stream_frame_fingerprint = None; - self.scroll_capture.consecutive_identical_stream_frames = 0; - self.scroll_capture.last_consumed_stream_frame_captured_at = None; - self.scroll_capture.last_stream_event_at = None; - self.scroll_capture.last_stream_poll_at = None; - self.scroll_capture.pending_post_stall_burst_after_seq = None; - self.scroll_capture.live_stream_stale_grace = None; - self.scroll_capture.last_duplicate_stream_refresh_at = None; + self.rebuild_active_scroll_capture_live_stream(); + self.refresh_active_worker_for_self_capture_exception_window_ids_if_safe(); + } + + #[cfg(target_os = "macos")] + pub(super) fn rebuild_active_scroll_capture_live_stream(&mut self) -> bool { + if !self.scroll_capture.active { + return false; } - self.refresh_active_worker_for_self_capture_exception_window_ids_if_safe(); + self.scroll_capture.live_stream = if self.should_use_scroll_capture_worker_sampling() { + None + } else { + match (self.scroll_capture.capture_rect_points, self.scroll_capture.capture_rect_pixels) + { + (Some(capture_rect_points), Some(capture_rect_pixels)) => { + Some(MacLiveFrameStream::with_scroll_capture_region_and_waker( + self.config.self_capture_exception_window_ids.clone(), + capture_rect_points, + capture_rect_pixels, + self.scroll_frame_waker.clone(), + )) + }, + _ => Some(MacLiveFrameStream::with_self_capture_exception_window_ids_and_waker( + self.config.self_capture_exception_window_ids.clone(), + self.scroll_frame_waker.clone(), + )), + } + }; + + self.scroll_capture.live_stream_backlog.clear(); + + self.scroll_capture.last_stream_frame_seq = 0; + self.scroll_capture.last_stream_frame_fingerprint = None; + self.scroll_capture.consecutive_identical_stream_frames = 0; + self.scroll_capture.last_consumed_stream_frame_captured_at = None; + self.scroll_capture.last_stream_event_at = None; + self.scroll_capture.last_stream_poll_at = None; + self.scroll_capture.pending_post_stall_burst_after_seq = None; + self.scroll_capture.live_stream_stale_grace = None; + self.scroll_capture.last_duplicate_stream_refresh_at = None; + + self.scroll_capture.live_stream.is_some() } #[cfg(target_os = "macos")] diff --git a/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs b/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs index b43df9f0..628125f6 100644 --- a/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/tests/self_capture_runtime.rs @@ -1,6 +1,8 @@ #[cfg(target_os = "macos")] use std::ptr; +#[cfg(target_os = "macos")] +use crate::overlay::RectPoints; #[cfg(target_os = "macos")] #[allow(unused_imports)] use crate::overlay::tests::WorkerRequestSendError; @@ -163,6 +165,47 @@ fn refresh_startup_live_stream_after_window_creation_rebuilds_and_reprimes_strea ); } +#[cfg(target_os = "macos")] +#[test] +fn rebuild_active_scroll_capture_live_stream_rebuilds_and_reprimes_after_aux_window_creation() { + let monitor = tests::test_monitor(); + let (mut session, _original_worker_debug_id) = tests::configured_session_with_macos_worker(); + + session.scroll_capture.live_stream.as_ref().unwrap().prime_monitor_nonblocking(monitor); + + session.scroll_capture.monitor = Some(monitor); + session.scroll_capture.capture_rect_points = Some(RectPoints::new(1, 2, 30, 40)); + session.scroll_capture.capture_rect_pixels = Some(RectPoints::new(2, 4, 60, 80)); + + session.scroll_capture.live_stream_backlog.push_back(ScrollCaptureLiveFrame { + frame_seq: 3, + captured_at: Instant::now(), + image: tests::test_frozen_image(), + }); + + session.scroll_capture.last_stream_frame_seq = 3; + session.scroll_capture.last_stream_event_at = Some(Instant::now()); + session.scroll_capture.last_stream_poll_at = Some(Instant::now()); + + assert!(session.rebuild_active_scroll_capture_live_stream()); + + let rebuilt_scroll_live_stream = session.scroll_capture.live_stream.as_ref().unwrap(); + + assert_eq!(rebuilt_scroll_live_stream.debug_self_capture_exception_window_ids(), &[17]); + assert_eq!(rebuilt_scroll_live_stream.debug_last_request_kind(), None); + assert!(session.scroll_capture.live_stream_backlog.is_empty()); + assert_eq!(session.scroll_capture.last_stream_frame_seq, 0); + assert!(session.scroll_capture.last_stream_event_at.is_none()); + assert!(session.scroll_capture.last_stream_poll_at.is_none()); + + rebuilt_scroll_live_stream.prime_monitor_nonblocking(monitor); + + assert_eq!( + session.scroll_capture.live_stream.as_ref().unwrap().debug_last_request_kind(), + Some("prime_monitor_nonblocking") + ); +} + #[cfg(target_os = "macos")] #[test] fn armed_freeze_capture_without_worker_restores_visibility_and_surfaces_error() { diff --git a/packages/rsnap-overlay/src/overlay/window_runtime.rs b/packages/rsnap-overlay/src/overlay/window_runtime.rs index dd33a0b1..2c6da24f 100644 --- a/packages/rsnap-overlay/src/overlay/window_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/window_runtime.rs @@ -332,6 +332,13 @@ impl OverlaySession { self.complete_startup_aux_window_creation(created_aux_windows); + if created_aux_windows + && let Some(monitor) = self.scroll_capture.monitor + && self.rebuild_active_scroll_capture_live_stream() + && let Some(live_stream) = self.scroll_capture.live_stream.as_ref() + { + live_stream.prime_monitor_nonblocking(monitor); + } if self.loupe_window_needed() { self.set_alt_loupe_window_visible(self.active_cursor_monitor(), true); } From ebcbee495ab7d5b6420306526c59bb4feb9df538 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Sat, 11 Apr 2026 16:33:02 +0800 Subject: [PATCH 8/8] {"schema":"maestro/commit/1","summary":"prune stale hud config cache entries","authority":"XY-240"} --- packages/rsnap-overlay/src/overlay.rs | 4 ++++ .../rsnap-overlay/src/overlay/capture_window_runtime.rs | 5 ++++- packages/rsnap-overlay/src/overlay/config_runtime.rs | 7 +++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/rsnap-overlay/src/overlay.rs b/packages/rsnap-overlay/src/overlay.rs index 69bf6610..e5dfc2c0 100644 --- a/packages/rsnap-overlay/src/overlay.rs +++ b/packages/rsnap-overlay/src/overlay.rs @@ -8232,6 +8232,10 @@ impl OverlaySession { self.event_loop_last_progress_monitor_id = None; self.event_loop_last_progress_detail = None; self.event_loop_last_stall_warn_at = None; + + #[cfg(target_os = "macos")] + self.macos_hud_window_config_cache.clear(); + self.toolbar_left_button_down = false; self.toolbar_left_button_went_down = false; self.toolbar_left_button_went_up = false; diff --git a/packages/rsnap-overlay/src/overlay/capture_window_runtime.rs b/packages/rsnap-overlay/src/overlay/capture_window_runtime.rs index 24aef8a2..3b3373e0 100644 --- a/packages/rsnap-overlay/src/overlay/capture_window_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/capture_window_runtime.rs @@ -150,7 +150,10 @@ impl OverlaySession { #[cfg(target_os = "macos")] pub(super) fn destroy_live_only_aux_windows(&mut self) { - self.loupe_window = None; + if let Some(loupe_window) = self.loupe_window.take() { + self.remove_macos_hud_window_config_cache_entry(loupe_window.window.id()); + } + self.loupe_inner_size_points = None; self.loupe_outer_pos = None; self.pending_loupe_outer_pos = None; diff --git a/packages/rsnap-overlay/src/overlay/config_runtime.rs b/packages/rsnap-overlay/src/overlay/config_runtime.rs index 3445d85d..126f0347 100644 --- a/packages/rsnap-overlay/src/overlay/config_runtime.rs +++ b/packages/rsnap-overlay/src/overlay/config_runtime.rs @@ -1,4 +1,6 @@ use winit::window::Window; +#[cfg(target_os = "macos")] +use winit::window::WindowId; #[cfg(target_os = "macos")] use crate::backend; @@ -255,6 +257,11 @@ impl OverlaySession { let _ = self.macos_hud_window_config_cache.insert(window.id(), desired); } + #[cfg(target_os = "macos")] + pub(super) fn remove_macos_hud_window_config_cache_entry(&mut self, window_id: WindowId) { + let _ = self.macos_hud_window_config_cache.remove(&window_id); + } + fn handle_fake_hud_blur_toggle(&mut self, prev_fake_blur: bool, new_fake_blur: bool) { if prev_fake_blur == new_fake_blur { return;