Allow recreating wasm event loop with spawn (#2897)

This commit is contained in:
Josh Groves 2023-06-23 15:01:42 -02:30 committed by GitHub
parent 864a1d5924
commit bc216b8f67
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 29 additions and 6 deletions

View file

@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre
# Unreleased # 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. - **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, 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. - On Android, added `EventLoopBuilderExtAndroid::handle_volume_keys` to indicate that the application will handle the volume keys manually.

View file

@ -9,9 +9,9 @@
//! handle events. //! handle events.
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::Deref; use std::ops::Deref;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{error, fmt}; use std::{error, fmt};
use once_cell::sync::OnceCell;
use raw_window_handle::{HasRawDisplayHandle, RawDisplayHandle}; use raw_window_handle::{HasRawDisplayHandle, RawDisplayHandle};
#[cfg(not(wasm_platform))] #[cfg(not(wasm_platform))]
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -69,6 +69,8 @@ impl EventLoopBuilder<()> {
} }
} }
static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
impl<T> EventLoopBuilder<T> { impl<T> EventLoopBuilder<T> {
/// Start building a new event loop, with the given type as the user event /// Start building a new event loop, with the given type as the user event
/// type. /// type.
@ -114,10 +116,10 @@ impl<T> EventLoopBuilder<T> {
)] )]
#[inline] #[inline]
pub fn build(&mut self) -> EventLoop<T> { pub fn build(&mut self) -> EventLoop<T> {
static EVENT_LOOP_CREATED: OnceCell<()> = OnceCell::new(); if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) {
if EVENT_LOOP_CREATED.set(()).is_err() {
panic!("Creating EventLoop multiple times is not supported."); panic!("Creating EventLoop multiple times is not supported.");
} }
// Certain platforms accept a mutable reference in their API. // Certain platforms accept a mutable reference in their API.
#[allow(clippy::unnecessary_mut_passed)] #[allow(clippy::unnecessary_mut_passed)]
EventLoop { EventLoop {
@ -125,6 +127,11 @@ impl<T> EventLoopBuilder<T> {
_marker: PhantomData, _marker: PhantomData,
} }
} }
#[cfg(wasm_platform)]
pub(crate) fn allow_event_loop_recreation() {
EVENT_LOOP_CREATED.store(false, Ordering::Relaxed);
}
} }
impl<T> fmt::Debug for EventLoop<T> { impl<T> fmt::Debug for EventLoop<T> {

View file

@ -66,6 +66,11 @@ pub trait EventLoopExtWebSys {
/// ///
/// Unlike `run`, this returns immediately, and doesn't throw an exception in order to /// Unlike `run`, this returns immediately, and doesn't throw an exception in order to
/// satisfy its `!` return type. /// 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<F>(self, event_handler: F) fn spawn<F>(self, event_handler: F)
where where
F: 'static F: 'static

View file

@ -8,7 +8,9 @@ pub use self::window_target::EventLoopWindowTarget;
use super::{backend, device, window}; use super::{backend, device, window};
use crate::event::Event; 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; use std::marker::PhantomData;
@ -33,7 +35,7 @@ impl<T> EventLoop<T> {
where where
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow), F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &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 // 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 '!' // compiler this function won't return, giving it a return type of '!'
@ -44,7 +46,15 @@ impl<T> EventLoop<T> {
unreachable!(); unreachable!();
} }
pub fn spawn<F>(self, mut event_handler: F) pub fn spawn<F>(self, event_handler: F)
where
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
{
EventLoopBuilder::<T>::allow_event_loop_recreation();
self.spawn_inner(event_handler);
}
fn spawn_inner<F>(self, mut event_handler: F)
where where
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow), F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
{ {