display_link/
macos.rs

1#![cfg(target_os = "macos")]
2
3pub mod cvdisplaylink;
4
5use crate::{
6    macos::cvdisplaylink::{CVDisplayLink, CVTimeStamp, DisplayLink as RawDisplayLink},
7    PauseError, ResumeError,
8};
9use std::{any::Any, ffi::c_void, mem};
10use time_point::TimePoint;
11
12unsafe extern "C" fn render<F>(
13    _: *mut CVDisplayLink,
14    _: *const CVTimeStamp,
15    in_out_timestamp: *const CVTimeStamp,
16    _: i64,
17    _: *mut i64,
18    display_link_context: *mut c_void,
19) -> i32
20where
21    F: FnMut(TimePoint),
22{
23    let in_out_timestamp = &*in_out_timestamp;
24    let time = mem::transmute::<_, std::time::Instant>(in_out_timestamp.host_time);
25    let f = &mut *(display_link_context as *mut F);
26    f(TimePoint::from_std_instant(time));
27    0
28}
29
30#[derive(Debug)]
31pub struct DisplayLink {
32    is_paused:    bool,
33    func:         Box<dyn Any>,
34    display_link: RawDisplayLink,
35}
36
37impl Drop for DisplayLink {
38    fn drop(&mut self) {
39        if !self.is_paused {
40            unsafe {
41                self.display_link.stop();
42            }
43        }
44    }
45}
46
47impl DisplayLink {
48    fn new_impl<R, F>(make_raw: R, callback: F) -> Option<Self>
49    where
50        R: FnOnce() -> Option<RawDisplayLink>,
51        F: 'static + FnMut(TimePoint) + Send,
52    {
53        let func = Box::new(callback);
54        unsafe {
55            let raw = Box::into_raw(func);
56            let func = Box::from_raw(raw);
57            let mut display_link = make_raw()?;
58            display_link.set_output_callback(render::<F>, raw as *mut c_void);
59            Some(DisplayLink {
60                is_paused: true,
61                func,
62                display_link,
63            })
64        }
65    }
66
67    /// Creates a new iOS `DisplayLink` instance.
68    ///
69    /// macos _does_ require the callback to be `Send`.
70    pub fn new<F>(callback: F) -> Option<Self>
71    where
72        F: 'static + FnMut(TimePoint) + Send,
73    {
74        Self::new_impl(|| unsafe { RawDisplayLink::new() }, callback)
75    }
76
77    pub fn on_display<F>(display_id: u32, callback: F) -> Option<Self>
78    where
79        F: 'static + FnMut(TimePoint) + Send,
80    {
81        Self::new_impl(
82            || unsafe { RawDisplayLink::on_display(display_id) },
83            callback,
84        )
85    }
86
87    #[cfg(feature = "winit")]
88    pub fn on_monitor<F>(monitor: &winit::monitor::MonitorHandle, callback: F) -> Option<Self>
89    where
90        F: 'static + FnMut(TimePoint) + Send,
91    {
92        use winit::platform::macos::MonitorHandleExtMacOS;
93        let id = monitor.native_id();
94        Self::on_display(id, callback)
95    }
96
97    pub fn set_current_display(&mut self, display_id: u32) {
98        unsafe { self.display_link.set_current_display(display_id) }
99    }
100
101    #[cfg(feature = "winit")]
102    pub fn set_current_monitor(&mut self, monitor: &winit::monitor::MonitorHandle) {
103        use winit::platform::macos::MonitorHandleExtMacOS;
104        let id = monitor.native_id();
105        self.set_current_display(id)
106    }
107
108    pub fn is_paused(&self) -> bool {
109        self.is_paused
110    }
111
112    pub fn pause(&mut self) -> Result<(), PauseError> {
113        if self.is_paused {
114            Err(PauseError::AlreadyPaused)
115        } else {
116            unsafe {
117                self.display_link.stop();
118                self.is_paused = true;
119                Ok(())
120            }
121        }
122    }
123
124    pub fn resume(&mut self) -> Result<(), ResumeError> {
125        if !self.is_paused {
126            Err(ResumeError::AlreadyRunning)
127        } else {
128            unsafe {
129                self.display_link.start();
130                self.is_paused = false;
131                Ok(())
132            }
133        }
134    }
135}