diff --git a/src/platform_impl/web/event_loop/mod.rs b/src/platform_impl/web/event_loop/mod.rs index 48c95fc2..4e0857b0 100644 --- a/src/platform_impl/web/event_loop/mod.rs +++ b/src/platform_impl/web/event_loop/mod.rs @@ -1,4 +1,5 @@ use std::marker::PhantomData; +use std::sync::mpsc; use crate::error::EventLoopError; use crate::event::Event; @@ -12,10 +13,12 @@ mod state; mod window_target; pub use proxy::EventLoopProxy; -pub use window_target::EventLoopWindowTarget; +pub use window_target::{EventLoopWindowTarget, Placeholder}; pub struct EventLoop { elw: RootEventLoopWindowTarget, + user_event_sender: mpsc::Sender, + user_event_receiver: mpsc::Receiver, } #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -23,11 +26,14 @@ pub(crate) struct PlatformSpecificEventLoopAttributes {} impl EventLoop { pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> Result { + let (user_event_sender, user_event_receiver) = mpsc::channel(); Ok(EventLoop { elw: RootEventLoopWindowTarget { p: EventLoopWindowTarget::new(), _marker: PhantomData, }, + user_event_sender, + user_event_receiver, }) } @@ -41,8 +47,18 @@ impl EventLoop { }; // SAFETY: Don't use `move` to make sure we leak the `event_handler` and `target`. - let handler: Box = - Box::new(|event, flow| event_handler(event, &target, flow)); + let handler: Box, &mut ControlFlow)> = + Box::new(|event, flow| { + let event = match event.map_nonuser_event() { + Ok(event) => event, + Err(_placeholder) => Event::UserEvent( + self.user_event_receiver + .try_recv() + .expect("handler waken up without user event"), + ), + }; + event_handler(event, &target, flow) + }); // 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`. @@ -68,13 +84,23 @@ impl EventLoop { }; self.elw.p.run( - Box::new(move |event, flow| event_handler(event, &target, flow)), + Box::new(move |event, flow| { + let event = match event.map_nonuser_event() { + Ok(event) => event, + Err(_placeholder) => Event::UserEvent( + self.user_event_receiver + .try_recv() + .expect("handler waken up without user event"), + ), + }; + event_handler(event, &target, flow) + }), true, ); } pub fn create_proxy(&self) -> EventLoopProxy { - self.elw.p.proxy() + EventLoopProxy::new(self.elw.p.runner.clone(), self.user_event_sender.clone()) } pub fn window_target(&self) -> &RootEventLoopWindowTarget { diff --git a/src/platform_impl/web/event_loop/proxy.rs b/src/platform_impl/web/event_loop/proxy.rs index 7094daaf..464a3fe6 100644 --- a/src/platform_impl/web/event_loop/proxy.rs +++ b/src/platform_impl/web/event_loop/proxy.rs @@ -1,24 +1,31 @@ +use std::sync::mpsc; + use super::runner; +use super::window_target::Placeholder; use crate::event::Event; use crate::event_loop::EventLoopClosed; use crate::platform_impl::platform::r#async::Channel; pub struct EventLoopProxy { - runner: Channel, T>, + // used to wake the event loop handler, not to actually pass data + runner: Channel, Placeholder>, + sender: mpsc::Sender, } impl EventLoopProxy { - pub fn new(runner: runner::Shared) -> Self { + pub fn new(runner: runner::Shared, sender: mpsc::Sender) -> Self { Self { runner: Channel::new(runner, |runner, event| { runner.send_event(Event::UserEvent(event)) }) .unwrap(), + sender, } } pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.runner.send(event); + self.sender.send(event).expect("out of memory"); + self.runner.send(Placeholder); Ok(()) } } @@ -27,6 +34,7 @@ impl Clone for EventLoopProxy { fn clone(&self) -> Self { Self { runner: self.runner.clone(), + sender: self.sender.clone(), } } } diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 7429e98f..03ccfa19 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -2,6 +2,7 @@ use std::cell::{Cell, RefCell}; use std::clone::Clone; use std::collections::{vec_deque::IntoIter as VecDequeIter, VecDeque}; use std::iter; +use std::marker::PhantomData; use std::rc::Rc; use std::sync::atomic::Ordering; @@ -43,9 +44,15 @@ impl Clone for ModifiersShared { } } +// compiler complain about leaking private type into public API +// I don't understand why, maybe it's a wasm target thing +// until the backend is cleaned up, make it public, or alias to () +pub struct Placeholder; + pub struct EventLoopWindowTarget { - pub(crate) runner: runner::Shared, + pub(crate) runner: runner::Shared, modifiers: ModifiersShared, + _marker: PhantomData, } impl Clone for EventLoopWindowTarget { @@ -53,6 +60,7 @@ impl Clone for EventLoopWindowTarget { Self { runner: self.runner.clone(), modifiers: self.modifiers.clone(), + _marker: PhantomData, } } } @@ -62,14 +70,23 @@ impl EventLoopWindowTarget { Self { runner: runner::Shared::new(), modifiers: ModifiersShared::default(), + _marker: PhantomData, } } - pub fn proxy(&self) -> EventLoopProxy { - EventLoopProxy::new(self.runner.clone()) - } + // `EventLoopProxy` is paired with `EventLoop` directly now + // the window target should only cares about windows + // pub fn proxy(&self) -> EventLoopProxy { + // EventLoopProxy::new(self.runner.clone()) + // } - pub fn run(&self, event_handler: Box>, event_loop_recreation: bool) { + // Question: + // should `run` (and many other methods) be moved to `EventLoop` too? + pub fn run( + &self, + event_handler: Box>, + event_loop_recreation: bool, + ) { self.runner.event_loop_recreation(event_loop_recreation); self.runner.set_listener(event_handler); }