diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 2d1953699f5a6..1e20caada5219 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -19,7 +19,7 @@ use bevy_reflect::prelude::ReflectDefault; use bevy_reflect::Reflect; use bevy_window::{RawHandleWrapperHolder, WindowEvent}; use core::marker::PhantomData; -use winit::event_loop::EventLoop; +use winit::{event_loop::EventLoop, window::WindowId}; use bevy_a11y::AccessibilityRequested; use bevy_app::{App, Last, Plugin}; @@ -122,6 +122,7 @@ impl Plugin for WinitPlugin { app.init_non_send_resource::() .init_resource::() .init_resource::() + .add_event::() .set_runner(winit_runner::) .add_systems( Last, @@ -155,6 +156,21 @@ impl Plugin for WinitPlugin { #[reflect(Debug, Default)] pub struct WakeUp; +/// The original window event as produced by Winit. This is meant as an escape +/// hatch for power users that wish to add custom Winit integrations. +/// If you want to process events for your app or game, you should instead use +/// `bevy::window::WindowEvent`, or one of its sub-events. +/// +/// When you receive this event it has already been handled by Bevy's main loop. +/// Sending these events will NOT cause them to be processed by Bevy. +#[derive(Debug, Clone, Event)] +pub struct RawWinitWindowEvent { + /// The window for which the event was fired. + pub window_id: WindowId, + /// The raw winit window event. + pub event: winit::event::WindowEvent, +} + /// A wrapper type around [`winit::event_loop::EventLoopProxy`] with the specific /// [`winit::event::Event::UserEvent`] used in the [`WinitPlugin`]. /// diff --git a/crates/bevy_winit/src/state.rs b/crates/bevy_winit/src/state.rs index 9217968d31d7e..4c65a4fe0d760 100644 --- a/crates/bevy_winit/src/state.rs +++ b/crates/bevy_winit/src/state.rs @@ -46,8 +46,8 @@ use crate::{ accessibility::AccessKitAdapters, converters, create_windows, system::{create_monitors, CachedWindow}, - AppSendEvent, CreateMonitorParams, CreateWindowParams, EventLoopProxyWrapper, UpdateMode, - WinitSettings, WinitWindows, + AppSendEvent, CreateMonitorParams, CreateWindowParams, EventLoopProxyWrapper, + RawWinitWindowEvent, UpdateMode, WinitSettings, WinitWindows, }; /// Persistent state that is used to run the [`App`] according to the current @@ -80,6 +80,8 @@ struct WinitAppRunnerState { previous_lifecycle: AppLifecycle, /// Bevy window events to send bevy_window_events: Vec, + /// Raw Winit window events to send + raw_winit_events: Vec, _marker: PhantomData, event_writer_system_state: SystemState<( @@ -121,6 +123,7 @@ impl WinitAppRunnerState { // 3 seems to be enough, 5 is a safe margin startup_forced_updates: 5, bevy_window_events: Vec::new(), + raw_winit_events: Vec::new(), _marker: PhantomData, event_writer_system_state, } @@ -250,6 +253,12 @@ impl ApplicationHandler for WinitAppRunnerState { return; }; + // Store a copy of the event to send to an EventWriter later. + self.raw_winit_events.push(RawWinitWindowEvent { + window_id, + event: event.clone(), + }); + // Allow AccessKit to respond to `WindowEvent`s before they reach // the engine. if let Some(adapter) = access_kit_adapters.get_mut(&window) { @@ -688,14 +697,16 @@ impl WinitAppRunnerState { } fn forward_bevy_events(&mut self) { + let raw_winit_events = self.raw_winit_events.drain(..).collect::>(); let buffered_events = self.bevy_window_events.drain(..).collect::>(); + let world = self.world_mut(); - if buffered_events.is_empty() { - return; + if !raw_winit_events.is_empty() { + world + .resource_mut::>() + .send_batch(raw_winit_events); } - let world = self.world_mut(); - for winit_event in buffered_events.iter() { match winit_event.clone() { BevyWindowEvent::AppLifecycle(e) => { @@ -782,9 +793,11 @@ impl WinitAppRunnerState { } } - world - .resource_mut::>() - .send_batch(buffered_events); + if !buffered_events.is_empty() { + world + .resource_mut::>() + .send_batch(buffered_events); + } } #[cfg(feature = "custom_cursor")]