Remove remaining internal use of !Send
resources (#18386)
# Objective Remaining work for and closes #17682. First half of work for that issue was completed in [PR 17730](https://github.com/bevyengine/bevy/pull/17730). However, the rest of the work ended up getting blocked because we needed a way of forcing systems to run on the main thread without the use of `!Send` resources. That was unblocked by [PR 18301](https://github.com/bevyengine/bevy/pull/18301). This work should finish unblocking the resources-as-components effort. # Testing Ran several examples using my Linux machine, just to make sure things are working as expected and no surprises pop up. --------- Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com> Co-authored-by: François Mockers <francois.mockers@vleue.com>
This commit is contained in:
parent
7b1c9f192e
commit
770f10bc19
@ -117,3 +117,22 @@ impl Plugin for GilrsPlugin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// Regression test for https://github.com/bevyengine/bevy/issues/17697
|
||||
#[test]
|
||||
fn world_is_truly_send() {
|
||||
let mut app = App::new();
|
||||
app.add_plugins(GilrsPlugin);
|
||||
let world = core::mem::take(app.world_mut());
|
||||
|
||||
let handler = std::thread::spawn(move || {
|
||||
drop(world);
|
||||
});
|
||||
|
||||
handler.join().unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use alloc::{collections::VecDeque, sync::Arc};
|
||||
use bevy_input_focus::InputFocus;
|
||||
use core::cell::RefCell;
|
||||
use std::sync::Mutex;
|
||||
use winit::event_loop::ActiveEventLoop;
|
||||
|
||||
@ -16,13 +17,26 @@ use bevy_a11y::{
|
||||
};
|
||||
use bevy_app::{App, Plugin, PostUpdate};
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::{entity::EntityHashMap, prelude::*};
|
||||
use bevy_ecs::{entity::EntityHashMap, prelude::*, system::NonSendMarker};
|
||||
use bevy_window::{PrimaryWindow, Window, WindowClosed};
|
||||
|
||||
thread_local! {
|
||||
/// Temporary storage of access kit adapter data to replace usage of `!Send` resources. This will be replaced with proper
|
||||
/// storage of `!Send` data after issue #17667 is complete.
|
||||
pub static ACCESS_KIT_ADAPTERS: RefCell<AccessKitAdapters> = const { RefCell::new(AccessKitAdapters::new()) };
|
||||
}
|
||||
|
||||
/// Maps window entities to their `AccessKit` [`Adapter`]s.
|
||||
#[derive(Default, Deref, DerefMut)]
|
||||
pub struct AccessKitAdapters(pub EntityHashMap<Adapter>);
|
||||
|
||||
impl AccessKitAdapters {
|
||||
/// Creates a new empty `AccessKitAdapters`.
|
||||
pub const fn new() -> Self {
|
||||
Self(EntityHashMap::new())
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps window entities to their respective [`ActionRequest`]s.
|
||||
#[derive(Resource, Default, Deref, DerefMut)]
|
||||
pub struct WinitActionRequestHandlers(pub EntityHashMap<Arc<Mutex<WinitActionRequestHandler>>>);
|
||||
@ -144,14 +158,16 @@ pub(crate) fn prepare_accessibility_for_window(
|
||||
}
|
||||
|
||||
fn window_closed(
|
||||
mut adapters: NonSendMut<AccessKitAdapters>,
|
||||
mut handlers: ResMut<WinitActionRequestHandlers>,
|
||||
mut events: EventReader<WindowClosed>,
|
||||
_non_send_marker: NonSendMarker,
|
||||
) {
|
||||
for WindowClosed { window, .. } in events.read() {
|
||||
adapters.remove(window);
|
||||
handlers.remove(window);
|
||||
}
|
||||
ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| {
|
||||
for WindowClosed { window, .. } in events.read() {
|
||||
adapters.remove(window);
|
||||
handlers.remove(window);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn poll_receivers(
|
||||
@ -174,7 +190,6 @@ fn should_update_accessibility_nodes(
|
||||
}
|
||||
|
||||
fn update_accessibility_nodes(
|
||||
mut adapters: NonSendMut<AccessKitAdapters>,
|
||||
focus: Option<Res<InputFocus>>,
|
||||
primary_window: Query<(Entity, &Window), With<PrimaryWindow>>,
|
||||
nodes: Query<(
|
||||
@ -184,35 +199,38 @@ fn update_accessibility_nodes(
|
||||
Option<&ChildOf>,
|
||||
)>,
|
||||
node_entities: Query<Entity, With<AccessibilityNode>>,
|
||||
_non_send_marker: NonSendMarker,
|
||||
) {
|
||||
let Ok((primary_window_id, primary_window)) = primary_window.single() else {
|
||||
return;
|
||||
};
|
||||
let Some(adapter) = adapters.get_mut(&primary_window_id) else {
|
||||
return;
|
||||
};
|
||||
let Some(focus) = focus else {
|
||||
return;
|
||||
};
|
||||
if focus.is_changed() || !nodes.is_empty() {
|
||||
// Don't panic if the focused entity does not currently exist
|
||||
// It's probably waiting to be spawned
|
||||
if let Some(focused_entity) = focus.0 {
|
||||
if !node_entities.contains(focused_entity) {
|
||||
return;
|
||||
ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| {
|
||||
let Ok((primary_window_id, primary_window)) = primary_window.single() else {
|
||||
return;
|
||||
};
|
||||
let Some(adapter) = adapters.get_mut(&primary_window_id) else {
|
||||
return;
|
||||
};
|
||||
let Some(focus) = focus else {
|
||||
return;
|
||||
};
|
||||
if focus.is_changed() || !nodes.is_empty() {
|
||||
// Don't panic if the focused entity does not currently exist
|
||||
// It's probably waiting to be spawned
|
||||
if let Some(focused_entity) = focus.0 {
|
||||
if !node_entities.contains(focused_entity) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
adapter.update_if_active(|| {
|
||||
update_adapter(
|
||||
nodes,
|
||||
node_entities,
|
||||
primary_window,
|
||||
primary_window_id,
|
||||
focus,
|
||||
)
|
||||
});
|
||||
}
|
||||
adapter.update_if_active(|| {
|
||||
update_adapter(
|
||||
nodes,
|
||||
node_entities,
|
||||
primary_window,
|
||||
primary_window_id,
|
||||
focus,
|
||||
)
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn update_adapter(
|
||||
@ -290,8 +308,7 @@ pub struct AccessKitPlugin;
|
||||
|
||||
impl Plugin for AccessKitPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_non_send_resource::<AccessKitAdapters>()
|
||||
.init_resource::<WinitActionRequestHandlers>()
|
||||
app.init_resource::<WinitActionRequestHandlers>()
|
||||
.add_event::<ActionRequestWrapper>()
|
||||
.add_systems(
|
||||
PostUpdate,
|
||||
|
@ -18,6 +18,7 @@ use bevy_derive::Deref;
|
||||
use bevy_reflect::prelude::ReflectDefault;
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_window::{RawHandleWrapperHolder, WindowEvent};
|
||||
use core::cell::RefCell;
|
||||
use core::marker::PhantomData;
|
||||
use winit::{event_loop::EventLoop, window::WindowId};
|
||||
|
||||
@ -37,7 +38,7 @@ pub use winit_config::*;
|
||||
pub use winit_windows::*;
|
||||
|
||||
use crate::{
|
||||
accessibility::{AccessKitAdapters, AccessKitPlugin, WinitActionRequestHandlers},
|
||||
accessibility::{AccessKitPlugin, WinitActionRequestHandlers},
|
||||
state::winit_runner,
|
||||
winit_monitors::WinitMonitors,
|
||||
};
|
||||
@ -53,6 +54,10 @@ mod winit_config;
|
||||
mod winit_monitors;
|
||||
mod winit_windows;
|
||||
|
||||
thread_local! {
|
||||
static WINIT_WINDOWS: RefCell<WinitWindows> = const { RefCell::new(WinitWindows::new()) };
|
||||
}
|
||||
|
||||
/// A [`Plugin`] that uses `winit` to create and manage windows, and receive window and input
|
||||
/// events.
|
||||
///
|
||||
@ -124,8 +129,7 @@ impl<T: Event> Plugin for WinitPlugin<T> {
|
||||
.build()
|
||||
.expect("Failed to build event loop");
|
||||
|
||||
app.init_non_send_resource::<WinitWindows>()
|
||||
.init_resource::<WinitMonitors>()
|
||||
app.init_resource::<WinitMonitors>()
|
||||
.init_resource::<WinitSettings>()
|
||||
.insert_resource(DisplayHandleWrapper(event_loop.owned_display_handle()))
|
||||
.add_event::<RawWinitWindowEvent>()
|
||||
@ -210,8 +214,6 @@ pub type CreateWindowParams<'w, 's, F = ()> = (
|
||||
F,
|
||||
>,
|
||||
EventWriter<'w, WindowCreated>,
|
||||
NonSendMut<'w, WinitWindows>,
|
||||
NonSendMut<'w, AccessKitAdapters>,
|
||||
ResMut<'w, WinitActionRequestHandlers>,
|
||||
Res<'w, AccessibilityRequested>,
|
||||
Res<'w, WinitMonitors>,
|
||||
|
@ -3,7 +3,7 @@ use bevy_app::{App, AppExit, PluginsState};
|
||||
#[cfg(feature = "custom_cursor")]
|
||||
use bevy_asset::AssetId;
|
||||
use bevy_ecs::{
|
||||
change_detection::{DetectChanges, NonSendMut, Res},
|
||||
change_detection::{DetectChanges, Res},
|
||||
entity::Entity,
|
||||
event::{EventCursor, EventWriter},
|
||||
prelude::*,
|
||||
@ -49,11 +49,11 @@ use bevy_window::{
|
||||
use bevy_window::{PrimaryWindow, RawHandleWrapper};
|
||||
|
||||
use crate::{
|
||||
accessibility::AccessKitAdapters,
|
||||
accessibility::ACCESS_KIT_ADAPTERS,
|
||||
converters, create_windows,
|
||||
system::{create_monitors, CachedWindow, WinitWindowPressedKeys},
|
||||
AppSendEvent, CreateMonitorParams, CreateWindowParams, EventLoopProxyWrapper,
|
||||
RawWinitWindowEvent, UpdateMode, WinitSettings, WinitWindows,
|
||||
RawWinitWindowEvent, UpdateMode, WinitSettings, WINIT_WINDOWS,
|
||||
};
|
||||
|
||||
/// Persistent state that is used to run the [`App`] according to the current
|
||||
@ -94,7 +94,6 @@ struct WinitAppRunnerState<T: Event> {
|
||||
EventWriter<'static, WindowResized>,
|
||||
EventWriter<'static, WindowBackendScaleFactorChanged>,
|
||||
EventWriter<'static, WindowScaleFactorChanged>,
|
||||
NonSend<'static, WinitWindows>,
|
||||
Query<
|
||||
'static,
|
||||
'static,
|
||||
@ -104,7 +103,6 @@ struct WinitAppRunnerState<T: Event> {
|
||||
&'static mut WinitWindowPressedKeys,
|
||||
),
|
||||
>,
|
||||
NonSendMut<'static, AccessKitAdapters>,
|
||||
)>,
|
||||
}
|
||||
|
||||
@ -117,9 +115,7 @@ impl<T: Event> WinitAppRunnerState<T> {
|
||||
EventWriter<WindowResized>,
|
||||
EventWriter<WindowBackendScaleFactorChanged>,
|
||||
EventWriter<WindowScaleFactorChanged>,
|
||||
NonSend<WinitWindows>,
|
||||
Query<(&mut Window, &mut CachedWindow, &mut WinitWindowPressedKeys)>,
|
||||
NonSendMut<AccessKitAdapters>,
|
||||
)> = SystemState::new(app.world_mut());
|
||||
|
||||
Self {
|
||||
@ -255,219 +251,236 @@ impl<T: Event> ApplicationHandler<T> for WinitAppRunnerState<T> {
|
||||
) {
|
||||
self.window_event_received = true;
|
||||
|
||||
let (
|
||||
mut window_resized,
|
||||
mut window_backend_scale_factor_changed,
|
||||
mut window_scale_factor_changed,
|
||||
winit_windows,
|
||||
mut windows,
|
||||
mut access_kit_adapters,
|
||||
) = self.event_writer_system_state.get_mut(self.app.world_mut());
|
||||
#[cfg_attr(
|
||||
not(target_os = "windows"),
|
||||
expect(unused_mut, reason = "only needs to be mut on windows for now")
|
||||
)]
|
||||
let mut manual_run_redraw_requested = false;
|
||||
|
||||
let Some(window) = winit_windows.get_window_entity(window_id) else {
|
||||
warn!("Skipped event {event:?} for unknown winit Window Id {window_id:?}");
|
||||
return;
|
||||
};
|
||||
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.event_writer_system_state.get_mut(self.app.world_mut());
|
||||
|
||||
let Ok((mut win, _, mut pressed_keys)) = windows.get_mut(window) else {
|
||||
warn!("Window {window:?} is missing `Window` component, skipping event {event:?}");
|
||||
return;
|
||||
};
|
||||
let Some(window) = winit_windows.get_window_entity(window_id) else {
|
||||
warn!("Skipped event {event:?} for unknown winit Window Id {window_id:?}");
|
||||
return;
|
||||
};
|
||||
|
||||
// Store a copy of the event to send to an EventWriter later.
|
||||
self.raw_winit_events.push(RawWinitWindowEvent {
|
||||
window_id,
|
||||
event: event.clone(),
|
||||
});
|
||||
let Ok((mut win, _, mut pressed_keys)) = windows.get_mut(window) else {
|
||||
warn!(
|
||||
"Window {window:?} is missing `Window` component, skipping event {event:?}"
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
// Allow AccessKit to respond to `WindowEvent`s before they reach
|
||||
// the engine.
|
||||
if let Some(adapter) = access_kit_adapters.get_mut(&window) {
|
||||
if let Some(winit_window) = winit_windows.get_window(window) {
|
||||
adapter.process_event(winit_window, &event);
|
||||
}
|
||||
}
|
||||
|
||||
match event {
|
||||
WindowEvent::Resized(size) => {
|
||||
react_to_resize(window, &mut win, size, &mut window_resized);
|
||||
}
|
||||
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,
|
||||
);
|
||||
}
|
||||
WindowEvent::CloseRequested => self
|
||||
.bevy_window_events
|
||||
.send(WindowCloseRequested { window }),
|
||||
WindowEvent::KeyboardInput {
|
||||
ref event,
|
||||
// On some platforms, winit sends "synthetic" key press events when the window
|
||||
// gains or loses focus. These are not implemented on every platform, so we ignore
|
||||
// winit's synthetic key pressed and implement the same mechanism ourselves.
|
||||
// (See the `WinitWindowPressedKeys` component)
|
||||
is_synthetic: false,
|
||||
..
|
||||
} => {
|
||||
let keyboard_input = converters::convert_keyboard_input(event, window);
|
||||
if event.state.is_pressed() {
|
||||
pressed_keys
|
||||
.0
|
||||
.insert(keyboard_input.key_code, keyboard_input.logical_key.clone());
|
||||
} else {
|
||||
pressed_keys.0.remove(&keyboard_input.key_code);
|
||||
}
|
||||
self.bevy_window_events.send(keyboard_input);
|
||||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let physical_position = DVec2::new(position.x, position.y);
|
||||
|
||||
let last_position = win.physical_cursor_position();
|
||||
let delta = last_position.map(|last_pos| {
|
||||
(physical_position.as_vec2() - last_pos) / win.resolution.scale_factor()
|
||||
// Store a copy of the event to send to an EventWriter later.
|
||||
self.raw_winit_events.push(RawWinitWindowEvent {
|
||||
window_id,
|
||||
event: event.clone(),
|
||||
});
|
||||
|
||||
win.set_physical_cursor_position(Some(physical_position));
|
||||
let position = (physical_position / win.resolution.scale_factor() as f64).as_vec2();
|
||||
self.bevy_window_events.send(CursorMoved {
|
||||
window,
|
||||
position,
|
||||
delta,
|
||||
});
|
||||
}
|
||||
WindowEvent::CursorEntered { .. } => {
|
||||
self.bevy_window_events.send(CursorEntered { window });
|
||||
}
|
||||
WindowEvent::CursorLeft { .. } => {
|
||||
win.set_physical_cursor_position(None);
|
||||
self.bevy_window_events.send(CursorLeft { window });
|
||||
}
|
||||
WindowEvent::MouseInput { state, button, .. } => {
|
||||
self.bevy_window_events.send(MouseButtonInput {
|
||||
button: converters::convert_mouse_button(button),
|
||||
state: converters::convert_element_state(state),
|
||||
window,
|
||||
});
|
||||
}
|
||||
WindowEvent::PinchGesture { delta, .. } => {
|
||||
self.bevy_window_events.send(PinchGesture(delta as f32));
|
||||
}
|
||||
WindowEvent::RotationGesture { delta, .. } => {
|
||||
self.bevy_window_events.send(RotationGesture(delta));
|
||||
}
|
||||
WindowEvent::DoubleTapGesture { .. } => {
|
||||
self.bevy_window_events.send(DoubleTapGesture);
|
||||
}
|
||||
WindowEvent::PanGesture { delta, .. } => {
|
||||
self.bevy_window_events.send(PanGesture(Vec2 {
|
||||
x: delta.x,
|
||||
y: delta.y,
|
||||
}));
|
||||
}
|
||||
WindowEvent::MouseWheel { delta, .. } => match delta {
|
||||
event::MouseScrollDelta::LineDelta(x, y) => {
|
||||
self.bevy_window_events.send(MouseWheel {
|
||||
unit: MouseScrollUnit::Line,
|
||||
x,
|
||||
y,
|
||||
window,
|
||||
});
|
||||
}
|
||||
event::MouseScrollDelta::PixelDelta(p) => {
|
||||
self.bevy_window_events.send(MouseWheel {
|
||||
unit: MouseScrollUnit::Pixel,
|
||||
x: p.x as f32,
|
||||
y: p.y as f32,
|
||||
window,
|
||||
});
|
||||
}
|
||||
},
|
||||
WindowEvent::Touch(touch) => {
|
||||
let location = touch
|
||||
.location
|
||||
.to_logical(win.resolution.scale_factor() as f64);
|
||||
self.bevy_window_events
|
||||
.send(converters::convert_touch_input(touch, location, window));
|
||||
}
|
||||
WindowEvent::Focused(focused) => {
|
||||
win.focused = focused;
|
||||
self.bevy_window_events
|
||||
.send(WindowFocused { window, focused });
|
||||
}
|
||||
WindowEvent::Occluded(occluded) => {
|
||||
self.bevy_window_events
|
||||
.send(WindowOccluded { window, occluded });
|
||||
}
|
||||
WindowEvent::DroppedFile(path_buf) => {
|
||||
self.bevy_window_events
|
||||
.send(FileDragAndDrop::DroppedFile { window, path_buf });
|
||||
}
|
||||
WindowEvent::HoveredFile(path_buf) => {
|
||||
self.bevy_window_events
|
||||
.send(FileDragAndDrop::HoveredFile { window, path_buf });
|
||||
}
|
||||
WindowEvent::HoveredFileCancelled => {
|
||||
self.bevy_window_events
|
||||
.send(FileDragAndDrop::HoveredFileCanceled { window });
|
||||
}
|
||||
WindowEvent::Moved(position) => {
|
||||
let position = ivec2(position.x, position.y);
|
||||
win.position.set(position);
|
||||
self.bevy_window_events
|
||||
.send(WindowMoved { window, position });
|
||||
}
|
||||
WindowEvent::Ime(event) => match event {
|
||||
event::Ime::Preedit(value, cursor) => {
|
||||
self.bevy_window_events.send(Ime::Preedit {
|
||||
window,
|
||||
value,
|
||||
cursor,
|
||||
});
|
||||
}
|
||||
event::Ime::Commit(value) => {
|
||||
self.bevy_window_events.send(Ime::Commit { window, value });
|
||||
}
|
||||
event::Ime::Enabled => {
|
||||
self.bevy_window_events.send(Ime::Enabled { window });
|
||||
}
|
||||
event::Ime::Disabled => {
|
||||
self.bevy_window_events.send(Ime::Disabled { window });
|
||||
}
|
||||
},
|
||||
WindowEvent::ThemeChanged(theme) => {
|
||||
self.bevy_window_events.send(WindowThemeChanged {
|
||||
window,
|
||||
theme: converters::convert_winit_theme(theme),
|
||||
});
|
||||
}
|
||||
WindowEvent::Destroyed => {
|
||||
self.bevy_window_events.send(WindowDestroyed { window });
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
self.ran_update_since_last_redraw = false;
|
||||
|
||||
// https://github.com/bevyengine/bevy/issues/17488
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// Have the startup behavior run in about_to_wait, which prevents issues with
|
||||
// invisible window creation. https://github.com/bevyengine/bevy/issues/18027
|
||||
if self.startup_forced_updates == 0 {
|
||||
self.redraw_requested(_event_loop);
|
||||
// Allow AccessKit to respond to `WindowEvent`s before they reach
|
||||
// the engine.
|
||||
if let Some(adapter) = access_kit_adapters.get_mut(&window) {
|
||||
if let Some(winit_window) = winit_windows.get_window(window) {
|
||||
adapter.process_event(winit_window, &event);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let mut windows = self.world_mut().query::<(&mut Window, &mut CachedWindow)>();
|
||||
if let Ok((window_component, mut cache)) = windows.get_mut(self.world_mut(), window) {
|
||||
if window_component.is_changed() {
|
||||
cache.window = window_component.clone();
|
||||
}
|
||||
match event {
|
||||
WindowEvent::Resized(size) => {
|
||||
react_to_resize(window, &mut win, size, &mut window_resized);
|
||||
}
|
||||
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,
|
||||
);
|
||||
}
|
||||
WindowEvent::CloseRequested => self
|
||||
.bevy_window_events
|
||||
.send(WindowCloseRequested { window }),
|
||||
WindowEvent::KeyboardInput {
|
||||
ref event,
|
||||
// On some platforms, winit sends "synthetic" key press events when the window
|
||||
// gains or loses focus. These are not implemented on every platform, so we ignore
|
||||
// winit's synthetic key pressed and implement the same mechanism ourselves.
|
||||
// (See the `WinitWindowPressedKeys` component)
|
||||
is_synthetic: false,
|
||||
..
|
||||
} => {
|
||||
let keyboard_input = converters::convert_keyboard_input(event, window);
|
||||
if event.state.is_pressed() {
|
||||
pressed_keys.0.insert(
|
||||
keyboard_input.key_code,
|
||||
keyboard_input.logical_key.clone(),
|
||||
);
|
||||
} else {
|
||||
pressed_keys.0.remove(&keyboard_input.key_code);
|
||||
}
|
||||
self.bevy_window_events.send(keyboard_input);
|
||||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let physical_position = DVec2::new(position.x, position.y);
|
||||
|
||||
let last_position = win.physical_cursor_position();
|
||||
let delta = last_position.map(|last_pos| {
|
||||
(physical_position.as_vec2() - last_pos) / win.resolution.scale_factor()
|
||||
});
|
||||
|
||||
win.set_physical_cursor_position(Some(physical_position));
|
||||
let position =
|
||||
(physical_position / win.resolution.scale_factor() as f64).as_vec2();
|
||||
self.bevy_window_events.send(CursorMoved {
|
||||
window,
|
||||
position,
|
||||
delta,
|
||||
});
|
||||
}
|
||||
WindowEvent::CursorEntered { .. } => {
|
||||
self.bevy_window_events.send(CursorEntered { window });
|
||||
}
|
||||
WindowEvent::CursorLeft { .. } => {
|
||||
win.set_physical_cursor_position(None);
|
||||
self.bevy_window_events.send(CursorLeft { window });
|
||||
}
|
||||
WindowEvent::MouseInput { state, button, .. } => {
|
||||
self.bevy_window_events.send(MouseButtonInput {
|
||||
button: converters::convert_mouse_button(button),
|
||||
state: converters::convert_element_state(state),
|
||||
window,
|
||||
});
|
||||
}
|
||||
WindowEvent::PinchGesture { delta, .. } => {
|
||||
self.bevy_window_events.send(PinchGesture(delta as f32));
|
||||
}
|
||||
WindowEvent::RotationGesture { delta, .. } => {
|
||||
self.bevy_window_events.send(RotationGesture(delta));
|
||||
}
|
||||
WindowEvent::DoubleTapGesture { .. } => {
|
||||
self.bevy_window_events.send(DoubleTapGesture);
|
||||
}
|
||||
WindowEvent::PanGesture { delta, .. } => {
|
||||
self.bevy_window_events.send(PanGesture(Vec2 {
|
||||
x: delta.x,
|
||||
y: delta.y,
|
||||
}));
|
||||
}
|
||||
WindowEvent::MouseWheel { delta, .. } => match delta {
|
||||
event::MouseScrollDelta::LineDelta(x, y) => {
|
||||
self.bevy_window_events.send(MouseWheel {
|
||||
unit: MouseScrollUnit::Line,
|
||||
x,
|
||||
y,
|
||||
window,
|
||||
});
|
||||
}
|
||||
event::MouseScrollDelta::PixelDelta(p) => {
|
||||
self.bevy_window_events.send(MouseWheel {
|
||||
unit: MouseScrollUnit::Pixel,
|
||||
x: p.x as f32,
|
||||
y: p.y as f32,
|
||||
window,
|
||||
});
|
||||
}
|
||||
},
|
||||
WindowEvent::Touch(touch) => {
|
||||
let location = touch
|
||||
.location
|
||||
.to_logical(win.resolution.scale_factor() as f64);
|
||||
self.bevy_window_events
|
||||
.send(converters::convert_touch_input(touch, location, window));
|
||||
}
|
||||
WindowEvent::Focused(focused) => {
|
||||
win.focused = focused;
|
||||
self.bevy_window_events
|
||||
.send(WindowFocused { window, focused });
|
||||
}
|
||||
WindowEvent::Occluded(occluded) => {
|
||||
self.bevy_window_events
|
||||
.send(WindowOccluded { window, occluded });
|
||||
}
|
||||
WindowEvent::DroppedFile(path_buf) => {
|
||||
self.bevy_window_events
|
||||
.send(FileDragAndDrop::DroppedFile { window, path_buf });
|
||||
}
|
||||
WindowEvent::HoveredFile(path_buf) => {
|
||||
self.bevy_window_events
|
||||
.send(FileDragAndDrop::HoveredFile { window, path_buf });
|
||||
}
|
||||
WindowEvent::HoveredFileCancelled => {
|
||||
self.bevy_window_events
|
||||
.send(FileDragAndDrop::HoveredFileCanceled { window });
|
||||
}
|
||||
WindowEvent::Moved(position) => {
|
||||
let position = ivec2(position.x, position.y);
|
||||
win.position.set(position);
|
||||
self.bevy_window_events
|
||||
.send(WindowMoved { window, position });
|
||||
}
|
||||
WindowEvent::Ime(event) => match event {
|
||||
event::Ime::Preedit(value, cursor) => {
|
||||
self.bevy_window_events.send(Ime::Preedit {
|
||||
window,
|
||||
value,
|
||||
cursor,
|
||||
});
|
||||
}
|
||||
event::Ime::Commit(value) => {
|
||||
self.bevy_window_events.send(Ime::Commit { window, value });
|
||||
}
|
||||
event::Ime::Enabled => {
|
||||
self.bevy_window_events.send(Ime::Enabled { window });
|
||||
}
|
||||
event::Ime::Disabled => {
|
||||
self.bevy_window_events.send(Ime::Disabled { window });
|
||||
}
|
||||
},
|
||||
WindowEvent::ThemeChanged(theme) => {
|
||||
self.bevy_window_events.send(WindowThemeChanged {
|
||||
window,
|
||||
theme: converters::convert_winit_theme(theme),
|
||||
});
|
||||
}
|
||||
WindowEvent::Destroyed => {
|
||||
self.bevy_window_events.send(WindowDestroyed { window });
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
self.ran_update_since_last_redraw = false;
|
||||
|
||||
// https://github.com/bevyengine/bevy/issues/17488
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// Have the startup behavior run in about_to_wait, which prevents issues with
|
||||
// invisible window creation. https://github.com/bevyengine/bevy/issues/18027
|
||||
if self.startup_forced_updates == 0 {
|
||||
manual_run_redraw_requested = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let mut windows = self.world_mut().query::<(&mut Window, &mut CachedWindow)>();
|
||||
if let Ok((window_component, mut cache)) = windows.get_mut(self.world_mut(), window)
|
||||
{
|
||||
if window_component.is_changed() {
|
||||
cache.window = window_component.clone();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if manual_run_redraw_requested {
|
||||
self.redraw_requested(_event_loop);
|
||||
}
|
||||
}
|
||||
|
||||
@ -506,19 +519,20 @@ impl<T: Event> ApplicationHandler<T> for WinitAppRunnerState<T> {
|
||||
// invisible window creation. https://github.com/bevyengine/bevy/issues/18027
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let winit_windows = self.world().non_send_resource::<WinitWindows>();
|
||||
let headless = winit_windows.windows.is_empty();
|
||||
let exiting = self.app_exit.is_some();
|
||||
let reactive = matches!(self.update_mode, UpdateMode::Reactive { .. });
|
||||
let all_invisible = winit_windows
|
||||
.windows
|
||||
.iter()
|
||||
.all(|(_, w)| !w.is_visible().unwrap_or(false));
|
||||
if !exiting
|
||||
&& (self.startup_forced_updates > 0 || headless || all_invisible || reactive)
|
||||
{
|
||||
self.redraw_requested(event_loop);
|
||||
}
|
||||
WINIT_WINDOWS.with_borrow(|winit_windows| {
|
||||
let headless = winit_windows.windows.is_empty();
|
||||
let exiting = self.app_exit.is_some();
|
||||
let reactive = matches!(self.update_mode, UpdateMode::Reactive { .. });
|
||||
let all_invisible = winit_windows
|
||||
.windows
|
||||
.iter()
|
||||
.all(|(_, w)| !w.is_visible().unwrap_or(false));
|
||||
if !exiting
|
||||
&& (self.startup_forced_updates > 0 || headless || all_invisible || reactive)
|
||||
{
|
||||
self.redraw_requested(event_loop);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -591,35 +605,33 @@ impl<T: Event> WinitAppRunnerState<T> {
|
||||
// Get windows that are cached but without raw handles. Those window were already created, but got their
|
||||
// handle wrapper removed when the app was suspended.
|
||||
let mut query = self.world_mut()
|
||||
.query_filtered::<(Entity, &Window), (With<CachedWindow>, Without<bevy_window::RawHandleWrapper>)>();
|
||||
.query_filtered::<(Entity, &Window), (With<CachedWindow>, Without<RawHandleWrapper>)>();
|
||||
if let Ok((entity, window)) = query.single(&self.world()) {
|
||||
let window = window.clone();
|
||||
|
||||
let mut create_window =
|
||||
SystemState::<CreateWindowParams>::from_world(self.world_mut());
|
||||
WINIT_WINDOWS.with_borrow_mut(|winit_windows| {
|
||||
ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| {
|
||||
let mut create_window =
|
||||
SystemState::<CreateWindowParams>::from_world(self.world_mut());
|
||||
|
||||
let (
|
||||
..,
|
||||
mut winit_windows,
|
||||
mut adapters,
|
||||
mut handlers,
|
||||
accessibility_requested,
|
||||
monitors,
|
||||
) = create_window.get_mut(self.world_mut());
|
||||
let (.., mut handlers, accessibility_requested, monitors) =
|
||||
create_window.get_mut(self.world_mut());
|
||||
|
||||
let winit_window = winit_windows.create_window(
|
||||
event_loop,
|
||||
entity,
|
||||
&window,
|
||||
&mut adapters,
|
||||
&mut handlers,
|
||||
&accessibility_requested,
|
||||
&monitors,
|
||||
);
|
||||
let winit_window = winit_windows.create_window(
|
||||
event_loop,
|
||||
entity,
|
||||
&window,
|
||||
adapters,
|
||||
&mut handlers,
|
||||
&accessibility_requested,
|
||||
&monitors,
|
||||
);
|
||||
|
||||
let wrapper = RawHandleWrapper::new(winit_window).unwrap();
|
||||
let wrapper = RawHandleWrapper::new(winit_window).unwrap();
|
||||
|
||||
self.world_mut().entity_mut(entity).insert(wrapper);
|
||||
self.world_mut().entity_mut(entity).insert(wrapper);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -684,9 +696,10 @@ impl<T: Event> WinitAppRunnerState<T> {
|
||||
all(target_os = "linux", any(feature = "x11", feature = "wayland"))
|
||||
)))]
|
||||
{
|
||||
let winit_windows = self.world().non_send_resource::<WinitWindows>();
|
||||
let visible = winit_windows.windows.iter().any(|(_, w)| {
|
||||
w.is_visible().unwrap_or(false)
|
||||
let visible = WINIT_WINDOWS.with_borrow(|winit_windows| {
|
||||
winit_windows.windows.iter().any(|(_, w)| {
|
||||
w.is_visible().unwrap_or(false)
|
||||
})
|
||||
});
|
||||
|
||||
event_loop.set_control_flow(if visible {
|
||||
@ -716,10 +729,11 @@ impl<T: Event> WinitAppRunnerState<T> {
|
||||
}
|
||||
|
||||
if self.redraw_requested && self.lifecycle != AppLifecycle::Suspended {
|
||||
let winit_windows = self.world().non_send_resource::<WinitWindows>();
|
||||
for window in winit_windows.windows.values() {
|
||||
window.request_redraw();
|
||||
}
|
||||
WINIT_WINDOWS.with_borrow(|winit_windows| {
|
||||
for window in winit_windows.windows.values() {
|
||||
window.request_redraw();
|
||||
}
|
||||
});
|
||||
self.redraw_requested = false;
|
||||
}
|
||||
|
||||
@ -871,48 +885,47 @@ impl<T: Event> WinitAppRunnerState<T> {
|
||||
fn update_cursors(&mut self, #[cfg(feature = "custom_cursor")] event_loop: &ActiveEventLoop) {
|
||||
#[cfg(feature = "custom_cursor")]
|
||||
let mut windows_state: SystemState<(
|
||||
NonSendMut<WinitWindows>,
|
||||
ResMut<CustomCursorCache>,
|
||||
Query<(Entity, &mut PendingCursor), Changed<PendingCursor>>,
|
||||
)> = SystemState::new(self.world_mut());
|
||||
#[cfg(feature = "custom_cursor")]
|
||||
let (winit_windows, mut cursor_cache, mut windows) =
|
||||
windows_state.get_mut(self.world_mut());
|
||||
let (mut cursor_cache, mut windows) = windows_state.get_mut(self.world_mut());
|
||||
#[cfg(not(feature = "custom_cursor"))]
|
||||
let mut windows_state: SystemState<(
|
||||
NonSendMut<WinitWindows>,
|
||||
Query<(Entity, &mut PendingCursor), Changed<PendingCursor>>,
|
||||
)> = SystemState::new(self.world_mut());
|
||||
#[cfg(not(feature = "custom_cursor"))]
|
||||
let (winit_windows, mut windows) = windows_state.get_mut(self.world_mut());
|
||||
let (mut windows,) = windows_state.get_mut(self.world_mut());
|
||||
|
||||
for (entity, mut pending_cursor) in windows.iter_mut() {
|
||||
let Some(winit_window) = winit_windows.get_window(entity) else {
|
||||
continue;
|
||||
};
|
||||
let Some(pending_cursor) = pending_cursor.0.take() else {
|
||||
continue;
|
||||
};
|
||||
WINIT_WINDOWS.with_borrow(|winit_windows| {
|
||||
for (entity, mut pending_cursor) in windows.iter_mut() {
|
||||
let Some(winit_window) = winit_windows.get_window(entity) else {
|
||||
continue;
|
||||
};
|
||||
let Some(pending_cursor) = pending_cursor.0.take() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let final_cursor: winit::window::Cursor = match pending_cursor {
|
||||
#[cfg(feature = "custom_cursor")]
|
||||
CursorSource::CustomCached(cache_key) => {
|
||||
let Some(cached_cursor) = cursor_cache.0.get(&cache_key) else {
|
||||
error!("Cursor should have been cached, but was not found");
|
||||
continue;
|
||||
};
|
||||
cached_cursor.clone().into()
|
||||
}
|
||||
#[cfg(feature = "custom_cursor")]
|
||||
CursorSource::Custom((cache_key, cursor)) => {
|
||||
let custom_cursor = event_loop.create_custom_cursor(cursor);
|
||||
cursor_cache.0.insert(cache_key, custom_cursor.clone());
|
||||
custom_cursor.into()
|
||||
}
|
||||
CursorSource::System(system_cursor) => system_cursor.into(),
|
||||
};
|
||||
winit_window.set_cursor(final_cursor);
|
||||
}
|
||||
let final_cursor: winit::window::Cursor = match pending_cursor {
|
||||
#[cfg(feature = "custom_cursor")]
|
||||
CursorSource::CustomCached(cache_key) => {
|
||||
let Some(cached_cursor) = cursor_cache.0.get(&cache_key) else {
|
||||
error!("Cursor should have been cached, but was not found");
|
||||
continue;
|
||||
};
|
||||
cached_cursor.clone().into()
|
||||
}
|
||||
#[cfg(feature = "custom_cursor")]
|
||||
CursorSource::Custom((cache_key, cursor)) => {
|
||||
let custom_cursor = event_loop.create_custom_cursor(cursor);
|
||||
cursor_cache.0.insert(cache_key, custom_cursor.clone());
|
||||
custom_cursor.into()
|
||||
}
|
||||
CursorSource::System(system_cursor) => system_cursor.into(),
|
||||
};
|
||||
winit_window.set_cursor(final_cursor);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ use bevy_ecs::{
|
||||
prelude::{Changed, Component},
|
||||
query::QueryFilter,
|
||||
removal_detection::RemovedComponents,
|
||||
system::{Local, NonSendMut, Query, SystemParamItem},
|
||||
system::{Local, NonSendMarker, Query, SystemParamItem},
|
||||
};
|
||||
use bevy_input::keyboard::{Key, KeyCode, KeyboardFocusLost, KeyboardInput};
|
||||
use bevy_window::{
|
||||
@ -30,6 +30,7 @@ use winit::platform::ios::WindowExtIOS;
|
||||
use winit::platform::web::WindowExtWebSys;
|
||||
|
||||
use crate::{
|
||||
accessibility::ACCESS_KIT_ADAPTERS,
|
||||
converters::{
|
||||
convert_enabled_buttons, convert_resize_direction, convert_window_level,
|
||||
convert_window_theme, convert_winit_theme,
|
||||
@ -37,7 +38,7 @@ use crate::{
|
||||
get_selected_videomode, select_monitor,
|
||||
state::react_to_resize,
|
||||
winit_monitors::WinitMonitors,
|
||||
CreateMonitorParams, CreateWindowParams, WinitWindows,
|
||||
CreateMonitorParams, CreateWindowParams, WINIT_WINDOWS,
|
||||
};
|
||||
|
||||
/// Creates new windows on the [`winit`] backend for each entity with a newly-added
|
||||
@ -51,78 +52,80 @@ pub fn create_windows<F: QueryFilter + 'static>(
|
||||
mut commands,
|
||||
mut created_windows,
|
||||
mut window_created_events,
|
||||
mut winit_windows,
|
||||
mut adapters,
|
||||
mut handlers,
|
||||
accessibility_requested,
|
||||
monitors,
|
||||
): SystemParamItem<CreateWindowParams<F>>,
|
||||
) {
|
||||
for (entity, mut window, handle_holder) in &mut created_windows {
|
||||
if winit_windows.get_window(entity).is_some() {
|
||||
continue;
|
||||
}
|
||||
WINIT_WINDOWS.with_borrow_mut(|winit_windows| {
|
||||
ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| {
|
||||
for (entity, mut window, handle_holder) in &mut created_windows {
|
||||
if winit_windows.get_window(entity).is_some() {
|
||||
continue;
|
||||
}
|
||||
|
||||
info!("Creating new window {} ({})", window.title.as_str(), entity);
|
||||
info!("Creating new window {} ({})", window.title.as_str(), entity);
|
||||
|
||||
let winit_window = winit_windows.create_window(
|
||||
event_loop,
|
||||
entity,
|
||||
&window,
|
||||
&mut adapters,
|
||||
&mut handlers,
|
||||
&accessibility_requested,
|
||||
&monitors,
|
||||
);
|
||||
let winit_window = winit_windows.create_window(
|
||||
event_loop,
|
||||
entity,
|
||||
&window,
|
||||
adapters,
|
||||
&mut handlers,
|
||||
&accessibility_requested,
|
||||
&monitors,
|
||||
);
|
||||
|
||||
if let Some(theme) = winit_window.theme() {
|
||||
window.window_theme = Some(convert_winit_theme(theme));
|
||||
}
|
||||
if let Some(theme) = winit_window.theme() {
|
||||
window.window_theme = Some(convert_winit_theme(theme));
|
||||
}
|
||||
|
||||
window
|
||||
.resolution
|
||||
.set_scale_factor_and_apply_to_physical_size(winit_window.scale_factor() as f32);
|
||||
window
|
||||
.resolution
|
||||
.set_scale_factor_and_apply_to_physical_size(winit_window.scale_factor() as f32);
|
||||
|
||||
commands.entity(entity).insert((
|
||||
CachedWindow {
|
||||
window: window.clone(),
|
||||
},
|
||||
WinitWindowPressedKeys::default(),
|
||||
));
|
||||
commands.entity(entity).insert((
|
||||
CachedWindow {
|
||||
window: window.clone(),
|
||||
},
|
||||
WinitWindowPressedKeys::default(),
|
||||
));
|
||||
|
||||
if let Ok(handle_wrapper) = RawHandleWrapper::new(winit_window) {
|
||||
commands.entity(entity).insert(handle_wrapper.clone());
|
||||
if let Some(handle_holder) = handle_holder {
|
||||
*handle_holder.0.lock().unwrap() = Some(handle_wrapper);
|
||||
if let Ok(handle_wrapper) = RawHandleWrapper::new(winit_window) {
|
||||
commands.entity(entity).insert(handle_wrapper.clone());
|
||||
if let Some(handle_holder) = handle_holder {
|
||||
*handle_holder.0.lock().unwrap() = Some(handle_wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
#[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();
|
||||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
|
||||
window_created_events.write(WindowCreated { window: entity });
|
||||
}
|
||||
}
|
||||
|
||||
#[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();
|
||||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
|
||||
window_created_events.write(WindowCreated { window: entity });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Check whether keyboard focus was lost. This is different from window
|
||||
@ -239,9 +242,9 @@ pub(crate) fn despawn_windows(
|
||||
window_entities: Query<Entity, With<Window>>,
|
||||
mut closing_events: EventWriter<WindowClosing>,
|
||||
mut closed_events: EventWriter<WindowClosed>,
|
||||
mut winit_windows: NonSendMut<WinitWindows>,
|
||||
mut windows_to_drop: Local<Vec<WindowWrapper<winit::window::Window>>>,
|
||||
mut exit_events: EventReader<AppExit>,
|
||||
_non_send_marker: NonSendMarker,
|
||||
) {
|
||||
// Drop all the windows that are waiting to be closed
|
||||
windows_to_drop.clear();
|
||||
@ -254,13 +257,15 @@ pub(crate) fn despawn_windows(
|
||||
// rather than having the component added
|
||||
// and removed in the same frame.
|
||||
if !window_entities.contains(window) {
|
||||
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);
|
||||
}
|
||||
WINIT_WINDOWS.with_borrow_mut(|winit_windows| {
|
||||
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);
|
||||
}
|
||||
});
|
||||
closed_events.write(WindowClosed { window });
|
||||
}
|
||||
}
|
||||
@ -291,296 +296,298 @@ pub struct CachedWindow {
|
||||
/// - [`Window::focused`] cannot be manually changed to `false` after the window is created.
|
||||
pub(crate) fn changed_windows(
|
||||
mut changed_windows: Query<(Entity, &mut Window, &mut CachedWindow), Changed<Window>>,
|
||||
winit_windows: NonSendMut<WinitWindows>,
|
||||
monitors: Res<WinitMonitors>,
|
||||
mut window_resized: EventWriter<WindowResized>,
|
||||
_non_send_marker: NonSendMarker,
|
||||
) {
|
||||
for (entity, mut window, mut cache) in &mut changed_windows {
|
||||
let Some(winit_window) = winit_windows.get_window(entity) else {
|
||||
continue;
|
||||
};
|
||||
WINIT_WINDOWS.with_borrow(|winit_windows| {
|
||||
for (entity, mut window, mut cache) in &mut changed_windows {
|
||||
let Some(winit_window) = winit_windows.get_window(entity) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if window.title != cache.window.title {
|
||||
winit_window.set_title(window.title.as_str());
|
||||
}
|
||||
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 {
|
||||
WindowMode::BorderlessFullscreen(monitor_selection) => {
|
||||
Some(Some(winit::window::Fullscreen::Borderless(select_monitor(
|
||||
&monitors,
|
||||
winit_window.primary_monitor(),
|
||||
winit_window.current_monitor(),
|
||||
&monitor_selection,
|
||||
))))
|
||||
}
|
||||
WindowMode::Fullscreen(monitor_selection, video_mode_selection) => {
|
||||
let monitor = &select_monitor(
|
||||
&monitors,
|
||||
winit_window.primary_monitor(),
|
||||
winit_window.current_monitor(),
|
||||
&monitor_selection,
|
||||
)
|
||||
.unwrap_or_else(|| {
|
||||
panic!("Could not find monitor for {:?}", monitor_selection)
|
||||
});
|
||||
|
||||
if let Some(video_mode) = get_selected_videomode(monitor, &video_mode_selection)
|
||||
{
|
||||
Some(Some(winit::window::Fullscreen::Exclusive(video_mode)))
|
||||
} else {
|
||||
warn!(
|
||||
"Could not find valid fullscreen video mode for {:?} {:?}",
|
||||
monitor_selection, video_mode_selection
|
||||
);
|
||||
None
|
||||
if window.mode != cache.window.mode {
|
||||
let new_mode = match window.mode {
|
||||
WindowMode::BorderlessFullscreen(monitor_selection) => {
|
||||
Some(Some(winit::window::Fullscreen::Borderless(select_monitor(
|
||||
&monitors,
|
||||
winit_window.primary_monitor(),
|
||||
winit_window.current_monitor(),
|
||||
&monitor_selection,
|
||||
))))
|
||||
}
|
||||
}
|
||||
WindowMode::Windowed => Some(None),
|
||||
};
|
||||
WindowMode::Fullscreen(monitor_selection, video_mode_selection) => {
|
||||
let monitor = &select_monitor(
|
||||
&monitors,
|
||||
winit_window.primary_monitor(),
|
||||
winit_window.current_monitor(),
|
||||
&monitor_selection,
|
||||
)
|
||||
.unwrap_or_else(|| {
|
||||
panic!("Could not find monitor for {:?}", monitor_selection)
|
||||
});
|
||||
|
||||
if let Some(new_mode) = new_mode {
|
||||
if winit_window.fullscreen() != new_mode {
|
||||
winit_window.set_fullscreen(new_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if window.resolution != cache.window.resolution {
|
||||
let mut physical_size = PhysicalSize::new(
|
||||
window.resolution.physical_width(),
|
||||
window.resolution.physical_height(),
|
||||
);
|
||||
|
||||
let cached_physical_size = PhysicalSize::new(
|
||||
cache.window.physical_width(),
|
||||
cache.window.physical_height(),
|
||||
);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if let Err(err) = winit_window.set_cursor_position(position) {
|
||||
error!("could not set cursor position: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if window.cursor_options.grab_mode != cache.window.cursor_options.grab_mode
|
||||
&& crate::winit_windows::attempt_grab(winit_window, window.cursor_options.grab_mode)
|
||||
.is_err()
|
||||
{
|
||||
window.cursor_options.grab_mode = cache.window.cursor_options.grab_mode;
|
||||
}
|
||||
|
||||
if window.cursor_options.visible != cache.window.cursor_options.visible {
|
||||
winit_window.set_cursor_visible(window.cursor_options.visible);
|
||||
}
|
||||
|
||||
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;
|
||||
warn!(
|
||||
"Could not set cursor hit test for window {}: {}",
|
||||
window.title, err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if window.decorations != cache.window.decorations
|
||||
&& window.decorations != winit_window.is_decorated()
|
||||
{
|
||||
winit_window.set_decorations(window.decorations);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
if window.position != cache.window.position {
|
||||
if let Some(position) = crate::winit_window_position(
|
||||
&window.position,
|
||||
&window.resolution,
|
||||
&monitors,
|
||||
winit_window.primary_monitor(),
|
||||
winit_window.current_monitor(),
|
||||
) {
|
||||
let should_set = match winit_window.outer_position() {
|
||||
Ok(current_position) => current_position != position,
|
||||
_ => true,
|
||||
if let Some(video_mode) = get_selected_videomode(monitor, &video_mode_selection)
|
||||
{
|
||||
Some(Some(winit::window::Fullscreen::Exclusive(video_mode)))
|
||||
} else {
|
||||
warn!(
|
||||
"Could not find valid fullscreen video mode for {:?} {:?}",
|
||||
monitor_selection, video_mode_selection
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
WindowMode::Windowed => Some(None),
|
||||
};
|
||||
|
||||
if should_set {
|
||||
winit_window.set_outer_position(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(maximized) = window.internal.take_maximize_request() {
|
||||
winit_window.set_maximized(maximized);
|
||||
}
|
||||
|
||||
if let Some(minimized) = window.internal.take_minimize_request() {
|
||||
winit_window.set_minimized(minimized);
|
||||
}
|
||||
|
||||
if window.internal.take_move_request() {
|
||||
if let Err(e) = winit_window.drag_window() {
|
||||
warn!("Winit returned an error while attempting to drag the window: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(resize_direction) = window.internal.take_resize_request() {
|
||||
if let Err(e) =
|
||||
winit_window.drag_resize_window(convert_resize_direction(resize_direction))
|
||||
{
|
||||
warn!("Winit returned an error while attempting to drag resize the window: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
if window.focused != cache.window.focused && window.focused {
|
||||
winit_window.focus_window();
|
||||
}
|
||||
|
||||
if window.window_level != cache.window.window_level {
|
||||
winit_window.set_window_level(convert_window_level(window.window_level));
|
||||
}
|
||||
|
||||
// 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.");
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
if window.canvas != cache.window.canvas {
|
||||
window.canvas.clone_from(&cache.window.canvas);
|
||||
warn!(
|
||||
"Bevy currently doesn't support modifying the window canvas after initialization."
|
||||
);
|
||||
}
|
||||
|
||||
if window.ime_enabled != cache.window.ime_enabled {
|
||||
winit_window.set_ime_allowed(window.ime_enabled);
|
||||
}
|
||||
|
||||
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),
|
||||
);
|
||||
}
|
||||
|
||||
if window.window_theme != cache.window.window_theme {
|
||||
winit_window.set_theme(window.window_theme.map(convert_window_theme));
|
||||
}
|
||||
|
||||
if window.visible != cache.window.visible {
|
||||
winit_window.set_visible(window.visible);
|
||||
}
|
||||
|
||||
#[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");
|
||||
if let Some(new_mode) = new_mode {
|
||||
if winit_window.fullscreen() != new_mode {
|
||||
winit_window.set_fullscreen(new_mode);
|
||||
}
|
||||
(Some((min, max)), _) => winit_window.recognize_pan_gesture(true, min, max),
|
||||
_ => winit_window.recognize_pan_gesture(false, 0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
if window.prefers_home_indicator_hidden != cache.window.prefers_home_indicator_hidden {
|
||||
winit_window
|
||||
.set_prefers_home_indicator_hidden(window.prefers_home_indicator_hidden);
|
||||
if window.resolution != cache.window.resolution {
|
||||
let mut physical_size = PhysicalSize::new(
|
||||
window.resolution.physical_width(),
|
||||
window.resolution.physical_height(),
|
||||
);
|
||||
|
||||
let cached_physical_size = PhysicalSize::new(
|
||||
cache.window.physical_width(),
|
||||
cache.window.physical_height(),
|
||||
);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
if window.prefers_status_bar_hidden != cache.window.prefers_status_bar_hidden {
|
||||
winit_window.set_prefers_status_bar_hidden(window.prefers_status_bar_hidden);
|
||||
|
||||
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);
|
||||
|
||||
if let Err(err) = winit_window.set_cursor_position(position) {
|
||||
error!("could not set cursor position: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
if window.preferred_screen_edges_deferring_system_gestures
|
||||
!= cache
|
||||
.window
|
||||
.preferred_screen_edges_deferring_system_gestures
|
||||
|
||||
if window.cursor_options.grab_mode != cache.window.cursor_options.grab_mode
|
||||
&& crate::winit_windows::attempt_grab(winit_window, window.cursor_options.grab_mode)
|
||||
.is_err()
|
||||
{
|
||||
use crate::converters::convert_screen_edge;
|
||||
let preferred_edge =
|
||||
convert_screen_edge(window.preferred_screen_edges_deferring_system_gestures);
|
||||
winit_window.set_preferred_screen_edges_deferring_system_gestures(preferred_edge);
|
||||
window.cursor_options.grab_mode = cache.window.cursor_options.grab_mode;
|
||||
}
|
||||
|
||||
if window.cursor_options.visible != cache.window.cursor_options.visible {
|
||||
winit_window.set_cursor_visible(window.cursor_options.visible);
|
||||
}
|
||||
|
||||
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;
|
||||
warn!(
|
||||
"Could not set cursor hit test for window {}: {}",
|
||||
window.title, err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if window.decorations != cache.window.decorations
|
||||
&& window.decorations != winit_window.is_decorated()
|
||||
{
|
||||
winit_window.set_decorations(window.decorations);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
if window.position != cache.window.position {
|
||||
if let Some(position) = crate::winit_window_position(
|
||||
&window.position,
|
||||
&window.resolution,
|
||||
&monitors,
|
||||
winit_window.primary_monitor(),
|
||||
winit_window.current_monitor(),
|
||||
) {
|
||||
let should_set = match winit_window.outer_position() {
|
||||
Ok(current_position) => current_position != position,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
if should_set {
|
||||
winit_window.set_outer_position(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(maximized) = window.internal.take_maximize_request() {
|
||||
winit_window.set_maximized(maximized);
|
||||
}
|
||||
|
||||
if let Some(minimized) = window.internal.take_minimize_request() {
|
||||
winit_window.set_minimized(minimized);
|
||||
}
|
||||
|
||||
if window.internal.take_move_request() {
|
||||
if let Err(e) = winit_window.drag_window() {
|
||||
warn!("Winit returned an error while attempting to drag the window: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(resize_direction) = window.internal.take_resize_request() {
|
||||
if let Err(e) =
|
||||
winit_window.drag_resize_window(convert_resize_direction(resize_direction))
|
||||
{
|
||||
warn!("Winit returned an error while attempting to drag resize the window: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
if window.focused != cache.window.focused && window.focused {
|
||||
winit_window.focus_window();
|
||||
}
|
||||
|
||||
if window.window_level != cache.window.window_level {
|
||||
winit_window.set_window_level(convert_window_level(window.window_level));
|
||||
}
|
||||
|
||||
// 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.");
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
if window.canvas != cache.window.canvas {
|
||||
window.canvas.clone_from(&cache.window.canvas);
|
||||
warn!(
|
||||
"Bevy currently doesn't support modifying the window canvas after initialization."
|
||||
);
|
||||
}
|
||||
|
||||
if window.ime_enabled != cache.window.ime_enabled {
|
||||
winit_window.set_ime_allowed(window.ime_enabled);
|
||||
}
|
||||
|
||||
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),
|
||||
);
|
||||
}
|
||||
|
||||
if window.window_theme != cache.window.window_theme {
|
||||
winit_window.set_theme(window.window_theme.map(convert_window_theme));
|
||||
}
|
||||
|
||||
if window.visible != cache.window.visible {
|
||||
winit_window.set_visible(window.visible);
|
||||
}
|
||||
|
||||
#[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),
|
||||
}
|
||||
}
|
||||
|
||||
if window.prefers_home_indicator_hidden != cache.window.prefers_home_indicator_hidden {
|
||||
winit_window
|
||||
.set_prefers_home_indicator_hidden(window.prefers_home_indicator_hidden);
|
||||
}
|
||||
if window.prefers_status_bar_hidden != cache.window.prefers_status_bar_hidden {
|
||||
winit_window.set_prefers_status_bar_hidden(window.prefers_status_bar_hidden);
|
||||
}
|
||||
if window.preferred_screen_edges_deferring_system_gestures
|
||||
!= cache
|
||||
.window
|
||||
.preferred_screen_edges_deferring_system_gestures
|
||||
{
|
||||
use crate::converters::convert_screen_edge;
|
||||
let preferred_edge =
|
||||
convert_screen_edge(window.preferred_screen_edges_deferring_system_gestures);
|
||||
winit_window.set_preferred_screen_edges_deferring_system_gestures(preferred_edge);
|
||||
}
|
||||
}
|
||||
cache.window = window.clone();
|
||||
}
|
||||
cache.window = window.clone();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// This keeps track of which keys are pressed on each window.
|
||||
|
@ -42,6 +42,16 @@ pub struct WinitWindows {
|
||||
}
|
||||
|
||||
impl WinitWindows {
|
||||
/// Creates a new instance of `WinitWindows`.
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
windows: HashMap::new(),
|
||||
entity_to_winit: EntityHashMap::new(),
|
||||
winit_to_entity: HashMap::new(),
|
||||
_not_send_sync: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `winit` window and associates it with our entity.
|
||||
pub fn create_window(
|
||||
&mut self,
|
||||
|
Loading…
Reference in New Issue
Block a user