2025-01-02 03:29:42 +03:00
|
|
|
use std::collections::{HashSet, VecDeque};
|
2022-03-07 22:58:12 +01:00
|
|
|
use std::hash::Hash;
|
2024-07-23 19:59:37 +02:00
|
|
|
use std::num::{NonZeroU16, NonZeroU32};
|
2022-04-30 13:58:51 +02:00
|
|
|
use std::{io, mem, ptr};
|
2024-04-26 19:11:44 +04:00
|
|
|
|
2022-03-07 22:58:12 +01:00
|
|
|
use windows_sys::Win32::Foundation::{BOOL, HWND, LPARAM, POINT, RECT};
|
|
|
|
|
use windows_sys::Win32::Graphics::Gdi::{
|
|
|
|
|
EnumDisplayMonitors, EnumDisplaySettingsExW, GetMonitorInfoW, MonitorFromPoint,
|
|
|
|
|
MonitorFromWindow, DEVMODEW, DM_BITSPERPEL, DM_DISPLAYFREQUENCY, DM_PELSHEIGHT, DM_PELSWIDTH,
|
2022-07-08 13:25:56 +03:00
|
|
|
ENUM_CURRENT_SETTINGS, HDC, HMONITOR, MONITORINFO, MONITORINFOEXW, MONITOR_DEFAULTTONEAREST,
|
|
|
|
|
MONITOR_DEFAULTTOPRIMARY,
|
2019-06-21 11:33:15 -04:00
|
|
|
};
|
2015-01-01 23:09:16 -08:00
|
|
|
|
2022-04-30 13:58:51 +02:00
|
|
|
use super::util::decode_wide;
|
2019-06-21 11:33:15 -04:00
|
|
|
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
2025-01-02 03:29:42 +03:00
|
|
|
use crate::monitor::VideoMode;
|
2019-06-21 11:33:15 -04:00
|
|
|
use crate::platform_impl::platform::dpi::{dpi_to_scale_factor, get_monitor_dpi};
|
2022-03-07 22:58:12 +01:00
|
|
|
use crate::platform_impl::platform::util::has_flag;
|
2017-09-01 11:04:57 +02:00
|
|
|
|
2019-10-03 21:19:10 +01:00
|
|
|
#[derive(Clone)]
|
2023-12-26 22:12:33 +01:00
|
|
|
pub struct VideoModeHandle {
|
2025-01-02 03:29:42 +03:00
|
|
|
pub(crate) mode: VideoMode,
|
2022-03-24 05:08:04 +11:00
|
|
|
// DEVMODEW is huge so we box it to avoid blowing up the size of winit::window::Fullscreen
|
|
|
|
|
pub(crate) native_video_mode: Box<DEVMODEW>,
|
2015-03-24 13:29:17 -07:00
|
|
|
}
|
2014-07-31 10:52:05 +02:00
|
|
|
|
2023-12-26 22:12:33 +01:00
|
|
|
impl PartialEq for VideoModeHandle {
|
2019-10-03 21:19:10 +01:00
|
|
|
fn eq(&self, other: &Self) -> bool {
|
2025-01-02 03:29:42 +03:00
|
|
|
self.mode == other.mode
|
2019-10-03 21:19:10 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-26 22:12:33 +01:00
|
|
|
impl Eq for VideoModeHandle {}
|
2019-10-03 21:19:10 +01:00
|
|
|
|
2023-12-26 22:12:33 +01:00
|
|
|
impl std::hash::Hash for VideoModeHandle {
|
2019-10-03 21:19:10 +01:00
|
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
2025-01-02 03:29:42 +03:00
|
|
|
self.mode.hash(state);
|
2019-10-03 21:19:10 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-26 22:12:33 +01:00
|
|
|
impl std::fmt::Debug for VideoModeHandle {
|
2019-10-03 21:19:10 +01:00
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
2025-01-02 03:29:42 +03:00
|
|
|
f.debug_struct("VideoMode").field("mode", &self.mode).finish()
|
2019-10-03 21:19:10 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-26 22:12:33 +01:00
|
|
|
impl VideoModeHandle {
|
2025-01-02 03:29:42 +03:00
|
|
|
fn new(native_video_mode: DEVMODEW) -> Self {
|
2024-07-21 00:01:43 +02:00
|
|
|
const REQUIRED_FIELDS: u32 =
|
|
|
|
|
DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
|
2025-01-02 03:29:42 +03:00
|
|
|
assert!(has_flag(native_video_mode.dmFields, REQUIRED_FIELDS));
|
2019-07-29 21:16:14 +03:00
|
|
|
|
2025-01-02 03:29:42 +03:00
|
|
|
let mode = VideoMode {
|
|
|
|
|
size: (native_video_mode.dmPelsWidth, native_video_mode.dmPelsHeight).into(),
|
|
|
|
|
bit_depth: NonZeroU16::new(native_video_mode.dmBitsPerPel as u16),
|
|
|
|
|
refresh_rate_millihertz: NonZeroU32::new(native_video_mode.dmDisplayFrequency * 1000),
|
|
|
|
|
};
|
2019-07-29 21:16:14 +03:00
|
|
|
|
2025-01-02 03:29:42 +03:00
|
|
|
VideoModeHandle { mode, native_video_mode: Box::new(native_video_mode) }
|
2019-07-29 21:16:14 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
|
|
|
|
|
pub struct MonitorHandle(HMONITOR);
|
|
|
|
|
|
2025-02-23 22:40:11 -05:00
|
|
|
// Send and Sync are not implemented for HMONITOR, we have to wrap it and implement them manually.
|
2014-08-01 23:02:26 +02:00
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
unsafe impl Send for MonitorHandle {}
|
2025-02-23 22:40:11 -05:00
|
|
|
unsafe impl Sync 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,
|
2022-03-07 22:58:12 +01:00
|
|
|
_place: *mut RECT,
|
2018-06-14 19:42:18 -04:00
|
|
|
data: LPARAM,
|
|
|
|
|
) -> BOOL {
|
2019-02-05 10:30:33 -05:00
|
|
|
let monitors = data as *mut VecDeque<MonitorHandle>;
|
2023-09-29 16:07:44 +02:00
|
|
|
unsafe { (*monitors).push_back(MonitorHandle::new(hmonitor)) };
|
2022-03-07 22:58:12 +01:00
|
|
|
true.into() // 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 {
|
2022-03-07 22:58:12 +01:00
|
|
|
EnumDisplayMonitors(
|
2025-02-23 22:40:11 -05:00
|
|
|
ptr::null_mut(),
|
2022-03-07 22:58:12 +01:00
|
|
|
ptr::null(),
|
2018-06-16 10:14:12 -04:00
|
|
|
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 };
|
2022-03-07 22:58:12 +01:00
|
|
|
let hmonitor = unsafe { MonitorFromPoint(ORIGIN, 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 {
|
2022-03-07 22:58:12 +01:00
|
|
|
let hmonitor = unsafe { MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) };
|
2019-07-29 21:16:14 +03:00
|
|
|
MonitorHandle::new(hmonitor)
|
2019-02-05 10:30:33 -05:00
|
|
|
}
|
|
|
|
|
|
2022-03-07 22:58:12 +01:00
|
|
|
pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result<MONITORINFOEXW, io::Error> {
|
|
|
|
|
let mut monitor_info: MONITORINFOEXW = unsafe { mem::zeroed() };
|
|
|
|
|
monitor_info.monitorInfo.cbSize = mem::size_of::<MONITORINFOEXW>() as u32;
|
2018-06-14 19:42:18 -04:00
|
|
|
let status = unsafe {
|
2022-03-07 22:58:12 +01:00
|
|
|
GetMonitorInfoW(hmonitor, &mut monitor_info as *mut MONITORINFOEXW as *mut MONITORINFO)
|
2018-06-14 19:42:18 -04:00
|
|
|
};
|
2022-03-07 22:58:12 +01:00
|
|
|
if status == false.into() {
|
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();
|
2022-04-30 13:58:51 +02:00
|
|
|
Some(decode_wide(&monitor_info.szDevice).to_string_lossy().to_string())
|
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
|
|
|
}
|
|
|
|
|
|
2024-07-21 00:40:57 +02:00
|
|
|
pub(crate) fn size(&self) -> PhysicalSize<u32> {
|
2022-03-07 22:58:12 +01:00
|
|
|
let rc_monitor = get_monitor_info(self.0).unwrap().monitorInfo.rcMonitor;
|
2019-07-29 21:16:14 +03:00
|
|
|
PhysicalSize {
|
2022-03-07 22:58:12 +01:00
|
|
|
width: (rc_monitor.right - rc_monitor.left) as u32,
|
|
|
|
|
height: (rc_monitor.bottom - rc_monitor.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]
|
2024-07-23 19:59:37 +02:00
|
|
|
pub fn position(&self) -> Option<PhysicalPosition<i32>> {
|
2023-10-20 18:51:04 +08:00
|
|
|
get_monitor_info(self.0)
|
|
|
|
|
.map(|info| {
|
|
|
|
|
let rc_monitor = info.monitorInfo.rcMonitor;
|
|
|
|
|
PhysicalPosition { x: rc_monitor.left, y: rc_monitor.top }
|
|
|
|
|
})
|
2024-07-23 19:59:37 +02:00
|
|
|
.ok()
|
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
|
|
|
|
2024-07-21 00:01:43 +02:00
|
|
|
#[inline]
|
2025-01-02 03:29:42 +03:00
|
|
|
pub fn current_video_mode(&self) -> Option<VideoMode> {
|
2024-07-21 00:01:43 +02:00
|
|
|
let monitor_info = get_monitor_info(self.0).ok()?;
|
|
|
|
|
let device_name = monitor_info.szDevice.as_ptr();
|
|
|
|
|
unsafe {
|
|
|
|
|
let mut mode: DEVMODEW = mem::zeroed();
|
|
|
|
|
mode.dmSize = mem::size_of_val(&mode) as u16;
|
|
|
|
|
if EnumDisplaySettingsExW(device_name, ENUM_CURRENT_SETTINGS, &mut mode, 0)
|
|
|
|
|
== false.into()
|
|
|
|
|
{
|
|
|
|
|
None
|
|
|
|
|
} else {
|
2025-01-02 03:29:42 +03:00
|
|
|
Some(VideoModeHandle::new(mode).mode)
|
2024-07-21 00:01:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-02 03:29:42 +03:00
|
|
|
pub(crate) fn video_mode_handles(&self) -> impl Iterator<Item = VideoModeHandle> {
|
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
|
2025-01-02 03:29:42 +03:00
|
|
|
let mut modes = HashSet::<VideoModeHandle>::new();
|
2023-12-06 17:47:33 +01:00
|
|
|
|
|
|
|
|
let monitor_info = match get_monitor_info(self.0) {
|
|
|
|
|
Ok(monitor_info) => monitor_info,
|
|
|
|
|
Err(error) => {
|
2024-02-25 19:20:39 -08:00
|
|
|
tracing::warn!("Error from get_monitor_info: {error}");
|
2025-01-02 03:29:42 +03:00
|
|
|
return modes.into_iter();
|
2023-12-06 17:47:33 +01:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let device_name = monitor_info.szDevice.as_ptr();
|
|
|
|
|
let mut i = 0;
|
2019-06-12 21:07:25 +03:00
|
|
|
loop {
|
2023-12-06 17:47:33 +01:00
|
|
|
let mut mode: DEVMODEW = unsafe { mem::zeroed() };
|
|
|
|
|
mode.dmSize = mem::size_of_val(&mode) as u16;
|
|
|
|
|
if unsafe { EnumDisplaySettingsExW(device_name, i, &mut mode, 0) } == false.into() {
|
|
|
|
|
break;
|
2019-06-12 21:07:25 +03:00
|
|
|
}
|
2023-12-06 17:47:33 +01:00
|
|
|
|
2023-12-26 22:12:33 +01:00
|
|
|
// Use Ord impl of RootVideoModeHandle
|
2025-01-02 03:29:42 +03:00
|
|
|
modes.insert(VideoModeHandle::new(mode));
|
2023-12-06 17:47:33 +01:00
|
|
|
|
|
|
|
|
i += 1;
|
2019-06-12 21:07:25 +03:00
|
|
|
}
|
|
|
|
|
|
2025-01-02 03:29:42 +03:00
|
|
|
modes.into_iter()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
|
|
|
|
|
self.video_mode_handles().map(|mode| mode.mode)
|
2019-06-12 21:07:25 +03:00
|
|
|
}
|
2014-07-31 10:52:05 +02:00
|
|
|
}
|