From bc216b8f67015c8cee9ac6e9b64391c5b43ce765 Mon Sep 17 00:00:00 2001 From: Josh Groves Date: Fri, 23 Jun 2023 15:01:42 -0230 Subject: [PATCH] Allow recreating wasm event loop with `spawn` (#2897) --- CHANGELOG.md | 1 + src/event_loop.rs | 13 ++++++++++--- src/platform/web.rs | 5 +++++ src/platform_impl/web/event_loop/mod.rs | 16 +++++++++++++--- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb4b49735c..d8abaaeadc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- On Web, allow event loops to be recreated with `spawn`. - **Breaking:** Rename `Window::set_ime_position` to `Window::set_ime_cursor_area` adding a way to set exclusive zone. - On Android, changed default behavior of Android to ignore volume keys letting the operating system handle them. - On Android, added `EventLoopBuilderExtAndroid::handle_volume_keys` to indicate that the application will handle the volume keys manually. diff --git a/src/event_loop.rs b/src/event_loop.rs index cf8f6a12da..9e59b37b91 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -9,9 +9,9 @@ //! handle events. use std::marker::PhantomData; use std::ops::Deref; +use std::sync::atomic::{AtomicBool, Ordering}; use std::{error, fmt}; -use once_cell::sync::OnceCell; use raw_window_handle::{HasRawDisplayHandle, RawDisplayHandle}; #[cfg(not(wasm_platform))] use std::time::{Duration, Instant}; @@ -69,6 +69,8 @@ impl EventLoopBuilder<()> { } } +static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false); + impl EventLoopBuilder { /// Start building a new event loop, with the given type as the user event /// type. @@ -114,10 +116,10 @@ impl EventLoopBuilder { )] #[inline] pub fn build(&mut self) -> EventLoop { - static EVENT_LOOP_CREATED: OnceCell<()> = OnceCell::new(); - if EVENT_LOOP_CREATED.set(()).is_err() { + if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) { panic!("Creating EventLoop multiple times is not supported."); } + // Certain platforms accept a mutable reference in their API. #[allow(clippy::unnecessary_mut_passed)] EventLoop { @@ -125,6 +127,11 @@ impl EventLoopBuilder { _marker: PhantomData, } } + + #[cfg(wasm_platform)] + pub(crate) fn allow_event_loop_recreation() { + EVENT_LOOP_CREATED.store(false, Ordering::Relaxed); + } } impl fmt::Debug for EventLoop { diff --git a/src/platform/web.rs b/src/platform/web.rs index 51e7ce4713..fbbba9f447 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -66,6 +66,11 @@ pub trait EventLoopExtWebSys { /// /// Unlike `run`, this returns immediately, and doesn't throw an exception in order to /// satisfy its `!` return type. + /// + /// Once the event loop has been destroyed, it's possible to reinitialize another event loop + /// by calling this function again. This can be useful if you want to recreate the event loop + /// while the WebAssembly module is still loaded. For example, this can be used to recreate the + /// event loop when switching between tabs on a single page application. fn spawn(self, event_handler: F) where F: 'static diff --git a/src/platform_impl/web/event_loop/mod.rs b/src/platform_impl/web/event_loop/mod.rs index e93a79daf2..28598dde8c 100644 --- a/src/platform_impl/web/event_loop/mod.rs +++ b/src/platform_impl/web/event_loop/mod.rs @@ -8,7 +8,9 @@ pub use self::window_target::EventLoopWindowTarget; use super::{backend, device, window}; use crate::event::Event; -use crate::event_loop::{ControlFlow, EventLoopWindowTarget as RootEventLoopWindowTarget}; +use crate::event_loop::{ + ControlFlow, EventLoopBuilder, EventLoopWindowTarget as RootEventLoopWindowTarget, +}; use std::marker::PhantomData; @@ -33,7 +35,7 @@ impl EventLoop { where F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget, &mut ControlFlow), { - self.spawn(event_handler); + self.spawn_inner(event_handler); // 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 '!' @@ -44,7 +46,15 @@ impl EventLoop { unreachable!(); } - pub fn spawn(self, mut event_handler: F) + pub fn spawn(self, event_handler: F) + where + F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget, &mut ControlFlow), + { + EventLoopBuilder::::allow_event_loop_recreation(); + self.spawn_inner(event_handler); + } + + fn spawn_inner(self, mut event_handler: F) where F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget, &mut ControlFlow), {