From a0ca34d9e85584fdee525ed08a48eb97f8991078 Mon Sep 17 00:00:00 2001 From: "Ngo Iok Ui (Wu Yu Wei)" Date: Thu, 19 Dec 2024 14:21:09 +0900 Subject: [PATCH] chore: remove deprecated winit method (#34638) * Add ApplicationHandler stub Signed-off-by: Wu Wayne * Implement ApplicationHandler Signed-off-by: Wu Wayne * Abstract common methods Signed-off-by: Wu Wayne * Impliment headless mode Signed-off-by: Wu Yuwei * Apply suggestions Signed-off-by: Wu Yuwei --------- Signed-off-by: Wu Wayne Signed-off-by: Wu Yuwei --- ports/servoshell/desktop/app.rs | 657 ++++++++++++++---------- ports/servoshell/desktop/cli.rs | 9 +- ports/servoshell/desktop/events_loop.rs | 93 +--- 3 files changed, 392 insertions(+), 367 deletions(-) diff --git a/ports/servoshell/desktop/app.rs b/ports/servoshell/desktop/app.rs index 9f14b3b8521..7ee1f9713bd 100644 --- a/ports/servoshell/desktop/app.rs +++ b/ports/servoshell/desktop/app.rs @@ -11,20 +11,24 @@ use std::time::Instant; use std::{env, fs}; use gleam::gl; -use log::{info, trace, warn}; +use log::{info, trace}; use servo::compositing::windowing::EmbedderEvent; use servo::compositing::CompositeTarget; use servo::config::{opts, set_pref}; +use servo::embedder_traits::EventLoopWaker; use servo::servo_config::pref; +use servo::url::ServoUrl; use servo::Servo; use surfman::GLApi; use webxr::glwindow::GlWindowDiscovery; #[cfg(target_os = "windows")] use webxr::openxr::{AppInfo, OpenXrDiscovery}; +use winit::application::ApplicationHandler; use winit::event::WindowEvent; +use winit::event_loop::{ActiveEventLoop, ControlFlow}; use winit::window::WindowId; -use super::events_loop::{EventsLoop, WakerEvent}; +use super::events_loop::{EventLoopGuard, EventsLoop, WakerEvent}; use super::minibrowser::Minibrowser; use super::webview::WebViewManager; use super::{headed_window, headless_window}; @@ -41,6 +45,11 @@ pub struct App { suspended: Cell, windows: HashMap>, minibrowser: Option>, + user_agent: Option, + waker: Box, + initial_url: ServoUrl, + t_start: Instant, + t: Instant, } enum Present { @@ -60,15 +69,13 @@ enum PumpResult { } impl App { - pub fn run( + pub fn new( + events_loop: &EventsLoop, no_native_titlebar: bool, device_pixel_ratio_override: Option, user_agent: Option, url: Option, - ) { - let events_loop = EventsLoop::new(opts::get().headless, opts::get().output_file.is_some()) - .expect("Failed to create events loop"); - + ) -> Self { // Implements window methods, used by compositor. let window = if opts::get().headless { // 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| { fs::metadata(path).is_ok() }); - + let t = Instant::now(); let mut app = App { event_queue: RefCell::new(vec![]), webviews: RefCell::new(webviews), @@ -99,6 +106,11 @@ impl App { suspended: Cell::new(false), windows: HashMap::new(), 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() { @@ -136,245 +148,77 @@ impl App { window.set_toolbar_height(minibrowser.toolbar_height); } - let t_start = Instant::now(); - let mut t = t_start; - let ev_waker = events_loop.create_event_loop_waker(); - events_loop.run_forever(move |event, control_flow| { - let now = Instant::now(); - trace_winit_event!(event, "@{:?} (+{:?}) {event:?}", now - t_start, now - t); - t = now; - if let winit::event::Event::NewEvents(winit::event::StartCause::Init) = event { - let surfman = window.rendering_context(); + app.windows.insert(window.id(), window); - let openxr_discovery = if pref!(dom.webxr.openxr.enabled) && !opts::get().headless { - #[cfg(target_os = "windows")] - let openxr = { - let app_info = AppInfo::new("Servoshell", 0, "Servo", 0); - Some(XrDiscovery::OpenXr(OpenXrDiscovery::new(None, app_info))) - }; - #[cfg(not(target_os = "windows"))] - let openxr = None; - - openxr - } else { - None - }; - - let glwindow_discovery = - if pref!(dom.webxr.glwindow.enabled) && !opts::get().headless { - let window = window.clone(); - let factory = Box::new(move || { - with_current_event_loop(|w| Ok(window.new_glwindow(w))) - .expect("An event loop should always be active in headed mode") - }); - Some(XrDiscovery::GlWindow(GlWindowDiscovery::new( - surfman.connection(), - surfman.adapter(), - surfman.context_attributes(), - factory, - ))) - } else { - None - }; - - let xr_discovery = openxr_discovery.or(glwindow_discovery); - - let window = window.clone(); - // Implements embedder methods, used by libservo and constellation. - let embedder = Box::new(EmbedderCallbacks::new(ev_waker.clone(), xr_discovery)); - - let composite_target = if app.minibrowser.is_some() { - CompositeTarget::Fbo - } else { - CompositeTarget::Window - }; - let servo_data = Servo::new( - embedder, - window.clone(), - user_agent.clone(), - composite_target, - ); - let mut servo = servo_data.servo; - - servo.handle_events(vec![EmbedderEvent::NewWebView( - initial_url.to_owned(), - servo_data.browser_id, - )]); - servo.setup_logging(); - - app.windows.insert(window.id(), window.clone()); - app.servo = Some(servo); - } - - // If self.servo is None here, it means that we're in the process of shutting down, - // 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 => {}, - } - }, - } - }); + app } - fn is_animating(&self) -> bool { + /// Initialize Application once event loop start running. + pub fn init(&mut self) { + 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 openxr_discovery = if pref!(dom.webxr.openxr.enabled) && !opts::get().headless { + #[cfg(target_os = "windows")] + let openxr = { + let app_info = AppInfo::new("Servoshell", 0, "Servo", 0); + Some(XrDiscovery::OpenXr(OpenXrDiscovery::new(None, app_info))) + }; + #[cfg(not(target_os = "windows"))] + let openxr = None; + + openxr + } else { + None + }; + + let glwindow_discovery = if pref!(dom.webxr.glwindow.enabled) && !opts::get().headless { + let window = window.clone(); + let factory = Box::new(move || { + with_current_event_loop(|w| Ok(window.new_glwindow(w))) + .expect("An event loop should always be active in headed mode") + }); + Some(XrDiscovery::GlWindow(GlWindowDiscovery::new( + surfman.connection(), + surfman.adapter(), + surfman.context_attributes(), + factory, + ))) + } else { + None + }; + + let xr_discovery = openxr_discovery.or(glwindow_discovery); + + let window = window.clone(); + // Implements embedder methods, used by libservo and constellation. + let embedder = Box::new(EmbedderCallbacks::new(self.waker.clone(), xr_discovery)); + + let composite_target = if self.minibrowser.is_some() { + CompositeTarget::Fbo + } else { + CompositeTarget::Window + }; + let servo_data = Servo::new( + embedder, + window.clone(), + self.user_agent.clone(), + composite_target, + ); + let mut servo = servo_data.servo; + + servo.handle_events(vec![EmbedderEvent::NewWebView( + self.initial_url.to_owned(), + servo_data.browser_id, + )]); + servo.setup_logging(); + + self.servo = Some(servo); + } + + pub fn is_animating(&self) -> bool { self.windows.iter().any(|(_, window)| window.is_animating()) } @@ -382,46 +226,6 @@ impl App { 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) { - 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 /// 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, + ) { + 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> { self.minibrowser.as_ref().map(|x| x.borrow_mut()) } } + +impl ApplicationHandler 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); + } +} diff --git a/ports/servoshell/desktop/cli.rs b/ports/servoshell/desktop/cli.rs index 78dd8426924..f590f022bb7 100644 --- a/ports/servoshell/desktop/cli.rs +++ b/ports/servoshell/desktop/cli.rs @@ -9,6 +9,7 @@ use log::error; use servo::config::opts::{self, ArgumentParsingResult}; use servo::servo_config::pref; +use super::events_loop::EventsLoop; use crate::desktop::app::App; use crate::panic_hook; @@ -99,12 +100,18 @@ pub fn main() { 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, device_pixel_ratio_override, user_agent, url_opt.map(|s| s.to_string()), ); + event_loop.run_app(&mut app); + crate::platform::deinit(clean_shutdown) } diff --git a/ports/servoshell/desktop/events_loop.rs b/ports/servoshell/desktop/events_loop.rs index c5dd7072612..c598dd5f2b3 100644 --- a/ports/servoshell/desktop/events_loop.rs +++ b/ports/servoshell/desktop/events_loop.rs @@ -11,11 +11,12 @@ use std::time; use log::warn; use servo::embedder_traits::EventLoopWaker; use winit::error::EventLoopError; -use winit::event::{Event, StartCause}; use winit::event_loop::{ActiveEventLoop, 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; @@ -86,40 +87,25 @@ impl EventsLoop { }, } } - - pub fn run_forever(self, mut callback: F) - where - F: 'static + FnMut(Event, &mut ControlFlow), - { + 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."); - #[allow(deprecated)] events_loop - .run(move |e, window_target| { - let mut control_flow = ControlFlow::default(); - let _guard = EventLoopGuard::new(window_target); - callback(e, &mut control_flow); - control_flow.apply_to(window_target); - }) + .run_app(app) .expect("Failed while running events loop"); }, EventLoop::Headless(ref data) => { let (flag, condvar) = &**data; - let mut event = Event::NewEvents(StartCause::Init); + app.init(); loop { self.sleep(flag, condvar); - let mut control_flow = ControlFlow::Poll; - callback(event, &mut control_flow); - event = Event::::UserEvent(WakerEvent); - - if control_flow != ControlFlow::Poll { - *flag.lock().unwrap() = false; - } - - if control_flow == ControlFlow::Exit { + if app.handle_events_with_headless() { 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 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 { proxy: Arc>>, } @@ -241,10 +170,10 @@ thread_local! { static CURRENT_EVENT_LOOP: Cell> = const { Cell::new(None) }; } -struct EventLoopGuard; +pub struct EventLoopGuard; impl EventLoopGuard { - fn new(event_loop: &ActiveEventLoop) -> Self { + pub fn new(event_loop: &ActiveEventLoop) -> Self { CURRENT_EVENT_LOOP.with(|cell| { assert!( cell.get().is_none(),