Skip to main content

core_graphics2/
display_stream.rs

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}