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