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 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}