From d90cb57c2efececd4fd0654a7953c0f49cfbce12 Mon Sep 17 00:00:00 2001 From: Robin Kellner <54271059+R081n@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:17:17 +0100 Subject: [PATCH] Add the ability to request a redraw from an external source (#12197) Hi, this is a minimal implementation of #12159. I wasn't sure if the `EventLoopProxy` should be wrapped somewhat to make it more explicit. # Objective Minimal implementation of #12159 When using `UpdateMode::Reactive` it is currently not possible to request a redraw when a long running task is finished or an external source has new data. This makes the following possible which will then run an app update once ``` rust // EventLoopProxy is Send on most architectures // The EventLoopProxy can also be saved in a thread local for WASM or a static in other architecturecs pub fn example(proxy: NonSend>) { let clone: EventLoopProxy<()> = proxy.clone(); thread::spawn(move || { // do long work clone.send_event(()); }); } ``` ## Solution By using the EventLoopProxy one can manually send events from external threads to the event loop as `UserEvent`s. This simply sets redraw_requested when a `UserEvent` is received. ## Changelog - Added the ability to request a redraw from an external source --------- Co-authored-by: Kellner, Robin --- crates/bevy_winit/src/lib.rs | 24 ++++++++++++++++++------ crates/bevy_winit/src/system.rs | 2 +- crates/bevy_winit/src/winit_config.rs | 4 ++++ crates/bevy_winit/src/winit_windows.rs | 2 +- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index f8515dd3c7fb4..784e569b0ccc2 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -83,7 +83,7 @@ pub struct WinitPlugin { impl Plugin for WinitPlugin { fn build(&self, app: &mut App) { - let mut event_loop_builder = EventLoopBuilder::<()>::with_user_event(); + let mut event_loop_builder = EventLoopBuilder::::with_user_event(); // linux check is needed because x11 might be enabled on other platforms. #[cfg(all(target_os = "linux", feature = "x11"))] @@ -243,6 +243,15 @@ type CreateWindowParams<'w, 's, F = ()> = ( Res<'w, AccessibilityRequested>, ); +/// The [`winit::event_loop::EventLoopProxy`] with the specific [`winit::event::Event::UserEvent`] used in the [`winit_runner`]. +/// +/// The `EventLoopProxy` can be used to request a redraw from outside bevy. +/// +/// Use `NonSend` to receive this resource. +pub type EventLoopProxy = winit::event_loop::EventLoopProxy; + +type UserEvent = RequestRedraw; + /// The default [`App::runner`] for the [`WinitPlugin`] plugin. /// /// Overriding the app's [runner](bevy_app::App::runner) while using `WinitPlugin` will bypass the @@ -255,7 +264,7 @@ pub fn winit_runner(mut app: App) { let event_loop = app .world - .remove_non_send_resource::>() + .remove_non_send_resource::>() .unwrap(); app.world @@ -281,7 +290,7 @@ pub fn winit_runner(mut app: App) { SystemState::>>::from_world(&mut app.world); let mut winit_events = Vec::default(); // set up the event loop - let event_handler = move |event, event_loop: &EventLoopWindowTarget<()>| { + let event_handler = move |event, event_loop: &EventLoopWindowTarget| { handle_winit_event( &mut app, &mut app_exit_event_reader, @@ -318,8 +327,8 @@ fn handle_winit_event( focused_windows_state: &mut SystemState<(Res, Query<&Window>)>, redraw_event_reader: &mut ManualEventReader, winit_events: &mut Vec, - event: Event<()>, - event_loop: &EventLoopWindowTarget<()>, + event: Event, + event_loop: &EventLoopWindowTarget, ) { #[cfg(feature = "trace")] let _span = bevy_utils::tracing::info_span!("winit event_handler").entered(); @@ -698,6 +707,9 @@ fn handle_winit_event( event_loop.set_control_flow(ControlFlow::Wait); } } + Event::UserEvent(RequestRedraw) => { + runner_state.redraw_requested = true; + } _ => (), } @@ -712,7 +724,7 @@ fn run_app_update_if_should( runner_state: &mut WinitAppRunnerState, app: &mut App, focused_windows_state: &mut SystemState<(Res, Query<&Window>)>, - event_loop: &EventLoopWindowTarget<()>, + event_loop: &EventLoopWindowTarget, create_window: &mut SystemState>>, app_exit_event_reader: &mut ManualEventReader, redraw_event_reader: &mut ManualEventReader, diff --git a/crates/bevy_winit/src/system.rs b/crates/bevy_winit/src/system.rs index af6d017f26da5..0000c45369d44 100644 --- a/crates/bevy_winit/src/system.rs +++ b/crates/bevy_winit/src/system.rs @@ -35,7 +35,7 @@ use crate::{ /// default values. #[allow(clippy::too_many_arguments)] pub(crate) fn create_windows( - event_loop: &EventLoopWindowTarget<()>, + event_loop: &EventLoopWindowTarget, ( mut commands, mut created_windows, diff --git a/crates/bevy_winit/src/winit_config.rs b/crates/bevy_winit/src/winit_config.rs index 78168309093fb..f2cb424ec5f44 100644 --- a/crates/bevy_winit/src/winit_config.rs +++ b/crates/bevy_winit/src/winit_config.rs @@ -28,6 +28,8 @@ impl WinitSettings { /// /// [`Reactive`](UpdateMode::Reactive) if windows have focus, /// [`ReactiveLowPower`](UpdateMode::ReactiveLowPower) otherwise. + /// + /// Use the [`EventLoopProxy`](crate::EventLoopProxy) to request a redraw from outside bevy. pub fn desktop_app() -> Self { WinitSettings { focused_mode: UpdateMode::Reactive { @@ -72,6 +74,7 @@ pub enum UpdateMode { /// - a redraw has been requested by [`RequestRedraw`](bevy_window::RequestRedraw) /// - new [window](`winit::event::WindowEvent`) or [raw input](`winit::event::DeviceEvent`) /// events have appeared + /// - a redraw has been requested with the [`EventLoopProxy`](crate::EventLoopProxy) Reactive { /// The approximate time from the start of one update to the next. /// @@ -84,6 +87,7 @@ pub enum UpdateMode { /// - `wait` time has elapsed since the previous update /// - a redraw has been requested by [`RequestRedraw`](bevy_window::RequestRedraw) /// - new [window events](`winit::event::WindowEvent`) have appeared + /// - a redraw has been requested with the [`EventLoopProxy`](crate::EventLoopProxy) /// /// **Note:** Unlike [`Reactive`](`UpdateMode::Reactive`), this mode will ignore events that /// don't come from interacting with a window, like [`MouseMotion`](winit::event::DeviceEvent::MouseMotion). diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index 3188ac2714fdf..4375e7c277744 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -39,7 +39,7 @@ impl WinitWindows { /// Creates a `winit` window and associates it with our entity. pub fn create_window( &mut self, - event_loop: &winit::event_loop::EventLoopWindowTarget<()>, + event_loop: &winit::event_loop::EventLoopWindowTarget, entity: Entity, window: &Window, adapters: &mut AccessKitAdapters,