Skip to content

Commit

Permalink
web: Avoid top-level Event
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Feb 24, 2025
1 parent 23011c6 commit 063168f
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 84 deletions.
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),
}
}
88 changes: 50 additions & 38 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 Down Expand Up @@ -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.into_iter().map(Into::into));
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 Down Expand Up @@ -653,11 +666,7 @@ impl Shared {

// 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),
self.0.event_loop_proxy.take().then_some(Event::UserWakeUp).map(Event::from),
);

events.pop_front()
Expand Down Expand Up @@ -845,13 +854,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

0 comments on commit 063168f

Please sign in to comment.