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.
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:
- I believe the window's resolution gets set based on the primary monitor's usable space (1048px height due to the Gnome bar)
- Then when we move to the secondary monitor, we try to center this same window in the full 1080px height
- 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):
pub fn winit_window_position(
position: &WindowPosition,
resolution: &WindowResolution, // This comes from primary monitor (1048px height)
monitors: &WinitMonitors,
primary_monitor: Option<MonitorHandle>,
current_monitor: Option<MonitorHandle>,
) -> Option<PhysicalPosition<i32>> {
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::<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>())
}
}
}