Skip to main content

core_graphics2/
display_stream.rs

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