1 use winapi::{
2     shared::{
3         minwindef::{BOOL, DWORD, LPARAM, TRUE, WORD},
4         windef::{HDC, HMONITOR, HWND, LPRECT, POINT},
5     },
6     um::{wingdi, winuser},
7 };
8 
9 use std::{
10     collections::{BTreeSet, VecDeque},
11     io, mem, ptr,
12 };
13 
14 use super::util;
15 use crate::{
16     dpi::{PhysicalPosition, PhysicalSize},
17     monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
18     platform_impl::platform::{
19         dpi::{dpi_to_scale_factor, get_monitor_dpi},
20         window::Window,
21     },
22 };
23 
24 #[derive(Clone)]
25 pub struct VideoMode {
26     pub(crate) size: (u32, u32),
27     pub(crate) bit_depth: u16,
28     pub(crate) refresh_rate: u16,
29     pub(crate) monitor: MonitorHandle,
30     pub(crate) native_video_mode: wingdi::DEVMODEW,
31 }
32 
33 impl PartialEq for VideoMode {
eq(&self, other: &Self) -> bool34     fn eq(&self, other: &Self) -> bool {
35         self.size == other.size
36             && self.bit_depth == other.bit_depth
37             && self.refresh_rate == other.refresh_rate
38             && self.monitor == other.monitor
39     }
40 }
41 
42 impl Eq for VideoMode {}
43 
44 impl std::hash::Hash for VideoMode {
hash<H: std::hash::Hasher>(&self, state: &mut H)45     fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
46         self.size.hash(state);
47         self.bit_depth.hash(state);
48         self.refresh_rate.hash(state);
49         self.monitor.hash(state);
50     }
51 }
52 
53 impl std::fmt::Debug for VideoMode {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result54     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55         f.debug_struct("VideoMode")
56             .field("size", &self.size)
57             .field("bit_depth", &self.bit_depth)
58             .field("refresh_rate", &self.refresh_rate)
59             .field("monitor", &self.monitor)
60             .finish()
61     }
62 }
63 
64 impl VideoMode {
size(&self) -> PhysicalSize<u32>65     pub fn size(&self) -> PhysicalSize<u32> {
66         self.size.into()
67     }
68 
bit_depth(&self) -> u1669     pub fn bit_depth(&self) -> u16 {
70         self.bit_depth
71     }
72 
refresh_rate(&self) -> u1673     pub fn refresh_rate(&self) -> u16 {
74         self.refresh_rate
75     }
76 
monitor(&self) -> RootMonitorHandle77     pub fn monitor(&self) -> RootMonitorHandle {
78         RootMonitorHandle {
79             inner: self.monitor.clone(),
80         }
81     }
82 }
83 
84 #[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
85 pub struct MonitorHandle(HMONITOR);
86 
87 // Send is not implemented for HMONITOR, we have to wrap it and implement it manually.
88 // For more info see:
89 // https://github.com/retep998/winapi-rs/issues/360
90 // https://github.com/retep998/winapi-rs/issues/396
91 
92 unsafe impl Send for MonitorHandle {}
93 
monitor_enum_proc( hmonitor: HMONITOR, _hdc: HDC, _place: LPRECT, data: LPARAM, ) -> BOOL94 unsafe extern "system" fn monitor_enum_proc(
95     hmonitor: HMONITOR,
96     _hdc: HDC,
97     _place: LPRECT,
98     data: LPARAM,
99 ) -> BOOL {
100     let monitors = data as *mut VecDeque<MonitorHandle>;
101     (*monitors).push_back(MonitorHandle::new(hmonitor));
102     TRUE // continue enumeration
103 }
104 
available_monitors() -> VecDeque<MonitorHandle>105 pub fn available_monitors() -> VecDeque<MonitorHandle> {
106     let mut monitors: VecDeque<MonitorHandle> = VecDeque::new();
107     unsafe {
108         winuser::EnumDisplayMonitors(
109             ptr::null_mut(),
110             ptr::null_mut(),
111             Some(monitor_enum_proc),
112             &mut monitors as *mut _ as LPARAM,
113         );
114     }
115     monitors
116 }
117 
primary_monitor() -> MonitorHandle118 pub fn primary_monitor() -> MonitorHandle {
119     const ORIGIN: POINT = POINT { x: 0, y: 0 };
120     let hmonitor = unsafe { winuser::MonitorFromPoint(ORIGIN, winuser::MONITOR_DEFAULTTOPRIMARY) };
121     MonitorHandle::new(hmonitor)
122 }
123 
current_monitor(hwnd: HWND) -> MonitorHandle124 pub fn current_monitor(hwnd: HWND) -> MonitorHandle {
125     let hmonitor = unsafe { winuser::MonitorFromWindow(hwnd, winuser::MONITOR_DEFAULTTONEAREST) };
126     MonitorHandle::new(hmonitor)
127 }
128 
129 impl Window {
available_monitors(&self) -> VecDeque<MonitorHandle>130     pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
131         available_monitors()
132     }
133 
primary_monitor(&self) -> Option<RootMonitorHandle>134     pub fn primary_monitor(&self) -> Option<RootMonitorHandle> {
135         let monitor = primary_monitor();
136         Some(RootMonitorHandle { inner: monitor })
137     }
138 }
139 
get_monitor_info(hmonitor: HMONITOR) -> Result<winuser::MONITORINFOEXW, io::Error>140 pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result<winuser::MONITORINFOEXW, io::Error> {
141     let mut monitor_info: winuser::MONITORINFOEXW = unsafe { mem::zeroed() };
142     monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
143     let status = unsafe {
144         winuser::GetMonitorInfoW(
145             hmonitor,
146             &mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO,
147         )
148     };
149     if status == 0 {
150         Err(io::Error::last_os_error())
151     } else {
152         Ok(monitor_info)
153     }
154 }
155 
156 impl MonitorHandle {
new(hmonitor: HMONITOR) -> Self157     pub(crate) fn new(hmonitor: HMONITOR) -> Self {
158         MonitorHandle(hmonitor)
159     }
160 
161     #[inline]
name(&self) -> Option<String>162     pub fn name(&self) -> Option<String> {
163         let monitor_info = get_monitor_info(self.0).unwrap();
164         Some(util::wchar_ptr_to_string(monitor_info.szDevice.as_ptr()))
165     }
166 
167     #[inline]
native_identifier(&self) -> String168     pub fn native_identifier(&self) -> String {
169         self.name().unwrap()
170     }
171 
172     #[inline]
hmonitor(&self) -> HMONITOR173     pub fn hmonitor(&self) -> HMONITOR {
174         self.0
175     }
176 
177     #[inline]
size(&self) -> PhysicalSize<u32>178     pub fn size(&self) -> PhysicalSize<u32> {
179         let monitor_info = get_monitor_info(self.0).unwrap();
180         PhysicalSize {
181             width: (monitor_info.rcMonitor.right - monitor_info.rcMonitor.left) as u32,
182             height: (monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top) as u32,
183         }
184     }
185 
186     #[inline]
position(&self) -> PhysicalPosition<i32>187     pub fn position(&self) -> PhysicalPosition<i32> {
188         let monitor_info = get_monitor_info(self.0).unwrap();
189         PhysicalPosition {
190             x: monitor_info.rcMonitor.left,
191             y: monitor_info.rcMonitor.top,
192         }
193     }
194 
195     #[inline]
scale_factor(&self) -> f64196     pub fn scale_factor(&self) -> f64 {
197         dpi_to_scale_factor(get_monitor_dpi(self.0).unwrap_or(96))
198     }
199 
200     #[inline]
video_modes(&self) -> impl Iterator<Item = RootVideoMode>201     pub fn video_modes(&self) -> impl Iterator<Item = RootVideoMode> {
202         // EnumDisplaySettingsExW can return duplicate values (or some of the
203         // fields are probably changing, but we aren't looking at those fields
204         // anyway), so we're using a BTreeSet deduplicate
205         let mut modes = BTreeSet::new();
206         let mut i = 0;
207 
208         loop {
209             unsafe {
210                 let monitor_info = get_monitor_info(self.0).unwrap();
211                 let device_name = monitor_info.szDevice.as_ptr();
212                 let mut mode: wingdi::DEVMODEW = mem::zeroed();
213                 mode.dmSize = mem::size_of_val(&mode) as WORD;
214                 if winuser::EnumDisplaySettingsExW(device_name, i, &mut mode, 0) == 0 {
215                     break;
216                 }
217                 i += 1;
218 
219                 const REQUIRED_FIELDS: DWORD = wingdi::DM_BITSPERPEL
220                     | wingdi::DM_PELSWIDTH
221                     | wingdi::DM_PELSHEIGHT
222                     | wingdi::DM_DISPLAYFREQUENCY;
223                 assert!(mode.dmFields & REQUIRED_FIELDS == REQUIRED_FIELDS);
224 
225                 modes.insert(RootVideoMode {
226                     video_mode: VideoMode {
227                         size: (mode.dmPelsWidth, mode.dmPelsHeight),
228                         bit_depth: mode.dmBitsPerPel as u16,
229                         refresh_rate: mode.dmDisplayFrequency as u16,
230                         monitor: self.clone(),
231                         native_video_mode: mode,
232                     },
233                 });
234             }
235         }
236 
237         modes.into_iter()
238     }
239 }
240