2023-01-19 00:38:28 +00:00
use bevy_ecs ::{
entity ::Entity ,
event ::EventWriter ,
2024-01-28 21:09:23 +00:00
prelude ::{ Changed , Component } ,
query ::QueryFilter ,
2023-02-04 20:53:37 +00:00
removal_detection ::RemovedComponents ,
2024-06-05 18:13:59 +00:00
system ::{ Local , NonSendMut , Query , SystemParamItem } ,
2023-01-19 00:38:28 +00:00
} ;
2024-01-28 21:09:23 +00:00
use bevy_utils ::tracing ::{ error , info , warn } ;
2024-02-10 20:17:04 +00:00
use bevy_window ::{
2024-08-06 10:54:37 +00:00
ClosingWindow , Monitor , PrimaryMonitor , RawHandleWrapper , VideoMode , Window , WindowClosed ,
WindowClosing , WindowCreated , WindowMode , WindowResized , WindowWrapper ,
2024-02-10 20:17:04 +00:00
} ;
2023-01-19 00:38:28 +00:00
fix: upgrade to winit v0.30 (#13366)
# Objective
- Upgrade winit to v0.30
- Fixes https://github.com/bevyengine/bevy/issues/13331
## Solution
This is a rewrite/adaptation of the new trait system described and
implemented in `winit` v0.30.
## Migration Guide
The custom UserEvent is now renamed as WakeUp, used to wake up the loop
if anything happens outside the app (a new
[custom_user_event](https://github.com/bevyengine/bevy/pull/13366/files#diff-2de8c0a8d3028d0059a3d80ae31b2bbc1cde2595ce2d317ea378fe3e0cf6ef2d)
shows this behavior.
The internal `UpdateState` has been removed and replaced internally by
the AppLifecycle. When changed, the AppLifecycle is sent as an event.
The `UpdateMode` now accepts only two values: `Continuous` and
`Reactive`, but the latter exposes 3 new properties to enable reactive
to device, user or window events. The previous `UpdateMode::Reactive` is
now equivalent to `UpdateMode::reactive()`, while
`UpdateMode::ReactiveLowPower` to `UpdateMode::reactive_low_power()`.
The `ApplicationLifecycle` has been renamed as `AppLifecycle`, and now
contains the possible values of the application state inside the event
loop:
* `Idle`: the loop has not started yet
* `Running` (previously called `Started`): the loop is running
* `WillSuspend`: the loop is going to be suspended
* `Suspended`: the loop is suspended
* `WillResume`: the loop is going to be resumed
Note: the `Resumed` state has been removed since the resumed app is just
running.
Finally, now that `winit` enables this, it extends the `WinitPlugin` to
support custom events.
## Test platforms
- [x] Windows
- [x] MacOs
- [x] Linux (x11)
- [x] Linux (Wayland)
- [x] Android
- [x] iOS
- [x] WASM/WebGPU
- [x] WASM/WebGL2
## Outstanding issues / regressions
- [ ] iOS: build failed in CI
- blocking, but may just be flakiness
- [x] Cross-platform: when the window is maximised, changes in the scale
factor don't apply, to make them apply one has to make the window
smaller again. (Re-maximising keeps the updated scale factor)
- non-blocking, but good to fix
- [ ] Android: it's pretty easy to quickly open and close the app and
then the music keeps playing when suspended.
- non-blocking but worrying
- [ ] Web: the application will hang when switching tabs
- Not new, duplicate of https://github.com/bevyengine/bevy/issues/13486
- [ ] Cross-platform?: Screenshot failure, `ERROR present_frames:
wgpu_core::present: No work has been submitted for this frame before`
taking the first screenshot, but after pressing space
- non-blocking, but good to fix
---------
Co-authored-by: François <francois.mockers@vleue.com>
2024-06-03 13:06:48 +00:00
use winit ::dpi ::{ LogicalPosition , LogicalSize , PhysicalPosition , PhysicalSize } ;
use winit ::event_loop ::ActiveEventLoop ;
2023-01-19 00:38:28 +00:00
2024-06-08 21:42:01 +00:00
use bevy_app ::AppExit ;
use bevy_ecs ::prelude ::EventReader ;
2024-05-12 15:56:01 +00:00
use bevy_ecs ::query ::With ;
2024-08-06 10:54:37 +00:00
use bevy_ecs ::system ::Res ;
use bevy_math ::{ IVec2 , UVec2 } ;
2024-06-04 12:44:25 +00:00
#[ cfg(target_os = " ios " ) ]
use winit ::platform ::ios ::WindowExtIOS ;
2024-03-03 14:33:30 +00:00
#[ cfg(target_arch = " wasm32 " ) ]
use winit ::platform ::web ::WindowExtWebSys ;
fix: upgrade to winit v0.30 (#13366)
# Objective
- Upgrade winit to v0.30
- Fixes https://github.com/bevyengine/bevy/issues/13331
## Solution
This is a rewrite/adaptation of the new trait system described and
implemented in `winit` v0.30.
## Migration Guide
The custom UserEvent is now renamed as WakeUp, used to wake up the loop
if anything happens outside the app (a new
[custom_user_event](https://github.com/bevyengine/bevy/pull/13366/files#diff-2de8c0a8d3028d0059a3d80ae31b2bbc1cde2595ce2d317ea378fe3e0cf6ef2d)
shows this behavior.
The internal `UpdateState` has been removed and replaced internally by
the AppLifecycle. When changed, the AppLifecycle is sent as an event.
The `UpdateMode` now accepts only two values: `Continuous` and
`Reactive`, but the latter exposes 3 new properties to enable reactive
to device, user or window events. The previous `UpdateMode::Reactive` is
now equivalent to `UpdateMode::reactive()`, while
`UpdateMode::ReactiveLowPower` to `UpdateMode::reactive_low_power()`.
The `ApplicationLifecycle` has been renamed as `AppLifecycle`, and now
contains the possible values of the application state inside the event
loop:
* `Idle`: the loop has not started yet
* `Running` (previously called `Started`): the loop is running
* `WillSuspend`: the loop is going to be suspended
* `Suspended`: the loop is suspended
* `WillResume`: the loop is going to be resumed
Note: the `Resumed` state has been removed since the resumed app is just
running.
Finally, now that `winit` enables this, it extends the `WinitPlugin` to
support custom events.
## Test platforms
- [x] Windows
- [x] MacOs
- [x] Linux (x11)
- [x] Linux (Wayland)
- [x] Android
- [x] iOS
- [x] WASM/WebGPU
- [x] WASM/WebGL2
## Outstanding issues / regressions
- [ ] iOS: build failed in CI
- blocking, but may just be flakiness
- [x] Cross-platform: when the window is maximised, changes in the scale
factor don't apply, to make them apply one has to make the window
smaller again. (Re-maximising keeps the updated scale factor)
- non-blocking, but good to fix
- [ ] Android: it's pretty easy to quickly open and close the app and
then the music keeps playing when suspended.
- non-blocking but worrying
- [ ] Web: the application will hang when switching tabs
- Not new, duplicate of https://github.com/bevyengine/bevy/issues/13486
- [ ] Cross-platform?: Screenshot failure, `ERROR present_frames:
wgpu_core::present: No work has been submitted for this frame before`
taking the first screenshot, but after pressing space
- non-blocking, but good to fix
---------
Co-authored-by: François <francois.mockers@vleue.com>
2024-06-03 13:06:48 +00:00
use crate ::state ::react_to_resize ;
2024-08-06 10:54:37 +00:00
use crate ::winit_monitors ::WinitMonitors ;
2023-02-03 16:41:39 +00:00
use crate ::{
2023-07-23 01:02:40 +00:00
converters ::{
2024-08-12 15:49:03 +00:00
convert_enabled_buttons , convert_window_level , convert_window_theme , convert_winit_theme ,
2023-07-23 01:02:40 +00:00
} ,
2024-08-06 10:54:37 +00:00
get_best_videomode , get_fitting_videomode , select_monitor , CreateMonitorParams ,
CreateWindowParams , WinitWindows ,
2023-02-03 16:41:39 +00:00
} ;
2023-01-19 00:38:28 +00:00
2023-07-31 21:41:59 +00:00
/// Creates new windows on the [`winit`] backend for each entity with a newly-added
/// [`Window`] component.
2023-01-19 00:38:28 +00:00
///
2023-07-31 21:41:59 +00:00
/// If any of these entities are missing required components, those will be added with their
/// default values.
2023-03-01 22:45:04 +00:00
#[ allow(clippy::too_many_arguments) ]
2024-03-12 14:54:06 +00:00
pub fn create_windows < F : QueryFilter + 'static > (
fix: upgrade to winit v0.30 (#13366)
# Objective
- Upgrade winit to v0.30
- Fixes https://github.com/bevyengine/bevy/issues/13331
## Solution
This is a rewrite/adaptation of the new trait system described and
implemented in `winit` v0.30.
## Migration Guide
The custom UserEvent is now renamed as WakeUp, used to wake up the loop
if anything happens outside the app (a new
[custom_user_event](https://github.com/bevyengine/bevy/pull/13366/files#diff-2de8c0a8d3028d0059a3d80ae31b2bbc1cde2595ce2d317ea378fe3e0cf6ef2d)
shows this behavior.
The internal `UpdateState` has been removed and replaced internally by
the AppLifecycle. When changed, the AppLifecycle is sent as an event.
The `UpdateMode` now accepts only two values: `Continuous` and
`Reactive`, but the latter exposes 3 new properties to enable reactive
to device, user or window events. The previous `UpdateMode::Reactive` is
now equivalent to `UpdateMode::reactive()`, while
`UpdateMode::ReactiveLowPower` to `UpdateMode::reactive_low_power()`.
The `ApplicationLifecycle` has been renamed as `AppLifecycle`, and now
contains the possible values of the application state inside the event
loop:
* `Idle`: the loop has not started yet
* `Running` (previously called `Started`): the loop is running
* `WillSuspend`: the loop is going to be suspended
* `Suspended`: the loop is suspended
* `WillResume`: the loop is going to be resumed
Note: the `Resumed` state has been removed since the resumed app is just
running.
Finally, now that `winit` enables this, it extends the `WinitPlugin` to
support custom events.
## Test platforms
- [x] Windows
- [x] MacOs
- [x] Linux (x11)
- [x] Linux (Wayland)
- [x] Android
- [x] iOS
- [x] WASM/WebGPU
- [x] WASM/WebGL2
## Outstanding issues / regressions
- [ ] iOS: build failed in CI
- blocking, but may just be flakiness
- [x] Cross-platform: when the window is maximised, changes in the scale
factor don't apply, to make them apply one has to make the window
smaller again. (Re-maximising keeps the updated scale factor)
- non-blocking, but good to fix
- [ ] Android: it's pretty easy to quickly open and close the app and
then the music keeps playing when suspended.
- non-blocking but worrying
- [ ] Web: the application will hang when switching tabs
- Not new, duplicate of https://github.com/bevyengine/bevy/issues/13486
- [ ] Cross-platform?: Screenshot failure, `ERROR present_frames:
wgpu_core::present: No work has been submitted for this frame before`
taking the first screenshot, but after pressing space
- non-blocking, but good to fix
---------
Co-authored-by: François <francois.mockers@vleue.com>
2024-06-03 13:06:48 +00:00
event_loop : & ActiveEventLoop ,
2024-01-28 21:09:23 +00:00
(
mut commands ,
mut created_windows ,
mut window_created_events ,
mut winit_windows ,
mut adapters ,
mut handlers ,
accessibility_requested ,
2024-08-06 10:54:37 +00:00
monitors ,
2024-01-28 21:09:23 +00:00
) : SystemParamItem < CreateWindowParams < F > > ,
2023-01-19 00:38:28 +00:00
) {
fix: upgrade to winit v0.30 (#13366)
# Objective
- Upgrade winit to v0.30
- Fixes https://github.com/bevyengine/bevy/issues/13331
## Solution
This is a rewrite/adaptation of the new trait system described and
implemented in `winit` v0.30.
## Migration Guide
The custom UserEvent is now renamed as WakeUp, used to wake up the loop
if anything happens outside the app (a new
[custom_user_event](https://github.com/bevyengine/bevy/pull/13366/files#diff-2de8c0a8d3028d0059a3d80ae31b2bbc1cde2595ce2d317ea378fe3e0cf6ef2d)
shows this behavior.
The internal `UpdateState` has been removed and replaced internally by
the AppLifecycle. When changed, the AppLifecycle is sent as an event.
The `UpdateMode` now accepts only two values: `Continuous` and
`Reactive`, but the latter exposes 3 new properties to enable reactive
to device, user or window events. The previous `UpdateMode::Reactive` is
now equivalent to `UpdateMode::reactive()`, while
`UpdateMode::ReactiveLowPower` to `UpdateMode::reactive_low_power()`.
The `ApplicationLifecycle` has been renamed as `AppLifecycle`, and now
contains the possible values of the application state inside the event
loop:
* `Idle`: the loop has not started yet
* `Running` (previously called `Started`): the loop is running
* `WillSuspend`: the loop is going to be suspended
* `Suspended`: the loop is suspended
* `WillResume`: the loop is going to be resumed
Note: the `Resumed` state has been removed since the resumed app is just
running.
Finally, now that `winit` enables this, it extends the `WinitPlugin` to
support custom events.
## Test platforms
- [x] Windows
- [x] MacOs
- [x] Linux (x11)
- [x] Linux (Wayland)
- [x] Android
- [x] iOS
- [x] WASM/WebGPU
- [x] WASM/WebGL2
## Outstanding issues / regressions
- [ ] iOS: build failed in CI
- blocking, but may just be flakiness
- [x] Cross-platform: when the window is maximised, changes in the scale
factor don't apply, to make them apply one has to make the window
smaller again. (Re-maximising keeps the updated scale factor)
- non-blocking, but good to fix
- [ ] Android: it's pretty easy to quickly open and close the app and
then the music keeps playing when suspended.
- non-blocking but worrying
- [ ] Web: the application will hang when switching tabs
- Not new, duplicate of https://github.com/bevyengine/bevy/issues/13486
- [ ] Cross-platform?: Screenshot failure, `ERROR present_frames:
wgpu_core::present: No work has been submitted for this frame before`
taking the first screenshot, but after pressing space
- non-blocking, but good to fix
---------
Co-authored-by: François <francois.mockers@vleue.com>
2024-06-03 13:06:48 +00:00
for ( entity , mut window , handle_holder ) in & mut created_windows {
2023-01-19 00:38:28 +00:00
if winit_windows . get_window ( entity ) . is_some ( ) {
continue ;
}
info! (
" Creating new window {:?} ({:?}) " ,
2023-02-07 14:18:13 +00:00
window . title . as_str ( ) ,
2023-01-19 00:38:28 +00:00
entity
) ;
2023-03-01 22:45:04 +00:00
let winit_window = winit_windows . create_window (
event_loop ,
entity ,
& window ,
& mut adapters ,
& mut handlers ,
2023-09-02 18:35:06 +00:00
& accessibility_requested ,
2024-08-06 10:54:37 +00:00
& monitors ,
2023-03-01 22:45:04 +00:00
) ;
2023-06-05 21:04:22 +00:00
if let Some ( theme ) = winit_window . theme ( ) {
window . window_theme = Some ( convert_winit_theme ( theme ) ) ;
}
2023-02-07 14:18:13 +00:00
window
2023-01-19 00:38:28 +00:00
. resolution
2024-06-21 18:04:57 +00:00
. set_scale_factor_and_apply_to_physical_size ( winit_window . scale_factor ( ) as f32 ) ;
fix: rewrite winit loop (#12669)
# Objective
- Simplifies/clarifies the winit loop.
- Fixes #12612.
## Solution
The Winit loop runs following this flow:
* NewEvents
* Any number of other events, that can be 0, including RequestRedraw
* AboutToWait
Bevy also uses the UpdateMode, to define how the next loop has to run.
It can be essentially:
* Continuous, using ControlFlow::Wait for windowed apps, and
ControlFlow::Poll for windowless apps
* Reactive/ReactiveLowPower, using ControlFlow::WaitUntil with a
specific wait delay
The changes are made to follow this pattern, so that
* NewEvents define if the WaitUntil has been canceled because we
received a Winit event.
* AboutToWait:
* checks if the window has to be redrawn
* otherwise calls app.update() if the WaitUntil timeout has elapsed
* updates the ControlFlow accordingly
To make the code more logical:
* AboutToWait checks if any Bevy's RequestRedraw event has been emitted
* create_windows is run every cycle, at the beginning of the loop
* the ActiveState (that could be renamed ActivityState) is updated in
AboutToWait, symmetrically for WillSuspend/WillResume
* the AppExit events are checked every loop cycle, to exit the app early
## Platform-specific testing
- [x] Windows
- [x] MacOs
- [x] Linux (x11)
- [x] Linux (Wayland)
- [x] Android
- [x] iOS
- [x] WASM/WebGL2 (Chrome)
- [x] WASM/WebGL2 (Firefox)
- [x] WASM/WebGL2 (Safari)
- [x] WASM/WebGpu (Chrome)
---------
Co-authored-by: François <francois.mockers@vleue.com>
2024-05-02 19:57:19 +00:00
commands . entity ( entity ) . insert ( CachedWindow {
window : window . clone ( ) ,
} ) ;
if let Ok ( handle_wrapper ) = RawHandleWrapper ::new ( winit_window ) {
fix: upgrade to winit v0.30 (#13366)
# Objective
- Upgrade winit to v0.30
- Fixes https://github.com/bevyengine/bevy/issues/13331
## Solution
This is a rewrite/adaptation of the new trait system described and
implemented in `winit` v0.30.
## Migration Guide
The custom UserEvent is now renamed as WakeUp, used to wake up the loop
if anything happens outside the app (a new
[custom_user_event](https://github.com/bevyengine/bevy/pull/13366/files#diff-2de8c0a8d3028d0059a3d80ae31b2bbc1cde2595ce2d317ea378fe3e0cf6ef2d)
shows this behavior.
The internal `UpdateState` has been removed and replaced internally by
the AppLifecycle. When changed, the AppLifecycle is sent as an event.
The `UpdateMode` now accepts only two values: `Continuous` and
`Reactive`, but the latter exposes 3 new properties to enable reactive
to device, user or window events. The previous `UpdateMode::Reactive` is
now equivalent to `UpdateMode::reactive()`, while
`UpdateMode::ReactiveLowPower` to `UpdateMode::reactive_low_power()`.
The `ApplicationLifecycle` has been renamed as `AppLifecycle`, and now
contains the possible values of the application state inside the event
loop:
* `Idle`: the loop has not started yet
* `Running` (previously called `Started`): the loop is running
* `WillSuspend`: the loop is going to be suspended
* `Suspended`: the loop is suspended
* `WillResume`: the loop is going to be resumed
Note: the `Resumed` state has been removed since the resumed app is just
running.
Finally, now that `winit` enables this, it extends the `WinitPlugin` to
support custom events.
## Test platforms
- [x] Windows
- [x] MacOs
- [x] Linux (x11)
- [x] Linux (Wayland)
- [x] Android
- [x] iOS
- [x] WASM/WebGPU
- [x] WASM/WebGL2
## Outstanding issues / regressions
- [ ] iOS: build failed in CI
- blocking, but may just be flakiness
- [x] Cross-platform: when the window is maximised, changes in the scale
factor don't apply, to make them apply one has to make the window
smaller again. (Re-maximising keeps the updated scale factor)
- non-blocking, but good to fix
- [ ] Android: it's pretty easy to quickly open and close the app and
then the music keeps playing when suspended.
- non-blocking but worrying
- [ ] Web: the application will hang when switching tabs
- Not new, duplicate of https://github.com/bevyengine/bevy/issues/13486
- [ ] Cross-platform?: Screenshot failure, `ERROR present_frames:
wgpu_core::present: No work has been submitted for this frame before`
taking the first screenshot, but after pressing space
- non-blocking, but good to fix
---------
Co-authored-by: François <francois.mockers@vleue.com>
2024-06-03 13:06:48 +00:00
let mut entity = commands . entity ( entity ) ;
entity . insert ( handle_wrapper . clone ( ) ) ;
if let Some ( handle_holder ) = handle_holder {
* handle_holder . 0. lock ( ) . unwrap ( ) = Some ( handle_wrapper ) ;
}
fix: rewrite winit loop (#12669)
# Objective
- Simplifies/clarifies the winit loop.
- Fixes #12612.
## Solution
The Winit loop runs following this flow:
* NewEvents
* Any number of other events, that can be 0, including RequestRedraw
* AboutToWait
Bevy also uses the UpdateMode, to define how the next loop has to run.
It can be essentially:
* Continuous, using ControlFlow::Wait for windowed apps, and
ControlFlow::Poll for windowless apps
* Reactive/ReactiveLowPower, using ControlFlow::WaitUntil with a
specific wait delay
The changes are made to follow this pattern, so that
* NewEvents define if the WaitUntil has been canceled because we
received a Winit event.
* AboutToWait:
* checks if the window has to be redrawn
* otherwise calls app.update() if the WaitUntil timeout has elapsed
* updates the ControlFlow accordingly
To make the code more logical:
* AboutToWait checks if any Bevy's RequestRedraw event has been emitted
* create_windows is run every cycle, at the beginning of the loop
* the ActiveState (that could be renamed ActivityState) is updated in
AboutToWait, symmetrically for WillSuspend/WillResume
* the AppExit events are checked every loop cycle, to exit the app early
## Platform-specific testing
- [x] Windows
- [x] MacOs
- [x] Linux (x11)
- [x] Linux (Wayland)
- [x] Android
- [x] iOS
- [x] WASM/WebGL2 (Chrome)
- [x] WASM/WebGL2 (Firefox)
- [x] WASM/WebGL2 (Safari)
- [x] WASM/WebGpu (Chrome)
---------
Co-authored-by: François <francois.mockers@vleue.com>
2024-05-02 19:57:19 +00:00
}
2023-01-19 00:38:28 +00:00
2024-03-03 14:33:30 +00:00
#[ cfg(target_arch = " wasm32 " ) ]
{
if window . fit_canvas_to_parent {
let canvas = winit_window
. canvas ( )
. expect ( " window.canvas() can only be called in main thread. " ) ;
let style = canvas . style ( ) ;
style . set_property ( " width " , " 100% " ) . unwrap ( ) ;
style . set_property ( " height " , " 100% " ) . unwrap ( ) ;
}
}
2024-06-04 12:44:25 +00:00
#[ cfg(target_os = " ios " ) ]
{
winit_window . recognize_pinch_gesture ( window . recognize_pinch_gesture ) ;
winit_window . recognize_rotation_gesture ( window . recognize_rotation_gesture ) ;
winit_window . recognize_doubletap_gesture ( window . recognize_doubletap_gesture ) ;
if let Some ( ( min , max ) ) = window . recognize_pan_gesture {
winit_window . recognize_pan_gesture ( true , min , max ) ;
} else {
winit_window . recognize_pan_gesture ( false , 0 , 0 ) ;
}
}
2024-01-28 21:09:23 +00:00
window_created_events . send ( WindowCreated { window : entity } ) ;
2023-01-19 00:38:28 +00:00
}
}
2024-08-06 10:54:37 +00:00
/// Synchronize available monitors as reported by [`winit`] with [`Monitor`] entities in the world.
pub fn create_monitors (
event_loop : & ActiveEventLoop ,
( mut commands , mut monitors ) : SystemParamItem < CreateMonitorParams > ,
) {
let primary_monitor = event_loop . primary_monitor ( ) ;
let mut seen_monitors = vec! [ false ; monitors . monitors . len ( ) ] ;
' outer : for monitor in event_loop . available_monitors ( ) {
for ( idx , ( m , _ ) ) in monitors . monitors . iter ( ) . enumerate ( ) {
if & monitor = = m {
seen_monitors [ idx ] = true ;
continue 'outer ;
}
}
let size = monitor . size ( ) ;
let position = monitor . position ( ) ;
let entity = commands
. spawn ( Monitor {
name : monitor . name ( ) ,
physical_height : size . height ,
physical_width : size . width ,
physical_position : IVec2 ::new ( position . x , position . y ) ,
refresh_rate_millihertz : monitor . refresh_rate_millihertz ( ) ,
scale_factor : monitor . scale_factor ( ) ,
video_modes : monitor
. video_modes ( )
. map ( | v | {
let size = v . size ( ) ;
VideoMode {
physical_size : UVec2 ::new ( size . width , size . height ) ,
bit_depth : v . bit_depth ( ) ,
refresh_rate_millihertz : v . refresh_rate_millihertz ( ) ,
}
} )
. collect ( ) ,
} )
. id ( ) ;
if primary_monitor . as_ref ( ) = = Some ( & monitor ) {
commands . entity ( entity ) . insert ( PrimaryMonitor ) ;
}
seen_monitors . push ( true ) ;
monitors . monitors . push ( ( monitor , entity ) ) ;
}
let mut idx = 0 ;
monitors . monitors . retain ( | ( _m , entity ) | {
if seen_monitors [ idx ] {
idx + = 1 ;
true
} else {
info! ( " Monitor removed {:?} " , entity ) ;
commands . entity ( * entity ) . despawn ( ) ;
idx + = 1 ;
false
}
} ) ;
}
2024-06-08 21:42:01 +00:00
#[ allow(clippy::too_many_arguments) ]
2023-07-31 21:41:59 +00:00
pub ( crate ) fn despawn_windows (
2024-05-12 15:56:01 +00:00
closing : Query < Entity , With < ClosingWindow > > ,
2023-02-04 20:53:37 +00:00
mut closed : RemovedComponents < Window > ,
2024-06-08 21:42:01 +00:00
window_entities : Query < Entity , With < Window > > ,
2024-05-12 15:56:01 +00:00
mut closing_events : EventWriter < WindowClosing > ,
mut closed_events : EventWriter < WindowClosed > ,
2023-01-19 00:38:28 +00:00
mut winit_windows : NonSendMut < WinitWindows > ,
2024-06-05 18:13:59 +00:00
mut windows_to_drop : Local < Vec < WindowWrapper < winit ::window ::Window > > > ,
2024-06-08 21:42:01 +00:00
mut exit_events : EventReader < AppExit > ,
2023-01-19 00:38:28 +00:00
) {
2024-06-05 18:13:59 +00:00
// Drop all the windows that are waiting to be closed
windows_to_drop . clear ( ) ;
2024-05-12 15:56:01 +00:00
for window in closing . iter ( ) {
closing_events . send ( WindowClosing { window } ) ;
}
2023-09-15 12:37:20 +00:00
for window in closed . read ( ) {
2023-01-19 00:38:28 +00:00
info! ( " Closing window {:?} " , window ) ;
2023-01-31 01:47:00 +00:00
// Guard to verify that the window is in fact actually gone,
2024-05-12 15:56:01 +00:00
// rather than having the component added
// and removed in the same frame.
2023-01-31 01:47:00 +00:00
if ! window_entities . contains ( window ) {
2024-06-05 18:13:59 +00:00
if let Some ( window ) = winit_windows . remove_window ( window ) {
// Keeping WindowWrapper that are dropped for one frame
// Otherwise the last `Arc` of the window could be in the rendering thread, and dropped there
// This would hang on macOS
// Keeping the wrapper and dropping it next frame in this system ensure its dropped in the main thread
windows_to_drop . push ( window ) ;
}
2024-05-12 15:56:01 +00:00
closed_events . send ( WindowClosed { window } ) ;
2023-01-31 01:47:00 +00:00
}
2024-06-08 21:42:01 +00:00
}
// On macOS, when exiting, we need to tell the rendering thread the windows are about to
// close to ensure that they are dropped on the main thread. Otherwise, the app will hang.
if ! exit_events . is_empty ( ) {
exit_events . clear ( ) ;
for window in window_entities . iter ( ) {
closing_events . send ( WindowClosing { window } ) ;
}
2023-01-19 00:38:28 +00:00
}
}
2023-02-07 14:18:13 +00:00
/// The cached state of the window so we can check which properties were changed from within the app.
2023-01-19 00:38:28 +00:00
#[ derive(Debug, Clone, Component) ]
2023-02-07 14:18:13 +00:00
pub struct CachedWindow {
pub window : Window ,
2023-01-19 00:38:28 +00:00
}
2023-07-31 21:41:59 +00:00
/// Propagates changes from [`Window`] entities to the [`winit`] backend.
///
/// # Notes
///
/// - [`Window::present_mode`] and [`Window::composite_alpha_mode`] changes are handled by the `bevy_render` crate.
/// - [`Window::transparent`] cannot be changed after the window is created.
/// - [`Window::canvas`] cannot be changed after the window is created.
/// - [`Window::focused`] cannot be manually changed to `false` after the window is created.
pub ( crate ) fn changed_windows (
2023-02-07 14:18:13 +00:00
mut changed_windows : Query < ( Entity , & mut Window , & mut CachedWindow ) , Changed < Window > > ,
2023-01-19 00:38:28 +00:00
winit_windows : NonSendMut < WinitWindows > ,
2024-08-06 10:54:37 +00:00
monitors : Res < WinitMonitors > ,
2024-01-28 21:09:23 +00:00
mut window_resized : EventWriter < WindowResized > ,
2023-01-19 00:38:28 +00:00
) {
2023-02-07 14:18:13 +00:00
for ( entity , mut window , mut cache ) in & mut changed_windows {
2024-02-10 20:17:04 +00:00
let Some ( winit_window ) = winit_windows . get_window ( entity ) else {
continue ;
} ;
2023-01-19 00:38:28 +00:00
2024-02-10 20:17:04 +00:00
if window . title ! = cache . window . title {
winit_window . set_title ( window . title . as_str ( ) ) ;
}
if window . mode ! = cache . window . mode {
let new_mode = match window . mode {
2024-08-06 10:54:37 +00:00
WindowMode ::BorderlessFullscreen ( monitor_selection ) = > {
Some ( Some ( winit ::window ::Fullscreen ::Borderless ( select_monitor (
& monitors ,
winit_window . primary_monitor ( ) ,
winit_window . current_monitor ( ) ,
& monitor_selection ,
) ) ) )
2024-02-10 20:17:04 +00:00
}
2024-08-06 10:54:37 +00:00
mode @ ( WindowMode ::Fullscreen ( _ ) | WindowMode ::SizedFullscreen ( _ ) ) = > {
let videomode = match mode {
WindowMode ::Fullscreen ( monitor_selection ) = > get_best_videomode (
& select_monitor (
& monitors ,
winit_window . primary_monitor ( ) ,
winit_window . current_monitor ( ) ,
& monitor_selection ,
)
. unwrap_or_else ( | | {
panic! ( " Could not find monitor for {:?} " , monitor_selection )
} ) ,
) ,
WindowMode ::SizedFullscreen ( monitor_selection ) = > get_fitting_videomode (
& select_monitor (
& monitors ,
winit_window . primary_monitor ( ) ,
winit_window . current_monitor ( ) ,
& monitor_selection ,
)
. unwrap_or_else ( | | {
panic! ( " Could not find monitor for {:?} " , monitor_selection )
} ) ,
window . width ( ) as u32 ,
window . height ( ) as u32 ,
) ,
_ = > unreachable! ( ) ,
} ;
Some ( Some ( winit ::window ::Fullscreen ::Exclusive ( videomode ) ) )
2024-02-10 20:17:04 +00:00
}
WindowMode ::Windowed = > Some ( None ) ,
} ;
2023-01-19 00:38:28 +00:00
2024-02-10 20:17:04 +00:00
if let Some ( new_mode ) = new_mode {
2023-01-19 00:38:28 +00:00
if winit_window . fullscreen ( ) ! = new_mode {
winit_window . set_fullscreen ( new_mode ) ;
}
}
2024-02-10 20:17:04 +00:00
}
fix: upgrade to winit v0.30 (#13366)
# Objective
- Upgrade winit to v0.30
- Fixes https://github.com/bevyengine/bevy/issues/13331
## Solution
This is a rewrite/adaptation of the new trait system described and
implemented in `winit` v0.30.
## Migration Guide
The custom UserEvent is now renamed as WakeUp, used to wake up the loop
if anything happens outside the app (a new
[custom_user_event](https://github.com/bevyengine/bevy/pull/13366/files#diff-2de8c0a8d3028d0059a3d80ae31b2bbc1cde2595ce2d317ea378fe3e0cf6ef2d)
shows this behavior.
The internal `UpdateState` has been removed and replaced internally by
the AppLifecycle. When changed, the AppLifecycle is sent as an event.
The `UpdateMode` now accepts only two values: `Continuous` and
`Reactive`, but the latter exposes 3 new properties to enable reactive
to device, user or window events. The previous `UpdateMode::Reactive` is
now equivalent to `UpdateMode::reactive()`, while
`UpdateMode::ReactiveLowPower` to `UpdateMode::reactive_low_power()`.
The `ApplicationLifecycle` has been renamed as `AppLifecycle`, and now
contains the possible values of the application state inside the event
loop:
* `Idle`: the loop has not started yet
* `Running` (previously called `Started`): the loop is running
* `WillSuspend`: the loop is going to be suspended
* `Suspended`: the loop is suspended
* `WillResume`: the loop is going to be resumed
Note: the `Resumed` state has been removed since the resumed app is just
running.
Finally, now that `winit` enables this, it extends the `WinitPlugin` to
support custom events.
## Test platforms
- [x] Windows
- [x] MacOs
- [x] Linux (x11)
- [x] Linux (Wayland)
- [x] Android
- [x] iOS
- [x] WASM/WebGPU
- [x] WASM/WebGL2
## Outstanding issues / regressions
- [ ] iOS: build failed in CI
- blocking, but may just be flakiness
- [x] Cross-platform: when the window is maximised, changes in the scale
factor don't apply, to make them apply one has to make the window
smaller again. (Re-maximising keeps the updated scale factor)
- non-blocking, but good to fix
- [ ] Android: it's pretty easy to quickly open and close the app and
then the music keeps playing when suspended.
- non-blocking but worrying
- [ ] Web: the application will hang when switching tabs
- Not new, duplicate of https://github.com/bevyengine/bevy/issues/13486
- [ ] Cross-platform?: Screenshot failure, `ERROR present_frames:
wgpu_core::present: No work has been submitted for this frame before`
taking the first screenshot, but after pressing space
- non-blocking, but good to fix
---------
Co-authored-by: François <francois.mockers@vleue.com>
2024-06-03 13:06:48 +00:00
2024-02-10 20:17:04 +00:00
if window . resolution ! = cache . window . resolution {
2024-06-20 11:22:47 +00:00
let mut physical_size = PhysicalSize ::new (
window . resolution . physical_width ( ) ,
window . resolution . physical_height ( ) ,
) ;
fix: upgrade to winit v0.30 (#13366)
# Objective
- Upgrade winit to v0.30
- Fixes https://github.com/bevyengine/bevy/issues/13331
## Solution
This is a rewrite/adaptation of the new trait system described and
implemented in `winit` v0.30.
## Migration Guide
The custom UserEvent is now renamed as WakeUp, used to wake up the loop
if anything happens outside the app (a new
[custom_user_event](https://github.com/bevyengine/bevy/pull/13366/files#diff-2de8c0a8d3028d0059a3d80ae31b2bbc1cde2595ce2d317ea378fe3e0cf6ef2d)
shows this behavior.
The internal `UpdateState` has been removed and replaced internally by
the AppLifecycle. When changed, the AppLifecycle is sent as an event.
The `UpdateMode` now accepts only two values: `Continuous` and
`Reactive`, but the latter exposes 3 new properties to enable reactive
to device, user or window events. The previous `UpdateMode::Reactive` is
now equivalent to `UpdateMode::reactive()`, while
`UpdateMode::ReactiveLowPower` to `UpdateMode::reactive_low_power()`.
The `ApplicationLifecycle` has been renamed as `AppLifecycle`, and now
contains the possible values of the application state inside the event
loop:
* `Idle`: the loop has not started yet
* `Running` (previously called `Started`): the loop is running
* `WillSuspend`: the loop is going to be suspended
* `Suspended`: the loop is suspended
* `WillResume`: the loop is going to be resumed
Note: the `Resumed` state has been removed since the resumed app is just
running.
Finally, now that `winit` enables this, it extends the `WinitPlugin` to
support custom events.
## Test platforms
- [x] Windows
- [x] MacOs
- [x] Linux (x11)
- [x] Linux (Wayland)
- [x] Android
- [x] iOS
- [x] WASM/WebGPU
- [x] WASM/WebGL2
## Outstanding issues / regressions
- [ ] iOS: build failed in CI
- blocking, but may just be flakiness
- [x] Cross-platform: when the window is maximised, changes in the scale
factor don't apply, to make them apply one has to make the window
smaller again. (Re-maximising keeps the updated scale factor)
- non-blocking, but good to fix
- [ ] Android: it's pretty easy to quickly open and close the app and
then the music keeps playing when suspended.
- non-blocking but worrying
- [ ] Web: the application will hang when switching tabs
- Not new, duplicate of https://github.com/bevyengine/bevy/issues/13486
- [ ] Cross-platform?: Screenshot failure, `ERROR present_frames:
wgpu_core::present: No work has been submitted for this frame before`
taking the first screenshot, but after pressing space
- non-blocking, but good to fix
---------
Co-authored-by: François <francois.mockers@vleue.com>
2024-06-03 13:06:48 +00:00
let cached_physical_size = PhysicalSize ::new (
cache . window . physical_width ( ) ,
cache . window . physical_height ( ) ,
2024-02-10 20:17:04 +00:00
) ;
fix: upgrade to winit v0.30 (#13366)
# Objective
- Upgrade winit to v0.30
- Fixes https://github.com/bevyengine/bevy/issues/13331
## Solution
This is a rewrite/adaptation of the new trait system described and
implemented in `winit` v0.30.
## Migration Guide
The custom UserEvent is now renamed as WakeUp, used to wake up the loop
if anything happens outside the app (a new
[custom_user_event](https://github.com/bevyengine/bevy/pull/13366/files#diff-2de8c0a8d3028d0059a3d80ae31b2bbc1cde2595ce2d317ea378fe3e0cf6ef2d)
shows this behavior.
The internal `UpdateState` has been removed and replaced internally by
the AppLifecycle. When changed, the AppLifecycle is sent as an event.
The `UpdateMode` now accepts only two values: `Continuous` and
`Reactive`, but the latter exposes 3 new properties to enable reactive
to device, user or window events. The previous `UpdateMode::Reactive` is
now equivalent to `UpdateMode::reactive()`, while
`UpdateMode::ReactiveLowPower` to `UpdateMode::reactive_low_power()`.
The `ApplicationLifecycle` has been renamed as `AppLifecycle`, and now
contains the possible values of the application state inside the event
loop:
* `Idle`: the loop has not started yet
* `Running` (previously called `Started`): the loop is running
* `WillSuspend`: the loop is going to be suspended
* `Suspended`: the loop is suspended
* `WillResume`: the loop is going to be resumed
Note: the `Resumed` state has been removed since the resumed app is just
running.
Finally, now that `winit` enables this, it extends the `WinitPlugin` to
support custom events.
## Test platforms
- [x] Windows
- [x] MacOs
- [x] Linux (x11)
- [x] Linux (Wayland)
- [x] Android
- [x] iOS
- [x] WASM/WebGPU
- [x] WASM/WebGL2
## Outstanding issues / regressions
- [ ] iOS: build failed in CI
- blocking, but may just be flakiness
- [x] Cross-platform: when the window is maximised, changes in the scale
factor don't apply, to make them apply one has to make the window
smaller again. (Re-maximising keeps the updated scale factor)
- non-blocking, but good to fix
- [ ] Android: it's pretty easy to quickly open and close the app and
then the music keeps playing when suspended.
- non-blocking but worrying
- [ ] Web: the application will hang when switching tabs
- Not new, duplicate of https://github.com/bevyengine/bevy/issues/13486
- [ ] Cross-platform?: Screenshot failure, `ERROR present_frames:
wgpu_core::present: No work has been submitted for this frame before`
taking the first screenshot, but after pressing space
- non-blocking, but good to fix
---------
Co-authored-by: François <francois.mockers@vleue.com>
2024-06-03 13:06:48 +00:00
let base_scale_factor = window . resolution . base_scale_factor ( ) ;
// Note: this may be different from `winit`'s base scale factor if
// `scale_factor_override` is set to Some(f32)
let scale_factor = window . scale_factor ( ) ;
let cached_scale_factor = cache . window . scale_factor ( ) ;
// Check and update `winit`'s physical size only if the window is not maximized
if scale_factor ! = cached_scale_factor & & ! winit_window . is_maximized ( ) {
let logical_size =
if let Some ( cached_factor ) = cache . window . resolution . scale_factor_override ( ) {
physical_size . to_logical ::< f32 > ( cached_factor as f64 )
} else {
physical_size . to_logical ::< f32 > ( base_scale_factor as f64 )
} ;
// Scale factor changed, updating physical and logical size
if let Some ( forced_factor ) = window . resolution . scale_factor_override ( ) {
// This window is overriding the OS-suggested DPI, so its physical size
// should be set based on the overriding value. Its logical size already
// incorporates any resize constraints.
physical_size = logical_size . to_physical ::< u32 > ( forced_factor as f64 ) ;
} else {
physical_size = logical_size . to_physical ::< u32 > ( base_scale_factor as f64 ) ;
}
}
if physical_size ! = cached_physical_size {
if let Some ( new_physical_size ) = winit_window . request_inner_size ( physical_size ) {
react_to_resize ( entity , & mut window , new_physical_size , & mut window_resized ) ;
}
2023-01-19 00:38:28 +00:00
}
2024-02-10 20:17:04 +00:00
}
2023-01-19 00:38:28 +00:00
2024-02-10 20:17:04 +00:00
if window . physical_cursor_position ( ) ! = cache . window . physical_cursor_position ( ) {
if let Some ( physical_position ) = window . physical_cursor_position ( ) {
let position = PhysicalPosition ::new ( physical_position . x , physical_position . y ) ;
2023-01-19 00:38:28 +00:00
2024-02-10 20:17:04 +00:00
if let Err ( err ) = winit_window . set_cursor_position ( position ) {
error! ( " could not set cursor position: {:?} " , err ) ;
2023-01-19 00:38:28 +00:00
}
}
2024-02-10 20:17:04 +00:00
}
2023-01-19 00:38:28 +00:00
2024-08-12 15:49:03 +00:00
if window . cursor_options . grab_mode ! = cache . window . cursor_options . grab_mode {
crate ::winit_windows ::attempt_grab ( winit_window , window . cursor_options . grab_mode ) ;
2024-02-10 20:17:04 +00:00
}
2023-01-19 00:38:28 +00:00
2024-08-12 15:49:03 +00:00
if window . cursor_options . visible ! = cache . window . cursor_options . visible {
winit_window . set_cursor_visible ( window . cursor_options . visible ) ;
2024-02-10 20:17:04 +00:00
}
2023-01-19 00:38:28 +00:00
2024-08-12 15:49:03 +00:00
if window . cursor_options . hit_test ! = cache . window . cursor_options . hit_test {
if let Err ( err ) = winit_window . set_cursor_hittest ( window . cursor_options . hit_test ) {
window . cursor_options . hit_test = cache . window . cursor_options . hit_test ;
2024-02-10 20:17:04 +00:00
warn! (
" Could not set cursor hit test for window {:?}: {:?} " ,
window . title , err
) ;
2023-01-19 00:38:28 +00:00
}
2024-02-10 20:17:04 +00:00
}
2023-01-19 00:38:28 +00:00
2024-02-10 20:17:04 +00:00
if window . decorations ! = cache . window . decorations
& & window . decorations ! = winit_window . is_decorated ( )
{
winit_window . set_decorations ( window . decorations ) ;
}
2023-01-19 00:38:28 +00:00
2024-02-10 20:17:04 +00:00
if window . resizable ! = cache . window . resizable
& & window . resizable ! = winit_window . is_resizable ( )
{
winit_window . set_resizable ( window . resizable ) ;
}
if window . enabled_buttons ! = cache . window . enabled_buttons {
winit_window . set_enabled_buttons ( convert_enabled_buttons ( window . enabled_buttons ) ) ;
}
2023-01-19 00:38:28 +00:00
2024-02-10 20:17:04 +00:00
if window . resize_constraints ! = cache . window . resize_constraints {
let constraints = window . resize_constraints . check_constraints ( ) ;
let min_inner_size = LogicalSize {
width : constraints . min_width ,
height : constraints . min_height ,
} ;
let max_inner_size = LogicalSize {
width : constraints . max_width ,
height : constraints . max_height ,
} ;
winit_window . set_min_inner_size ( Some ( min_inner_size ) ) ;
if constraints . max_width . is_finite ( ) & & constraints . max_height . is_finite ( ) {
winit_window . set_max_inner_size ( Some ( max_inner_size ) ) ;
2023-07-23 01:02:40 +00:00
}
2024-02-10 20:17:04 +00:00
}
2023-07-23 01:02:40 +00:00
2024-02-10 20:17:04 +00:00
if window . position ! = cache . window . position {
if let Some ( position ) = crate ::winit_window_position (
& window . position ,
& window . resolution ,
2024-08-06 10:54:37 +00:00
& monitors ,
2024-02-10 20:17:04 +00:00
winit_window . primary_monitor ( ) ,
winit_window . current_monitor ( ) ,
) {
let should_set = match winit_window . outer_position ( ) {
Ok ( current_position ) = > current_position ! = position ,
_ = > true ,
2023-01-19 00:38:28 +00:00
} ;
2024-02-10 20:17:04 +00:00
if should_set {
winit_window . set_outer_position ( position ) ;
2023-01-19 00:38:28 +00:00
}
}
2024-02-10 20:17:04 +00:00
}
2023-01-19 00:38:28 +00:00
2024-02-10 20:17:04 +00:00
if let Some ( maximized ) = window . internal . take_maximize_request ( ) {
winit_window . set_maximized ( maximized ) ;
}
2023-01-19 00:38:28 +00:00
2024-02-10 20:17:04 +00:00
if let Some ( minimized ) = window . internal . take_minimize_request ( ) {
winit_window . set_minimized ( minimized ) ;
}
2023-01-19 00:38:28 +00:00
2024-02-10 20:17:04 +00:00
if window . focused ! = cache . window . focused & & window . focused {
winit_window . focus_window ( ) ;
}
2023-01-19 00:38:28 +00:00
2024-02-10 20:17:04 +00:00
if window . window_level ! = cache . window . window_level {
winit_window . set_window_level ( convert_window_level ( window . window_level ) ) ;
}
2023-01-19 00:38:28 +00:00
2024-02-10 20:17:04 +00:00
// Currently unsupported changes
if window . transparent ! = cache . window . transparent {
window . transparent = cache . window . transparent ;
warn! ( " Winit does not currently support updating transparency after window creation. " ) ;
}
2023-01-19 00:38:28 +00:00
2024-02-10 20:17:04 +00:00
#[ cfg(target_arch = " wasm32 " ) ]
if window . canvas ! = cache . window . canvas {
2024-04-20 09:15:42 +00:00
window . canvas . clone_from ( & cache . window . canvas ) ;
2024-02-10 20:17:04 +00:00
warn! (
" Bevy currently doesn't support modifying the window canvas after initialization. "
) ;
}
2023-01-29 20:27:29 +00:00
2024-02-10 20:17:04 +00:00
if window . ime_enabled ! = cache . window . ime_enabled {
winit_window . set_ime_allowed ( window . ime_enabled ) ;
}
2023-01-29 20:27:29 +00:00
2024-02-10 20:17:04 +00:00
if window . ime_position ! = cache . window . ime_position {
winit_window . set_ime_cursor_area (
LogicalPosition ::new ( window . ime_position . x , window . ime_position . y ) ,
PhysicalSize ::new ( 10 , 10 ) ,
) ;
}
2023-06-05 21:04:22 +00:00
2024-02-10 20:17:04 +00:00
if window . window_theme ! = cache . window . window_theme {
winit_window . set_theme ( window . window_theme . map ( convert_window_theme ) ) ;
}
2023-08-20 22:42:07 +00:00
2024-02-10 20:17:04 +00:00
if window . visible ! = cache . window . visible {
winit_window . set_visible ( window . visible ) ;
2023-01-19 00:38:28 +00:00
}
2024-02-10 20:17:04 +00:00
2024-06-04 12:44:25 +00:00
#[ cfg(target_os = " ios " ) ]
{
if window . recognize_pinch_gesture ! = cache . window . recognize_pinch_gesture {
winit_window . recognize_pinch_gesture ( window . recognize_pinch_gesture ) ;
}
if window . recognize_rotation_gesture ! = cache . window . recognize_rotation_gesture {
winit_window . recognize_rotation_gesture ( window . recognize_rotation_gesture ) ;
}
if window . recognize_doubletap_gesture ! = cache . window . recognize_doubletap_gesture {
winit_window . recognize_doubletap_gesture ( window . recognize_doubletap_gesture ) ;
}
if window . recognize_pan_gesture ! = cache . window . recognize_pan_gesture {
match (
window . recognize_pan_gesture ,
cache . window . recognize_pan_gesture ,
) {
( Some ( _ ) , Some ( _ ) ) = > {
warn! ( " Bevy currently doesn't support modifying PanGesture number of fingers recognition. Please disable it before re-enabling it with the new number of fingers " ) ;
}
( Some ( ( min , max ) ) , _ ) = > winit_window . recognize_pan_gesture ( true , min , max ) ,
_ = > winit_window . recognize_pan_gesture ( false , 0 , 0 ) ,
}
}
}
2024-02-10 20:17:04 +00:00
cache . window = window . clone ( ) ;
2023-01-19 00:38:28 +00:00
}
}