screencapturekit/stream/
content_filter.rs1use std::ffi::c_void;
25use std::fmt;
26
27use crate::{
28 ffi,
29 shareable_content::{SCDisplay, SCRunningApplication, SCWindow},
30};
31
32pub struct SCContentFilter(*const c_void);
61
62impl PartialEq for SCContentFilter {
63 fn eq(&self, other: &Self) -> bool {
64 self.0 == other.0
65 }
66}
67
68impl Eq for SCContentFilter {}
69
70impl std::hash::Hash for SCContentFilter {
71 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
72 self.0.hash(state);
73 }
74}
75
76impl Default for SCContentFilter {
77 fn default() -> Self {
78 Self(std::ptr::null())
79 }
80}
81
82impl SCContentFilter {
83 #[must_use]
102 pub fn builder() -> SCContentFilterBuilder {
103 SCContentFilterBuilder::new()
104 }
105
106 #[deprecated(since = "1.0.0", note = "Use `builder()` instead")]
108 #[must_use]
109 pub fn build() -> SCContentFilterBuilder {
110 SCContentFilterBuilder::new()
111 }
112
113 pub(crate) fn as_ptr(&self) -> *const c_void {
115 self.0
116 }
117
118 #[must_use]
122 pub fn set_content_rect(self, rect: crate::stream::configuration::Rect) -> Self {
123 unsafe {
124 ffi::sc_content_filter_set_content_rect(
125 self.0,
126 rect.origin.x,
127 rect.origin.y,
128 rect.size.width,
129 rect.size.height,
130 );
131 }
132 self
133 }
134
135 pub fn get_content_rect(&self) -> crate::stream::configuration::Rect {
137 unsafe {
138 let mut x = 0.0;
139 let mut y = 0.0;
140 let mut width = 0.0;
141 let mut height = 0.0;
142 ffi::sc_content_filter_get_content_rect(
143 self.0,
144 &mut x,
145 &mut y,
146 &mut width,
147 &mut height,
148 );
149 crate::stream::configuration::Rect::new(
150 crate::stream::configuration::Point::new(x, y),
151 crate::stream::configuration::Size::new(width, height),
152 )
153 }
154 }
155}
156
157impl Drop for SCContentFilter {
158 fn drop(&mut self) {
159 unsafe {
160 ffi::sc_content_filter_release(self.0);
161 }
162 }
163}
164
165pub type SCContentFilterRef = *const c_void;
168
169extern "C" {}
170
171impl Clone for SCContentFilter {
172 fn clone(&self) -> Self {
173 unsafe { Self(crate::ffi::sc_content_filter_retain(self.0)) }
174 }
175}
176
177impl fmt::Debug for SCContentFilter {
178 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179 f.debug_struct("SCContentFilter")
180 .field("ptr", &self.0)
181 .finish()
182 }
183}
184
185impl fmt::Display for SCContentFilter {
186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 write!(f, "SCContentFilter")
188 }
189}
190
191unsafe impl Send for SCContentFilter {}
194unsafe impl Sync for SCContentFilter {}
195
196pub struct SCContentFilterBuilder {
228 filter_type: FilterType,
229 content_rect: Option<crate::stream::configuration::Rect>,
230}
231
232enum FilterType {
233 None,
234 Window(SCWindow),
235 DisplayExcluding {
236 display: SCDisplay,
237 windows: Vec<SCWindow>,
238 },
239 DisplayIncluding {
240 display: SCDisplay,
241 windows: Vec<SCWindow>,
242 },
243 DisplayApplications {
244 display: SCDisplay,
245 applications: Vec<SCRunningApplication>,
246 excepting_windows: Vec<SCWindow>,
247 },
248}
249
250impl SCContentFilterBuilder {
251 fn new() -> Self {
252 Self {
253 filter_type: FilterType::None,
254 content_rect: None,
255 }
256 }
257
258 #[must_use]
260 pub fn display(mut self, display: &SCDisplay) -> Self {
261 self.filter_type = FilterType::DisplayExcluding {
262 display: display.clone(),
263 windows: Vec::new(),
264 };
265 self
266 }
267
268 #[must_use]
270 pub fn window(mut self, window: &SCWindow) -> Self {
271 self.filter_type = FilterType::Window(window.clone());
272 self
273 }
274
275 #[must_use]
277 pub fn exclude_windows(mut self, windows: &[&SCWindow]) -> Self {
278 if let FilterType::DisplayExcluding {
279 windows: ref mut excluded,
280 ..
281 } = self.filter_type
282 {
283 *excluded = windows.iter().map(|w| (*w).clone()).collect();
284 }
285 self
286 }
287
288 #[must_use]
290 pub fn include_windows(mut self, windows: &[&SCWindow]) -> Self {
291 if let FilterType::DisplayExcluding { display, .. } = self.filter_type {
292 self.filter_type = FilterType::DisplayIncluding {
293 display,
294 windows: windows.iter().map(|w| (*w).clone()).collect(),
295 };
296 }
297 self
298 }
299
300 #[must_use]
302 pub fn include_applications(
303 mut self,
304 applications: &[&SCRunningApplication],
305 excepting_windows: &[&SCWindow],
306 ) -> Self {
307 if let FilterType::DisplayExcluding { display, .. }
308 | FilterType::DisplayIncluding { display, .. } = self.filter_type
309 {
310 self.filter_type = FilterType::DisplayApplications {
311 display,
312 applications: applications.iter().map(|a| (*a).clone()).collect(),
313 excepting_windows: excepting_windows.iter().map(|w| (*w).clone()).collect(),
314 };
315 }
316 self
317 }
318
319 #[must_use]
321 pub fn content_rect(mut self, rect: crate::stream::configuration::Rect) -> Self {
322 self.content_rect = Some(rect);
323 self
324 }
325
326 #[must_use]
328 pub fn build(self) -> SCContentFilter {
329 let filter = match self.filter_type {
330 FilterType::Window(window) => unsafe {
331 let ptr =
332 ffi::sc_content_filter_create_with_desktop_independent_window(window.as_ptr());
333 SCContentFilter(ptr)
334 },
335 FilterType::DisplayExcluding { display, windows } => {
336 let window_refs: Vec<&SCWindow> = windows.iter().collect();
337 unsafe {
338 let window_ptrs: Vec<*const c_void> =
339 window_refs.iter().map(|w| w.as_ptr()).collect();
340
341 let ptr = if window_ptrs.is_empty() {
342 ffi::sc_content_filter_create_with_display_excluding_windows(
343 display.as_ptr(),
344 std::ptr::null(),
345 0,
346 )
347 } else {
348 #[allow(clippy::cast_possible_wrap)]
349 ffi::sc_content_filter_create_with_display_excluding_windows(
350 display.as_ptr(),
351 window_ptrs.as_ptr(),
352 window_ptrs.len() as isize,
353 )
354 };
355 SCContentFilter(ptr)
356 }
357 }
358 FilterType::DisplayIncluding { display, windows } => {
359 let window_refs: Vec<&SCWindow> = windows.iter().collect();
360 unsafe {
361 let window_ptrs: Vec<*const c_void> =
362 window_refs.iter().map(|w| w.as_ptr()).collect();
363
364 let ptr = if window_ptrs.is_empty() {
365 ffi::sc_content_filter_create_with_display_including_windows(
366 display.as_ptr(),
367 std::ptr::null(),
368 0,
369 )
370 } else {
371 #[allow(clippy::cast_possible_wrap)]
372 ffi::sc_content_filter_create_with_display_including_windows(
373 display.as_ptr(),
374 window_ptrs.as_ptr(),
375 window_ptrs.len() as isize,
376 )
377 };
378 SCContentFilter(ptr)
379 }
380 }
381 FilterType::DisplayApplications {
382 display,
383 applications,
384 excepting_windows,
385 } => {
386 let app_refs: Vec<&SCRunningApplication> = applications.iter().collect();
387 let window_refs: Vec<&SCWindow> = excepting_windows.iter().collect();
388 unsafe {
389 let app_ptrs: Vec<*const c_void> =
390 app_refs.iter().map(|a| a.as_ptr()).collect();
391
392 let window_ptrs: Vec<*const c_void> =
393 window_refs.iter().map(|w| w.as_ptr()).collect();
394
395 #[allow(clippy::cast_possible_wrap)]
396 let ptr = ffi::sc_content_filter_create_with_display_including_applications_excepting_windows(
397 display.as_ptr(),
398 if app_ptrs.is_empty() { std::ptr::null() } else { app_ptrs.as_ptr() },
399 app_ptrs.len() as isize,
400 if window_ptrs.is_empty() { std::ptr::null() } else { window_ptrs.as_ptr() },
401 window_ptrs.len() as isize,
402 );
403 SCContentFilter(ptr)
404 }
405 }
406 FilterType::None => {
407 SCContentFilter(std::ptr::null())
409 }
410 };
411
412 if let Some(rect) = self.content_rect {
414 filter.set_content_rect(rect)
415 } else {
416 filter
417 }
418 }
419}