
# Objective - #16883 - Improve the default behaviour of the exclusive fullscreen API. ## Solution This PR changes the exclusive fullscreen window mode to require the type `WindowMode::Fullscreen(MonitorSelection, VideoModeSelection)` and removes `WindowMode::SizedFullscreen`. This API somewhat intentionally more closely resembles Winit 0.31's upcoming fullscreen and video mode API. The new VideoModeSelection enum is specified as follows: ```rust pub enum VideoModeSelection { /// Uses the video mode that the monitor is already in. Current, /// Uses a given [`crate::monitor::VideoMode`]. A list of video modes supported by the monitor /// is supplied by [`crate::monitor::Monitor::video_modes`]. Specific(VideoMode), } ``` ### Changing default behaviour This might be contentious because it removes the previous behaviour of `WindowMode::Fullscreen` which selected the highest resolution possible. While the previous behaviour would be quite easy to re-implement as additional options, or as an impl method on Monitor, I would argue that this isn't an implementation that should be encouraged. From the perspective of a Windows user, I prefer what the majority of modern games do when entering fullscreen which is to preserve the OS's current resolution settings, which allows exclusive fullscreen to be entered faster, and to only have it change if I manually select it in either the options of the game or the OS. The highest resolution available is not necessarily what the user prefers. I am open to changing this if I have just missed a good use case for it. Likewise, the only functionality that `WindowMode::SizedFullscreen` provided was that it selected the resolution closest to the current size of the window so it was removed since this behaviour can be replicated via the new `VideoModeSelection::Specific` if necessary. ## Out of scope WindowResolution and scale factor act strangely in exclusive fullscreen, this PR doesn't address it or regress it. ## Testing - Tested on Windows 11 and macOS 12.7 - Linux untested ## Migration Guide `WindowMode::SizedFullscreen(MonitorSelection)` and `WindowMode::Fullscreen(MonitorSelection)` has become `WindowMode::Fullscreen(MonitorSelection, VideoModeSelection)`. Previously, the VideoMode was selected based on the closest resolution to the current window size for SizedFullscreen and the largest resolution for Fullscreen. It is possible to replicate that behaviour by searching `Monitor::video_modes` and selecting it with `VideoModeSelection::Specific(VideoMode)` but it is recommended to use `VideoModeSelection::Current` as the default video mode when entering fullscreen.
109 lines
3.2 KiB
Rust
109 lines
3.2 KiB
Rust
//! Displays information about available monitors (displays).
|
|
|
|
use bevy::{
|
|
prelude::*,
|
|
render::camera::RenderTarget,
|
|
window::{ExitCondition, Monitor, WindowMode, WindowRef},
|
|
};
|
|
|
|
fn main() {
|
|
App::new()
|
|
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
|
primary_window: None,
|
|
exit_condition: ExitCondition::DontExit,
|
|
..default()
|
|
}))
|
|
.add_systems(Update, (update, close_on_esc))
|
|
.run();
|
|
}
|
|
|
|
#[derive(Component)]
|
|
struct MonitorRef(Entity);
|
|
|
|
fn update(
|
|
mut commands: Commands,
|
|
monitors_added: Query<(Entity, &Monitor), Added<Monitor>>,
|
|
mut monitors_removed: RemovedComponents<Monitor>,
|
|
monitor_refs: Query<(Entity, &MonitorRef)>,
|
|
) {
|
|
for (entity, monitor) in monitors_added.iter() {
|
|
// Spawn a new window on each monitor
|
|
let name = monitor.name.clone().unwrap_or_else(|| "<no name>".into());
|
|
let size = format!("{}x{}px", monitor.physical_height, monitor.physical_width);
|
|
let hz = monitor
|
|
.refresh_rate_millihertz
|
|
.map(|x| format!("{}Hz", x as f32 / 1000.0))
|
|
.unwrap_or_else(|| "<unknown>".into());
|
|
let position = format!(
|
|
"x={} y={}",
|
|
monitor.physical_position.x, monitor.physical_position.y
|
|
);
|
|
let scale = format!("{:.2}", monitor.scale_factor);
|
|
|
|
let window = commands
|
|
.spawn((
|
|
Window {
|
|
title: name.clone(),
|
|
mode: WindowMode::Fullscreen(
|
|
MonitorSelection::Entity(entity),
|
|
VideoModeSelection::Current,
|
|
),
|
|
position: WindowPosition::Centered(MonitorSelection::Entity(entity)),
|
|
..default()
|
|
},
|
|
MonitorRef(entity),
|
|
))
|
|
.id();
|
|
|
|
let camera = commands
|
|
.spawn((
|
|
Camera2d,
|
|
Camera {
|
|
target: RenderTarget::Window(WindowRef::Entity(window)),
|
|
..default()
|
|
},
|
|
))
|
|
.id();
|
|
|
|
let info_text = format!(
|
|
"Monitor: {name}\nSize: {size}\nRefresh rate: {hz}\nPosition: {position}\nScale: {scale}\n\n",
|
|
);
|
|
commands.spawn((
|
|
Text(info_text),
|
|
Node {
|
|
position_type: PositionType::Relative,
|
|
height: Val::Percent(100.0),
|
|
width: Val::Percent(100.0),
|
|
..default()
|
|
},
|
|
UiTargetCamera(camera),
|
|
MonitorRef(entity),
|
|
));
|
|
}
|
|
|
|
// Remove windows for removed monitors
|
|
for monitor_entity in monitors_removed.read() {
|
|
for (ref_entity, monitor_ref) in monitor_refs.iter() {
|
|
if monitor_ref.0 == monitor_entity {
|
|
commands.entity(ref_entity).despawn();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn close_on_esc(
|
|
mut commands: Commands,
|
|
focused_windows: Query<(Entity, &Window)>,
|
|
input: Res<ButtonInput<KeyCode>>,
|
|
) {
|
|
for (window, focus) in focused_windows.iter() {
|
|
if !focus.focused {
|
|
continue;
|
|
}
|
|
|
|
if input.just_pressed(KeyCode::Escape) {
|
|
commands.entity(window).despawn();
|
|
}
|
|
}
|
|
}
|