Skip to main content

core_video/
display_link.rs

1use std::ptr::{null, null_mut};
2
3use block::{Block, ConcreteBlock};
4use core_foundation::{
5    base::{Boolean, CFIndex, CFTypeID, TCFType},
6    impl_CFTypeDescription, impl_TCFType,
7};
8use core_graphics::display::CGDirectDisplayID;
9use libc::c_void;
10
11#[repr(C)]
12pub struct __CVDisplayLink(c_void);
13
14pub type CVDisplayLinkRef = *mut __CVDisplayLink;
15
16use crate::{
17    base::{CVOptionFlags, CVTime, CVTimeStamp},
18    r#return::{kCVReturnSuccess, CVReturn},
19    CGLContextObj, CGLPixelFormatObj,
20};
21
22pub type CVDisplayLinkOutputCallback = extern "C" fn(
23    displayLink: CVDisplayLinkRef,
24    inNow: *const CVTimeStamp,
25    inOutputTime: *const CVTimeStamp,
26    flagsIn: CVOptionFlags,
27    flagsOut: *mut CVOptionFlags,
28    displayLinkContext: *mut c_void,
29) -> CVReturn;
30
31pub type CVDisplayLinkOutputHandler =
32    *const Block<(CVDisplayLinkRef, *const CVTimeStamp, *const CVTimeStamp, CVOptionFlags, *mut CVOptionFlags), CVReturn>;
33
34pub type CGOpenGLDisplayMask = u32;
35
36extern "C" {
37    pub fn CVDisplayLinkGetTypeID() -> CFTypeID;
38    pub fn CVDisplayLinkCreateWithCGDisplays(
39        displayArray: *const CGDirectDisplayID,
40        count: CFIndex,
41        displayLinkOut: *mut CVDisplayLinkRef,
42    ) -> CVReturn;
43    pub fn CVDisplayLinkCreateWithOpenGLDisplayMask(mask: CGOpenGLDisplayMask, displayLinkOut: *mut CVDisplayLinkRef) -> CVReturn;
44    pub fn CVDisplayLinkCreateWithCGDisplay(displayID: CGDirectDisplayID, displayLinkOut: *mut CVDisplayLinkRef) -> CVReturn;
45    pub fn CVDisplayLinkCreateWithActiveCGDisplays(displayLinkOut: *mut CVDisplayLinkRef) -> CVReturn;
46    pub fn CVDisplayLinkSetCurrentCGDisplay(displayLink: CVDisplayLinkRef, displayID: CGDirectDisplayID) -> CVReturn;
47    pub fn CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(
48        displayLink: CVDisplayLinkRef,
49        cglContext: CGLContextObj,
50        CGLPixelFormatObj: CGLPixelFormatObj,
51    ) -> CVReturn;
52    pub fn CVDisplayLinkGetCurrentCGDisplay(displayLink: CVDisplayLinkRef) -> CGDirectDisplayID;
53    pub fn CVDisplayLinkSetOutputCallback(displayLink: CVDisplayLinkRef, callback: CVDisplayLinkOutputCallback, userInfo: *mut c_void) -> CVReturn;
54    pub fn CVDisplayLinkSetOutputHandler(displayLink: CVDisplayLinkRef, handler: CVDisplayLinkOutputHandler) -> CVReturn;
55    pub fn CVDisplayLinkStart(displayLink: CVDisplayLinkRef) -> CVReturn;
56    pub fn CVDisplayLinkStop(displayLink: CVDisplayLinkRef) -> CVReturn;
57    pub fn CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLink: CVDisplayLinkRef) -> CVTime;
58    pub fn CVDisplayLinkGetOutputVideoLatency(displayLink: CVDisplayLinkRef) -> CVTime;
59    pub fn CVDisplayLinkGetActualOutputVideoRefreshPeriod(displayLink: CVDisplayLinkRef) -> CVTime;
60    pub fn CVDisplayLinkIsRunning(displayLink: CVDisplayLinkRef) -> Boolean;
61    pub fn CVDisplayLinkGetCurrentTime(displayLink: CVDisplayLinkRef, outTime: *mut CVTime) -> CVReturn;
62    pub fn CVDisplayLinkTranslateTime(displayLink: CVDisplayLinkRef, inTime: *const CVTime, outTime: *mut CVTime) -> CVReturn;
63    pub fn CVDisplayLinkRetain(displayLink: CVDisplayLinkRef) -> CVDisplayLinkRef;
64    pub fn CVDisplayLinkRelease(displayLink: CVDisplayLinkRef);
65}
66
67pub struct CVDisplayLink(CVDisplayLinkRef);
68
69impl Drop for CVDisplayLink {
70    fn drop(&mut self) {
71        unsafe { CVDisplayLinkRelease(self.0) }
72    }
73}
74
75impl_TCFType!(CVDisplayLink, CVDisplayLinkRef, CVDisplayLinkGetTypeID);
76impl_CFTypeDescription!(CVDisplayLink);
77
78impl CVDisplayLink {
79    #[inline]
80    pub fn from_cg_displays(display_array: &[CGDirectDisplayID]) -> Result<CVDisplayLink, CVReturn> {
81        let mut display_link: CVDisplayLinkRef = null_mut();
82        unsafe {
83            let result = CVDisplayLinkCreateWithCGDisplays(display_array.as_ptr(), display_array.len() as CFIndex, &mut display_link);
84            if result == kCVReturnSuccess {
85                Ok(TCFType::wrap_under_create_rule(display_link))
86            } else {
87                Err(result)
88            }
89        }
90    }
91
92    #[inline]
93    pub fn from_opengl_display_mask(mask: CGOpenGLDisplayMask) -> Result<CVDisplayLink, CVReturn> {
94        let mut display_link: CVDisplayLinkRef = null_mut();
95        unsafe {
96            let result = CVDisplayLinkCreateWithOpenGLDisplayMask(mask, &mut display_link);
97            if result == kCVReturnSuccess {
98                Ok(TCFType::wrap_under_create_rule(display_link))
99            } else {
100                Err(result)
101            }
102        }
103    }
104
105    #[inline]
106    pub fn from_cg_display(display_id: CGDirectDisplayID) -> Result<CVDisplayLink, CVReturn> {
107        let mut display_link: CVDisplayLinkRef = null_mut();
108        unsafe {
109            let result = CVDisplayLinkCreateWithCGDisplay(display_id, &mut display_link);
110            if result == kCVReturnSuccess {
111                Ok(TCFType::wrap_under_create_rule(display_link))
112            } else {
113                Err(result)
114            }
115        }
116    }
117
118    #[inline]
119    pub fn from_active_cg_displays() -> Result<CVDisplayLink, CVReturn> {
120        let mut display_link: CVDisplayLinkRef = null_mut();
121        unsafe {
122            let result = CVDisplayLinkCreateWithActiveCGDisplays(&mut display_link);
123            if result == kCVReturnSuccess {
124                Ok(TCFType::wrap_under_create_rule(display_link))
125            } else {
126                Err(result)
127            }
128        }
129    }
130
131    #[inline]
132    pub fn set_current_cg_display(&self, display_id: CGDirectDisplayID) -> Result<(), CVReturn> {
133        let result = unsafe { CVDisplayLinkSetCurrentCGDisplay(self.as_concrete_TypeRef(), display_id) };
134        if result == kCVReturnSuccess {
135            Ok(())
136        } else {
137            Err(result)
138        }
139    }
140
141    #[inline]
142    pub unsafe fn set_current_cg_display_from_opengl_context(
143        &self,
144        cgl_context: CGLContextObj,
145        cgl_pixel_format: CGLPixelFormatObj,
146    ) -> Result<(), CVReturn> {
147        let result = unsafe { CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(self.as_concrete_TypeRef(), cgl_context, cgl_pixel_format) };
148        if result == kCVReturnSuccess {
149            Ok(())
150        } else {
151            Err(result)
152        }
153    }
154
155    #[inline]
156    pub fn get_current_cg_display(&self) -> CGDirectDisplayID {
157        unsafe { CVDisplayLinkGetCurrentCGDisplay(self.as_concrete_TypeRef()) }
158    }
159
160    pub unsafe fn set_output_callback(&self, callback: CVDisplayLinkOutputCallback, user_info: *mut c_void) -> Result<(), CVReturn> {
161        let result = unsafe { CVDisplayLinkSetOutputCallback(self.as_concrete_TypeRef(), callback, user_info) };
162        if result == kCVReturnSuccess {
163            Ok(())
164        } else {
165            Err(result)
166        }
167    }
168
169    pub fn set_output_closure<F>(&self, closure: Option<F>) -> Result<(), CVReturn>
170    where
171        F: Fn(&CVDisplayLink, &CVTimeStamp, &CVTimeStamp, CVOptionFlags, &mut CVOptionFlags) -> CVReturn + 'static,
172    {
173        let handler = closure.map(|closure| {
174            ConcreteBlock::new(
175                move |display_link: CVDisplayLinkRef,
176                      in_now: *const CVTimeStamp,
177                      in_output_time: *const CVTimeStamp,
178                      flags_in: CVOptionFlags,
179                      flags_out: *mut CVOptionFlags|
180                      -> CVReturn {
181                    let display_link = unsafe { CVDisplayLink::wrap_under_get_rule(display_link) };
182                    let in_now = unsafe { &*in_now };
183                    let in_output_time = unsafe { &*in_output_time };
184                    let flags_out = unsafe { &mut *flags_out };
185                    closure(&display_link, &in_now, &in_output_time, flags_in, flags_out)
186                },
187            )
188            .copy()
189        });
190        let result = unsafe { CVDisplayLinkSetOutputHandler(self.as_concrete_TypeRef(), handler.as_ref().map_or(null(), |h| &**h)) };
191        if result == kCVReturnSuccess {
192            Ok(())
193        } else {
194            Err(result)
195        }
196    }
197
198    #[inline]
199    pub fn start(&self) -> Result<(), CVReturn> {
200        let result = unsafe { CVDisplayLinkStart(self.as_concrete_TypeRef()) };
201        if result == kCVReturnSuccess {
202            Ok(())
203        } else {
204            Err(result)
205        }
206    }
207
208    #[inline]
209    pub fn stop(&self) -> Result<(), CVReturn> {
210        let result = unsafe { CVDisplayLinkStop(self.as_concrete_TypeRef()) };
211        if result == kCVReturnSuccess {
212            Ok(())
213        } else {
214            Err(result)
215        }
216    }
217
218    #[inline]
219    pub fn get_nominal_output_video_refresh_period(&self) -> CVTime {
220        unsafe { CVDisplayLinkGetNominalOutputVideoRefreshPeriod(self.as_concrete_TypeRef()) }
221    }
222
223    #[inline]
224    pub fn get_output_video_latency(&self) -> CVTime {
225        unsafe { CVDisplayLinkGetOutputVideoLatency(self.as_concrete_TypeRef()) }
226    }
227
228    #[inline]
229    pub fn get_actual_output_video_refresh_period(&self) -> CVTime {
230        unsafe { CVDisplayLinkGetActualOutputVideoRefreshPeriod(self.as_concrete_TypeRef()) }
231    }
232
233    #[inline]
234    pub fn is_running(&self) -> bool {
235        unsafe { CVDisplayLinkIsRunning(self.as_concrete_TypeRef()) != 0 }
236    }
237
238    #[inline]
239    pub fn get_current_time(&self) -> Result<CVTime, CVReturn> {
240        let mut outTime = CVTime::default();
241        let result = unsafe { CVDisplayLinkGetCurrentTime(self.as_concrete_TypeRef(), &mut outTime) };
242        if result == kCVReturnSuccess {
243            Ok(outTime)
244        } else {
245            Err(result)
246        }
247    }
248
249    #[inline]
250    pub fn translate_time(&self, in_time: &CVTime) -> Result<CVTime, CVReturn> {
251        let mut out_time = CVTime::default();
252        let result = unsafe { CVDisplayLinkTranslateTime(self.as_concrete_TypeRef(), in_time, &mut out_time) };
253        if result == kCVReturnSuccess {
254            Ok(out_time)
255        } else {
256            Err(result)
257        }
258    }
259}