i_slint_backend_winit/
frame_throttle.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use std::rc::{Rc, Weak};
5
6use i_slint_core::timers::{Timer, TimerMode};
7
8use crate::winitwindowadapter::WinitWindowAdapter;
9
10pub fn create_frame_throttle(
11    window_adapter: Weak<WinitWindowAdapter>,
12    _is_wayland: bool,
13) -> Box<dyn FrameThrottle> {
14    if _is_wayland {
15        WinitBasedFrameThrottle::new(window_adapter)
16    } else {
17        TimerBasedFrameThrottle::new(window_adapter)
18    }
19}
20
21pub trait FrameThrottle {
22    fn request_throttled_redraw(&self);
23}
24
25struct TimerBasedFrameThrottle {
26    window_adapter: Weak<WinitWindowAdapter>,
27    timer: Rc<Timer>,
28}
29
30impl TimerBasedFrameThrottle {
31    fn new(window_adapter: Weak<WinitWindowAdapter>) -> Box<dyn FrameThrottle> {
32        Box::new(Self { window_adapter, timer: Rc::new(Timer::default()) })
33    }
34}
35
36impl FrameThrottle for TimerBasedFrameThrottle {
37    fn request_throttled_redraw(&self) {
38        if self.timer.running() {
39            return;
40        }
41        let refresh_interval_millihertz = self
42            .window_adapter
43            .upgrade()
44            .and_then(|adapter| adapter.winit_window())
45            .and_then(|winit_window| winit_window.current_monitor())
46            .and_then(|monitor| monitor.refresh_rate_millihertz())
47            .unwrap_or(60000) as u64;
48        let window_adapter = self.window_adapter.clone();
49        let timer = Rc::downgrade(&self.timer);
50        let interval =
51            std::time::Duration::from_millis((1000 * 1000) / refresh_interval_millihertz);
52        self.timer.start(TimerMode::Repeated, interval, move || {
53            redraw_now(&window_adapter);
54
55            let Some(timer) = timer.upgrade() else { return };
56            let Some(window_adapter) = window_adapter.upgrade() else { return };
57
58            let keep_running = window_adapter.pending_redraw();
59
60            if timer.running() {
61                if !keep_running {
62                    timer.stop();
63                }
64            } else {
65                if keep_running {
66                    timer.restart();
67                }
68            }
69        });
70    }
71}
72
73fn redraw_now(window_adapter: &Weak<WinitWindowAdapter>) {
74    let Some(winit_window) = window_adapter.upgrade().and_then(|adapter| adapter.winit_window())
75    else {
76        return;
77    };
78    winit_window.request_redraw();
79}
80
81struct WinitBasedFrameThrottle {
82    window_adapter: Weak<WinitWindowAdapter>,
83}
84
85impl WinitBasedFrameThrottle {
86    fn new(window_adapter: Weak<WinitWindowAdapter>) -> Box<dyn FrameThrottle> {
87        Box::new(Self { window_adapter })
88    }
89}
90
91impl FrameThrottle for WinitBasedFrameThrottle {
92    fn request_throttled_redraw(&self) {
93        redraw_now(&self.window_adapter)
94    }
95}