core_graphics2/
display_stream.rs

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}