monitor: refactor MonitorHandle to store dyn object

This also alters `VideoMode` to be a regular object and not reference
the `MonitorHandle`, since it's a static data.

Given that `VideoMode` set may change during runtime keeping the
reference as a some sort of validity may not be idea and propagating
errors when changing video mode could be more reliable.
This commit is contained in:
Kirill Chibisov 2024-09-21 20:27:12 +03:00
parent be1baf164c
commit f1c5afd84e
43 changed files with 726 additions and 826 deletions

View file

@ -1,7 +1,7 @@
use std::collections::{HashSet, VecDeque};
use std::hash::Hash;
use std::num::{NonZeroU16, NonZeroU32};
use std::{io, mem, ptr};
use std::{io, iter, mem, ptr};
use windows_sys::Win32::Foundation::{BOOL, HWND, LPARAM, POINT, RECT};
use windows_sys::Win32::Graphics::Gdi::{
@ -13,7 +13,7 @@ use windows_sys::Win32::Graphics::Gdi::{
use super::util::decode_wide;
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::monitor::VideoMode;
use crate::monitor::{MonitorHandleProvider, VideoMode};
use crate::platform_impl::platform::dpi::{dpi_to_scale_factor, get_monitor_dpi};
use crate::platform_impl::platform::util::has_flag;
@ -60,14 +60,6 @@ impl VideoModeHandle {
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
pub struct MonitorHandle(HMONITOR);
// Send and Sync are not implemented for HMONITOR, we have to wrap it and implement them manually.
unsafe impl Send for MonitorHandle {}
unsafe impl Sync for MonitorHandle {}
unsafe extern "system" fn monitor_enum_proc(
hmonitor: HMONITOR,
_hdc: HDC,
@ -116,27 +108,19 @@ pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result<MONITORINFOEXW, io:
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
pub struct MonitorHandle(HMONITOR);
// Send and Sync are not implemented for HMONITOR, we have to wrap it and implement them manually.
unsafe impl Send for MonitorHandle {}
unsafe impl Sync for MonitorHandle {}
impl MonitorHandle {
pub(crate) fn new(hmonitor: HMONITOR) -> Self {
MonitorHandle(hmonitor)
}
#[inline]
pub fn name(&self) -> Option<String> {
let monitor_info = get_monitor_info(self.0).unwrap();
Some(decode_wide(&monitor_info.szDevice).to_string_lossy().to_string())
}
#[inline]
pub fn native_identifier(&self) -> String {
self.name().unwrap()
}
#[inline]
pub fn hmonitor(&self) -> HMONITOR {
self.0
}
pub(crate) fn size(&self) -> PhysicalSize<u32> {
let rc_monitor = get_monitor_info(self.0).unwrap().monitorInfo.rcMonitor;
PhysicalSize {
@ -145,39 +129,7 @@ impl MonitorHandle {
}
}
#[inline]
pub fn position(&self) -> Option<PhysicalPosition<i32>> {
get_monitor_info(self.0)
.map(|info| {
let rc_monitor = info.monitorInfo.rcMonitor;
PhysicalPosition { x: rc_monitor.left, y: rc_monitor.top }
})
.ok()
}
#[inline]
pub fn scale_factor(&self) -> f64 {
dpi_to_scale_factor(get_monitor_dpi(self.0).unwrap_or(96))
}
#[inline]
pub fn current_video_mode(&self) -> Option<VideoMode> {
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 {
Some(VideoModeHandle::new(mode).mode)
}
}
}
pub(crate) fn video_mode_handles(&self) -> impl Iterator<Item = VideoModeHandle> {
pub(crate) fn video_mode_handles(&self) -> Box<dyn Iterator<Item = VideoModeHandle>> {
// EnumDisplaySettingsExW can return duplicate values (or some of the
// fields are probably changing, but we aren't looking at those fields
// anyway), so we're using a BTreeSet deduplicate
@ -187,7 +139,7 @@ impl MonitorHandle {
Ok(monitor_info) => monitor_info,
Err(error) => {
tracing::warn!("Error from get_monitor_info: {error}");
return modes.into_iter();
return Box::new(iter::empty());
},
};
@ -206,10 +158,54 @@ impl MonitorHandle {
i += 1;
}
modes.into_iter()
}
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.video_mode_handles().map(|mode| mode.mode)
Box::new(modes.into_iter())
}
}
impl MonitorHandleProvider for MonitorHandle {
fn id(&self) -> u128 {
self.native_id() as _
}
fn native_id(&self) -> u64 {
self.0 as _
}
fn name(&self) -> Option<std::borrow::Cow<'_, str>> {
let monitor_info = get_monitor_info(self.0).unwrap();
Some(decode_wide(&monitor_info.szDevice).to_string_lossy().to_string().into())
}
fn position(&self) -> Option<PhysicalPosition<i32>> {
get_monitor_info(self.0)
.map(|info| {
let rc_monitor = info.monitorInfo.rcMonitor;
PhysicalPosition { x: rc_monitor.left, y: rc_monitor.top }
})
.ok()
}
fn scale_factor(&self) -> f64 {
dpi_to_scale_factor(get_monitor_dpi(self.0).unwrap_or(96))
}
fn current_video_mode(&self) -> Option<crate::monitor::VideoMode> {
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 {
Some(VideoModeHandle::new(mode).mode)
}
}
}
fn video_modes(&self) -> Box<dyn Iterator<Item = VideoMode>> {
Box::new(self.video_mode_handles().map(|mode| mode.mode))
}
}