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