Push window resize and scale factor messages to bevy_window_events (#24046)

I was initially using `MessageReader<WindowResized>` in my system for my
app but once my system grew to use more and more window events, I
refactored to using `MessageReader<WindowEvent>` and matching on its
variants. This is where I ran into the issue of the
`WindowEvent::WindowResized` case never matching.

When making this PR, I noticed
`WindowEvent::WindowBackendScaleFactorChanged` and
`WindowEvent::WindowScaleFactorChanged` had the same issue.

# Objective

Fixes #15268

## Solution

Instead of writing into `MessageWriter<WindowResized>`,
`MessageWriter<WindowBackendScaleFactorChanged>` and
`MessageWriter<WindowScaleFactorChanged>`, push into
`bevy_window_events` where it gets sent to the `forward_bevy_events`
function for syncing the messages.

## Testing

I made local modifications to the `window_resizing` example to use a
`MessageReader<WindowEvent>` instead of `MessageReader<WindowResized>`
like so:
```rs
fn on_resize_system(
    mut text: Single<&mut Text, With<ResolutionText>>,
    mut resize_reader: MessageReader<WindowEvent>,
) {
    for e in resize_reader.read() {
        if let WindowEvent::WindowResized(e) = e {
            // When resolution is being changed
            text.0 = format!("{:.1} x {:.1}", e.width, e.height);
        }
    }
}
```
I ran this example on linux wayland.
This commit is contained in:
Dea
2026-05-04 07:46:24 -07:00
committed by Carter Anderson
parent fc5c1806b4
commit 9ee0573ebd
3 changed files with 226 additions and 67 deletions
+209 -54
View File
@@ -3,7 +3,7 @@ use bevy_app::{App, AppExit, PluginsState};
use bevy_ecs::{
change_detection::{DetectChanges, Res},
entity::Entity,
message::{MessageCursor, MessageWriter},
message::MessageCursor,
prelude::*,
system::SystemState,
world::FromWorld,
@@ -79,10 +79,7 @@ pub(crate) struct WinitAppRunnerState {
/// Raw Winit window events to send
raw_winit_events: Vec<RawWinitWindowEvent>,
message_writer_system_state: SystemState<(
MessageWriter<'static, WindowResized>,
MessageWriter<'static, WindowBackendScaleFactorChanged>,
MessageWriter<'static, WindowScaleFactorChanged>,
windows_system_state: SystemState<
Query<
'static,
'static,
@@ -92,19 +89,16 @@ pub(crate) struct WinitAppRunnerState {
&'static mut WinitWindowPressedKeys,
),
>,
)>,
>,
/// time at which next tick is scheduled to run when `update_mode` is [`UpdateMode::Reactive`]
scheduled_tick_start: Option<Instant>,
}
impl WinitAppRunnerState {
fn new(mut app: App) -> Self {
let message_writer_system_state: SystemState<(
MessageWriter<WindowResized>,
MessageWriter<WindowBackendScaleFactorChanged>,
MessageWriter<WindowScaleFactorChanged>,
let windows_system_state: SystemState<
Query<(&mut Window, &mut CachedWindow, &mut WinitWindowPressedKeys)>,
)> = SystemState::new(app.world_mut());
> = SystemState::new(app.world_mut());
Self {
app,
@@ -122,7 +116,7 @@ impl WinitAppRunnerState {
startup_forced_updates: 5,
bevy_window_events: Vec::new(),
raw_winit_events: Vec::new(),
message_writer_system_state,
windows_system_state,
scheduled_tick_start: None,
}
}
@@ -219,13 +213,8 @@ impl ApplicationHandler<WinitUserEvent> for WinitAppRunnerState {
WINIT_WINDOWS.with_borrow(|winit_windows| {
ACCESS_KIT_ADAPTERS.with_borrow_mut(|access_kit_adapters| {
let (
mut window_resized,
mut window_backend_scale_factor_changed,
mut window_scale_factor_changed,
mut windows,
) = self
.message_writer_system_state
let mut windows = self
.windows_system_state
.get_mut(self.app.world_mut())
.unwrap();
@@ -256,17 +245,18 @@ impl ApplicationHandler<WinitUserEvent> for WinitAppRunnerState {
}
match event {
WindowEvent::Resized(size) => {
react_to_resize(window, &mut win, size, &mut window_resized);
}
WindowEvent::Resized(size) => self
.bevy_window_events
.send(react_to_resize(window, &mut win, size)),
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
react_to_scale_factor_change(
window,
&mut win,
scale_factor,
&mut window_backend_scale_factor_changed,
&mut window_scale_factor_changed,
);
let (window_backend_scale_factor_changed, window_scale_factor_changed) =
react_to_scale_factor_change(window, &mut win, scale_factor);
self.bevy_window_events
.send(window_backend_scale_factor_changed);
if let Some(window_scale_factor_changed) = window_scale_factor_changed {
self.bevy_window_events.send(window_scale_factor_changed);
}
}
WindowEvent::CloseRequested => self
.bevy_window_events
@@ -926,42 +916,51 @@ pub(crate) fn react_to_resize(
window_entity: Entity,
window: &mut Window,
size: PhysicalSize<u32>,
window_resized: &mut MessageWriter<WindowResized>,
) {
) -> WindowResized {
window
.resolution
.set_physical_resolution(size.width, size.height);
window_resized.write(WindowResized {
WindowResized {
window: window_entity,
width: window.width(),
height: window.height(),
});
}
}
pub(crate) fn react_to_scale_factor_change(
window_entity: Entity,
window: &mut Window,
scale_factor: f64,
window_backend_scale_factor_changed: &mut MessageWriter<WindowBackendScaleFactorChanged>,
window_scale_factor_changed: &mut MessageWriter<WindowScaleFactorChanged>,
) -> (
WindowBackendScaleFactorChanged,
Option<WindowScaleFactorChanged>,
) {
let prior_factor = window.resolution.scale_factor();
window.resolution.set_scale_factor(scale_factor as f32);
window_backend_scale_factor_changed.write(WindowBackendScaleFactorChanged {
let window_backend_scale_factor_changed = WindowBackendScaleFactorChanged {
window: window_entity,
scale_factor,
});
};
let scale_factor_override = window.resolution.scale_factor_override();
if scale_factor_override.is_none() && !relative_eq!(scale_factor as f32, prior_factor) {
window_scale_factor_changed.write(WindowScaleFactorChanged {
window: window_entity,
scale_factor,
});
}
let window_scale_factor_changed =
if scale_factor_override.is_none() && !relative_eq!(scale_factor as f32, prior_factor) {
let window_scale_factor_changed = WindowScaleFactorChanged {
window: window_entity,
scale_factor,
};
Some(window_scale_factor_changed)
} else {
None
};
(
window_backend_scale_factor_changed,
window_scale_factor_changed,
)
}
#[cfg(test)]
@@ -1011,6 +1010,30 @@ mod tests {
})
);
assert_eq!(window_scale_factor_changed_messages_iter.next(), None);
let window_event_messages = app.world().resource::<Messages<BevyWindowEvent>>();
assert_eq!(window_event_messages.len(), 2);
let mut window_event_messages_iter = window_event_messages.iter_current_update_messages();
assert_eq!(
window_event_messages_iter.next(),
Some(&BevyWindowEvent::WindowBackendScaleFactorChanged(
WindowBackendScaleFactorChanged {
window: window_entity,
scale_factor: 2.0
}
))
);
assert_eq!(
window_event_messages_iter.next(),
Some(&BevyWindowEvent::WindowScaleFactorChanged(
WindowScaleFactorChanged {
window: window_entity,
scale_factor: 2.0
}
))
);
assert_eq!(window_event_messages_iter.next(), None);
}
#[test]
@@ -1043,6 +1066,21 @@ mod tests {
let window_scale_factor_changed_messages =
app.world().resource::<Messages<WindowScaleFactorChanged>>();
assert!(window_scale_factor_changed_messages.is_empty());
let window_event_messages = app.world().resource::<Messages<BevyWindowEvent>>();
assert_eq!(window_event_messages.len(), 1);
let mut window_event_messages_iter = window_event_messages.iter_current_update_messages();
assert_eq!(
window_event_messages_iter.next(),
Some(&BevyWindowEvent::WindowBackendScaleFactorChanged(
WindowBackendScaleFactorChanged {
window: window_entity,
scale_factor: 1.0
}
))
);
assert_eq!(window_event_messages_iter.next(), None);
}
fn setup_react_to_scale_factor_change_test_app(
@@ -1052,20 +1090,30 @@ mod tests {
let mut app = App::new();
app.add_message::<WindowBackendScaleFactorChanged>();
app.add_message::<WindowScaleFactorChanged>();
app.add_message::<BevyWindowEvent>();
app.add_systems(
Update,
move |mut window: Single<(Entity, &mut Window)>,
mut window_backend_scale_factor_changed: MessageWriter<
WindowBackendScaleFactorChanged,
>,
mut window_scale_factor_changed: MessageWriter<WindowScaleFactorChanged>| {
react_to_scale_factor_change(
window.0,
&mut window.1,
changed_scale_factor,
&mut window_backend_scale_factor_changed,
&mut window_scale_factor_changed,
);
mut window_backend_scale_factor_changed_writer: MessageWriter<
WindowBackendScaleFactorChanged,
>,
mut window_scale_factor_changed_writer: MessageWriter<
WindowScaleFactorChanged,
>,
mut window_event: MessageWriter<BevyWindowEvent>| {
let (window_backend_scale_factor_changed, window_scale_factor_changed) =
react_to_scale_factor_change(window.0, &mut window.1, changed_scale_factor);
window_backend_scale_factor_changed_writer
.write(window_backend_scale_factor_changed.clone());
window_event.write(BevyWindowEvent::WindowBackendScaleFactorChanged(
window_backend_scale_factor_changed,
));
if let Some(window_scale_factor_changed) = window_scale_factor_changed {
window_scale_factor_changed_writer.write(window_scale_factor_changed.clone());
window_event.write(BevyWindowEvent::WindowScaleFactorChanged(
window_scale_factor_changed,
));
}
},
);
@@ -1075,4 +1123,111 @@ mod tests {
(app, window_entity)
}
#[test]
fn test_react_to_resize_with_changed_size() {
let (mut app, window_entity) =
setup_react_to_resize(PhysicalSize::new(1280, 720), PhysicalSize::new(1920, 1080));
app.update();
let window = app.world().get::<Window>(window_entity).unwrap();
assert_eq!(window.resolution.physical_width(), 1920);
assert_eq!(window.resolution.physical_height(), 1080);
let window_resized_messages = app.world().resource::<Messages<WindowResized>>();
assert_eq!(window_resized_messages.len(), 1);
let mut window_resized_messages_iter =
window_resized_messages.iter_current_update_messages();
assert_eq!(
window_resized_messages_iter.next(),
Some(&WindowResized {
window: window_entity,
width: 1920.0,
height: 1080.0
})
);
assert_eq!(window_resized_messages_iter.next(), None);
let window_event_messages = app.world().resource::<Messages<BevyWindowEvent>>();
assert_eq!(window_event_messages.len(), 1);
let mut window_event_messages_iter = window_event_messages.iter_current_update_messages();
assert_eq!(
window_event_messages_iter.next(),
Some(&BevyWindowEvent::WindowResized(WindowResized {
window: window_entity,
width: 1920.0,
height: 1080.0
}))
);
assert_eq!(window_event_messages_iter.next(), None);
}
#[test]
fn test_react_to_resize_with_same_size() {
let (mut app, window_entity) =
setup_react_to_resize(PhysicalSize::new(1280, 720), PhysicalSize::new(1280, 720));
app.update();
let window = app.world().get::<Window>(window_entity).unwrap();
assert_eq!(window.resolution.physical_width(), 1280);
assert_eq!(window.resolution.physical_height(), 720);
let window_resized_messages = app.world().resource::<Messages<WindowResized>>();
assert_eq!(window_resized_messages.len(), 1);
let mut window_resized_messages_iter =
window_resized_messages.iter_current_update_messages();
assert_eq!(
window_resized_messages_iter.next(),
Some(&WindowResized {
window: window_entity,
width: 1280.0,
height: 720.0
})
);
assert_eq!(window_resized_messages_iter.next(), None);
let window_event_messages = app.world().resource::<Messages<BevyWindowEvent>>();
assert_eq!(window_event_messages.len(), 1);
let mut window_event_messages_iter = window_event_messages.iter_current_update_messages();
assert_eq!(
window_event_messages_iter.next(),
Some(&BevyWindowEvent::WindowResized(WindowResized {
window: window_entity,
width: 1280.0,
height: 720.0
}))
);
assert_eq!(window_event_messages_iter.next(), None);
}
fn setup_react_to_resize(
initial_size: PhysicalSize<u32>,
changed_size: PhysicalSize<u32>,
) -> (App, Entity) {
let mut app = App::new();
app.add_message::<WindowResized>();
app.add_message::<BevyWindowEvent>();
app.add_systems(
Update,
move |mut window: Single<(Entity, &mut Window)>,
mut window_resized_writer: MessageWriter<WindowResized>,
mut window_event: MessageWriter<BevyWindowEvent>| {
let window_resized = react_to_resize(window.0, &mut window.1, changed_size);
window_resized_writer.write(window_resized.clone());
window_event.write(BevyWindowEvent::WindowResized(window_resized));
},
);
let mut window = Window::default();
window
.resolution
.set_physical_resolution(initial_size.width, initial_size.height);
let window_entity = app.world_mut().spawn(window).id();
(app, window_entity)
}
}
+4 -1
View File
@@ -310,6 +310,7 @@ pub(crate) fn changed_windows(
>,
monitors: Res<WinitMonitors>,
mut window_resized: MessageWriter<WindowResized>,
mut window_event: MessageWriter<WindowEvent>,
_non_send_marker: NonSendMarker,
) {
WINIT_WINDOWS.with_borrow(|winit_windows| {
@@ -423,7 +424,9 @@ pub(crate) fn changed_windows(
if physical_size != cached_physical_size
&& let Some(new_physical_size) = winit_window.request_inner_size(physical_size) {
react_to_resize(entity, &mut window, new_physical_size, &mut window_resized);
let event = react_to_resize(entity, &mut window, new_physical_size);
window_resized.write(event.clone());
window_event.write(WindowEvent::WindowResized(event));
}
}
@@ -1,17 +1,18 @@
diff --git a/crates/bevy_winit/src/state.rs b/crates/bevy_winit/src/state.rs
index df0aab42d..6e28a6e9c 100644
index d4933d7d3..259f8236e 100644
--- a/crates/bevy_winit/src/state.rs
+++ b/crates/bevy_winit/src/state.rs
@@ -208,6 +208,12 @@ impl<T: Event> ApplicationHandler<T> for WinitAppRunnerState<T> {
adapter.process_event(winit_window, &event);
}
@@ -244,6 +244,13 @@ impl ApplicationHandler<WinitUserEvent> for WinitAppRunnerState {
adapter.process_event(winit_window, &event);
}
+ window_resized.write(WindowResized {
+ window,
+ width: win.width(),
+ height: win.height(),
+ });
+ self.bevy_window_events
+ .push(BevyWindowEvent::WindowResized(WindowResized {
+ window,
+ width: win.width(),
+ height: win.height(),
+ }));
+
match event {
WindowEvent::Resized(size) => {
react_to_resize(window, &mut win, size, &mut window_resized);
match event {
WindowEvent::Resized(size) => self
.bevy_window_events