mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
This changes removes animation tracking from the `WindowMethods` trait and moves it to `ServoDelegate` and `WebViewDelegate`. - Animation changes per-`WebView` are now triggered in the compositor only when the value is updated there, rather than right after ticking animations. - Both `WebView` and `Servo` now expose an `animation()` method, so tracking animation state actually becomes unecessary in many cases, such as that of desktop servoshell, which can just read the value when the event loop spins. Testing: No tests necessary as the API layer is still untested. Later, tests will be added for the `WebView` API and this can be tested then. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Signed-off-by: Martin Robinson <mrobinson@igalia.com>
160 lines
5.8 KiB
Rust
160 lines
5.8 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
//! An event loop implementation that works in headless mode.
|
|
|
|
use std::sync::{Arc, Condvar, Mutex};
|
|
use std::time;
|
|
|
|
use log::warn;
|
|
use servo::EventLoopWaker;
|
|
use winit::error::EventLoopError;
|
|
use winit::event_loop::EventLoop as WinitEventLoop;
|
|
#[cfg(target_os = "macos")]
|
|
use winit::platform::macos::{ActivationPolicy, EventLoopBuilderExtMacOS};
|
|
|
|
use super::app::App;
|
|
|
|
/// Another process or thread has kicked the OS event loop with EventLoopWaker.
|
|
#[derive(Debug)]
|
|
pub struct WakerEvent;
|
|
|
|
/// The real or fake OS event loop.
|
|
#[allow(dead_code)]
|
|
enum EventLoop {
|
|
/// A real Winit windowing event loop.
|
|
Winit(Option<winit::event_loop::EventLoop<WakerEvent>>),
|
|
/// A fake event loop which contains a signalling flag used to ensure
|
|
/// that pending events get processed in a timely fashion, and a condition
|
|
/// variable to allow waiting on that flag changing state.
|
|
Headless(Arc<(Mutex<bool>, Condvar)>),
|
|
}
|
|
|
|
pub struct EventsLoop(EventLoop);
|
|
|
|
impl EventsLoop {
|
|
// Ideally, we could use the winit event loop in both modes,
|
|
// but on Linux, the event loop requires a X11 server.
|
|
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
|
pub fn new(_headless: bool, _has_output_file: bool) -> Result<EventsLoop, EventLoopError> {
|
|
Ok(EventsLoop(EventLoop::Winit(Some(
|
|
WinitEventLoop::with_user_event().build()?,
|
|
))))
|
|
}
|
|
#[cfg(target_os = "linux")]
|
|
pub fn new(headless: bool, _has_output_file: bool) -> Result<EventsLoop, EventLoopError> {
|
|
Ok(EventsLoop(if headless {
|
|
EventLoop::Headless(Arc::new((Mutex::new(false), Condvar::new())))
|
|
} else {
|
|
EventLoop::Winit(Some(WinitEventLoop::with_user_event().build()?))
|
|
}))
|
|
}
|
|
#[cfg(target_os = "macos")]
|
|
pub fn new(headless: bool, _has_output_file: bool) -> Result<EventsLoop, EventLoopError> {
|
|
Ok(EventsLoop(if headless {
|
|
EventLoop::Headless(Arc::new((Mutex::new(false), Condvar::new())))
|
|
} else {
|
|
let mut event_loop_builder = WinitEventLoop::with_user_event();
|
|
if _has_output_file {
|
|
// Prevent the window from showing in Dock.app, stealing focus,
|
|
// when generating an output file.
|
|
event_loop_builder.with_activation_policy(ActivationPolicy::Prohibited);
|
|
}
|
|
EventLoop::Winit(Some(event_loop_builder.build()?))
|
|
}))
|
|
}
|
|
}
|
|
|
|
impl EventsLoop {
|
|
pub fn create_event_loop_waker(&self) -> Box<dyn EventLoopWaker> {
|
|
match self.0 {
|
|
EventLoop::Winit(ref events_loop) => {
|
|
let events_loop = events_loop
|
|
.as_ref()
|
|
.expect("Can't create waker for unavailable event loop.");
|
|
Box::new(HeadedEventLoopWaker::new(events_loop))
|
|
},
|
|
EventLoop::Headless(ref data) => Box::new(HeadlessEventLoopWaker(data.clone())),
|
|
}
|
|
}
|
|
|
|
pub fn run_app(self, app: &mut App) {
|
|
match self.0 {
|
|
EventLoop::Winit(events_loop) => {
|
|
let events_loop = events_loop.expect("Can't run an unavailable event loop.");
|
|
events_loop
|
|
.run_app(app)
|
|
.expect("Failed while running events loop");
|
|
},
|
|
EventLoop::Headless(ref data) => {
|
|
let (flag, condvar) = &**data;
|
|
|
|
app.init(None);
|
|
loop {
|
|
self.sleep(flag, condvar);
|
|
if !app.handle_events_with_headless() {
|
|
break;
|
|
}
|
|
if !app.animating() {
|
|
*flag.lock().unwrap() = false;
|
|
}
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
fn sleep(&self, lock: &Mutex<bool>, condvar: &Condvar) {
|
|
// To avoid sleeping when we should be processing events, do two things:
|
|
// * before sleeping, check whether our signalling flag has been set
|
|
// * wait on a condition variable with a maximum timeout, to allow
|
|
// being woken up by any signals that occur while sleeping.
|
|
let guard = lock.lock().unwrap();
|
|
if *guard {
|
|
return;
|
|
}
|
|
let _ = condvar
|
|
.wait_timeout(guard, time::Duration::from_millis(5))
|
|
.unwrap();
|
|
}
|
|
}
|
|
|
|
struct HeadedEventLoopWaker {
|
|
proxy: Arc<Mutex<winit::event_loop::EventLoopProxy<WakerEvent>>>,
|
|
}
|
|
impl HeadedEventLoopWaker {
|
|
fn new(events_loop: &winit::event_loop::EventLoop<WakerEvent>) -> HeadedEventLoopWaker {
|
|
let proxy = Arc::new(Mutex::new(events_loop.create_proxy()));
|
|
HeadedEventLoopWaker { proxy }
|
|
}
|
|
}
|
|
impl EventLoopWaker for HeadedEventLoopWaker {
|
|
fn wake(&self) {
|
|
// Kick the OS event loop awake.
|
|
if let Err(err) = self.proxy.lock().unwrap().send_event(WakerEvent) {
|
|
warn!("Failed to wake up event loop ({}).", err);
|
|
}
|
|
}
|
|
fn clone_box(&self) -> Box<dyn EventLoopWaker> {
|
|
Box::new(HeadedEventLoopWaker {
|
|
proxy: self.proxy.clone(),
|
|
})
|
|
}
|
|
}
|
|
|
|
struct HeadlessEventLoopWaker(Arc<(Mutex<bool>, Condvar)>);
|
|
impl EventLoopWaker for HeadlessEventLoopWaker {
|
|
fn wake(&self) {
|
|
// Set the signalling flag and notify the condition variable.
|
|
// This ensures that any sleep operation is interrupted,
|
|
// and any non-sleeping operation will have a change to check
|
|
// the flag before going to sleep.
|
|
let (ref flag, ref condvar) = *self.0;
|
|
let mut flag = flag.lock().unwrap();
|
|
*flag = true;
|
|
condvar.notify_all();
|
|
}
|
|
fn clone_box(&self) -> Box<dyn EventLoopWaker> {
|
|
Box::new(HeadlessEventLoopWaker(self.0.clone()))
|
|
}
|
|
}
|