winit/platform_impl/windows/
monitor.rs1use std::collections::{BTreeSet, VecDeque};
2use std::hash::Hash;
3use std::{io, mem, ptr};
4
5use windows_sys::Win32::Foundation::{BOOL, HWND, LPARAM, POINT, RECT};
6use windows_sys::Win32::Graphics::Gdi::{
7 EnumDisplayMonitors, EnumDisplaySettingsExW, GetMonitorInfoW, MonitorFromPoint,
8 MonitorFromWindow, DEVMODEW, DM_BITSPERPEL, DM_DISPLAYFREQUENCY, DM_PELSHEIGHT, DM_PELSWIDTH,
9 ENUM_CURRENT_SETTINGS, HDC, HMONITOR, MONITORINFO, MONITORINFOEXW, MONITOR_DEFAULTTONEAREST,
10 MONITOR_DEFAULTTOPRIMARY,
11};
12
13use super::util::decode_wide;
14use crate::dpi::{PhysicalPosition, PhysicalSize};
15use crate::monitor::VideoModeHandle as RootVideoModeHandle;
16use crate::platform_impl::platform::dpi::{dpi_to_scale_factor, get_monitor_dpi};
17use crate::platform_impl::platform::util::has_flag;
18use crate::platform_impl::platform::window::Window;
19
20#[derive(Clone)]
21pub struct VideoModeHandle {
22 pub(crate) size: (u32, u32),
23 pub(crate) bit_depth: u16,
24 pub(crate) refresh_rate_millihertz: u32,
25 pub(crate) monitor: MonitorHandle,
26 pub(crate) native_video_mode: Box<DEVMODEW>,
28}
29
30impl PartialEq for VideoModeHandle {
31 fn eq(&self, other: &Self) -> bool {
32 self.size == other.size
33 && self.bit_depth == other.bit_depth
34 && self.refresh_rate_millihertz == other.refresh_rate_millihertz
35 && self.monitor == other.monitor
36 }
37}
38
39impl Eq for VideoModeHandle {}
40
41impl std::hash::Hash for VideoModeHandle {
42 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
43 self.size.hash(state);
44 self.bit_depth.hash(state);
45 self.refresh_rate_millihertz.hash(state);
46 self.monitor.hash(state);
47 }
48}
49
50impl std::fmt::Debug for VideoModeHandle {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 f.debug_struct("VideoModeHandle")
53 .field("size", &self.size)
54 .field("bit_depth", &self.bit_depth)
55 .field("refresh_rate_millihertz", &self.refresh_rate_millihertz)
56 .field("monitor", &self.monitor)
57 .finish()
58 }
59}
60
61impl VideoModeHandle {
62 pub fn size(&self) -> PhysicalSize<u32> {
63 self.size.into()
64 }
65
66 pub fn bit_depth(&self) -> u16 {
67 self.bit_depth
68 }
69
70 pub fn refresh_rate_millihertz(&self) -> u32 {
71 self.refresh_rate_millihertz
72 }
73
74 pub fn monitor(&self) -> MonitorHandle {
75 self.monitor.clone()
76 }
77}
78
79#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
80pub struct MonitorHandle(HMONITOR);
81
82unsafe impl Send for MonitorHandle {}
88
89unsafe extern "system" fn monitor_enum_proc(
90 hmonitor: HMONITOR,
91 _hdc: HDC,
92 _place: *mut RECT,
93 data: LPARAM,
94) -> BOOL {
95 let monitors = data as *mut VecDeque<MonitorHandle>;
96 unsafe { (*monitors).push_back(MonitorHandle::new(hmonitor)) };
97 true.into() }
99
100pub fn available_monitors() -> VecDeque<MonitorHandle> {
101 let mut monitors: VecDeque<MonitorHandle> = VecDeque::new();
102 unsafe {
103 EnumDisplayMonitors(
104 0,
105 ptr::null(),
106 Some(monitor_enum_proc),
107 &mut monitors as *mut _ as LPARAM,
108 );
109 }
110 monitors
111}
112
113pub fn primary_monitor() -> MonitorHandle {
114 const ORIGIN: POINT = POINT { x: 0, y: 0 };
115 let hmonitor = unsafe { MonitorFromPoint(ORIGIN, MONITOR_DEFAULTTOPRIMARY) };
116 MonitorHandle::new(hmonitor)
117}
118
119pub fn current_monitor(hwnd: HWND) -> MonitorHandle {
120 let hmonitor = unsafe { MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) };
121 MonitorHandle::new(hmonitor)
122}
123
124impl Window {
125 pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
126 available_monitors()
127 }
128
129 pub fn primary_monitor(&self) -> Option<MonitorHandle> {
130 let monitor = primary_monitor();
131 Some(monitor)
132 }
133}
134
135pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result<MONITORINFOEXW, io::Error> {
136 let mut monitor_info: MONITORINFOEXW = unsafe { mem::zeroed() };
137 monitor_info.monitorInfo.cbSize = mem::size_of::<MONITORINFOEXW>() as u32;
138 let status = unsafe {
139 GetMonitorInfoW(hmonitor, &mut monitor_info as *mut MONITORINFOEXW as *mut MONITORINFO)
140 };
141 if status == false.into() {
142 Err(io::Error::last_os_error())
143 } else {
144 Ok(monitor_info)
145 }
146}
147
148impl MonitorHandle {
149 pub(crate) fn new(hmonitor: HMONITOR) -> Self {
150 MonitorHandle(hmonitor)
151 }
152
153 #[inline]
154 pub fn name(&self) -> Option<String> {
155 let monitor_info = get_monitor_info(self.0).unwrap();
156 Some(decode_wide(&monitor_info.szDevice).to_string_lossy().to_string())
157 }
158
159 #[inline]
160 pub fn native_identifier(&self) -> String {
161 self.name().unwrap()
162 }
163
164 #[inline]
165 pub fn hmonitor(&self) -> HMONITOR {
166 self.0
167 }
168
169 #[inline]
170 pub fn size(&self) -> PhysicalSize<u32> {
171 let rc_monitor = get_monitor_info(self.0).unwrap().monitorInfo.rcMonitor;
172 PhysicalSize {
173 width: (rc_monitor.right - rc_monitor.left) as u32,
174 height: (rc_monitor.bottom - rc_monitor.top) as u32,
175 }
176 }
177
178 #[inline]
179 pub fn refresh_rate_millihertz(&self) -> Option<u32> {
180 let monitor_info = get_monitor_info(self.0).ok()?;
181 let device_name = monitor_info.szDevice.as_ptr();
182 unsafe {
183 let mut mode: DEVMODEW = mem::zeroed();
184 mode.dmSize = mem::size_of_val(&mode) as u16;
185 if EnumDisplaySettingsExW(device_name, ENUM_CURRENT_SETTINGS, &mut mode, 0)
186 == false.into()
187 {
188 None
189 } else {
190 Some(mode.dmDisplayFrequency * 1000)
191 }
192 }
193 }
194
195 #[inline]
196 pub fn position(&self) -> PhysicalPosition<i32> {
197 get_monitor_info(self.0)
198 .map(|info| {
199 let rc_monitor = info.monitorInfo.rcMonitor;
200 PhysicalPosition { x: rc_monitor.left, y: rc_monitor.top }
201 })
202 .unwrap_or(PhysicalPosition { x: 0, y: 0 })
203 }
204
205 #[inline]
206 pub fn scale_factor(&self) -> f64 {
207 dpi_to_scale_factor(get_monitor_dpi(self.0).unwrap_or(96))
208 }
209
210 #[inline]
211 pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
212 let mut modes = BTreeSet::<RootVideoModeHandle>::new();
216 let mod_map = |mode: RootVideoModeHandle| mode.video_mode;
217
218 let monitor_info = match get_monitor_info(self.0) {
219 Ok(monitor_info) => monitor_info,
220 Err(error) => {
221 tracing::warn!("Error from get_monitor_info: {error}");
222 return modes.into_iter().map(mod_map);
223 },
224 };
225
226 let device_name = monitor_info.szDevice.as_ptr();
227
228 let mut i = 0;
229 loop {
230 let mut mode: DEVMODEW = unsafe { mem::zeroed() };
231 mode.dmSize = mem::size_of_val(&mode) as u16;
232 if unsafe { EnumDisplaySettingsExW(device_name, i, &mut mode, 0) } == false.into() {
233 break;
234 }
235
236 const REQUIRED_FIELDS: u32 =
237 DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
238 assert!(has_flag(mode.dmFields, REQUIRED_FIELDS));
239
240 modes.insert(RootVideoModeHandle {
242 video_mode: VideoModeHandle {
243 size: (mode.dmPelsWidth, mode.dmPelsHeight),
244 bit_depth: mode.dmBitsPerPel as u16,
245 refresh_rate_millihertz: mode.dmDisplayFrequency * 1000,
246 monitor: self.clone(),
247 native_video_mode: Box::new(mode),
248 },
249 });
250
251 i += 1;
252 }
253
254 modes.into_iter().map(mod_map)
255 }
256}