Use EnumDisplayMonitors to enumerate monitors on Windows (#327)

* Use EnumDisplayMonitors to enumerate monitors on Windows

* Add requested changes
This commit is contained in:
kryptan 2017-10-25 18:12:39 +03:00 committed by tomaka
parent d10312c6b1
commit 760e588627
3 changed files with 70 additions and 102 deletions

View file

@ -9,6 +9,9 @@
- `MonitorId::get_position` now returns `(i32, i32)` instead of `(u32, u32)`. - `MonitorId::get_position` now returns `(i32, i32)` instead of `(u32, u32)`.
- Rewrite of the wayland backend to use wayland-client-0.11 - Rewrite of the wayland backend to use wayland-client-0.11
- Support for dead keys on wayland for keyboard utf8 input - 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) # Version 0.8.3 (2017-10-11)

View file

@ -1,5 +1,6 @@
#![cfg(target_os = "windows")] #![cfg(target_os = "windows")]
use std::os::raw::c_void;
use libc; use libc;
use MonitorId; use MonitorId;
use Window; use Window;
@ -37,8 +38,11 @@ impl WindowBuilderExt for WindowBuilder {
/// Additional methods on `MonitorId` that are specific to Windows. /// Additional methods on `MonitorId` that are specific to Windows.
pub trait MonitorIdExt { 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; fn native_id(&self) -> String;
/// Returns the handle of the monitor - `HMONITOR`.
fn hmonitor(&self) -> *mut c_void;
} }
impl MonitorIdExt for MonitorId { impl MonitorIdExt for MonitorId {
@ -46,4 +50,9 @@ impl MonitorIdExt for MonitorId {
fn native_id(&self) -> String { fn native_id(&self) -> String {
self.inner.get_native_identifier() self.inner.get_native_identifier()
} }
#[inline]
fn hmonitor(&self) -> *mut c_void {
self.inner.get_hmonitor() as *mut _
}
} }

View file

@ -2,7 +2,7 @@ use winapi;
use user32; use user32;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::mem; use std::{mem, ptr};
use super::EventsLoop; use super::EventsLoop;
@ -12,16 +12,12 @@ pub struct MonitorId {
/// The system name of the adapter. /// The system name of the adapter.
adapter_name: [winapi::WCHAR; 32], adapter_name: [winapi::WCHAR; 32],
/// Monitor handle.
hmonitor: HMonitor,
/// The system name of the monitor. /// The system name of the monitor.
monitor_name: String, 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. /// True if this is the primary monitor.
primary: bool, primary: bool,
@ -32,59 +28,19 @@ pub struct MonitorId {
/// The current resolution in pixels on the monitor. /// The current resolution in pixels on the monitor.
dimensions: (u32, u32), dimensions: (u32, u32),
/// DPI scaling factor.
hidpi_factor: f32,
} }
struct DeviceEnumerator { // Send is not implemented for HMONITOR, we have to wrap it and implement it manually.
parent_device: *const winapi::WCHAR, // For more info see:
current_index: u32, // https://github.com/retep998/winapi-rs/issues/360
} // https://github.com/retep998/winapi-rs/issues/396
#[derive(Clone)]
struct HMonitor(winapi::HMONITOR);
impl DeviceEnumerator { unsafe impl Send for HMonitor {}
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<winapi::DISPLAY_DEVICEW> {
use std::mem;
loop {
let mut output: winapi::DISPLAY_DEVICEW = unsafe { mem::zeroed() };
output.cb = mem::size_of::<winapi::DISPLAY_DEVICEW>() 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
}
}
fn wchar_as_string(wchar: &[winapi::WCHAR]) -> String { fn wchar_as_string(wchar: &[winapi::WCHAR]) -> String {
String::from_utf16_lossy(wchar) String::from_utf16_lossy(wchar)
@ -92,51 +48,45 @@ fn wchar_as_string(wchar: &[winapi::WCHAR]) -> String {
.to_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<MonitorId>;
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::<winapi::MONITORINFOEXW>() 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 { impl EventsLoop {
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> { pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
// return value unsafe {
let mut result = VecDeque::new(); let mut result: VecDeque<MonitorId> = VecDeque::new();
user32::EnumDisplayMonitors(ptr::null_mut(), ptr::null_mut(), Some(monitor_enum_proc), &mut result as *mut _ as winapi::LPARAM);
for adapter in DeviceEnumerator::adapters() {
// getting the position
let (position, dimensions) = unsafe {
let mut dev: winapi::DEVMODEW = mem::zeroed();
dev.dmSize = mem::size_of::<winapi::DEVMODEW>() 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,
});
}
}
result result
} }
}
pub fn get_primary_monitor(&self) -> MonitorId { 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 // TODO: it is possible to query the win32 API for the primary monitor, this should be done
// instead // instead
for monitor in self.get_available_monitors().into_iter() { for monitor in self.get_available_monitors().into_iter() {
@ -153,7 +103,7 @@ impl MonitorId {
/// See the docs if the crate root file. /// See the docs if the crate root file.
#[inline] #[inline]
pub fn get_name(&self) -> Option<String> { pub fn get_name(&self) -> Option<String> {
Some(self.readable_name.clone()) Some(self.monitor_name.clone())
} }
/// See the docs of the crate root file. /// See the docs of the crate root file.
@ -162,10 +112,16 @@ impl MonitorId {
self.monitor_name.clone() 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] #[inline]
pub fn get_dimensions(&self) -> (u32, u32) { 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 self.dimensions
} }
@ -184,6 +140,6 @@ impl MonitorId {
#[inline] #[inline]
pub fn get_hidpi_factor(&self) -> f32 { pub fn get_hidpi_factor(&self) -> f32 {
1.0 self.hidpi_factor
} }
} }