diff --git a/game/src/systems/display_settings.rs b/game/src/systems/display_settings.rs index 6384d15..5d8455c 100644 --- a/game/src/systems/display_settings.rs +++ b/game/src/systems/display_settings.rs @@ -58,6 +58,7 @@ impl System for DisplaySettings { for mode in [ WindowMode::Windowed, WindowMode::BorderlessFullscreen, + WindowMode::ExclusiveFullscreen, ] { ui.selectable_value( &mut state.selected_fullscreen_mode, @@ -88,5 +89,6 @@ fn window_mode_label(mode: WindowMode) -> &'static str { match mode { WindowMode::Windowed => "Windowed", WindowMode::BorderlessFullscreen => "Borderless Fullscreen", + WindowMode::ExclusiveFullscreen => "Exclusive Fullscreen", } } diff --git a/platform/Cargo.toml b/platform/Cargo.toml index 1b7b5b2..16fec56 100644 --- a/platform/Cargo.toml +++ b/platform/Cargo.toml @@ -8,5 +8,5 @@ winit = "0.30.12" raidillon_core = { path = "../core" } raidillon_assets = { path = "../asset" } glam = "0.30.5" -serde = "1.0.228" +serde = { version = "1.0.228", features = ["derive"] } toml = "0.9.8" diff --git a/platform/src/settings.rs b/platform/src/settings.rs index f6d092f..a901137 100644 --- a/platform/src/settings.rs +++ b/platform/src/settings.rs @@ -1,4 +1,4 @@ -use winit::dpi::LogicalSize; +use winit::monitor::{MonitorHandle, VideoModeHandle}; use winit::window::{Fullscreen, Window}; use serde::{Serialize, Deserialize}; use std::error::Error; @@ -19,6 +19,7 @@ pub fn default_config_path() -> PathBuf { #[serde(rename_all = "snake_case")] pub enum WindowMode { BorderlessFullscreen, + ExclusiveFullscreen, #[default] Windowed, } @@ -93,9 +94,73 @@ impl DisplaySettings { let monitor = window.current_monitor().or_else(|| window.primary_monitor()); window.set_fullscreen(Some(Fullscreen::Borderless(monitor))); } + WindowMode::ExclusiveFullscreen => { + let monitor = window.current_monitor().or_else(|| window.primary_monitor()); + match monitor { + Some(monitor) => { + if let Some(video_mode) = pick_best_video_mode(&monitor) { + window.set_fullscreen(Some(Fullscreen::Exclusive(video_mode))); + } else { + // fallback to borderless + window.set_fullscreen(Some(Fullscreen::Borderless(Some(monitor)))); + } + } + None => { + // no monitor info, fallback to windowed + window.set_fullscreen(None); + } + } + } WindowMode::Windowed => { window.set_fullscreen(None); }, } } } + +fn pick_best_video_mode(monitor: &MonitorHandle) -> Option { + let target_size = monitor.size(); + + let mut best_native: Option = None; + let mut best_any: Option = None; + + for mode in monitor.video_modes() { + if mode.size() == target_size { + let replace = match best_native.as_ref() { + None => true, + Some(best) => { + (mode.refresh_rate_millihertz(), mode.bit_depth()) + > (best.refresh_rate_millihertz(), best.bit_depth()) + } + }; + if replace { + best_native = Some(mode.clone()); + } + } + + let replace = match best_any.as_ref() { + None => true, + Some(best) => is_better_video_mode(&mode, best), + }; + if replace { + best_any = Some(mode); + } + } + + best_native.or(best_any) +} + +fn is_better_video_mode(a: &VideoModeHandle, b: &VideoModeHandle) -> bool { + let a_size = a.size(); + let b_size = b.size(); + let a_area = u64::from(a_size.width) * u64::from(a_size.height); + let b_area = u64::from(b_size.width) * u64::from(b_size.height); + + match a_area.cmp(&b_area) { + std::cmp::Ordering::Greater => true, + std::cmp::Ordering::Less => false, + std::cmp::Ordering::Equal => { + (a.refresh_rate_millihertz(), a.bit_depth()) > (b.refresh_rate_millihertz(), b.bit_depth()) + } + } +}