use std::cell::{Cell, LazyCell, RefCell};
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::{self, JoinHandle};
use std::time::Duration;
use crossbeam_channel::{RecvTimeoutError, Sender};
use embedder_traits::{EventLoopWaker, RefreshDriver};
use log::warn;
use servo_constellation_traits::EmbedderToConstellationMessage;
use timers::{BoxedTimerCallback, TimerEventRequest, TimerScheduler};
use crate::painter::Painter;
use crate::webview_renderer::WebViewRenderer;
pub(crate) struct BaseRefreshDriver {
waiting_for_frame: Arc<AtomicBool>,
event_loop_waker: Box<dyn EventLoopWaker>,
observers: RefCell<Vec<Rc<dyn RefreshDriverObserver>>>,
refresh_driver: Rc<dyn RefreshDriver>,
}
impl BaseRefreshDriver {
pub(crate) fn new(
event_loop_waker: Box<dyn EventLoopWaker>,
refresh_driver: Option<Rc<dyn RefreshDriver>>,
timer_refresh_driver: &LazyCell<Rc<TimerRefreshDriver>>,
) -> Self {
let refresh_driver = refresh_driver.unwrap_or_else(|| (**timer_refresh_driver).clone());
Self {
waiting_for_frame: Arc::new(AtomicBool::new(false)),
event_loop_waker,
observers: Default::default(),
refresh_driver,
}
}
pub(crate) fn add_observer(&self, observer: Rc<dyn RefreshDriverObserver>) {
let mut observers = self.observers.borrow_mut();
observers.push(observer);
if observers.len() == 1 {
self.observe_next_frame();
}
}
pub(crate) fn notify_will_paint(&self, painter: &mut Painter) {
let still_has_observers = {
let mut observers = self.observers.borrow_mut();
observers.retain(|observer| observer.frame_started(painter));
!observers.is_empty()
};
if still_has_observers {
self.observe_next_frame();
}
}
fn observe_next_frame(&self) {
self.waiting_for_frame.store(true, Ordering::Relaxed);
let waiting_for_frame = self.waiting_for_frame.clone();
let event_loop_waker = self.event_loop_waker.clone_box();
self.refresh_driver.observe_next_frame(Box::new(move || {
waiting_for_frame.store(false, Ordering::Relaxed);
event_loop_waker.wake();
}));
}
pub(crate) fn wait_to_paint(&self) -> bool {
!self.observers.borrow().is_empty() && self.waiting_for_frame.load(Ordering::Relaxed)
}
}
pub(crate) trait RefreshDriverObserver {
fn frame_started(&self, painter: &mut Painter) -> bool;
}
pub(crate) struct AnimationRefreshDriverObserver {
pub(crate) constellation_sender: Sender<EmbedderToConstellationMessage>,
pub(crate) animating: Cell<bool>,
}
impl AnimationRefreshDriverObserver {
pub(crate) fn new(constellation_sender: Sender<EmbedderToConstellationMessage>) -> Self {
Self {
constellation_sender,
animating: Default::default(),
}
}
pub(crate) fn notify_animation_state_changed(
&self,
webview_renderer: &WebViewRenderer,
) -> bool {
if !webview_renderer.animating() {
return false;
}
if let Err(error) =
self.constellation_sender
.send(EmbedderToConstellationMessage::TickAnimation(vec![
webview_renderer.id,
]))
{
warn!("Sending tick to constellation failed ({error:?}).");
}
if self.animating.get() {
return false;
}
self.animating.set(true);
true
}
}
impl RefreshDriverObserver for AnimationRefreshDriverObserver {
fn frame_started(&self, painter: &mut Painter) -> bool {
let animating_webviews = painter.animating_webviews();
if animating_webviews.is_empty() {
self.animating.set(false);
return false;
}
if let Err(error) =
self.constellation_sender
.send(EmbedderToConstellationMessage::TickAnimation(
animating_webviews,
))
{
warn!("Sending tick to constellation failed ({error:?}).");
return false;
}
self.animating.set(true);
true
}
}
enum TimerThreadMessage {
Request(TimerEventRequest),
Quit,
}
pub(crate) struct TimerRefreshDriver {
sender: Sender<TimerThreadMessage>,
join_handle: Option<JoinHandle<()>>,
}
impl Default for TimerRefreshDriver {
fn default() -> Self {
let (sender, receiver) = crossbeam_channel::unbounded::<TimerThreadMessage>();
let join_handle = thread::Builder::new()
.name(String::from("PaintTimerThread"))
.spawn(move || {
let mut scheduler = TimerScheduler::default();
loop {
let recv_result = match scheduler.next_deadline() {
Some(deadline) => receiver.recv_deadline(deadline),
None => receiver.recv().map_err(|_| RecvTimeoutError::Disconnected),
};
match recv_result {
Ok(TimerThreadMessage::Request(request)) => {
scheduler.schedule_timer(request);
},
Err(RecvTimeoutError::Timeout) => scheduler.dispatch_completed_timers(),
Ok(TimerThreadMessage::Quit) | Err(RecvTimeoutError::Disconnected) => {
return;
},
}
}
})
.expect("Could not create RefreshDriver timer thread.");
Self {
sender,
join_handle: Some(join_handle),
}
}
}
impl TimerRefreshDriver {
pub(crate) fn queue_timer(&self, duration: Duration, callback: BoxedTimerCallback) {
let _ = self
.sender
.send(TimerThreadMessage::Request(TimerEventRequest {
callback,
duration,
}));
}
}
impl RefreshDriver for TimerRefreshDriver {
fn observe_next_frame(&self, new_start_frame_callback: Box<dyn Fn() + Send + 'static>) {
const FRAME_DURATION: Duration = Duration::from_millis(1000 / 120);
self.queue_timer(FRAME_DURATION, new_start_frame_callback);
}
}
impl Drop for TimerRefreshDriver {
fn drop(&mut self) {
let _ = self.sender.send(TimerThreadMessage::Quit);
if let Some(join_handle) = self.join_handle.take() {
let _ = join_handle.join();
}
}
}