Skip to main content

core_graphics2/
display_stream.rs

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}