Skip to content

Commit 28c16b9

Browse files
committed
Support monitor selection for all window modes. (bevyengine#5878)
# Objective Support monitor selection for all window modes. Fixes bevyengine#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 <justthecooldude@gmail.com>
1 parent 092bb71 commit 28c16b9

File tree

4 files changed

+141
-107
lines changed

4 files changed

+141
-107
lines changed

crates/bevy_window/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ pub mod prelude {
1717
#[doc(hidden)]
1818
pub use crate::{
1919
CursorEntered, CursorIcon, CursorLeft, CursorMoved, FileDragAndDrop, MonitorSelection,
20-
ReceivedCharacter, Window, WindowDescriptor, WindowMoved, WindowPosition, Windows,
20+
ReceivedCharacter, Window, WindowDescriptor, WindowMode, WindowMoved, WindowPosition,
21+
Windows,
2122
};
2223
}
2324

crates/bevy_window/src/window.rs

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -272,11 +272,12 @@ pub enum WindowCommand {
272272
SetMinimized {
273273
minimized: bool,
274274
},
275-
/// Set the window's position on the screen.
275+
/// Set the window's position on the selected monitor.
276276
SetPosition {
277+
monitor_selection: MonitorSelection,
277278
position: IVec2,
278279
},
279-
/// Modifies the position of the window to be in the center of the current monitor
280+
/// Sets the position of the window to be in the center of the selected monitor.
280281
Center(MonitorSelection),
281282
/// Set the window's [`WindowResizeConstraints`]
282283
SetResizeConstraints {
@@ -416,12 +417,9 @@ impl Window {
416417
.push(WindowCommand::SetMinimized { minimized });
417418
}
418419

419-
/// Modifies the position of the window in physical pixels.
420+
/// Sets the `position` of the window on the selected `monitor` in physical pixels.
420421
///
421-
/// Note that the top-left hand corner of the desktop is not necessarily the same as the screen.
422-
/// If the user uses a desktop with multiple monitors, the top-left hand corner of the
423-
/// desktop is the top-left hand corner of the monitor at the top-left of the desktop. This
424-
/// automatically un-maximizes the window if it's maximized.
422+
/// This automatically un-maximizes the window if it's maximized.
425423
///
426424
/// # Platform-specific
427425
///
@@ -430,9 +428,11 @@ impl Window {
430428
/// - Web: Sets the top-left coordinates relative to the viewport.
431429
/// - Android / Wayland: Unsupported.
432430
#[inline]
433-
pub fn set_position(&mut self, position: IVec2) {
434-
self.command_queue
435-
.push(WindowCommand::SetPosition { position });
431+
pub fn set_position(&mut self, monitor: MonitorSelection, position: IVec2) {
432+
self.command_queue.push(WindowCommand::SetPosition {
433+
monitor_selection: monitor,
434+
position,
435+
});
436436
}
437437

438438
/// Modifies the position of the window to be in the center of the current monitor
@@ -747,27 +747,31 @@ impl Window {
747747
/// Defines where window should be placed at on creation.
748748
#[derive(Debug, Clone, Copy)]
749749
pub enum WindowPosition {
750-
/// Position will be set by the window manager
750+
/// The position will be set by the window manager.
751751
Automatic,
752-
/// Window will be centered on the selected monitor
752+
/// Center the window on the monitor.
753+
///
754+
/// The monitor to center the window on can be selected with the `monitor` field in `WindowDescriptor`.
755+
Centered,
756+
/// The window's top-left corner will be placed at the specified position in pixels.
753757
///
754-
/// Note that this does not account for window decorations.
755-
Centered(MonitorSelection),
756-
/// The window's top-left corner will be placed at the specified position (in pixels)
758+
/// (0,0) represents top-left corner of the monitor.
757759
///
758-
/// (0,0) represents top-left corner of screen space.
760+
/// The monitor to position the window on can be selected with the `monitor` field in `WindowDescriptor`.
759761
At(Vec2),
760762
}
761763

762764
/// Defines which monitor to use.
763765
#[derive(Debug, Clone, Copy)]
764766
pub enum MonitorSelection {
765767
/// Uses current monitor of the window.
768+
///
769+
/// Will fall back to the system default if the window has not yet been created.
766770
Current,
767771
/// Uses primary monitor of the system.
768772
Primary,
769773
/// Uses monitor with the specified index.
770-
Number(usize),
774+
Index(usize),
771775
}
772776

773777
/// Describes the information needed for creating a window.
@@ -789,7 +793,15 @@ pub struct WindowDescriptor {
789793
/// May vary from the physical height due to different pixel density on different monitors.
790794
pub height: f32,
791795
/// The position on the screen that the window will be placed at.
796+
///
797+
/// The monitor to place the window on can be selected with the `monitor` field.
798+
///
799+
/// Ignored if `mode` is set to something other than [`WindowMode::Windowed`]
800+
///
801+
/// `WindowPosition::Automatic` will be overridden with `WindowPosition::At(Vec2::ZERO)` if a specific monitor is selected.
792802
pub position: WindowPosition,
803+
/// The monitor to place the window on.
804+
pub monitor: MonitorSelection,
793805
/// Sets minimum and maximum resize limits.
794806
pub resize_constraints: WindowResizeConstraints,
795807
/// Overrides the window's ratio of physical pixels to logical pixels.
@@ -819,6 +831,8 @@ pub struct WindowDescriptor {
819831
/// Sets whether the window locks the cursor inside its borders when the window has focus.
820832
pub cursor_locked: bool,
821833
/// Sets the [`WindowMode`](crate::WindowMode).
834+
///
835+
/// The monitor to go fullscreen on can be selected with the `monitor` field.
822836
pub mode: WindowMode,
823837
/// Sets whether the background of the window should be transparent.
824838
///
@@ -854,6 +868,7 @@ impl Default for WindowDescriptor {
854868
width: 1280.,
855869
height: 720.,
856870
position: WindowPosition::Automatic,
871+
monitor: MonitorSelection::Current,
857872
resize_constraints: WindowResizeConstraints::default(),
858873
scale_factor_override: None,
859874
present_mode: PresentMode::Fifo,

crates/bevy_winit/src/lib.rs

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use bevy_window::{
3131
};
3232

3333
use winit::{
34-
dpi::{LogicalSize, PhysicalPosition},
34+
dpi::{LogicalPosition, LogicalSize, PhysicalPosition},
3535
event::{self, DeviceEvent, Event, StartCause, WindowEvent},
3636
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
3737
};
@@ -149,7 +149,7 @@ fn change_window(
149149
let window = winit_windows.get_window(id).unwrap();
150150
let inner_size = window.inner_size().to_logical::<f32>(window.scale_factor());
151151
window
152-
.set_cursor_position(winit::dpi::LogicalPosition::new(
152+
.set_cursor_position(LogicalPosition::new(
153153
position.x,
154154
inner_size.height - position.y,
155155
))
@@ -163,12 +163,26 @@ fn change_window(
163163
let window = winit_windows.get_window(id).unwrap();
164164
window.set_minimized(minimized);
165165
}
166-
bevy_window::WindowCommand::SetPosition { position } => {
166+
bevy_window::WindowCommand::SetPosition {
167+
monitor_selection,
168+
position,
169+
} => {
167170
let window = winit_windows.get_window(id).unwrap();
168-
window.set_outer_position(PhysicalPosition {
169-
x: position[0],
170-
y: position[1],
171-
});
171+
172+
use bevy_window::MonitorSelection::*;
173+
let maybe_monitor = match monitor_selection {
174+
Current => window.current_monitor(),
175+
Primary => window.primary_monitor(),
176+
Index(i) => window.available_monitors().nth(i),
177+
};
178+
if let Some(monitor) = maybe_monitor {
179+
let monitor_position = DVec2::from(<(_, _)>::from(monitor.position()));
180+
let position = monitor_position + position.as_dvec2();
181+
182+
window.set_outer_position(LogicalPosition::new(position.x, position.y));
183+
} else {
184+
warn!("Couldn't get monitor selected with: {monitor_selection:?}");
185+
}
172186
}
173187
bevy_window::WindowCommand::Center(monitor_selection) => {
174188
let window = winit_windows.get_window(id).unwrap();
@@ -177,19 +191,20 @@ fn change_window(
177191
let maybe_monitor = match monitor_selection {
178192
Current => window.current_monitor(),
179193
Primary => window.primary_monitor(),
180-
Number(n) => window.available_monitors().nth(n),
194+
Index(i) => window.available_monitors().nth(i),
181195
};
182196

183197
if let Some(monitor) = maybe_monitor {
184-
let screen_size = monitor.size();
198+
let monitor_size = monitor.size();
199+
let monitor_position = monitor.position().cast::<f64>();
185200

186201
let window_size = window.outer_size();
187202

188203
window.set_outer_position(PhysicalPosition {
189-
x: screen_size.width.saturating_sub(window_size.width) as f64 / 2.
190-
+ monitor.position().x as f64,
191-
y: screen_size.height.saturating_sub(window_size.height) as f64 / 2.
192-
+ monitor.position().y as f64,
204+
x: monitor_size.width.saturating_sub(window_size.width) as f64 / 2.
205+
+ monitor_position.x,
206+
y: monitor_size.height.saturating_sub(window_size.height) as f64 / 2.
207+
+ monitor_position.y,
193208
});
194209
} else {
195210
warn!("Couldn't get monitor selected with: {monitor_selection:?}");
@@ -578,12 +593,12 @@ pub fn winit_runner_with(mut app: App) {
578593
}
579594
}
580595
event::Event::DeviceEvent {
581-
event: DeviceEvent::MouseMotion { delta },
596+
event: DeviceEvent::MouseMotion { delta: (x, y) },
582597
..
583598
} => {
584599
let mut mouse_motion_events = app.world.resource_mut::<Events<MouseMotion>>();
585600
mouse_motion_events.send(MouseMotion {
586-
delta: Vec2::new(delta.0 as f32, delta.1 as f32),
601+
delta: DVec2 { x, y }.as_vec2(),
587602
});
588603
}
589604
event::Event::Suspended => {

crates/bevy_winit/src/winit_windows.rs

Lines changed: 77 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
use bevy_math::IVec2;
2-
use bevy_utils::{tracing::warn, HashMap};
3-
use bevy_window::{Window, WindowDescriptor, WindowId, WindowMode};
1+
use bevy_math::{DVec2, IVec2};
2+
use bevy_utils::HashMap;
3+
use bevy_window::{MonitorSelection, Window, WindowDescriptor, WindowId, WindowMode};
44
use raw_window_handle::HasRawWindowHandle;
5-
use winit::dpi::{LogicalPosition, LogicalSize, PhysicalPosition};
5+
use winit::{
6+
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize},
7+
window::Fullscreen,
8+
};
69

710
#[derive(Debug, Default)]
811
pub struct WinitWindows {
@@ -24,86 +27,43 @@ impl WinitWindows {
2427
) -> Window {
2528
let mut winit_window_builder = winit::window::WindowBuilder::new();
2629

30+
let &WindowDescriptor {
31+
width,
32+
height,
33+
position,
34+
monitor,
35+
scale_factor_override,
36+
..
37+
} = window_descriptor;
38+
39+
let logical_size = LogicalSize::new(width, height);
40+
41+
let monitor = match monitor {
42+
MonitorSelection::Current => None,
43+
MonitorSelection::Primary => event_loop.primary_monitor(),
44+
MonitorSelection::Index(i) => event_loop.available_monitors().nth(i),
45+
};
46+
47+
let selected_or_primary_monitor = monitor.clone().or_else(|| event_loop.primary_monitor());
48+
2749
winit_window_builder = match window_descriptor.mode {
28-
WindowMode::BorderlessFullscreen => winit_window_builder.with_fullscreen(Some(
29-
winit::window::Fullscreen::Borderless(event_loop.primary_monitor()),
50+
WindowMode::BorderlessFullscreen => winit_window_builder
51+
.with_fullscreen(Some(Fullscreen::Borderless(selected_or_primary_monitor))),
52+
WindowMode::Fullscreen => winit_window_builder.with_fullscreen(Some(
53+
Fullscreen::Exclusive(get_best_videomode(&selected_or_primary_monitor.unwrap())),
3054
)),
31-
WindowMode::Fullscreen => {
32-
winit_window_builder.with_fullscreen(Some(winit::window::Fullscreen::Exclusive(
33-
get_best_videomode(&event_loop.primary_monitor().unwrap()),
34-
)))
35-
}
3655
WindowMode::SizedFullscreen => winit_window_builder.with_fullscreen(Some(
37-
winit::window::Fullscreen::Exclusive(get_fitting_videomode(
38-
&event_loop.primary_monitor().unwrap(),
56+
Fullscreen::Exclusive(get_fitting_videomode(
57+
&selected_or_primary_monitor.unwrap(),
3958
window_descriptor.width as u32,
4059
window_descriptor.height as u32,
4160
)),
4261
)),
4362
_ => {
44-
let WindowDescriptor {
45-
width,
46-
height,
47-
position,
48-
scale_factor_override,
49-
..
50-
} = window_descriptor;
51-
52-
use bevy_window::WindowPosition::*;
53-
match position {
54-
Automatic => { /* Window manager will handle position */ }
55-
Centered(monitor_selection) => {
56-
use bevy_window::MonitorSelection::*;
57-
let maybe_monitor = match monitor_selection {
58-
Current => {
59-
warn!("Can't select current monitor on window creation!");
60-
None
61-
}
62-
Primary => event_loop.primary_monitor(),
63-
Number(n) => event_loop.available_monitors().nth(*n),
64-
};
65-
66-
if let Some(monitor) = maybe_monitor {
67-
let screen_size = monitor.size();
68-
69-
let scale_factor = monitor.scale_factor();
70-
71-
// Logical to physical window size
72-
let (width, height): (u32, u32) = LogicalSize::new(*width, *height)
73-
.to_physical::<u32>(scale_factor)
74-
.into();
75-
76-
let position = PhysicalPosition {
77-
x: screen_size.width.saturating_sub(width) as f64 / 2.
78-
+ monitor.position().x as f64,
79-
y: screen_size.height.saturating_sub(height) as f64 / 2.
80-
+ monitor.position().y as f64,
81-
};
82-
83-
winit_window_builder = winit_window_builder.with_position(position);
84-
} else {
85-
warn!("Couldn't get monitor selected with: {monitor_selection:?}");
86-
}
87-
}
88-
At(position) => {
89-
if let Some(sf) = scale_factor_override {
90-
winit_window_builder = winit_window_builder.with_position(
91-
LogicalPosition::new(position[0] as f64, position[1] as f64)
92-
.to_physical::<f64>(*sf),
93-
);
94-
} else {
95-
winit_window_builder = winit_window_builder.with_position(
96-
LogicalPosition::new(position[0] as f64, position[1] as f64),
97-
);
98-
}
99-
}
100-
}
101-
10263
if let Some(sf) = scale_factor_override {
103-
winit_window_builder
104-
.with_inner_size(LogicalSize::new(*width, *height).to_physical::<f64>(*sf))
64+
winit_window_builder.with_inner_size(logical_size.to_physical::<f64>(sf))
10565
} else {
106-
winit_window_builder.with_inner_size(LogicalSize::new(*width, *height))
66+
winit_window_builder.with_inner_size(logical_size)
10767
}
10868
}
10969
.with_resizable(window_descriptor.resizable)
@@ -155,6 +115,49 @@ impl WinitWindows {
155115

156116
let winit_window = winit_window_builder.build(event_loop).unwrap();
157117

118+
if window_descriptor.mode == WindowMode::Windowed {
119+
use bevy_window::WindowPosition::*;
120+
match position {
121+
Automatic => {
122+
if let Some(monitor) = monitor {
123+
winit_window.set_outer_position(monitor.position());
124+
}
125+
}
126+
Centered => {
127+
if let Some(monitor) = monitor.or_else(|| winit_window.current_monitor()) {
128+
let monitor_position = monitor.position().cast::<f64>();
129+
let size = monitor.size();
130+
131+
// Logical to physical window size
132+
let PhysicalSize::<u32> { width, height } =
133+
logical_size.to_physical(monitor.scale_factor());
134+
135+
let position = PhysicalPosition {
136+
x: size.width.saturating_sub(width) as f64 / 2. + monitor_position.x,
137+
y: size.height.saturating_sub(height) as f64 / 2. + monitor_position.y,
138+
};
139+
140+
winit_window.set_outer_position(position);
141+
}
142+
}
143+
At(position) => {
144+
if let Some(monitor) = monitor.or_else(|| winit_window.current_monitor()) {
145+
let monitor_position = DVec2::from(<(_, _)>::from(monitor.position()));
146+
let position = monitor_position + position.as_dvec2();
147+
148+
if let Some(sf) = scale_factor_override {
149+
winit_window.set_outer_position(
150+
LogicalPosition::new(position.x, position.y).to_physical::<f64>(sf),
151+
);
152+
} else {
153+
winit_window
154+
.set_outer_position(LogicalPosition::new(position.x, position.y));
155+
}
156+
}
157+
}
158+
}
159+
}
160+
158161
if window_descriptor.cursor_locked {
159162
match winit_window.set_cursor_grab(true) {
160163
Ok(_) | Err(winit::error::ExternalError::NotSupported(_)) => {}

0 commit comments

Comments
 (0)