diff --git a/_release-content/migration-guides/windowplugin_exit_systems_moved_to_last.md b/_release-content/migration-guides/windowplugin_exit_systems_moved_to_last.md new file mode 100644 index 0000000000000..c60375b84abef --- /dev/null +++ b/_release-content/migration-guides/windowplugin_exit_systems_moved_to_last.md @@ -0,0 +1,8 @@ +--- +title: "`WindowPlugin` exit systems moved to `Last`" +pull_requests: [23624] +--- + +`bevy::window::close_when_requested`, `bevy::window::exit_on_all_closed` and `bevy::window::exit_on_primary_closed` have all been moved into the `Last` schedule to prevent systems that run after `Update` and rely on windows existing from panicking on the last frame of the application. + +`exit_on_all_closed` and `exit_on_primary_closed` have also been added to a new `SystemSet`, `ExitSystems`. diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index 608b6fc2f5e23..ab47aac8387dc 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -46,6 +46,7 @@ pub mod prelude { use alloc::sync::Arc; use bevy_app::prelude::*; +use bevy_ecs::schedule::IntoScheduleConfigs; use bevy_platform::sync::Mutex; impl Default for WindowPlugin { @@ -87,14 +88,14 @@ pub struct WindowPlugin { /// surprise your users. It is recommended to leave this setting to /// either [`ExitCondition::OnAllClosed`] or [`ExitCondition::OnPrimaryClosed`]. /// - /// [`ExitCondition::OnAllClosed`] will add [`exit_on_all_closed`] to [`Update`]. - /// [`ExitCondition::OnPrimaryClosed`] will add [`exit_on_primary_closed`] to [`Update`]. + /// [`ExitCondition::OnAllClosed`] will add [`exit_on_all_closed`] to [`Last`]. + /// [`ExitCondition::OnPrimaryClosed`] will add [`exit_on_primary_closed`] to [`Last`]. pub exit_condition: ExitCondition, /// Whether to close windows when they are requested to be closed (i.e. /// when the close button is pressed). /// - /// If true, this plugin will add [`close_when_requested`] to [`Update`]. + /// If true, this plugin will add [`close_when_requested`] to [`Last`]. /// If this system (or a replacement) is not running, the close button will have no effect. /// This may surprise your users. It is recommended to leave this setting as `true`. pub close_when_requested: bool, @@ -137,17 +138,17 @@ impl Plugin for WindowPlugin { match self.exit_condition { ExitCondition::OnPrimaryClosed => { - app.add_systems(PostUpdate, exit_on_primary_closed); + app.add_systems(Last, exit_on_primary_closed.in_set(ExitSystems)); } ExitCondition::OnAllClosed => { - app.add_systems(PostUpdate, exit_on_all_closed); + app.add_systems(Last, exit_on_all_closed.in_set(ExitSystems)); } ExitCondition::DontExit => {} } if self.close_when_requested { // Need to run before `exit_on_*` systems - app.add_systems(Update, close_when_requested); + app.add_systems(Last, close_when_requested.before(ExitSystems)); } } } @@ -157,11 +158,11 @@ impl Plugin for WindowPlugin { pub enum ExitCondition { /// Close application when the primary window is closed /// - /// The plugin will add [`exit_on_primary_closed`] to [`PostUpdate`]. + /// The plugin will add [`exit_on_primary_closed`] to [`Last`]. OnPrimaryClosed, /// Close application when all windows are closed /// - /// The plugin will add [`exit_on_all_closed`] to [`PostUpdate`]. + /// The plugin will add [`exit_on_all_closed`] to [`Last`]. OnAllClosed, /// Keep application running headless even after closing all windows /// diff --git a/crates/bevy_window/src/system.rs b/crates/bevy_window/src/system.rs index 112770e6097f4..9426def046e05 100644 --- a/crates/bevy_window/src/system.rs +++ b/crates/bevy_window/src/system.rs @@ -3,6 +3,11 @@ use crate::{ClosingWindow, PrimaryWindow, Window, WindowCloseRequested}; use bevy_app::AppExit; use bevy_ecs::prelude::*; +/// A [`SystemSet`] for the system that exits the application. +/// Which can be either [`exit_on_all_closed`] or [`exit_on_primary_closed`]. +#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] +pub struct ExitSystems; + /// Exit the application when there are no open windows. /// /// This system is added by the [`WindowPlugin`] in the default configuration. diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 2bd3456e96259..3dff3dc655686 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -16,7 +16,7 @@ extern crate alloc; use bevy_derive::Deref; use bevy_reflect::Reflect; -use bevy_window::{RawHandleWrapperHolder, WindowEvent}; +use bevy_window::{ExitSystems, RawHandleWrapperHolder, WindowEvent}; use core::cell::RefCell; use winit::{event_loop::EventLoop, window::WindowId}; @@ -140,7 +140,7 @@ impl Plugin for WinitPlugin { // so we don't need to care about its ordering relative to `changed_windows` changed_windows.ambiguous_with(exit_on_all_closed), changed_cursor_options, - despawn_windows, + despawn_windows.after(ExitSystems), check_keyboard_focus_lost, ) .chain(),