mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
chore: remove deprecated winit method (#34638)
* Add ApplicationHandler stub Signed-off-by: Wu Wayne <yuweiwu@pm.me> * Implement ApplicationHandler Signed-off-by: Wu Wayne <yuweiwu@pm.me> * Abstract common methods Signed-off-by: Wu Wayne <yuweiwu@pm.me> * Impliment headless mode Signed-off-by: Wu Yuwei <yuweiwu@pm.me> * Apply suggestions Signed-off-by: Wu Yuwei <yuweiwu@pm.me> --------- Signed-off-by: Wu Wayne <yuweiwu@pm.me> Signed-off-by: Wu Yuwei <yuweiwu@pm.me>
This commit is contained in:
parent
fcf996196b
commit
a0ca34d9e8
3 changed files with 392 additions and 367 deletions
|
@ -11,20 +11,24 @@ use std::time::Instant;
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
|
|
||||||
use gleam::gl;
|
use gleam::gl;
|
||||||
use log::{info, trace, warn};
|
use log::{info, trace};
|
||||||
use servo::compositing::windowing::EmbedderEvent;
|
use servo::compositing::windowing::EmbedderEvent;
|
||||||
use servo::compositing::CompositeTarget;
|
use servo::compositing::CompositeTarget;
|
||||||
use servo::config::{opts, set_pref};
|
use servo::config::{opts, set_pref};
|
||||||
|
use servo::embedder_traits::EventLoopWaker;
|
||||||
use servo::servo_config::pref;
|
use servo::servo_config::pref;
|
||||||
|
use servo::url::ServoUrl;
|
||||||
use servo::Servo;
|
use servo::Servo;
|
||||||
use surfman::GLApi;
|
use surfman::GLApi;
|
||||||
use webxr::glwindow::GlWindowDiscovery;
|
use webxr::glwindow::GlWindowDiscovery;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
use webxr::openxr::{AppInfo, OpenXrDiscovery};
|
use webxr::openxr::{AppInfo, OpenXrDiscovery};
|
||||||
|
use winit::application::ApplicationHandler;
|
||||||
use winit::event::WindowEvent;
|
use winit::event::WindowEvent;
|
||||||
|
use winit::event_loop::{ActiveEventLoop, ControlFlow};
|
||||||
use winit::window::WindowId;
|
use winit::window::WindowId;
|
||||||
|
|
||||||
use super::events_loop::{EventsLoop, WakerEvent};
|
use super::events_loop::{EventLoopGuard, EventsLoop, WakerEvent};
|
||||||
use super::minibrowser::Minibrowser;
|
use super::minibrowser::Minibrowser;
|
||||||
use super::webview::WebViewManager;
|
use super::webview::WebViewManager;
|
||||||
use super::{headed_window, headless_window};
|
use super::{headed_window, headless_window};
|
||||||
|
@ -41,6 +45,11 @@ pub struct App {
|
||||||
suspended: Cell<bool>,
|
suspended: Cell<bool>,
|
||||||
windows: HashMap<WindowId, Rc<dyn WindowPortsMethods>>,
|
windows: HashMap<WindowId, Rc<dyn WindowPortsMethods>>,
|
||||||
minibrowser: Option<RefCell<Minibrowser>>,
|
minibrowser: Option<RefCell<Minibrowser>>,
|
||||||
|
user_agent: Option<String>,
|
||||||
|
waker: Box<dyn EventLoopWaker>,
|
||||||
|
initial_url: ServoUrl,
|
||||||
|
t_start: Instant,
|
||||||
|
t: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Present {
|
enum Present {
|
||||||
|
@ -60,15 +69,13 @@ enum PumpResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn run(
|
pub fn new(
|
||||||
|
events_loop: &EventsLoop,
|
||||||
no_native_titlebar: bool,
|
no_native_titlebar: bool,
|
||||||
device_pixel_ratio_override: Option<f32>,
|
device_pixel_ratio_override: Option<f32>,
|
||||||
user_agent: Option<String>,
|
user_agent: Option<String>,
|
||||||
url: Option<String>,
|
url: Option<String>,
|
||||||
) {
|
) -> Self {
|
||||||
let events_loop = EventsLoop::new(opts::get().headless, opts::get().output_file.is_some())
|
|
||||||
.expect("Failed to create events loop");
|
|
||||||
|
|
||||||
// Implements window methods, used by compositor.
|
// Implements window methods, used by compositor.
|
||||||
let window = if opts::get().headless {
|
let window = if opts::get().headless {
|
||||||
// GL video rendering is not supported on headless windows.
|
// GL video rendering is not supported on headless windows.
|
||||||
|
@ -91,7 +98,7 @@ impl App {
|
||||||
let initial_url = get_default_url(url.as_deref(), env::current_dir().unwrap(), |path| {
|
let initial_url = get_default_url(url.as_deref(), env::current_dir().unwrap(), |path| {
|
||||||
fs::metadata(path).is_ok()
|
fs::metadata(path).is_ok()
|
||||||
});
|
});
|
||||||
|
let t = Instant::now();
|
||||||
let mut app = App {
|
let mut app = App {
|
||||||
event_queue: RefCell::new(vec![]),
|
event_queue: RefCell::new(vec![]),
|
||||||
webviews: RefCell::new(webviews),
|
webviews: RefCell::new(webviews),
|
||||||
|
@ -99,6 +106,11 @@ impl App {
|
||||||
suspended: Cell::new(false),
|
suspended: Cell::new(false),
|
||||||
windows: HashMap::new(),
|
windows: HashMap::new(),
|
||||||
minibrowser: None,
|
minibrowser: None,
|
||||||
|
user_agent,
|
||||||
|
waker: events_loop.create_event_loop_waker(),
|
||||||
|
initial_url: initial_url.clone(),
|
||||||
|
t_start: t,
|
||||||
|
t,
|
||||||
};
|
};
|
||||||
|
|
||||||
if window.winit_window().is_some() {
|
if window.winit_window().is_some() {
|
||||||
|
@ -136,14 +148,16 @@ impl App {
|
||||||
window.set_toolbar_height(minibrowser.toolbar_height);
|
window.set_toolbar_height(minibrowser.toolbar_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
let t_start = Instant::now();
|
app.windows.insert(window.id(), window);
|
||||||
let mut t = t_start;
|
|
||||||
let ev_waker = events_loop.create_event_loop_waker();
|
app
|
||||||
events_loop.run_forever(move |event, control_flow| {
|
}
|
||||||
let now = Instant::now();
|
|
||||||
trace_winit_event!(event, "@{:?} (+{:?}) {event:?}", now - t_start, now - t);
|
/// Initialize Application once event loop start running.
|
||||||
t = now;
|
pub fn init(&mut self) {
|
||||||
if let winit::event::Event::NewEvents(winit::event::StartCause::Init) = event {
|
self.suspended.set(false);
|
||||||
|
self.event_queue.borrow_mut().push(EmbedderEvent::Idle);
|
||||||
|
let (_, window) = self.windows.iter().next().unwrap();
|
||||||
let surfman = window.rendering_context();
|
let surfman = window.rendering_context();
|
||||||
|
|
||||||
let openxr_discovery = if pref!(dom.webxr.openxr.enabled) && !opts::get().headless {
|
let openxr_discovery = if pref!(dom.webxr.openxr.enabled) && !opts::get().headless {
|
||||||
|
@ -160,8 +174,7 @@ impl App {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let glwindow_discovery =
|
let glwindow_discovery = if pref!(dom.webxr.glwindow.enabled) && !opts::get().headless {
|
||||||
if pref!(dom.webxr.glwindow.enabled) && !opts::get().headless {
|
|
||||||
let window = window.clone();
|
let window = window.clone();
|
||||||
let factory = Box::new(move || {
|
let factory = Box::new(move || {
|
||||||
with_current_event_loop(|w| Ok(window.new_glwindow(w)))
|
with_current_event_loop(|w| Ok(window.new_glwindow(w)))
|
||||||
|
@ -181,9 +194,9 @@ impl App {
|
||||||
|
|
||||||
let window = window.clone();
|
let window = window.clone();
|
||||||
// Implements embedder methods, used by libservo and constellation.
|
// Implements embedder methods, used by libservo and constellation.
|
||||||
let embedder = Box::new(EmbedderCallbacks::new(ev_waker.clone(), xr_discovery));
|
let embedder = Box::new(EmbedderCallbacks::new(self.waker.clone(), xr_discovery));
|
||||||
|
|
||||||
let composite_target = if app.minibrowser.is_some() {
|
let composite_target = if self.minibrowser.is_some() {
|
||||||
CompositeTarget::Fbo
|
CompositeTarget::Fbo
|
||||||
} else {
|
} else {
|
||||||
CompositeTarget::Window
|
CompositeTarget::Window
|
||||||
|
@ -191,190 +204,21 @@ impl App {
|
||||||
let servo_data = Servo::new(
|
let servo_data = Servo::new(
|
||||||
embedder,
|
embedder,
|
||||||
window.clone(),
|
window.clone(),
|
||||||
user_agent.clone(),
|
self.user_agent.clone(),
|
||||||
composite_target,
|
composite_target,
|
||||||
);
|
);
|
||||||
let mut servo = servo_data.servo;
|
let mut servo = servo_data.servo;
|
||||||
|
|
||||||
servo.handle_events(vec![EmbedderEvent::NewWebView(
|
servo.handle_events(vec![EmbedderEvent::NewWebView(
|
||||||
initial_url.to_owned(),
|
self.initial_url.to_owned(),
|
||||||
servo_data.browser_id,
|
servo_data.browser_id,
|
||||||
)]);
|
)]);
|
||||||
servo.setup_logging();
|
servo.setup_logging();
|
||||||
|
|
||||||
app.windows.insert(window.id(), window.clone());
|
self.servo = Some(servo);
|
||||||
app.servo = Some(servo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If self.servo is None here, it means that we're in the process of shutting down,
|
pub fn is_animating(&self) -> bool {
|
||||||
// let's ignore events.
|
|
||||||
if app.servo.is_none() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let winit::event::Event::WindowEvent {
|
|
||||||
window_id: _,
|
|
||||||
event: winit::event::WindowEvent::RedrawRequested,
|
|
||||||
} = event
|
|
||||||
{
|
|
||||||
// We need to redraw the window for some reason.
|
|
||||||
trace!("RedrawRequested");
|
|
||||||
|
|
||||||
// WARNING: do not defer painting or presenting to some later tick of the event
|
|
||||||
// loop or servoshell may become unresponsive! (servo#30312)
|
|
||||||
if let Some(mut minibrowser) = app.minibrowser() {
|
|
||||||
minibrowser.update(
|
|
||||||
window.winit_window().unwrap(),
|
|
||||||
&mut app.webviews.borrow_mut(),
|
|
||||||
app.servo.as_ref().unwrap().offscreen_framebuffer_id(),
|
|
||||||
"RedrawRequested",
|
|
||||||
);
|
|
||||||
minibrowser.paint(window.winit_window().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
app.servo.as_mut().unwrap().present();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the event
|
|
||||||
let mut consumed = false;
|
|
||||||
if let Some(mut minibrowser) = app.minibrowser() {
|
|
||||||
match event {
|
|
||||||
winit::event::Event::WindowEvent {
|
|
||||||
event: WindowEvent::ScaleFactorChanged { scale_factor, .. },
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
// Intercept any ScaleFactorChanged events away from EguiGlow::on_window_event, so
|
|
||||||
// we can use our own logic for calculating the scale factor and set egui’s
|
|
||||||
// scale factor to that value manually.
|
|
||||||
let desired_scale_factor = window.hidpi_factor().get();
|
|
||||||
let effective_egui_zoom_factor = desired_scale_factor / scale_factor as f32;
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"window scale factor changed to {}, setting egui zoom factor to {}",
|
|
||||||
scale_factor, effective_egui_zoom_factor
|
|
||||||
);
|
|
||||||
|
|
||||||
minibrowser
|
|
||||||
.context
|
|
||||||
.egui_ctx
|
|
||||||
.set_zoom_factor(effective_egui_zoom_factor);
|
|
||||||
|
|
||||||
// Request a winit redraw event, so we can recomposite, update and paint
|
|
||||||
// the minibrowser, and present the new frame.
|
|
||||||
window.winit_window().unwrap().request_redraw();
|
|
||||||
},
|
|
||||||
winit::event::Event::WindowEvent {
|
|
||||||
ref event,
|
|
||||||
window_id: _,
|
|
||||||
} => {
|
|
||||||
let response =
|
|
||||||
minibrowser.on_window_event(window.winit_window().unwrap(), event);
|
|
||||||
// Update minibrowser if there's resize event to sync up with window.
|
|
||||||
if let WindowEvent::Resized(_) = event {
|
|
||||||
minibrowser.update(
|
|
||||||
window.winit_window().unwrap(),
|
|
||||||
&mut app.webviews.borrow_mut(),
|
|
||||||
app.servo.as_ref().unwrap().offscreen_framebuffer_id(),
|
|
||||||
"Sync WebView size with Window Resize event",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if response.repaint && *event != winit::event::WindowEvent::RedrawRequested
|
|
||||||
{
|
|
||||||
// Request a winit redraw event, so we can recomposite, update and paint
|
|
||||||
// the minibrowser, and present the new frame.
|
|
||||||
window.winit_window().unwrap().request_redraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO how do we handle the tab key? (see doc for consumed)
|
|
||||||
// Note that servo doesn’t yet support tabbing through links and inputs
|
|
||||||
consumed = response.consumed;
|
|
||||||
},
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !consumed {
|
|
||||||
app.queue_embedder_events_for_winit_event(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
let animating = app.is_animating();
|
|
||||||
|
|
||||||
// Block until the window gets an event
|
|
||||||
if !animating || app.suspended.get() {
|
|
||||||
control_flow.set_wait();
|
|
||||||
} else {
|
|
||||||
control_flow.set_poll();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume and handle any events from the Minibrowser.
|
|
||||||
if let Some(minibrowser) = app.minibrowser() {
|
|
||||||
let webviews = &mut app.webviews.borrow_mut();
|
|
||||||
let app_event_queue = &mut app.event_queue.borrow_mut();
|
|
||||||
minibrowser.queue_embedder_events_for_minibrowser_events(webviews, app_event_queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
match app.handle_events() {
|
|
||||||
PumpResult::Shutdown => {
|
|
||||||
control_flow.set_exit();
|
|
||||||
app.servo.take().unwrap().deinit();
|
|
||||||
if let Some(mut minibrowser) = app.minibrowser() {
|
|
||||||
minibrowser.context.destroy();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PumpResult::Continue { update, present } => {
|
|
||||||
if update {
|
|
||||||
if let Some(mut minibrowser) = app.minibrowser() {
|
|
||||||
let webviews = &mut app.webviews.borrow_mut();
|
|
||||||
if minibrowser.update_webview_data(webviews) {
|
|
||||||
// Update the minibrowser immediately. While we could update by requesting a
|
|
||||||
// redraw, doing so would delay the location update by two frames.
|
|
||||||
minibrowser.update(
|
|
||||||
window.winit_window().unwrap(),
|
|
||||||
webviews,
|
|
||||||
app.servo.as_ref().unwrap().offscreen_framebuffer_id(),
|
|
||||||
"update_location_in_toolbar",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match present {
|
|
||||||
Present::Immediate => {
|
|
||||||
// The window was resized.
|
|
||||||
trace!("PumpResult::Present::Immediate");
|
|
||||||
|
|
||||||
// If we had resized any of the viewports in response to this, we would need to
|
|
||||||
// call Servo::repaint_synchronously. At the moment we don’t, so there won’t be
|
|
||||||
// any paint scheduled, and calling it would hang the compositor forever.
|
|
||||||
if let Some(mut minibrowser) = app.minibrowser() {
|
|
||||||
minibrowser.update(
|
|
||||||
window.winit_window().unwrap(),
|
|
||||||
&mut app.webviews.borrow_mut(),
|
|
||||||
app.servo.as_ref().unwrap().offscreen_framebuffer_id(),
|
|
||||||
"PumpResult::Present::Immediate",
|
|
||||||
);
|
|
||||||
minibrowser.paint(window.winit_window().unwrap());
|
|
||||||
}
|
|
||||||
app.servo.as_mut().unwrap().present();
|
|
||||||
},
|
|
||||||
Present::Deferred => {
|
|
||||||
// The compositor has painted to this frame.
|
|
||||||
trace!("PumpResult::Present::Deferred");
|
|
||||||
|
|
||||||
// Request a winit redraw event, so we can paint the minibrowser and present.
|
|
||||||
// Otherwise, it's in headless mode and we present directly.
|
|
||||||
if let Some(window) = window.winit_window() {
|
|
||||||
window.request_redraw();
|
|
||||||
} else {
|
|
||||||
app.servo.as_mut().unwrap().present();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Present::None => {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_animating(&self) -> bool {
|
|
||||||
self.windows.iter().any(|(_, window)| window.is_animating())
|
self.windows.iter().any(|(_, window)| window.is_animating())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,46 +226,6 @@ impl App {
|
||||||
std::mem::take(&mut *self.event_queue.borrow_mut())
|
std::mem::take(&mut *self.event_queue.borrow_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the given winit Event, possibly converting it to an [EmbedderEvent] and
|
|
||||||
/// routing that to the App or relevant Window event queues.
|
|
||||||
fn queue_embedder_events_for_winit_event(&self, event: winit::event::Event<WakerEvent>) {
|
|
||||||
match event {
|
|
||||||
// App level events
|
|
||||||
winit::event::Event::Suspended => {
|
|
||||||
self.suspended.set(true);
|
|
||||||
},
|
|
||||||
winit::event::Event::Resumed => {
|
|
||||||
self.suspended.set(false);
|
|
||||||
self.event_queue.borrow_mut().push(EmbedderEvent::Idle);
|
|
||||||
},
|
|
||||||
winit::event::Event::UserEvent(_) => {
|
|
||||||
self.event_queue.borrow_mut().push(EmbedderEvent::Idle);
|
|
||||||
},
|
|
||||||
winit::event::Event::DeviceEvent { .. } => {},
|
|
||||||
|
|
||||||
// Window level events
|
|
||||||
winit::event::Event::WindowEvent {
|
|
||||||
window_id, event, ..
|
|
||||||
} => match self.windows.get(&window_id) {
|
|
||||||
None => {
|
|
||||||
warn!("Got an event from unknown window");
|
|
||||||
},
|
|
||||||
Some(window) => {
|
|
||||||
if event == winit::event::WindowEvent::RedrawRequested {
|
|
||||||
self.event_queue.borrow_mut().push(EmbedderEvent::Idle);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.queue_embedder_events_for_winit_event(event);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
winit::event::Event::LoopExiting |
|
|
||||||
winit::event::Event::AboutToWait |
|
|
||||||
winit::event::Event::MemoryWarning |
|
|
||||||
winit::event::Event::NewEvents(..) => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pumps events and messages between the embedder and Servo, where embedder events flow
|
/// Pumps events and messages between the embedder and Servo, where embedder events flow
|
||||||
/// towards Servo and embedder messages flow away from Servo, and also runs the compositor.
|
/// towards Servo and embedder messages flow away from Servo, and also runs the compositor.
|
||||||
///
|
///
|
||||||
|
@ -491,7 +295,292 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle events with winit contexts
|
||||||
|
pub fn handle_events_with_winit(
|
||||||
|
&mut self,
|
||||||
|
event_loop: &ActiveEventLoop,
|
||||||
|
window: Rc<dyn WindowPortsMethods>,
|
||||||
|
) {
|
||||||
|
match self.handle_events() {
|
||||||
|
PumpResult::Shutdown => {
|
||||||
|
event_loop.exit();
|
||||||
|
self.servo.take().unwrap().deinit();
|
||||||
|
if let Some(mut minibrowser) = self.minibrowser() {
|
||||||
|
minibrowser.context.destroy();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PumpResult::Continue { update, present } => {
|
||||||
|
if update {
|
||||||
|
if let Some(mut minibrowser) = self.minibrowser() {
|
||||||
|
let webviews = &mut self.webviews.borrow_mut();
|
||||||
|
if minibrowser.update_webview_data(webviews) {
|
||||||
|
// Update the minibrowser immediately. While we could update by requesting a
|
||||||
|
// redraw, doing so would delay the location update by two frames.
|
||||||
|
minibrowser.update(
|
||||||
|
window.winit_window().unwrap(),
|
||||||
|
webviews,
|
||||||
|
self.servo.as_ref().unwrap().offscreen_framebuffer_id(),
|
||||||
|
"update_location_in_toolbar",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match present {
|
||||||
|
Present::Immediate => {
|
||||||
|
// The window was resized.
|
||||||
|
trace!("PumpResult::Present::Immediate");
|
||||||
|
|
||||||
|
// If we had resized any of the viewports in response to this, we would need to
|
||||||
|
// call Servo::repaint_synchronously. At the moment we don’t, so there won’t be
|
||||||
|
// any paint scheduled, and calling it would hang the compositor forever.
|
||||||
|
if let Some(mut minibrowser) = self.minibrowser() {
|
||||||
|
minibrowser.update(
|
||||||
|
window.winit_window().unwrap(),
|
||||||
|
&mut self.webviews.borrow_mut(),
|
||||||
|
self.servo.as_ref().unwrap().offscreen_framebuffer_id(),
|
||||||
|
"PumpResult::Present::Immediate",
|
||||||
|
);
|
||||||
|
minibrowser.paint(window.winit_window().unwrap());
|
||||||
|
}
|
||||||
|
self.servo.as_mut().unwrap().present();
|
||||||
|
},
|
||||||
|
Present::Deferred => {
|
||||||
|
// The compositor has painted to this frame.
|
||||||
|
trace!("PumpResult::Present::Deferred");
|
||||||
|
// Request a winit redraw event, so we can paint the minibrowser and present.
|
||||||
|
// Otherwise, it's in headless mode and we present directly.
|
||||||
|
if let Some(window) = window.winit_window() {
|
||||||
|
window.request_redraw();
|
||||||
|
} else {
|
||||||
|
self.servo.as_mut().unwrap().present();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Present::None => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle all servo events with headless mode. Return true if servo request to shutdown.
|
||||||
|
pub fn handle_events_with_headless(&mut self) -> bool {
|
||||||
|
let now = Instant::now();
|
||||||
|
let event = winit::event::Event::UserEvent(WakerEvent);
|
||||||
|
trace_winit_event!(
|
||||||
|
event,
|
||||||
|
"@{:?} (+{:?}) {event:?}",
|
||||||
|
now - self.t_start,
|
||||||
|
now - self.t
|
||||||
|
);
|
||||||
|
self.t = now;
|
||||||
|
// If self.servo is None here, it means that we're in the process of shutting down,
|
||||||
|
// let's ignore events.
|
||||||
|
if self.servo.is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.event_queue.borrow_mut().push(EmbedderEvent::Idle);
|
||||||
|
|
||||||
|
let mut exit = false;
|
||||||
|
match self.handle_events() {
|
||||||
|
PumpResult::Shutdown => {
|
||||||
|
exit = true;
|
||||||
|
self.servo.take().unwrap().deinit();
|
||||||
|
if let Some(mut minibrowser) = self.minibrowser() {
|
||||||
|
minibrowser.context.destroy();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PumpResult::Continue { present, .. } => {
|
||||||
|
match present {
|
||||||
|
Present::Immediate => {
|
||||||
|
// The window was resized.
|
||||||
|
trace!("PumpResult::Present::Immediate");
|
||||||
|
self.servo.as_mut().unwrap().present();
|
||||||
|
},
|
||||||
|
Present::Deferred => {
|
||||||
|
// The compositor has painted to this frame.
|
||||||
|
trace!("PumpResult::Present::Deferred");
|
||||||
|
// In headless mode, we present directly.
|
||||||
|
self.servo.as_mut().unwrap().present();
|
||||||
|
},
|
||||||
|
Present::None => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
fn minibrowser(&self) -> Option<RefMut<Minibrowser>> {
|
fn minibrowser(&self) -> Option<RefMut<Minibrowser>> {
|
||||||
self.minibrowser.as_ref().map(|x| x.borrow_mut())
|
self.minibrowser.as_ref().map(|x| x.borrow_mut())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ApplicationHandler<WakerEvent> for App {
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
let _guard = EventLoopGuard::new(event_loop);
|
||||||
|
self.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_event(
|
||||||
|
&mut self,
|
||||||
|
event_loop: &ActiveEventLoop,
|
||||||
|
window_id: WindowId,
|
||||||
|
event: WindowEvent,
|
||||||
|
) {
|
||||||
|
let now = Instant::now();
|
||||||
|
trace_winit_event!(
|
||||||
|
event,
|
||||||
|
"@{:?} (+{:?}) {event:?}",
|
||||||
|
now - self.t_start,
|
||||||
|
now - self.t
|
||||||
|
);
|
||||||
|
self.t = now;
|
||||||
|
// If self.servo is None here, it means that we're in the process of shutting down,
|
||||||
|
// let's ignore events.
|
||||||
|
if self.servo.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(window) = self.windows.get(&window_id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let window = window.clone();
|
||||||
|
|
||||||
|
if event == winit::event::WindowEvent::RedrawRequested {
|
||||||
|
// We need to redraw the window for some reason.
|
||||||
|
trace!("RedrawRequested");
|
||||||
|
|
||||||
|
// WARNING: do not defer painting or presenting to some later tick of the event
|
||||||
|
// loop or servoshell may become unresponsive! (servo#30312)
|
||||||
|
if let Some(mut minibrowser) = self.minibrowser() {
|
||||||
|
minibrowser.update(
|
||||||
|
window.winit_window().unwrap(),
|
||||||
|
&mut self.webviews.borrow_mut(),
|
||||||
|
self.servo.as_ref().unwrap().offscreen_framebuffer_id(),
|
||||||
|
"RedrawRequested",
|
||||||
|
);
|
||||||
|
minibrowser.paint(window.winit_window().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.servo.as_mut().unwrap().present();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the event
|
||||||
|
let mut consumed = false;
|
||||||
|
if let Some(mut minibrowser) = self.minibrowser() {
|
||||||
|
match event {
|
||||||
|
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
||||||
|
// Intercept any ScaleFactorChanged events away from EguiGlow::on_window_event, so
|
||||||
|
// we can use our own logic for calculating the scale factor and set egui’s
|
||||||
|
// scale factor to that value manually.
|
||||||
|
let desired_scale_factor = window.hidpi_factor().get();
|
||||||
|
let effective_egui_zoom_factor = desired_scale_factor / scale_factor as f32;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"window scale factor changed to {}, setting egui zoom factor to {}",
|
||||||
|
scale_factor, effective_egui_zoom_factor
|
||||||
|
);
|
||||||
|
|
||||||
|
minibrowser
|
||||||
|
.context
|
||||||
|
.egui_ctx
|
||||||
|
.set_zoom_factor(effective_egui_zoom_factor);
|
||||||
|
|
||||||
|
// Request a winit redraw event, so we can recomposite, update and paint
|
||||||
|
// the minibrowser, and present the new frame.
|
||||||
|
window.winit_window().unwrap().request_redraw();
|
||||||
|
},
|
||||||
|
ref event => {
|
||||||
|
let response =
|
||||||
|
minibrowser.on_window_event(window.winit_window().unwrap(), event);
|
||||||
|
// Update minibrowser if there's resize event to sync up with window.
|
||||||
|
if let WindowEvent::Resized(_) = event {
|
||||||
|
minibrowser.update(
|
||||||
|
window.winit_window().unwrap(),
|
||||||
|
&mut self.webviews.borrow_mut(),
|
||||||
|
self.servo.as_ref().unwrap().offscreen_framebuffer_id(),
|
||||||
|
"Sync WebView size with Window Resize event",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if response.repaint && *event != winit::event::WindowEvent::RedrawRequested {
|
||||||
|
// Request a winit redraw event, so we can recomposite, update and paint
|
||||||
|
// the minibrowser, and present the new frame.
|
||||||
|
window.winit_window().unwrap().request_redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO how do we handle the tab key? (see doc for consumed)
|
||||||
|
// Note that servo doesn’t yet support tabbing through links and inputs
|
||||||
|
consumed = response.consumed;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !consumed {
|
||||||
|
if event == winit::event::WindowEvent::RedrawRequested {
|
||||||
|
self.event_queue.borrow_mut().push(EmbedderEvent::Idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.queue_embedder_events_for_winit_event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
let animating = self.is_animating();
|
||||||
|
|
||||||
|
// Block until the window gets an event
|
||||||
|
if !animating || self.suspended.get() {
|
||||||
|
event_loop.set_control_flow(ControlFlow::Wait);
|
||||||
|
} else {
|
||||||
|
event_loop.set_control_flow(ControlFlow::Poll);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume and handle any events from the Minibrowser.
|
||||||
|
if let Some(minibrowser) = self.minibrowser() {
|
||||||
|
let webviews = &mut self.webviews.borrow_mut();
|
||||||
|
let app_event_queue = &mut self.event_queue.borrow_mut();
|
||||||
|
minibrowser.queue_embedder_events_for_minibrowser_events(webviews, app_event_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.handle_events_with_winit(event_loop, window);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: WakerEvent) {
|
||||||
|
let now = Instant::now();
|
||||||
|
let event = winit::event::Event::UserEvent(event);
|
||||||
|
trace_winit_event!(
|
||||||
|
event,
|
||||||
|
"@{:?} (+{:?}) {event:?}",
|
||||||
|
now - self.t_start,
|
||||||
|
now - self.t
|
||||||
|
);
|
||||||
|
self.t = now;
|
||||||
|
// If self.servo is None here, it means that we're in the process of shutting down,
|
||||||
|
// let's ignore events.
|
||||||
|
if self.servo.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.event_queue.borrow_mut().push(EmbedderEvent::Idle);
|
||||||
|
|
||||||
|
let Some(window) = self.windows.values().next() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let window = window.clone();
|
||||||
|
|
||||||
|
let animating = self.is_animating();
|
||||||
|
|
||||||
|
// Block until the window gets an event
|
||||||
|
if !animating || self.suspended.get() {
|
||||||
|
event_loop.set_control_flow(ControlFlow::Wait);
|
||||||
|
} else {
|
||||||
|
event_loop.set_control_flow(ControlFlow::Poll);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume and handle any events from the Minibrowser.
|
||||||
|
if let Some(minibrowser) = self.minibrowser() {
|
||||||
|
let webviews = &mut self.webviews.borrow_mut();
|
||||||
|
let app_event_queue = &mut self.event_queue.borrow_mut();
|
||||||
|
minibrowser.queue_embedder_events_for_minibrowser_events(webviews, app_event_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.handle_events_with_winit(event_loop, window);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn suspended(&mut self, _: &ActiveEventLoop) {
|
||||||
|
self.suspended.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use log::error;
|
||||||
use servo::config::opts::{self, ArgumentParsingResult};
|
use servo::config::opts::{self, ArgumentParsingResult};
|
||||||
use servo::servo_config::pref;
|
use servo::servo_config::pref;
|
||||||
|
|
||||||
|
use super::events_loop::EventsLoop;
|
||||||
use crate::desktop::app::App;
|
use crate::desktop::app::App;
|
||||||
use crate::panic_hook;
|
use crate::panic_hook;
|
||||||
|
|
||||||
|
@ -99,12 +100,18 @@ pub fn main() {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
App::run(
|
let event_loop = EventsLoop::new(opts::get().headless, opts::get().output_file.is_some())
|
||||||
|
.expect("Failed to create events loop");
|
||||||
|
|
||||||
|
let mut app = App::new(
|
||||||
|
&event_loop,
|
||||||
do_not_use_native_titlebar,
|
do_not_use_native_titlebar,
|
||||||
device_pixel_ratio_override,
|
device_pixel_ratio_override,
|
||||||
user_agent,
|
user_agent,
|
||||||
url_opt.map(|s| s.to_string()),
|
url_opt.map(|s| s.to_string()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
event_loop.run_app(&mut app);
|
||||||
|
|
||||||
crate::platform::deinit(clean_shutdown)
|
crate::platform::deinit(clean_shutdown)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,12 @@ use std::time;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use servo::embedder_traits::EventLoopWaker;
|
use servo::embedder_traits::EventLoopWaker;
|
||||||
use winit::error::EventLoopError;
|
use winit::error::EventLoopError;
|
||||||
use winit::event::{Event, StartCause};
|
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoop as WinitEventLoop};
|
use winit::event_loop::{ActiveEventLoop, EventLoop as WinitEventLoop};
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use winit::platform::macos::{ActivationPolicy, EventLoopBuilderExtMacOS};
|
use winit::platform::macos::{ActivationPolicy, EventLoopBuilderExtMacOS};
|
||||||
|
|
||||||
|
use super::app::App;
|
||||||
|
|
||||||
/// Another process or thread has kicked the OS event loop with EventLoopWaker.
|
/// Another process or thread has kicked the OS event loop with EventLoopWaker.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct WakerEvent;
|
pub struct WakerEvent;
|
||||||
|
@ -86,40 +87,25 @@ impl EventsLoop {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn run_app(self, app: &mut App) {
|
||||||
pub fn run_forever<F>(self, mut callback: F)
|
|
||||||
where
|
|
||||||
F: 'static + FnMut(Event<WakerEvent>, &mut ControlFlow),
|
|
||||||
{
|
|
||||||
match self.0 {
|
match self.0 {
|
||||||
EventLoop::Winit(events_loop) => {
|
EventLoop::Winit(events_loop) => {
|
||||||
let events_loop = events_loop.expect("Can't run an unavailable event loop.");
|
let events_loop = events_loop.expect("Can't run an unavailable event loop.");
|
||||||
#[allow(deprecated)]
|
|
||||||
events_loop
|
events_loop
|
||||||
.run(move |e, window_target| {
|
.run_app(app)
|
||||||
let mut control_flow = ControlFlow::default();
|
|
||||||
let _guard = EventLoopGuard::new(window_target);
|
|
||||||
callback(e, &mut control_flow);
|
|
||||||
control_flow.apply_to(window_target);
|
|
||||||
})
|
|
||||||
.expect("Failed while running events loop");
|
.expect("Failed while running events loop");
|
||||||
},
|
},
|
||||||
EventLoop::Headless(ref data) => {
|
EventLoop::Headless(ref data) => {
|
||||||
let (flag, condvar) = &**data;
|
let (flag, condvar) = &**data;
|
||||||
let mut event = Event::NewEvents(StartCause::Init);
|
app.init();
|
||||||
loop {
|
loop {
|
||||||
self.sleep(flag, condvar);
|
self.sleep(flag, condvar);
|
||||||
let mut control_flow = ControlFlow::Poll;
|
if app.handle_events_with_headless() {
|
||||||
callback(event, &mut control_flow);
|
|
||||||
event = Event::<WakerEvent>::UserEvent(WakerEvent);
|
|
||||||
|
|
||||||
if control_flow != ControlFlow::Poll {
|
|
||||||
*flag.lock().unwrap() = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if control_flow == ControlFlow::Exit {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if !app.is_animating() {
|
||||||
|
*flag.lock().unwrap() = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -140,63 +126,6 @@ impl EventsLoop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
|
||||||
pub enum ControlFlow {
|
|
||||||
Poll,
|
|
||||||
|
|
||||||
#[default]
|
|
||||||
Wait,
|
|
||||||
|
|
||||||
WaitUntil(std::time::Instant),
|
|
||||||
|
|
||||||
/// winit removed their ControlFlow::Exit variant in 0.29.2
|
|
||||||
Exit,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ControlFlow {
|
|
||||||
fn apply_to(self, window_target: &ActiveEventLoop) {
|
|
||||||
match self {
|
|
||||||
ControlFlow::Poll => {
|
|
||||||
window_target.set_control_flow(winit::event_loop::ControlFlow::Poll)
|
|
||||||
},
|
|
||||||
ControlFlow::Wait => {
|
|
||||||
window_target.set_control_flow(winit::event_loop::ControlFlow::Wait)
|
|
||||||
},
|
|
||||||
ControlFlow::WaitUntil(instant) => {
|
|
||||||
window_target.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(instant))
|
|
||||||
},
|
|
||||||
ControlFlow::Exit => window_target.exit(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_poll(&mut self) {
|
|
||||||
*self = ControlFlow::Poll;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_wait(&mut self) {
|
|
||||||
*self = ControlFlow::Wait;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub fn set_wait_until(&mut self, instant: std::time::Instant) {
|
|
||||||
*self = ControlFlow::WaitUntil(instant);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_exit(&mut self) {
|
|
||||||
*self = ControlFlow::Exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<winit::event_loop::ControlFlow> for ControlFlow {
|
|
||||||
fn from(cf: winit::event_loop::ControlFlow) -> Self {
|
|
||||||
match cf {
|
|
||||||
winit::event_loop::ControlFlow::Poll => Self::Poll,
|
|
||||||
winit::event_loop::ControlFlow::Wait => Self::Wait,
|
|
||||||
winit::event_loop::ControlFlow::WaitUntil(instant) => Self::WaitUntil(instant),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct HeadedEventLoopWaker {
|
struct HeadedEventLoopWaker {
|
||||||
proxy: Arc<Mutex<winit::event_loop::EventLoopProxy<WakerEvent>>>,
|
proxy: Arc<Mutex<winit::event_loop::EventLoopProxy<WakerEvent>>>,
|
||||||
}
|
}
|
||||||
|
@ -241,10 +170,10 @@ thread_local! {
|
||||||
static CURRENT_EVENT_LOOP: Cell<Option<*const ActiveEventLoop>> = const { Cell::new(None) };
|
static CURRENT_EVENT_LOOP: Cell<Option<*const ActiveEventLoop>> = const { Cell::new(None) };
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EventLoopGuard;
|
pub struct EventLoopGuard;
|
||||||
|
|
||||||
impl EventLoopGuard {
|
impl EventLoopGuard {
|
||||||
fn new(event_loop: &ActiveEventLoop) -> Self {
|
pub fn new(event_loop: &ActiveEventLoop) -> Self {
|
||||||
CURRENT_EVENT_LOOP.with(|cell| {
|
CURRENT_EVENT_LOOP.with(|cell| {
|
||||||
assert!(
|
assert!(
|
||||||
cell.get().is_none(),
|
cell.get().is_none(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue