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_process_id: 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_process_id) == 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    /// Async callback-based shareable content retrieval with options
121    pub fn sc_shareable_content_get_with_options(
122        exclude_desktop_windows: bool,
123        on_screen_windows_only: bool,
124        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
125        user_data: *mut c_void,
126    );
127
128    pub fn sc_shareable_content_get(
129        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
130        user_data: *mut c_void,
131    );
132    pub fn sc_shareable_content_get_current_process_displays(
133        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
134        user_data: *mut c_void,
135    );
136    pub fn sc_shareable_content_current_process_is_available() -> bool;
137    pub fn sc_shareable_content_get_below_window(
138        exclude_desktop_windows: bool,
139        reference_window: *const c_void,
140        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
141        user_data: *mut c_void,
142    );
143    pub fn sc_shareable_content_get_above_window(
144        exclude_desktop_windows: bool,
145        reference_window: *const c_void,
146        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
147        user_data: *mut c_void,
148    );
149    pub fn sc_shareable_content_retain(content: *const c_void) -> *const c_void;
150    pub fn sc_shareable_content_release(content: *const c_void);
151    pub fn sc_shareable_content_get_displays_count(content: *const c_void) -> isize;
152    pub fn sc_shareable_content_get_display_at(
153        content: *const c_void,
154        index: isize,
155    ) -> *const c_void;
156    pub fn sc_shareable_content_get_windows_count(content: *const c_void) -> isize;
157    pub fn sc_shareable_content_get_window_at(
158        content: *const c_void,
159        index: isize,
160    ) -> *const c_void;
161    pub fn sc_shareable_content_get_applications_count(content: *const c_void) -> isize;
162    pub fn sc_shareable_content_get_application_at(
163        content: *const c_void,
164        index: isize,
165    ) -> *const c_void;
166
167    // Batch retrieval functions (optimized FFI)
168    pub fn sc_shareable_content_get_displays_batch(
169        content: *const c_void,
170        buffer: *mut c_void, // Actually *mut FFIDisplayData
171        max_displays: isize,
172    ) -> isize;
173
174    pub fn sc_shareable_content_get_applications_batch(
175        content: *const c_void,
176        buffer: *mut c_void, // Actually *mut FFIApplicationData
177        max_apps: isize,
178        string_buffer: *mut i8,
179        string_buffer_size: isize,
180        string_buffer_used: *mut isize,
181    ) -> isize;
182
183    pub fn sc_shareable_content_get_windows_batch(
184        content: *const c_void,
185        buffer: *mut c_void, // Actually *mut FFIWindowData
186        max_windows: isize,
187        string_buffer: *mut i8,
188        string_buffer_size: isize,
189        string_buffer_used: *mut isize,
190    ) -> isize;
191}
192
193// MARK: - SCDisplay
194extern "C" {
195    pub fn sc_display_retain(display: *const c_void) -> *const c_void;
196    pub fn sc_display_release(display: *const c_void);
197    pub fn sc_display_get_display_id(display: *const c_void) -> u32;
198    pub fn sc_display_get_width(display: *const c_void) -> isize;
199    pub fn sc_display_get_height(display: *const c_void) -> isize;
200    pub fn sc_display_get_frame(
201        display: *const c_void,
202        x: *mut f64,
203        y: *mut f64,
204        width: *mut f64,
205        height: *mut f64,
206    );
207    /// Get display frame (same as `sc_display_get_frame`, kept for API compatibility)
208    pub fn sc_display_get_frame_packed(
209        display: *const c_void,
210        x: *mut f64,
211        y: *mut f64,
212        width: *mut f64,
213        height: *mut f64,
214    );
215}
216
217// MARK: - SCWindow
218extern "C" {
219    pub fn sc_window_retain(window: *const c_void) -> *const c_void;
220    pub fn sc_window_release(window: *const c_void);
221    pub fn sc_window_get_window_id(window: *const c_void) -> u32;
222    pub fn sc_window_get_frame(
223        window: *const c_void,
224        x: *mut f64,
225        y: *mut f64,
226        width: *mut f64,
227        height: *mut f64,
228    );
229    /// Get window frame (same as `sc_window_get_frame`, kept for API compatibility)
230    pub fn sc_window_get_frame_packed(
231        window: *const c_void,
232        x: *mut f64,
233        y: *mut f64,
234        width: *mut f64,
235        height: *mut f64,
236    );
237    pub fn sc_window_get_title(window: *const c_void, buffer: *mut i8, buffer_size: isize) -> bool;
238    /// Get window title as owned string (caller must free with `sc_free_string`)
239    pub fn sc_window_get_title_owned(window: *const c_void) -> *mut i8;
240    pub fn sc_window_get_window_layer(window: *const c_void) -> isize;
241    pub fn sc_window_is_on_screen(window: *const c_void) -> bool;
242    pub fn sc_window_get_owning_application(window: *const c_void) -> *const c_void;
243    pub fn sc_window_is_active(window: *const c_void) -> bool;
244}
245
246// MARK: - SCRunningApplication
247extern "C" {
248    pub fn sc_running_application_retain(app: *const c_void) -> *const c_void;
249    pub fn sc_running_application_release(app: *const c_void);
250    pub fn sc_running_application_get_bundle_identifier(
251        app: *const c_void,
252        buffer: *mut i8,
253        buffer_size: isize,
254    ) -> bool;
255    /// Get bundle identifier as owned string (caller must free with `sc_free_string`)
256    pub fn sc_running_application_get_bundle_identifier_owned(app: *const c_void) -> *mut i8;
257    pub fn sc_running_application_get_application_name(
258        app: *const c_void,
259        buffer: *mut i8,
260        buffer_size: isize,
261    ) -> bool;
262    /// Get application name as owned string (caller must free with `sc_free_string`)
263    pub fn sc_running_application_get_application_name_owned(app: *const c_void) -> *mut i8;
264    pub fn sc_running_application_get_process_id(app: *const c_void) -> i32;
265}
266
267// MARK: - String memory management
268extern "C" {
269    /// Free a string allocated by Swift (strdup)
270    pub fn sc_free_string(str: *mut i8);
271}
272
273// MARK: - SCStreamConfiguration
274extern "C" {
275    pub fn sc_stream_configuration_create() -> *const c_void;
276    pub fn sc_stream_configuration_copy(config: *const c_void) -> *const c_void;
277    pub fn sc_stream_configuration_retain(config: *const c_void) -> *const c_void;
278    pub fn sc_stream_configuration_release(config: *const c_void);
279
280    pub fn sc_stream_configuration_set_width(config: *const c_void, width: isize);
281    pub fn sc_stream_configuration_get_width(config: *const c_void) -> isize;
282
283    pub fn sc_stream_configuration_set_height(config: *const c_void, height: isize);
284    pub fn sc_stream_configuration_get_height(config: *const c_void) -> isize;
285
286    pub fn sc_stream_configuration_set_shows_cursor(config: *const c_void, shows_cursor: bool);
287    pub fn sc_stream_configuration_get_shows_cursor(config: *const c_void) -> bool;
288
289    pub fn sc_stream_configuration_set_scales_to_fit(config: *const c_void, scales_to_fit: bool);
290    pub fn sc_stream_configuration_get_scales_to_fit(config: *const c_void) -> bool;
291
292    pub fn sc_stream_configuration_set_captures_audio(config: *const c_void, captures_audio: bool);
293    pub fn sc_stream_configuration_get_captures_audio(config: *const c_void) -> bool;
294
295    pub fn sc_stream_configuration_set_sample_rate(config: *const c_void, sample_rate: isize);
296    pub fn sc_stream_configuration_get_sample_rate(config: *const c_void) -> isize;
297
298    pub fn sc_stream_configuration_set_channel_count(config: *const c_void, channel_count: isize);
299    pub fn sc_stream_configuration_get_channel_count(config: *const c_void) -> isize;
300
301    pub fn sc_stream_configuration_set_queue_depth(config: *const c_void, queue_depth: isize);
302    pub fn sc_stream_configuration_get_queue_depth(config: *const c_void) -> isize;
303
304    pub fn sc_stream_configuration_set_pixel_format(config: *const c_void, pixel_format: u32);
305    pub fn sc_stream_configuration_get_pixel_format(config: *const c_void) -> u32;
306
307    pub fn sc_stream_configuration_set_minimum_frame_interval(
308        config: *const c_void,
309        value: i64,
310        timescale: i32,
311        flags: u32,
312        epoch: i64,
313    );
314    pub fn sc_stream_configuration_get_minimum_frame_interval(
315        config: *const c_void,
316        value: *mut i64,
317        timescale: *mut i32,
318        flags: *mut u32,
319        epoch: *mut i64,
320    );
321
322    pub fn sc_stream_configuration_set_source_rect(
323        config: *const c_void,
324        x: f64,
325        y: f64,
326        width: f64,
327        height: f64,
328    );
329    pub fn sc_stream_configuration_get_source_rect(
330        config: *const c_void,
331        x: *mut f64,
332        y: *mut f64,
333        width: *mut f64,
334        height: *mut f64,
335    );
336
337    pub fn sc_stream_configuration_set_destination_rect(
338        config: *const c_void,
339        x: f64,
340        y: f64,
341        width: f64,
342        height: f64,
343    );
344    pub fn sc_stream_configuration_get_destination_rect(
345        config: *const c_void,
346        x: *mut f64,
347        y: *mut f64,
348        width: *mut f64,
349        height: *mut f64,
350    );
351
352    pub fn sc_stream_configuration_set_preserves_aspect_ratio(
353        config: *const c_void,
354        preserves_aspect_ratio: bool,
355    ) -> bool;
356    pub fn sc_stream_configuration_get_preserves_aspect_ratio(config: *const c_void) -> bool;
357
358    pub fn sc_stream_configuration_set_ignores_shadows_single_window(
359        config: *const c_void,
360        ignores_shadows: bool,
361    ) -> bool;
362    pub fn sc_stream_configuration_get_ignores_shadows_single_window(config: *const c_void)
363        -> bool;
364
365    pub fn sc_stream_configuration_set_should_be_opaque(
366        config: *const c_void,
367        should_be_opaque: bool,
368    ) -> bool;
369    pub fn sc_stream_configuration_get_should_be_opaque(config: *const c_void) -> bool;
370
371    pub fn sc_stream_configuration_set_includes_child_windows(
372        config: *const c_void,
373        includes_child_windows: bool,
374    ) -> bool;
375    pub fn sc_stream_configuration_get_includes_child_windows(config: *const c_void) -> bool;
376
377    pub fn sc_stream_configuration_set_presenter_overlay_privacy_alert_setting(
378        config: *const c_void,
379        setting: i32,
380    ) -> bool;
381    pub fn sc_stream_configuration_get_presenter_overlay_privacy_alert_setting(
382        config: *const c_void,
383        setting: *mut i32,
384    ) -> bool;
385
386    pub fn sc_stream_configuration_set_background_color(
387        config: *const c_void,
388        r: f32,
389        g: f32,
390        b: f32,
391        a: f32,
392    );
393    pub fn sc_stream_configuration_get_background_color(
394        config: *const c_void,
395        r: *mut f32,
396        g: *mut f32,
397        b: *mut f32,
398        a: *mut f32,
399    ) -> bool;
400    pub fn sc_stream_configuration_set_color_space_name(config: *const c_void, name: *const i8);
401    pub fn sc_stream_configuration_get_color_space_name(
402        config: *const c_void,
403        buffer: *mut i8,
404        buffer_size: isize,
405    ) -> bool;
406    pub fn sc_stream_configuration_set_color_matrix(config: *const c_void, matrix: *const i8);
407    pub fn sc_stream_configuration_get_color_matrix(
408        config: *const c_void,
409        buffer: *mut i8,
410        buffer_size: isize,
411    ) -> bool;
412
413    // macOS 14.0+ - capture resolution type
414    pub fn sc_stream_configuration_set_capture_resolution_type(
415        config: *const c_void,
416        value: i32,
417    ) -> bool;
418    pub fn sc_stream_configuration_get_capture_resolution_type(
419        config: *const c_void,
420        value: *mut i32,
421    ) -> bool;
422
423    pub fn sc_stream_configuration_set_ignores_shadow_display_configuration(
424        config: *const c_void,
425        ignores_shadow: bool,
426    ) -> bool;
427    pub fn sc_stream_configuration_get_ignores_shadow_display_configuration(
428        config: *const c_void,
429    ) -> bool;
430
431    pub fn sc_stream_configuration_set_preserve_aspect_ratio(
432        config: *const c_void,
433        preserve: bool,
434    ) -> bool;
435    pub fn sc_stream_configuration_get_preserve_aspect_ratio(config: *const c_void) -> bool;
436
437    pub fn sc_stream_configuration_set_captures_shadows_only(
438        config: *const c_void,
439        captures_shadows_only: bool,
440    ) -> bool;
441    pub fn sc_stream_configuration_get_captures_shadows_only(config: *const c_void) -> bool;
442
443    pub fn sc_stream_configuration_set_captures_microphone(
444        config: *const c_void,
445        captures_microphone: bool,
446    ) -> bool;
447    pub fn sc_stream_configuration_get_captures_microphone(config: *const c_void) -> bool;
448
449    pub fn sc_stream_configuration_set_excludes_current_process_audio(
450        config: *const c_void,
451        excludes: bool,
452    );
453    pub fn sc_stream_configuration_get_excludes_current_process_audio(
454        config: *const c_void,
455    ) -> bool;
456
457    pub fn sc_stream_configuration_set_microphone_capture_device_id(
458        config: *const c_void,
459        device_id: *const i8,
460    ) -> bool;
461    pub fn sc_stream_configuration_get_microphone_capture_device_id(
462        config: *const c_void,
463        buffer: *mut i8,
464        buffer_size: isize,
465    ) -> bool;
466
467    pub fn sc_stream_configuration_set_stream_name(config: *const c_void, name: *const i8) -> bool;
468    pub fn sc_stream_configuration_get_stream_name(
469        config: *const c_void,
470        buffer: *mut i8,
471        buffer_size: isize,
472    ) -> bool;
473
474    pub fn sc_stream_configuration_set_capture_dynamic_range(
475        config: *const c_void,
476        value: i32,
477    ) -> bool;
478    pub fn sc_stream_configuration_get_capture_dynamic_range(
479        config: *const c_void,
480        value: *mut i32,
481    ) -> bool;
482}
483
484// MARK: - SCContentFilter
485extern "C" {
486    pub fn sc_content_filter_create_with_desktop_independent_window(
487        window: *const c_void,
488    ) -> *const c_void;
489    pub fn sc_content_filter_create_with_display_excluding_windows(
490        display: *const c_void,
491        windows: *const *const c_void,
492        windows_count: isize,
493    ) -> *const c_void;
494    pub fn sc_content_filter_create_with_display_including_windows(
495        display: *const c_void,
496        windows: *const *const c_void,
497        windows_count: isize,
498    ) -> *const c_void;
499    pub fn sc_content_filter_create_with_display_including_applications_excepting_windows(
500        display: *const c_void,
501        apps: *const *const c_void,
502        apps_count: isize,
503        windows: *const *const c_void,
504        windows_count: isize,
505    ) -> *const c_void;
506    pub fn sc_content_filter_create_with_display_excluding_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_retain(filter: *const c_void) -> *const c_void;
514    pub fn sc_content_filter_release(filter: *const c_void);
515    pub fn sc_content_filter_get_content_rect(
516        filter: *const c_void,
517        x: *mut f64,
518        y: *mut f64,
519        width: *mut f64,
520        height: *mut f64,
521    );
522    /// Get content filter content rect (same as `sc_content_filter_get_content_rect`)
523    pub fn sc_content_filter_get_content_rect_packed(
524        filter: *const c_void,
525        x: *mut f64,
526        y: *mut f64,
527        width: *mut f64,
528        height: *mut f64,
529    );
530}
531
532// MARK: - SCStream
533extern "C" {
534    pub fn sc_stream_create(
535        filter: *const c_void,
536        config: *const c_void,
537        context: *mut c_void,
538        error_callback: extern "C" fn(*mut c_void, i32, *const i8),
539        sample_callback: extern "C" fn(*mut c_void, *const c_void, i32),
540        context_retain: extern "C" fn(*mut c_void),
541        context_release: extern "C" fn(*mut c_void),
542    ) -> *const c_void;
543    pub fn sc_stream_add_stream_output(stream: *const c_void, output_type: i32) -> bool;
544    pub fn sc_stream_add_stream_output_with_queue(
545        stream: *const c_void,
546        output_type: i32,
547        dispatch_queue: *const c_void,
548    ) -> bool;
549    pub fn sc_stream_remove_stream_output(stream: *const c_void, output_type: i32) -> bool;
550    pub fn sc_stream_set_delegate_event_callback(
551        stream: *const c_void,
552        callback: extern "C" fn(*mut c_void, i32),
553    ) -> bool;
554    pub fn sc_stream_start_capture(
555        stream: *const c_void,
556        context: *mut c_void,
557        callback: extern "C" fn(*mut c_void, bool, *const i8),
558    );
559    pub fn sc_stream_stop_capture(
560        stream: *const c_void,
561        context: *mut c_void,
562        callback: extern "C" fn(*mut c_void, bool, *const i8),
563    );
564    pub fn sc_stream_update_configuration(
565        stream: *const c_void,
566        config: *const c_void,
567        context: *mut c_void,
568        callback: extern "C" fn(*mut c_void, bool, *const i8),
569    );
570    pub fn sc_stream_update_content_filter(
571        stream: *const c_void,
572        filter: *const c_void,
573        context: *mut c_void,
574        callback: extern "C" fn(*mut c_void, bool, *const i8),
575    );
576    pub fn sc_stream_add_recording_output(
577        stream: *const c_void,
578        recording_output: *const c_void,
579        callback: extern "C" fn(*mut c_void, bool, *const i8),
580        context: *mut c_void,
581    );
582    pub fn sc_stream_remove_recording_output(
583        stream: *const c_void,
584        recording_output: *const c_void,
585        callback: extern "C" fn(*mut c_void, bool, *const i8),
586        context: *mut c_void,
587    );
588    pub fn sc_stream_retain(stream: *const c_void) -> *const c_void;
589    pub fn sc_stream_release(stream: *const c_void);
590
591    // macOS 13.0+ - synchronizationClock
592    pub fn sc_stream_get_synchronization_clock(stream: *const c_void) -> *const c_void;
593}
594
595// MARK: - Dispatch Queue
596extern "C" {
597    pub fn dispatch_queue_release(queue: *const c_void);
598    pub fn dispatch_queue_retain(queue: *const c_void) -> *const c_void;
599}
600
601// MARK: - IOSurface
602extern "C" {
603    pub fn cv_pixel_buffer_get_iosurface(pixel_buffer: *const c_void) -> *const c_void;
604    pub fn cv_pixel_buffer_is_backed_by_iosurface(pixel_buffer: *const c_void) -> bool;
605    pub fn iosurface_get_width(iosurface: *const c_void) -> isize;
606    pub fn iosurface_get_height(iosurface: *const c_void) -> isize;
607    pub fn iosurface_get_bytes_per_row(iosurface: *const c_void) -> isize;
608    pub fn iosurface_get_pixel_format(iosurface: *const c_void) -> u32;
609    pub fn iosurface_get_base_address(iosurface: *const c_void) -> *mut u8;
610    pub fn iosurface_lock(iosurface: *const c_void, options: u32) -> i32;
611    pub fn iosurface_unlock(iosurface: *const c_void, options: u32) -> i32;
612    pub fn iosurface_is_in_use(iosurface: *const c_void) -> bool;
613    pub fn iosurface_release(iosurface: *const c_void);
614
615    // Plane functions (for multi-planar formats like YCbCr 420)
616    pub fn iosurface_get_plane_count(iosurface: *const c_void) -> isize;
617    pub fn iosurface_get_width_of_plane(iosurface: *const c_void, plane: isize) -> isize;
618    pub fn iosurface_get_height_of_plane(iosurface: *const c_void, plane: isize) -> isize;
619    pub fn iosurface_get_bytes_per_row_of_plane(iosurface: *const c_void, plane: isize) -> isize;
620
621    // IOSurface creation (for testing)
622    pub fn io_surface_create(
623        width: usize,
624        height: usize,
625        pixel_format: u32,
626        bytes_per_element: usize,
627        surface_out: *mut *mut c_void,
628    ) -> i32;
629
630    // IOSurface creation with full properties (for multi-planar formats)
631    pub fn io_surface_create_with_properties(
632        width: usize,
633        height: usize,
634        pixel_format: u32,
635        bytes_per_element: usize,
636        bytes_per_row: usize,
637        alloc_size: usize,
638        plane_count: usize,
639        plane_widths: *const usize,
640        plane_heights: *const usize,
641        plane_bytes_per_row: *const usize,
642        plane_bytes_per_element: *const usize,
643        plane_offsets: *const usize,
644        plane_sizes: *const usize,
645        surface_out: *mut *mut c_void,
646    ) -> i32;
647}
648
649// MARK: - SCContentSharingPicker (macOS 14.0+)
650extern "C" {
651    pub fn sc_content_sharing_picker_is_available() -> bool;
652    pub fn sc_content_sharing_picker_configuration_create() -> *const c_void;
653    pub fn sc_content_sharing_picker_configuration_set_allowed_picker_modes(
654        config: *const c_void,
655        modes: *const i32,
656        count: usize,
657    );
658    pub fn sc_content_sharing_picker_configuration_get_allowed_picker_modes_mask(
659        config: *const c_void,
660    ) -> u64;
661    pub fn sc_content_sharing_picker_configuration_set_allows_changing_selected_content(
662        config: *const c_void,
663        allows: bool,
664    );
665    pub fn sc_content_sharing_picker_configuration_get_allows_changing_selected_content(
666        config: *const c_void,
667    ) -> bool;
668    pub fn sc_content_sharing_picker_configuration_set_excluded_bundle_ids(
669        config: *const c_void,
670        bundle_ids: *const *const i8,
671        count: usize,
672    );
673    pub fn sc_content_sharing_picker_configuration_get_excluded_bundle_ids_count(
674        config: *const c_void,
675    ) -> usize;
676    pub fn sc_content_sharing_picker_configuration_get_excluded_bundle_id_at(
677        config: *const c_void,
678        index: usize,
679        buffer: *mut i8,
680        buffer_size: usize,
681    ) -> bool;
682    pub fn sc_content_sharing_picker_configuration_set_excluded_window_ids(
683        config: *const c_void,
684        window_ids: *const u32,
685        count: usize,
686    );
687    pub fn sc_content_sharing_picker_configuration_get_excluded_window_ids_count(
688        config: *const c_void,
689    ) -> usize;
690    pub fn sc_content_sharing_picker_configuration_get_excluded_window_id_at(
691        config: *const c_void,
692        index: usize,
693    ) -> u32;
694    pub fn sc_content_sharing_picker_configuration_retain(config: *const c_void) -> *const c_void;
695    pub fn sc_content_sharing_picker_configuration_copy(config: *const c_void) -> *const c_void;
696    pub fn sc_content_sharing_picker_configuration_release(config: *const c_void);
697
698    // Picker maximum stream count
699    pub fn sc_content_sharing_picker_set_maximum_stream_count(count: usize);
700    pub fn sc_content_sharing_picker_get_maximum_stream_count() -> usize;
701
702    /// Returns a fresh `SCContentSharingPickerConfiguration` initialised with
703    /// the system's `defaultConfiguration` baseline. Caller owns the returned
704    /// configuration and must release it via the standard
705    /// `sc_content_sharing_picker_configuration_release`.
706    pub fn sc_content_sharing_picker_create_default_configuration() -> *const c_void;
707
708    /// Read whether the shared content-sharing picker is currently active.
709    pub fn sc_content_sharing_picker_get_active() -> bool;
710    /// Mark the shared content-sharing picker active or inactive.
711    pub fn sc_content_sharing_picker_set_active(active: bool);
712
713    /// Assign the picker's process-wide `defaultConfiguration`.
714    /// Returns false without enqueueing work when called off the main thread.
715    pub fn sc_content_sharing_picker_set_default_configuration(config: *const c_void) -> bool;
716    /// Assign (or clear, when `config` is null) the per-stream configuration.
717    /// Returns false without enqueueing work when called off the main thread.
718    pub fn sc_content_sharing_picker_set_configuration_for_stream(
719        config: *const c_void,
720        stream: *const c_void,
721    ) -> bool;
722
723    /// Register a repeating observer. Returns a non-zero token, or 0 on failure.
724    ///
725    /// `callback` receives `(event, result_ptr, message, stream, user_data)`
726    /// where the event is 1 = updated (`result_ptr` non-null), 0 = cancelled,
727    /// -1 = start failed (message non-null). `stream` is a non-owning identity
728    /// pointer when the event targets an existing stream. `context_release` is
729    /// invoked exactly once after detachment.
730    pub fn sc_content_sharing_picker_add_observer(
731        callback: extern "C" fn(i32, *const c_void, *const i8, *const c_void, *mut c_void),
732        context_release: extern "C" fn(*mut c_void),
733        user_data: *mut c_void,
734    ) -> i64;
735    /// Remove a repeating observer by token. Returns true if it was live.
736    pub fn sc_content_sharing_picker_remove_observer(token: i64) -> bool;
737    /// Remove every repeating observer; returns how many were removed.
738    pub fn sc_content_sharing_picker_remove_all_observers() -> usize;
739
740    /// Present the picker. `style` < 0 presents without a content-style hint.
741    pub fn sc_content_sharing_picker_present(style: i32);
742    /// Present the picker targeting an existing stream.
743    pub fn sc_content_sharing_picker_present_for_stream(stream: *const c_void, style: i32);
744    /// Deactivate the picker and undo any activation-policy promotion.
745    pub fn sc_content_sharing_picker_deactivate();
746
747    pub fn sc_content_sharing_picker_show(
748        config: *const c_void,
749        callback: extern "C" fn(i32, *const c_void, *mut c_void),
750        user_data: *mut c_void,
751    );
752    pub fn sc_content_sharing_picker_show_with_result(
753        config: *const c_void,
754        callback: extern "C" fn(i32, *const c_void, *mut c_void),
755        user_data: *mut c_void,
756    );
757    pub fn sc_content_sharing_picker_show_for_stream(
758        config: *const c_void,
759        stream: *const c_void,
760        callback: extern "C" fn(i32, *const c_void, *mut c_void),
761        user_data: *mut c_void,
762    );
763    pub fn sc_content_sharing_picker_show_using_style(
764        config: *const c_void,
765        style: i32,
766        callback: extern "C" fn(i32, *const c_void, *mut c_void),
767        user_data: *mut c_void,
768    );
769    pub fn sc_content_sharing_picker_show_for_stream_using_style(
770        config: *const c_void,
771        stream: *const c_void,
772        style: i32,
773        callback: extern "C" fn(i32, *const c_void, *mut c_void),
774        user_data: *mut c_void,
775    );
776    pub fn sc_picker_result_get_filter(result: *const c_void) -> *const c_void;
777    pub fn sc_picker_result_get_content_rect(
778        result: *const c_void,
779        x: *mut f64,
780        y: *mut f64,
781        width: *mut f64,
782        height: *mut f64,
783    );
784    pub fn sc_picker_result_get_scale(result: *const c_void) -> f64;
785    pub fn sc_picker_result_get_windows_count(result: *const c_void) -> usize;
786    pub fn sc_picker_result_get_window_at(result: *const c_void, index: usize) -> *const c_void;
787    pub fn sc_picker_result_get_displays_count(result: *const c_void) -> usize;
788    pub fn sc_picker_result_get_display_at(result: *const c_void, index: usize) -> *const c_void;
789    pub fn sc_picker_result_get_applications_count(result: *const c_void) -> usize;
790    pub fn sc_picker_result_get_application_at(
791        result: *const c_void,
792        index: usize,
793    ) -> *const c_void;
794    pub fn sc_picker_result_release(result: *const c_void);
795}
796
797// MARK: - SCRecordingOutput (macOS 15.0+)
798extern "C" {
799    pub fn sc_recording_output_is_available() -> bool;
800    pub fn sc_recording_output_configuration_create() -> *const c_void;
801    pub fn sc_recording_output_configuration_copy(config: *const c_void) -> *const c_void;
802    pub fn sc_recording_output_configuration_set_output_url(config: *const c_void, path: *const i8);
803    pub fn sc_recording_output_configuration_get_output_url(
804        config: *const c_void,
805        buffer: *mut i8,
806        buffer_size: isize,
807    ) -> bool;
808    pub fn sc_recording_output_configuration_get_output_path_owned(
809        config: *const c_void,
810    ) -> *mut i8;
811    pub fn sc_recording_output_configuration_set_video_codec(config: *const c_void, codec: i32);
812    pub fn sc_recording_output_configuration_retain(config: *const c_void) -> *const c_void;
813    pub fn sc_recording_output_configuration_release(config: *const c_void);
814    pub fn sc_recording_output_create(config: *const c_void) -> *const c_void;
815    pub fn sc_recording_output_retain(output: *const c_void) -> *const c_void;
816    pub fn sc_recording_output_release(output: *const c_void);
817}
818
819// MARK: - SCScreenshotManager (macOS 14.0+)
820extern "C" {
821    pub fn sc_screenshot_manager_capture_image(
822        content_filter: *const c_void,
823        config: *const c_void,
824        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
825        user_data: *mut c_void,
826    );
827    pub fn sc_screenshot_manager_capture_sample_buffer(
828        content_filter: *const c_void,
829        config: *const c_void,
830        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
831        user_data: *mut c_void,
832    );
833    pub fn sc_screenshot_manager_capture_image_in_rect(
834        x: f64,
835        y: f64,
836        width: f64,
837        height: f64,
838        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
839        user_data: *mut c_void,
840    );
841    pub fn cgimage_get_width(image: *const c_void) -> usize;
842    pub fn cgimage_get_height(image: *const c_void) -> usize;
843    pub fn cgimage_get_data(
844        image: *const c_void,
845        out_ptr: *mut *const u8,
846        out_length: *mut usize,
847    ) -> bool;
848    pub fn cgimage_free_data(ptr: *mut u8);
849    /// Render the `CGImage`'s RGBA bytes directly into a caller-owned buffer.
850    /// Returns the number of bytes written (= width*height*4) on success, or 0
851    /// on failure. Replaces the legacy `cgimage_get_data` + `cgimage_free_data`
852    /// pair which made an extra Swift-side malloc + memcpy before handing the
853    /// buffer back; this single-call form lets Rust own the allocation and
854    /// removes one full RGBA-sized memcpy from the screenshot decode path.
855    pub fn cgimage_render_rgba_into(
856        image: *const c_void,
857        dest: *mut u8,
858        dest_capacity: usize,
859    ) -> usize;
860    /// Render the `CGImage` directly as **BGRA** (the source pixel layout for
861    /// every `ScreenCaptureKit`-produced `CGImage`). Saves the per-pixel
862    /// channel-swap that `cgimage_render_rgba_into` forces — measured at
863    /// ~20 ms per 4K screenshot. Use when the consumer (Metal, wgpu, ffmpeg)
864    /// accepts BGRA natively.
865    pub fn cgimage_render_bgra_into(
866        image: *const c_void,
867        dest: *mut u8,
868        dest_capacity: usize,
869    ) -> usize;
870    /// Strided variant of [`cgimage_render_rgba_into`]: renders RGBA bytes
871    /// using a caller-specified row stride (`dest_bytes_per_row`) instead of
872    /// the tightly-packed `width * 4`. Lets consumers with row-aligned/padded
873    /// buffers (GPU upload, wgpu) avoid an extra repack. Returns the number of
874    /// bytes spanned (`height * dest_bytes_per_row`) on success, or 0 on
875    /// failure (stride too small or buffer too small).
876    pub fn cgimage_render_rgba_into_strided(
877        image: *const c_void,
878        dest: *mut u8,
879        dest_capacity: usize,
880        dest_bytes_per_row: usize,
881    ) -> usize;
882    /// Strided **BGRA** variant. See [`cgimage_render_bgra_into`] and
883    /// [`cgimage_render_rgba_into_strided`].
884    pub fn cgimage_render_bgra_into_strided(
885        image: *const c_void,
886        dest: *mut u8,
887        dest_capacity: usize,
888        dest_bytes_per_row: usize,
889    ) -> usize;
890    pub fn cgimage_release(image: *const c_void);
891    pub fn cgimage_save_png(image: *const c_void, path: *const i8) -> bool;
892    pub fn cgimage_save_to_file(
893        image: *const c_void,
894        path: *const i8,
895        format: i32,
896        quality: f32,
897    ) -> bool;
898}
899
900// MARK: - SCScreenshotConfiguration (macOS 26.0+)
901extern "C" {
902    pub fn sc_screenshot_configuration_create() -> *const c_void;
903    pub fn sc_screenshot_configuration_set_width(config: *const c_void, width: isize);
904    pub fn sc_screenshot_configuration_set_height(config: *const c_void, height: isize);
905    pub fn sc_screenshot_configuration_set_shows_cursor(config: *const c_void, shows_cursor: bool);
906    pub fn sc_screenshot_configuration_set_source_rect(
907        config: *const c_void,
908        x: f64,
909        y: f64,
910        width: f64,
911        height: f64,
912    );
913    pub fn sc_screenshot_configuration_set_destination_rect(
914        config: *const c_void,
915        x: f64,
916        y: f64,
917        width: f64,
918        height: f64,
919    );
920    pub fn sc_screenshot_configuration_set_ignore_shadows(
921        config: *const c_void,
922        ignore_shadows: bool,
923    );
924    pub fn sc_screenshot_configuration_set_ignore_clipping(
925        config: *const c_void,
926        ignore_clipping: bool,
927    );
928    pub fn sc_screenshot_configuration_set_include_child_windows(
929        config: *const c_void,
930        include_child_windows: bool,
931    );
932    pub fn sc_screenshot_configuration_set_display_intent(
933        config: *const c_void,
934        display_intent: i32,
935    ) -> bool;
936    pub fn sc_screenshot_configuration_set_dynamic_range(
937        config: *const c_void,
938        dynamic_range: i32,
939    ) -> bool;
940    pub fn sc_screenshot_configuration_set_file_url(config: *const c_void, path: *const i8);
941    pub fn sc_screenshot_configuration_clear_file_url(config: *const c_void);
942    pub fn sc_screenshot_configuration_get_width(config: *const c_void) -> isize;
943    pub fn sc_screenshot_configuration_get_height(config: *const c_void) -> isize;
944    pub fn sc_screenshot_configuration_get_shows_cursor(config: *const c_void) -> bool;
945    pub fn sc_screenshot_configuration_get_source_rect(
946        config: *const c_void,
947        x: *mut f64,
948        y: *mut f64,
949        width: *mut f64,
950        height: *mut f64,
951    );
952    pub fn sc_screenshot_configuration_get_destination_rect(
953        config: *const c_void,
954        x: *mut f64,
955        y: *mut f64,
956        width: *mut f64,
957        height: *mut f64,
958    );
959    pub fn sc_screenshot_configuration_get_ignore_shadows(config: *const c_void) -> bool;
960    pub fn sc_screenshot_configuration_get_ignore_clipping(config: *const c_void) -> bool;
961    pub fn sc_screenshot_configuration_get_include_child_windows(config: *const c_void) -> bool;
962    pub fn sc_screenshot_configuration_get_display_intent(
963        config: *const c_void,
964        value: *mut i32,
965    ) -> bool;
966    pub fn sc_screenshot_configuration_get_dynamic_range(
967        config: *const c_void,
968        value: *mut i32,
969    ) -> bool;
970    pub fn sc_screenshot_configuration_get_file_path_owned(config: *const c_void) -> *mut i8;
971    pub fn sc_screenshot_configuration_release(config: *const c_void);
972
973    // Content type support (macOS 26.0+)
974    pub fn sc_screenshot_configuration_set_content_type(
975        config: *const c_void,
976        identifier: *const i8,
977    );
978    pub fn sc_screenshot_configuration_get_content_type(
979        config: *const c_void,
980        buffer: *mut i8,
981        buffer_size: usize,
982    ) -> bool;
983    pub fn sc_screenshot_configuration_get_supported_content_types_count() -> usize;
984    pub fn sc_screenshot_configuration_get_supported_content_type_at(
985        index: usize,
986        buffer: *mut i8,
987        buffer_size: usize,
988    ) -> bool;
989}
990
991// MARK: - SCScreenshotOutput (macOS 26.0+)
992extern "C" {
993    pub fn sc_screenshot_output_get_sdr_image(output: *const c_void) -> *const c_void;
994    pub fn sc_screenshot_output_get_hdr_image(output: *const c_void) -> *const c_void;
995    pub fn sc_screenshot_output_get_file_url(
996        output: *const c_void,
997        buffer: *mut i8,
998        buffer_size: isize,
999    ) -> bool;
1000    pub fn sc_screenshot_output_get_file_path_owned(output: *const c_void) -> *mut i8;
1001    pub fn sc_screenshot_output_release(output: *const c_void);
1002
1003    pub fn sc_screenshot_manager_capture_screenshot(
1004        content_filter: *const c_void,
1005        config: *const c_void,
1006        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
1007        user_data: *mut c_void,
1008    );
1009    pub fn sc_screenshot_manager_capture_screenshot_in_rect(
1010        x: f64,
1011        y: f64,
1012        width: f64,
1013        height: f64,
1014        config: *const c_void,
1015        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
1016        user_data: *mut c_void,
1017    );
1018}
1019
1020// MARK: - SCStreamConfiguration additional properties
1021extern "C" {
1022    // macOS 15.0+ - showMouseClicks
1023    pub fn sc_stream_configuration_set_shows_mouse_clicks(
1024        config: *const c_void,
1025        value: bool,
1026    ) -> bool;
1027    pub fn sc_stream_configuration_get_shows_mouse_clicks(config: *const c_void) -> bool;
1028
1029    // macOS 14.0+ - ignoreShadowsDisplay
1030    pub fn sc_stream_configuration_set_ignores_shadows_display(
1031        config: *const c_void,
1032        value: bool,
1033    ) -> bool;
1034    pub fn sc_stream_configuration_get_ignores_shadows_display(config: *const c_void) -> bool;
1035
1036    // macOS 14.0+ - ignoreGlobalClipDisplay
1037    pub fn sc_stream_configuration_set_ignore_global_clip_display(
1038        config: *const c_void,
1039        value: bool,
1040    ) -> bool;
1041    pub fn sc_stream_configuration_get_ignore_global_clip_display(config: *const c_void) -> bool;
1042
1043    // macOS 14.0+ - ignoreGlobalClipSingleWindow
1044    pub fn sc_stream_configuration_set_ignore_global_clip_single_window(
1045        config: *const c_void,
1046        value: bool,
1047    ) -> bool;
1048    pub fn sc_stream_configuration_get_ignore_global_clip_single_window(
1049        config: *const c_void,
1050    ) -> bool;
1051
1052    // macOS 15.0+ - preset-based configuration
1053    pub fn sc_stream_configuration_create_with_preset(preset: i32) -> *const c_void;
1054}
1055
1056// MARK: - SCContentFilter additional properties
1057extern "C" {
1058    // macOS 14.0+ - style and pointPixelScale
1059    pub fn sc_content_filter_get_style(filter: *const c_void, value: *mut i32) -> bool;
1060    pub fn sc_content_filter_get_point_pixel_scale(filter: *const c_void) -> f32;
1061    pub fn sc_content_filter_get_stream_type(filter: *const c_void, value: *mut i32) -> bool;
1062
1063    // macOS 14.2+ - includeMenuBar
1064    pub fn sc_content_filter_set_include_menu_bar(filter: *const c_void, include: bool) -> bool;
1065    pub fn sc_content_filter_get_include_menu_bar(filter: *const c_void) -> bool;
1066
1067    // macOS 15.2+ - included content arrays
1068    pub fn sc_content_filter_get_included_displays_count(filter: *const c_void) -> isize;
1069    pub fn sc_content_filter_get_included_display_at(
1070        filter: *const c_void,
1071        index: isize,
1072    ) -> *const c_void;
1073    pub fn sc_content_filter_get_included_windows_count(filter: *const c_void) -> isize;
1074    pub fn sc_content_filter_get_included_window_at(
1075        filter: *const c_void,
1076        index: isize,
1077    ) -> *const c_void;
1078    pub fn sc_content_filter_get_included_applications_count(filter: *const c_void) -> isize;
1079    pub fn sc_content_filter_get_included_application_at(
1080        filter: *const c_void,
1081        index: isize,
1082    ) -> *const c_void;
1083}
1084
1085// MARK: - SCShareableContentInfo (macOS 14.0+)
1086extern "C" {
1087    pub fn sc_shareable_content_info_for_filter(filter: *const c_void) -> *const c_void;
1088    pub fn sc_shareable_content_info_get_style(info: *const c_void, value: *mut i32) -> bool;
1089    pub fn sc_shareable_content_info_get_point_pixel_scale(info: *const c_void) -> f32;
1090    pub fn sc_shareable_content_info_get_content_rect(
1091        info: *const c_void,
1092        x: *mut f64,
1093        y: *mut f64,
1094        width: *mut f64,
1095        height: *mut f64,
1096    );
1097    /// Get shareable content info rect (same as `sc_shareable_content_info_get_content_rect`)
1098    pub fn sc_shareable_content_info_get_content_rect_packed(
1099        info: *const c_void,
1100        x: *mut f64,
1101        y: *mut f64,
1102        width: *mut f64,
1103        height: *mut f64,
1104    );
1105    pub fn sc_shareable_content_info_retain(info: *const c_void) -> *const c_void;
1106    pub fn sc_shareable_content_info_release(info: *const c_void);
1107}
1108
1109// MARK: - SCRecordingOutput additional (macOS 15.0+)
1110extern "C" {
1111    pub fn sc_recording_output_configuration_set_output_file_type(
1112        config: *const c_void,
1113        file_type: i32,
1114    );
1115    pub fn sc_recording_output_configuration_set_output_file_type_identifier(
1116        config: *const c_void,
1117        identifier: *const i8,
1118    );
1119    pub fn sc_recording_output_configuration_get_output_file_type_identifier_owned(
1120        config: *const c_void,
1121    ) -> *mut i8;
1122    pub fn sc_recording_output_configuration_get_output_file_type(config: *const c_void) -> i32;
1123    pub fn sc_recording_output_configuration_get_video_codec(config: *const c_void) -> i32;
1124    pub fn sc_recording_output_configuration_set_video_codec_identifier(
1125        config: *const c_void,
1126        identifier: *const i8,
1127    );
1128    pub fn sc_recording_output_configuration_get_video_codec_identifier_owned(
1129        config: *const c_void,
1130    ) -> *mut i8;
1131    pub fn sc_recording_output_configuration_get_available_video_codecs_count(
1132        config: *const c_void,
1133    ) -> isize;
1134    pub fn sc_recording_output_configuration_get_available_video_codec_at(
1135        config: *const c_void,
1136        index: isize,
1137    ) -> i32;
1138    pub fn sc_recording_output_configuration_get_available_video_codec_identifier_at_owned(
1139        config: *const c_void,
1140        index: isize,
1141    ) -> *mut i8;
1142    pub fn sc_recording_output_configuration_get_available_output_file_types_count(
1143        config: *const c_void,
1144    ) -> isize;
1145    pub fn sc_recording_output_configuration_get_available_output_file_type_at(
1146        config: *const c_void,
1147        index: isize,
1148    ) -> i32;
1149    pub fn sc_recording_output_configuration_get_available_output_file_type_identifier_at_owned(
1150        config: *const c_void,
1151        index: isize,
1152    ) -> *mut i8;
1153    pub fn sc_recording_output_create_with_delegate(
1154        config: *const c_void,
1155        started_callback: Option<extern "C" fn(*mut c_void)>,
1156        failed_callback: Option<extern "C" fn(*mut c_void, i32, *const i8)>,
1157        finished_callback: Option<extern "C" fn(*mut c_void)>,
1158        context_release: Option<extern "C" fn(*mut c_void)>,
1159        context: *mut c_void,
1160    ) -> *const c_void;
1161    pub fn sc_recording_output_get_recorded_duration(
1162        output: *const c_void,
1163        value: *mut i64,
1164        timescale: *mut i32,
1165    );
1166    pub fn sc_recording_output_get_recorded_file_size(output: *const c_void) -> i64;
1167    pub fn sc_recording_output_wait_until_terminal(
1168        output: *const c_void,
1169        context: *mut c_void,
1170        callback: extern "C" fn(*mut c_void, bool, *const i8),
1171    );
1172}
1173
1174// MARK: - Audio Input Devices (AVFoundation)
1175extern "C" {
1176    pub fn sc_audio_input_devices_snapshot_create() -> *const c_void;
1177    pub fn sc_audio_input_devices_snapshot_release(snapshot: *const c_void);
1178    pub fn sc_audio_input_devices_snapshot_count(snapshot: *const c_void) -> isize;
1179    pub fn sc_audio_input_devices_snapshot_default_index(snapshot: *const c_void) -> isize;
1180    pub fn sc_audio_input_devices_snapshot_id_owned(
1181        snapshot: *const c_void,
1182        index: isize,
1183    ) -> *mut i8;
1184    pub fn sc_audio_input_devices_snapshot_name_owned(
1185        snapshot: *const c_void,
1186        index: isize,
1187    ) -> *mut i8;
1188    pub fn sc_audio_input_devices_snapshot_is_default(
1189        snapshot: *const c_void,
1190        index: isize,
1191    ) -> bool;
1192
1193    /// Get the count of available audio input devices
1194    pub fn sc_audio_get_input_device_count() -> isize;
1195
1196    /// Get audio input device ID at index into buffer
1197    pub fn sc_audio_get_input_device_id(index: isize, buffer: *mut i8, buffer_size: isize) -> bool;
1198
1199    /// Get audio input device name at index into buffer
1200    pub fn sc_audio_get_input_device_name(
1201        index: isize,
1202        buffer: *mut i8,
1203        buffer_size: isize,
1204    ) -> bool;
1205
1206    /// Check if the device at index is the default audio input device
1207    pub fn sc_audio_is_default_input_device(index: isize) -> bool;
1208
1209    /// Get the default audio input device ID into buffer
1210    pub fn sc_audio_get_default_input_device_id(buffer: *mut i8, buffer_size: isize) -> bool;
1211
1212    /// Get the default audio input device name into buffer
1213    pub fn sc_audio_get_default_input_device_name(buffer: *mut i8, buffer_size: isize) -> bool;
1214}
1215
1216#[cfg(all(test, not(feature = "macos_26_0")))]
1217mod fallback_tests {
1218    use std::ffi::c_void;
1219    use std::ptr::NonNull;
1220
1221    #[test]
1222    #[cfg(not(feature = "macos_14_0"))]
1223    fn shareable_content_info_style_stub_reports_unsupported() {
1224        let info = NonNull::<c_void>::dangling().as_ptr().cast_const();
1225        let mut style = -1_i32;
1226        assert!(!unsafe { super::sc_shareable_content_info_get_style(info, &raw mut style) });
1227        assert_eq!(style, 0);
1228    }
1229
1230    #[test]
1231    #[cfg(not(feature = "macos_14_0"))]
1232    fn stream_configuration_reports_unsupported_without_the_macos_14_sdk() {
1233        let config = unsafe { super::sc_stream_configuration_create() };
1234        assert!(!config.is_null());
1235        let mut raw = -1_i32;
1236        unsafe {
1237            assert!(!super::sc_stream_configuration_set_should_be_opaque(
1238                config, true
1239            ));
1240            assert!(!super::sc_stream_configuration_set_stream_name(
1241                config,
1242                std::ptr::null()
1243            ));
1244            assert!(!super::sc_stream_configuration_set_capture_resolution_type(
1245                config, 1
1246            ));
1247            assert!(!super::sc_stream_configuration_get_capture_resolution_type(
1248                config,
1249                &raw mut raw,
1250            ));
1251            super::sc_stream_configuration_release(config);
1252        }
1253    }
1254
1255    #[test]
1256    #[cfg(not(feature = "macos_15_0"))]
1257    fn preset_and_dynamic_range_stubs_report_unsupported() {
1258        assert!(unsafe { super::sc_stream_configuration_create_with_preset(0) }.is_null());
1259        let config = unsafe { super::sc_stream_configuration_create() };
1260        let mut range = -1_i32;
1261        unsafe {
1262            assert!(!super::sc_stream_configuration_set_capture_dynamic_range(
1263                config, 1
1264            ));
1265            assert!(!super::sc_stream_configuration_get_capture_dynamic_range(
1266                config,
1267                &raw mut range,
1268            ));
1269            super::sc_stream_configuration_release(config);
1270        }
1271        assert_eq!(range, 0);
1272    }
1273
1274    #[test]
1275    fn screenshot_configuration_stubs_report_unsupported() {
1276        let config = NonNull::<c_void>::dangling().as_ptr().cast_const();
1277        let mut intent = -1_i32;
1278        let mut range = -1_i32;
1279        unsafe {
1280            assert!(!super::sc_screenshot_configuration_set_display_intent(
1281                config, 0
1282            ));
1283            assert!(!super::sc_screenshot_configuration_set_dynamic_range(
1284                config, 0
1285            ));
1286            assert!(!super::sc_screenshot_configuration_get_display_intent(
1287                config,
1288                &raw mut intent,
1289            ));
1290            assert!(!super::sc_screenshot_configuration_get_dynamic_range(
1291                config,
1292                &raw mut range,
1293            ));
1294        }
1295        assert_eq!((intent, range), (0, 0));
1296    }
1297}