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