Skip to content

Commit

Permalink
bevy_winit(emit raw winit events) (#15884)
Browse files Browse the repository at this point in the history
# Objective

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

XRef: #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 <[email protected]>
  • Loading branch information
HugoPeters1024 and IceSentry authored Dec 3, 2024
1 parent 1fe38b8 commit b9cc6e1
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 10 deletions.
18 changes: 17 additions & 1 deletion crates/bevy_winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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`].
///
Expand Down
31 changes: 22 additions & 9 deletions crates/bevy_winit/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<(
Expand Down Expand Up @@ -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,
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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<_>>();
let world = self.world_mut();

if buffered_events.is_empty() {
return;
if !raw_winit_events.is_empty() {
world
.resource_mut::<Events<RawWinitWindowEvent>>()
.send_batch(raw_winit_events);
}

let world = self.world_mut();

for winit_event in buffered_events.iter() {
match winit_event.clone() {
BevyWindowEvent::AppLifecycle(e) => {
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit b9cc6e1

Please sign in to comment.