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