use winapi; use user32; use std::collections::VecDeque; use std::mem; use super::EventsLoop; /// Win32 implementation of the main `MonitorId` object. #[derive(Clone)] pub struct MonitorId { /// The system name of the adapter. adapter_name: [winapi::WCHAR; 32], /// 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, /// 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), /// The current resolution in pixels on the monitor. dimensions: (u32, u32), } struct DeviceEnumerator { parent_device: *const winapi::WCHAR, current_index: u32, } 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 } } fn wchar_as_string(wchar: &[winapi::WCHAR]) -> String { String::from_utf16_lossy(wchar) .trim_right_matches(0 as char) .to_string() } 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, }); } } result } pub fn get_primary_monitor(&self) -> MonitorId { // we simply get all available monitors and return the one with the `PRIMARY_DEVICE` 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() { if monitor.primary { return monitor; } } panic!("Failed to find the primary monitor") } } impl MonitorId { /// See the docs if the crate root file. #[inline] pub fn get_name(&self) -> Option { Some(self.readable_name.clone()) } /// See the docs of the crate root file. #[inline] pub fn get_native_identifier(&self) -> String { self.monitor_name.clone() } /// See the docs if the crate root file. #[inline] pub fn get_dimensions(&self) -> (u32, u32) { // TODO: retreive the dimensions every time this is called self.dimensions } /// This is a Win32-only function for `MonitorId` that returns the system name of the adapter /// device. #[inline] pub fn get_adapter_name(&self) -> &[winapi::WCHAR] { &self.adapter_name } /// A window that is positioned at these coordinates will overlap the monitor. #[inline] pub fn get_position(&self) -> (i32, i32) { self.position } #[inline] pub fn get_hidpi_factor(&self) -> f32 { 1.0 } }