bevyengine / bevyengine/bevy

bevy_winit: Window centering is incorrect when the primary monitor's usable space differs from the intended monitor's

Open
#17,838 0 comments 0 reactions 0 assignees View on GitHub
A-Windowing C-Bug S-Ready-For-Implementation
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.