Skip to content

Commit

Permalink
Allow recreating wasm event loop with spawn (#2897)
Browse files Browse the repository at this point in the history
  • Loading branch information
grovesNL authored Jun 23, 2023
1 parent 864a1d5 commit bc216b8
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 10 additions & 3 deletions src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -69,6 +69,8 @@ impl EventLoopBuilder<()> {
}
}

static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);

impl<T> EventLoopBuilder<T> {
/// Start building a new event loop, with the given type as the user event
/// type.
Expand Down Expand Up @@ -114,17 +116,22 @@ impl<T> EventLoopBuilder<T> {
)]
#[inline]
pub fn build(&mut self) -> EventLoop<T> {
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 {
event_loop: platform_impl::EventLoop::new(&mut self.platform_specific),
_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> {
Expand Down
5 changes: 5 additions & 0 deletions src/platform/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<F>(self, event_handler: F)
where
F: 'static
Expand Down
16 changes: 13 additions & 3 deletions src/platform_impl/web/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -33,7 +35,7 @@ impl<T> EventLoop<T> {
where
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
// compiler this function won't return, giving it a return type of '!'
Expand All @@ -44,7 +46,15 @@ impl<T> EventLoop<T> {
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
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
{
Expand Down

0 comments on commit bc216b8

Please sign in to comment.