display_linker.rs

  1use std::{
  2    ffi::c_void,
  3    mem,
  4    sync::{Arc, Weak},
  5};
  6
  7use crate::{DisplayId, PlatformDisplayLinker};
  8use collections::HashMap;
  9use parking_lot::Mutex;
 10pub use sys::CVTimeStamp as VideoTimestamp;
 11
 12pub struct MacDisplayLinker {
 13    links: Mutex<HashMap<DisplayId, MacDisplayLink>>,
 14}
 15
 16struct MacDisplayLink {
 17    system_link: Mutex<sys::DisplayLink>,
 18    _output_callback: Arc<OutputCallback>,
 19}
 20
 21unsafe impl Send for MacDisplayLink {}
 22
 23impl MacDisplayLinker {
 24    pub fn new() -> Self {
 25        MacDisplayLinker {
 26            links: Default::default(),
 27        }
 28    }
 29}
 30
 31type OutputCallback = Mutex<Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp)>>;
 32
 33impl PlatformDisplayLinker for MacDisplayLinker {
 34    fn set_output_callback(
 35        &self,
 36        display_id: DisplayId,
 37        output_callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp)>,
 38    ) {
 39        if let Some(mut system_link) = unsafe { sys::DisplayLink::on_display(display_id.0) } {
 40            let callback = Arc::new(Mutex::new(output_callback));
 41            let weak_callback_ptr: *const OutputCallback = Arc::downgrade(&callback).into_raw();
 42            unsafe { system_link.set_output_callback(trampoline, weak_callback_ptr as *mut c_void) }
 43
 44            self.links.lock().insert(
 45                display_id,
 46                MacDisplayLink {
 47                    _output_callback: callback,
 48                    system_link: Mutex::new(system_link),
 49                },
 50            );
 51        } else {
 52            log::warn!("DisplayLink could not be obtained for {:?}", display_id);
 53            return;
 54        }
 55    }
 56
 57    fn start(&self, display_id: DisplayId) {
 58        if let Some(link) = self.links.lock().get_mut(&display_id) {
 59            unsafe {
 60                link.system_link.lock().start();
 61            }
 62        } else {
 63            log::warn!("No DisplayLink callback registered for {:?}", display_id)
 64        }
 65    }
 66
 67    fn stop(&self, display_id: DisplayId) {
 68        if let Some(link) = self.links.lock().get_mut(&display_id) {
 69            unsafe {
 70                link.system_link.lock().stop();
 71            }
 72        } else {
 73            log::warn!("No DisplayLink callback registered for {:?}", display_id)
 74        }
 75    }
 76}
 77
 78unsafe extern "C" fn trampoline(
 79    _display_link_out: *mut sys::CVDisplayLink,
 80    current_time: *const sys::CVTimeStamp,
 81    output_time: *const sys::CVTimeStamp,
 82    _flags_in: i64,
 83    _flags_out: *mut i64,
 84    user_data: *mut c_void,
 85) -> i32 {
 86    if let Some((current_time, output_time)) = current_time.as_ref().zip(output_time.as_ref()) {
 87        let output_callback: Weak<OutputCallback> =
 88            Weak::from_raw(user_data as *mut OutputCallback);
 89        if let Some(output_callback) = output_callback.upgrade() {
 90            (output_callback.lock())(current_time, output_time)
 91        }
 92        mem::forget(output_callback);
 93    }
 94    0
 95}
 96
 97mod sys {
 98    //! Derived from display-link crate under the fololwing license:
 99    //! https://github.com/BrainiumLLC/display-link/blob/master/LICENSE-MIT
100    //! Apple docs: [CVDisplayLink](https://developer.apple.com/documentation/corevideo/cvdisplaylinkoutputcallback?language=objc)
101    #![allow(dead_code, non_upper_case_globals)]
102
103    use foreign_types::{foreign_type, ForeignType};
104    use std::{
105        ffi::c_void,
106        fmt::{Debug, Formatter, Result},
107    };
108
109    #[derive(Debug)]
110    pub enum CVDisplayLink {}
111
112    foreign_type! {
113        type CType = CVDisplayLink;
114        fn drop = CVDisplayLinkRelease;
115        fn clone = CVDisplayLinkRetain;
116        pub struct DisplayLink;
117        pub struct DisplayLinkRef;
118    }
119
120    impl Debug for DisplayLink {
121        fn fmt(&self, formatter: &mut Formatter) -> Result {
122            formatter
123                .debug_tuple("DisplayLink")
124                .field(&self.as_ptr())
125                .finish()
126        }
127    }
128
129    #[repr(C)]
130    #[derive(Clone, Copy)]
131    pub struct CVTimeStamp {
132        pub version: u32,
133        pub video_time_scale: i32,
134        pub video_time: i64,
135        pub host_time: u64,
136        pub rate_scalar: f64,
137        pub video_refresh_period: i64,
138        pub smpte_time: CVSMPTETime,
139        pub flags: u64,
140        pub reserved: u64,
141    }
142
143    pub type CVTimeStampFlags = u64;
144
145    pub const kCVTimeStampVideoTimeValid: CVTimeStampFlags = 1 << 0;
146    pub const kCVTimeStampHostTimeValid: CVTimeStampFlags = 1 << 1;
147    pub const kCVTimeStampSMPTETimeValid: CVTimeStampFlags = 1 << 2;
148    pub const kCVTimeStampVideoRefreshPeriodValid: CVTimeStampFlags = 1 << 3;
149    pub const kCVTimeStampRateScalarValid: CVTimeStampFlags = 1 << 4;
150    pub const kCVTimeStampTopField: CVTimeStampFlags = 1 << 16;
151    pub const kCVTimeStampBottomField: CVTimeStampFlags = 1 << 17;
152    pub const kCVTimeStampVideoHostTimeValid: CVTimeStampFlags =
153        kCVTimeStampVideoTimeValid | kCVTimeStampHostTimeValid;
154    pub const kCVTimeStampIsInterlaced: CVTimeStampFlags =
155        kCVTimeStampTopField | kCVTimeStampBottomField;
156
157    #[repr(C)]
158    #[derive(Clone, Copy)]
159    pub struct CVSMPTETime {
160        pub subframes: i16,
161        pub subframe_divisor: i16,
162        pub counter: u32,
163        pub time_type: u32,
164        pub flags: u32,
165        pub hours: i16,
166        pub minutes: i16,
167        pub seconds: i16,
168        pub frames: i16,
169    }
170
171    pub type CVSMPTETimeType = u32;
172
173    pub const kCVSMPTETimeType24: CVSMPTETimeType = 0;
174    pub const kCVSMPTETimeType25: CVSMPTETimeType = 1;
175    pub const kCVSMPTETimeType30Drop: CVSMPTETimeType = 2;
176    pub const kCVSMPTETimeType30: CVSMPTETimeType = 3;
177    pub const kCVSMPTETimeType2997: CVSMPTETimeType = 4;
178    pub const kCVSMPTETimeType2997Drop: CVSMPTETimeType = 5;
179    pub const kCVSMPTETimeType60: CVSMPTETimeType = 6;
180    pub const kCVSMPTETimeType5994: CVSMPTETimeType = 7;
181
182    pub type CVSMPTETimeFlags = u32;
183
184    pub const kCVSMPTETimeValid: CVSMPTETimeFlags = 1 << 0;
185    pub const kCVSMPTETimeRunning: CVSMPTETimeFlags = 1 << 1;
186
187    pub type CVDisplayLinkOutputCallback = unsafe extern "C" fn(
188        display_link_out: *mut CVDisplayLink,
189        // A pointer to the current timestamp. This represents the timestamp when the callback is called.
190        current_time: *const CVTimeStamp,
191        // A pointer to the output timestamp. This represents the timestamp for when the frame will be displayed.
192        output_time: *const CVTimeStamp,
193        // Unused
194        flags_in: i64,
195        // Unused
196        flags_out: *mut i64,
197        // A pointer to app-defined data.
198        display_link_context: *mut c_void,
199    ) -> i32;
200
201    #[link(name = "CoreFoundation", kind = "framework")]
202    #[link(name = "CoreVideo", kind = "framework")]
203    #[allow(improper_ctypes)]
204    extern "C" {
205        pub fn CVDisplayLinkCreateWithActiveCGDisplays(
206            display_link_out: *mut *mut CVDisplayLink,
207        ) -> i32;
208        pub fn CVDisplayLinkCreateWithCGDisplay(
209            display_id: u32,
210            display_link_out: *mut *mut CVDisplayLink,
211        ) -> i32;
212        pub fn CVDisplayLinkSetOutputCallback(
213            display_link: &mut DisplayLinkRef,
214            callback: CVDisplayLinkOutputCallback,
215            user_info: *mut c_void,
216        ) -> i32;
217        pub fn CVDisplayLinkSetCurrentCGDisplay(
218            display_link: &mut DisplayLinkRef,
219            display_id: u32,
220        ) -> i32;
221        pub fn CVDisplayLinkStart(display_link: &mut DisplayLinkRef) -> i32;
222        pub fn CVDisplayLinkStop(display_link: &mut DisplayLinkRef) -> i32;
223        pub fn CVDisplayLinkRelease(display_link: *mut CVDisplayLink);
224        pub fn CVDisplayLinkRetain(display_link: *mut CVDisplayLink) -> *mut CVDisplayLink;
225    }
226
227    impl DisplayLink {
228        /// Apple docs: [CVDisplayLinkCreateWithActiveCGDisplays](https://developer.apple.com/documentation/corevideo/1456863-cvdisplaylinkcreatewithactivecgd?language=objc)
229        pub unsafe fn new() -> Option<Self> {
230            let mut display_link: *mut CVDisplayLink = 0 as _;
231            let code = CVDisplayLinkCreateWithActiveCGDisplays(&mut display_link);
232            if code == 0 {
233                Some(DisplayLink::from_ptr(display_link))
234            } else {
235                None
236            }
237        }
238
239        /// Apple docs: [CVDisplayLinkCreateWithCGDisplay](https://developer.apple.com/documentation/corevideo/1456981-cvdisplaylinkcreatewithcgdisplay?language=objc)
240        pub unsafe fn on_display(display_id: u32) -> Option<Self> {
241            let mut display_link: *mut CVDisplayLink = 0 as _;
242            let code = CVDisplayLinkCreateWithCGDisplay(display_id, &mut display_link);
243            if code == 0 {
244                Some(DisplayLink::from_ptr(display_link))
245            } else {
246                None
247            }
248        }
249    }
250
251    impl DisplayLinkRef {
252        /// Apple docs: [CVDisplayLinkSetOutputCallback](https://developer.apple.com/documentation/corevideo/1457096-cvdisplaylinksetoutputcallback?language=objc)
253        pub unsafe fn set_output_callback(
254            &mut self,
255            callback: CVDisplayLinkOutputCallback,
256            user_info: *mut c_void,
257        ) {
258            assert_eq!(CVDisplayLinkSetOutputCallback(self, callback, user_info), 0);
259        }
260
261        /// Apple docs: [CVDisplayLinkSetCurrentCGDisplay](https://developer.apple.com/documentation/corevideo/1456768-cvdisplaylinksetcurrentcgdisplay?language=objc)
262        pub unsafe fn set_current_display(&mut self, display_id: u32) {
263            assert_eq!(CVDisplayLinkSetCurrentCGDisplay(self, display_id), 0);
264        }
265
266        /// Apple docs: [CVDisplayLinkStart](https://developer.apple.com/documentation/corevideo/1457193-cvdisplaylinkstart?language=objc)
267        pub unsafe fn start(&mut self) {
268            assert_eq!(CVDisplayLinkStart(self), 0);
269        }
270
271        /// Apple docs: [CVDisplayLinkStop](https://developer.apple.com/documentation/corevideo/1457281-cvdisplaylinkstop?language=objc)
272        pub unsafe fn stop(&mut self) {
273            assert_eq!(CVDisplayLinkStop(self), 0);
274        }
275    }
276}