screencapturekit/
content_sharing_picker.rs

1//! `SCContentSharingPicker` - UI for selecting content to share
2//!
3//! Available on macOS 14.0+
4//! Provides a system UI for users to select displays, windows, or applications to share.
5//!
6//! Requires the `macos_14_0` feature flag to be enabled.
7
8use crate::shareable_content::{SCDisplay, SCRunningApplication, SCWindow};
9use crate::utils::sync_completion::SyncCompletion;
10use std::ffi::c_void;
11
12extern "C" fn picker_callback(result_code: i32, stream_ptr: *const c_void, user_data: *mut c_void) {
13    let result = match result_code {
14        0 => SCContentSharingPickerResult::Cancelled,
15        1 if !stream_ptr.is_null() => {
16            // For now, return Cancelled since we need the stream to be properly typed
17            // In a real implementation, we'd need to extract the content from the stream
18            SCContentSharingPickerResult::Cancelled
19        }
20        -1 => SCContentSharingPickerResult::Error("Picker failed".to_string()),
21        _ => SCContentSharingPickerResult::Cancelled,
22    };
23
24    unsafe { SyncCompletion::complete_ok(user_data, result) };
25}
26
27/// Picker style determines what content types can be selected
28#[repr(i32)]
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
30pub enum SCContentSharingPickerMode {
31    /// Allow selection of both displays and windows
32    #[default]
33    SingleWindow = 0,
34    /// Allow selection of multiple items
35    Multiple = 1,
36    /// Only allow display selection
37    SingleDisplay = 2,
38}
39
40/// Configuration for the content sharing picker
41pub struct SCContentSharingPickerConfiguration {
42    ptr: *const c_void,
43}
44
45impl SCContentSharingPickerConfiguration {
46    #[must_use]
47    pub fn new() -> Self {
48        let ptr = unsafe { crate::ffi::sc_content_sharing_picker_configuration_create() };
49        Self { ptr }
50    }
51
52    /// Set allowed picker modes
53    pub fn set_allowed_picker_modes(&mut self, modes: &[SCContentSharingPickerMode]) {
54        let mode_values: Vec<i32> = modes.iter().map(|m| *m as i32).collect();
55        unsafe {
56            crate::ffi::sc_content_sharing_picker_configuration_set_allowed_picker_modes(
57                self.ptr,
58                mode_values.as_ptr(),
59                mode_values.len(),
60            );
61        }
62    }
63
64    #[must_use]
65    pub const fn as_ptr(&self) -> *const c_void {
66        self.ptr
67    }
68}
69
70impl Default for SCContentSharingPickerConfiguration {
71    fn default() -> Self {
72        Self::new()
73    }
74}
75
76impl Clone for SCContentSharingPickerConfiguration {
77    fn clone(&self) -> Self {
78        unsafe {
79            Self {
80                ptr: crate::ffi::sc_content_sharing_picker_configuration_retain(self.ptr),
81            }
82        }
83    }
84}
85
86impl Drop for SCContentSharingPickerConfiguration {
87    fn drop(&mut self) {
88        if !self.ptr.is_null() {
89            unsafe {
90                crate::ffi::sc_content_sharing_picker_configuration_release(self.ptr);
91            }
92        }
93    }
94}
95
96impl std::fmt::Debug for SCContentSharingPickerConfiguration {
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        f.debug_struct("SCContentSharingPickerConfiguration")
99            .field("ptr", &self.ptr)
100            .finish()
101    }
102}
103
104/// Result from the content sharing picker
105#[derive(Debug, Clone, PartialEq, Eq, Hash)]
106pub enum SCContentSharingPickerResult {
107    /// User selected display content
108    Display(SCDisplay),
109    /// User selected a window
110    Window(SCWindow),
111    /// User selected an application
112    Application(SCRunningApplication),
113    /// User cancelled the picker
114    Cancelled,
115    /// An error occurred
116    Error(String),
117}
118
119/// System UI for selecting content to share
120///
121/// Available on macOS 14.0+
122pub struct SCContentSharingPicker;
123
124impl SCContentSharingPicker {
125    /// Show the picker UI and wait for user selection
126    ///
127    /// # Panics
128    /// Panics if the internal mutex is poisoned.
129    pub fn show(config: &SCContentSharingPickerConfiguration) -> SCContentSharingPickerResult {
130        let (completion, context) = SyncCompletion::<SCContentSharingPickerResult>::new();
131
132        unsafe {
133            crate::ffi::sc_content_sharing_picker_show(config.as_ptr(), picker_callback, context);
134        }
135
136        completion
137            .wait()
138            .unwrap_or_else(SCContentSharingPickerResult::Error)
139    }
140}
141
142// Safety: SCContentSharingPickerConfiguration wraps an Objective-C object that is thread-safe
143unsafe impl Send for SCContentSharingPickerConfiguration {}
144unsafe impl Sync for SCContentSharingPickerConfiguration {}