Skip to main content

cidre/sc/
shareable_content.rs

1use crate::{arc, cg, define_cls, define_obj_type, ns, objc, sc, sys};
2
3#[cfg(feature = "blocks")]
4use crate::blocks;
5
6#[doc(alias = "SCShareableContentStyle")]
7#[derive(Debug, Copy, Clone, Eq, PartialEq)]
8#[repr(isize)]
9pub enum Style {
10    /// No specific shareable content style.
11    None,
12    /// Window content.
13    Window,
14    /// Display content.
15    Display,
16    /// Application content.
17    Application,
18}
19
20define_obj_type!(
21    /// A running application that can own shareable content.
22    #[doc(alias = "SCRunningApplication")]
23    pub RunningApp(ns::Id)
24);
25
26impl RunningApp {
27    /// The application's bundle identifier.
28    #[objc::msg_send(bundleIdentifier)]
29    pub fn bundle_id(&self) -> arc::R<ns::String>;
30
31    /// The localized application name.
32    #[objc::msg_send(applicationName)]
33    pub fn app_name(&self) -> arc::R<ns::String>;
34
35    /// The application's process identifier.
36    #[objc::msg_send(processID)]
37    pub fn process_id(&self) -> sys::Pid;
38}
39
40define_obj_type!(
41    /// A display that can be shared or captured.
42    #[doc(alias = "SCDisplay")]
43    pub Display(ns::Id)
44);
45
46impl Display {
47    /// The Core Graphics display identifier.
48    #[objc::msg_send(displayID)]
49    pub fn display_id(&self) -> cg::DirectDisplayId;
50
51    /// The display width in pixels.
52    #[objc::msg_send(width)]
53    pub fn width(&self) -> isize;
54
55    /// The display height in pixels.
56    #[objc::msg_send(height)]
57    pub fn height(&self) -> isize;
58
59    /// The display frame in points.
60    #[objc::msg_send(frame)]
61    pub fn frame(&self) -> cg::Rect;
62}
63
64define_obj_type!(
65    /// A window that can be shared or captured.
66    #[doc(alias = "SCWindow")]
67    pub Window(ns::Id)
68);
69
70impl Window {
71    /// The Core Graphics window identifier.
72    #[objc::msg_send(windowID)]
73    pub fn id(&self) -> cg::WindowId;
74
75    /// The window frame in points.
76    #[objc::msg_send(frame)]
77    pub fn frame(&self) -> cg::Rect;
78
79    /// The window title, if available.
80    #[objc::msg_send(title)]
81    pub fn title(&self) -> Option<arc::R<ns::String>>;
82
83    /// The Core Graphics window layer.
84    #[objc::msg_send(windowLayer)]
85    pub fn window_layer(&self) -> ns::Integer;
86
87    /// The application that owns the window, if available.
88    #[objc::msg_send(owningApplication)]
89    pub fn owning_app(&self) -> Option<arc::R<RunningApp>>;
90
91    /// Whether the window is currently onscreen.
92    #[objc::msg_send(isOnScreen)]
93    pub fn is_on_screen(&self) -> bool;
94
95    /// Whether the window is active.
96    #[objc::msg_send(isActive)]
97    #[objc::available(macos = 13.1)]
98    pub fn is_active(&self) -> bool;
99}
100
101impl std::fmt::Display for Window {
102    #[cfg(not(all(target_os = "macos", feature = "macos_13_1")))]
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        f.debug_struct("Window")
105            .field("id", &self.id())
106            .field("frame", &self.frame())
107            .field("title", &self.title())
108            .field("is_on_screen", &self.is_on_screen())
109            .finish()
110    }
111    #[cfg(all(target_os = "macos", feature = "macos_13_1"))]
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        f.debug_struct("Window")
114            .field("id", &self.id())
115            .field("frame", &self.frame())
116            .field("title", &self.title())
117            .field("is_on_screen", &self.is_on_screen())
118            .field("is_active", &self.is_active())
119            .finish()
120    }
121}
122
123unsafe extern "C" {
124    static SC_SHAREABLE_CONTENT: &'static objc::Class<ShareableContent>;
125}
126
127define_obj_type!(
128    /// A snapshot of content that ScreenCaptureKit can share or capture.
129    #[doc(alias = "SCShareableContent")]
130    pub ShareableContent(ns::Id)
131);
132
133unsafe impl Send for ShareableContent {}
134
135impl ShareableContent {
136    define_cls!(SC_SHAREABLE_CONTENT);
137
138    /// The shareable windows in this snapshot.
139    #[objc::msg_send(windows)]
140    pub fn windows(&self) -> arc::R<ns::Array<Window>>;
141
142    /// The shareable displays in this snapshot.
143    #[objc::msg_send(displays)]
144    pub fn displays(&self) -> arc::R<ns::Array<Display>>;
145
146    /// The shareable applications in this snapshot.
147    #[objc::msg_send(applications)]
148    pub fn apps(&self) -> arc::R<ns::Array<RunningApp>>;
149
150    #[cfg(feature = "blocks")]
151    /// Retrieves the current shareable content snapshot.
152    #[objc::msg_send(getShareableContentWithCompletionHandler:)]
153    pub fn current_with_ch_block(block: &mut blocks::ResultCh<Self>);
154
155    #[cfg(feature = "blocks")]
156    /// Retrieves the current shareable content snapshot.
157    pub fn current_with_ch(f: impl FnMut(Option<&Self>, Option<&ns::Error>) + 'static) {
158        let mut block = blocks::ResultCh::new2(f);
159        Self::current_with_ch_block(&mut block);
160    }
161
162    #[cfg(all(feature = "blocks", feature = "async"))]
163    /// Retrieves the current shareable content snapshot.
164    pub async fn current() -> Result<arc::R<Self>, arc::R<ns::Error>> {
165        let (future, mut block) = blocks::result();
166        Self::current_with_ch_block(&mut block);
167        future.await
168    }
169
170    #[cfg(feature = "blocks")]
171    /// Retrieves shareable content for the current process.
172    #[objc::msg_send(getCurrentProcessShareableContentWithCompletionHandler:)]
173    pub fn current_process_with_ch(block: &mut blocks::ResultCh<Self>);
174
175    #[cfg(all(feature = "blocks", feature = "async"))]
176    /// Retrieves shareable content for the current process.
177    pub async fn current_process() -> Result<arc::R<Self>, arc::R<ns::Error>> {
178        let (future, mut block) = blocks::result();
179        Self::current_process_with_ch(&mut block);
180        future.await
181    }
182
183    /// Returns metadata for a content filter.
184    #[objc::msg_send(infoForFilter:)]
185    pub fn info_for_filter(filter: &sc::ContentFilter) -> arc::R<Info>;
186}
187
188define_obj_type!(
189    /// Metadata describing the content represented by a filter.
190    #[doc(alias = "SCShareableContentInfo")]
191    pub Info(ns::Id)
192);
193
194impl Info {
195    /// The style of content represented by the filter.
196    #[objc::msg_send(style)]
197    pub fn style(&self) -> Style;
198
199    /// The point-to-pixel scale for the content.
200    #[objc::msg_send(pointPixelScale)]
201    pub fn point_pixel_scale(&self) -> f32;
202
203    /// The content rectangle in points.
204    #[objc::msg_send(contentRect)]
205    pub fn content_rect(&self) -> cg::Rect;
206}
207
208#[cfg(test)]
209mod tests {
210
211    use crate::{
212        blocks, define_obj_type, dispatch, objc,
213        sc::{
214            self,
215            stream::{Delegate, DelegateImpl, Output, OutputImpl},
216        },
217    };
218
219    use super::ShareableContent;
220
221    define_obj_type!(OutputObj + OutputImpl, usize, OUTPUT_CLS);
222
223    impl Output for OutputObj {}
224
225    #[objc::add_methods]
226    impl OutputImpl for OutputObj {}
227
228    define_obj_type!(DelegateObj + DelegateImpl, usize, OUTPUT_CLS);
229
230    impl Delegate for DelegateObj {}
231
232    #[objc::add_methods]
233    impl DelegateImpl for DelegateObj {}
234
235    #[tokio::test]
236    pub async fn current() {
237        let f = sc::ShareableContent::current().await.expect("result");
238        assert!(!f.windows().is_empty());
239        println!(
240            "current retain count {:?} {:?}",
241            f.as_type_ref().retain_count(),
242            f.windows().len()
243        );
244    }
245
246    #[tokio::test]
247    pub async fn current2() {
248        let f = sc::ShareableContent::current().await.expect("result");
249        assert!(!f.windows().is_empty());
250        println!(
251            "current retain count {:?} {:?}",
252            f.as_type_ref().retain_count(),
253            f.windows().len()
254        );
255    }
256
257    #[test]
258    pub fn current_ch() {
259        let sema = dispatch::Semaphore::new(0);
260
261        let signal_guard = sema.guard();
262        let mut bl = blocks::ResultCh::new2(move |content, error| {
263            _ = &signal_guard;
264            println!("nice {:?} {:?}", content, error);
265        });
266
267        dispatch::Queue::global(0).unwrap().async_mut(move || {
268            ShareableContent::current_with_ch_block(&mut bl);
269        });
270
271        sema.wait_forever();
272    }
273}