Remove need for EventLoopProxy to be NonSend (#14198)

# Objective

- Continue to pare down the uses on NonSend resources in the engine. In
this case, EventLoopProxy used to be `!Sync`, but is now `Sync` in the
latest version of winit.

## Solution

- New type `EventLoopProxy` as `EventLoopProxyWrapper` to make it into a
normal resource.
- Update the `custom_user_event` example as it no longer needs to
indirectly access the `EventLoopProxy` through a static variable
anymore.

## Testing

- Ran the example. The resource exists just for users to use, so there
aren't any in engine uses for it currently.

---

## Changelog

- make EventLoopProxy into a regular resource. 

## Migration Guide

`EventLoopProxy` has been renamed to `EventLoopProxyWrapper` and is now
`Send`, making it an ordinary resource.

Before:
```rust
event_loop_system(event_loop: NonSend<EventLoopProxy<MyEvent>>) {
    event_loop.send_event(MyEvent);
}
```

After:
```rust
event_loop_system(event_loop: Res<EventLoopProxy<MyEvent>>) {
    event_loop.send_event(MyEvent);
}
```
This commit is contained in:
Mike 2024-07-15 23:59:01 -07:00 committed by GitHub
parent 1042f09c2e
commit cfcb56f5b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 29 additions and 35 deletions

View file

@ -12,6 +12,7 @@
//! The app's [runner](bevy_app::App::runner) is set by `WinitPlugin` and handles the `winit` [`EventLoop`]. //! The app's [runner](bevy_app::App::runner) is set by `WinitPlugin` and handles the `winit` [`EventLoop`].
//! See `winit_runner` for details. //! See `winit_runner` for details.
use bevy_derive::Deref;
use bevy_window::RawHandleWrapperHolder; use bevy_window::RawHandleWrapperHolder;
use std::marker::PhantomData; use std::marker::PhantomData;
use winit::event_loop::EventLoop; use winit::event_loop::EventLoop;
@ -25,6 +26,7 @@ use bevy_ecs::prelude::*;
use bevy_window::{exit_on_all_closed, Window, WindowCreated}; use bevy_window::{exit_on_all_closed, Window, WindowCreated};
pub use system::create_windows; pub use system::create_windows;
use system::{changed_windows, despawn_windows}; use system::{changed_windows, despawn_windows};
pub use winit::event_loop::EventLoopProxy;
pub use winit_config::*; pub use winit_config::*;
pub use winit_event::*; pub use winit_event::*;
pub use winit_windows::*; pub use winit_windows::*;
@ -142,12 +144,14 @@ impl<T: Event> Plugin for WinitPlugin<T> {
#[derive(Debug, Default, Clone, Copy, Event)] #[derive(Debug, Default, Clone, Copy, Event)]
pub struct WakeUp; pub struct WakeUp;
/// A re-export of [`winit::event_loop::EventLoopProxy`]. /// A wrapper type around [`winit::event_loop::EventLoopProxy`] with the specific
/// [`winit::event::Event::UserEvent`] used in the [`WinitPlugin`].
/// ///
/// The `EventLoopProxy` can be used to request a redraw from outside bevy. /// The `EventLoopProxy` can be used to request a redraw from outside bevy.
/// ///
/// Use `NonSend<EventLoopProxy>` to receive this resource. /// Use `Res<EventLoopProxy>` to receive this resource.
pub type EventLoopProxy<T> = winit::event_loop::EventLoopProxy<T>; #[derive(Resource, Deref)]
pub struct EventLoopProxyWrapper<T: 'static>(winit::event_loop::EventLoopProxy<T>);
trait AppSendEvent { trait AppSendEvent {
fn send(&mut self, event: impl Into<WinitEvent>); fn send(&mut self, event: impl Into<WinitEvent>);

View file

@ -37,8 +37,8 @@ use bevy_window::{PrimaryWindow, RawHandleWrapper};
use crate::accessibility::AccessKitAdapters; use crate::accessibility::AccessKitAdapters;
use crate::system::CachedWindow; use crate::system::CachedWindow;
use crate::{ use crate::{
converters, create_windows, AppSendEvent, CreateWindowParams, UpdateMode, WinitEvent, converters, create_windows, AppSendEvent, CreateWindowParams, EventLoopProxyWrapper,
WinitSettings, WinitWindows, UpdateMode, WinitEvent, WinitSettings, WinitWindows,
}; };
/// Persistent state that is used to run the [`App`] according to the current /// Persistent state that is used to run the [`App`] according to the current
@ -763,7 +763,7 @@ pub fn winit_runner<T: Event>(mut app: App) -> AppExit {
.unwrap(); .unwrap();
app.world_mut() app.world_mut()
.insert_non_send_resource(event_loop.create_proxy()); .insert_resource(EventLoopProxyWrapper(event_loop.create_proxy()));
let mut runner_state = WinitAppRunnerState::new(app); let mut runner_state = WinitAppRunnerState::new(app);

View file

@ -1,9 +1,8 @@
//! Shows how to create a custom event that can be handled by `winit`'s event loop. //! Shows how to create a custom event that can be handled by `winit`'s event loop.
use bevy::prelude::*; use bevy::prelude::*;
use bevy::winit::{EventLoopProxy, WakeUp, WinitPlugin}; use bevy::winit::{EventLoopProxyWrapper, WakeUp, WinitPlugin};
use std::fmt::Formatter; use std::fmt::Formatter;
use std::sync::OnceLock;
#[derive(Default, Debug, Event)] #[derive(Default, Debug, Event)]
enum CustomEvent { enum CustomEvent {
@ -21,8 +20,6 @@ impl std::fmt::Display for CustomEvent {
} }
} }
static EVENT_LOOP_PROXY: OnceLock<EventLoopProxy<CustomEvent>> = OnceLock::new();
fn main() { fn main() {
let winit_plugin = WinitPlugin::<CustomEvent>::default(); let winit_plugin = WinitPlugin::<CustomEvent>::default();
@ -39,7 +36,6 @@ fn main() {
Startup, Startup,
( (
setup, setup,
expose_event_loop_proxy,
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
wasm::setup_js_closure, wasm::setup_js_closure,
), ),
@ -52,11 +48,10 @@ fn setup(mut commands: Commands) {
commands.spawn(Camera2dBundle::default()); commands.spawn(Camera2dBundle::default());
} }
fn send_event(input: Res<ButtonInput<KeyCode>>) { fn send_event(
let Some(event_loop_proxy) = EVENT_LOOP_PROXY.get() else { input: Res<ButtonInput<KeyCode>>,
return; event_loop_proxy: Res<EventLoopProxyWrapper<CustomEvent>>,
}; ) {
if input.just_pressed(KeyCode::Space) { if input.just_pressed(KeyCode::Space) {
let _ = event_loop_proxy.send_event(CustomEvent::WakeUp); let _ = event_loop_proxy.send_event(CustomEvent::WakeUp);
} }
@ -64,18 +59,15 @@ fn send_event(input: Res<ButtonInput<KeyCode>>) {
// This simulates sending a custom event through an external thread. // This simulates sending a custom event through an external thread.
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
if input.just_pressed(KeyCode::KeyE) { if input.just_pressed(KeyCode::KeyE) {
let handler = std::thread::spawn(|| { let event_loop_proxy = event_loop_proxy.clone();
let _ = event_loop_proxy.send_event(CustomEvent::Key('e')); let handler = std::thread::spawn(move || {
let _ = event_loop_proxy.clone().send_event(CustomEvent::Key('e'));
}); });
handler.join().unwrap(); handler.join().unwrap();
} }
} }
fn expose_event_loop_proxy(event_loop_proxy: NonSend<EventLoopProxy<CustomEvent>>) {
EVENT_LOOP_PROXY.set((*event_loop_proxy).clone()).unwrap();
}
fn handle_event(mut events: EventReader<CustomEvent>) { fn handle_event(mut events: EventReader<CustomEvent>) {
for evt in events.read() { for evt in events.read() {
info!("Received event: {evt:?}"); info!("Received event: {evt:?}");
@ -87,19 +79,21 @@ fn handle_event(mut events: EventReader<CustomEvent>) {
/// the loop if that's currently waiting for a timeout or a user event. /// the loop if that's currently waiting for a timeout or a user event.
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub(crate) mod wasm { pub(crate) mod wasm {
use crate::{CustomEvent, EVENT_LOOP_PROXY}; use super::*;
use bevy::winit::EventLoopProxy;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
use web_sys::KeyboardEvent; use web_sys::KeyboardEvent;
pub(crate) fn setup_js_closure() { pub(crate) fn setup_js_closure(event_loop: Res<EventLoopProxyWrapper<CustomEvent>>) {
let window = web_sys::window().unwrap(); let window = web_sys::window().unwrap();
let document = window.document().unwrap(); let document = window.document().unwrap();
let event_loop = event_loop.clone();
let closure = Closure::wrap(Box::new(move |event: KeyboardEvent| { let closure = Closure::wrap(Box::new(move |event: KeyboardEvent| {
let key = event.key(); let key = event.key();
if key == "e" { if key == "e" {
send_custom_event('e').unwrap(); send_custom_event('e', &event_loop).unwrap();
} }
}) as Box<dyn FnMut(KeyboardEvent)>); }) as Box<dyn FnMut(KeyboardEvent)>);
@ -110,13 +104,9 @@ pub(crate) mod wasm {
closure.forget(); closure.forget();
} }
fn send_custom_event(ch: char) -> Result<(), String> { fn send_custom_event(ch: char, proxy: &EventLoopProxy<CustomEvent>) -> Result<(), String> {
if let Some(proxy) = EVENT_LOOP_PROXY.get() { proxy
proxy .send_event(CustomEvent::Key(ch))
.send_event(CustomEvent::Key(ch)) .map_err(|_| "Failed to send event".to_string())
.map_err(|_| "Failed to send event".to_string())
} else {
Err("Event loop proxy not found".to_string())
}
} }
} }

View file

@ -7,7 +7,7 @@ use bevy::{
prelude::*, prelude::*,
utils::Duration, utils::Duration,
window::{PresentMode, RequestRedraw, WindowPlugin}, window::{PresentMode, RequestRedraw, WindowPlugin},
winit::{EventLoopProxy, WakeUp, WinitSettings}, winit::{EventLoopProxyWrapper, WakeUp, WinitSettings},
}; };
fn main() { fn main() {
@ -55,7 +55,7 @@ enum ExampleMode {
fn update_winit( fn update_winit(
mode: Res<ExampleMode>, mode: Res<ExampleMode>,
mut winit_config: ResMut<WinitSettings>, mut winit_config: ResMut<WinitSettings>,
event_loop_proxy: NonSend<EventLoopProxy<WakeUp>>, event_loop_proxy: Res<EventLoopProxyWrapper<WakeUp>>,
mut redraw_request_events: EventWriter<RequestRedraw>, mut redraw_request_events: EventWriter<RequestRedraw>,
) { ) {
use ExampleMode::*; use ExampleMode::*;