screen_capture_kit/
stream.rs

1use std::ptr::null_mut;
2
3use block2::RcBlock;
4use core_foundation::{base::TCFType, string::CFStringRef};
5use core_graphics::color::CGColor;
6use core_media::{sample_buffer::CMSampleBufferRef, time::CMTime, OSType};
7use dispatch2::Queue;
8use libc::size_t;
9use objc2::{
10    encode::{Encode, Encoding},
11    extern_class, msg_send, msg_send_id,
12    mutability::InteriorMutable,
13    rc::{Allocated, Id},
14    runtime::ProtocolObject,
15    ClassType, ProtocolType,
16};
17use objc2_foundation::{CGRect, NSArray, NSError, NSInteger, NSObject, NSObjectProtocol, NSString};
18
19use crate::{
20    encode,
21    shareable_content::{SCDisplay, SCRunningApplication, SCWindow},
22};
23
24#[repr(transparent)]
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub struct SCStreamOutputType(pub NSInteger);
27
28impl SCStreamOutputType {
29    #[doc(alias = "SCStreamOutputTypeScreen")]
30    pub const Screen: Self = Self(0);
31    #[doc(alias = "SCStreamOutputTypeAudio")]
32    pub const Audio: Self = Self(1);
33}
34
35unsafe impl Encode for SCStreamOutputType {
36    const ENCODING: Encoding = Encoding::Int;
37}
38
39#[repr(transparent)]
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub struct SCFrameStatus(pub NSInteger);
42
43impl SCFrameStatus {
44    #[doc(alias = "SCFrameStatusComplete")]
45    pub const Complete: Self = Self(0);
46    #[doc(alias = "SCFrameStatusIdle")]
47    pub const Idle: Self = Self(1);
48    #[doc(alias = "SCFrameStatusBlank")]
49    pub const Blank: Self = Self(2);
50    #[doc(alias = "SCFrameStatusSuspended")]
51    pub const Suspended: Self = Self(3);
52    #[doc(alias = "SCFrameStatusStarted")]
53    pub const Started: Self = Self(4);
54    #[doc(alias = "SCFrameStatusStopped")]
55    pub const Stopped: Self = Self(5);
56}
57
58extern_class!(
59    #[derive(Debug, PartialEq, Eq, Hash)]
60    pub struct SCContentFilter;
61
62    unsafe impl ClassType for SCContentFilter {
63        type Super = NSObject;
64        type Mutability = InteriorMutable;
65    }
66);
67
68unsafe impl NSObjectProtocol for SCContentFilter {}
69
70impl SCContentFilter {
71    pub fn new() -> Id<Self> {
72        unsafe { msg_send_id![SCContentFilter::class(), new] }
73    }
74
75    pub fn init_with_desktop_independent_window(this: Allocated<Self>, window: &SCWindow) -> Id<Self> {
76        unsafe { msg_send_id![this, initWithDesktopIndependentWindow: window] }
77    }
78
79    pub fn init_with_display_exclude_windows(this: Allocated<Self>, display: &SCDisplay, excluded: &NSArray<SCWindow>) -> Id<Self> {
80        unsafe { msg_send_id![this, initWithDisplay: display excludingWindows: excluded] }
81    }
82
83    pub fn init_with_display_include_windows(this: Allocated<Self>, display: &SCDisplay, included: &NSArray<SCWindow>) -> Id<Self> {
84        unsafe { msg_send_id![this, initWithDisplay: display includingWindows: included] }
85    }
86
87    pub fn init_with_display_exclude_applications(
88        this: Allocated<Self>,
89        display: &SCDisplay,
90        applications: &NSArray<SCRunningApplication>,
91        excepting_windows: &NSArray<SCWindow>,
92    ) -> Id<Self> {
93        unsafe { msg_send_id![this, initWithDisplay: display excludingApplications: applications exceptingWindows: excepting_windows] }
94    }
95
96    pub fn init_with_display_include_applications(
97        this: Allocated<Self>,
98        display: &SCDisplay,
99        applications: &NSArray<SCRunningApplication>,
100        excepting_windows: &NSArray<SCWindow>,
101    ) -> Id<Self> {
102        unsafe { msg_send_id![this, initWithDisplay: display includingApplications: applications exceptingWindows: excepting_windows] }
103    }
104}
105
106extern_class!(
107    #[derive(Debug, PartialEq, Eq, Hash)]
108    pub struct SCStreamConfiguration;
109
110    unsafe impl ClassType for SCStreamConfiguration {
111        type Super = NSObject;
112        type Mutability = InteriorMutable;
113    }
114);
115
116unsafe impl NSObjectProtocol for SCStreamConfiguration {}
117
118impl SCStreamConfiguration {
119    pub fn new() -> Id<Self> {
120        unsafe { msg_send_id![SCStreamConfiguration::class(), new] }
121    }
122
123    pub fn get_height(&self) -> size_t {
124        unsafe { msg_send![self, height] }
125    }
126
127    pub fn set_height(&self, height: size_t) {
128        unsafe { msg_send![self, setHeight: height] }
129    }
130
131    pub fn get_width(&self) -> size_t {
132        unsafe { msg_send![self, width] }
133    }
134
135    pub fn set_width(&self, width: size_t) {
136        unsafe { msg_send![self, setWidth: width] }
137    }
138
139    pub fn get_minimum_frame_interval(&self) -> CMTime {
140        unsafe { msg_send![self, minimumFrameInterval] }
141    }
142
143    pub fn set_minimum_frame_interval(&self, interval: CMTime) {
144        unsafe { msg_send![self, setMinimumFrameInterval: interval] }
145    }
146
147    pub fn get_pixel_format(&self) -> OSType {
148        unsafe { msg_send![self, pixelFormat] }
149    }
150
151    pub fn set_pixel_format(&self, format: OSType) {
152        unsafe { msg_send![self, setPixelFormat: format] }
153    }
154
155    pub fn get_scales_to_fit(&self) -> bool {
156        unsafe { msg_send![self, scalesToFit] }
157    }
158
159    pub fn set_scales_to_fit(&self, scales_to_fit: bool) {
160        unsafe { msg_send![self, setScalesToFit: scales_to_fit] }
161    }
162
163    pub fn get_show_cursor(&self) -> bool {
164        unsafe { msg_send![self, showCursor] }
165    }
166
167    pub fn set_show_cursor(&self, show_cursor: bool) {
168        unsafe { msg_send![self, setShowCursor: show_cursor] }
169    }
170
171    pub fn get_background_color(&self) -> CGColor {
172        unsafe { CGColor::wrap_under_get_rule(msg_send![self, backgroundColor]) }
173    }
174
175    pub fn set_background_color(&self, color: CGColor) {
176        unsafe { msg_send![self, setBackgroundColor: color.as_concrete_TypeRef()] }
177    }
178
179    pub fn get_source_rect(&self) -> CGRect {
180        unsafe { msg_send![self, sourceRect] }
181    }
182
183    pub fn set_source_rect(&self, rect: CGRect) {
184        unsafe { msg_send![self, setSourceRect: rect] }
185    }
186
187    pub fn get_destination_rect(&self) -> CGRect {
188        unsafe { msg_send![self, destinationRect] }
189    }
190
191    pub fn set_destination_rect(&self, rect: CGRect) {
192        unsafe { msg_send![self, setDestinationRect: rect] }
193    }
194
195    pub fn get_queue_depth(&self) -> NSInteger {
196        unsafe { msg_send![self, queueDepth] }
197    }
198
199    pub fn set_queue_depth(&self, depth: NSInteger) {
200        unsafe { msg_send![self, setQueueDepth: depth] }
201    }
202
203    pub fn get_color_matrix(&self) -> CFStringRef {
204        unsafe {
205            let color_matrix: encode::CFStringRef = msg_send![self, colorMatrix];
206            color_matrix as CFStringRef
207        }
208    }
209
210    pub fn set_color_matrix(&self, matrix: CFStringRef) {
211        unsafe { msg_send![self, setColorMatrix: matrix as encode::CFStringRef] }
212    }
213
214    pub fn get_color_space_name(&self) -> CFStringRef {
215        unsafe {
216            let color_space_name: encode::CFStringRef = msg_send![self, colorSpaceName];
217            color_space_name as CFStringRef
218        }
219    }
220
221    pub fn set_color_space_name(&self, name: CFStringRef) {
222        unsafe { msg_send![self, setColorSpaceName: name as encode::CFStringRef] }
223    }
224
225    pub fn get_captures_audio(&self) -> bool {
226        unsafe { msg_send![self, capturesAudio] }
227    }
228
229    pub fn set_captures_audio(&self, captures_audio: bool) {
230        unsafe { msg_send![self, setCapturesAudio: captures_audio] }
231    }
232
233    pub fn get_sample_rate(&self) -> f64 {
234        unsafe { msg_send![self, sampleRate] }
235    }
236
237    pub fn set_sample_rate(&self, rate: f64) {
238        unsafe { msg_send![self, setSampleRate: rate] }
239    }
240
241    pub fn get_channel_count(&self) -> size_t {
242        unsafe { msg_send![self, channelCount] }
243    }
244
245    pub fn set_channel_count(&self, count: size_t) {
246        unsafe { msg_send![self, setChannelCount: count] }
247    }
248
249    pub fn get_excludes_current_process_audio(&self) -> bool {
250        unsafe { msg_send![self, excludesCurrentProcessAudio] }
251    }
252
253    pub fn set_excludes_current_process_audio(&self, excludes_current_process_audio: bool) {
254        unsafe { msg_send![self, setExcludesCurrentProcessAudio: excludes_current_process_audio] }
255    }
256}
257
258pub type SCStreamFrameInfo = NSString;
259
260extern "C" {
261    pub static SCStreamFrameInfoStatus: &'static NSString;
262    pub static SCStreamFrameInfoDisplayTime: &'static NSString;
263    pub static SCStreamFrameInfoScaleFactor: &'static NSString;
264    pub static SCStreamFrameInfoContentScale: &'static NSString;
265    pub static SCStreamFrameInfoContentRect: &'static NSString;
266    pub static SCStreamFrameInfoDirtyRects: &'static NSString;
267    pub static SCStreamFrameInfoScreenRect: &'static NSString;
268}
269
270extern_class!(
271    #[derive(Debug, PartialEq, Eq, Hash)]
272    pub struct SCStream;
273
274    unsafe impl ClassType for SCStream {
275        type Super = NSObject;
276        type Mutability = InteriorMutable;
277    }
278);
279
280unsafe impl NSObjectProtocol for SCStream {}
281
282type CompletionHandler = RcBlock<dyn Fn(*mut NSError)>;
283
284impl SCStream {
285    pub fn new() -> Id<Self> {
286        unsafe { msg_send_id![SCStream::class(), new] }
287    }
288
289    pub fn init_with_filter(
290        this: Allocated<Self>,
291        filter: &SCContentFilter,
292        configuration: &SCStreamConfiguration,
293        delegate: &ProtocolObject<dyn SCStreamDelegate>,
294    ) -> Id<Self> {
295        unsafe { msg_send_id![this, initWithFilter: filter configuration: configuration delegate: delegate] }
296    }
297
298    pub fn add_stream_output(
299        &self,
300        output: &ProtocolObject<dyn SCStreamOutput>,
301        output_type: SCStreamOutputType,
302        queue: &Queue,
303    ) -> Result<bool, Id<NSError>> {
304        let mut error: *mut NSError = null_mut();
305        let result = unsafe {
306            msg_send![self, addStreamOutput: output type: output_type.0 sampleHandlerQueue: queue.as_raw() as *const NSObject error: &mut error]
307        };
308        if result {
309            Ok(result)
310        } else {
311            Err(unsafe { Id::retain(error).unwrap() })
312        }
313    }
314
315    pub fn remove_stream_output(&self, output: &ProtocolObject<dyn SCStreamOutput>, output_type: SCStreamOutputType) -> Result<bool, Id<NSError>> {
316        let mut error: *mut NSError = null_mut();
317        let result = unsafe { msg_send![self, removeStreamOutput: output type: output_type.0 error: &mut error] };
318        if result {
319            Ok(result)
320        } else {
321            Err(unsafe { Id::retain(error).unwrap() })
322        }
323    }
324
325    fn new_completion_handler<F>(closure: F) -> CompletionHandler
326    where
327        F: Fn(Option<Id<NSError>>) + 'static,
328    {
329        RcBlock::new(move |error: *mut NSError| {
330            closure(if error.is_null() {
331                None
332            } else {
333                unsafe { Id::retain(error) }
334            });
335        })
336    }
337
338    pub fn update_content_filter<F>(&self, content_filter: &SCContentFilter, closure: F)
339    where
340        F: Fn(Option<Id<NSError>>) + 'static,
341    {
342        let handler = Self::new_completion_handler(closure);
343        unsafe { msg_send![self, updateContentFilter: content_filter completionHandler: &*handler] }
344    }
345
346    pub fn update_configuration<F>(&self, stream_config: &SCStreamConfiguration, closure: F)
347    where
348        F: Fn(Option<Id<NSError>>) + 'static,
349    {
350        let handler = Self::new_completion_handler(closure);
351        unsafe { msg_send![self, updateConfiguration: stream_config completionHandler: &*handler] }
352    }
353
354    pub fn start_capture<F>(&self, closure: F)
355    where
356        F: Fn(Option<Id<NSError>>) + 'static,
357    {
358        let handler = Self::new_completion_handler(closure);
359        unsafe { msg_send![self, startCaptureWithCompletionHandler: &*handler] }
360    }
361
362    pub fn stop_capture<F>(&self, closure: F)
363    where
364        F: Fn(Option<Id<NSError>>) + 'static,
365    {
366        let handler = Self::new_completion_handler(closure);
367        unsafe { msg_send![self, stopCaptureWithCompletionHandler: &*handler] }
368    }
369}
370
371extern_protocol!(
372    pub unsafe trait SCStreamOutput: NSObjectProtocol {
373        #[method(stream:didOutputSampleBuffer:ofType:)]
374        #[optional]
375        unsafe fn stream_did_output_sample_buffer(&self, stream: &SCStream, sample_buffer: CMSampleBufferRef, of_type: SCStreamOutputType);
376    }
377
378    unsafe impl ProtocolType for dyn SCStreamOutput {}
379);
380
381extern_protocol!(
382    pub unsafe trait SCStreamDelegate: NSObjectProtocol {
383        #[method(stream:didStopWithError:)]
384        #[optional]
385        unsafe fn stream_did_stop_with_error(&self, stream: &SCStream, error: &NSError);
386    }
387
388    unsafe impl ProtocolType for dyn SCStreamDelegate {}
389);