bevy_winit: Window centering is incorrect when the primary monitor's usable space differs from the intended monitor's
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 22h
- Merged PRs (30d)
- 161
Description
## Bevy version
Bevy version 0.15.1
## What you did
I created a window with `WindowPosition::Centered` and positioned it on my secondary monitor. I noticed the window appears off-center when the primary monitor has a Gnome top bar and the secondary monitor doesn't.
```rust
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
resolution: WindowResolution::new(1600., 900.).with_scale_factor_override(1.0),
position: WindowPosition::Centered(MonitorSelection::Index(1)),
..default()
}),
```
My setup:
- Primary monitor: 1920x1080 with Gnome top bar (32 pixels height), so 1920x1048 usable space
- Secondary monitor: 1920x1080 without Gnome top bar
- Both monitors at 100% scaling
## What went wrong
Expected:
The window should be perfectly centered on the secondary monitor since it has the full 1920x1080 space available.
Actual:
The window sits too low on the screen, there's more space at the top than at the bottom. I measured the offset and it's exactly 16 pixels, which interestingly is half the height of the Gnome top bar from my primary monitor.
## Additional information
I think I found where this happens in `winit_windows.rs` in the `winit_window_position()` function. Here's my theory of what's going on:
1. I believe the window's resolution gets set based on the primary monitor's usable space (1048px height due to the Gnome bar)
2. Then when we move to the secondary monitor, we try to center this same window in the full 1080px height
3. This would explain why we get that 16px extra space at the top - we're using a window sized for a smaller space but centering it in a larger one
Here's the code where I think this happens (I removed the original comments and added my explanation):
```rust
pub fn winit_window_position(
position: &WindowPosition,
resolution: &WindowResolution, // This comes from primary monitor (1048px height)
monitors: &WinitMonitors,
primary_monitor: Option,
current_monitor: Option,
) -> Option> {
match position {
WindowPosition::Automatic => {
None
}
WindowPosition::Centered(monitor_selection) => {
let maybe_monitor = select_monitor(
monitors,
primary_monitor,
current_monitor,
monitor_selection,
);
if let Some(monitor) = maybe_monitor {
let screen_size = monitor.size(); // Gets full 1080px height for secondary
let scale_factor = match resolution.scale_factor_override() {
Some(scale_factor_override) => scale_factor_override as f64,
None => monitor.scale_factor(),
};
// Using primary monitor's resolution in secondary monitor's space
let (width, height): (u32, u32) =
LogicalSize::new(resolution.width(), resolution.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,
};
Some(position.cast::())
} 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::())
}
}
}
```
Contributor guide
Assessment
This issue has not been assessed yet.