2019-06-21 11:33:15 -04:00
|
|
|
use winapi::{
|
|
|
|
|
shared::{
|
|
|
|
|
minwindef::{BOOL, DWORD, LPARAM, TRUE, WORD},
|
|
|
|
|
windef::{HDC, HMONITOR, HWND, LPRECT, POINT},
|
|
|
|
|
},
|
2019-07-29 21:16:14 +03:00
|
|
|
um::{wingdi, winuser},
|
2019-06-21 11:33:15 -04:00
|
|
|
};
|
2014-07-31 10:52:05 +02:00
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
use std::{
|
2019-07-29 21:16:14 +03:00
|
|
|
collections::{BTreeSet, VecDeque},
|
2019-06-21 11:33:15 -04:00
|
|
|
io, mem, ptr,
|
|
|
|
|
};
|
2015-01-01 23:09:16 -08:00
|
|
|
|
2020-07-04 15:46:41 -04:00
|
|
|
use super::util;
|
2019-06-21 11:33:15 -04:00
|
|
|
use crate::{
|
|
|
|
|
dpi::{PhysicalPosition, PhysicalSize},
|
2019-07-29 21:16:14 +03:00
|
|
|
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
|
2019-06-21 11:33:15 -04:00
|
|
|
platform_impl::platform::{
|
|
|
|
|
dpi::{dpi_to_scale_factor, get_monitor_dpi},
|
|
|
|
|
window::Window,
|
|
|
|
|
},
|
|
|
|
|
};
|
2017-09-01 11:04:57 +02:00
|
|
|
|
2019-10-03 21:19:10 +01:00
|
|
|
#[derive(Clone)]
|
2019-07-29 21:16:14 +03:00
|
|
|
pub struct VideoMode {
|
|
|
|
|
pub(crate) size: (u32, u32),
|
|
|
|
|
pub(crate) bit_depth: u16,
|
|
|
|
|
pub(crate) refresh_rate: u16,
|
|
|
|
|
pub(crate) monitor: MonitorHandle,
|
|
|
|
|
pub(crate) native_video_mode: wingdi::DEVMODEW,
|
2015-03-24 13:29:17 -07:00
|
|
|
}
|
2014-07-31 10:52:05 +02:00
|
|
|
|
2019-10-03 21:19:10 +01:00
|
|
|
impl PartialEq for VideoMode {
|
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
|
self.size == other.size
|
|
|
|
|
&& self.bit_depth == other.bit_depth
|
|
|
|
|
&& self.refresh_rate == other.refresh_rate
|
|
|
|
|
&& self.monitor == other.monitor
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Eq for VideoMode {}
|
|
|
|
|
|
|
|
|
|
impl std::hash::Hash for VideoMode {
|
|
|
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
|
|
|
self.size.hash(state);
|
|
|
|
|
self.bit_depth.hash(state);
|
|
|
|
|
self.refresh_rate.hash(state);
|
|
|
|
|
self.monitor.hash(state);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl std::fmt::Debug for VideoMode {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
f.debug_struct("VideoMode")
|
|
|
|
|
.field("size", &self.size)
|
|
|
|
|
.field("bit_depth", &self.bit_depth)
|
|
|
|
|
.field("refresh_rate", &self.refresh_rate)
|
|
|
|
|
.field("monitor", &self.monitor)
|
|
|
|
|
.finish()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
impl VideoMode {
|
2020-01-04 01:33:07 -05:00
|
|
|
pub fn size(&self) -> PhysicalSize<u32> {
|
2019-07-29 21:16:14 +03:00
|
|
|
self.size.into()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn bit_depth(&self) -> u16 {
|
|
|
|
|
self.bit_depth
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn refresh_rate(&self) -> u16 {
|
|
|
|
|
self.refresh_rate
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn monitor(&self) -> RootMonitorHandle {
|
|
|
|
|
RootMonitorHandle {
|
|
|
|
|
inner: self.monitor.clone(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
|
|
|
|
|
pub struct MonitorHandle(HMONITOR);
|
|
|
|
|
|
2017-10-25 18:12:39 +03: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
|
2014-08-01 23:02:26 +02:00
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
unsafe impl Send for MonitorHandle {}
|
2014-07-31 10:52:05 +02:00
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
unsafe extern "system" fn monitor_enum_proc(
|
|
|
|
|
hmonitor: HMONITOR,
|
|
|
|
|
_hdc: HDC,
|
|
|
|
|
_place: LPRECT,
|
|
|
|
|
data: LPARAM,
|
|
|
|
|
) -> BOOL {
|
2019-02-05 10:30:33 -05:00
|
|
|
let monitors = data as *mut VecDeque<MonitorHandle>;
|
2019-07-29 21:16:14 +03:00
|
|
|
(*monitors).push_back(MonitorHandle::new(hmonitor));
|
2018-06-14 19:42:18 -04:00
|
|
|
TRUE // continue enumeration
|
2017-10-25 18:12:39 +03:00
|
|
|
}
|
|
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn available_monitors() -> VecDeque<MonitorHandle> {
|
2019-02-05 10:30:33 -05:00
|
|
|
let mut monitors: VecDeque<MonitorHandle> = VecDeque::new();
|
2018-06-16 10:14:12 -04:00
|
|
|
unsafe {
|
|
|
|
|
winuser::EnumDisplayMonitors(
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
Some(monitor_enum_proc),
|
|
|
|
|
&mut monitors as *mut _ as LPARAM,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
monitors
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn primary_monitor() -> MonitorHandle {
|
2018-06-16 10:14:12 -04:00
|
|
|
const ORIGIN: POINT = POINT { x: 0, y: 0 };
|
2019-06-21 11:33:15 -04:00
|
|
|
let hmonitor = unsafe { winuser::MonitorFromPoint(ORIGIN, winuser::MONITOR_DEFAULTTOPRIMARY) };
|
2019-07-29 21:16:14 +03:00
|
|
|
MonitorHandle::new(hmonitor)
|
2018-06-16 10:14:12 -04:00
|
|
|
}
|
|
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn current_monitor(hwnd: HWND) -> MonitorHandle {
|
2019-06-21 11:33:15 -04:00
|
|
|
let hmonitor = unsafe { winuser::MonitorFromWindow(hwnd, winuser::MONITOR_DEFAULTTONEAREST) };
|
2019-07-29 21:16:14 +03:00
|
|
|
MonitorHandle::new(hmonitor)
|
2019-02-05 10:30:33 -05:00
|
|
|
}
|
|
|
|
|
|
2018-06-16 10:14:12 -04:00
|
|
|
impl Window {
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
|
|
|
|
available_monitors()
|
2018-06-16 10:14:12 -04:00
|
|
|
}
|
|
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn primary_monitor(&self) -> MonitorHandle {
|
|
|
|
|
primary_monitor()
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
|
|
|
|
}
|
2014-07-31 10:52:05 +02:00
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result<winuser::MONITORINFOEXW, io::Error> {
|
2019-06-29 00:07:36 +02:00
|
|
|
let mut monitor_info: winuser::MONITORINFOEXW = unsafe { mem::zeroed() };
|
2018-06-14 19:42:18 -04:00
|
|
|
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 {
|
2019-05-29 21:29:54 -04:00
|
|
|
Err(io::Error::last_os_error())
|
2018-06-14 19:42:18 -04:00
|
|
|
} else {
|
|
|
|
|
Ok(monitor_info)
|
2017-09-01 11:04:57 +02:00
|
|
|
}
|
2014-07-31 10:52:05 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-05 10:30:33 -05:00
|
|
|
impl MonitorHandle {
|
2019-07-29 21:16:14 +03:00
|
|
|
pub(crate) fn new(hmonitor: HMONITOR) -> Self {
|
|
|
|
|
MonitorHandle(hmonitor)
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
|
|
|
|
|
2015-09-21 14:42:05 +02:00
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn name(&self) -> Option<String> {
|
2019-07-29 21:16:14 +03:00
|
|
|
let monitor_info = get_monitor_info(self.0).unwrap();
|
|
|
|
|
Some(util::wchar_ptr_to_string(monitor_info.szDevice.as_ptr()))
|
2014-07-31 10:52:05 +02:00
|
|
|
}
|
|
|
|
|
|
2015-09-21 14:42:05 +02:00
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn native_identifier(&self) -> String {
|
2019-07-29 21:16:14 +03:00
|
|
|
self.name().unwrap()
|
2015-03-16 13:52:58 -07:00
|
|
|
}
|
|
|
|
|
|
2017-10-25 18:12:39 +03:00
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn hmonitor(&self) -> HMONITOR {
|
2019-07-29 21:16:14 +03:00
|
|
|
self.0
|
2017-10-25 18:12:39 +03:00
|
|
|
}
|
|
|
|
|
|
2015-09-21 14:42:05 +02:00
|
|
|
#[inline]
|
2020-01-04 01:33:07 -05:00
|
|
|
pub fn size(&self) -> PhysicalSize<u32> {
|
2019-07-29 21:16:14 +03:00
|
|
|
let monitor_info = get_monitor_info(self.0).unwrap();
|
|
|
|
|
PhysicalSize {
|
2019-06-19 16:49:43 -04:00
|
|
|
width: (monitor_info.rcMonitor.right - monitor_info.rcMonitor.left) as u32,
|
|
|
|
|
height: (monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top) as u32,
|
2019-07-29 21:16:14 +03:00
|
|
|
}
|
2014-08-02 11:17:49 +02:00
|
|
|
}
|
|
|
|
|
|
2015-09-21 14:42:05 +02:00
|
|
|
#[inline]
|
2020-01-04 01:33:07 -05:00
|
|
|
pub fn position(&self) -> PhysicalPosition<i32> {
|
2019-07-29 21:16:14 +03:00
|
|
|
let monitor_info = get_monitor_info(self.0).unwrap();
|
|
|
|
|
PhysicalPosition {
|
2020-01-04 01:33:07 -05:00
|
|
|
x: monitor_info.rcMonitor.left,
|
|
|
|
|
y: monitor_info.rcMonitor.top,
|
2019-07-29 21:16:14 +03:00
|
|
|
}
|
2014-07-31 10:52:05 +02:00
|
|
|
}
|
2017-10-17 14:56:38 +03:00
|
|
|
|
|
|
|
|
#[inline]
|
2020-01-03 14:52:27 -05:00
|
|
|
pub fn scale_factor(&self) -> f64 {
|
2019-07-29 21:16:14 +03:00
|
|
|
dpi_to_scale_factor(get_monitor_dpi(self.0).unwrap_or(96))
|
2017-10-17 14:56:38 +03:00
|
|
|
}
|
2019-06-12 21:07:25 +03:00
|
|
|
|
|
|
|
|
#[inline]
|
2019-07-29 21:16:14 +03:00
|
|
|
pub fn video_modes(&self) -> impl Iterator<Item = RootVideoMode> {
|
2019-06-12 21:07:25 +03:00
|
|
|
// EnumDisplaySettingsExW can return duplicate values (or some of the
|
|
|
|
|
// fields are probably changing, but we aren't looking at those fields
|
2019-07-29 21:16:14 +03:00
|
|
|
// anyway), so we're using a BTreeSet deduplicate
|
|
|
|
|
let mut modes = BTreeSet::new();
|
2019-06-12 21:07:25 +03:00
|
|
|
let mut i = 0;
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
unsafe {
|
2019-07-29 21:16:14 +03:00
|
|
|
let monitor_info = get_monitor_info(self.0).unwrap();
|
|
|
|
|
let device_name = monitor_info.szDevice.as_ptr();
|
2019-06-12 21:07:25 +03:00
|
|
|
let mut mode: wingdi::DEVMODEW = mem::zeroed();
|
|
|
|
|
mode.dmSize = mem::size_of_val(&mode) as WORD;
|
|
|
|
|
if winuser::EnumDisplaySettingsExW(device_name, i, &mut mode, 0) == 0 {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
i += 1;
|
|
|
|
|
|
|
|
|
|
const REQUIRED_FIELDS: DWORD = wingdi::DM_BITSPERPEL
|
|
|
|
|
| wingdi::DM_PELSWIDTH
|
|
|
|
|
| wingdi::DM_PELSHEIGHT
|
|
|
|
|
| wingdi::DM_DISPLAYFREQUENCY;
|
|
|
|
|
assert!(mode.dmFields & REQUIRED_FIELDS == REQUIRED_FIELDS);
|
|
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
modes.insert(RootVideoMode {
|
|
|
|
|
video_mode: VideoMode {
|
|
|
|
|
size: (mode.dmPelsWidth, mode.dmPelsHeight),
|
|
|
|
|
bit_depth: mode.dmBitsPerPel as u16,
|
|
|
|
|
refresh_rate: mode.dmDisplayFrequency as u16,
|
|
|
|
|
monitor: self.clone(),
|
|
|
|
|
native_video_mode: mode,
|
|
|
|
|
},
|
2019-06-12 21:07:25 +03:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
modes.into_iter()
|
|
|
|
|
}
|
2014-07-31 10:52:05 +02:00
|
|
|
}
|