diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f6d7176154..e7012b41194 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased - Bump MSRV from `1.60` to `1.64`. +- On Web, `EventLoopProxy` now implements `Send`. # 0.28.2 diff --git a/Cargo.toml b/Cargo.toml index c4661617a99..17049d1028e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -148,8 +148,9 @@ features = [ 'WheelEvent' ] -[target.'cfg(target_arch = "wasm32")'.dependencies.wasm-bindgen] -version = "0.2.45" +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = "0.2.45" +wasm-bindgen-futures = "0.4.31" [target.'cfg(target_arch = "wasm32")'.dev-dependencies] console_log = "0.2" diff --git a/src/platform_impl/web/event_loop/proxy.rs b/src/platform_impl/web/event_loop/proxy.rs index 1c709923177..f3595cfb923 100644 --- a/src/platform_impl/web/event_loop/proxy.rs +++ b/src/platform_impl/web/event_loop/proxy.rs @@ -1,26 +1,96 @@ -use super::runner; -use crate::event::Event; +use std::future::Future; +use std::pin::Pin; +use std::sync::mpsc::{self, Receiver, RecvError, SendError, Sender, TryRecvError}; +use std::sync::{Arc, Mutex}; +use std::task::{Context, Poll, Waker}; + use crate::event_loop::EventLoopClosed; pub struct EventLoopProxy { - runner: runner::Shared, + sender: AsyncSender, } impl EventLoopProxy { - pub fn new(runner: runner::Shared) -> Self { - Self { runner } + pub fn new(sender: AsyncSender) -> Self { + Self { sender } } pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.runner.send_event(Event::UserEvent(event)); - Ok(()) + match self.sender.send(event) { + Ok(()) => Ok(()), + Err(SendError(val)) => Err(EventLoopClosed(val)), + } } } impl Clone for EventLoopProxy { fn clone(&self) -> Self { Self { - runner: self.runner.clone(), + sender: self.sender.clone(), + } + } +} + +pub fn channel() -> (AsyncSender, AsyncReceiver) { + let (sender, receiver) = mpsc::channel(); + let waker = Arc::new(Mutex::new(None)); + + let sender = AsyncSender { + sender, + waker: Arc::clone(&waker), + }; + let receiver = AsyncReceiver { receiver, waker }; + + (sender, receiver) +} + +pub struct AsyncSender { + sender: Sender, + waker: Arc>>, +} + +impl AsyncSender { + pub fn send(&self, event: T) -> Result<(), SendError> { + self.sender.send(event)?; + + if let Some(waker) = self.waker.lock().unwrap().take() { + waker.wake(); + } + + Ok(()) + } +} + +impl Clone for AsyncSender { + fn clone(&self) -> Self { + Self { + sender: self.sender.clone(), + waker: self.waker.clone(), + } + } +} + +pub struct AsyncReceiver { + receiver: Receiver, + waker: Arc>>, +} + +impl Future for AsyncReceiver { + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.receiver.try_recv() { + Ok(event) => Poll::Ready(Ok(event)), + Err(TryRecvError::Empty) => { + *self.waker.lock().unwrap() = Some(cx.waker().clone()); + + match self.receiver.try_recv() { + Ok(event) => Poll::Ready(Ok(event)), + Err(TryRecvError::Empty) => Poll::Pending, + Err(TryRecvError::Disconnected) => Poll::Ready(Err(RecvError)), + } + } + Err(TryRecvError::Disconnected) => Poll::Ready(Err(RecvError)), } } } diff --git a/src/platform_impl/web/event_loop/runner.rs b/src/platform_impl/web/event_loop/runner.rs index bdb68ff466e..7bcc48f5b34 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -1,3 +1,5 @@ +use super::proxy::{self, AsyncSender}; +use super::EventLoopProxy; use super::{super::ScaleChangeArgs, backend, state::State}; use crate::event::{Event, StartCause}; use crate::event_loop::ControlFlow; @@ -32,6 +34,7 @@ pub struct Execution { destroy_pending: RefCell>, scale_change_detector: RefCell>, unload_event_handle: RefCell>, + proxy_sender: AsyncSender, } enum RunnerEnum { @@ -99,7 +102,9 @@ impl Runner { impl Shared { pub fn new() -> Self { - Shared(Rc::new(Execution { + let (proxy_sender, mut proxy_receiver) = proxy::channel(); + + let this = Shared(Rc::new(Execution { runner: RefCell::new(RunnerEnum::Pending), events: RefCell::new(VecDeque::new()), id: RefCell::new(0), @@ -108,7 +113,22 @@ impl Shared { destroy_pending: RefCell::new(VecDeque::new()), scale_change_detector: RefCell::new(None), unload_event_handle: RefCell::new(None), - })) + proxy_sender, + })); + + wasm_bindgen_futures::spawn_local({ + let runner = this.clone(); + async move { + while let Ok(value) = (&mut proxy_receiver).await { + runner.send_event(Event::UserEvent(value)) + } + + // An error was returned because the channel was closed, which + // happens when the event loop gets closed, so we can stop now. + } + }); + + this } pub fn add_canvas(&self, id: WindowId, canvas: &Rc>) { @@ -155,6 +175,10 @@ impl Shared { *id } + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy::new(self.0.proxy_sender.clone()) + } + pub fn request_redraw(&self, id: WindowId) { self.0.redraw_pending.borrow_mut().insert(id); } diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 17aafd946da..1c4d6f8be8f 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -36,7 +36,7 @@ impl EventLoopWindowTarget { } pub fn proxy(&self) -> EventLoopProxy { - EventLoopProxy::new(self.runner.clone()) + self.runner.create_proxy() } pub fn run(&self, event_handler: Box>) { diff --git a/tests/send_objects.rs b/tests/send_objects.rs index 15fd8793396..bda19c2cb3c 100644 --- a/tests/send_objects.rs +++ b/tests/send_objects.rs @@ -1,7 +1,6 @@ #[allow(dead_code)] fn needs_send() {} -#[cfg(not(wasm_platform))] #[test] fn event_loop_proxy_send() { #[allow(dead_code)]