Skip to main content

screencapturekit/ffi/
mod.rs

1//! Swift FFI bridge to `ScreenCaptureKit`
2use std::ffi::c_void;
3
4// MARK: - FFI Packed Data Structures
5
6/// Packed `CGRect` for efficient FFI transfer (32 bytes)
7#[repr(C)]
8#[derive(Debug, Clone, Copy, Default)]
9pub struct FFIRect {
10    pub x: f64,
11    pub y: f64,
12    pub width: f64,
13    pub height: f64,
14}
15
16/// Packed display data for batch retrieval (48 bytes)
17#[repr(C)]
18#[derive(Debug, Clone, Copy)]
19pub struct FFIDisplayData {
20    pub display_id: u32,
21    pub width: i32,
22    pub height: i32,
23    pub frame: FFIRect,
24}
25
26/// Packed window data for batch retrieval
27#[repr(C)]
28#[derive(Debug, Clone, Copy)]
29pub struct FFIWindowData {
30    pub window_id: u32,
31    pub window_layer: i32,
32    pub is_on_screen: bool,
33    pub is_active: bool,
34    pub frame: FFIRect,
35    pub title_offset: u32,
36    pub title_length: u32,
37    pub owning_app_index: i32,
38    #[doc(hidden)]
39    pub _padding: i32,
40}
41
42/// Packed application data for batch retrieval
43#[repr(C)]
44#[derive(Debug, Clone, Copy)]
45pub struct FFIApplicationData {
46    pub process_id: i32,
47    #[doc(hidden)]
48    pub _padding: i32,
49    pub bundle_id_offset: u32,
50    pub bundle_id_length: u32,
51    pub app_name_offset: u32,
52    pub app_name_length: u32,
53}
54
55// MARK: - ABI Layout Assertions
56//
57// The four `#[repr(C)]` structs above are passed by value (or via packed
58// buffers) across the Rust <-> Swift `@_cdecl` FFI boundary. Their Swift
59// counterparts live in `swift-bridge/Sources/ScreenCaptureKitBridge/Core.swift`
60// (`@frozen public struct FFIRect/FFIDisplayData/FFIWindowData/FFIApplicationData`).
61//
62// These compile-time assertions pin the exact ABI shared with Swift: any change
63// to a field type, field order, or padding fails the build immediately instead
64// of silently corrupting marshalled data at runtime. If you change the layout
65// here you MUST mirror it in Core.swift (and vice versa); the cross-language
66// `sc_verify_ffi_layout` check in `tests/ffi_layout_tests.rs` guards that too.
67use core::mem::{align_of, offset_of, size_of};
68
69const _: () = assert!(size_of::<FFIRect>() == 32);
70const _: () = assert!(align_of::<FFIRect>() == 8);
71const _: () = assert!(offset_of!(FFIRect, x) == 0);
72const _: () = assert!(offset_of!(FFIRect, y) == 8);
73const _: () = assert!(offset_of!(FFIRect, width) == 16);
74const _: () = assert!(offset_of!(FFIRect, height) == 24);
75
76const _: () = assert!(size_of::<FFIDisplayData>() == 48);
77const _: () = assert!(align_of::<FFIDisplayData>() == 8);
78const _: () = assert!(offset_of!(FFIDisplayData, display_id) == 0);
79const _: () = assert!(offset_of!(FFIDisplayData, width) == 4);
80const _: () = assert!(offset_of!(FFIDisplayData, height) == 8);
81const _: () = assert!(offset_of!(FFIDisplayData, frame) == 16);
82
83const _: () = assert!(size_of::<FFIWindowData>() == 64);
84const _: () = assert!(align_of::<FFIWindowData>() == 8);
85const _: () = assert!(offset_of!(FFIWindowData, window_id) == 0);
86const _: () = assert!(offset_of!(FFIWindowData, window_layer) == 4);
87const _: () = assert!(offset_of!(FFIWindowData, is_on_screen) == 8);
88const _: () = assert!(offset_of!(FFIWindowData, is_active) == 9);
89const _: () = assert!(offset_of!(FFIWindowData, frame) == 16);
90const _: () = assert!(offset_of!(FFIWindowData, title_offset) == 48);
91const _: () = assert!(offset_of!(FFIWindowData, title_length) == 52);
92const _: () = assert!(offset_of!(FFIWindowData, owning_app_index) == 56);
93const _: () = assert!(offset_of!(FFIWindowData, _padding) == 60);
94
95const _: () = assert!(size_of::<FFIApplicationData>() == 24);
96const _: () = assert!(align_of::<FFIApplicationData>() == 4);
97const _: () = assert!(offset_of!(FFIApplicationData, process_id) == 0);
98const _: () = assert!(offset_of!(FFIApplicationData, _padding) == 4);
99const _: () = assert!(offset_of!(FFIApplicationData, bundle_id_offset) == 8);
100const _: () = assert!(offset_of!(FFIApplicationData, bundle_id_length) == 12);
101const _: () = assert!(offset_of!(FFIApplicationData, app_name_offset) == 16);
102const _: () = assert!(offset_of!(FFIApplicationData, app_name_length) == 20);
103
104// MARK: - CoreGraphics Initialization
105extern "C" {
106    /// Force CoreGraphics initialization by calling `CGMainDisplayID`
107    /// This prevents `CGS_REQUIRE_INIT` crashes on headless systems
108    pub fn sc_initialize_core_graphics();
109
110    /// Cross-language ABI check implemented in the Swift bridge.
111    ///
112    /// Returns `true` only if the Swift `MemoryLayout` (size, stride and
113    /// alignment) of all four FFI structs matches the values pinned on the
114    /// Rust side. Verified by `tests/ffi_layout_tests.rs`.
115    pub fn sc_verify_ffi_layout() -> bool;
116}
117
118// MARK: - SCShareableContent
119extern "C" {
120    /// Synchronous blocking call to get shareable content.
121    ///
122    /// Returns the content pointer on success, or writes an error message
123    /// (NUL-terminated UTF-8) into `error_buffer`.
124    ///
125    /// # Safety
126    ///
127    /// `error_buffer` must point to at least `error_buffer_size` writable
128    /// bytes. The Swift bridge writes at most `error_buffer_size - 1`
129    /// payload bytes plus a NUL terminator (i.e. it respects the size
130    /// argument and never writes past the buffer). `error_buffer_size`
131    /// is `isize` for ABI compatibility with Apple's pattern, but values
132    /// must be > 0; pass `crate::utils::ffi_string::DEFAULT_BUFFER_SIZE`
133    /// (1024) or `SMALL_BUFFER_SIZE` (256) cast to `isize` for typical
134    /// error messages.
135    pub fn sc_shareable_content_get_sync(
136        exclude_desktop_windows: bool,
137        on_screen_windows_only: bool,
138        error_buffer: *mut i8,
139        error_buffer_size: isize,
140    ) -> *const c_void;
141
142    /// Async callback-based shareable content retrieval with options
143    pub fn sc_shareable_content_get_with_options(
144        exclude_desktop_windows: bool,
145        on_screen_windows_only: bool,
146        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
147        user_data: *mut c_void,
148    );
149
150    pub fn sc_shareable_content_get(
151        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
152        user_data: *mut c_void,
153    );
154    pub fn sc_shareable_content_get_current_process_displays(
155        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
156        user_data: *mut c_void,
157    );
158    pub fn sc_shareable_content_get_below_window(
159        exclude_desktop_windows: bool,
160        reference_window: *const c_void,
161        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
162        user_data: *mut c_void,
163    );
164    pub fn sc_shareable_content_get_above_window(
165        exclude_desktop_windows: bool,
166        reference_window: *const c_void,
167        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
168        user_data: *mut c_void,
169    );
170    pub fn sc_shareable_content_retain(content: *const c_void) -> *const c_void;
171    pub fn sc_shareable_content_release(content: *const c_void);
172    pub fn sc_shareable_content_get_displays_count(content: *const c_void) -> isize;
173    pub fn sc_shareable_content_get_display_at(
174        content: *const c_void,
175        index: isize,
176    ) -> *const c_void;
177    pub fn sc_shareable_content_get_windows_count(content: *const c_void) -> isize;
178    pub fn sc_shareable_content_get_window_at(
179        content: *const c_void,
180        index: isize,
181    ) -> *const c_void;
182    pub fn sc_shareable_content_get_applications_count(content: *const c_void) -> isize;
183    pub fn sc_shareable_content_get_application_at(
184        content: *const c_void,
185        index: isize,
186    ) -> *const c_void;
187
188    // Batch retrieval functions (optimized FFI)
189    pub fn sc_shareable_content_get_displays_batch(
190        content: *const c_void,
191        buffer: *mut c_void, // Actually *mut FFIDisplayData
192        max_displays: isize,
193    ) -> isize;
194
195    pub fn sc_shareable_content_get_applications_batch(
196        content: *const c_void,
197        buffer: *mut c_void, // Actually *mut FFIApplicationData
198        max_apps: isize,
199        string_buffer: *mut i8,
200        string_buffer_size: isize,
201        string_buffer_used: *mut isize,
202    ) -> isize;
203
204    pub fn sc_shareable_content_get_windows_batch(
205        content: *const c_void,
206        buffer: *mut c_void, // Actually *mut FFIWindowData
207        max_windows: isize,
208        string_buffer: *mut i8,
209        string_buffer_size: isize,
210        string_buffer_used: *mut isize,
211        app_pointers: *mut *const c_void,
212        max_apps: isize,
213        app_count: *mut isize,
214    ) -> isize;
215}
216
217// MARK: - SCDisplay
218extern "C" {
219    pub fn sc_display_retain(display: *const c_void) -> *const c_void;
220    pub fn sc_display_release(display: *const c_void);
221    pub fn sc_display_get_display_id(display: *const c_void) -> u32;
222    pub fn sc_display_get_width(display: *const c_void) -> isize;
223    pub fn sc_display_get_height(display: *const c_void) -> isize;
224    pub fn sc_display_get_frame(
225        display: *const c_void,
226        x: *mut f64,
227        y: *mut f64,
228        width: *mut f64,
229        height: *mut f64,
230    );
231    /// Get display frame (same as `sc_display_get_frame`, kept for API compatibility)
232    pub fn sc_display_get_frame_packed(
233        display: *const c_void,
234        x: *mut f64,
235        y: *mut f64,
236        width: *mut f64,
237        height: *mut f64,
238    );
239}
240
241// MARK: - SCWindow
242extern "C" {
243    pub fn sc_window_retain(window: *const c_void) -> *const c_void;
244    pub fn sc_window_release(window: *const c_void);
245    pub fn sc_window_get_window_id(window: *const c_void) -> u32;
246    pub fn sc_window_get_frame(
247        window: *const c_void,
248        x: *mut f64,
249        y: *mut f64,
250        width: *mut f64,
251        height: *mut f64,
252    );
253    /// Get window frame (same as `sc_window_get_frame`, kept for API compatibility)
254    pub fn sc_window_get_frame_packed(
255        window: *const c_void,
256        x: *mut f64,
257        y: *mut f64,
258        width: *mut f64,
259        height: *mut f64,
260    );
261    pub fn sc_window_get_title(window: *const c_void, buffer: *mut i8, buffer_size: isize) -> bool;
262    /// Get window title as owned string (caller must free with `sc_free_string`)
263    pub fn sc_window_get_title_owned(window: *const c_void) -> *mut i8;
264    pub fn sc_window_get_window_layer(window: *const c_void) -> isize;
265    pub fn sc_window_is_on_screen(window: *const c_void) -> bool;
266    pub fn sc_window_get_owning_application(window: *const c_void) -> *const c_void;
267    pub fn sc_window_is_active(window: *const c_void) -> bool;
268}
269
270// MARK: - SCRunningApplication
271extern "C" {
272    pub fn sc_running_application_retain(app: *const c_void) -> *const c_void;
273    pub fn sc_running_application_release(app: *const c_void);
274    pub fn sc_running_application_get_bundle_identifier(
275        app: *const c_void,
276        buffer: *mut i8,
277        buffer_size: isize,
278    ) -> bool;
279    /// Get bundle identifier as owned string (caller must free with `sc_free_string`)
280    pub fn sc_running_application_get_bundle_identifier_owned(app: *const c_void) -> *mut i8;
281    pub fn sc_running_application_get_application_name(
282        app: *const c_void,
283        buffer: *mut i8,
284        buffer_size: isize,
285    ) -> bool;
286    /// Get application name as owned string (caller must free with `sc_free_string`)
287    pub fn sc_running_application_get_application_name_owned(app: *const c_void) -> *mut i8;
288    pub fn sc_running_application_get_process_id(app: *const c_void) -> i32;
289}
290
291// MARK: - String memory management
292extern "C" {
293    /// Free a string allocated by Swift (strdup)
294    pub fn sc_free_string(str: *mut i8);
295}
296
297// MARK: - SCStreamConfiguration
298extern "C" {
299    pub fn sc_stream_configuration_create() -> *const c_void;
300    pub fn sc_stream_configuration_retain(config: *const c_void) -> *const c_void;
301    pub fn sc_stream_configuration_release(config: *const c_void);
302
303    pub fn sc_stream_configuration_set_width(config: *const c_void, width: isize);
304    pub fn sc_stream_configuration_get_width(config: *const c_void) -> isize;
305
306    pub fn sc_stream_configuration_set_height(config: *const c_void, height: isize);
307    pub fn sc_stream_configuration_get_height(config: *const c_void) -> isize;
308
309    pub fn sc_stream_configuration_set_shows_cursor(config: *const c_void, shows_cursor: bool);
310    pub fn sc_stream_configuration_get_shows_cursor(config: *const c_void) -> bool;
311
312    pub fn sc_stream_configuration_set_scales_to_fit(config: *const c_void, scales_to_fit: bool);
313    pub fn sc_stream_configuration_get_scales_to_fit(config: *const c_void) -> bool;
314
315    pub fn sc_stream_configuration_set_captures_audio(config: *const c_void, captures_audio: bool);
316    pub fn sc_stream_configuration_get_captures_audio(config: *const c_void) -> bool;
317
318    pub fn sc_stream_configuration_set_sample_rate(config: *const c_void, sample_rate: isize);
319    pub fn sc_stream_configuration_get_sample_rate(config: *const c_void) -> isize;
320
321    pub fn sc_stream_configuration_set_channel_count(config: *const c_void, channel_count: isize);
322    pub fn sc_stream_configuration_get_channel_count(config: *const c_void) -> isize;
323
324    pub fn sc_stream_configuration_set_queue_depth(config: *const c_void, queue_depth: isize);
325    pub fn sc_stream_configuration_get_queue_depth(config: *const c_void) -> isize;
326
327    pub fn sc_stream_configuration_set_pixel_format(config: *const c_void, pixel_format: u32);
328    pub fn sc_stream_configuration_get_pixel_format(config: *const c_void) -> u32;
329
330    pub fn sc_stream_configuration_set_minimum_frame_interval(
331        config: *const c_void,
332        value: i64,
333        timescale: i32,
334        flags: u32,
335        epoch: i64,
336    );
337    pub fn sc_stream_configuration_get_minimum_frame_interval(
338        config: *const c_void,
339        value: *mut i64,
340        timescale: *mut i32,
341        flags: *mut u32,
342        epoch: *mut i64,
343    );
344
345    pub fn sc_stream_configuration_set_source_rect(
346        config: *const c_void,
347        x: f64,
348        y: f64,
349        width: f64,
350        height: f64,
351    );
352    pub fn sc_stream_configuration_get_source_rect(
353        config: *const c_void,
354        x: *mut f64,
355        y: *mut f64,
356        width: *mut f64,
357        height: *mut f64,
358    );
359
360    pub fn sc_stream_configuration_set_destination_rect(
361        config: *const c_void,
362        x: f64,
363        y: f64,
364        width: f64,
365        height: f64,
366    );
367    pub fn sc_stream_configuration_get_destination_rect(
368        config: *const c_void,
369        x: *mut f64,
370        y: *mut f64,
371        width: *mut f64,
372        height: *mut f64,
373    );
374
375    pub fn sc_stream_configuration_set_preserves_aspect_ratio(
376        config: *const c_void,
377        preserves_aspect_ratio: bool,
378    );
379    pub fn sc_stream_configuration_get_preserves_aspect_ratio(config: *const c_void) -> bool;
380
381    pub fn sc_stream_configuration_set_ignores_shadows_single_window(
382        config: *const c_void,
383        ignores_shadows: bool,
384    );
385    pub fn sc_stream_configuration_get_ignores_shadows_single_window(config: *const c_void)
386        -> bool;
387
388    pub fn sc_stream_configuration_set_should_be_opaque(
389        config: *const c_void,
390        should_be_opaque: bool,
391    );
392    pub fn sc_stream_configuration_get_should_be_opaque(config: *const c_void) -> bool;
393
394    pub fn sc_stream_configuration_set_includes_child_windows(
395        config: *const c_void,
396        includes_child_windows: bool,
397    );
398    pub fn sc_stream_configuration_get_includes_child_windows(config: *const c_void) -> bool;
399
400    pub fn sc_stream_configuration_set_presenter_overlay_privacy_alert_setting(
401        config: *const c_void,
402        setting: i32,
403    );
404    pub fn sc_stream_configuration_get_presenter_overlay_privacy_alert_setting(
405        config: *const c_void,
406    ) -> i32;
407
408    pub fn sc_stream_configuration_set_background_color(
409        config: *const c_void,
410        r: f32,
411        g: f32,
412        b: f32,
413        a: f32,
414    );
415    pub fn sc_stream_configuration_get_background_color(
416        config: *const c_void,
417        r: *mut f32,
418        g: *mut f32,
419        b: *mut f32,
420        a: *mut f32,
421    ) -> bool;
422    pub fn sc_stream_configuration_set_color_space_name(config: *const c_void, name: *const i8);
423    pub fn sc_stream_configuration_get_color_space_name(
424        config: *const c_void,
425        buffer: *mut i8,
426        buffer_size: isize,
427    ) -> bool;
428    pub fn sc_stream_configuration_set_color_matrix(config: *const c_void, matrix: *const i8);
429    pub fn sc_stream_configuration_get_color_matrix(
430        config: *const c_void,
431        buffer: *mut i8,
432        buffer_size: isize,
433    ) -> bool;
434
435    // macOS 14.0+ - capture resolution type
436    pub fn sc_stream_configuration_set_capture_resolution_type(config: *const c_void, value: i32);
437    pub fn sc_stream_configuration_get_capture_resolution_type(config: *const c_void) -> i32;
438
439    pub fn sc_stream_configuration_set_ignores_shadow_display_configuration(
440        config: *const c_void,
441        ignores_shadow: bool,
442    );
443    pub fn sc_stream_configuration_get_ignores_shadow_display_configuration(
444        config: *const c_void,
445    ) -> bool;
446
447    pub fn sc_stream_configuration_set_preserve_aspect_ratio(config: *const c_void, preserve: bool);
448    pub fn sc_stream_configuration_get_preserve_aspect_ratio(config: *const c_void) -> bool;
449
450    pub fn sc_stream_configuration_set_captures_shadows_only(
451        config: *const c_void,
452        captures_shadows_only: bool,
453    );
454    pub fn sc_stream_configuration_get_captures_shadows_only(config: *const c_void) -> bool;
455
456    pub fn sc_stream_configuration_set_captures_microphone(
457        config: *const c_void,
458        captures_microphone: bool,
459    );
460    pub fn sc_stream_configuration_get_captures_microphone(config: *const c_void) -> bool;
461
462    pub fn sc_stream_configuration_set_excludes_current_process_audio(
463        config: *const c_void,
464        excludes: bool,
465    );
466    pub fn sc_stream_configuration_get_excludes_current_process_audio(
467        config: *const c_void,
468    ) -> bool;
469
470    pub fn sc_stream_configuration_set_microphone_capture_device_id(
471        config: *const c_void,
472        device_id: *const i8,
473    );
474    pub fn sc_stream_configuration_get_microphone_capture_device_id(
475        config: *const c_void,
476        buffer: *mut i8,
477        buffer_size: isize,
478    ) -> bool;
479
480    pub fn sc_stream_configuration_set_stream_name(config: *const c_void, name: *const i8);
481    pub fn sc_stream_configuration_get_stream_name(
482        config: *const c_void,
483        buffer: *mut i8,
484        buffer_size: isize,
485    ) -> bool;
486
487    pub fn sc_stream_configuration_set_capture_dynamic_range(config: *const c_void, value: i32);
488    pub fn sc_stream_configuration_get_capture_dynamic_range(config: *const c_void) -> i32;
489}
490
491// MARK: - SCContentFilter
492extern "C" {
493    pub fn sc_content_filter_create_with_desktop_independent_window(
494        window: *const c_void,
495    ) -> *const c_void;
496    pub fn sc_content_filter_create_with_display_excluding_windows(
497        display: *const c_void,
498        windows: *const *const c_void,
499        windows_count: isize,
500    ) -> *const c_void;
501    pub fn sc_content_filter_create_with_display_including_windows(
502        display: *const c_void,
503        windows: *const *const c_void,
504        windows_count: isize,
505    ) -> *const c_void;
506    pub fn sc_content_filter_create_with_display_including_applications_excepting_windows(
507        display: *const c_void,
508        apps: *const *const c_void,
509        apps_count: isize,
510        windows: *const *const c_void,
511        windows_count: isize,
512    ) -> *const c_void;
513    pub fn sc_content_filter_create_with_display_excluding_applications_excepting_windows(
514        display: *const c_void,
515        apps: *const *const c_void,
516        apps_count: isize,
517        windows: *const *const c_void,
518        windows_count: isize,
519    ) -> *const c_void;
520    pub fn sc_content_filter_retain(filter: *const c_void) -> *const c_void;
521    pub fn sc_content_filter_release(filter: *const c_void);
522    pub fn sc_content_filter_set_content_rect(
523        filter: *const c_void,
524        x: f64,
525        y: f64,
526        width: f64,
527        height: f64,
528    );
529    pub fn sc_content_filter_get_content_rect(
530        filter: *const c_void,
531        x: *mut f64,
532        y: *mut f64,
533        width: *mut f64,
534        height: *mut f64,
535    );
536    /// Get content filter content rect (same as `sc_content_filter_get_content_rect`)
537    pub fn sc_content_filter_get_content_rect_packed(
538        filter: *const c_void,
539        x: *mut f64,
540        y: *mut f64,
541        width: *mut f64,
542        height: *mut f64,
543    );
544}
545
546// MARK: - SCStream
547extern "C" {
548    pub fn sc_stream_create(
549        filter: *const c_void,
550        config: *const c_void,
551        context: *mut c_void,
552        error_callback: extern "C" fn(*mut c_void, i32, *const i8),
553        sample_callback: extern "C" fn(*mut c_void, *const c_void, i32),
554        context_retain: extern "C" fn(*mut c_void),
555        context_release: extern "C" fn(*mut c_void),
556    ) -> *const c_void;
557    pub fn sc_stream_add_stream_output(stream: *const c_void, output_type: i32) -> bool;
558    pub fn sc_stream_add_stream_output_with_queue(
559        stream: *const c_void,
560        output_type: i32,
561        dispatch_queue: *const c_void,
562    ) -> bool;
563    pub fn sc_stream_remove_stream_output(stream: *const c_void, output_type: i32) -> bool;
564    pub fn sc_stream_start_capture(
565        stream: *const c_void,
566        context: *mut c_void,
567        callback: extern "C" fn(*mut c_void, bool, *const i8),
568    );
569    pub fn sc_stream_stop_capture(
570        stream: *const c_void,
571        context: *mut c_void,
572        callback: extern "C" fn(*mut c_void, bool, *const i8),
573    );
574    pub fn sc_stream_update_configuration(
575        stream: *const c_void,
576        config: *const c_void,
577        context: *mut c_void,
578        callback: extern "C" fn(*mut c_void, bool, *const i8),
579    );
580    pub fn sc_stream_update_content_filter(
581        stream: *const c_void,
582        filter: *const c_void,
583        context: *mut c_void,
584        callback: extern "C" fn(*mut c_void, bool, *const i8),
585    );
586    pub fn sc_stream_add_recording_output(
587        stream: *const c_void,
588        recording_output: *const c_void,
589        callback: extern "C" fn(*mut c_void, bool, *const i8),
590        context: *mut c_void,
591    );
592    pub fn sc_stream_remove_recording_output(
593        stream: *const c_void,
594        recording_output: *const c_void,
595        callback: extern "C" fn(*mut c_void, bool, *const i8),
596        context: *mut c_void,
597    );
598    pub fn sc_stream_retain(stream: *const c_void) -> *const c_void;
599    pub fn sc_stream_release(stream: *const c_void);
600
601    // macOS 13.0+ - synchronizationClock
602    pub fn sc_stream_get_synchronization_clock(stream: *const c_void) -> *const c_void;
603}
604
605// MARK: - Dispatch Queue
606extern "C" {
607    pub fn dispatch_queue_create(label: *const i8, qos: i32) -> *const c_void;
608    pub fn dispatch_queue_release(queue: *const c_void);
609    pub fn dispatch_queue_retain(queue: *const c_void) -> *const c_void;
610}
611
612// MARK: - IOSurface
613extern "C" {
614    pub fn cv_pixel_buffer_get_iosurface(pixel_buffer: *const c_void) -> *const c_void;
615    pub fn cv_pixel_buffer_is_backed_by_iosurface(pixel_buffer: *const c_void) -> bool;
616    pub fn iosurface_get_width(iosurface: *const c_void) -> isize;
617    pub fn iosurface_get_height(iosurface: *const c_void) -> isize;
618    pub fn iosurface_get_bytes_per_row(iosurface: *const c_void) -> isize;
619    pub fn iosurface_get_pixel_format(iosurface: *const c_void) -> u32;
620    pub fn iosurface_get_base_address(iosurface: *const c_void) -> *mut u8;
621    pub fn iosurface_lock(iosurface: *const c_void, options: u32) -> i32;
622    pub fn iosurface_unlock(iosurface: *const c_void, options: u32) -> i32;
623    pub fn iosurface_is_in_use(iosurface: *const c_void) -> bool;
624    pub fn iosurface_release(iosurface: *const c_void);
625
626    // Plane functions (for multi-planar formats like YCbCr 420)
627    pub fn iosurface_get_plane_count(iosurface: *const c_void) -> isize;
628    pub fn iosurface_get_width_of_plane(iosurface: *const c_void, plane: isize) -> isize;
629    pub fn iosurface_get_height_of_plane(iosurface: *const c_void, plane: isize) -> isize;
630    pub fn iosurface_get_bytes_per_row_of_plane(iosurface: *const c_void, plane: isize) -> isize;
631
632    // IOSurface creation (for testing)
633    pub fn io_surface_create(
634        width: usize,
635        height: usize,
636        pixel_format: u32,
637        bytes_per_element: usize,
638        surface_out: *mut *mut c_void,
639    ) -> i32;
640
641    // IOSurface creation with full properties (for multi-planar formats)
642    pub fn io_surface_create_with_properties(
643        width: usize,
644        height: usize,
645        pixel_format: u32,
646        bytes_per_element: usize,
647        bytes_per_row: usize,
648        alloc_size: usize,
649        plane_count: usize,
650        plane_widths: *const usize,
651        plane_heights: *const usize,
652        plane_bytes_per_row: *const usize,
653        plane_bytes_per_element: *const usize,
654        plane_offsets: *const usize,
655        plane_sizes: *const usize,
656        surface_out: *mut *mut c_void,
657    ) -> i32;
658}
659
660// MARK: - SCContentSharingPicker (macOS 14.0+)
661extern "C" {
662    pub fn sc_content_sharing_picker_configuration_create() -> *const c_void;
663    pub fn sc_content_sharing_picker_configuration_set_allowed_picker_modes(
664        config: *const c_void,
665        modes: *const i32,
666        count: usize,
667    );
668    pub fn sc_content_sharing_picker_configuration_get_allowed_picker_modes_mask(
669        config: *const c_void,
670    ) -> u64;
671    pub fn sc_content_sharing_picker_configuration_set_allows_changing_selected_content(
672        config: *const c_void,
673        allows: bool,
674    );
675    pub fn sc_content_sharing_picker_configuration_get_allows_changing_selected_content(
676        config: *const c_void,
677    ) -> bool;
678    pub fn sc_content_sharing_picker_configuration_set_excluded_bundle_ids(
679        config: *const c_void,
680        bundle_ids: *const *const i8,
681        count: usize,
682    );
683    pub fn sc_content_sharing_picker_configuration_get_excluded_bundle_ids_count(
684        config: *const c_void,
685    ) -> usize;
686    pub fn sc_content_sharing_picker_configuration_get_excluded_bundle_id_at(
687        config: *const c_void,
688        index: usize,
689        buffer: *mut i8,
690        buffer_size: usize,
691    ) -> bool;
692    pub fn sc_content_sharing_picker_configuration_set_excluded_window_ids(
693        config: *const c_void,
694        window_ids: *const u32,
695        count: usize,
696    );
697    pub fn sc_content_sharing_picker_configuration_get_excluded_window_ids_count(
698        config: *const c_void,
699    ) -> usize;
700    pub fn sc_content_sharing_picker_configuration_get_excluded_window_id_at(
701        config: *const c_void,
702        index: usize,
703    ) -> u32;
704    pub fn sc_content_sharing_picker_configuration_retain(config: *const c_void) -> *const c_void;
705    pub fn sc_content_sharing_picker_configuration_release(config: *const c_void);
706
707    // Picker maximum stream count
708    pub fn sc_content_sharing_picker_set_maximum_stream_count(count: usize);
709    pub fn sc_content_sharing_picker_get_maximum_stream_count() -> usize;
710
711    /// Returns a fresh `SCContentSharingPickerConfiguration` initialised with
712    /// the system's `defaultConfiguration` baseline. Caller owns the returned
713    /// configuration and must release it via the standard
714    /// `sc_content_sharing_picker_configuration_release`.
715    pub fn sc_content_sharing_picker_create_default_configuration() -> *const c_void;
716
717    /// Read whether the shared content-sharing picker is currently active.
718    pub fn sc_content_sharing_picker_get_active() -> bool;
719    /// Mark the shared content-sharing picker active or inactive.
720    pub fn sc_content_sharing_picker_set_active(active: bool);
721
722    pub fn sc_content_sharing_picker_show(
723        config: *const c_void,
724        callback: extern "C" fn(i32, *const c_void, *mut c_void),
725        user_data: *mut c_void,
726    );
727    pub fn sc_content_sharing_picker_show_with_result(
728        config: *const c_void,
729        callback: extern "C" fn(i32, *const c_void, *mut c_void),
730        user_data: *mut c_void,
731    );
732    pub fn sc_content_sharing_picker_show_for_stream(
733        config: *const c_void,
734        stream: *const c_void,
735        callback: extern "C" fn(i32, *const c_void, *mut c_void),
736        user_data: *mut c_void,
737    );
738    pub fn sc_content_sharing_picker_show_using_style(
739        config: *const c_void,
740        style: i32,
741        callback: extern "C" fn(i32, *const c_void, *mut c_void),
742        user_data: *mut c_void,
743    );
744    pub fn sc_content_sharing_picker_show_for_stream_using_style(
745        config: *const c_void,
746        stream: *const c_void,
747        style: i32,
748        callback: extern "C" fn(i32, *const c_void, *mut c_void),
749        user_data: *mut c_void,
750    );
751    pub fn sc_picker_result_get_filter(result: *const c_void) -> *const c_void;
752    pub fn sc_picker_result_get_content_rect(
753        result: *const c_void,
754        x: *mut f64,
755        y: *mut f64,
756        width: *mut f64,
757        height: *mut f64,
758    );
759    pub fn sc_picker_result_get_scale(result: *const c_void) -> f64;
760    pub fn sc_picker_result_get_windows_count(result: *const c_void) -> usize;
761    pub fn sc_picker_result_get_window_at(result: *const c_void, index: usize) -> *const c_void;
762    pub fn sc_picker_result_get_displays_count(result: *const c_void) -> usize;
763    pub fn sc_picker_result_get_display_at(result: *const c_void, index: usize) -> *const c_void;
764    pub fn sc_picker_result_get_applications_count(result: *const c_void) -> usize;
765    pub fn sc_picker_result_get_application_at(
766        result: *const c_void,
767        index: usize,
768    ) -> *const c_void;
769    pub fn sc_picker_result_release(result: *const c_void);
770}
771
772// MARK: - SCRecordingOutput (macOS 15.0+)
773extern "C" {
774    pub fn sc_recording_output_configuration_create() -> *const c_void;
775    pub fn sc_recording_output_configuration_set_output_url(config: *const c_void, path: *const i8);
776    pub fn sc_recording_output_configuration_get_output_url(
777        config: *const c_void,
778        buffer: *mut i8,
779        buffer_size: isize,
780    ) -> bool;
781    pub fn sc_recording_output_configuration_set_video_codec(config: *const c_void, codec: i32);
782    pub fn sc_recording_output_configuration_retain(config: *const c_void) -> *const c_void;
783    pub fn sc_recording_output_configuration_release(config: *const c_void);
784    pub fn sc_recording_output_create(config: *const c_void) -> *const c_void;
785    pub fn sc_recording_output_retain(output: *const c_void) -> *const c_void;
786    pub fn sc_recording_output_release(output: *const c_void);
787}
788
789// MARK: - SCScreenshotManager (macOS 14.0+)
790extern "C" {
791    pub fn sc_screenshot_manager_capture_image(
792        content_filter: *const c_void,
793        config: *const c_void,
794        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
795        user_data: *mut c_void,
796    );
797    pub fn sc_screenshot_manager_capture_sample_buffer(
798        content_filter: *const c_void,
799        config: *const c_void,
800        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
801        user_data: *mut c_void,
802    );
803    pub fn sc_screenshot_manager_capture_image_in_rect(
804        x: f64,
805        y: f64,
806        width: f64,
807        height: f64,
808        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
809        user_data: *mut c_void,
810    );
811    pub fn cgimage_get_width(image: *const c_void) -> usize;
812    pub fn cgimage_get_height(image: *const c_void) -> usize;
813    pub fn cgimage_get_data(
814        image: *const c_void,
815        out_ptr: *mut *const u8,
816        out_length: *mut usize,
817    ) -> bool;
818    pub fn cgimage_free_data(ptr: *mut u8);
819    /// Render the `CGImage`'s RGBA bytes directly into a caller-owned buffer.
820    /// Returns the number of bytes written (= width*height*4) on success, or 0
821    /// on failure. Replaces the legacy `cgimage_get_data` + `cgimage_free_data`
822    /// pair which made an extra Swift-side malloc + memcpy before handing the
823    /// buffer back; this single-call form lets Rust own the allocation and
824    /// removes one full RGBA-sized memcpy from the screenshot decode path.
825    pub fn cgimage_render_rgba_into(
826        image: *const c_void,
827        dest: *mut u8,
828        dest_capacity: usize,
829    ) -> usize;
830    /// Render the `CGImage` directly as **BGRA** (the source pixel layout for
831    /// every `ScreenCaptureKit`-produced `CGImage`). Saves the per-pixel
832    /// channel-swap that `cgimage_render_rgba_into` forces — measured at
833    /// ~20 ms per 4K screenshot. Use when the consumer (Metal, wgpu, ffmpeg)
834    /// accepts BGRA natively.
835    pub fn cgimage_render_bgra_into(
836        image: *const c_void,
837        dest: *mut u8,
838        dest_capacity: usize,
839    ) -> usize;
840    /// Strided variant of [`cgimage_render_rgba_into`]: renders RGBA bytes
841    /// using a caller-specified row stride (`dest_bytes_per_row`) instead of
842    /// the tightly-packed `width * 4`. Lets consumers with row-aligned/padded
843    /// buffers (GPU upload, wgpu) avoid an extra repack. Returns the number of
844    /// bytes spanned (`height * dest_bytes_per_row`) on success, or 0 on
845    /// failure (stride too small or buffer too small).
846    pub fn cgimage_render_rgba_into_strided(
847        image: *const c_void,
848        dest: *mut u8,
849        dest_capacity: usize,
850        dest_bytes_per_row: usize,
851    ) -> usize;
852    /// Strided **BGRA** variant. See [`cgimage_render_bgra_into`] and
853    /// [`cgimage_render_rgba_into_strided`].
854    pub fn cgimage_render_bgra_into_strided(
855        image: *const c_void,
856        dest: *mut u8,
857        dest_capacity: usize,
858        dest_bytes_per_row: usize,
859    ) -> usize;
860    pub fn cgimage_release(image: *const c_void);
861    pub fn cgimage_save_png(image: *const c_void, path: *const i8) -> bool;
862    pub fn cgimage_save_to_file(
863        image: *const c_void,
864        path: *const i8,
865        format: i32,
866        quality: f32,
867    ) -> bool;
868}
869
870// MARK: - SCScreenshotConfiguration (macOS 26.0+)
871extern "C" {
872    pub fn sc_screenshot_configuration_create() -> *const c_void;
873    pub fn sc_screenshot_configuration_set_width(config: *const c_void, width: isize);
874    pub fn sc_screenshot_configuration_set_height(config: *const c_void, height: isize);
875    pub fn sc_screenshot_configuration_set_shows_cursor(config: *const c_void, shows_cursor: bool);
876    pub fn sc_screenshot_configuration_set_source_rect(
877        config: *const c_void,
878        x: f64,
879        y: f64,
880        width: f64,
881        height: f64,
882    );
883    pub fn sc_screenshot_configuration_set_destination_rect(
884        config: *const c_void,
885        x: f64,
886        y: f64,
887        width: f64,
888        height: f64,
889    );
890    pub fn sc_screenshot_configuration_set_ignore_shadows(
891        config: *const c_void,
892        ignore_shadows: bool,
893    );
894    pub fn sc_screenshot_configuration_set_ignore_clipping(
895        config: *const c_void,
896        ignore_clipping: bool,
897    );
898    pub fn sc_screenshot_configuration_set_include_child_windows(
899        config: *const c_void,
900        include_child_windows: bool,
901    );
902    pub fn sc_screenshot_configuration_set_display_intent(
903        config: *const c_void,
904        display_intent: i32,
905    );
906    pub fn sc_screenshot_configuration_set_dynamic_range(config: *const c_void, dynamic_range: i32);
907    pub fn sc_screenshot_configuration_set_file_url(config: *const c_void, path: *const i8);
908    pub fn sc_screenshot_configuration_release(config: *const c_void);
909
910    // Content type support (macOS 26.0+)
911    pub fn sc_screenshot_configuration_set_content_type(
912        config: *const c_void,
913        identifier: *const i8,
914    );
915    pub fn sc_screenshot_configuration_get_content_type(
916        config: *const c_void,
917        buffer: *mut i8,
918        buffer_size: usize,
919    ) -> bool;
920    pub fn sc_screenshot_configuration_get_supported_content_types_count() -> usize;
921    pub fn sc_screenshot_configuration_get_supported_content_type_at(
922        index: usize,
923        buffer: *mut i8,
924        buffer_size: usize,
925    ) -> bool;
926}
927
928// MARK: - SCScreenshotOutput (macOS 26.0+)
929extern "C" {
930    pub fn sc_screenshot_output_get_sdr_image(output: *const c_void) -> *const c_void;
931    pub fn sc_screenshot_output_get_hdr_image(output: *const c_void) -> *const c_void;
932    pub fn sc_screenshot_output_get_file_url(
933        output: *const c_void,
934        buffer: *mut i8,
935        buffer_size: isize,
936    ) -> bool;
937    pub fn sc_screenshot_output_release(output: *const c_void);
938
939    pub fn sc_screenshot_manager_capture_screenshot(
940        content_filter: *const c_void,
941        config: *const c_void,
942        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
943        user_data: *mut c_void,
944    );
945    pub fn sc_screenshot_manager_capture_screenshot_in_rect(
946        x: f64,
947        y: f64,
948        width: f64,
949        height: f64,
950        config: *const c_void,
951        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
952        user_data: *mut c_void,
953    );
954}
955
956// MARK: - SCStreamConfiguration additional properties
957extern "C" {
958    // macOS 15.0+ - showMouseClicks
959    pub fn sc_stream_configuration_set_shows_mouse_clicks(config: *const c_void, value: bool);
960    pub fn sc_stream_configuration_get_shows_mouse_clicks(config: *const c_void) -> bool;
961
962    // macOS 14.0+ - ignoreShadowsDisplay
963    pub fn sc_stream_configuration_set_ignores_shadows_display(config: *const c_void, value: bool);
964    pub fn sc_stream_configuration_get_ignores_shadows_display(config: *const c_void) -> bool;
965
966    // macOS 14.0+ - ignoreGlobalClipDisplay
967    pub fn sc_stream_configuration_set_ignore_global_clip_display(
968        config: *const c_void,
969        value: bool,
970    );
971    pub fn sc_stream_configuration_get_ignore_global_clip_display(config: *const c_void) -> bool;
972
973    // macOS 14.0+ - ignoreGlobalClipSingleWindow
974    pub fn sc_stream_configuration_set_ignore_global_clip_single_window(
975        config: *const c_void,
976        value: bool,
977    );
978    pub fn sc_stream_configuration_get_ignore_global_clip_single_window(
979        config: *const c_void,
980    ) -> bool;
981
982    // macOS 15.0+ - preset-based configuration
983    pub fn sc_stream_configuration_create_with_preset(preset: i32) -> *const c_void;
984}
985
986// MARK: - SCContentFilter additional properties
987extern "C" {
988    // macOS 14.0+ - style and pointPixelScale
989    pub fn sc_content_filter_get_style(filter: *const c_void) -> i32;
990    pub fn sc_content_filter_get_point_pixel_scale(filter: *const c_void) -> f32;
991    pub fn sc_content_filter_get_stream_type(filter: *const c_void) -> i32;
992
993    // macOS 14.2+ - includeMenuBar
994    pub fn sc_content_filter_set_include_menu_bar(filter: *const c_void, include: bool);
995    pub fn sc_content_filter_get_include_menu_bar(filter: *const c_void) -> bool;
996
997    // macOS 15.2+ - included content arrays
998    pub fn sc_content_filter_get_included_displays_count(filter: *const c_void) -> isize;
999    pub fn sc_content_filter_get_included_display_at(
1000        filter: *const c_void,
1001        index: isize,
1002    ) -> *const c_void;
1003    pub fn sc_content_filter_get_included_windows_count(filter: *const c_void) -> isize;
1004    pub fn sc_content_filter_get_included_window_at(
1005        filter: *const c_void,
1006        index: isize,
1007    ) -> *const c_void;
1008    pub fn sc_content_filter_get_included_applications_count(filter: *const c_void) -> isize;
1009    pub fn sc_content_filter_get_included_application_at(
1010        filter: *const c_void,
1011        index: isize,
1012    ) -> *const c_void;
1013}
1014
1015// MARK: - SCShareableContentInfo (macOS 14.0+)
1016extern "C" {
1017    pub fn sc_shareable_content_info_for_filter(filter: *const c_void) -> *const c_void;
1018    pub fn sc_shareable_content_info_get_style(info: *const c_void) -> i32;
1019    pub fn sc_shareable_content_info_get_point_pixel_scale(info: *const c_void) -> f32;
1020    pub fn sc_shareable_content_info_get_content_rect(
1021        info: *const c_void,
1022        x: *mut f64,
1023        y: *mut f64,
1024        width: *mut f64,
1025        height: *mut f64,
1026    );
1027    /// Get shareable content info rect (same as `sc_shareable_content_info_get_content_rect`)
1028    pub fn sc_shareable_content_info_get_content_rect_packed(
1029        info: *const c_void,
1030        x: *mut f64,
1031        y: *mut f64,
1032        width: *mut f64,
1033        height: *mut f64,
1034    );
1035    pub fn sc_shareable_content_info_retain(info: *const c_void) -> *const c_void;
1036    pub fn sc_shareable_content_info_release(info: *const c_void);
1037}
1038
1039// MARK: - SCRecordingOutput additional (macOS 15.0+)
1040extern "C" {
1041    pub fn sc_recording_output_configuration_set_output_file_type(
1042        config: *const c_void,
1043        file_type: i32,
1044    );
1045    pub fn sc_recording_output_configuration_get_output_file_type(config: *const c_void) -> i32;
1046    pub fn sc_recording_output_configuration_get_video_codec(config: *const c_void) -> i32;
1047    pub fn sc_recording_output_configuration_get_available_video_codecs_count(
1048        config: *const c_void,
1049    ) -> isize;
1050    pub fn sc_recording_output_configuration_get_available_video_codec_at(
1051        config: *const c_void,
1052        index: isize,
1053    ) -> i32;
1054    pub fn sc_recording_output_configuration_get_available_output_file_types_count(
1055        config: *const c_void,
1056    ) -> isize;
1057    pub fn sc_recording_output_configuration_get_available_output_file_type_at(
1058        config: *const c_void,
1059        index: isize,
1060    ) -> i32;
1061    pub fn sc_recording_output_create_with_delegate(
1062        config: *const c_void,
1063        started_callback: Option<extern "C" fn(*mut c_void)>,
1064        failed_callback: Option<extern "C" fn(*mut c_void, i32, *const i8)>,
1065        finished_callback: Option<extern "C" fn(*mut c_void)>,
1066        context: *mut c_void,
1067    ) -> *const c_void;
1068    pub fn sc_recording_output_get_recorded_duration(
1069        output: *const c_void,
1070        value: *mut i64,
1071        timescale: *mut i32,
1072    );
1073    pub fn sc_recording_output_get_recorded_file_size(output: *const c_void) -> i64;
1074}
1075
1076// MARK: - Audio Input Devices (AVFoundation)
1077extern "C" {
1078    /// Get the count of available audio input devices
1079    pub fn sc_audio_get_input_device_count() -> isize;
1080
1081    /// Get audio input device ID at index into buffer
1082    pub fn sc_audio_get_input_device_id(index: isize, buffer: *mut i8, buffer_size: isize) -> bool;
1083
1084    /// Get audio input device name at index into buffer
1085    pub fn sc_audio_get_input_device_name(
1086        index: isize,
1087        buffer: *mut i8,
1088        buffer_size: isize,
1089    ) -> bool;
1090
1091    /// Check if the device at index is the default audio input device
1092    pub fn sc_audio_is_default_input_device(index: isize) -> bool;
1093
1094    /// Get the default audio input device ID into buffer
1095    pub fn sc_audio_get_default_input_device_id(buffer: *mut i8, buffer_size: isize) -> bool;
1096
1097    /// Get the default audio input device name into buffer
1098    pub fn sc_audio_get_default_input_device_name(buffer: *mut i8, buffer_size: isize) -> bool;
1099}