2024-03-29 16:02:25 +00:00
|
|
|
use bevy_a11y::AccessibilityRequested;
|
2023-01-19 00:38:28 +00:00
|
|
|
use bevy_ecs::entity::Entity;
|
|
|
|
|
2024-02-12 15:02:24 +00:00
|
|
|
use bevy_ecs::entity::EntityHashMap;
|
|
|
|
use bevy_utils::{tracing::warn, HashMap};
|
2023-01-19 00:38:28 +00:00
|
|
|
use bevy_window::{CursorGrabMode, Window, WindowMode, WindowPosition, WindowResolution};
|
|
|
|
|
2022-09-06 14:45:44 +00:00
|
|
|
use winit::{
|
2023-01-19 00:38:28 +00:00
|
|
|
dpi::{LogicalSize, PhysicalPosition},
|
|
|
|
monitor::MonitorHandle,
|
2022-09-06 14:45:44 +00:00
|
|
|
};
|
2020-03-30 21:53:32 +00:00
|
|
|
|
2023-03-01 22:45:04 +00:00
|
|
|
use crate::{
|
2024-03-29 16:02:25 +00:00
|
|
|
accessibility::{prepare_accessibility_for_window, AccessKitAdapters, WinitActionHandlers},
|
2023-07-23 01:02:40 +00:00
|
|
|
converters::{convert_enabled_buttons, convert_window_level, convert_window_theme},
|
2023-03-01 22:45:04 +00:00
|
|
|
};
|
2023-02-03 16:41:39 +00:00
|
|
|
|
2023-08-31 19:05:49 +00:00
|
|
|
/// A resource mapping window entities to their `winit`-backend [`Window`](winit::window::Window)
|
2023-07-31 21:41:59 +00:00
|
|
|
/// states.
|
2020-10-08 18:43:01 +00:00
|
|
|
#[derive(Debug, Default)]
|
2020-03-30 21:53:32 +00:00
|
|
|
pub struct WinitWindows {
|
2023-03-21 19:59:30 +00:00
|
|
|
/// Stores [`winit`] windows by window identifier.
|
2020-03-30 21:53:32 +00:00
|
|
|
pub windows: HashMap<winit::window::WindowId, winit::window::Window>,
|
2023-03-21 19:59:30 +00:00
|
|
|
/// Maps entities to `winit` window identifiers.
|
2024-02-12 15:02:24 +00:00
|
|
|
pub entity_to_winit: EntityHashMap<winit::window::WindowId>,
|
2023-03-21 19:59:30 +00:00
|
|
|
/// Maps `winit` window identifiers to entities.
|
2023-01-19 00:38:28 +00:00
|
|
|
pub winit_to_entity: HashMap<winit::window::WindowId, Entity>,
|
2023-07-31 21:41:59 +00:00
|
|
|
// Many `winit` window functions (e.g. `set_window_icon`) can only be called on the main thread.
|
|
|
|
// If they're called on other threads, the program might hang. This marker indicates that this
|
|
|
|
// type is not thread-safe and will be `!Send` and `!Sync`.
|
2022-02-24 01:40:02 +00:00
|
|
|
_not_send_sync: core::marker::PhantomData<*const ()>,
|
2020-03-30 21:53:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl WinitWindows {
|
2023-03-21 19:59:30 +00:00
|
|
|
/// Creates a `winit` window and associates it with our entity.
|
2020-03-30 21:53:32 +00:00
|
|
|
pub fn create_window(
|
|
|
|
&mut self,
|
2024-03-04 17:17:17 +00:00
|
|
|
event_loop: &winit::event_loop::EventLoopWindowTarget<crate::UserEvent>,
|
2023-01-19 00:38:28 +00:00
|
|
|
entity: Entity,
|
|
|
|
window: &Window,
|
2023-03-01 22:45:04 +00:00
|
|
|
adapters: &mut AccessKitAdapters,
|
|
|
|
handlers: &mut WinitActionHandlers,
|
2023-09-02 18:35:06 +00:00
|
|
|
accessibility_requested: &AccessibilityRequested,
|
2023-01-19 00:38:28 +00:00
|
|
|
) -> &winit::window::Window {
|
2020-08-13 08:47:40 +00:00
|
|
|
let mut winit_window_builder = winit::window::WindowBuilder::new();
|
2020-08-13 06:18:10 +00:00
|
|
|
|
2023-03-01 22:45:04 +00:00
|
|
|
// Due to a UIA limitation, winit windows need to be invisible for the
|
|
|
|
// AccessKit adapter is initialized.
|
|
|
|
winit_window_builder = winit_window_builder.with_visible(false);
|
|
|
|
|
2023-01-19 00:38:28 +00:00
|
|
|
winit_window_builder = match window.mode {
|
|
|
|
WindowMode::BorderlessFullscreen => winit_window_builder.with_fullscreen(Some(
|
|
|
|
winit::window::Fullscreen::Borderless(event_loop.primary_monitor()),
|
2020-08-13 09:27:51 +00:00
|
|
|
)),
|
2024-02-10 20:17:04 +00:00
|
|
|
mode @ (WindowMode::Fullscreen | WindowMode::SizedFullscreen) => {
|
|
|
|
if let Some(primary_monitor) = event_loop.primary_monitor() {
|
|
|
|
let videomode = match mode {
|
|
|
|
WindowMode::Fullscreen => get_best_videomode(&primary_monitor),
|
|
|
|
WindowMode::SizedFullscreen => get_fitting_videomode(
|
|
|
|
&primary_monitor,
|
|
|
|
window.width() as u32,
|
|
|
|
window.height() as u32,
|
|
|
|
),
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
|
|
|
|
winit_window_builder
|
|
|
|
.with_fullscreen(Some(winit::window::Fullscreen::Exclusive(videomode)))
|
|
|
|
} else {
|
|
|
|
warn!("Could not determine primary monitor, ignoring exclusive fullscreen request for window {:?}", window.title);
|
|
|
|
winit_window_builder
|
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
}
|
2022-12-16 20:14:11 +00:00
|
|
|
WindowMode::Windowed => {
|
2023-01-19 00:38:28 +00:00
|
|
|
if let Some(position) = winit_window_position(
|
|
|
|
&window.position,
|
|
|
|
&window.resolution,
|
|
|
|
event_loop.available_monitors(),
|
|
|
|
event_loop.primary_monitor(),
|
|
|
|
None,
|
|
|
|
) {
|
|
|
|
winit_window_builder = winit_window_builder.with_position(position);
|
|
|
|
}
|
|
|
|
|
|
|
|
let logical_size = LogicalSize::new(window.width(), window.height());
|
|
|
|
if let Some(sf) = window.resolution.scale_factor_override() {
|
2023-12-14 14:56:40 +00:00
|
|
|
winit_window_builder.with_inner_size(logical_size.to_physical::<f64>(sf.into()))
|
2022-07-04 13:04:14 +00:00
|
|
|
} else {
|
2022-09-06 14:45:44 +00:00
|
|
|
winit_window_builder.with_inner_size(logical_size)
|
2020-12-28 20:26:50 +00:00
|
|
|
}
|
|
|
|
}
|
2022-12-16 20:14:11 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
winit_window_builder = winit_window_builder
|
2023-02-03 16:41:39 +00:00
|
|
|
.with_window_level(convert_window_level(window.window_level))
|
2023-06-05 21:04:22 +00:00
|
|
|
.with_theme(window.window_theme.map(convert_window_theme))
|
2023-01-19 00:38:28 +00:00
|
|
|
.with_resizable(window.resizable)
|
2023-07-23 01:02:40 +00:00
|
|
|
.with_enabled_buttons(convert_enabled_buttons(window.enabled_buttons))
|
2023-01-19 00:38:28 +00:00
|
|
|
.with_decorations(window.decorations)
|
2023-08-20 22:42:07 +00:00
|
|
|
.with_transparent(window.transparent)
|
|
|
|
.with_visible(window.visible);
|
2020-07-26 19:36:01 +00:00
|
|
|
|
2024-03-18 17:41:42 +00:00
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
{
|
|
|
|
use winit::platform::windows::WindowBuilderExtWindows;
|
2024-03-21 18:08:47 +00:00
|
|
|
winit_window_builder = winit_window_builder.with_skip_taskbar(window.skip_taskbar);
|
2024-03-18 17:41:42 +00:00
|
|
|
}
|
|
|
|
|
2024-02-05 13:35:35 +00:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "linux",
|
|
|
|
target_os = "dragonfly",
|
|
|
|
target_os = "freebsd",
|
|
|
|
target_os = "netbsd",
|
|
|
|
target_os = "openbsd",
|
|
|
|
target_os = "windows"
|
|
|
|
))]
|
|
|
|
if let Some(name) = &window.name {
|
|
|
|
#[cfg(all(
|
|
|
|
feature = "wayland",
|
|
|
|
any(
|
|
|
|
target_os = "linux",
|
|
|
|
target_os = "dragonfly",
|
|
|
|
target_os = "freebsd",
|
|
|
|
target_os = "netbsd",
|
|
|
|
target_os = "openbsd"
|
|
|
|
)
|
|
|
|
))]
|
|
|
|
{
|
|
|
|
winit_window_builder = winit::platform::wayland::WindowBuilderExtWayland::with_name(
|
|
|
|
winit_window_builder,
|
|
|
|
name.clone(),
|
|
|
|
"",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(all(
|
|
|
|
feature = "x11",
|
|
|
|
any(
|
|
|
|
target_os = "linux",
|
|
|
|
target_os = "dragonfly",
|
|
|
|
target_os = "freebsd",
|
|
|
|
target_os = "netbsd",
|
|
|
|
target_os = "openbsd"
|
|
|
|
)
|
|
|
|
))]
|
|
|
|
{
|
|
|
|
winit_window_builder = winit::platform::x11::WindowBuilderExtX11::with_name(
|
|
|
|
winit_window_builder,
|
|
|
|
name.clone(),
|
|
|
|
"",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
{
|
|
|
|
winit_window_builder =
|
|
|
|
winit::platform::windows::WindowBuilderExtWindows::with_class_name(
|
|
|
|
winit_window_builder,
|
|
|
|
name.clone(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-19 00:38:28 +00:00
|
|
|
let constraints = window.resize_constraints.check_constraints();
|
2021-03-03 02:56:50 +00:00
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
|
|
|
let winit_window_builder =
|
|
|
|
if constraints.max_width.is_finite() && constraints.max_height.is_finite() {
|
|
|
|
winit_window_builder
|
|
|
|
.with_min_inner_size(min_inner_size)
|
|
|
|
.with_max_inner_size(max_inner_size)
|
|
|
|
} else {
|
|
|
|
winit_window_builder.with_min_inner_size(min_inner_size)
|
|
|
|
};
|
|
|
|
|
2020-09-21 23:12:34 +00:00
|
|
|
#[allow(unused_mut)]
|
2023-01-19 00:38:28 +00:00
|
|
|
let mut winit_window_builder = winit_window_builder.with_title(window.title.as_str());
|
2020-09-21 23:12:34 +00:00
|
|
|
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
|
|
{
|
|
|
|
use wasm_bindgen::JsCast;
|
|
|
|
use winit::platform::web::WindowBuilderExtWebSys;
|
|
|
|
|
2023-01-19 00:38:28 +00:00
|
|
|
if let Some(selector) = &window.canvas {
|
2020-09-21 23:12:34 +00:00
|
|
|
let window = web_sys::window().unwrap();
|
|
|
|
let document = window.document().unwrap();
|
|
|
|
let canvas = document
|
2024-04-20 09:15:42 +00:00
|
|
|
.query_selector(selector)
|
2020-12-02 19:31:16 +00:00
|
|
|
.expect("Cannot query for canvas element.");
|
2020-09-21 23:12:34 +00:00
|
|
|
if let Some(canvas) = canvas {
|
|
|
|
let canvas = canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok();
|
|
|
|
winit_window_builder = winit_window_builder.with_canvas(canvas);
|
|
|
|
} else {
|
2020-12-02 19:31:16 +00:00
|
|
|
panic!("Cannot find element: {}.", selector);
|
2020-09-21 23:12:34 +00:00
|
|
|
}
|
|
|
|
}
|
Allow not preventing default event behaviors on wasm (#7304)
# Objective
On wasm, bevy applications currently prevent any of the normal browser hotkeys from working normally (Ctrl+R, F12, F5, Ctrl+F5, tab, etc.).
Some of those events you may want to override, perhaps you can hold the tab key for showing in-game stats?
However, if you want to make a well-behaved game, you probably don't want to needlessly prevent that behavior unless you have a good reason.
Secondary motivation: Also, consider the workaround presented here to get audio working: https://developer.chrome.com/blog/web-audio-autoplay/#moving-forward ; It won't work (for keydown events) if we stop event propagation.
## Solution
- Winit has a field that allows it to not stop event propagation, expose it on the window settings to allow the user to choose the desired behavior. Default to `true` for backwards compatibility.
---
## Changelog
- Added `Window::prevent_default_event_handling` . This allows bevy apps to not override default browser behavior on hotkeys like F5, F12, Ctrl+R etc.
2023-01-22 23:35:32 +00:00
|
|
|
|
|
|
|
winit_window_builder =
|
2023-12-24 17:44:50 +00:00
|
|
|
winit_window_builder.with_prevent_default(window.prevent_default_event_handling);
|
|
|
|
winit_window_builder = winit_window_builder.with_append(true);
|
2020-09-21 23:12:34 +00:00
|
|
|
}
|
|
|
|
|
2021-07-29 20:52:15 +00:00
|
|
|
let winit_window = winit_window_builder.build(event_loop).unwrap();
|
2023-03-01 22:45:04 +00:00
|
|
|
let name = window.title.clone();
|
2024-03-29 16:02:25 +00:00
|
|
|
prepare_accessibility_for_window(
|
2023-03-01 22:45:04 +00:00
|
|
|
&winit_window,
|
2024-03-29 16:02:25 +00:00
|
|
|
entity,
|
|
|
|
name,
|
|
|
|
accessibility_requested.clone(),
|
|
|
|
adapters,
|
|
|
|
handlers,
|
2023-03-01 22:45:04 +00:00
|
|
|
);
|
2020-08-13 09:27:51 +00:00
|
|
|
|
2023-07-31 21:41:59 +00:00
|
|
|
// Do not set the grab mode on window creation if it's none. It can fail on mobile.
|
2023-01-19 00:38:28 +00:00
|
|
|
if window.cursor.grab_mode != CursorGrabMode::None {
|
|
|
|
attempt_grab(&winit_window, window.cursor.grab_mode);
|
2022-09-06 14:45:44 +00:00
|
|
|
}
|
|
|
|
|
2023-01-19 00:38:28 +00:00
|
|
|
winit_window.set_cursor_visible(window.cursor.visible);
|
2023-03-13 15:31:13 +00:00
|
|
|
|
2023-07-31 21:41:59 +00:00
|
|
|
// Do not set the cursor hittest on window creation if it's false, as it will always fail on
|
|
|
|
// some platforms and log an unfixable warning.
|
2023-03-13 15:31:13 +00:00
|
|
|
if !window.cursor.hit_test {
|
|
|
|
if let Err(err) = winit_window.set_cursor_hittest(window.cursor.hit_test) {
|
|
|
|
warn!(
|
|
|
|
"Could not set cursor hit test for window {:?}: {:?}",
|
|
|
|
window.title, err
|
|
|
|
);
|
|
|
|
}
|
2023-03-09 05:39:49 +00:00
|
|
|
}
|
2020-10-16 21:07:01 +00:00
|
|
|
|
2023-01-19 00:38:28 +00:00
|
|
|
self.entity_to_winit.insert(entity, winit_window.id());
|
|
|
|
self.winit_to_entity.insert(winit_window.id(), entity);
|
2020-03-30 21:53:32 +00:00
|
|
|
|
2023-01-19 00:38:28 +00:00
|
|
|
self.windows
|
|
|
|
.entry(winit_window.id())
|
|
|
|
.insert(winit_window)
|
|
|
|
.into_mut()
|
2020-03-30 21:53:32 +00:00
|
|
|
}
|
|
|
|
|
2023-01-19 00:38:28 +00:00
|
|
|
/// Get the winit window that is associated with our entity.
|
|
|
|
pub fn get_window(&self, entity: Entity) -> Option<&winit::window::Window> {
|
|
|
|
self.entity_to_winit
|
|
|
|
.get(&entity)
|
|
|
|
.and_then(|winit_id| self.windows.get(winit_id))
|
2020-03-30 21:53:32 +00:00
|
|
|
}
|
|
|
|
|
2023-01-19 00:38:28 +00:00
|
|
|
/// Get the entity associated with the winit window id.
|
|
|
|
///
|
|
|
|
/// This is mostly just an intermediary step between us and winit.
|
|
|
|
pub fn get_window_entity(&self, winit_id: winit::window::WindowId) -> Option<Entity> {
|
|
|
|
self.winit_to_entity.get(&winit_id).cloned()
|
2020-03-30 21:53:32 +00:00
|
|
|
}
|
2022-05-05 13:35:43 +00:00
|
|
|
|
2023-01-19 00:38:28 +00:00
|
|
|
/// Remove a window from winit.
|
|
|
|
///
|
|
|
|
/// This should mostly just be called when the window is closing.
|
|
|
|
pub fn remove_window(&mut self, entity: Entity) -> Option<winit::window::Window> {
|
|
|
|
let winit_id = self.entity_to_winit.remove(&entity)?;
|
2024-03-29 01:37:13 +00:00
|
|
|
self.winit_to_entity.remove(&winit_id);
|
2022-05-05 13:35:43 +00:00
|
|
|
self.windows.remove(&winit_id)
|
|
|
|
}
|
2020-03-30 21:53:32 +00:00
|
|
|
}
|
2021-11-30 23:51:11 +00:00
|
|
|
|
2023-03-21 19:59:30 +00:00
|
|
|
/// Gets the "best" video mode which fits the given dimensions.
|
|
|
|
///
|
|
|
|
/// The heuristic for "best" prioritizes width, height, and refresh rate in that order.
|
2020-10-15 18:42:19 +00:00
|
|
|
pub fn get_fitting_videomode(
|
2023-11-28 23:43:40 +00:00
|
|
|
monitor: &MonitorHandle,
|
2020-10-15 18:42:19 +00:00
|
|
|
width: u32,
|
|
|
|
height: u32,
|
2020-08-13 08:47:40 +00:00
|
|
|
) -> winit::monitor::VideoMode {
|
|
|
|
let mut modes = monitor.video_modes().collect::<Vec<_>>();
|
|
|
|
|
|
|
|
fn abs_diff(a: u32, b: u32) -> u32 {
|
|
|
|
if a > b {
|
|
|
|
return a - b;
|
|
|
|
}
|
|
|
|
b - a
|
|
|
|
}
|
|
|
|
|
|
|
|
modes.sort_by(|a, b| {
|
|
|
|
use std::cmp::Ordering::*;
|
2020-10-15 18:42:19 +00:00
|
|
|
match abs_diff(a.size().width, width).cmp(&abs_diff(b.size().width, width)) {
|
2020-08-13 08:47:40 +00:00
|
|
|
Equal => {
|
2020-10-15 18:42:19 +00:00
|
|
|
match abs_diff(a.size().height, height).cmp(&abs_diff(b.size().height, height)) {
|
Update `wgpu` to 0.14.0, `naga` to `0.10.0`, `winit` to 0.27.4, `raw-window-handle` to 0.5.0, `ndk` to 0.7 (#6218)
# Objective
- Update `wgpu` to 0.14.0, `naga` to `0.10.0`, `winit` to 0.27.4, `raw-window-handle` to 0.5.0, `ndk` to 0.7.
## Solution
---
## Changelog
### Changed
- Changed `RawWindowHandleWrapper` to `RawHandleWrapper` which wraps both `RawWindowHandle` and `RawDisplayHandle`, which satisfies the `impl HasRawWindowHandle and HasRawDisplayHandle` that `wgpu` 0.14.0 requires.
- Changed `bevy_window::WindowDescriptor`'s `cursor_locked` to `cursor_grab_mode`, change its type from `bool` to `bevy_window::CursorGrabMode`.
## Migration Guide
- Adjust usage of `bevy_window::WindowDescriptor`'s `cursor_locked` to `cursor_grab_mode`, and adjust its type from `bool` to `bevy_window::CursorGrabMode`.
2022-10-19 17:40:23 +00:00
|
|
|
Equal => b
|
|
|
|
.refresh_rate_millihertz()
|
|
|
|
.cmp(&a.refresh_rate_millihertz()),
|
2020-08-13 08:47:40 +00:00
|
|
|
default => default,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default => default,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
modes.first().unwrap().clone()
|
|
|
|
}
|
|
|
|
|
2023-03-21 19:59:30 +00:00
|
|
|
/// Gets the "best" videomode from a monitor.
|
|
|
|
///
|
|
|
|
/// The heuristic for "best" prioritizes width, height, and refresh rate in that order.
|
2023-11-28 23:43:40 +00:00
|
|
|
pub fn get_best_videomode(monitor: &MonitorHandle) -> winit::monitor::VideoMode {
|
2020-08-13 08:47:40 +00:00
|
|
|
let mut modes = monitor.video_modes().collect::<Vec<_>>();
|
|
|
|
modes.sort_by(|a, b| {
|
|
|
|
use std::cmp::Ordering::*;
|
|
|
|
match b.size().width.cmp(&a.size().width) {
|
|
|
|
Equal => match b.size().height.cmp(&a.size().height) {
|
Update `wgpu` to 0.14.0, `naga` to `0.10.0`, `winit` to 0.27.4, `raw-window-handle` to 0.5.0, `ndk` to 0.7 (#6218)
# Objective
- Update `wgpu` to 0.14.0, `naga` to `0.10.0`, `winit` to 0.27.4, `raw-window-handle` to 0.5.0, `ndk` to 0.7.
## Solution
---
## Changelog
### Changed
- Changed `RawWindowHandleWrapper` to `RawHandleWrapper` which wraps both `RawWindowHandle` and `RawDisplayHandle`, which satisfies the `impl HasRawWindowHandle and HasRawDisplayHandle` that `wgpu` 0.14.0 requires.
- Changed `bevy_window::WindowDescriptor`'s `cursor_locked` to `cursor_grab_mode`, change its type from `bool` to `bevy_window::CursorGrabMode`.
## Migration Guide
- Adjust usage of `bevy_window::WindowDescriptor`'s `cursor_locked` to `cursor_grab_mode`, and adjust its type from `bool` to `bevy_window::CursorGrabMode`.
2022-10-19 17:40:23 +00:00
|
|
|
Equal => b
|
|
|
|
.refresh_rate_millihertz()
|
|
|
|
.cmp(&a.refresh_rate_millihertz()),
|
2020-08-13 08:47:40 +00:00
|
|
|
default => default,
|
|
|
|
},
|
|
|
|
default => default,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
modes.first().unwrap().clone()
|
|
|
|
}
|
2023-01-19 00:38:28 +00:00
|
|
|
|
|
|
|
pub(crate) fn attempt_grab(winit_window: &winit::window::Window, grab_mode: CursorGrabMode) {
|
|
|
|
let grab_result = match grab_mode {
|
2023-11-28 23:43:40 +00:00
|
|
|
CursorGrabMode::None => winit_window.set_cursor_grab(winit::window::CursorGrabMode::None),
|
|
|
|
CursorGrabMode::Confined => winit_window
|
2023-01-19 00:38:28 +00:00
|
|
|
.set_cursor_grab(winit::window::CursorGrabMode::Confined)
|
|
|
|
.or_else(|_e| winit_window.set_cursor_grab(winit::window::CursorGrabMode::Locked)),
|
2023-11-28 23:43:40 +00:00
|
|
|
CursorGrabMode::Locked => winit_window
|
2023-01-19 00:38:28 +00:00
|
|
|
.set_cursor_grab(winit::window::CursorGrabMode::Locked)
|
|
|
|
.or_else(|_e| winit_window.set_cursor_grab(winit::window::CursorGrabMode::Confined)),
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Err(err) = grab_result {
|
|
|
|
let err_desc = match grab_mode {
|
2023-11-28 23:43:40 +00:00
|
|
|
CursorGrabMode::Confined | CursorGrabMode::Locked => "grab",
|
|
|
|
CursorGrabMode::None => "ungrab",
|
2023-01-19 00:38:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
bevy_utils::tracing::error!("Unable to {} cursor: {}", err_desc, err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-21 19:59:30 +00:00
|
|
|
/// Compute the physical window position for a given [`WindowPosition`].
|
2023-01-19 00:38:28 +00:00
|
|
|
// Ideally we could generify this across window backends, but we only really have winit atm
|
|
|
|
// so whatever.
|
|
|
|
pub fn winit_window_position(
|
|
|
|
position: &WindowPosition,
|
|
|
|
resolution: &WindowResolution,
|
|
|
|
mut available_monitors: impl Iterator<Item = MonitorHandle>,
|
|
|
|
primary_monitor: Option<MonitorHandle>,
|
|
|
|
current_monitor: Option<MonitorHandle>,
|
|
|
|
) -> Option<PhysicalPosition<i32>> {
|
|
|
|
match position {
|
|
|
|
WindowPosition::Automatic => {
|
|
|
|
/* Window manager will handle position */
|
|
|
|
None
|
|
|
|
}
|
|
|
|
WindowPosition::Centered(monitor_selection) => {
|
|
|
|
use bevy_window::MonitorSelection::*;
|
|
|
|
let maybe_monitor = match monitor_selection {
|
|
|
|
Current => {
|
|
|
|
if current_monitor.is_none() {
|
|
|
|
warn!("Can't select current monitor on window creation or cannot find current monitor!");
|
|
|
|
}
|
|
|
|
current_monitor
|
|
|
|
}
|
|
|
|
Primary => primary_monitor,
|
|
|
|
Index(n) => available_monitors.nth(*n),
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(monitor) = maybe_monitor {
|
|
|
|
let screen_size = monitor.size();
|
|
|
|
|
2023-07-31 21:41:59 +00:00
|
|
|
// We use the monitors scale factor here since `WindowResolution.scale_factor` is
|
|
|
|
// not yet populated when windows are created during plugin setup.
|
2023-06-21 14:00:35 +00:00
|
|
|
let scale_factor = monitor.scale_factor();
|
2023-01-19 00:38:28 +00:00
|
|
|
|
|
|
|
// Logical to physical window size
|
|
|
|
let (width, height): (u32, u32) =
|
|
|
|
LogicalSize::new(resolution.width(), resolution.height())
|
|
|
|
.to_physical::<u32>(scale_factor)
|
|
|
|
.into();
|
|
|
|
|
|
|
|
let position = PhysicalPosition {
|
|
|
|
x: screen_size.width.saturating_sub(width) as f64 / 2.
|
|
|
|
+ monitor.position().x as f64,
|
|
|
|
y: screen_size.height.saturating_sub(height) as f64 / 2.
|
|
|
|
+ monitor.position().y as f64,
|
|
|
|
};
|
|
|
|
|
|
|
|
Some(position.cast::<i32>())
|
|
|
|
} else {
|
|
|
|
warn!("Couldn't get monitor selected with: {monitor_selection:?}");
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
WindowPosition::At(position) => {
|
|
|
|
Some(PhysicalPosition::new(position[0] as f64, position[1] as f64).cast::<i32>())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|