1use std::{
2 ptr::{null, NonNull},
3 slice::from_raw_parts,
4};
5
6use block::{Block, ConcreteBlock, RcBlock};
7use core_foundation::{
8 base::{CFType, CFTypeID, TCFType},
9 declare_TCFType,
10 dictionary::{CFDictionary, CFDictionaryRef},
11 impl_CFTypeDescription, impl_TCFType,
12 runloop::{CFRunLoopSource, CFRunLoopSourceRef},
13 string::{CFString, CFStringRef},
14};
15use dispatch2::{DispatchObject, DispatchQueue};
16use libc::{c_void, size_t};
17use objc2_core_foundation::CFRetained;
18use objc2_io_surface::IOSurfaceRef;
19
20#[allow(non_camel_case_types)]
21pub type dispatch_queue_t = *mut c_void;
22
23use crate::{base::CGFloat, display::CGDirectDisplayID, error::CGError, geometry::CGRect};
24
25#[repr(C)]
26pub struct __CGDisplayStream(c_void);
27
28pub type CGDisplayStreamRef = *mut __CGDisplayStream;
29
30#[repr(C)]
31pub struct __CGDisplayStreamUpdate(c_void);
32
33pub type CGDisplayStreamUpdateRef = *const __CGDisplayStreamUpdate;
34
35#[repr(i32)]
36#[derive(PartialEq, Eq, Debug, Clone, Copy)]
37pub enum CGDisplayStreamUpdateRectType {
38 #[doc(alias = "kCGDisplayStreamUpdateRefreshedRects")]
39 RefreshedRects = 0,
40 #[doc(alias = "kCGDisplayStreamUpdateMovedRects")]
41 MovedRects = 1,
42 #[doc(alias = "kCGDisplayStreamUpdateDirtyRects")]
43 DirtyRects = 2,
44 #[doc(alias = "kCGDisplayStreamUpdateReducedDirtyRects")]
45 ReducedDirtyRects = 3,
46}
47
48#[repr(i32)]
49#[derive(PartialEq, Eq, Debug, Clone, Copy)]
50pub enum CGDisplayStreamFrameStatus {
51 #[doc(alias = "kCGDisplayStreamFrameStatusFrameComplete")]
52 FrameComplete = 0,
53 #[doc(alias = "kCGDisplayStreamFrameStatusFrameIdle")]
54 FrameIdle = 1,
55 #[doc(alias = "kCGDisplayStreamFrameStatusFrameBlank")]
56 FrameBlank = 2,
57 #[doc(alias = "kCGDisplayStreamFrameStatusStopped")]
58 Stopped = 3,
59}
60
61pub type CGDisplayStreamFrameAvailableHandler = *const Block<(CGDisplayStreamFrameStatus, u64, *const IOSurfaceRef, CGDisplayStreamUpdateRef), ()>;
62
63extern "C" {
64 pub fn CGDisplayStreamUpdateGetTypeID() -> CFTypeID;
65 pub fn CGDisplayStreamUpdateGetRects(
66 update: CGDisplayStreamUpdateRef,
67 rectType: CGDisplayStreamUpdateRectType,
68 rectCount: *mut usize,
69 ) -> *const CGRect;
70 pub fn CGDisplayStreamUpdateCreateMergedUpdate(
71 firstUpdate: CGDisplayStreamUpdateRef,
72 secondUpdate: CGDisplayStreamUpdateRef,
73 ) -> CGDisplayStreamUpdateRef;
74 pub fn CGDisplayStreamUpdateGetMovedRectsDelta(updateRef: CGDisplayStreamUpdateRef, dx: *mut CGFloat, dy: *mut CGFloat);
75 pub fn CGDisplayStreamUpdateGetDropCount(updateRef: CGDisplayStreamUpdateRef) -> size_t;
76
77 pub static kCGDisplayStreamSourceRect: CFStringRef;
78 pub static kCGDisplayStreamDestinationRect: CFStringRef;
79 pub static kCGDisplayStreamPreserveAspectRatio: CFStringRef;
80 pub static kCGDisplayStreamColorSpace: CFStringRef;
81 pub static kCGDisplayStreamMinimumFrameTime: CFStringRef;
82 pub static kCGDisplayStreamShowCursor: CFStringRef;
83 pub static kCGDisplayStreamQueueDepth: CFStringRef;
84 pub static kCGDisplayStreamYCbCrMatrix: CFStringRef;
85 pub static kCGDisplayStreamYCbCrMatrix_ITU_R_709_2: CFStringRef;
86 pub static kCGDisplayStreamYCbCrMatrix_ITU_R_601_4: CFStringRef;
87 pub static kCGDisplayStreamYCbCrMatrix_SMPTE_240M_1995: CFStringRef;
88
89 pub fn CGDisplayStreamGetTypeID() -> CFTypeID;
90 pub fn CGDisplayStreamCreate(
91 display: CGDirectDisplayID,
92 outputWidth: size_t,
93 outputHeight: size_t,
94 pixelFormat: i32,
95 properties: CFDictionaryRef,
96 handler: CGDisplayStreamFrameAvailableHandler,
97 ) -> CGDisplayStreamRef;
98 pub fn CGDisplayStreamCreateWithDispatchQueue(
99 display: CGDirectDisplayID,
100 outputWidth: size_t,
101 outputHeight: size_t,
102 pixelFormat: i32,
103 properties: CFDictionaryRef,
104 queue: dispatch_queue_t,
105 handler: CGDisplayStreamFrameAvailableHandler,
106 ) -> CGDisplayStreamRef;
107 pub fn CGDisplayStreamStart(stream: CGDisplayStreamRef) -> CGError;
108 pub fn CGDisplayStreamStop(stream: CGDisplayStreamRef) -> CGError;
109 pub fn CGDisplayStreamGetRunLoopSource(stream: CGDisplayStreamRef) -> CFRunLoopSourceRef;
110}
111
112declare_TCFType!(CGDisplayStreamUpdate, CGDisplayStreamUpdateRef);
113impl_TCFType!(CGDisplayStreamUpdate, CGDisplayStreamUpdateRef, CGDisplayStreamUpdateGetTypeID);
114impl_CFTypeDescription!(CGDisplayStreamUpdate);
115
116impl CGDisplayStreamUpdate {
117 pub fn new_merged_update(&self, other: &CGDisplayStreamUpdate) -> Result<CGDisplayStreamUpdate, ()> {
118 unsafe {
119 let update = CGDisplayStreamUpdateCreateMergedUpdate(self.as_concrete_TypeRef(), other.as_concrete_TypeRef());
120 if update.is_null() {
121 Err(())
122 } else {
123 Ok(TCFType::wrap_under_create_rule(update))
124 }
125 }
126 }
127
128 pub fn rects(&self, rect_type: CGDisplayStreamUpdateRectType) -> &[CGRect] {
129 unsafe {
130 let mut rect_count = 0;
131 let rects = CGDisplayStreamUpdateGetRects(self.as_concrete_TypeRef(), rect_type, &mut rect_count);
132 if rects.is_null() || rect_count == 0 {
133 &[]
134 } else {
135 from_raw_parts(rects, rect_count)
136 }
137 }
138 }
139
140 pub fn moved_rects_delta(&self) -> (CGFloat, CGFloat) {
141 unsafe {
142 let mut dx = 0.0;
143 let mut dy = 0.0;
144 CGDisplayStreamUpdateGetMovedRectsDelta(self.as_concrete_TypeRef(), &mut dx, &mut dy);
145 (dx, dy)
146 }
147 }
148
149 pub fn drop_count(&self) -> usize {
150 unsafe { CGDisplayStreamUpdateGetDropCount(self.as_concrete_TypeRef()) }
151 }
152}
153
154declare_TCFType!(CGDisplayStream, CGDisplayStreamRef);
155impl_TCFType!(CGDisplayStream, CGDisplayStreamRef, CGDisplayStreamGetTypeID);
156impl_CFTypeDescription!(CGDisplayStream);
157
158impl CGDisplayStream {
159 fn new_frame_available_handler<F>(closure: F) -> RcBlock<(CGDisplayStreamFrameStatus, u64, *const IOSurfaceRef, CGDisplayStreamUpdateRef), ()>
160 where
161 F: Fn(CGDisplayStreamFrameStatus, u64, Option<CFRetained<IOSurfaceRef>>, Option<CGDisplayStreamUpdate>) + 'static,
162 {
163 ConcreteBlock::new(
164 move |status: CGDisplayStreamFrameStatus, timestamp: u64, surface: *const IOSurfaceRef, update: CGDisplayStreamUpdateRef| {
165 let surface = NonNull::new(surface.cast_mut()).map(|surface| unsafe { CFRetained::retain(surface) });
168 let update = if update.is_null() {
169 None
170 } else {
171 Some(unsafe { CGDisplayStreamUpdate::wrap_under_get_rule(update as CGDisplayStreamUpdateRef) })
172 };
173 closure(status, timestamp, surface, update);
174 },
175 )
176 .copy()
177 }
178
179 pub fn new<F>(
180 display: CGDirectDisplayID,
181 output_width: size_t,
182 output_height: size_t,
183 pixel_format: i32,
184 properties: Option<&CFDictionary<CFString, CFType>>,
185 closure: F,
186 ) -> Result<CGDisplayStream, ()>
187 where
188 F: Fn(CGDisplayStreamFrameStatus, u64, Option<CFRetained<IOSurfaceRef>>, Option<CGDisplayStreamUpdate>) + 'static,
189 {
190 let stream = unsafe {
191 CGDisplayStreamCreate(
192 display,
193 output_width,
194 output_height,
195 pixel_format,
196 properties.map(|properties| properties.as_concrete_TypeRef()).unwrap_or(null()),
197 &*Self::new_frame_available_handler(closure),
198 )
199 };
200 if stream.is_null() {
201 Err(())
202 } else {
203 Ok(unsafe { TCFType::wrap_under_create_rule(stream) })
204 }
205 }
206
207 pub fn new_with_dispatch_queue<F>(
208 display: CGDirectDisplayID,
209 output_width: size_t,
210 output_height: size_t,
211 pixel_format: i32,
212 properties: Option<&CFDictionary<CFString, CFType>>,
213 queue: &DispatchQueue,
214 closure: F,
215 ) -> Result<CGDisplayStream, ()>
216 where
217 F: Fn(CGDisplayStreamFrameStatus, u64, Option<CFRetained<IOSurfaceRef>>, Option<CGDisplayStreamUpdate>) + 'static,
218 {
219 let stream = unsafe {
220 CGDisplayStreamCreateWithDispatchQueue(
221 display,
222 output_width,
223 output_height,
224 pixel_format,
225 properties.map(|properties| properties.as_concrete_TypeRef()).unwrap_or(null()),
226 queue.as_raw().as_ptr() as dispatch_queue_t,
227 &*Self::new_frame_available_handler(closure),
228 )
229 };
230 if stream.is_null() {
231 Err(())
232 } else {
233 Ok(unsafe { TCFType::wrap_under_create_rule(stream) })
234 }
235 }
236
237 pub fn start(&self) -> CGError {
238 unsafe { CGDisplayStreamStart(self.as_concrete_TypeRef()) }
239 }
240
241 pub fn stop(&self) -> CGError {
242 unsafe { CGDisplayStreamStop(self.as_concrete_TypeRef()) }
243 }
244
245 pub fn run_loop_source(&self) -> Option<CFRunLoopSource> {
246 unsafe {
247 let source = CGDisplayStreamGetRunLoopSource(self.as_concrete_TypeRef());
248 if source.is_null() {
249 None
250 } else {
251 Some(TCFType::wrap_under_create_rule(source))
252 }
253 }
254 }
255}
256
257pub enum CGDisplayStreamProperties {
258 SourceRect,
259 DestinationRect,
260 PreserveAspectRatio,
261 ColorSpace,
262 MinimumFrameTime,
263 ShowCursor,
264 QueueDepth,
265 YCbCrMatrix,
266}
267
268impl From<CGDisplayStreamProperties> for CFStringRef {
269 fn from(key: CGDisplayStreamProperties) -> Self {
270 unsafe {
271 match key {
272 CGDisplayStreamProperties::SourceRect => kCGDisplayStreamSourceRect,
273 CGDisplayStreamProperties::DestinationRect => kCGDisplayStreamDestinationRect,
274 CGDisplayStreamProperties::PreserveAspectRatio => kCGDisplayStreamPreserveAspectRatio,
275 CGDisplayStreamProperties::ColorSpace => kCGDisplayStreamColorSpace,
276 CGDisplayStreamProperties::MinimumFrameTime => kCGDisplayStreamMinimumFrameTime,
277 CGDisplayStreamProperties::ShowCursor => kCGDisplayStreamShowCursor,
278 CGDisplayStreamProperties::QueueDepth => kCGDisplayStreamQueueDepth,
279 CGDisplayStreamProperties::YCbCrMatrix => kCGDisplayStreamYCbCrMatrix,
280 }
281 }
282 }
283}
284
285impl From<CGDisplayStreamProperties> for CFString {
286 fn from(key: CGDisplayStreamProperties) -> Self {
287 unsafe { CFString::wrap_under_get_rule(CFStringRef::from(key)) }
288 }
289}
290
291pub enum CGDisplayStreamYCbCrMatrices {
292 ITU_R_709_2,
293 ITU_R_601_4,
294 SMPTE_240M_1995,
295}
296
297impl From<CGDisplayStreamYCbCrMatrices> for CFStringRef {
298 fn from(matrix: CGDisplayStreamYCbCrMatrices) -> Self {
299 unsafe {
300 match matrix {
301 CGDisplayStreamYCbCrMatrices::ITU_R_709_2 => kCGDisplayStreamYCbCrMatrix_ITU_R_709_2,
302 CGDisplayStreamYCbCrMatrices::ITU_R_601_4 => kCGDisplayStreamYCbCrMatrix_ITU_R_601_4,
303 CGDisplayStreamYCbCrMatrices::SMPTE_240M_1995 => kCGDisplayStreamYCbCrMatrix_SMPTE_240M_1995,
304 }
305 }
306 }
307}
308
309impl From<CGDisplayStreamYCbCrMatrices> for CFString {
310 fn from(matrix: CGDisplayStreamYCbCrMatrices) -> Self {
311 unsafe { CFString::wrap_under_get_rule(CFStringRef::from(matrix)) }
312 }
313}