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}