From 28c16b9713e12efa3a026616e4f763b599ed65a0 Mon Sep 17 00:00:00 2001 From: ira Date: Tue, 6 Sep 2022 14:45:44 +0000 Subject: [PATCH] Support monitor selection for all window modes. (#5878) # Objective Support monitor selection for all window modes. Fixes #5875. ## Changelog * Moved `MonitorSelection` out of `WindowPosition::Centered`, into `WindowDescriptor`. * `WindowPosition::At` is now relative to the monitor instead of being in 'desktop space'. * Renamed `MonitorSelection::Number` to `MonitorSelection::Index` for clarity. * Added `WindowMode` to the prelude. * `Window::set_position` is now relative to a monitor and takes a `MonitorSelection` as argument. ## Migration Guide `MonitorSelection` was moved out of `WindowPosition::Centered`, into `WindowDescriptor`. `MonitorSelection::Number` was renamed to `MonitorSelection::Index`. ```rust // Before .insert_resource(WindowDescriptor { position: WindowPosition::Centered(MonitorSelection::Number(1)), ..default() }) // After .insert_resource(WindowDescriptor { monitor: MonitorSelection::Index(1), position: WindowPosition::Centered, ..default() }) ``` `Window::set_position` now takes a `MonitorSelection` as argument. ```rust window.set_position(MonitorSelection::Current, position); ``` Co-authored-by: devil-ira --- crates/bevy_window/src/lib.rs | 3 +- crates/bevy_window/src/window.rs | 49 +++++--- crates/bevy_winit/src/lib.rs | 45 +++++--- crates/bevy_winit/src/winit_windows.rs | 151 +++++++++++++------------ 4 files changed, 141 insertions(+), 107 deletions(-) diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index 005675f2ae..2961d03c59 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -17,7 +17,8 @@ pub mod prelude { #[doc(hidden)] pub use crate::{ CursorEntered, CursorIcon, CursorLeft, CursorMoved, FileDragAndDrop, MonitorSelection, - ReceivedCharacter, Window, WindowDescriptor, WindowMoved, WindowPosition, Windows, + ReceivedCharacter, Window, WindowDescriptor, WindowMode, WindowMoved, WindowPosition, + Windows, }; } diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 2ab7761b92..11e8bad32c 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -272,11 +272,12 @@ pub enum WindowCommand { SetMinimized { minimized: bool, }, - /// Set the window's position on the screen. + /// Set the window's position on the selected monitor. SetPosition { + monitor_selection: MonitorSelection, position: IVec2, }, - /// Modifies the position of the window to be in the center of the current monitor + /// Sets the position of the window to be in the center of the selected monitor. Center(MonitorSelection), /// Set the window's [`WindowResizeConstraints`] SetResizeConstraints { @@ -416,12 +417,9 @@ impl Window { .push(WindowCommand::SetMinimized { minimized }); } - /// Modifies the position of the window in physical pixels. + /// Sets the `position` of the window on the selected `monitor` in physical pixels. /// - /// Note that the top-left hand corner of the desktop is not necessarily the same as the screen. - /// If the user uses a desktop with multiple monitors, the top-left hand corner of the - /// desktop is the top-left hand corner of the monitor at the top-left of the desktop. This - /// automatically un-maximizes the window if it's maximized. + /// This automatically un-maximizes the window if it's maximized. /// /// # Platform-specific /// @@ -430,9 +428,11 @@ impl Window { /// - Web: Sets the top-left coordinates relative to the viewport. /// - Android / Wayland: Unsupported. #[inline] - pub fn set_position(&mut self, position: IVec2) { - self.command_queue - .push(WindowCommand::SetPosition { position }); + pub fn set_position(&mut self, monitor: MonitorSelection, position: IVec2) { + self.command_queue.push(WindowCommand::SetPosition { + monitor_selection: monitor, + position, + }); } /// Modifies the position of the window to be in the center of the current monitor @@ -747,15 +747,17 @@ impl Window { /// Defines where window should be placed at on creation. #[derive(Debug, Clone, Copy)] pub enum WindowPosition { - /// Position will be set by the window manager + /// The position will be set by the window manager. Automatic, - /// Window will be centered on the selected monitor + /// Center the window on the monitor. /// - /// Note that this does not account for window decorations. - Centered(MonitorSelection), - /// The window's top-left corner will be placed at the specified position (in pixels) + /// The monitor to center the window on can be selected with the `monitor` field in `WindowDescriptor`. + Centered, + /// The window's top-left corner will be placed at the specified position in pixels. /// - /// (0,0) represents top-left corner of screen space. + /// (0,0) represents top-left corner of the monitor. + /// + /// The monitor to position the window on can be selected with the `monitor` field in `WindowDescriptor`. At(Vec2), } @@ -763,11 +765,13 @@ pub enum WindowPosition { #[derive(Debug, Clone, Copy)] pub enum MonitorSelection { /// Uses current monitor of the window. + /// + /// Will fall back to the system default if the window has not yet been created. Current, /// Uses primary monitor of the system. Primary, /// Uses monitor with the specified index. - Number(usize), + Index(usize), } /// Describes the information needed for creating a window. @@ -789,7 +793,15 @@ pub struct WindowDescriptor { /// May vary from the physical height due to different pixel density on different monitors. pub height: f32, /// The position on the screen that the window will be placed at. + /// + /// The monitor to place the window on can be selected with the `monitor` field. + /// + /// Ignored if `mode` is set to something other than [`WindowMode::Windowed`] + /// + /// `WindowPosition::Automatic` will be overridden with `WindowPosition::At(Vec2::ZERO)` if a specific monitor is selected. pub position: WindowPosition, + /// The monitor to place the window on. + pub monitor: MonitorSelection, /// Sets minimum and maximum resize limits. pub resize_constraints: WindowResizeConstraints, /// Overrides the window's ratio of physical pixels to logical pixels. @@ -819,6 +831,8 @@ pub struct WindowDescriptor { /// Sets whether the window locks the cursor inside its borders when the window has focus. pub cursor_locked: bool, /// Sets the [`WindowMode`](crate::WindowMode). + /// + /// The monitor to go fullscreen on can be selected with the `monitor` field. pub mode: WindowMode, /// Sets whether the background of the window should be transparent. /// @@ -854,6 +868,7 @@ impl Default for WindowDescriptor { width: 1280., height: 720., position: WindowPosition::Automatic, + monitor: MonitorSelection::Current, resize_constraints: WindowResizeConstraints::default(), scale_factor_override: None, present_mode: PresentMode::Fifo, diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index b48dd44114..4ca444b37f 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -31,7 +31,7 @@ use bevy_window::{ }; use winit::{ - dpi::{LogicalSize, PhysicalPosition}, + dpi::{LogicalPosition, LogicalSize, PhysicalPosition}, event::{self, DeviceEvent, Event, StartCause, WindowEvent}, event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget}, }; @@ -149,7 +149,7 @@ fn change_window( let window = winit_windows.get_window(id).unwrap(); let inner_size = window.inner_size().to_logical::(window.scale_factor()); window - .set_cursor_position(winit::dpi::LogicalPosition::new( + .set_cursor_position(LogicalPosition::new( position.x, inner_size.height - position.y, )) @@ -163,12 +163,26 @@ fn change_window( let window = winit_windows.get_window(id).unwrap(); window.set_minimized(minimized); } - bevy_window::WindowCommand::SetPosition { position } => { + bevy_window::WindowCommand::SetPosition { + monitor_selection, + position, + } => { let window = winit_windows.get_window(id).unwrap(); - window.set_outer_position(PhysicalPosition { - x: position[0], - y: position[1], - }); + + use bevy_window::MonitorSelection::*; + let maybe_monitor = match monitor_selection { + Current => window.current_monitor(), + Primary => window.primary_monitor(), + Index(i) => window.available_monitors().nth(i), + }; + if let Some(monitor) = maybe_monitor { + let monitor_position = DVec2::from(<(_, _)>::from(monitor.position())); + let position = monitor_position + position.as_dvec2(); + + window.set_outer_position(LogicalPosition::new(position.x, position.y)); + } else { + warn!("Couldn't get monitor selected with: {monitor_selection:?}"); + } } bevy_window::WindowCommand::Center(monitor_selection) => { let window = winit_windows.get_window(id).unwrap(); @@ -177,19 +191,20 @@ fn change_window( let maybe_monitor = match monitor_selection { Current => window.current_monitor(), Primary => window.primary_monitor(), - Number(n) => window.available_monitors().nth(n), + Index(i) => window.available_monitors().nth(i), }; if let Some(monitor) = maybe_monitor { - let screen_size = monitor.size(); + let monitor_size = monitor.size(); + let monitor_position = monitor.position().cast::(); let window_size = window.outer_size(); window.set_outer_position(PhysicalPosition { - x: screen_size.width.saturating_sub(window_size.width) as f64 / 2. - + monitor.position().x as f64, - y: screen_size.height.saturating_sub(window_size.height) as f64 / 2. - + monitor.position().y as f64, + x: monitor_size.width.saturating_sub(window_size.width) as f64 / 2. + + monitor_position.x, + y: monitor_size.height.saturating_sub(window_size.height) as f64 / 2. + + monitor_position.y, }); } else { warn!("Couldn't get monitor selected with: {monitor_selection:?}"); @@ -578,12 +593,12 @@ pub fn winit_runner_with(mut app: App) { } } event::Event::DeviceEvent { - event: DeviceEvent::MouseMotion { delta }, + event: DeviceEvent::MouseMotion { delta: (x, y) }, .. } => { let mut mouse_motion_events = app.world.resource_mut::>(); mouse_motion_events.send(MouseMotion { - delta: Vec2::new(delta.0 as f32, delta.1 as f32), + delta: DVec2 { x, y }.as_vec2(), }); } event::Event::Suspended => { diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index 3eb514daad..4467574874 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -1,8 +1,11 @@ -use bevy_math::IVec2; -use bevy_utils::{tracing::warn, HashMap}; -use bevy_window::{Window, WindowDescriptor, WindowId, WindowMode}; +use bevy_math::{DVec2, IVec2}; +use bevy_utils::HashMap; +use bevy_window::{MonitorSelection, Window, WindowDescriptor, WindowId, WindowMode}; use raw_window_handle::HasRawWindowHandle; -use winit::dpi::{LogicalPosition, LogicalSize, PhysicalPosition}; +use winit::{ + dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}, + window::Fullscreen, +}; #[derive(Debug, Default)] pub struct WinitWindows { @@ -24,86 +27,43 @@ impl WinitWindows { ) -> Window { let mut winit_window_builder = winit::window::WindowBuilder::new(); + let &WindowDescriptor { + width, + height, + position, + monitor, + scale_factor_override, + .. + } = window_descriptor; + + let logical_size = LogicalSize::new(width, height); + + let monitor = match monitor { + MonitorSelection::Current => None, + MonitorSelection::Primary => event_loop.primary_monitor(), + MonitorSelection::Index(i) => event_loop.available_monitors().nth(i), + }; + + let selected_or_primary_monitor = monitor.clone().or_else(|| event_loop.primary_monitor()); + winit_window_builder = match window_descriptor.mode { - WindowMode::BorderlessFullscreen => winit_window_builder.with_fullscreen(Some( - winit::window::Fullscreen::Borderless(event_loop.primary_monitor()), + WindowMode::BorderlessFullscreen => winit_window_builder + .with_fullscreen(Some(Fullscreen::Borderless(selected_or_primary_monitor))), + WindowMode::Fullscreen => winit_window_builder.with_fullscreen(Some( + Fullscreen::Exclusive(get_best_videomode(&selected_or_primary_monitor.unwrap())), )), - WindowMode::Fullscreen => { - winit_window_builder.with_fullscreen(Some(winit::window::Fullscreen::Exclusive( - get_best_videomode(&event_loop.primary_monitor().unwrap()), - ))) - } WindowMode::SizedFullscreen => winit_window_builder.with_fullscreen(Some( - winit::window::Fullscreen::Exclusive(get_fitting_videomode( - &event_loop.primary_monitor().unwrap(), + Fullscreen::Exclusive(get_fitting_videomode( + &selected_or_primary_monitor.unwrap(), window_descriptor.width as u32, window_descriptor.height as u32, )), )), _ => { - let WindowDescriptor { - width, - height, - position, - scale_factor_override, - .. - } = window_descriptor; - - use bevy_window::WindowPosition::*; - match position { - Automatic => { /* Window manager will handle position */ } - Centered(monitor_selection) => { - use bevy_window::MonitorSelection::*; - let maybe_monitor = match monitor_selection { - Current => { - warn!("Can't select current monitor on window creation!"); - None - } - Primary => event_loop.primary_monitor(), - Number(n) => event_loop.available_monitors().nth(*n), - }; - - if let Some(monitor) = maybe_monitor { - let screen_size = monitor.size(); - - let scale_factor = monitor.scale_factor(); - - // Logical to physical window size - let (width, height): (u32, u32) = LogicalSize::new(*width, *height) - .to_physical::(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, - }; - - winit_window_builder = winit_window_builder.with_position(position); - } else { - warn!("Couldn't get monitor selected with: {monitor_selection:?}"); - } - } - At(position) => { - if let Some(sf) = scale_factor_override { - winit_window_builder = winit_window_builder.with_position( - LogicalPosition::new(position[0] as f64, position[1] as f64) - .to_physical::(*sf), - ); - } else { - winit_window_builder = winit_window_builder.with_position( - LogicalPosition::new(position[0] as f64, position[1] as f64), - ); - } - } - } - if let Some(sf) = scale_factor_override { - winit_window_builder - .with_inner_size(LogicalSize::new(*width, *height).to_physical::(*sf)) + winit_window_builder.with_inner_size(logical_size.to_physical::(sf)) } else { - winit_window_builder.with_inner_size(LogicalSize::new(*width, *height)) + winit_window_builder.with_inner_size(logical_size) } } .with_resizable(window_descriptor.resizable) @@ -155,6 +115,49 @@ impl WinitWindows { let winit_window = winit_window_builder.build(event_loop).unwrap(); + if window_descriptor.mode == WindowMode::Windowed { + use bevy_window::WindowPosition::*; + match position { + Automatic => { + if let Some(monitor) = monitor { + winit_window.set_outer_position(monitor.position()); + } + } + Centered => { + if let Some(monitor) = monitor.or_else(|| winit_window.current_monitor()) { + let monitor_position = monitor.position().cast::(); + let size = monitor.size(); + + // Logical to physical window size + let PhysicalSize:: { width, height } = + logical_size.to_physical(monitor.scale_factor()); + + let position = PhysicalPosition { + x: size.width.saturating_sub(width) as f64 / 2. + monitor_position.x, + y: size.height.saturating_sub(height) as f64 / 2. + monitor_position.y, + }; + + winit_window.set_outer_position(position); + } + } + At(position) => { + if let Some(monitor) = monitor.or_else(|| winit_window.current_monitor()) { + let monitor_position = DVec2::from(<(_, _)>::from(monitor.position())); + let position = monitor_position + position.as_dvec2(); + + if let Some(sf) = scale_factor_override { + winit_window.set_outer_position( + LogicalPosition::new(position.x, position.y).to_physical::(sf), + ); + } else { + winit_window + .set_outer_position(LogicalPosition::new(position.x, position.y)); + } + } + } + } + } + if window_descriptor.cursor_locked { match winit_window.set_cursor_grab(true) { Ok(_) | Err(winit::error::ExternalError::NotSupported(_)) => {}