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