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