ferroui_native/
ferro_native_render_timer.rs1use crate::interop::*;
4use ferroui_base::rendering::{IRenderTimer, RenderTimerTick};
5use ferroui_microcom::ComPtr;
6use std::sync::atomic::{AtomicBool, Ordering};
7use std::sync::{Arc, Mutex, PoisonError};
8use std::time::Instant;
9
10struct Shared {
12 stopwatch: Instant,
13 tick: Mutex<Option<RenderTimerTick>>,
14}
15
16struct TickCallback(Arc<Shared>);
19
20impl IFrnActionCallbackImpl for TickCallback {
21 fn run(&self) {
22 crate::callback_base::guard((), || {
23 let tick = self.0.tick.lock().unwrap_or_else(PoisonError::into_inner).clone();
24 if let Some(tick) = tick {
25 tick(self.0.stopwatch.elapsed());
26 }
27 })
28 }
29}
30
31pub struct FerroNativeRenderTimer {
33 platform_render_timer: ComPtr<IFrnPlatformRenderTimer>,
34 shared: Arc<Shared>,
35 registered: AtomicBool,
36}
37
38unsafe impl Send for FerroNativeRenderTimer {}
43unsafe impl Sync for FerroNativeRenderTimer {}
45
46impl FerroNativeRenderTimer {
47 pub fn new(platform_render_timer: ComPtr<IFrnPlatformRenderTimer>) -> Self {
48 Self {
49 platform_render_timer,
50 shared: Arc::new(Shared { stopwatch: Instant::now(), tick: Mutex::new(None) }),
51 registered: AtomicBool::new(false),
52 }
53 }
54
55 fn ensure_registered(&self) {
56 if !self.registered.swap(true, Ordering::SeqCst) {
57 let callback = IFrnActionCallback::from_impl(TickCallback(self.shared.clone()));
58 let registration_result = self.platform_render_timer.register_tick(Some(&callback));
59 if registration_result != 0 {
60 panic!(
61 "The native backend was not able to start the render timer. Native error code is: {registration_result}"
62 );
63 }
64 }
65 }
66}
67
68impl IRenderTimer for FerroNativeRenderTimer {
69 fn tick(&self) -> Option<RenderTimerTick> {
70 self.shared.tick.lock().unwrap_or_else(PoisonError::into_inner).clone()
71 }
72
73 fn set_tick(&self, value: Option<RenderTimerTick>) {
74 match value {
75 Some(value) => {
76 *self.shared.tick.lock().unwrap_or_else(PoisonError::into_inner) = Some(value);
77 self.ensure_registered();
78 self.platform_render_timer.start();
79 }
80 None => {
81 self.platform_render_timer.stop();
82 *self.shared.tick.lock().unwrap_or_else(PoisonError::into_inner) = None;
83 }
84 }
85 }
86
87 fn runs_in_background(&self) -> bool {
88 self.platform_render_timer.runs_in_background()
89 }
90}