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_start_capture_with_timeout_cleanup(
560        stream: *const c_void,
561        context: *mut c_void,
562        callback: extern "C" fn(*mut c_void, bool, *const i8) -> bool,
563    );
564    pub fn sc_stream_stop_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_update_configuration(
570        stream: *const c_void,
571        config: *const c_void,
572        context: *mut c_void,
573        callback: extern "C" fn(*mut c_void, bool, *const i8),
574    );
575    pub fn sc_stream_update_content_filter(
576        stream: *const c_void,
577        filter: *const c_void,
578        context: *mut c_void,
579        callback: extern "C" fn(*mut c_void, bool, *const i8),
580    );
581    pub fn sc_stream_add_recording_output(
582        stream: *const c_void,
583        recording_output: *const c_void,
584        callback: extern "C" fn(*mut c_void, bool, *const i8),
585        context: *mut c_void,
586    );
587    pub fn sc_stream_remove_recording_output(
588        stream: *const c_void,
589        recording_output: *const c_void,
590        callback: extern "C" fn(*mut c_void, bool, *const i8),
591        context: *mut c_void,
592    );
593    pub fn sc_stream_retain(stream: *const c_void) -> *const c_void;
594    pub fn sc_stream_release(stream: *const c_void);
595
596    // macOS 13.0+ - synchronizationClock
597    pub fn sc_stream_get_synchronization_clock(stream: *const c_void) -> *const c_void;
598}
599
600// MARK: - Dispatch Queue
601extern "C" {
602    pub fn dispatch_queue_release(queue: *const c_void);
603    pub fn dispatch_queue_retain(queue: *const c_void) -> *const c_void;
604}
605
606// MARK: - IOSurface
607extern "C" {
608    pub fn cv_pixel_buffer_get_iosurface(pixel_buffer: *const c_void) -> *const c_void;
609    pub fn cv_pixel_buffer_is_backed_by_iosurface(pixel_buffer: *const c_void) -> bool;
610    pub fn iosurface_get_width(iosurface: *const c_void) -> isize;
611    pub fn iosurface_get_height(iosurface: *const c_void) -> isize;
612    pub fn iosurface_get_bytes_per_row(iosurface: *const c_void) -> isize;
613    pub fn iosurface_get_pixel_format(iosurface: *const c_void) -> u32;
614    pub fn iosurface_get_base_address(iosurface: *const c_void) -> *mut u8;
615    pub fn iosurface_lock(iosurface: *const c_void, options: u32) -> i32;
616    pub fn iosurface_unlock(iosurface: *const c_void, options: u32) -> i32;
617    pub fn iosurface_is_in_use(iosurface: *const c_void) -> bool;
618    pub fn iosurface_release(iosurface: *const c_void);
619
620    // Plane functions (for multi-planar formats like YCbCr 420)
621    pub fn iosurface_get_plane_count(iosurface: *const c_void) -> isize;
622    pub fn iosurface_get_width_of_plane(iosurface: *const c_void, plane: isize) -> isize;
623    pub fn iosurface_get_height_of_plane(iosurface: *const c_void, plane: isize) -> isize;
624    pub fn iosurface_get_bytes_per_row_of_plane(iosurface: *const c_void, plane: isize) -> isize;
625
626    // IOSurface creation (for testing)
627    pub fn io_surface_create(
628        width: usize,
629        height: usize,
630        pixel_format: u32,
631        bytes_per_element: usize,
632        surface_out: *mut *mut c_void,
633    ) -> i32;
634
635    // IOSurface creation with full properties (for multi-planar formats)
636    pub fn io_surface_create_with_properties(
637        width: usize,
638        height: usize,
639        pixel_format: u32,
640        bytes_per_element: usize,
641        bytes_per_row: usize,
642        alloc_size: usize,
643        plane_count: usize,
644        plane_widths: *const usize,
645        plane_heights: *const usize,
646        plane_bytes_per_row: *const usize,
647        plane_bytes_per_element: *const usize,
648        plane_offsets: *const usize,
649        plane_sizes: *const usize,
650        surface_out: *mut *mut c_void,
651    ) -> i32;
652}
653
654// MARK: - SCContentSharingPicker (macOS 14.0+)
655extern "C" {
656    pub fn sc_content_sharing_picker_is_available() -> bool;
657    pub fn sc_content_sharing_picker_configuration_create() -> *const c_void;
658    pub fn sc_content_sharing_picker_configuration_set_allowed_picker_modes(
659        config: *const c_void,
660        modes: *const i32,
661        count: usize,
662    );
663    pub fn sc_content_sharing_picker_configuration_get_allowed_picker_modes_mask(
664        config: *const c_void,
665    ) -> u64;
666    pub fn sc_content_sharing_picker_configuration_set_allows_changing_selected_content(
667        config: *const c_void,
668        allows: bool,
669    );
670    pub fn sc_content_sharing_picker_configuration_get_allows_changing_selected_content(
671        config: *const c_void,
672    ) -> bool;
673    pub fn sc_content_sharing_picker_configuration_set_excluded_bundle_ids(
674        config: *const c_void,
675        bundle_ids: *const *const i8,
676        count: usize,
677    );
678    pub fn sc_content_sharing_picker_configuration_get_excluded_bundle_ids_count(
679        config: *const c_void,
680    ) -> usize;
681    pub fn sc_content_sharing_picker_configuration_get_excluded_bundle_id_at(
682        config: *const c_void,
683        index: usize,
684        buffer: *mut i8,
685        buffer_size: usize,
686    ) -> bool;
687    pub fn sc_content_sharing_picker_configuration_set_excluded_window_ids(
688        config: *const c_void,
689        window_ids: *const u32,
690        count: usize,
691    );
692    pub fn sc_content_sharing_picker_configuration_get_excluded_window_ids_count(
693        config: *const c_void,
694    ) -> usize;
695    pub fn sc_content_sharing_picker_configuration_get_excluded_window_id_at(
696        config: *const c_void,
697        index: usize,
698    ) -> u32;
699    pub fn sc_content_sharing_picker_configuration_retain(config: *const c_void) -> *const c_void;
700    pub fn sc_content_sharing_picker_configuration_copy(config: *const c_void) -> *const c_void;
701    pub fn sc_content_sharing_picker_configuration_release(config: *const c_void);
702
703    // Picker maximum stream count
704    pub fn sc_content_sharing_picker_set_maximum_stream_count(count: usize);
705    pub fn sc_content_sharing_picker_get_maximum_stream_count() -> usize;
706
707    /// Returns a fresh `SCContentSharingPickerConfiguration` initialised with
708    /// the system's `defaultConfiguration` baseline. Caller owns the returned
709    /// configuration and must release it via the standard
710    /// `sc_content_sharing_picker_configuration_release`.
711    pub fn sc_content_sharing_picker_create_default_configuration() -> *const c_void;
712
713    /// Read whether the shared content-sharing picker is currently active.
714    pub fn sc_content_sharing_picker_get_active() -> bool;
715    /// Mark the shared content-sharing picker active or inactive.
716    pub fn sc_content_sharing_picker_set_active(active: bool);
717
718    /// Assign the picker's process-wide `defaultConfiguration`.
719    /// Returns false without enqueueing work when called off the main thread.
720    pub fn sc_content_sharing_picker_set_default_configuration(config: *const c_void) -> bool;
721    /// Assign (or clear, when `config` is null) the per-stream configuration.
722    /// Returns false without enqueueing work when called off the main thread.
723    pub fn sc_content_sharing_picker_set_configuration_for_stream(
724        config: *const c_void,
725        stream: *const c_void,
726    ) -> bool;
727
728    /// Register a repeating observer. Returns a non-zero token, or 0 on failure.
729    ///
730    /// `callback` receives `(event, result_ptr, message, stream, user_data)`
731    /// where the event is 1 = updated (`result_ptr` non-null), 0 = cancelled,
732    /// -1 = start failed (message non-null). `stream` is a non-owning identity
733    /// pointer when the event targets an existing stream. `context_release` is
734    /// invoked exactly once after detachment.
735    pub fn sc_content_sharing_picker_add_observer(
736        callback: extern "C" fn(i32, *const c_void, *const i8, *const c_void, *mut c_void),
737        context_release: extern "C" fn(*mut c_void),
738        user_data: *mut c_void,
739    ) -> i64;
740    /// Remove a repeating observer by token. Returns true if it was live.
741    pub fn sc_content_sharing_picker_remove_observer(token: i64) -> bool;
742    /// Remove every repeating observer; returns how many were removed.
743    pub fn sc_content_sharing_picker_remove_all_observers() -> usize;
744
745    /// Present the picker. `style` < 0 presents without a content-style hint.
746    pub fn sc_content_sharing_picker_present(style: i32);
747    /// Present the picker targeting an existing stream.
748    pub fn sc_content_sharing_picker_present_for_stream(stream: *const c_void, style: i32);
749    /// Deactivate the picker and undo any activation-policy promotion.
750    pub fn sc_content_sharing_picker_deactivate();
751
752    pub fn sc_content_sharing_picker_show(
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_with_result(
758        config: *const c_void,
759        callback: extern "C" fn(i32, *const c_void, *mut c_void),
760        user_data: *mut c_void,
761    );
762    pub fn sc_content_sharing_picker_show_for_stream(
763        config: *const c_void,
764        stream: *const c_void,
765        callback: extern "C" fn(i32, *const c_void, *mut c_void),
766        user_data: *mut c_void,
767    );
768    pub fn sc_content_sharing_picker_show_using_style(
769        config: *const c_void,
770        style: i32,
771        callback: extern "C" fn(i32, *const c_void, *mut c_void),
772        user_data: *mut c_void,
773    );
774    pub fn sc_content_sharing_picker_show_for_stream_using_style(
775        config: *const c_void,
776        stream: *const c_void,
777        style: i32,
778        callback: extern "C" fn(i32, *const c_void, *mut c_void),
779        user_data: *mut c_void,
780    );
781    pub fn sc_picker_result_get_filter(result: *const c_void) -> *const c_void;
782    pub fn sc_picker_result_get_content_rect(
783        result: *const c_void,
784        x: *mut f64,
785        y: *mut f64,
786        width: *mut f64,
787        height: *mut f64,
788    );
789    pub fn sc_picker_result_get_scale(result: *const c_void) -> f64;
790    pub fn sc_picker_result_get_windows_count(result: *const c_void) -> usize;
791    pub fn sc_picker_result_get_window_at(result: *const c_void, index: usize) -> *const c_void;
792    pub fn sc_picker_result_get_displays_count(result: *const c_void) -> usize;
793    pub fn sc_picker_result_get_display_at(result: *const c_void, index: usize) -> *const c_void;
794    pub fn sc_picker_result_get_applications_count(result: *const c_void) -> usize;
795    pub fn sc_picker_result_get_application_at(
796        result: *const c_void,
797        index: usize,
798    ) -> *const c_void;
799    pub fn sc_picker_result_release(result: *const c_void);
800}
801
802// MARK: - SCRecordingOutput (macOS 15.0+)
803extern "C" {
804    pub fn sc_recording_output_is_available() -> bool;
805    pub fn sc_recording_output_configuration_create() -> *const c_void;
806    pub fn sc_recording_output_configuration_copy(config: *const c_void) -> *const c_void;
807    pub fn sc_recording_output_configuration_set_output_url(config: *const c_void, path: *const i8);
808    pub fn sc_recording_output_configuration_get_output_url(
809        config: *const c_void,
810        buffer: *mut i8,
811        buffer_size: isize,
812    ) -> bool;
813    pub fn sc_recording_output_configuration_get_output_path_owned(
814        config: *const c_void,
815    ) -> *mut i8;
816    pub fn sc_recording_output_configuration_set_video_codec(config: *const c_void, codec: i32);
817    pub fn sc_recording_output_configuration_retain(config: *const c_void) -> *const c_void;
818    pub fn sc_recording_output_configuration_release(config: *const c_void);
819    pub fn sc_recording_output_create(config: *const c_void) -> *const c_void;
820    pub fn sc_recording_output_retain(output: *const c_void) -> *const c_void;
821    pub fn sc_recording_output_release(output: *const c_void);
822}
823
824// MARK: - SCScreenshotManager (macOS 14.0+)
825extern "C" {
826    pub fn sc_screenshot_manager_capture_image(
827        content_filter: *const c_void,
828        config: *const c_void,
829        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
830        user_data: *mut c_void,
831    );
832    pub fn sc_screenshot_manager_capture_sample_buffer(
833        content_filter: *const c_void,
834        config: *const c_void,
835        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
836        user_data: *mut c_void,
837    );
838    pub fn sc_screenshot_manager_capture_image_in_rect(
839        x: f64,
840        y: f64,
841        width: f64,
842        height: f64,
843        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
844        user_data: *mut c_void,
845    );
846    pub fn cgimage_get_width(image: *const c_void) -> usize;
847    pub fn cgimage_get_height(image: *const c_void) -> usize;
848    pub fn cgimage_get_data(
849        image: *const c_void,
850        out_ptr: *mut *const u8,
851        out_length: *mut usize,
852    ) -> bool;
853    pub fn cgimage_free_data(ptr: *mut u8);
854    /// Render the `CGImage`'s RGBA bytes directly into a caller-owned buffer.
855    /// Returns the number of bytes written (= width*height*4) on success, or 0
856    /// on failure. Replaces the legacy `cgimage_get_data` + `cgimage_free_data`
857    /// pair which made an extra Swift-side malloc + memcpy before handing the
858    /// buffer back; this single-call form lets Rust own the allocation and
859    /// removes one full RGBA-sized memcpy from the screenshot decode path.
860    pub fn cgimage_render_rgba_into(
861        image: *const c_void,
862        dest: *mut u8,
863        dest_capacity: usize,
864    ) -> usize;
865    /// Render the `CGImage` directly as **BGRA** (the source pixel layout for
866    /// every `ScreenCaptureKit`-produced `CGImage`). Saves the per-pixel
867    /// channel-swap that `cgimage_render_rgba_into` forces — measured at
868    /// ~20 ms per 4K screenshot. Use when the consumer (Metal, wgpu, ffmpeg)
869    /// accepts BGRA natively.
870    pub fn cgimage_render_bgra_into(
871        image: *const c_void,
872        dest: *mut u8,
873        dest_capacity: usize,
874    ) -> usize;
875    /// Strided variant of [`cgimage_render_rgba_into`]: renders RGBA bytes
876    /// using a caller-specified row stride (`dest_bytes_per_row`) instead of
877    /// the tightly-packed `width * 4`. Lets consumers with row-aligned/padded
878    /// buffers (GPU upload, wgpu) avoid an extra repack. Returns the number of
879    /// bytes spanned (`height * dest_bytes_per_row`) on success, or 0 on
880    /// failure (stride too small or buffer too small).
881    pub fn cgimage_render_rgba_into_strided(
882        image: *const c_void,
883        dest: *mut u8,
884        dest_capacity: usize,
885        dest_bytes_per_row: usize,
886    ) -> usize;
887    /// Strided **BGRA** variant. See [`cgimage_render_bgra_into`] and
888    /// [`cgimage_render_rgba_into_strided`].
889    pub fn cgimage_render_bgra_into_strided(
890        image: *const c_void,
891        dest: *mut u8,
892        dest_capacity: usize,
893        dest_bytes_per_row: usize,
894    ) -> usize;
895    pub fn cgimage_release(image: *const c_void);
896    pub fn cgimage_save_png(image: *const c_void, path: *const i8) -> bool;
897    pub fn cgimage_save_to_file(
898        image: *const c_void,
899        path: *const i8,
900        format: i32,
901        quality: f32,
902    ) -> bool;
903}
904
905// MARK: - SCScreenshotConfiguration (macOS 26.0+)
906extern "C" {
907    pub fn sc_screenshot_configuration_create() -> *const c_void;
908    pub fn sc_screenshot_configuration_set_width(config: *const c_void, width: isize);
909    pub fn sc_screenshot_configuration_set_height(config: *const c_void, height: isize);
910    pub fn sc_screenshot_configuration_set_shows_cursor(config: *const c_void, shows_cursor: bool);
911    pub fn sc_screenshot_configuration_set_source_rect(
912        config: *const c_void,
913        x: f64,
914        y: f64,
915        width: f64,
916        height: f64,
917    );
918    pub fn sc_screenshot_configuration_set_destination_rect(
919        config: *const c_void,
920        x: f64,
921        y: f64,
922        width: f64,
923        height: f64,
924    );
925    pub fn sc_screenshot_configuration_set_ignore_shadows(
926        config: *const c_void,
927        ignore_shadows: bool,
928    );
929    pub fn sc_screenshot_configuration_set_ignore_clipping(
930        config: *const c_void,
931        ignore_clipping: bool,
932    );
933    pub fn sc_screenshot_configuration_set_include_child_windows(
934        config: *const c_void,
935        include_child_windows: bool,
936    );
937    pub fn sc_screenshot_configuration_set_display_intent(
938        config: *const c_void,
939        display_intent: i32,
940    ) -> bool;
941    pub fn sc_screenshot_configuration_set_dynamic_range(
942        config: *const c_void,
943        dynamic_range: i32,
944    ) -> bool;
945    pub fn sc_screenshot_configuration_set_file_url(config: *const c_void, path: *const i8);
946    pub fn sc_screenshot_configuration_clear_file_url(config: *const c_void);
947    pub fn sc_screenshot_configuration_get_width(config: *const c_void) -> isize;
948    pub fn sc_screenshot_configuration_get_height(config: *const c_void) -> isize;
949    pub fn sc_screenshot_configuration_get_shows_cursor(config: *const c_void) -> bool;
950    pub fn sc_screenshot_configuration_get_source_rect(
951        config: *const c_void,
952        x: *mut f64,
953        y: *mut f64,
954        width: *mut f64,
955        height: *mut f64,
956    );
957    pub fn sc_screenshot_configuration_get_destination_rect(
958        config: *const c_void,
959        x: *mut f64,
960        y: *mut f64,
961        width: *mut f64,
962        height: *mut f64,
963    );
964    pub fn sc_screenshot_configuration_get_ignore_shadows(config: *const c_void) -> bool;
965    pub fn sc_screenshot_configuration_get_ignore_clipping(config: *const c_void) -> bool;
966    pub fn sc_screenshot_configuration_get_include_child_windows(config: *const c_void) -> bool;
967    pub fn sc_screenshot_configuration_get_display_intent(
968        config: *const c_void,
969        value: *mut i32,
970    ) -> bool;
971    pub fn sc_screenshot_configuration_get_dynamic_range(
972        config: *const c_void,
973        value: *mut i32,
974    ) -> bool;
975    pub fn sc_screenshot_configuration_get_file_path_owned(config: *const c_void) -> *mut i8;
976    pub fn sc_screenshot_configuration_release(config: *const c_void);
977
978    // Content type support (macOS 26.0+)
979    pub fn sc_screenshot_configuration_set_content_type(
980        config: *const c_void,
981        identifier: *const i8,
982    );
983    pub fn sc_screenshot_configuration_get_content_type(
984        config: *const c_void,
985        buffer: *mut i8,
986        buffer_size: usize,
987    ) -> bool;
988    pub fn sc_screenshot_configuration_get_supported_content_types_count() -> usize;
989    pub fn sc_screenshot_configuration_get_supported_content_type_at(
990        index: usize,
991        buffer: *mut i8,
992        buffer_size: usize,
993    ) -> bool;
994}
995
996// MARK: - SCScreenshotOutput (macOS 26.0+)
997extern "C" {
998    pub fn sc_screenshot_output_get_sdr_image(output: *const c_void) -> *const c_void;
999    pub fn sc_screenshot_output_get_hdr_image(output: *const c_void) -> *const c_void;
1000    pub fn sc_screenshot_output_get_file_url(
1001        output: *const c_void,
1002        buffer: *mut i8,
1003        buffer_size: isize,
1004    ) -> bool;
1005    pub fn sc_screenshot_output_get_file_path_owned(output: *const c_void) -> *mut i8;
1006    pub fn sc_screenshot_output_release(output: *const c_void);
1007
1008    pub fn sc_screenshot_manager_capture_screenshot(
1009        content_filter: *const c_void,
1010        config: *const c_void,
1011        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
1012        user_data: *mut c_void,
1013    );
1014    pub fn sc_screenshot_manager_capture_screenshot_in_rect(
1015        x: f64,
1016        y: f64,
1017        width: f64,
1018        height: f64,
1019        config: *const c_void,
1020        callback: extern "C" fn(*const c_void, *const i8, *mut c_void),
1021        user_data: *mut c_void,
1022    );
1023}
1024
1025// MARK: - SCStreamConfiguration additional properties
1026extern "C" {
1027    // macOS 15.0+ - showMouseClicks
1028    pub fn sc_stream_configuration_set_shows_mouse_clicks(
1029        config: *const c_void,
1030        value: bool,
1031    ) -> bool;
1032    pub fn sc_stream_configuration_get_shows_mouse_clicks(config: *const c_void) -> bool;
1033
1034    // macOS 14.0+ - ignoreShadowsDisplay
1035    pub fn sc_stream_configuration_set_ignores_shadows_display(
1036        config: *const c_void,
1037        value: bool,
1038    ) -> bool;
1039    pub fn sc_stream_configuration_get_ignores_shadows_display(config: *const c_void) -> bool;
1040
1041    // macOS 14.0+ - ignoreGlobalClipDisplay
1042    pub fn sc_stream_configuration_set_ignore_global_clip_display(
1043        config: *const c_void,
1044        value: bool,
1045    ) -> bool;
1046    pub fn sc_stream_configuration_get_ignore_global_clip_display(config: *const c_void) -> bool;
1047
1048    // macOS 14.0+ - ignoreGlobalClipSingleWindow
1049    pub fn sc_stream_configuration_set_ignore_global_clip_single_window(
1050        config: *const c_void,
1051        value: bool,
1052    ) -> bool;
1053    pub fn sc_stream_configuration_get_ignore_global_clip_single_window(
1054        config: *const c_void,
1055    ) -> bool;
1056
1057    // macOS 15.0+ - preset-based configuration
1058    pub fn sc_stream_configuration_create_with_preset(preset: i32) -> *const c_void;
1059}
1060
1061// MARK: - SCContentFilter additional properties
1062extern "C" {
1063    // macOS 14.0+ - style and pointPixelScale
1064    pub fn sc_content_filter_get_style(filter: *const c_void, value: *mut i32) -> bool;
1065    pub fn sc_content_filter_get_point_pixel_scale(filter: *const c_void) -> f32;
1066    pub fn sc_content_filter_get_stream_type(filter: *const c_void, value: *mut i32) -> bool;
1067
1068    // macOS 14.2+ - includeMenuBar
1069    pub fn sc_content_filter_set_include_menu_bar(filter: *const c_void, include: bool) -> bool;
1070    pub fn sc_content_filter_get_include_menu_bar(filter: *const c_void) -> bool;
1071
1072    // macOS 15.2+ - included content arrays
1073    pub fn sc_content_filter_get_included_displays_count(filter: *const c_void) -> isize;
1074    pub fn sc_content_filter_get_included_display_at(
1075        filter: *const c_void,
1076        index: isize,
1077    ) -> *const c_void;
1078    pub fn sc_content_filter_get_included_windows_count(filter: *const c_void) -> isize;
1079    pub fn sc_content_filter_get_included_window_at(
1080        filter: *const c_void,
1081        index: isize,
1082    ) -> *const c_void;
1083    pub fn sc_content_filter_get_included_applications_count(filter: *const c_void) -> isize;
1084    pub fn sc_content_filter_get_included_application_at(
1085        filter: *const c_void,
1086        index: isize,
1087    ) -> *const c_void;
1088}
1089
1090// MARK: - SCShareableContentInfo (macOS 14.0+)
1091extern "C" {
1092    pub fn sc_shareable_content_info_for_filter(filter: *const c_void) -> *const c_void;
1093    pub fn sc_shareable_content_info_get_style(info: *const c_void, value: *mut i32) -> bool;
1094    pub fn sc_shareable_content_info_get_point_pixel_scale(info: *const c_void) -> f32;
1095    pub fn sc_shareable_content_info_get_content_rect(
1096        info: *const c_void,
1097        x: *mut f64,
1098        y: *mut f64,
1099        width: *mut f64,
1100        height: *mut f64,
1101    );
1102    /// Get shareable content info rect (same as `sc_shareable_content_info_get_content_rect`)
1103    pub fn sc_shareable_content_info_get_content_rect_packed(
1104        info: *const c_void,
1105        x: *mut f64,
1106        y: *mut f64,
1107        width: *mut f64,
1108        height: *mut f64,
1109    );
1110    pub fn sc_shareable_content_info_retain(info: *const c_void) -> *const c_void;
1111    pub fn sc_shareable_content_info_release(info: *const c_void);
1112}
1113
1114// MARK: - SCRecordingOutput additional (macOS 15.0+)
1115extern "C" {
1116    pub fn sc_recording_output_configuration_set_output_file_type(
1117        config: *const c_void,
1118        file_type: i32,
1119    );
1120    pub fn sc_recording_output_configuration_set_output_file_type_identifier(
1121        config: *const c_void,
1122        identifier: *const i8,
1123    );
1124    pub fn sc_recording_output_configuration_get_output_file_type_identifier_owned(
1125        config: *const c_void,
1126    ) -> *mut i8;
1127    pub fn sc_recording_output_configuration_get_output_file_type(config: *const c_void) -> i32;
1128    pub fn sc_recording_output_configuration_get_video_codec(config: *const c_void) -> i32;
1129    pub fn sc_recording_output_configuration_set_video_codec_identifier(
1130        config: *const c_void,
1131        identifier: *const i8,
1132    );
1133    pub fn sc_recording_output_configuration_get_video_codec_identifier_owned(
1134        config: *const c_void,
1135    ) -> *mut i8;
1136    pub fn sc_recording_output_configuration_get_available_video_codecs_count(
1137        config: *const c_void,
1138    ) -> isize;
1139    pub fn sc_recording_output_configuration_get_available_video_codec_at(
1140        config: *const c_void,
1141        index: isize,
1142    ) -> i32;
1143    pub fn sc_recording_output_configuration_get_available_video_codec_identifier_at_owned(
1144        config: *const c_void,
1145        index: isize,
1146    ) -> *mut i8;
1147    pub fn sc_recording_output_configuration_get_available_output_file_types_count(
1148        config: *const c_void,
1149    ) -> isize;
1150    pub fn sc_recording_output_configuration_get_available_output_file_type_at(
1151        config: *const c_void,
1152        index: isize,
1153    ) -> i32;
1154    pub fn sc_recording_output_configuration_get_available_output_file_type_identifier_at_owned(
1155        config: *const c_void,
1156        index: isize,
1157    ) -> *mut i8;
1158    pub fn sc_recording_output_create_with_delegate(
1159        config: *const c_void,
1160        started_callback: Option<extern "C" fn(*mut c_void)>,
1161        failed_callback: Option<extern "C" fn(*mut c_void, i32, *const i8)>,
1162        finished_callback: Option<extern "C" fn(*mut c_void)>,
1163        context_release: Option<extern "C" fn(*mut c_void)>,
1164        context: *mut c_void,
1165    ) -> *const c_void;
1166    pub fn sc_recording_output_get_recorded_duration(
1167        output: *const c_void,
1168        value: *mut i64,
1169        timescale: *mut i32,
1170    );
1171    pub fn sc_recording_output_get_recorded_file_size(output: *const c_void) -> i64;
1172    pub fn sc_recording_output_wait_until_terminal(
1173        output: *const c_void,
1174        context: *mut c_void,
1175        callback: extern "C" fn(*mut c_void, bool, *const i8),
1176    );
1177}
1178
1179// MARK: - Audio Input Devices (AVFoundation)
1180extern "C" {
1181    pub fn sc_audio_input_devices_snapshot_create() -> *const c_void;
1182    pub fn sc_audio_input_devices_snapshot_release(snapshot: *const c_void);
1183    pub fn sc_audio_input_devices_snapshot_count(snapshot: *const c_void) -> isize;
1184    pub fn sc_audio_input_devices_snapshot_default_index(snapshot: *const c_void) -> isize;
1185    pub fn sc_audio_input_devices_snapshot_id_owned(
1186        snapshot: *const c_void,
1187        index: isize,
1188    ) -> *mut i8;
1189    pub fn sc_audio_input_devices_snapshot_name_owned(
1190        snapshot: *const c_void,
1191        index: isize,
1192    ) -> *mut i8;
1193    pub fn sc_audio_input_devices_snapshot_is_default(
1194        snapshot: *const c_void,
1195        index: isize,
1196    ) -> bool;
1197
1198    /// Get the count of available audio input devices
1199    pub fn sc_audio_get_input_device_count() -> isize;
1200
1201    /// Get audio input device ID at index into buffer
1202    pub fn sc_audio_get_input_device_id(index: isize, buffer: *mut i8, buffer_size: isize) -> bool;
1203
1204    /// Get audio input device name at index into buffer
1205    pub fn sc_audio_get_input_device_name(
1206        index: isize,
1207        buffer: *mut i8,
1208        buffer_size: isize,
1209    ) -> bool;
1210
1211    /// Check if the device at index is the default audio input device
1212    pub fn sc_audio_is_default_input_device(index: isize) -> bool;
1213
1214    /// Get the default audio input device ID into buffer
1215    pub fn sc_audio_get_default_input_device_id(buffer: *mut i8, buffer_size: isize) -> bool;
1216
1217    /// Get the default audio input device name into buffer
1218    pub fn sc_audio_get_default_input_device_name(buffer: *mut i8, buffer_size: isize) -> bool;
1219}
1220
1221#[cfg(all(test, not(feature = "macos_26_0")))]
1222mod fallback_tests {
1223    use std::ffi::c_void;
1224    use std::ptr::NonNull;
1225
1226    #[test]
1227    #[cfg(not(feature = "macos_14_0"))]
1228    fn shareable_content_info_style_stub_reports_unsupported() {
1229        let info = NonNull::<c_void>::dangling().as_ptr().cast_const();
1230        let mut style = -1_i32;
1231        assert!(!unsafe { super::sc_shareable_content_info_get_style(info, &raw mut style) });
1232        assert_eq!(style, 0);
1233    }
1234
1235    #[test]
1236    #[cfg(not(feature = "macos_14_0"))]
1237    fn stream_configuration_reports_unsupported_without_the_macos_14_sdk() {
1238        let config = unsafe { super::sc_stream_configuration_create() };
1239        assert!(!config.is_null());
1240        let mut raw = -1_i32;
1241        unsafe {
1242            assert!(!super::sc_stream_configuration_set_should_be_opaque(
1243                config, true
1244            ));
1245            assert!(!super::sc_stream_configuration_set_stream_name(
1246                config,
1247                std::ptr::null()
1248            ));
1249            assert!(!super::sc_stream_configuration_set_capture_resolution_type(
1250                config, 1
1251            ));
1252            assert!(!super::sc_stream_configuration_get_capture_resolution_type(
1253                config,
1254                &raw mut raw,
1255            ));
1256            super::sc_stream_configuration_release(config);
1257        }
1258    }
1259
1260    #[test]
1261    #[cfg(not(feature = "macos_15_0"))]
1262    fn preset_and_dynamic_range_stubs_report_unsupported() {
1263        assert!(unsafe { super::sc_stream_configuration_create_with_preset(0) }.is_null());
1264        let config = unsafe { super::sc_stream_configuration_create() };
1265        let mut range = -1_i32;
1266        unsafe {
1267            assert!(!super::sc_stream_configuration_set_capture_dynamic_range(
1268                config, 1
1269            ));
1270            assert!(!super::sc_stream_configuration_get_capture_dynamic_range(
1271                config,
1272                &raw mut range,
1273            ));
1274            super::sc_stream_configuration_release(config);
1275        }
1276        assert_eq!(range, 0);
1277    }
1278
1279    #[test]
1280    fn screenshot_configuration_stubs_report_unsupported() {
1281        let config = NonNull::<c_void>::dangling().as_ptr().cast_const();
1282        let mut intent = -1_i32;
1283        let mut range = -1_i32;
1284        unsafe {
1285            assert!(!super::sc_screenshot_configuration_set_display_intent(
1286                config, 0
1287            ));
1288            assert!(!super::sc_screenshot_configuration_set_dynamic_range(
1289                config, 0
1290            ));
1291            assert!(!super::sc_screenshot_configuration_get_display_intent(
1292                config,
1293                &raw mut intent,
1294            ));
1295            assert!(!super::sc_screenshot_configuration_get_dynamic_range(
1296                config,
1297                &raw mut range,
1298            ));
1299        }
1300        assert_eq!((intent, range), (0, 0));
1301    }
1302}