Update winit_runner to use spawn_app for wasm32 target (#16630)

# Objective

- Fixes #12562
- Fixes #12195

## Solution

- Use `spawn_app` instead of `run_app` for web platform in
`winit_runner` as suggested in the
[document](https://docs.rs/winit/latest/winit/platform/web/trait.EventLoopExtWebSys.html#tymethod.spawn_app)

## Testing

- Did you test these changes? If so, how?

Tested on web. Created a react app which renders the bevy WASM app and
returns the disposer to JS. Js will call the disposer on component
unmount to stop the app, the disposer sends a signal to a `signal`
receiver in rust which exits the app like this:

```rust
fn handle_stop_signal(
    signal: NonSendMut<StopSignalReceiver>,
    window_entities: Query<(Entity, &Window)>,
    mut event_writer: EventWriter<WindowCloseRequested>,
) {
    if let Ok(_) = signal.try_recv() {
        for (entity, _window) in window_entities.iter() {
            info!("closing window entity: {:x}", entity.to_bits());
            event_writer.send(WindowCloseRequested { window: entity });
        }
    }
}
```


- Are there any parts that need more testing?

  - No

- How can other people (reviewers) test your changes? Is there anything
specific they need to know?

- Are all resources released after stopping the app like this? The WASM
is still loaded, the LogPlugin complains on the logger
re-initialization, but it's a warning anyway.

- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?

- Tested the WASM version on web platform and the native version on
MacOS.

---------

Co-authored-by: Martín Maita <47983254+mnmaita@users.noreply.github.com>
This commit is contained in:
Wei Xu 2025-01-06 15:21:24 -08:00 committed by GitHub
parent 4ba09f3dd9
commit fbc55b84e2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -24,6 +24,8 @@ use bevy_tasks::tick_global_task_pools_on_main_thread;
use bevy_utils::HashMap;
use bevy_utils::Instant;
use core::marker::PhantomData;
#[cfg(target_arch = "wasm32")]
use winit::platform::web::EventLoopExtWebSys;
use winit::{
application::ApplicationHandler,
dpi::PhysicalSize,
@ -868,19 +870,27 @@ pub fn winit_runner<T: Event>(mut app: App) -> AppExit {
app.world_mut()
.insert_resource(EventLoopProxyWrapper(event_loop.create_proxy()));
let mut runner_state = WinitAppRunnerState::new(app);
let runner_state = WinitAppRunnerState::new(app);
trace!("starting winit event loop");
// TODO(clean): the winit docs mention using `spawn` instead of `run` on Wasm.
if let Err(err) = event_loop.run_app(&mut runner_state) {
error!("winit event loop returned an error: {err}");
// The winit docs mention using `spawn` instead of `run` on Wasm.
// https://docs.rs/winit/latest/winit/platform/web/trait.EventLoopExtWebSys.html#tymethod.spawn_app
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
event_loop.spawn_app(runner_state);
AppExit::Success
} else {
let mut runner_state = runner_state;
if let Err(err) = event_loop.run_app(&mut runner_state) {
error!("winit event loop returned an error: {err}");
}
// If everything is working correctly then the event loop only exits after it's sent an exit code.
runner_state.app_exit.unwrap_or_else(|| {
error!("Failed to receive an app exit code! This is a bug");
AppExit::error()
})
}
}
// If everything is working correctly then the event loop only exits after it's sent an exit code.
runner_state.app_exit.unwrap_or_else(|| {
error!("Failed to receive a app exit code! This is a bug");
AppExit::error()
})
}
pub(crate) fn react_to_resize(