winit-sonoma-fix/src/platform/windows/monitor.rs

174 lines
5.1 KiB
Rust
Raw Normal View History

2018-06-15 09:42:18 +10:00
use winapi::shared::minwindef::{BOOL, DWORD, LPARAM, TRUE};
use winapi::shared::windef::{HDC, HMONITOR, HWND, LPRECT, POINT};
use winapi::um::winnt::LONG;
use winapi::um::winuser;
2014-07-31 18:52:05 +10:00
use std::{mem, ptr};
2018-06-15 09:42:18 +10:00
use std::collections::VecDeque;
Windows: Implement DeviceEvents (#482) Fixes #467 All variants other than Text have been implemented. While Text can be implemented using ToUnicode, that doesn't play nice with dead keys, IME, etc. Most of the mouse DeviceEvents were already implemented, but due to the flags that were used when registering for raw input events, they only worked when the window was in the foreground. This is also a step forward for #338, as DeviceIds are no longer useless on Windows. On DeviceEvents, the DeviceId contains that device's handle. While that handle could ostensibly be used by developers to query device information, my actual reason for choosing it is because it's simply a very easy way to handle this. As a fun bonus, this enabled me to create this method: DevideIdExt::get_persistent_identifier() -> Option<String> Using this gives you a unique identifier for the device that persists across replugs/reboots/etc., so it's ideal for something like device-specific configuration. There's a notable caveat to the new DeviceIds, which is that the value will always be 0 for a WindowEvent. There doesn't seem to be any straightforward way around this limitation. I was concerned that multi-window applications would receive n copies of every DeviceEvent, but Windows only sends them to one window per application. Lastly, there's a chance that these additions will cause antivirus/etc. software to detect winit applications as keyloggers. I don't know how likely that is to actually happen to people, but if it does become an issue, the raw input code is neatly sequestered and would be easy to make optional during compilation.
2018-04-29 02:42:33 +10:00
use super::{EventsLoop, util};
use dpi::{PhysicalPosition, PhysicalSize};
2018-06-15 09:42:18 +10:00
use platform::platform::dpi::{dpi_to_scale_factor, get_monitor_dpi};
use platform::platform::window::Window;
2015-09-24 17:11:59 +10:00
/// Win32 implementation of the main `MonitorId` object.
#[derive(Debug, Clone)]
2015-09-24 17:11:59 +10:00
pub struct MonitorId {
/// Monitor handle.
hmonitor: HMonitor,
/// The system name of the monitor.
monitor_name: String,
2015-04-12 17:32:25 +10:00
/// True if this is the primary monitor.
primary: bool,
/// The position of the monitor in pixels on the desktop.
///
/// A window that is positioned at these coordinates will overlap the monitor.
position: (i32, i32),
2014-08-02 19:17:49 +10:00
/// The current resolution in pixels on the monitor.
dimensions: (u32, u32),
2018-06-15 09:42:18 +10:00
/// DPI scale factor.
hidpi_factor: f64,
}
2014-07-31 18:52:05 +10:00
// Send is not implemented for HMONITOR, we have to wrap it and implement it manually.
// For more info see:
// https://github.com/retep998/winapi-rs/issues/360
// https://github.com/retep998/winapi-rs/issues/396
#[derive(Debug, Clone)]
struct HMonitor(HMONITOR);
unsafe impl Send for HMonitor {}
2014-07-31 18:52:05 +10:00
2018-06-15 09:42:18 +10:00
unsafe extern "system" fn monitor_enum_proc(
hmonitor: HMONITOR,
_hdc: HDC,
_place: LPRECT,
data: LPARAM,
) -> BOOL {
let monitors = data as *mut VecDeque<MonitorId>;
2018-06-15 09:42:18 +10:00
(*monitors).push_back(MonitorId::from_hmonitor(hmonitor));
TRUE // continue enumeration
}
pub fn get_available_monitors() -> VecDeque<MonitorId> {
let mut monitors: VecDeque<MonitorId> = VecDeque::new();
unsafe {
winuser::EnumDisplayMonitors(
ptr::null_mut(),
ptr::null_mut(),
Some(monitor_enum_proc),
&mut monitors as *mut _ as LPARAM,
);
}
monitors
}
pub fn get_primary_monitor() -> MonitorId {
const ORIGIN: POINT = POINT { x: 0, y: 0 };
let hmonitor = unsafe {
winuser::MonitorFromPoint(ORIGIN, winuser::MONITOR_DEFAULTTOPRIMARY)
};
MonitorId::from_hmonitor(hmonitor)
}
impl EventsLoop {
2018-06-15 09:42:18 +10:00
// TODO: Investigate opportunities for caching
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
get_available_monitors()
2014-07-31 18:52:05 +10:00
}
2018-06-15 09:42:18 +10:00
pub fn get_current_monitor(hwnd: HWND) -> MonitorId {
let hmonitor = unsafe {
winuser::MonitorFromWindow(hwnd, winuser::MONITOR_DEFAULTTONEAREST)
};
MonitorId::from_hmonitor(hmonitor)
}
pub fn get_primary_monitor(&self) -> MonitorId {
get_primary_monitor()
}
}
impl Window {
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
get_available_monitors()
}
pub fn get_primary_monitor(&self) -> MonitorId {
get_primary_monitor()
2018-06-15 09:42:18 +10:00
}
}
2014-07-31 18:52:05 +10:00
2018-06-15 09:42:18 +10:00
fn get_monitor_info(hmonitor: HMONITOR) -> Result<winuser::MONITORINFOEXW, util::WinError> {
let mut monitor_info: winuser::MONITORINFOEXW = unsafe { mem::uninitialized() };
monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
let status = unsafe {
winuser::GetMonitorInfoW(
hmonitor,
&mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO,
)
};
if status == 0 {
Err(util::WinError::from_last_error())
} else {
Ok(monitor_info)
}
2014-07-31 18:52:05 +10:00
}
2015-09-24 17:11:59 +10:00
impl MonitorId {
2018-06-15 09:42:18 +10:00
pub(crate) fn from_hmonitor(hmonitor: HMONITOR) -> Self {
let monitor_info = get_monitor_info(hmonitor).expect("`GetMonitorInfoW` failed");
let place = monitor_info.rcMonitor;
let dimensions = (
(place.right - place.left) as u32,
(place.bottom - place.top) as u32,
);
MonitorId {
hmonitor: HMonitor(hmonitor),
monitor_name: util::wchar_ptr_to_string(monitor_info.szDevice.as_ptr()),
primary: util::has_flag(monitor_info.dwFlags, winuser::MONITORINFOF_PRIMARY),
position: (place.left as i32, place.top as i32),
dimensions,
hidpi_factor: dpi_to_scale_factor(get_monitor_dpi(hmonitor).unwrap_or(96)),
}
}
pub(crate) fn contains_point(&self, point: &POINT) -> bool {
let left = self.position.0 as LONG;
let right = left + self.dimensions.0 as LONG;
let top = self.position.1 as LONG;
let bottom = top + self.dimensions.1 as LONG;
point.x >= left && point.x <= right && point.y >= top && point.y <= bottom
}
2015-09-21 22:42:05 +10:00
#[inline]
2014-07-31 18:52:05 +10:00
pub fn get_name(&self) -> Option<String> {
Some(self.monitor_name.clone())
2014-07-31 18:52:05 +10:00
}
2015-09-21 22:42:05 +10:00
#[inline]
pub fn get_native_identifier(&self) -> String {
self.monitor_name.clone()
}
#[inline]
pub fn get_hmonitor(&self) -> HMONITOR {
self.hmonitor.0
}
2015-09-21 22:42:05 +10:00
#[inline]
2018-06-15 09:42:18 +10:00
pub fn get_dimensions(&self) -> PhysicalSize {
self.dimensions.into()
2014-08-02 19:17:49 +10:00
}
2015-09-21 22:42:05 +10:00
#[inline]
2018-06-15 09:42:18 +10:00
pub fn get_position(&self) -> PhysicalPosition {
self.position.into()
2014-07-31 18:52:05 +10:00
}
#[inline]
2018-06-15 09:42:18 +10:00
pub fn get_hidpi_factor(&self) -> f64 {
self.hidpi_factor
}
2014-07-31 18:52:05 +10:00
}