@@ -31,6 +31,7 @@ use std::{
3131 any:: Any ,
3232 collections:: { HashMap , VecDeque } ,
3333 error:: Error as StdError ,
34+ mem,
3435 panic:: AssertUnwindSafe ,
3536 path:: { Path , PathBuf } ,
3637 str:: FromStr ,
@@ -64,6 +65,8 @@ use crate::{
6465 web_api:: ManagerExt ,
6566 windows:: { CapWindowId , ShowCapWindow } ,
6667} ;
68+ #[ cfg( target_os = "macos" ) ]
69+ use scap_targets:: Window ;
6770
6871#[ derive( Clone ) ]
6972pub struct InProgressRecordingCommon {
@@ -107,6 +110,7 @@ unsafe impl Sync for SendableShareableContent {}
107110#[ cfg( target_os = "macos" ) ]
108111async fn acquire_shareable_content_for_target (
109112 capture_target : & ScreenCaptureTarget ,
113+ excluded_windows : & [ scap_targets:: WindowId ] ,
110114) -> anyhow:: Result < SendableShareableContent > {
111115 let mut refreshed = false ;
112116
@@ -118,13 +122,20 @@ async fn acquire_shareable_content_for_target(
118122 . ok_or_else ( || anyhow ! ( "GetShareableContent/NotAvailable" ) ) ?,
119123 ) ;
120124
121- if !shareable_content_missing_target_display ( capture_target, shareable_content. retained ( ) )
122- . await
123- {
125+ let sc_content = shareable_content. retained ( ) ;
126+ let missing_display =
127+ shareable_content_missing_target_display ( capture_target, sc_content. clone ( ) ) . await ;
128+ let missing_windows =
129+ shareable_content_missing_windows ( excluded_windows, sc_content. clone ( ) ) . await ;
130+
131+ if !missing_display && ( !missing_windows || refreshed) {
132+ if missing_windows && refreshed {
133+ debug ! ( "Excluded windows missing from refreshed ScreenCaptureKit content" ) ;
134+ }
124135 return Ok ( shareable_content) ;
125136 }
126137
127- if refreshed {
138+ if refreshed && missing_display {
128139 return Err ( anyhow ! ( "GetShareableContent/DisplayMissing" ) ) ;
129140 }
130141
@@ -150,6 +161,74 @@ async fn shareable_content_missing_target_display(
150161 }
151162}
152163
164+ #[ cfg( target_os = "macos" ) ]
165+ async fn shareable_content_missing_windows (
166+ excluded_windows : & [ scap_targets:: WindowId ] ,
167+ shareable_content : cidre:: arc:: R < cidre:: sc:: ShareableContent > ,
168+ ) -> bool {
169+ if excluded_windows. is_empty ( ) {
170+ return false ;
171+ }
172+
173+ for window_id in excluded_windows {
174+ let Some ( window) = Window :: from_id ( window_id) else {
175+ continue ;
176+ } ;
177+
178+ if window
179+ . raw_handle ( )
180+ . as_sc ( shareable_content. clone ( ) )
181+ . await
182+ . is_none ( )
183+ {
184+ return true ;
185+ }
186+ }
187+
188+ false
189+ }
190+
191+ #[ cfg( target_os = "macos" ) ]
192+ async fn prune_excluded_windows_without_shareable_content (
193+ excluded_windows : & mut Vec < scap_targets:: WindowId > ,
194+ shareable_content : cidre:: arc:: R < cidre:: sc:: ShareableContent > ,
195+ ) {
196+ if excluded_windows. is_empty ( ) {
197+ return ;
198+ }
199+
200+ let mut removed = 0usize ;
201+ let mut pruned = Vec :: with_capacity ( excluded_windows. len ( ) ) ;
202+ let current = mem:: take ( excluded_windows) ;
203+
204+ for window_id in current {
205+ let Some ( window) = Window :: from_id ( & window_id) else {
206+ removed += 1 ;
207+ continue ;
208+ } ;
209+
210+ if window
211+ . raw_handle ( )
212+ . as_sc ( shareable_content. clone ( ) )
213+ . await
214+ . is_some ( )
215+ {
216+ pruned. push ( window_id) ;
217+ } else {
218+ removed += 1 ;
219+ }
220+ }
221+
222+ if removed > 0 {
223+ debug ! (
224+ removed,
225+ "Dropping excluded windows missing from ScreenCaptureKit content"
226+ ) ;
227+ }
228+
229+ * excluded_windows = pruned;
230+ }
231+
153232#[ cfg( target_os = "macos" ) ]
154233fn is_shareable_content_error ( err : & anyhow:: Error ) -> bool {
155234 err. chain ( ) . any ( |cause| {
@@ -613,17 +692,7 @@ pub async fn start_recording(
613692 state. camera_in_use = camera_feed. is_some ( ) ;
614693
615694 #[ cfg( target_os = "macos" ) ]
616- let mut shareable_content =
617- acquire_shareable_content_for_target ( & inputs. capture_target ) . await ?;
618-
619- let common = InProgressRecordingCommon {
620- target_name,
621- inputs : inputs. clone ( ) ,
622- recording_dir : recording_dir. clone ( ) ,
623- } ;
624-
625- #[ cfg( target_os = "macos" ) ]
626- let excluded_windows = {
695+ let mut excluded_windows = {
627696 let window_exclusions = general_settings
628697 . as_ref ( )
629698 . map_or_else ( general_settings:: default_excluded_windows, |settings| {
@@ -633,6 +702,24 @@ pub async fn start_recording(
633702 crate :: window_exclusion:: resolve_window_ids ( & window_exclusions)
634703 } ;
635704
705+ #[ cfg( target_os = "macos" ) ]
706+ let mut shareable_content =
707+ acquire_shareable_content_for_target ( & inputs. capture_target , & excluded_windows)
708+ . await ?;
709+
710+ #[ cfg( target_os = "macos" ) ]
711+ prune_excluded_windows_without_shareable_content (
712+ & mut excluded_windows,
713+ shareable_content. retained ( ) ,
714+ )
715+ . await ;
716+
717+ let common = InProgressRecordingCommon {
718+ target_name,
719+ inputs : inputs. clone ( ) ,
720+ recording_dir : recording_dir. clone ( ) ,
721+ } ;
722+
636723 let mut mic_restart_attempts = 0 ;
637724
638725 let done_fut = loop {
@@ -751,8 +838,16 @@ pub async fn start_recording(
751838 }
752839 #[ cfg( target_os = "macos" ) ]
753840 Err ( err) if is_shareable_content_error ( & err) => {
754- shareable_content =
755- acquire_shareable_content_for_target ( & inputs. capture_target ) . await ?;
841+ shareable_content = acquire_shareable_content_for_target (
842+ & inputs. capture_target ,
843+ & excluded_windows,
844+ )
845+ . await ?;
846+ prune_excluded_windows_without_shareable_content (
847+ & mut excluded_windows,
848+ shareable_content. retained ( ) ,
849+ )
850+ . await ;
756851 continue ;
757852 }
758853 Err ( err) if mic_restart_attempts == 0 && mic_actor_not_running ( & err) => {
0 commit comments