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);