use objc2::rc::Retained;
use objc2::runtime::{NSObject, NSObjectProtocol};
use objc2::{DefinedClass, MainThreadMarker, MainThreadOnly, define_class, msg_send, sel};
use objc2_foundation::{NSRunLoop, NSRunLoopCommonModes};
use objc2_quartz_core::CADisplayLink;
use winit::window::Window;
use super::ns_view;
pub(crate) struct Vsync {
link: Retained<CADisplayLink>,
_target: Retained<Ticker>,
}
impl Vsync {
pub(crate) fn start(window: &Window, on_tick: Box<dyn Fn()>) -> Option<Vsync> {
let mtm = MainThreadMarker::new()?;
let view = ns_view(window)?;
if !view.respondsToSelector(sel!(displayLinkWithTarget:selector:)) {
return None;
}
let target = Ticker::new(mtm, on_tick);
let link = unsafe { view.displayLinkWithTarget_selector(&target, sel!(tick:)) };
unsafe { link.addToRunLoop_forMode(&NSRunLoop::mainRunLoop(), NSRunLoopCommonModes) };
Some(Vsync {
link,
_target: target,
})
}
pub(crate) fn set_paused(&self, paused: bool) {
self.link.setPaused(paused);
}
}
impl Drop for Vsync {
fn drop(&mut self) {
self.link.invalidate();
}
}
struct Tick {
on_tick: Box<dyn Fn()>,
}
define_class!(
#[unsafe(super(NSObject))]
#[thread_kind = MainThreadOnly]
#[name = "InsetDisplayLinkTicker"]
#[ivars = Tick]
struct Ticker;
impl Ticker {
#[unsafe(method(tick:))]
fn tick(&self, _link: &CADisplayLink) {
(self.ivars().on_tick)();
}
}
);
impl Ticker {
fn new(mtm: MainThreadMarker, on_tick: Box<dyn Fn()>) -> Retained<Ticker> {
let this = mtm.alloc::<Ticker>().set_ivars(Tick { on_tick });
unsafe { msg_send![super(this), init] }
}
}