use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct DisplayMode {
pub width: u32,
pub height: u32,
pub refresh_hz: u32,
}
impl DisplayMode {
pub fn label(&self) -> String {
if self.refresh_hz == 0 {
format!("{} x {}", self.width, self.height)
} else {
format!("{} x {} ({}Hz)", self.width, self.height, self.refresh_hz)
}
}
}
pub fn normalize(mut modes: Vec<DisplayMode>) -> Vec<DisplayMode> {
modes.sort();
modes.dedup();
modes
}
pub fn index_of(modes: &[DisplayMode], choice: DisplayMode) -> usize {
if let Some(i) = modes.iter().position(|m| *m == choice) {
return i;
}
let same_res = modes
.iter()
.enumerate()
.filter(|(_, m)| m.width == choice.width && m.height == choice.height)
.min_by_key(|(_, m)| m.refresh_hz.abs_diff(choice.refresh_hz))
.map(|(i, _)| i);
if let Some(i) = same_res {
return i;
}
let choice_px = u64::from(choice.width) * u64::from(choice.height);
modes
.iter()
.enumerate()
.min_by_key(|(_, m)| {
let px = u64::from(m.width) * u64::from(m.height);
(
px.abs_diff(choice_px),
m.refresh_hz.abs_diff(choice.refresh_hz),
)
})
.map(|(i, _)| i)
.unwrap_or(0)
}
pub fn best_native_index(modes: &[DisplayMode], want: DisplayMode) -> Option<usize> {
let mut best: Option<(u32, usize)> = None;
for (i, m) in modes.iter().enumerate() {
if m.width != want.width || m.height != want.height {
continue;
}
if want.refresh_hz != 0 && m.refresh_hz == want.refresh_hz {
return Some(i);
}
if best.as_ref().is_none_or(|(hz, _)| m.refresh_hz > *hz) {
best = Some((m.refresh_hz, i));
}
}
best.map(|(_, i)| i)
}
pub fn fallback_modes() -> Vec<DisplayMode> {
[(1280, 720), (1600, 900), (1920, 1080), (2560, 1440)]
.into_iter()
.map(|(width, height)| DisplayMode {
width,
height,
refresh_hz: 0,
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
fn mode(width: u32, height: u32, refresh_hz: u32) -> DisplayMode {
DisplayMode {
width,
height,
refresh_hz,
}
}
#[test]
fn label_includes_rate_only_when_known() {
assert_eq!(mode(2560, 1440, 165).label(), "2560 x 1440 (165Hz)");
assert_eq!(mode(1920, 1080, 60).label(), "1920 x 1080 (60Hz)");
assert_eq!(mode(1280, 720, 0).label(), "1280 x 720");
}
#[test]
fn normalize_dedups_and_groups_rates_per_resolution() {
let raw = vec![
mode(2560, 1440, 60),
mode(1024, 768, 120),
mode(1024, 768, 60),
mode(2560, 1440, 165),
mode(1024, 768, 60), mode(1280, 720, 75),
];
let list = normalize(raw);
assert_eq!(
list,
vec![
mode(1024, 768, 60),
mode(1024, 768, 120),
mode(1280, 720, 75),
mode(2560, 1440, 60),
mode(2560, 1440, 165),
]
);
}
#[test]
fn index_of_prefers_exact_then_rate_then_resolution() {
let list = vec![
mode(1280, 720, 60),
mode(1280, 720, 120),
mode(1920, 1080, 60),
mode(2560, 1440, 165),
];
assert_eq!(index_of(&list, mode(1920, 1080, 60)), 2);
assert_eq!(index_of(&list, mode(1280, 720, 144)), 1);
assert_eq!(index_of(&list, mode(1280, 720, 0)), 0);
assert_eq!(index_of(&list, mode(1600, 900, 60)), 0);
assert_eq!(index_of(&list, mode(2048, 1152, 60)), 2);
assert_eq!(index_of(&[], mode(1920, 1080, 60)), 0);
}
#[test]
fn best_native_index_snaps_rate_and_rejects_unknown_resolution() {
let list = vec![
mode(1280, 720, 60),
mode(1280, 720, 120),
mode(1920, 1080, 60),
];
assert_eq!(best_native_index(&list, mode(1280, 720, 120)), Some(1));
assert_eq!(best_native_index(&list, mode(1280, 720, 0)), Some(1));
assert_eq!(best_native_index(&list, mode(1920, 1080, 144)), Some(2));
assert_eq!(best_native_index(&list, mode(2560, 1440, 60)), None);
}
#[test]
fn fallback_modes_are_sorted_rate_free_presets() {
let list = fallback_modes();
assert_eq!(normalize(list.clone()), list);
assert!(list.len() > 2, "must expand as a dropdown row");
assert!(list.iter().all(|m| m.refresh_hz == 0));
assert!(list.iter().any(|m| (m.width, m.height) == (1920, 1080)));
}
}