diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d5be754..1c8522a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ - `MonitorId::get_position` now returns `(i32, i32)` instead of `(u32, u32)`. - Rewrite of the wayland backend to use wayland-client-0.11 - Support for dead keys on wayland for keyboard utf8 input +- Monitor enumeration on Windows is now implemented using `EnumDisplayMonitors` instead of +`EnumDisplayDevices`. This changes the value returned by `MonitorId::get_name()`. +- On Windows added `MonitorIdExt::hmonitor` method # Version 0.8.3 (2017-10-11) diff --git a/src/os/windows.rs b/src/os/windows.rs index 6059bfd6..a7d36260 100644 --- a/src/os/windows.rs +++ b/src/os/windows.rs @@ -1,5 +1,6 @@ #![cfg(target_os = "windows")] +use std::os::raw::c_void; use libc; use MonitorId; use Window; @@ -37,8 +38,11 @@ impl WindowBuilderExt for WindowBuilder { /// Additional methods on `MonitorId` that are specific to Windows. pub trait MonitorIdExt { - /// Returns the name of the monitor specific to the Win32 API. + /// Returns the name of the monitor adapter specific to the Win32 API. fn native_id(&self) -> String; + + /// Returns the handle of the monitor - `HMONITOR`. + fn hmonitor(&self) -> *mut c_void; } impl MonitorIdExt for MonitorId { @@ -46,4 +50,9 @@ impl MonitorIdExt for MonitorId { fn native_id(&self) -> String { self.inner.get_native_identifier() } + + #[inline] + fn hmonitor(&self) -> *mut c_void { + self.inner.get_hmonitor() as *mut _ + } } diff --git a/src/platform/windows/monitor.rs b/src/platform/windows/monitor.rs index 90c8527e..fb17be80 100644 --- a/src/platform/windows/monitor.rs +++ b/src/platform/windows/monitor.rs @@ -2,7 +2,7 @@ use winapi; use user32; use std::collections::VecDeque; -use std::mem; +use std::{mem, ptr}; use super::EventsLoop; @@ -12,16 +12,12 @@ pub struct MonitorId { /// The system name of the adapter. adapter_name: [winapi::WCHAR; 32], + /// Monitor handle. + hmonitor: HMonitor, + /// The system name of the monitor. monitor_name: String, - /// Name to give to the user. - readable_name: String, - - /// See the `StateFlags` element here: - /// http://msdn.microsoft.com/en-us/library/dd183569(v=vs.85).aspx - flags: winapi::DWORD, - /// True if this is the primary monitor. primary: bool, @@ -32,59 +28,19 @@ pub struct MonitorId { /// The current resolution in pixels on the monitor. dimensions: (u32, u32), + + /// DPI scaling factor. + hidpi_factor: f32, } -struct DeviceEnumerator { - parent_device: *const winapi::WCHAR, - current_index: u32, -} +// 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(Clone)] +struct HMonitor(winapi::HMONITOR); -impl DeviceEnumerator { - fn adapters() -> DeviceEnumerator { - use std::ptr; - DeviceEnumerator { - parent_device: ptr::null(), - current_index: 0 - } - } - - fn monitors(adapter_name: *const winapi::WCHAR) -> DeviceEnumerator { - DeviceEnumerator { - parent_device: adapter_name, - current_index: 0 - } - } -} - -impl Iterator for DeviceEnumerator { - type Item = winapi::DISPLAY_DEVICEW; - fn next(&mut self) -> Option { - use std::mem; - loop { - let mut output: winapi::DISPLAY_DEVICEW = unsafe { mem::zeroed() }; - output.cb = mem::size_of::() as winapi::DWORD; - - if unsafe { user32::EnumDisplayDevicesW(self.parent_device, - self.current_index as winapi::DWORD, &mut output, 0) } == 0 - { - // the device doesn't exist, which means we have finished enumerating - break; - } - self.current_index += 1; - - if (output.StateFlags & winapi::DISPLAY_DEVICE_ACTIVE) == 0 || - (output.StateFlags & winapi::DISPLAY_DEVICE_MIRRORING_DRIVER) != 0 - { - // the device is not active - // the Win32 api usually returns a lot of inactive devices - continue; - } - - return Some(output); - } - None - } -} +unsafe impl Send for HMonitor {} fn wchar_as_string(wchar: &[winapi::WCHAR]) -> String { String::from_utf16_lossy(wchar) @@ -92,51 +48,45 @@ fn wchar_as_string(wchar: &[winapi::WCHAR]) -> String { .to_string() } +unsafe extern "system" fn monitor_enum_proc(hmonitor: winapi::HMONITOR, _: winapi::HDC, place: winapi::LPRECT, data: winapi::LPARAM) -> winapi::BOOL { + let monitors = data as *mut VecDeque; + + let place = *place; + let position = (place.left as i32, place.top as i32); + let dimensions = ((place.right - place.left) as u32, (place.bottom - place.top) as u32); + + let mut monitor_info: winapi::MONITORINFOEXW = mem::zeroed(); + monitor_info.cbSize = mem::size_of::() as winapi::DWORD; + if user32::GetMonitorInfoW(hmonitor, &mut monitor_info as *mut winapi::MONITORINFOEXW as *mut winapi::MONITORINFO) == 0 { + // Some error occurred, just skip this monitor and go on. + return winapi::TRUE; + } + + (*monitors).push_back(MonitorId { + adapter_name: monitor_info.szDevice, + hmonitor: HMonitor(hmonitor), + monitor_name: wchar_as_string(&monitor_info.szDevice), + primary: monitor_info.dwFlags & winapi::MONITORINFOF_PRIMARY != 0, + position, + dimensions, + hidpi_factor: 1.0, + }); + + // TRUE means continue enumeration. + winapi::TRUE +} + impl EventsLoop { pub fn get_available_monitors(&self) -> VecDeque { - // return value - let mut result = VecDeque::new(); - - for adapter in DeviceEnumerator::adapters() { - // getting the position - let (position, dimensions) = unsafe { - let mut dev: winapi::DEVMODEW = mem::zeroed(); - dev.dmSize = mem::size_of::() as winapi::WORD; - - if user32::EnumDisplaySettingsExW(adapter.DeviceName.as_ptr(), - winapi::ENUM_CURRENT_SETTINGS, - &mut dev, 0) == 0 - { - continue; - } - - let point: &winapi::POINTL = mem::transmute(&dev.union1); - let position = (point.x as i32, point.y as i32); - - let dimensions = (dev.dmPelsWidth as u32, dev.dmPelsHeight as u32); - - (position, dimensions) - }; - - for (num, monitor) in DeviceEnumerator::monitors(adapter.DeviceName.as_ptr()).enumerate() { - // adding to the resulting list - result.push_back(MonitorId { - adapter_name: adapter.DeviceName, - monitor_name: wchar_as_string(&monitor.DeviceName), - readable_name: wchar_as_string(&monitor.DeviceString), - flags: monitor.StateFlags, - primary: (adapter.StateFlags & winapi::DISPLAY_DEVICE_PRIMARY_DEVICE) != 0 && - num == 0, - position: position, - dimensions: dimensions, - }); - } + unsafe { + let mut result: VecDeque = VecDeque::new(); + user32::EnumDisplayMonitors(ptr::null_mut(), ptr::null_mut(), Some(monitor_enum_proc), &mut result as *mut _ as winapi::LPARAM); + result } - result } pub fn get_primary_monitor(&self) -> MonitorId { - // we simply get all available monitors and return the one with the `PRIMARY_DEVICE` flag + // we simply get all available monitors and return the one with the `MONITORINFOF_PRIMARY` flag // TODO: it is possible to query the win32 API for the primary monitor, this should be done // instead for monitor in self.get_available_monitors().into_iter() { @@ -153,7 +103,7 @@ impl MonitorId { /// See the docs if the crate root file. #[inline] pub fn get_name(&self) -> Option { - Some(self.readable_name.clone()) + Some(self.monitor_name.clone()) } /// See the docs of the crate root file. @@ -162,10 +112,16 @@ impl MonitorId { self.monitor_name.clone() } - /// See the docs if the crate root file. + /// See the docs of the crate root file. + #[inline] + pub fn get_hmonitor(&self) -> winapi::HMONITOR { + self.hmonitor.0 + } + + /// See the docs of the crate root file. #[inline] pub fn get_dimensions(&self) -> (u32, u32) { - // TODO: retreive the dimensions every time this is called + // TODO: retrieve the dimensions every time this is called self.dimensions } @@ -184,6 +140,6 @@ impl MonitorId { #[inline] pub fn get_hidpi_factor(&self) -> f32 { - 1.0 + self.hidpi_factor } }