diff --git a/CHANGELOG.md b/CHANGELOG.md index d49e5d0b..40b9cd1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,8 @@ And please only add new entries to the top of this list, right below the `# Unre - Fix on macOS `WindowBuilder::with_disallow_hidpi`, setting true or false by the user no matter the SO default value. - `EventLoopBuilder::build` will now panic when the `EventLoop` is being created more than once. - Added `From` for `WindowId` and `From` for `u64`. +- Added `MonitorHandle::refresh_rate_millihertz` to get monitor's refresh rate. +- **Breaking**, Replaced `VideoMode::refresh_rate` with `VideoMode::refresh_rate_millihertz` providing better precision. # 0.26.1 (2022-01-05) diff --git a/src/monitor.rs b/src/monitor.rs index 7611f94b..0ee48177 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -39,8 +39,8 @@ impl Ord for VideoMode { self.monitor().cmp(&other.monitor()).then( size.cmp(&other_size) .then( - self.refresh_rate() - .cmp(&other.refresh_rate()) + self.refresh_rate_millihertz() + .cmp(&other.refresh_rate_millihertz()) .then(self.bit_depth().cmp(&other.bit_depth())), ) .reverse(), @@ -68,12 +68,10 @@ impl VideoMode { self.video_mode.bit_depth() } - /// Returns the refresh rate of this video mode. **Note**: the returned - /// refresh rate is an integer approximation, and you shouldn't rely on this - /// value to be exact. + /// Returns the refresh rate of this video mode in mHz. #[inline] - pub fn refresh_rate(&self) -> u16 { - self.video_mode.refresh_rate() + pub fn refresh_rate_millihertz(&self) -> u32 { + self.video_mode.refresh_rate_millihertz() } /// Returns the monitor that this video mode is valid for. Each monitor has @@ -88,10 +86,10 @@ impl std::fmt::Display for VideoMode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "{}x{} @ {} Hz ({} bpp)", + "{}x{} @ {} mHz ({} bpp)", self.size().width, self.size().height, - self.refresh_rate(), + self.refresh_rate_millihertz(), self.bit_depth() ) } @@ -141,6 +139,15 @@ impl MonitorHandle { self.inner.position() } + /// The monitor refresh rate used by the system. + /// + /// When using exclusive fullscreen, the refresh rate of the [`VideoMode`] that was used to + /// enter fullscreen should be used instead. + #[inline] + pub fn refresh_rate_millihertz(&self) -> Option { + self.inner.refresh_rate_millihertz() + } + /// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa. /// /// See the [`dpi`](crate::dpi) module for more information. diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index e37bd1f5..1d39c862 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -857,6 +857,11 @@ impl MonitorHandle { .unwrap_or(1.0) } + pub fn refresh_rate_millihertz(&self) -> Option { + // FIXME no way to get real refrsh rate for now. + None + } + pub fn video_modes(&self) -> impl Iterator { let size = self.size().into(); // FIXME this is not the real refresh rate @@ -865,7 +870,7 @@ impl MonitorHandle { video_mode: VideoMode { size, bit_depth: 32, - refresh_rate: 60, + refresh_rate_millihertz: 60000, monitor: self.clone(), }, }) @@ -876,7 +881,7 @@ impl MonitorHandle { pub struct VideoMode { size: (u32, u32), bit_depth: u16, - refresh_rate: u16, + refresh_rate_millihertz: u32, monitor: MonitorHandle, } @@ -889,8 +894,8 @@ impl VideoMode { self.bit_depth } - pub fn refresh_rate(&self) -> u16 { - self.refresh_rate + pub fn refresh_rate_millihertz(&self) -> u32 { + self.refresh_rate_millihertz } pub fn monitor(&self) -> monitor::MonitorHandle { diff --git a/src/platform_impl/ios/monitor.rs b/src/platform_impl/ios/monitor.rs index 26f4af06..42516cb0 100644 --- a/src/platform_impl/ios/monitor.rs +++ b/src/platform_impl/ios/monitor.rs @@ -17,7 +17,7 @@ use crate::{ pub struct VideoMode { pub(crate) size: (u32, u32), pub(crate) bit_depth: u16, - pub(crate) refresh_rate: u16, + pub(crate) refresh_rate_millihertz: u32, pub(crate) screen_mode: NativeDisplayMode, pub(crate) monitor: MonitorHandle, } @@ -49,7 +49,7 @@ impl Clone for VideoMode { VideoMode { size: self.size, bit_depth: self.bit_depth, - refresh_rate: self.refresh_rate, + refresh_rate_millihertz: self.refresh_rate_millihertz, screen_mode: self.screen_mode.clone(), monitor: self.monitor.clone(), } @@ -59,30 +59,14 @@ impl Clone for VideoMode { impl VideoMode { unsafe fn retained_new(uiscreen: id, screen_mode: id) -> VideoMode { assert_main_thread!("`VideoMode` can only be created on the main thread on iOS"); - let os_capabilities = app_state::os_capabilities(); - let refresh_rate: NSInteger = if os_capabilities.maximum_frames_per_second { - msg_send![uiscreen, maximumFramesPerSecond] - } else { - // https://developer.apple.com/library/archive/technotes/tn2460/_index.html - // https://en.wikipedia.org/wiki/IPad_Pro#Model_comparison - // - // All iOS devices support 60 fps, and on devices where `maximumFramesPerSecond` is not - // supported, they are all guaranteed to have 60hz refresh rates. This does not - // correctly handle external displays. ProMotion displays support 120fps, but they were - // introduced at the same time as the `maximumFramesPerSecond` API. - // - // FIXME: earlier OSs could calculate the refresh rate using - // `-[CADisplayLink duration]`. - os_capabilities.maximum_frames_per_second_err_msg("defaulting to 60 fps"); - 60 - }; + let refresh_rate_millihertz = refresh_rate_millihertz(uiscreen); let size: CGSize = msg_send![screen_mode, size]; let screen_mode: id = msg_send![screen_mode, retain]; let screen_mode = NativeDisplayMode(screen_mode); VideoMode { size: (size.width as u32, size.height as u32), bit_depth: 32, - refresh_rate: refresh_rate as u16, + refresh_rate_millihertz, screen_mode, monitor: MonitorHandle::retained_new(uiscreen), } @@ -96,8 +80,8 @@ impl VideoMode { self.bit_depth } - pub fn refresh_rate(&self) -> u16 { - self.refresh_rate + pub fn refresh_rate_millihertz(&self) -> u32 { + self.refresh_rate_millihertz } pub fn monitor(&self) -> RootMonitorHandle { @@ -239,6 +223,10 @@ impl Inner { } } + pub fn refresh_rate_millihertz(&self) -> Option { + Some(refresh_rate_millihertz(self.uiscreen)) + } + pub fn video_modes(&self) -> impl Iterator { let mut modes = BTreeSet::new(); unsafe { @@ -257,6 +245,30 @@ impl Inner { } } +fn refresh_rate_millihertz(uiscreen: id) -> u32 { + let refresh_rate_millihertz: NSInteger = unsafe { + let os_capabilities = app_state::os_capabilities(); + if os_capabilities.maximum_frames_per_second { + msg_send![uiscreen, maximumFramesPerSecond] + } else { + // https://developer.apple.com/library/archive/technotes/tn2460/_index.html + // https://en.wikipedia.org/wiki/IPad_Pro#Model_comparison + // + // All iOS devices support 60 fps, and on devices where `maximumFramesPerSecond` is not + // supported, they are all guaranteed to have 60hz refresh rates. This does not + // correctly handle external displays. ProMotion displays support 120fps, but they were + // introduced at the same time as the `maximumFramesPerSecond` API. + // + // FIXME: earlier OSs could calculate the refresh rate using + // `-[CADisplayLink duration]`. + os_capabilities.maximum_frames_per_second_err_msg("defaulting to 60 fps"); + 60 + } + }; + + refresh_rate_millihertz as u32 * 1000 +} + // MonitorHandleExtIOS impl Inner { pub fn ui_screen(&self) -> id { diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 37b1d453..6876c48b 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -256,6 +256,11 @@ impl MonitorHandle { x11_or_wayland!(match self; MonitorHandle(m) => m.position()) } + #[inline] + pub fn refresh_rate_millihertz(&self) -> Option { + x11_or_wayland!(match self; MonitorHandle(m) => m.refresh_rate_millihertz()) + } + #[inline] pub fn scale_factor(&self) -> f64 { x11_or_wayland!(match self; MonitorHandle(m) => m.scale_factor() as f64) @@ -287,8 +292,8 @@ impl VideoMode { } #[inline] - pub fn refresh_rate(&self) -> u16 { - x11_or_wayland!(match self; VideoMode(m) => m.refresh_rate()) + pub fn refresh_rate_millihertz(&self) -> u32 { + x11_or_wayland!(match self; VideoMode(m) => m.refresh_rate_millihertz()) } #[inline] diff --git a/src/platform_impl/linux/wayland/output.rs b/src/platform_impl/linux/wayland/output.rs index a01b0d83..e3a43478 100644 --- a/src/platform_impl/linux/wayland/output.rs +++ b/src/platform_impl/linux/wayland/output.rs @@ -167,6 +167,16 @@ impl MonitorHandle { .into() } + #[inline] + pub fn refresh_rate_millihertz(&self) -> Option { + sctk::output::with_output_info(&self.proxy, |info| { + info.modes + .iter() + .find_map(|mode| mode.is_current.then(|| mode.refresh_rate as u32)) + }) + .flatten() + } + #[inline] pub fn scale_factor(&self) -> i32 { sctk::output::with_output_info(&self.proxy, |info| info.scale_factor).unwrap_or(1) @@ -182,7 +192,7 @@ impl MonitorHandle { modes.into_iter().map(move |mode| RootVideoMode { video_mode: PlatformVideoMode::Wayland(VideoMode { size: (mode.dimensions.0 as u32, mode.dimensions.1 as u32).into(), - refresh_rate: (mode.refresh_rate as f32 / 1000.0).round() as u16, + refresh_rate_millihertz: mode.refresh_rate as u32, bit_depth: 32, monitor: monitor.clone(), }), @@ -194,7 +204,7 @@ impl MonitorHandle { pub struct VideoMode { pub(crate) size: PhysicalSize, pub(crate) bit_depth: u16, - pub(crate) refresh_rate: u16, + pub(crate) refresh_rate_millihertz: u32, pub(crate) monitor: MonitorHandle, } @@ -210,8 +220,8 @@ impl VideoMode { } #[inline] - pub fn refresh_rate(&self) -> u16 { - self.refresh_rate + pub fn refresh_rate_millihertz(&self) -> u32 { + self.refresh_rate_millihertz } pub fn monitor(&self) -> RootMonitorHandle { diff --git a/src/platform_impl/linux/x11/monitor.rs b/src/platform_impl/linux/x11/monitor.rs index 9d3d3c8d..8a696e8a 100644 --- a/src/platform_impl/linux/x11/monitor.rs +++ b/src/platform_impl/linux/x11/monitor.rs @@ -1,4 +1,5 @@ use std::os::raw::*; +use std::slice; use once_cell::sync::Lazy; use parking_lot::Mutex; @@ -6,7 +7,7 @@ use parking_lot::Mutex; use super::{ ffi::{ RRCrtc, RRCrtcChangeNotifyMask, RRMode, RROutputPropertyNotifyMask, - RRScreenChangeNotifyMask, True, Window, XRRCrtcInfo, XRRScreenResources, + RRScreenChangeNotifyMask, True, Window, XRRCrtcInfo, XRRModeInfo, XRRScreenResources, }, util, XConnection, XError, }; @@ -30,7 +31,7 @@ pub fn invalidate_cached_monitor_list() -> Option> { pub struct VideoMode { pub(crate) size: (u32, u32), pub(crate) bit_depth: u16, - pub(crate) refresh_rate: u16, + pub(crate) refresh_rate_millihertz: u32, pub(crate) native_mode: RRMode, pub(crate) monitor: Option, } @@ -47,8 +48,8 @@ impl VideoMode { } #[inline] - pub fn refresh_rate(&self) -> u16 { - self.refresh_rate + pub fn refresh_rate_millihertz(&self) -> u32 { + self.refresh_rate_millihertz } #[inline] @@ -71,6 +72,8 @@ pub struct MonitorHandle { position: (i32, i32), /// If the monitor is the primary one primary: bool, + /// The refresh rate used by monitor. + refresh_rate_millihertz: Option, /// The DPI scale factor pub(crate) scale_factor: f64, /// Used to determine which windows are on this monitor @@ -105,6 +108,15 @@ impl std::hash::Hash for MonitorHandle { } } +#[inline] +pub fn mode_refresh_rate_millihertz(mode: &XRRModeInfo) -> Option { + if mode.dotClock > 0 && mode.hTotal > 0 && mode.vTotal > 0 { + Some((mode.dotClock as u64 * 1000 / (mode.hTotal as u64 * mode.vTotal as u64)) as u32) + } else { + None + } +} + impl MonitorHandle { fn new( xconn: &XConnection, @@ -116,10 +128,22 @@ impl MonitorHandle { let (name, scale_factor, video_modes) = unsafe { xconn.get_output_info(resources, crtc)? }; let dimensions = unsafe { ((*crtc).width as u32, (*crtc).height as u32) }; let position = unsafe { ((*crtc).x as i32, (*crtc).y as i32) }; + + // Get the refresh rate of the current video mode. + let current_mode = unsafe { (*crtc).mode }; + let screen_modes = + unsafe { slice::from_raw_parts((*resources).modes, (*resources).nmode as usize) }; + let refresh_rate_millihertz = screen_modes + .iter() + .find(|mode| mode.id == current_mode) + .and_then(mode_refresh_rate_millihertz); + let rect = util::AaRect::new(position, dimensions); + Some(MonitorHandle { id, name, + refresh_rate_millihertz, scale_factor, dimensions, position, @@ -136,6 +160,7 @@ impl MonitorHandle { scale_factor: 1.0, dimensions: (1, 1), position: (0, 0), + refresh_rate_millihertz: None, primary: true, rect: util::AaRect::new((0, 0), (1, 1)), video_modes: Vec::new(), @@ -164,6 +189,10 @@ impl MonitorHandle { self.position.into() } + pub fn refresh_rate_millihertz(&self) -> Option { + self.refresh_rate_millihertz + } + #[inline] pub fn scale_factor(&self) -> f64 { self.scale_factor diff --git a/src/platform_impl/linux/x11/util/randr.rs b/src/platform_impl/linux/x11/util/randr.rs index 35001524..2ce46e70 100644 --- a/src/platform_impl/linux/x11/util/randr.rs +++ b/src/platform_impl/linux/x11/util/randr.rs @@ -4,6 +4,7 @@ use super::{ ffi::{CurrentTime, RRCrtc, RRMode, Success, XRRCrtcInfo, XRRScreenResources}, *, }; +use crate::platform_impl::platform::x11::monitor; use crate::{dpi::validate_scale_factor, platform_impl::platform::x11::VideoMode}; /// Represents values of `WINIT_HIDPI_FACTOR`. @@ -80,18 +81,13 @@ impl XConnection { // XRROutputInfo contains an array of mode ids that correspond to // modes in the array in XRRScreenResources .filter(|x| output_modes.iter().any(|id| x.id == *id)) - .map(|x| { - let refresh_rate = if x.dotClock > 0 && x.hTotal > 0 && x.vTotal > 0 { - x.dotClock as u64 * 1000 / (x.hTotal as u64 * x.vTotal as u64) - } else { - 0 - }; - + .map(|mode| { VideoMode { - size: (x.width, x.height), - refresh_rate: (refresh_rate as f32 / 1000.0).round() as u16, + size: (mode.width, mode.height), + refresh_rate_millihertz: monitor::mode_refresh_rate_millihertz(mode) + .unwrap_or(0), bit_depth: bit_depth as u16, - native_mode: x.id, + native_mode: mode.id, // This is populated in `MonitorHandle::video_modes` as the // video mode is returned to the user monitor: None, diff --git a/src/platform_impl/macos/monitor.rs b/src/platform_impl/macos/monitor.rs index d421951e..2b93be6a 100644 --- a/src/platform_impl/macos/monitor.rs +++ b/src/platform_impl/macos/monitor.rs @@ -21,7 +21,7 @@ use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds}; pub struct VideoMode { pub(crate) size: (u32, u32), pub(crate) bit_depth: u16, - pub(crate) refresh_rate: u16, + pub(crate) refresh_rate_millihertz: u32, pub(crate) monitor: MonitorHandle, pub(crate) native_mode: NativeDisplayMode, } @@ -30,7 +30,7 @@ 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.refresh_rate_millihertz == other.refresh_rate_millihertz && self.monitor == other.monitor } } @@ -41,7 +41,7 @@ impl std::hash::Hash for VideoMode { fn hash(&self, state: &mut H) { self.size.hash(state); self.bit_depth.hash(state); - self.refresh_rate.hash(state); + self.refresh_rate_millihertz.hash(state); self.monitor.hash(state); } } @@ -51,7 +51,7 @@ impl std::fmt::Debug for VideoMode { f.debug_struct("VideoMode") .field("size", &self.size) .field("bit_depth", &self.bit_depth) - .field("refresh_rate", &self.refresh_rate) + .field("refresh_rate_millihertz", &self.refresh_rate_millihertz) .field("monitor", &self.monitor) .finish() } @@ -87,8 +87,8 @@ impl VideoMode { self.bit_depth } - pub fn refresh_rate(&self) -> u16 { - self.refresh_rate + pub fn refresh_rate_millihertz(&self) -> u32 { + self.refresh_rate_millihertz } pub fn monitor(&self) -> RootMonitorHandle { @@ -220,8 +220,8 @@ impl MonitorHandle { unsafe { NSScreen::backingScaleFactor(screen) as f64 } } - pub fn video_modes(&self) -> impl Iterator { - let cv_refresh_rate = unsafe { + pub fn refresh_rate_millihertz(&self) -> Option { + unsafe { let mut display_link = std::ptr::null_mut(); assert_eq!( ffi::CVDisplayLinkCreateWithCGDisplay(self.0, &mut display_link), @@ -233,9 +233,12 @@ impl MonitorHandle { // This value is indefinite if an invalid display link was specified assert!(time.flags & ffi::kCVTimeIsIndefinite == 0); - time.time_scale as i64 / time.time_value - }; + Some((time.time_scale as i64 / time.time_value * 1000) as u32) + } + } + pub fn video_modes(&self) -> impl Iterator { + let refresh_rate_millihertz = self.refresh_rate_millihertz().unwrap_or(0); let monitor = self.clone(); unsafe { @@ -255,14 +258,15 @@ impl MonitorHandle { }; modes.into_iter().map(move |mode| { - let cg_refresh_rate = ffi::CGDisplayModeGetRefreshRate(mode).round() as i64; + let cg_refresh_rate_millihertz = + ffi::CGDisplayModeGetRefreshRate(mode).round() as i64; // CGDisplayModeGetRefreshRate returns 0.0 for any display that // isn't a CRT - let refresh_rate = if cg_refresh_rate > 0 { - cg_refresh_rate + let refresh_rate_millihertz = if cg_refresh_rate_millihertz > 0 { + (cg_refresh_rate_millihertz * 1000) as u32 } else { - cv_refresh_rate + refresh_rate_millihertz }; let pixel_encoding = @@ -283,7 +287,7 @@ impl MonitorHandle { ffi::CGDisplayModeGetPixelWidth(mode) as u32, ffi::CGDisplayModeGetPixelHeight(mode) as u32, ), - refresh_rate: refresh_rate as u16, + refresh_rate_millihertz, bit_depth, monitor: monitor.clone(), native_mode: NativeDisplayMode(mode), diff --git a/src/platform_impl/web/monitor.rs b/src/platform_impl/web/monitor.rs index 780ffccd..5bbcc443 100644 --- a/src/platform_impl/web/monitor.rs +++ b/src/platform_impl/web/monitor.rs @@ -17,6 +17,10 @@ impl MonitorHandle { None } + pub fn refresh_rate_millihertz(&self) -> Option { + None + } + pub fn size(&self) -> PhysicalSize { PhysicalSize { width: 0, @@ -41,8 +45,8 @@ impl VideoMode { unimplemented!(); } - pub fn refresh_rate(&self) -> u16 { - 32 + pub fn refresh_rate_millihertz(&self) -> u32 { + 32000 } pub fn monitor(&self) -> RootMonitorHandle { diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index 595c75ef..46905956 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -9,8 +9,8 @@ use windows_sys::Win32::{ Graphics::Gdi::{ EnumDisplayMonitors, EnumDisplaySettingsExW, GetMonitorInfoW, MonitorFromPoint, MonitorFromWindow, DEVMODEW, DM_BITSPERPEL, DM_DISPLAYFREQUENCY, DM_PELSHEIGHT, - DM_PELSWIDTH, HDC, HMONITOR, MONITORINFO, MONITORINFOEXW, MONITOR_DEFAULTTONEAREST, - MONITOR_DEFAULTTOPRIMARY, + DM_PELSWIDTH, ENUM_CURRENT_SETTINGS, HDC, HMONITOR, MONITORINFO, MONITORINFOEXW, + MONITOR_DEFAULTTONEAREST, MONITOR_DEFAULTTOPRIMARY, }, }; @@ -29,7 +29,7 @@ use crate::{ pub struct VideoMode { pub(crate) size: (u32, u32), pub(crate) bit_depth: u16, - pub(crate) refresh_rate: u16, + pub(crate) refresh_rate_millihertz: u32, pub(crate) monitor: MonitorHandle, // DEVMODEW is huge so we box it to avoid blowing up the size of winit::window::Fullscreen pub(crate) native_video_mode: Box, @@ -39,7 +39,7 @@ 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.refresh_rate_millihertz == other.refresh_rate_millihertz && self.monitor == other.monitor } } @@ -50,7 +50,7 @@ impl std::hash::Hash for VideoMode { fn hash(&self, state: &mut H) { self.size.hash(state); self.bit_depth.hash(state); - self.refresh_rate.hash(state); + self.refresh_rate_millihertz.hash(state); self.monitor.hash(state); } } @@ -60,7 +60,7 @@ impl std::fmt::Debug for VideoMode { f.debug_struct("VideoMode") .field("size", &self.size) .field("bit_depth", &self.bit_depth) - .field("refresh_rate", &self.refresh_rate) + .field("refresh_rate_millihertz", &self.refresh_rate_millihertz) .field("monitor", &self.monitor) .finish() } @@ -75,8 +75,8 @@ impl VideoMode { self.bit_depth } - pub fn refresh_rate(&self) -> u16 { - self.refresh_rate + pub fn refresh_rate_millihertz(&self) -> u32 { + self.refresh_rate_millihertz } pub fn monitor(&self) -> RootMonitorHandle { @@ -192,6 +192,23 @@ impl MonitorHandle { } } + #[inline] + pub fn refresh_rate_millihertz(&self) -> Option { + let monitor_info = get_monitor_info(self.0).unwrap(); + 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(mode.dmDisplayFrequency * 1000) + } + } + } + #[inline] pub fn position(&self) -> PhysicalPosition { let rc_monitor = get_monitor_info(self.0).unwrap().monitorInfo.rcMonitor; @@ -233,7 +250,7 @@ impl MonitorHandle { video_mode: VideoMode { size: (mode.dmPelsWidth, mode.dmPelsHeight), bit_depth: mode.dmBitsPerPel as u16, - refresh_rate: mode.dmDisplayFrequency as u16, + refresh_rate_millihertz: mode.dmDisplayFrequency as u32 * 1000, monitor: self.clone(), native_video_mode: Box::new(mode), },