1 use winapi::shared::minwindef::{BOOL, DWORD, LPARAM, TRUE};
2 use winapi::shared::windef::{HDC, HMONITOR, HWND, LPRECT, POINT};
3 use winapi::um::winnt::LONG;
4 use winapi::um::winuser;
5 
6 use std::{mem, ptr};
7 use std::collections::VecDeque;
8 
9 use super::{EventsLoop, util};
10 use dpi::{PhysicalPosition, PhysicalSize};
11 use platform::platform::dpi::{dpi_to_scale_factor, get_monitor_dpi};
12 use platform::platform::window::Window;
13 
14 /// Win32 implementation of the main `MonitorId` object.
15 #[derive(Debug, Clone)]
16 pub struct MonitorId {
17     /// Monitor handle.
18     hmonitor: HMonitor,
19     /// The system name of the monitor.
20     monitor_name: String,
21     /// True if this is the primary monitor.
22     primary: bool,
23     /// The position of the monitor in pixels on the desktop.
24     ///
25     /// A window that is positioned at these coordinates will overlap the monitor.
26     position: (i32, i32),
27     /// The current resolution in pixels on the monitor.
28     dimensions: (u32, u32),
29     /// DPI scale factor.
30     hidpi_factor: f64,
31 }
32 
33 // Send is not implemented for HMONITOR, we have to wrap it and implement it manually.
34 // For more info see:
35 // https://github.com/retep998/winapi-rs/issues/360
36 // https://github.com/retep998/winapi-rs/issues/396
37 #[derive(Debug, Clone)]
38 struct HMonitor(HMONITOR);
39 
40 unsafe impl Send for HMonitor {}
41 
monitor_enum_proc( hmonitor: HMONITOR, _hdc: HDC, _place: LPRECT, data: LPARAM, ) -> BOOL42 unsafe extern "system" fn monitor_enum_proc(
43     hmonitor: HMONITOR,
44     _hdc: HDC,
45     _place: LPRECT,
46     data: LPARAM,
47 ) -> BOOL {
48     let monitors = data as *mut VecDeque<MonitorId>;
49     (*monitors).push_back(MonitorId::from_hmonitor(hmonitor));
50     TRUE // continue enumeration
51 }
52 
get_available_monitors() -> VecDeque<MonitorId>53 pub fn get_available_monitors() -> VecDeque<MonitorId> {
54     let mut monitors: VecDeque<MonitorId> = VecDeque::new();
55     unsafe {
56         winuser::EnumDisplayMonitors(
57             ptr::null_mut(),
58             ptr::null_mut(),
59             Some(monitor_enum_proc),
60             &mut monitors as *mut _ as LPARAM,
61         );
62     }
63     monitors
64 }
65 
get_primary_monitor() -> MonitorId66 pub fn get_primary_monitor() -> MonitorId {
67     const ORIGIN: POINT = POINT { x: 0, y: 0 };
68     let hmonitor = unsafe {
69         winuser::MonitorFromPoint(ORIGIN, winuser::MONITOR_DEFAULTTOPRIMARY)
70     };
71     MonitorId::from_hmonitor(hmonitor)
72 }
73 
74 impl EventsLoop {
75     // TODO: Investigate opportunities for caching
get_available_monitors(&self) -> VecDeque<MonitorId>76     pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
77         get_available_monitors()
78     }
79 
get_current_monitor(hwnd: HWND) -> MonitorId80     pub fn get_current_monitor(hwnd: HWND) -> MonitorId {
81         let hmonitor = unsafe {
82             winuser::MonitorFromWindow(hwnd, winuser::MONITOR_DEFAULTTONEAREST)
83         };
84         MonitorId::from_hmonitor(hmonitor)
85     }
86 
get_primary_monitor(&self) -> MonitorId87     pub fn get_primary_monitor(&self) -> MonitorId {
88         get_primary_monitor()
89     }
90 }
91 
92 impl Window {
get_available_monitors(&self) -> VecDeque<MonitorId>93     pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
94         get_available_monitors()
95     }
96 
get_primary_monitor(&self) -> MonitorId97     pub fn get_primary_monitor(&self) -> MonitorId {
98         get_primary_monitor()
99     }
100 }
101 
get_monitor_info(hmonitor: HMONITOR) -> Result<winuser::MONITORINFOEXW, util::WinError>102 pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result<winuser::MONITORINFOEXW, util::WinError> {
103     let mut monitor_info: winuser::MONITORINFOEXW = unsafe { mem::uninitialized() };
104     monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
105     let status = unsafe {
106         winuser::GetMonitorInfoW(
107             hmonitor,
108             &mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO,
109         )
110     };
111     if status == 0 {
112         Err(util::WinError::from_last_error())
113     } else {
114         Ok(monitor_info)
115     }
116 }
117 
118 impl MonitorId {
from_hmonitor(hmonitor: HMONITOR) -> Self119     pub(crate) fn from_hmonitor(hmonitor: HMONITOR) -> Self {
120         let monitor_info = get_monitor_info(hmonitor).expect("`GetMonitorInfoW` failed");
121         let place = monitor_info.rcMonitor;
122         let dimensions = (
123             (place.right - place.left) as u32,
124             (place.bottom - place.top) as u32,
125         );
126         MonitorId {
127             hmonitor: HMonitor(hmonitor),
128             monitor_name: util::wchar_ptr_to_string(monitor_info.szDevice.as_ptr()),
129             primary: util::has_flag(monitor_info.dwFlags, winuser::MONITORINFOF_PRIMARY),
130             position: (place.left as i32, place.top as i32),
131             dimensions,
132             hidpi_factor: dpi_to_scale_factor(get_monitor_dpi(hmonitor).unwrap_or(96)),
133         }
134     }
135 
contains_point(&self, point: &POINT) -> bool136     pub(crate) fn contains_point(&self, point: &POINT) -> bool {
137         let left = self.position.0 as LONG;
138         let right = left + self.dimensions.0 as LONG;
139         let top = self.position.1 as LONG;
140         let bottom = top + self.dimensions.1 as LONG;
141         point.x >= left && point.x <= right && point.y >= top && point.y <= bottom
142     }
143 
144     #[inline]
get_name(&self) -> Option<String>145     pub fn get_name(&self) -> Option<String> {
146         Some(self.monitor_name.clone())
147     }
148 
149     #[inline]
get_native_identifier(&self) -> String150     pub fn get_native_identifier(&self) -> String {
151         self.monitor_name.clone()
152     }
153 
154     #[inline]
get_hmonitor(&self) -> HMONITOR155     pub fn get_hmonitor(&self) -> HMONITOR {
156         self.hmonitor.0
157     }
158 
159     #[inline]
get_dimensions(&self) -> PhysicalSize160     pub fn get_dimensions(&self) -> PhysicalSize {
161         self.dimensions.into()
162     }
163 
164     #[inline]
get_position(&self) -> PhysicalPosition165     pub fn get_position(&self) -> PhysicalPosition {
166         self.position.into()
167     }
168 
169     #[inline]
get_hidpi_factor(&self) -> f64170     pub fn get_hidpi_factor(&self) -> f64 {
171         self.hidpi_factor
172     }
173 }
174