wutengine/windowing/
display.rs

1//! Information about the displays available to WutEngine
2
3use core::cmp::Ordering;
4use std::sync::OnceLock;
5
6use itertools::Itertools;
7use winit::event_loop::ActiveEventLoop;
8use winit::monitor::{MonitorHandle, VideoModeHandle};
9
10static DISPLAYS: OnceLock<AvailableDisplays> = OnceLock::new();
11
12/// The collection of available displays
13#[derive(Debug, Clone)]
14pub struct AvailableDisplays {
15    displays: Vec<Display>,
16    primary: Option<usize>,
17}
18
19impl AvailableDisplays {
20    /// Returns the primary display. If the primary display could not be properly
21    /// determined, returns the first display.
22    pub fn primary(&self) -> &Display {
23        self.primary
24            .map(|idx| &self.displays[idx])
25            .unwrap_or(&self.displays[0])
26    }
27
28    /// Returns all displays
29    pub fn all(&self) -> &[Display] {
30        &self.displays
31    }
32}
33
34/// An available display
35#[derive(Debug, Clone)]
36pub struct Display {
37    pub(super) handle: MonitorHandle,
38    pub(super) modes: Vec<VideoModeHandle>,
39    pub(super) largest_mode: VideoModeHandle,
40}
41
42impl Display {
43    /// Returns the name of the display
44    pub fn name(&self) -> String {
45        self.handle.name().unwrap()
46    }
47
48    /// Returns the supported exclusive fullscreen video modes
49    pub fn modes(&self) -> &[VideoModeHandle] {
50        &self.modes
51    }
52
53    /// Returns largest video mode supported by this display,
54    /// as determined by resolution primarily, and then refresh rate
55    pub fn largest_mode(&self) -> VideoModeHandle {
56        self.largest_mode.clone()
57    }
58}
59
60/// Obtains information about the available monitors
61/// from the event loop, and stores that in the global
62/// storage
63pub(crate) fn configure(event_loop: &ActiveEventLoop) {
64    log::trace!("Identifying monitors");
65
66    let available = event_loop.available_monitors().collect_vec();
67    let primary = event_loop.primary_monitor();
68
69    let mut displays = AvailableDisplays {
70        displays: available.into_iter().map(map_display).collect(),
71        primary: None,
72    };
73
74    if let Some(primary) = primary {
75        for (i, display) in displays.displays.iter().enumerate() {
76            if display.handle == primary {
77                displays.primary = Some(i);
78                break;
79            }
80        }
81    }
82
83    DISPLAYS
84        .set(displays)
85        .expect("Displays already configured!");
86}
87
88fn map_display(handle: MonitorHandle) -> Display {
89    let modes = handle.video_modes().collect_vec();
90
91    let mut largest_mode: Option<&VideoModeHandle> = None;
92
93    for mode in &modes {
94        if let Some(cur_largest) = largest_mode {
95            if better_mode(mode, cur_largest) {
96                largest_mode = Some(mode);
97            }
98        } else {
99            largest_mode = Some(mode);
100        }
101    }
102
103    Display {
104        handle,
105        largest_mode: largest_mode
106            .expect("Could not determine best video mode!")
107            .clone(),
108        modes,
109    }
110}
111
112/// Returnst whether `mode` is a "better" (larger, faster) video mode when compared to
113/// `compared_to`
114fn better_mode(mode: &VideoModeHandle, compared_to: &VideoModeHandle) -> bool {
115    let resolution_cmp = mode.size().cmp(&compared_to.size());
116    let refresh_cmp = mode
117        .refresh_rate_millihertz()
118        .cmp(&compared_to.refresh_rate_millihertz());
119
120    match resolution_cmp {
121        Ordering::Less => false,
122        Ordering::Equal => match refresh_cmp {
123            Ordering::Less => false,
124            Ordering::Equal => false,
125            Ordering::Greater => true,
126        },
127        Ordering::Greater => true,
128    }
129}
130
131/// Returns the available displays
132pub fn available_displays() -> AvailableDisplays {
133    DISPLAYS
134        .get()
135        .expect("Displays not yet initialized!")
136        .clone()
137}