bevy_winit(emit raw winit events) (#15884)

# Objective

- Exposes raw winit events making Bevy even more modular and powerful
for custom plugin developers (e.g. a custom render plugin).

XRef: https://github.com/bevyengine/bevy/issues/5977
It doesn't quite close the issue as sending events is not supported (or
not very useful to be precise). I would think that supporting that
requires some extra considerations by someone a bit more familiar with
the `bevy_winit` crate. That said, this PR could be a nice step forward.

## Solution

Emit `RawWinitWindowEvent` objects for each received event.

## Testing

I verified that the events are emitted using a basic test app. I don't
think it makes sense to solidify this behavior in one of the examples.

---

## Showcase

My example usage for a custom `egui_winit` integration:

```rust
for ev in winit_events.read() {
    if ev.window_id == window.id {
        let _ = egui_winit.on_window_event(&window, &ev.event);
    }
}
```

---------

Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
This commit is contained in:
HugoPeters1024 2024-12-03 18:20:43 +01:00 committed by GitHub
parent 1fe38b85f1
commit b9cc6e16da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 40 additions and 11 deletions

View file

@ -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};
@ -121,6 +121,7 @@ impl<T: Event> Plugin for WinitPlugin<T> {
app.init_non_send_resource::<WinitWindows>()
.init_resource::<WinitMonitors>()
.init_resource::<WinitSettings>()
.add_event::<RawWinitWindowEvent>()
.set_runner(winit_runner::<T>)
.add_systems(
Last,
@ -153,6 +154,21 @@ impl<T: Event> Plugin for WinitPlugin<T> {
#[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`].
///

View file

@ -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<T: Event> {
previous_lifecycle: AppLifecycle,
/// Bevy window events to send
bevy_window_events: Vec<bevy_window::WindowEvent>,
/// Raw Winit window events to send
raw_winit_events: Vec<RawWinitWindowEvent>,
_marker: PhantomData<T>,
event_writer_system_state: SystemState<(
@ -121,6 +123,7 @@ impl<T: Event> WinitAppRunnerState<T> {
// 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<T: Event> ApplicationHandler<T> for WinitAppRunnerState<T> {
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) {
@ -690,14 +699,16 @@ impl<T: Event> WinitAppRunnerState<T> {
}
fn forward_bevy_events(&mut self) {
let raw_winit_events = self.raw_winit_events.drain(..).collect::<Vec<_>>();
let buffered_events = self.bevy_window_events.drain(..).collect::<Vec<_>>();
if buffered_events.is_empty() {
return;
}
let world = self.world_mut();
if !raw_winit_events.is_empty() {
world
.resource_mut::<Events<RawWinitWindowEvent>>()
.send_batch(raw_winit_events);
}
for winit_event in buffered_events.iter() {
match winit_event.clone() {
BevyWindowEvent::AppLifecycle(e) => {
@ -784,9 +795,11 @@ impl<T: Event> WinitAppRunnerState<T> {
}
}
world
.resource_mut::<Events<BevyWindowEvent>>()
.send_batch(buffered_events);
if !buffered_events.is_empty() {
world
.resource_mut::<Events<BevyWindowEvent>>()
.send_batch(buffered_events);
}
}
fn update_cursors(&mut self, #[cfg(feature = "custom_cursor")] event_loop: &ActiveEventLoop) {