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