Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web: Avoid top-level Event #4142

Merged
merged 2 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 11 additions & 28 deletions src/platform_impl/web/event_loop/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use super::{backend, HasMonitorPermissionFuture, MonitorPermissionFuture};
use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, NotSupportedError};
use crate::event::Event;
use crate::event_loop::ActiveEventLoop as RootActiveEventLoop;
use crate::platform::web::{PollStrategy, WaitUntilStrategy};

Expand All @@ -24,20 +23,20 @@ impl EventLoop {
Ok(EventLoop { elw: ActiveEventLoop::new() })
}

pub fn run_app<A: ApplicationHandler>(self, mut app: A) -> ! {
let event_loop = self.elw.clone();

// SAFETY: Don't use `move` to make sure we leak the `event_handler` and `target`.
let handler: Box<dyn FnMut(Event)> =
Box::new(|event| handle_event(&mut app, &event_loop, event));
pub fn run_app<A: ApplicationHandler>(self, app: A) -> ! {
let app = Box::new(app);

// SAFETY: The `transmute` is necessary because `run()` requires `'static`. This is safe
// because this function will never return and all resources not cleaned up by the point we
// `throw` will leak, making this actually `'static`.
let handler = unsafe {
std::mem::transmute::<Box<dyn FnMut(Event)>, Box<dyn FnMut(Event) + 'static>>(handler)
let app = unsafe {
std::mem::transmute::<
Box<dyn ApplicationHandler + '_>,
Box<dyn ApplicationHandler + 'static>,
>(app)
};
self.elw.run(handler, false);

self.elw.run(app, false);

// Throw an exception to break out of Rust execution and use unreachable to tell the
// compiler this function won't return, giving it a return type of '!'
Expand All @@ -48,9 +47,8 @@ impl EventLoop {
unreachable!();
}

pub fn spawn_app<A: ApplicationHandler + 'static>(self, mut app: A) {
let event_loop = self.elw.clone();
self.elw.run(Box::new(move |event| handle_event(&mut app, &event_loop, event)), true);
pub fn spawn_app<A: ApplicationHandler + 'static>(self, app: A) {
self.elw.run(Box::new(app), true);
}

pub fn window_target(&self) -> &dyn RootActiveEventLoop {
Expand Down Expand Up @@ -85,18 +83,3 @@ impl EventLoop {
self.elw.runner.monitor().has_detailed_monitor_permission_async()
}
}

fn handle_event<A: ApplicationHandler>(app: &mut A, target: &ActiveEventLoop, event: Event) {
match event {
Event::NewEvents(cause) => app.new_events(target, cause),
Event::WindowEvent { window_id, event } => app.window_event(target, window_id, event),
Event::DeviceEvent { device_id, event } => app.device_event(target, device_id, event),
Event::UserWakeUp => app.proxy_wake_up(target),
Event::Suspended => app.suspended(target),
Event::Resumed => app.resumed(target),
Event::CreateSurfaces => app.can_create_surfaces(target),
Event::AboutToWait => app.about_to_wait(target),
Event::LoopExiting => app.exiting(target),
Event::MemoryWarning => app.memory_warning(target),
}
}
92 changes: 51 additions & 41 deletions src/platform_impl/web/event_loop/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ use web_time::{Duration, Instant};
use super::super::event;
use super::super::main_thread::MainThreadMarker;
use super::super::monitor::MonitorHandler;
use super::backend;
use super::proxy::EventLoopProxy;
use super::state::State;
use super::{backend, ActiveEventLoop};
use crate::application::ApplicationHandler;
use crate::dpi::PhysicalSize;
use crate::event::{DeviceEvent, ElementState, Event, RawKeyEvent, StartCause, WindowEvent};
use crate::event::{DeviceEvent, DeviceId, ElementState, RawKeyEvent, StartCause, WindowEvent};
use crate::event_loop::{ControlFlow, DeviceEvents};
use crate::platform::web::{PollStrategy, WaitUntilStrategy};
use crate::platform_impl::platform::backend::{EventListenerHandle, SafeAreaHandle};
Expand All @@ -27,8 +28,6 @@ use crate::window::WindowId;

pub struct Shared(Rc<Execution>);

pub(super) type EventHandler = dyn FnMut(Event);

impl Clone for Shared {
fn clone(&self) -> Self {
Shared(self.0.clone())
Expand All @@ -47,7 +46,7 @@ struct Execution {
runner: RefCell<RunnerEnum>,
suspended: Cell<bool>,
event_loop_recreation: Cell<bool>,
events: RefCell<VecDeque<EventWrapper>>,
events: RefCell<VecDeque<Event>>,
id: Cell<usize>,
window: web_sys::Window,
navigator: Navigator,
Expand Down Expand Up @@ -93,12 +92,13 @@ impl RunnerEnum {

struct Runner {
state: State,
event_handler: Box<EventHandler>,
app: Box<dyn ApplicationHandler>,
event_loop: ActiveEventLoop,
}

impl Runner {
pub fn new(event_handler: Box<EventHandler>) -> Self {
Runner { state: State::Init, event_handler }
pub fn new(app: Box<dyn ApplicationHandler>, event_loop: ActiveEventLoop) -> Self {
Runner { state: State::Init, app, event_loop }
}

/// Returns the corresponding `StartCause` for the current `state`, or `None`
Expand All @@ -115,19 +115,33 @@ impl Runner {
})
}

fn handle_single_event(&mut self, runner: &Shared, event: impl Into<EventWrapper>) {
match event.into() {
EventWrapper::Event(event) => (self.event_handler)(event),
EventWrapper::ScaleChange { canvas, size, scale } => {
fn handle_single_event(&mut self, runner: &Shared, event: Event) {
match event {
Event::NewEvents(cause) => self.app.new_events(&self.event_loop, cause),
Event::WindowEvent { window_id, event } => {
self.app.window_event(&self.event_loop, window_id, event)
},
Event::ScaleChange { canvas, size, scale } => {
if let Some(canvas) = canvas.upgrade() {
canvas.handle_scale_change(
runner,
|event| (self.event_handler)(event),
|window_id, event| {
self.app.window_event(&self.event_loop, window_id, event);
},
size,
scale,
)
}
},
Event::DeviceEvent { device_id, event } => {
self.app.device_event(&self.event_loop, device_id, event)
},
Event::UserWakeUp => self.app.proxy_wake_up(&self.event_loop),
Event::Suspended => self.app.suspended(&self.event_loop),
Event::Resumed => self.app.resumed(&self.event_loop),
Event::CreateSurfaces => self.app.can_create_surfaces(&self.event_loop),
Event::AboutToWait => self.app.about_to_wait(&self.event_loop),
Event::LoopExiting => self.app.exiting(&self.event_loop),
}
}
}
Expand Down Expand Up @@ -216,13 +230,13 @@ impl Shared {
self.0.destroy_pending.borrow_mut().push_back(id);
}

pub(crate) fn start(&self, event_handler: Box<EventHandler>) {
pub(crate) fn start(&self, app: Box<dyn ApplicationHandler>, event_loop: ActiveEventLoop) {
let mut runner = self.0.runner.borrow_mut();
assert!(matches!(*runner, RunnerEnum::Pending));
if self.0.monitor.is_initializing() {
*runner = RunnerEnum::Initializing(Runner::new(event_handler));
*runner = RunnerEnum::Initializing(Runner::new(app, event_loop));
} else {
*runner = RunnerEnum::Running(Runner::new(event_handler));
*runner = RunnerEnum::Running(Runner::new(app, event_loop));

drop(runner);

Expand Down Expand Up @@ -445,7 +459,7 @@ impl Shared {

pub fn request_redraw(&self, id: WindowId) {
self.0.redraw_pending.borrow_mut().insert(id);
self.send_events::<EventWrapper>(iter::empty());
self.send_events([]);
}

fn init(&self) {
Expand Down Expand Up @@ -473,7 +487,7 @@ impl Shared {
// Add an event to the event loop runner, from the user or an event handler
//
// It will determine if the event should be immediately sent to the user or buffered for later
pub(crate) fn send_event<E: Into<EventWrapper>>(&self, event: E) {
pub(crate) fn send_event(&self, event: Event) {
self.send_events(iter::once(event));
}

Expand Down Expand Up @@ -514,7 +528,7 @@ impl Shared {
// Add a series of events to the event loop runner
//
// It will determine if the event should be immediately sent to the user or buffered for later
pub(crate) fn send_events<E: Into<EventWrapper>>(&self, events: impl IntoIterator<Item = E>) {
pub(crate) fn send_events(&self, events: impl IntoIterator<Item = Event>) {
// If the event loop is closed, it should discard any new events
if self.is_closed() {
return;
Expand All @@ -539,7 +553,7 @@ impl Shared {
}
if !process_immediately {
// Queue these events to look at later
self.0.events.borrow_mut().extend(events.into_iter().map(Into::into));
self.0.events.borrow_mut().extend(events);
return;
}
// At this point, we know this is a fresh set of events
Expand All @@ -557,8 +571,7 @@ impl Shared {
// Take the start event, then the events provided to this function, and run an iteration of
// the event loop
let start_event = Event::NewEvents(start_cause);
let events =
iter::once(EventWrapper::from(start_event)).chain(events.into_iter().map(Into::into));
let events = iter::once(start_event).chain(events);
self.run_until_cleared(events);
}

Expand All @@ -579,9 +592,9 @@ impl Shared {
// cleared
//
// This will also process any events that have been queued or that are queued during processing
fn run_until_cleared<E: Into<EventWrapper>>(&self, events: impl Iterator<Item = E>) {
fn run_until_cleared(&self, events: impl Iterator<Item = Event>) {
for event in events {
self.handle_event(event.into());
self.handle_event(event);
}
self.process_destroy_pending_windows();

Expand Down Expand Up @@ -615,7 +628,7 @@ impl Shared {
// handle_event takes in events and either queues them or applies a callback
//
// It should only ever be called from `run_until_cleared`.
fn handle_event(&self, event: impl Into<EventWrapper>) {
fn handle_event(&self, event: Event) {
if self.is_closed() {
self.exit();
}
Expand All @@ -625,7 +638,7 @@ impl Shared {
},
// If an event is being handled without a runner somehow, add it to the event queue so
// it will eventually be processed
RunnerEnum::Pending => self.0.events.borrow_mut().push_back(event.into()),
RunnerEnum::Pending => self.0.events.borrow_mut().push_back(event),
// If the Runner has been destroyed, there is nothing to do.
RunnerEnum::Destroyed => return,
// This function should never be called if we are still waiting for something.
Expand All @@ -652,13 +665,7 @@ impl Shared {
let mut events = self.0.events.borrow_mut();

// Pre-fetch `UserEvent`s to avoid having to wait until the next event loop cycle.
events.extend(
self.0
.event_loop_proxy
.take()
.then_some(Event::UserWakeUp)
.map(EventWrapper::from),
);
events.extend(self.0.event_loop_proxy.take().then_some(Event::UserWakeUp));

events.pop_front()
};
Expand Down Expand Up @@ -845,13 +852,16 @@ impl WeakShared {
}
}

pub(crate) enum EventWrapper {
Event(Event),
#[allow(clippy::enum_variant_names)]
pub(crate) enum Event {
NewEvents(StartCause),
WindowEvent { window_id: WindowId, event: WindowEvent },
ScaleChange { canvas: Weak<backend::Canvas>, size: PhysicalSize<u32>, scale: f64 },
}

impl From<Event> for EventWrapper {
fn from(value: Event) -> Self {
Self::Event(value)
}
DeviceEvent { device_id: Option<DeviceId>, event: DeviceEvent },
Suspended,
CreateSurfaces,
Resumed,
AboutToWait,
LoopExiting,
UserWakeUp,
}
15 changes: 6 additions & 9 deletions src/platform_impl/web/event_loop/window_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ use web_sys::Element;

use super::super::monitor::MonitorPermissionFuture;
use super::super::{lock, KeyEventExtra};
use super::runner::EventWrapper;
use super::runner::Event;
use super::{backend, runner};
use crate::application::ApplicationHandler;
use crate::error::{NotSupportedError, RequestError};
use crate::event::{ElementState, Event, KeyEvent, TouchPhase, WindowEvent};
use crate::event::{ElementState, KeyEvent, TouchPhase, WindowEvent};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as CoreOwnedDisplayHandle,
Expand Down Expand Up @@ -54,13 +55,9 @@ impl ActiveEventLoop {
Self { runner: runner::Shared::new(), modifiers: ModifiersShared::default() }
}

pub(crate) fn run(
&self,
event_handler: Box<runner::EventHandler>,
event_loop_recreation: bool,
) {
pub(crate) fn run(&self, app: Box<dyn ApplicationHandler>, event_loop_recreation: bool) {
self.runner.event_loop_recreation(event_loop_recreation);
self.runner.start(event_handler);
self.runner.start(app, self.clone());
}

pub fn generate_id(&self) -> WindowId {
Expand Down Expand Up @@ -396,7 +393,7 @@ impl ActiveEventLoop {
let canvas = canvas_clone.clone();

move |size, scale| {
runner.send_event(EventWrapper::ScaleChange {
runner.send_event(Event::ScaleChange {
canvas: Rc::downgrade(&canvas),
size,
scale,
Expand Down
16 changes: 7 additions & 9 deletions src/platform_impl/web/web_sys/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use web_sys::{
};

use super::super::cursor::CursorHandler;
use super::super::event_loop::runner;
use super::super::main_thread::MainThreadMarker;
use super::animation_frame::AnimationFrameHandler;
use super::event_handle::EventListenerHandle;
Expand All @@ -23,7 +24,7 @@ use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use crate::error::RequestError;
use crate::event::{
ButtonSource, DeviceId, ElementState, MouseScrollDelta, PointerKind, PointerSource,
SurfaceSizeWriter,
SurfaceSizeWriter, WindowEvent,
};
use crate::keyboard::{Key, KeyLocation, ModifiersState, PhysicalKey};
use crate::platform_impl::Fullscreen;
Expand Down Expand Up @@ -487,20 +488,17 @@ impl Canvas {
pub(crate) fn handle_scale_change(
&self,
runner: &super::super::event_loop::runner::Shared,
event_handler: impl FnOnce(crate::event::Event),
event_handler: impl FnOnce(WindowId, WindowEvent),
current_size: PhysicalSize<u32>,
scale: f64,
) {
// First, we send the `ScaleFactorChanged` event:
self.set_current_size(current_size);
let new_size = {
let new_size = Arc::new(Mutex::new(current_size));
event_handler(crate::event::Event::WindowEvent {
window_id: self.id,
event: crate::event::WindowEvent::ScaleFactorChanged {
scale_factor: scale,
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_size)),
},
event_handler(self.id, WindowEvent::ScaleFactorChanged {
scale_factor: scale,
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_size)),
});

let new_size = *new_size.lock().unwrap();
Expand All @@ -523,7 +521,7 @@ impl Canvas {
} else if self.old_size() != new_size {
// Then we at least send a resized event.
self.set_old_size(new_size);
runner.send_event(crate::event::Event::WindowEvent {
runner.send_event(runner::Event::WindowEvent {
window_id: self.id,
event: crate::event::WindowEvent::SurfaceResized(new_size),
})
Expand Down