From 2e53533cc161d2bc6a0f579dccadf6c521999be7 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sun, 21 Jul 2024 00:01:43 +0200 Subject: [PATCH 1/4] Add `MonitorHandle::current_video_mode()` --- src/changelog/unreleased.md | 1 + src/monitor.rs | 6 ++ src/platform_impl/android/mod.rs | 8 +- src/platform_impl/apple/appkit/monitor.rs | 107 +++++++++++++--------- src/platform_impl/apple/uikit/monitor.rs | 10 ++ src/platform_impl/linux/mod.rs | 5 + src/platform_impl/linux/wayland/output.rs | 30 ++++-- src/platform_impl/linux/x11/monitor.rs | 22 ++--- src/platform_impl/linux/x11/util/randr.rs | 2 + src/platform_impl/orbital/mod.rs | 8 +- src/platform_impl/web/monitor.rs | 4 + src/platform_impl/windows/monitor.rs | 43 ++++++--- 12 files changed, 167 insertions(+), 79 deletions(-) diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 0ffccfb9b7..98ab2dc793 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -61,6 +61,7 @@ changelog entry. the primary finger in a multi-touch interaction. - Implement `Clone`, `Copy`, `Debug`, `Deserialize`, `Eq`, `Hash`, `Ord`, `PartialEq`, `PartialOrd` and `Serialize` on many types. +- Add `MonitorHandle::current_video_mode()`. ### Changed diff --git a/src/monitor.rs b/src/monitor.rs index f25eb79efd..d49742e368 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -201,6 +201,12 @@ impl MonitorHandle { self.inner.scale_factor() } + /// Returns the currently active video mode of this monitor. + #[inline] + pub fn current_video_mode(&self) -> Option { + self.inner.current_video_mode().map(|video_mode| VideoModeHandle { video_mode }) + } + /// Returns all fullscreen video modes supported by this monitor. #[inline] pub fn video_modes(&self) -> impl Iterator { diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 527f2f053f..1594f7d743 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -1003,17 +1003,21 @@ impl MonitorHandle { None } - pub fn video_modes(&self) -> impl Iterator { + pub fn current_video_mode(&self) -> Option { let size = self.size().into(); // FIXME this is not the real refresh rate // (it is guaranteed to support 32 bit color though) - std::iter::once(VideoModeHandle { + Some(VideoModeHandle { size, bit_depth: 32, refresh_rate_millihertz: 60000, monitor: self.clone(), }) } + + pub fn video_modes(&self) -> impl Iterator { + self.current_video_mode().into_iter() + } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] diff --git a/src/platform_impl/apple/appkit/monitor.rs b/src/platform_impl/apple/appkit/monitor.rs index e78d84f0be..6b793f762b 100644 --- a/src/platform_impl/apple/appkit/monitor.rs +++ b/src/platform_impl/apple/appkit/monitor.rs @@ -80,6 +80,34 @@ impl Clone for NativeDisplayMode { } impl VideoModeHandle { + fn new(monitor: MonitorHandle, mode: NativeDisplayMode, refresh_rate_millihertz: u32) -> Self { + unsafe { + let pixel_encoding = + CFString::wrap_under_create_rule(ffi::CGDisplayModeCopyPixelEncoding(mode.0)) + .to_string(); + let bit_depth = if pixel_encoding.eq_ignore_ascii_case(ffi::IO32BitDirectPixels) { + 32 + } else if pixel_encoding.eq_ignore_ascii_case(ffi::IO16BitDirectPixels) { + 16 + } else if pixel_encoding.eq_ignore_ascii_case(ffi::kIO30BitDirectPixels) { + 30 + } else { + unimplemented!() + }; + + VideoModeHandle { + size: PhysicalSize::new( + ffi::CGDisplayModeGetPixelWidth(mode.0) as u32, + ffi::CGDisplayModeGetPixelHeight(mode.0) as u32, + ), + refresh_rate_millihertz, + bit_depth, + monitor: monitor.clone(), + native_mode: mode, + } + } + } + pub fn size(&self) -> PhysicalSize { self.size } @@ -212,29 +240,15 @@ impl MonitorHandle { } pub fn refresh_rate_millihertz(&self) -> Option { - unsafe { - let current_display_mode = NativeDisplayMode(CGDisplayCopyDisplayMode(self.0) as _); - let refresh_rate = ffi::CGDisplayModeGetRefreshRate(current_display_mode.0); - if refresh_rate > 0.0 { - return Some((refresh_rate * 1000.0).round() as u32); - } - - let mut display_link = std::ptr::null_mut(); - if ffi::CVDisplayLinkCreateWithCGDisplay(self.0, &mut display_link) - != ffi::kCVReturnSuccess - { - return None; - } - let time = ffi::CVDisplayLinkGetNominalOutputVideoRefreshPeriod(display_link); - ffi::CVDisplayLinkRelease(display_link); - - // This value is indefinite if an invalid display link was specified - if time.flags & ffi::kCVTimeIsIndefinite != 0 { - return None; - } + let current_display_mode = + NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _); + refresh_rate_millihertz(self.0, ¤t_display_mode) + } - (time.time_scale as i64).checked_div(time.time_value).map(|v| (v * 1000) as u32) - } + pub fn current_video_mode(&self) -> Option { + let mode = NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _); + let refresh_rate_millihertz = refresh_rate_millihertz(self.0, &mode).unwrap_or(0); + Some(VideoModeHandle::new(self.clone(), mode, refresh_rate_millihertz)) } pub fn video_modes(&self) -> impl Iterator { @@ -268,29 +282,11 @@ impl MonitorHandle { refresh_rate_millihertz }; - let pixel_encoding = - CFString::wrap_under_create_rule(ffi::CGDisplayModeCopyPixelEncoding(mode)) - .to_string(); - let bit_depth = if pixel_encoding.eq_ignore_ascii_case(ffi::IO32BitDirectPixels) { - 32 - } else if pixel_encoding.eq_ignore_ascii_case(ffi::IO16BitDirectPixels) { - 16 - } else if pixel_encoding.eq_ignore_ascii_case(ffi::kIO30BitDirectPixels) { - 30 - } else { - unimplemented!() - }; - - VideoModeHandle { - size: PhysicalSize::new( - ffi::CGDisplayModeGetPixelWidth(mode) as u32, - ffi::CGDisplayModeGetPixelHeight(mode) as u32, - ), + VideoModeHandle::new( + monitor.clone(), + NativeDisplayMode(mode), refresh_rate_millihertz, - bit_depth, - monitor: monitor.clone(), - native_mode: NativeDisplayMode(mode), - } + ) }) } } @@ -349,3 +345,26 @@ pub(crate) fn flip_window_screen_coordinates(frame: NSRect) -> NSPoint { let y = main_screen_height - frame.size.height - frame.origin.y; NSPoint::new(frame.origin.x, y) } + +fn refresh_rate_millihertz(id: CGDirectDisplayID, mode: &NativeDisplayMode) -> Option { + unsafe { + let refresh_rate = ffi::CGDisplayModeGetRefreshRate(mode.0); + if refresh_rate > 0.0 { + return Some((refresh_rate * 1000.0).round() as u32); + } + + let mut display_link = std::ptr::null_mut(); + if ffi::CVDisplayLinkCreateWithCGDisplay(id, &mut display_link) != ffi::kCVReturnSuccess { + return None; + } + let time = ffi::CVDisplayLinkGetNominalOutputVideoRefreshPeriod(display_link); + ffi::CVDisplayLinkRelease(display_link); + + // This value is indefinite if an invalid display link was specified + if time.flags & ffi::kCVTimeIsIndefinite != 0 { + return None; + } + + (time.time_scale as i64).checked_div(time.time_value).map(|v| (v * 1000) as u32) + } +} diff --git a/src/platform_impl/apple/uikit/monitor.rs b/src/platform_impl/apple/uikit/monitor.rs index 1871ffb19a..2975afc762 100644 --- a/src/platform_impl/apple/uikit/monitor.rs +++ b/src/platform_impl/apple/uikit/monitor.rs @@ -182,6 +182,16 @@ impl MonitorHandle { Some(self.ui_screen.get_on_main(|ui_screen| refresh_rate_millihertz(ui_screen))) } + pub fn current_video_mode(&self) -> Option { + Some(run_on_main(|mtm| { + VideoModeHandle::new( + self.ui_screen(mtm).clone(), + self.ui_screen(mtm).currentMode().unwrap(), + mtm, + ) + })) + } + pub fn video_modes(&self) -> impl Iterator { run_on_main(|mtm| { let ui_screen = self.ui_screen(mtm); diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 734b328995..69edafdaed 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -261,6 +261,11 @@ impl MonitorHandle { x11_or_wayland!(match self; MonitorHandle(m) => m.scale_factor() as _) } + #[inline] + pub fn current_video_mode(&self) -> Option { + x11_or_wayland!(match self; MonitorHandle(m) => m.current_video_mode()) + } + #[inline] pub fn video_modes(&self) -> Box> { x11_or_wayland!(match self; MonitorHandle(m) => Box::new(m.video_modes())) diff --git a/src/platform_impl/linux/wayland/output.rs b/src/platform_impl/linux/wayland/output.rs index 42537ced88..8c793be664 100644 --- a/src/platform_impl/linux/wayland/output.rs +++ b/src/platform_impl/linux/wayland/output.rs @@ -1,4 +1,4 @@ -use sctk::output::OutputData; +use sctk::output::{Mode, OutputData}; use sctk::reexports::client::protocol::wl_output::WlOutput; use sctk::reexports::client::Proxy; @@ -73,6 +73,18 @@ impl MonitorHandle { output_data.scale_factor() } + #[inline] + pub fn current_video_mode(&self) -> Option { + let output_data = self.proxy.data::().unwrap(); + output_data.with_output_info(|info| { + let mode = info.modes.iter().find(|mode| mode.current).cloned(); + + mode.map(|mode| { + PlatformVideoModeHandle::Wayland(VideoModeHandle::new(self.clone(), mode)) + }) + }) + } + #[inline] pub fn video_modes(&self) -> impl Iterator { let output_data = self.proxy.data::().unwrap(); @@ -81,12 +93,7 @@ impl MonitorHandle { let monitor = self.clone(); modes.into_iter().map(move |mode| { - PlatformVideoModeHandle::Wayland(VideoModeHandle { - size: (mode.dimensions.0 as u32, mode.dimensions.1 as u32).into(), - refresh_rate_millihertz: mode.refresh_rate as u32, - bit_depth: 32, - monitor: monitor.clone(), - }) + PlatformVideoModeHandle::Wayland(VideoModeHandle::new(monitor.clone(), mode)) }) } } @@ -126,6 +133,15 @@ pub struct VideoModeHandle { } impl VideoModeHandle { + fn new(monitor: MonitorHandle, mode: Mode) -> Self { + VideoModeHandle { + size: (mode.dimensions.0 as u32, mode.dimensions.1 as u32).into(), + refresh_rate_millihertz: mode.refresh_rate as u32, + bit_depth: 32, + monitor: monitor.clone(), + } + } + #[inline] pub fn size(&self) -> PhysicalSize { self.size diff --git a/src/platform_impl/linux/x11/monitor.rs b/src/platform_impl/linux/x11/monitor.rs index 2a26a26d90..0dd4abfce7 100644 --- a/src/platform_impl/linux/x11/monitor.rs +++ b/src/platform_impl/linux/x11/monitor.rs @@ -18,6 +18,7 @@ impl XConnection { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct VideoModeHandle { + pub(crate) current: bool, pub(crate) size: (u32, u32), pub(crate) bit_depth: u16, pub(crate) refresh_rate_millihertz: u32, @@ -59,8 +60,6 @@ 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 @@ -117,20 +116,11 @@ impl MonitorHandle { let dimensions = (crtc.width as u32, crtc.height as u32); let position = (crtc.x as i32, crtc.y as i32); - // Get the refresh rate of the current video mode. - let current_mode = crtc.mode; - let screen_modes = resources.modes(); - 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, @@ -147,7 +137,6 @@ 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(), @@ -177,7 +166,9 @@ impl MonitorHandle { } pub fn refresh_rate_millihertz(&self) -> Option { - self.refresh_rate_millihertz + self.video_modes + .iter() + .find_map(|mode| mode.current.then_some(mode.refresh_rate_millihertz)) } #[inline] @@ -185,6 +176,11 @@ impl MonitorHandle { self.scale_factor } + #[inline] + pub fn current_video_mode(&self) -> Option { + self.video_modes.iter().find(|mode| mode.current).cloned().map(PlatformVideoModeHandle::X) + } + #[inline] pub fn video_modes(&self) -> impl Iterator { let monitor = self.clone(); diff --git a/src/platform_impl/linux/x11/util/randr.rs b/src/platform_impl/linux/x11/util/randr.rs index 19df178a85..4f27801bb8 100644 --- a/src/platform_impl/linux/x11/util/randr.rs +++ b/src/platform_impl/linux/x11/util/randr.rs @@ -74,6 +74,7 @@ impl XConnection { let bit_depth = self.default_root().root_depth; let output_modes = &output_info.modes; let resource_modes = resources.modes(); + let current_mode = crtc.mode; let modes = resource_modes .iter() @@ -82,6 +83,7 @@ impl XConnection { .filter(|x| output_modes.iter().any(|id| x.id == *id)) .map(|mode| { VideoModeHandle { + current: mode.id == current_mode, size: (mode.width.into(), mode.height.into()), refresh_rate_millihertz: monitor::mode_refresh_rate_millihertz(mode) .unwrap_or(0), diff --git a/src/platform_impl/orbital/mod.rs b/src/platform_impl/orbital/mod.rs index 34a8555309..4e0979cc25 100644 --- a/src/platform_impl/orbital/mod.rs +++ b/src/platform_impl/orbital/mod.rs @@ -217,17 +217,21 @@ impl MonitorHandle { None } - pub fn video_modes(&self) -> impl Iterator { + pub fn current_video_mode(&self) -> Option { let size = self.size().into(); // FIXME this is not the real refresh rate // (it is guaranteed to support 32 bit color though) - std::iter::once(VideoModeHandle { + Some(VideoModeHandle { size, bit_depth: 32, refresh_rate_millihertz: 60000, monitor: self.clone(), }) } + + pub fn video_modes(&self) -> impl Iterator { + self.current_video_mode().into_iter() + } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] diff --git a/src/platform_impl/web/monitor.rs b/src/platform_impl/web/monitor.rs index 8decd2a00a..678056d77d 100644 --- a/src/platform_impl/web/monitor.rs +++ b/src/platform_impl/web/monitor.rs @@ -89,6 +89,10 @@ impl MonitorHandle { }) } + pub fn current_video_mode(&self) -> Option { + Some(VideoModeHandle(self.clone())) + } + pub fn video_modes(&self) -> Once { iter::once(VideoModeHandle(self.clone())) } diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index 9a880db461..4f658167ae 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -59,6 +59,20 @@ impl std::fmt::Debug for VideoModeHandle { } impl VideoModeHandle { + fn new(monitor: MonitorHandle, mode: DEVMODEW) -> Self { + const REQUIRED_FIELDS: u32 = + DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; + assert!(has_flag(mode.dmFields, REQUIRED_FIELDS)); + + VideoModeHandle { + size: (mode.dmPelsWidth, mode.dmPelsHeight), + bit_depth: mode.dmBitsPerPel as u16, + refresh_rate_millihertz: mode.dmDisplayFrequency * 1000, + monitor, + native_video_mode: Box::new(mode), + } + } + pub fn size(&self) -> PhysicalSize { self.size.into() } @@ -207,6 +221,23 @@ impl MonitorHandle { dpi_to_scale_factor(get_monitor_dpi(self.0).unwrap_or(96)) } + #[inline] + pub fn current_video_mode(&self) -> Option { + 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(self.clone(), mode)) + } + } + } + #[inline] pub fn video_modes(&self) -> impl Iterator { // EnumDisplaySettingsExW can return duplicate values (or some of the @@ -233,19 +264,9 @@ impl MonitorHandle { break; } - const REQUIRED_FIELDS: u32 = - DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; - assert!(has_flag(mode.dmFields, REQUIRED_FIELDS)); - // Use Ord impl of RootVideoModeHandle modes.insert(RootVideoModeHandle { - video_mode: VideoModeHandle { - size: (mode.dmPelsWidth, mode.dmPelsHeight), - bit_depth: mode.dmBitsPerPel as u16, - refresh_rate_millihertz: mode.dmDisplayFrequency * 1000, - monitor: self.clone(), - native_video_mode: Box::new(mode), - }, + video_mode: VideoModeHandle::new(self.clone(), mode), }); i += 1; From 0ffcfd8a3a72e13538cad6b4303383c22e74b27a Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sun, 21 Jul 2024 00:40:57 +0200 Subject: [PATCH 2/4] Remove `MonitorHandle::size/refresh_rate_millihertz()` --- examples/window.rs | 28 +++++++++------ src/changelog/unreleased.md | 3 ++ src/monitor.rs | 32 ++--------------- src/platform_impl/android/mod.rs | 43 +++++++++-------------- src/platform_impl/apple/appkit/monitor.rs | 27 ++++++-------- src/platform_impl/apple/uikit/monitor.rs | 15 ++------ src/platform_impl/linux/mod.rs | 12 +------ src/platform_impl/linux/wayland/output.rs | 26 ++------------ src/platform_impl/linux/x11/monitor.rs | 28 ++------------- src/platform_impl/linux/x11/util/randr.rs | 3 +- src/platform_impl/orbital/mod.rs | 24 ++++--------- src/platform_impl/web/monitor.rs | 34 +++++++----------- src/platform_impl/windows/monitor.rs | 24 ++----------- 13 files changed, 81 insertions(+), 218 deletions(-) diff --git a/examples/window.rs b/examples/window.rs index b1862da43e..0b9b1ac711 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -302,15 +302,17 @@ impl Application { info!("{intro}: [no name]"); } - let PhysicalSize { width, height } = monitor.size(); - info!( - " Current mode: {width}x{height}{}", - if let Some(m_hz) = monitor.refresh_rate_millihertz() { - format!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000) - } else { - String::new() - } - ); + if let Some(current_mode) = monitor.current_video_mode() { + let PhysicalSize { width, height } = current_mode.size(); + info!( + " Current mode: {width}x{height}{}", + if let Some(m_hz) = current_mode.refresh_rate_millihertz() { + format!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000) + } else { + String::new() + } + ); + } let PhysicalPosition { x, y } = monitor.position(); info!(" Position: {x},{y}"); @@ -321,8 +323,12 @@ impl Application { for mode in monitor.video_modes() { let PhysicalSize { width, height } = mode.size(); let bits = mode.bit_depth(); - let m_hz = mode.refresh_rate_millihertz(); - info!(" {width}x{height}x{bits} @ {}.{} Hz", m_hz / 1000, m_hz % 1000); + let m_hz = if let Some(m_hz) = mode.refresh_rate_millihertz() { + format!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000) + } else { + String::new() + }; + info!(" {width}x{height}x{bits}{m_hz}"); } } } diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 98ab2dc793..b90ec4e5fc 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -95,6 +95,7 @@ changelog entry. accelerated, if the browser supports it. - `(Active)EventLoop::create_custom_cursor()` now returns a `Result`. - Changed how `ModifiersState` is serialized by Serde. +- `VideoModeHandle::refresh_rate_millihertz()` now returns an `Option`. ### Removed @@ -118,6 +119,8 @@ changelog entry. - Remove `DeviceEvent::Added` and `DeviceEvent::Removed`. - Remove `DeviceEvent::Motion` and `WindowEvent::AxisMotion`. - Remove `Touch::id` in favor of `Touch::finger_id`. +- Remove `MonitorHandle::size()` and `refresh_rate_millihertz()` in favor of + `MonitorHandle::current_video_mode()`. ### Fixed diff --git a/src/monitor.rs b/src/monitor.rs index d49742e368..df0b47dda1 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -64,12 +64,8 @@ impl VideoModeHandle { } /// Returns the refresh rate of this video mode in mHz. - /// - /// ## Platform-specific - /// - /// **Web:** Always returns `0`. #[inline] - pub fn refresh_rate_millihertz(&self) -> u32 { + pub fn refresh_rate_millihertz(&self) -> Option { self.video_mode.refresh_rate_millihertz() } @@ -85,10 +81,10 @@ impl std::fmt::Display for VideoModeHandle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "{}x{} @ {} mHz ({} bpp)", + "{}x{} {}({} bpp)", self.size().width, self.size().height, - self.refresh_rate_millihertz(), + self.refresh_rate_millihertz().map(|rate| format!("@ {rate} mHz ")).unwrap_or_default(), self.bit_depth() ) } @@ -139,12 +135,6 @@ impl MonitorHandle { self.inner.name() } - /// Returns the monitor's resolution. - #[inline] - pub fn size(&self) -> PhysicalSize { - self.inner.size() - } - /// Returns the top-left corner position of the monitor relative to the larger full /// screen area. /// @@ -161,22 +151,6 @@ impl MonitorHandle { self.inner.position() } - /// The monitor refresh rate used by the system. - /// - /// Return `Some` if succeed, or `None` if failed, which usually happens when the monitor - /// the window is on is removed. - /// - /// When using exclusive fullscreen, the refresh rate of the [`VideoModeHandle`] that was - /// used to enter fullscreen should be used instead. - /// - /// ## Platform-specific - /// - /// **Web:** Always returns [`None`]. - #[inline] - pub fn refresh_rate_millihertz(&self) -> Option { - self.inner.refresh_rate_millihertz() - } - /// Returns the scale factor of the underlying monitor. To map logical pixels to physical /// pixels and vice versa, use [`Window::scale_factor`]. /// diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 1594f7d743..4d4c433639 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -204,9 +204,7 @@ impl EventLoop { let old_scale_factor = monitor.scale_factor(); let scale_factor = monitor.scale_factor(); if (scale_factor - old_scale_factor).abs() < f64::EPSILON { - let new_inner_size = Arc::new(Mutex::new( - MonitorHandle::new(self.android_app.clone()).size(), - )); + let new_inner_size = Arc::new(Mutex::new(screen_size(&self.android_app))); let window_id = window::WindowId(WindowId); let event = event::WindowEvent::ScaleFactorChanged { inner_size_writer: InnerSizeWriter::new(Arc::downgrade( @@ -789,7 +787,7 @@ impl Window { } pub fn outer_size(&self) -> PhysicalSize { - MonitorHandle::new(self.app.clone()).size() + screen_size(&self.app) } pub fn set_min_inner_size(&self, _: Option) {} @@ -982,14 +980,6 @@ impl MonitorHandle { Some("Android Device".to_owned()) } - pub fn size(&self) -> PhysicalSize { - if let Some(native_window) = self.app.native_window() { - PhysicalSize::new(native_window.width() as _, native_window.height() as _) - } else { - PhysicalSize::new(0, 0) - } - } - pub fn position(&self) -> PhysicalPosition { (0, 0).into() } @@ -998,19 +988,11 @@ impl MonitorHandle { self.app.config().density().map(|dpi| dpi as f64 / 160.0).unwrap_or(1.0) } - pub fn refresh_rate_millihertz(&self) -> Option { - // FIXME no way to get real refresh rate for now. - None - } - pub fn current_video_mode(&self) -> Option { - let size = self.size().into(); - // FIXME this is not the real refresh rate - // (it is guaranteed to support 32 bit color though) Some(VideoModeHandle { - size, + size: screen_size(&self.app), + // FIXME: it is guaranteed to support 32 bit color though bit_depth: 32, - refresh_rate_millihertz: 60000, monitor: self.clone(), }) } @@ -1022,26 +1004,33 @@ impl MonitorHandle { #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct VideoModeHandle { - size: (u32, u32), + size: PhysicalSize, bit_depth: u16, - refresh_rate_millihertz: u32, monitor: MonitorHandle, } impl VideoModeHandle { pub fn size(&self) -> PhysicalSize { - self.size.into() + self.size } pub fn bit_depth(&self) -> u16 { self.bit_depth } - pub fn refresh_rate_millihertz(&self) -> u32 { - self.refresh_rate_millihertz + pub fn refresh_rate_millihertz(&self) -> Option { + None } pub fn monitor(&self) -> MonitorHandle { self.monitor.clone() } } + +fn screen_size(app: &AndroidApp) -> PhysicalSize { + if let Some(native_window) = app.native_window() { + PhysicalSize::new(native_window.width() as _, native_window.height() as _) + } else { + PhysicalSize::new(0, 0) + } +} diff --git a/src/platform_impl/apple/appkit/monitor.rs b/src/platform_impl/apple/appkit/monitor.rs index 6b793f762b..d6ca7a41e0 100644 --- a/src/platform_impl/apple/appkit/monitor.rs +++ b/src/platform_impl/apple/appkit/monitor.rs @@ -21,7 +21,7 @@ use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; pub struct VideoModeHandle { size: PhysicalSize, bit_depth: u16, - refresh_rate_millihertz: u32, + refresh_rate_millihertz: Option, pub(crate) monitor: MonitorHandle, pub(crate) native_mode: NativeDisplayMode, } @@ -80,7 +80,11 @@ impl Clone for NativeDisplayMode { } impl VideoModeHandle { - fn new(monitor: MonitorHandle, mode: NativeDisplayMode, refresh_rate_millihertz: u32) -> Self { + fn new( + monitor: MonitorHandle, + mode: NativeDisplayMode, + refresh_rate_millihertz: Option, + ) -> Self { unsafe { let pixel_encoding = CFString::wrap_under_create_rule(ffi::CGDisplayModeCopyPixelEncoding(mode.0)) @@ -116,7 +120,7 @@ impl VideoModeHandle { self.bit_depth } - pub fn refresh_rate_millihertz(&self) -> u32 { + pub fn refresh_rate_millihertz(&self) -> Option { self.refresh_rate_millihertz } @@ -186,7 +190,6 @@ impl fmt::Debug for MonitorHandle { f.debug_struct("MonitorHandle") .field("name", &self.name()) .field("native_identifier", &self.native_identifier()) - .field("size", &self.size()) .field("position", &self.position()) .field("scale_factor", &self.scale_factor()) .field("refresh_rate_millihertz", &self.refresh_rate_millihertz()) @@ -212,14 +215,6 @@ impl MonitorHandle { self.0 } - pub fn size(&self) -> PhysicalSize { - let MonitorHandle(display_id) = *self; - let display = CGDisplay::new(display_id); - let height = display.pixels_high(); - let width = display.pixels_wide(); - PhysicalSize::from_logical::<_, f64>((width as f64, height as f64), self.scale_factor()) - } - #[inline] pub fn position(&self) -> PhysicalPosition { // This is already in screen coordinates. If we were using `NSScreen`, @@ -239,7 +234,7 @@ impl MonitorHandle { }) } - pub fn refresh_rate_millihertz(&self) -> Option { + fn refresh_rate_millihertz(&self) -> Option { let current_display_mode = NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _); refresh_rate_millihertz(self.0, ¤t_display_mode) @@ -247,12 +242,12 @@ impl MonitorHandle { pub fn current_video_mode(&self) -> Option { let mode = NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _); - let refresh_rate_millihertz = refresh_rate_millihertz(self.0, &mode).unwrap_or(0); + let refresh_rate_millihertz = refresh_rate_millihertz(self.0, &mode); Some(VideoModeHandle::new(self.clone(), mode, refresh_rate_millihertz)) } pub fn video_modes(&self) -> impl Iterator { - let refresh_rate_millihertz = self.refresh_rate_millihertz().unwrap_or(0); + let refresh_rate_millihertz = self.refresh_rate_millihertz(); let monitor = self.clone(); unsafe { @@ -277,7 +272,7 @@ impl MonitorHandle { // CGDisplayModeGetRefreshRate returns 0.0 for any display that // isn't a CRT let refresh_rate_millihertz = if cg_refresh_rate_hertz > 0 { - (cg_refresh_rate_hertz * 1000) as u32 + Some((cg_refresh_rate_hertz * 1000) as u32) } else { refresh_rate_millihertz }; diff --git a/src/platform_impl/apple/uikit/monitor.rs b/src/platform_impl/apple/uikit/monitor.rs index 2975afc762..4485e25148 100644 --- a/src/platform_impl/apple/uikit/monitor.rs +++ b/src/platform_impl/apple/uikit/monitor.rs @@ -75,8 +75,8 @@ impl VideoModeHandle { self.bit_depth } - pub fn refresh_rate_millihertz(&self) -> u32 { - self.refresh_rate_millihertz + pub fn refresh_rate_millihertz(&self) -> Option { + Some(self.refresh_rate_millihertz) } pub fn monitor(&self) -> MonitorHandle { @@ -131,10 +131,8 @@ impl fmt::Debug for MonitorHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("MonitorHandle") .field("name", &self.name()) - .field("size", &self.size()) .field("position", &self.position()) .field("scale_factor", &self.scale_factor()) - .field("refresh_rate_millihertz", &self.refresh_rate_millihertz()) .finish_non_exhaustive() } } @@ -164,11 +162,6 @@ impl MonitorHandle { }) } - pub fn size(&self) -> PhysicalSize { - let bounds = self.ui_screen.get_on_main(|ui_screen| ui_screen.nativeBounds()); - PhysicalSize::new(bounds.size.width as u32, bounds.size.height as u32) - } - pub fn position(&self) -> PhysicalPosition { let bounds = self.ui_screen.get_on_main(|ui_screen| ui_screen.nativeBounds()); (bounds.origin.x as f64, bounds.origin.y as f64).into() @@ -178,10 +171,6 @@ impl MonitorHandle { self.ui_screen.get_on_main(|ui_screen| ui_screen.nativeScale()) as f64 } - pub fn refresh_rate_millihertz(&self) -> Option { - Some(self.ui_screen.get_on_main(|ui_screen| refresh_rate_millihertz(ui_screen))) - } - pub fn current_video_mode(&self) -> Option { Some(run_on_main(|mtm| { VideoModeHandle::new( diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 69edafdaed..039467dd67 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -241,21 +241,11 @@ impl MonitorHandle { x11_or_wayland!(match self; MonitorHandle(m) => m.native_identifier()) } - #[inline] - pub fn size(&self) -> PhysicalSize { - x11_or_wayland!(match self; MonitorHandle(m) => m.size()) - } - #[inline] pub fn position(&self) -> PhysicalPosition { 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 _) @@ -292,7 +282,7 @@ impl VideoModeHandle { } #[inline] - pub fn refresh_rate_millihertz(&self) -> u32 { + pub fn refresh_rate_millihertz(&self) -> Option { x11_or_wayland!(match self; VideoModeHandle(m) => m.refresh_rate_millihertz()) } diff --git a/src/platform_impl/linux/wayland/output.rs b/src/platform_impl/linux/wayland/output.rs index 8c793be664..3f7d5da75f 100644 --- a/src/platform_impl/linux/wayland/output.rs +++ b/src/platform_impl/linux/wayland/output.rs @@ -28,20 +28,6 @@ impl MonitorHandle { output_data.with_output_info(|info| info.id) } - #[inline] - pub fn size(&self) -> PhysicalSize { - let output_data = self.proxy.data::().unwrap(); - let dimensions = output_data.with_output_info(|info| { - info.modes.iter().find_map(|mode| mode.current.then_some(mode.dimensions)) - }); - - match dimensions { - Some((width, height)) => (width as u32, height as u32), - _ => (0, 0), - } - .into() - } - #[inline] pub fn position(&self) -> PhysicalPosition { let output_data = self.proxy.data::().unwrap(); @@ -59,14 +45,6 @@ impl MonitorHandle { }) } - #[inline] - pub fn refresh_rate_millihertz(&self) -> Option { - let output_data = self.proxy.data::().unwrap(); - output_data.with_output_info(|info| { - info.modes.iter().find_map(|mode| mode.current.then_some(mode.refresh_rate as u32)) - }) - } - #[inline] pub fn scale_factor(&self) -> i32 { let output_data = self.proxy.data::().unwrap(); @@ -153,8 +131,8 @@ impl VideoModeHandle { } #[inline] - pub fn refresh_rate_millihertz(&self) -> u32 { - self.refresh_rate_millihertz + pub fn refresh_rate_millihertz(&self) -> Option { + Some(self.refresh_rate_millihertz) } pub fn monitor(&self) -> MonitorHandle { diff --git a/src/platform_impl/linux/x11/monitor.rs b/src/platform_impl/linux/x11/monitor.rs index 0dd4abfce7..1347b8e18a 100644 --- a/src/platform_impl/linux/x11/monitor.rs +++ b/src/platform_impl/linux/x11/monitor.rs @@ -21,7 +21,7 @@ pub struct VideoModeHandle { pub(crate) current: bool, pub(crate) size: (u32, u32), pub(crate) bit_depth: u16, - pub(crate) refresh_rate_millihertz: u32, + pub(crate) refresh_rate_millihertz: Option, pub(crate) native_mode: randr::Mode, pub(crate) monitor: Option, } @@ -38,7 +38,7 @@ impl VideoModeHandle { } #[inline] - pub fn refresh_rate_millihertz(&self) -> u32 { + pub fn refresh_rate_millihertz(&self) -> Option { self.refresh_rate_millihertz } @@ -54,8 +54,6 @@ pub struct MonitorHandle { pub(crate) id: randr::Crtc, /// The name of the monitor pub(crate) name: String, - /// The size of the monitor - dimensions: (u32, u32), /// The position of the monitor in the X screen position: (i32, i32), /// If the monitor is the primary one @@ -118,16 +116,7 @@ impl MonitorHandle { let rect = util::AaRect::new(position, dimensions); - Some(MonitorHandle { - id, - name, - scale_factor, - dimensions, - position, - primary, - rect, - video_modes, - }) + Some(MonitorHandle { id, name, scale_factor, position, primary, rect, video_modes }) } pub fn dummy() -> Self { @@ -135,7 +124,6 @@ impl MonitorHandle { id: 0, name: "".into(), scale_factor: 1.0, - dimensions: (1, 1), position: (0, 0), primary: true, rect: util::AaRect::new((0, 0), (1, 1)), @@ -157,20 +145,10 @@ impl MonitorHandle { self.id as _ } - pub fn size(&self) -> PhysicalSize { - self.dimensions.into() - } - pub fn position(&self) -> PhysicalPosition { self.position.into() } - pub fn refresh_rate_millihertz(&self) -> Option { - self.video_modes - .iter() - .find_map(|mode| mode.current.then_some(mode.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 4f27801bb8..429e69acc2 100644 --- a/src/platform_impl/linux/x11/util/randr.rs +++ b/src/platform_impl/linux/x11/util/randr.rs @@ -85,8 +85,7 @@ impl XConnection { VideoModeHandle { current: mode.id == current_mode, size: (mode.width.into(), mode.height.into()), - refresh_rate_millihertz: monitor::mode_refresh_rate_millihertz(mode) - .unwrap_or(0), + refresh_rate_millihertz: monitor::mode_refresh_rate_millihertz(mode), bit_depth: bit_depth as u16, native_mode: mode.id, // This is populated in `MonitorHandle::video_modes` as the diff --git a/src/platform_impl/orbital/mod.rs b/src/platform_impl/orbital/mod.rs index 4e0979cc25..076db505d6 100644 --- a/src/platform_impl/orbital/mod.rs +++ b/src/platform_impl/orbital/mod.rs @@ -200,10 +200,6 @@ impl MonitorHandle { Some("Redox Device".to_owned()) } - pub fn size(&self) -> PhysicalSize { - PhysicalSize::new(0, 0) // TODO - } - pub fn position(&self) -> PhysicalPosition { (0, 0).into() } @@ -212,19 +208,11 @@ impl MonitorHandle { 1.0 // TODO } - pub fn refresh_rate_millihertz(&self) -> Option { - // FIXME no way to get real refresh rate for now. - None - } - pub fn current_video_mode(&self) -> Option { - let size = self.size().into(); - // FIXME this is not the real refresh rate // (it is guaranteed to support 32 bit color though) Some(VideoModeHandle { - size, + size: PhysicalSize::default(), // TODO bit_depth: 32, - refresh_rate_millihertz: 60000, monitor: self.clone(), }) } @@ -236,23 +224,23 @@ impl MonitorHandle { #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct VideoModeHandle { - size: (u32, u32), + size: PhysicalSize, bit_depth: u16, - refresh_rate_millihertz: u32, monitor: MonitorHandle, } impl VideoModeHandle { pub fn size(&self) -> PhysicalSize { - self.size.into() + self.size } pub fn bit_depth(&self) -> u16 { self.bit_depth } - pub fn refresh_rate_millihertz(&self) -> u32 { - self.refresh_rate_millihertz + pub fn refresh_rate_millihertz(&self) -> Option { + // TODO + None } pub fn monitor(&self) -> MonitorHandle { diff --git a/src/platform_impl/web/monitor.rs b/src/platform_impl/web/monitor.rs index 678056d77d..700a4484ba 100644 --- a/src/platform_impl/web/monitor.rs +++ b/src/platform_impl/web/monitor.rs @@ -71,24 +71,6 @@ impl MonitorHandle { }) } - pub fn refresh_rate_millihertz(&self) -> Option { - None - } - - pub fn size(&self) -> PhysicalSize { - self.inner.queue(|inner| { - let width = inner.screen.width().unwrap(); - let height = inner.screen.height().unwrap(); - - if let Some(Engine::Chromium) = inner.engine { - PhysicalSize::new(width, height).cast() - } else { - LogicalSize::new(width, height) - .to_physical(super::web_sys::scale_factor(&inner.window)) - } - }) - } - pub fn current_video_mode(&self) -> Option { Some(VideoModeHandle(self.clone())) } @@ -291,15 +273,25 @@ pub struct VideoModeHandle(pub(super) MonitorHandle); impl VideoModeHandle { pub fn size(&self) -> PhysicalSize { - self.0.size() + self.0.inner.queue(|inner| { + let width = inner.screen.width().unwrap(); + let height = inner.screen.height().unwrap(); + + if let Some(Engine::Chromium) = inner.engine { + PhysicalSize::new(width, height).cast() + } else { + LogicalSize::new(width, height) + .to_physical(super::web_sys::scale_factor(&inner.window)) + } + }) } pub fn bit_depth(&self) -> u16 { self.0.inner.queue(|inner| inner.screen.color_depth().unwrap()).try_into().unwrap() } - pub fn refresh_rate_millihertz(&self) -> u32 { - 0 + pub fn refresh_rate_millihertz(&self) -> Option { + None } pub fn monitor(&self) -> MonitorHandle { diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index 4f658167ae..e179e85eb1 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -81,8 +81,8 @@ impl VideoModeHandle { self.bit_depth } - pub fn refresh_rate_millihertz(&self) -> u32 { - self.refresh_rate_millihertz + pub fn refresh_rate_millihertz(&self) -> Option { + Some(self.refresh_rate_millihertz) } pub fn monitor(&self) -> MonitorHandle { @@ -180,8 +180,7 @@ impl MonitorHandle { self.0 } - #[inline] - pub fn size(&self) -> PhysicalSize { + pub(crate) fn size(&self) -> PhysicalSize { let rc_monitor = get_monitor_info(self.0).unwrap().monitorInfo.rcMonitor; PhysicalSize { width: (rc_monitor.right - rc_monitor.left) as u32, @@ -189,23 +188,6 @@ impl MonitorHandle { } } - #[inline] - pub fn refresh_rate_millihertz(&self) -> Option { - 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(mode.dmDisplayFrequency * 1000) - } - } - } - #[inline] pub fn position(&self) -> PhysicalPosition { get_monitor_info(self.0) From 58142680ce8ed67c8b16a0e08316ccaddeca15d4 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Tue, 23 Jul 2024 19:59:37 +0200 Subject: [PATCH 3/4] Various `Monitor/VideoModeHandle` methods now return an `Option` `VideoModeHandle::refresh_rate_millihertz()` and `bit_depth()` now return a `Option`. `MonitorHandle::position()` now returns an `Option`. On Orbital `MonitorHandle::name()` now returns `None` instead of a dummy name. --- examples/window.rs | 33 ++-- src/changelog/unreleased.md | 4 +- src/monitor.rs | 32 ++-- src/platform_impl/android/mod.rs | 19 +- src/platform_impl/apple/appkit/monitor.rs | 31 ++-- src/platform_impl/apple/uikit/monitor.rs | 21 +-- src/platform_impl/linux/mod.rs | 7 +- src/platform_impl/linux/wayland/output.rs | 22 +-- src/platform_impl/linux/x11/monitor.rs | 22 ++- src/platform_impl/linux/x11/util/randr.rs | 3 +- src/platform_impl/linux/x11/window.rs | 2 +- src/platform_impl/orbital/mod.rs | 24 +-- src/platform_impl/web/monitor.rs | 213 ++++++++++++++-------- src/platform_impl/windows/monitor.rs | 19 +- src/platform_impl/windows/window.rs | 2 +- 15 files changed, 259 insertions(+), 195 deletions(-) diff --git a/examples/window.rs b/examples/window.rs index 0b9b1ac711..61bd51bd72 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -304,31 +304,30 @@ impl Application { if let Some(current_mode) = monitor.current_video_mode() { let PhysicalSize { width, height } = current_mode.size(); - info!( - " Current mode: {width}x{height}{}", - if let Some(m_hz) = current_mode.refresh_rate_millihertz() { - format!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000) - } else { - String::new() - } - ); + let bits = + current_mode.bit_depth().map(|bits| format!("x{bits}")).unwrap_or_default(); + let m_hz = current_mode + .refresh_rate_millihertz() + .map(|m_hz| format!(" @ {}.{} Hz", m_hz.get() / 1000, m_hz.get() % 1000)) + .unwrap_or_default(); + info!(" {width}x{height}{bits}{m_hz}"); } - let PhysicalPosition { x, y } = monitor.position(); - info!(" Position: {x},{y}"); + if let Some(PhysicalPosition { x, y }) = monitor.position() { + info!(" Position: {x},{y}"); + } info!(" Scale factor: {}", monitor.scale_factor()); info!(" Available modes (width x height x bit-depth):"); for mode in monitor.video_modes() { let PhysicalSize { width, height } = mode.size(); - let bits = mode.bit_depth(); - let m_hz = if let Some(m_hz) = mode.refresh_rate_millihertz() { - format!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000) - } else { - String::new() - }; - info!(" {width}x{height}x{bits}{m_hz}"); + let bits = mode.bit_depth().map(|bits| format!("x{bits}")).unwrap_or_default(); + let m_hz = mode + .refresh_rate_millihertz() + .map(|m_hz| format!(" @ {}.{} Hz", m_hz.get() / 1000, m_hz.get() % 1000)) + .unwrap_or_default(); + info!(" {width}x{height}{bits}{m_hz}"); } } } diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index b90ec4e5fc..8d54167e3a 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -95,7 +95,8 @@ changelog entry. accelerated, if the browser supports it. - `(Active)EventLoop::create_custom_cursor()` now returns a `Result`. - Changed how `ModifiersState` is serialized by Serde. -- `VideoModeHandle::refresh_rate_millihertz()` now returns an `Option`. +- `VideoModeHandle::refresh_rate_millihertz()` and `bit_depth()` now return a `Option`. +- `MonitorHandle::position()` now returns an `Option`. ### Removed @@ -127,3 +128,4 @@ changelog entry. - On Web, pen events are now routed through to `WindowEvent::Cursor*`. - On macOS, fix panic when releasing not available monitor. - On MacOS, return the system theme in `Window::theme()` if no theme override is set. +- On Orbital, `MonitorHandle::name()` now returns `None` instead of a dummy name. diff --git a/src/monitor.rs b/src/monitor.rs index df0b47dda1..b774314dda 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -5,6 +5,8 @@ //! methods, which return an iterator of [`MonitorHandle`]: //! - [`ActiveEventLoop::available_monitors`][crate::event_loop::ActiveEventLoop::available_monitors]. //! - [`Window::available_monitors`][crate::window::Window::available_monitors]. +use std::num::{NonZeroU16, NonZeroU32}; + use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::platform_impl; @@ -44,7 +46,10 @@ impl Ord for VideoModeHandle { } impl VideoModeHandle { - /// Returns the resolution of this video mode. + /// Returns the resolution of this video mode. This **must not** be used to create your + /// rendering surface. Use [`Window::inner_size()`] instead. + /// + /// [`Window::inner_size()`]: crate::window::Window::inner_size #[inline] pub fn size(&self) -> PhysicalSize { self.video_mode.size() @@ -53,19 +58,14 @@ impl VideoModeHandle { /// Returns the bit depth of this video mode, as in how many bits you have /// available per color. This is generally 24 bits or 32 bits on modern /// systems, depending on whether the alpha channel is counted or not. - /// - /// ## Platform-specific - /// - /// - **Wayland / Orbital:** Always returns 32. - /// - **iOS:** Always returns 32. #[inline] - pub fn bit_depth(&self) -> u16 { + pub fn bit_depth(&self) -> Option { self.video_mode.bit_depth() } /// Returns the refresh rate of this video mode in mHz. #[inline] - pub fn refresh_rate_millihertz(&self) -> Option { + pub fn refresh_rate_millihertz(&self) -> Option { self.video_mode.refresh_rate_millihertz() } @@ -81,11 +81,11 @@ impl std::fmt::Display for VideoModeHandle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "{}x{} {}({} bpp)", + "{}x{} {}{}", self.size().width, self.size().height, self.refresh_rate_millihertz().map(|rate| format!("@ {rate} mHz ")).unwrap_or_default(), - self.bit_depth() + self.bit_depth().map(|bit_depth| format!("({bit_depth} bpp)")).unwrap_or_default(), ) } } @@ -112,11 +112,17 @@ impl std::fmt::Display for VideoModeHandle { /// to check. /// /// [`Window`]: crate::window::Window -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct MonitorHandle { pub(crate) inner: platform_impl::MonitorHandle, } +impl std::fmt::Debug for MonitorHandle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner.fmt(f) + } +} + impl MonitorHandle { /// Returns a human-readable name of the monitor. /// @@ -140,14 +146,14 @@ impl MonitorHandle { /// /// ## Platform-specific /// - /// **Web:** Always returns [`Default`] without + /// **Web:** Always returns [`None`] without #[cfg_attr( any(web_platform, docsrs), doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." )] #[cfg_attr(not(any(web_platform, docsrs)), doc = "detailed monitor permissions.")] #[inline] - pub fn position(&self) -> PhysicalPosition { + pub fn position(&self) -> Option> { self.inner.position() } diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 4d4c433639..b5605f7e87 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -2,6 +2,7 @@ use std::any::Any; use std::cell::Cell; use std::collections::VecDeque; use std::hash::Hash; +use std::num::{NonZeroU16, NonZeroU32}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; @@ -980,8 +981,8 @@ impl MonitorHandle { Some("Android Device".to_owned()) } - pub fn position(&self) -> PhysicalPosition { - (0, 0).into() + pub fn position(&self) -> Option> { + None } pub fn scale_factor(&self) -> f64 { @@ -989,12 +990,7 @@ impl MonitorHandle { } pub fn current_video_mode(&self) -> Option { - Some(VideoModeHandle { - size: screen_size(&self.app), - // FIXME: it is guaranteed to support 32 bit color though - bit_depth: 32, - monitor: self.clone(), - }) + Some(VideoModeHandle { size: screen_size(&self.app), monitor: self.clone() }) } pub fn video_modes(&self) -> impl Iterator { @@ -1005,7 +1001,6 @@ impl MonitorHandle { #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct VideoModeHandle { size: PhysicalSize, - bit_depth: u16, monitor: MonitorHandle, } @@ -1014,11 +1009,11 @@ impl VideoModeHandle { self.size } - pub fn bit_depth(&self) -> u16 { - self.bit_depth + pub fn bit_depth(&self) -> Option { + None } - pub fn refresh_rate_millihertz(&self) -> Option { + pub fn refresh_rate_millihertz(&self) -> Option { None } diff --git a/src/platform_impl/apple/appkit/monitor.rs b/src/platform_impl/apple/appkit/monitor.rs index d6ca7a41e0..8917c36a07 100644 --- a/src/platform_impl/apple/appkit/monitor.rs +++ b/src/platform_impl/apple/appkit/monitor.rs @@ -2,6 +2,7 @@ use std::collections::VecDeque; use std::fmt; +use std::num::{NonZeroU16, NonZeroU32}; use core_foundation::array::{CFArrayGetCount, CFArrayGetValueAtIndex}; use core_foundation::base::{CFRelease, TCFType}; @@ -20,8 +21,8 @@ use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; #[derive(Clone)] pub struct VideoModeHandle { size: PhysicalSize, - bit_depth: u16, - refresh_rate_millihertz: Option, + bit_depth: Option, + refresh_rate_millihertz: Option, pub(crate) monitor: MonitorHandle, pub(crate) native_mode: NativeDisplayMode, } @@ -83,7 +84,7 @@ impl VideoModeHandle { fn new( monitor: MonitorHandle, mode: NativeDisplayMode, - refresh_rate_millihertz: Option, + refresh_rate_millihertz: Option, ) -> Self { unsafe { let pixel_encoding = @@ -105,7 +106,7 @@ impl VideoModeHandle { ffi::CGDisplayModeGetPixelHeight(mode.0) as u32, ), refresh_rate_millihertz, - bit_depth, + bit_depth: NonZeroU16::new(bit_depth), monitor: monitor.clone(), native_mode: mode, } @@ -116,11 +117,11 @@ impl VideoModeHandle { self.size } - pub fn bit_depth(&self) -> u16 { + pub fn bit_depth(&self) -> Option { self.bit_depth } - pub fn refresh_rate_millihertz(&self) -> Option { + pub fn refresh_rate_millihertz(&self) -> Option { self.refresh_rate_millihertz } @@ -192,7 +193,6 @@ impl fmt::Debug for MonitorHandle { .field("native_identifier", &self.native_identifier()) .field("position", &self.position()) .field("scale_factor", &self.scale_factor()) - .field("refresh_rate_millihertz", &self.refresh_rate_millihertz()) .finish_non_exhaustive() } } @@ -216,13 +216,13 @@ impl MonitorHandle { } #[inline] - pub fn position(&self) -> PhysicalPosition { + pub fn position(&self) -> Option> { // This is already in screen coordinates. If we were using `NSScreen`, // then a conversion would've been needed: // flip_window_screen_coordinates(self.ns_screen(mtm)?.frame()) let bounds = unsafe { CGDisplayBounds(self.native_identifier()) }; let position = LogicalPosition::new(bounds.origin.x, bounds.origin.y); - position.to_physical(self.scale_factor()) + Some(position.to_physical(self.scale_factor())) } pub fn scale_factor(&self) -> f64 { @@ -234,7 +234,7 @@ impl MonitorHandle { }) } - fn refresh_rate_millihertz(&self) -> Option { + fn refresh_rate_millihertz(&self) -> Option { let current_display_mode = NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _); refresh_rate_millihertz(self.0, ¤t_display_mode) @@ -272,7 +272,7 @@ impl MonitorHandle { // CGDisplayModeGetRefreshRate returns 0.0 for any display that // isn't a CRT let refresh_rate_millihertz = if cg_refresh_rate_hertz > 0 { - Some((cg_refresh_rate_hertz * 1000) as u32) + NonZeroU32::new((cg_refresh_rate_hertz * 1000) as u32) } else { refresh_rate_millihertz }; @@ -341,11 +341,11 @@ pub(crate) fn flip_window_screen_coordinates(frame: NSRect) -> NSPoint { NSPoint::new(frame.origin.x, y) } -fn refresh_rate_millihertz(id: CGDirectDisplayID, mode: &NativeDisplayMode) -> Option { +fn refresh_rate_millihertz(id: CGDirectDisplayID, mode: &NativeDisplayMode) -> Option { unsafe { let refresh_rate = ffi::CGDisplayModeGetRefreshRate(mode.0); if refresh_rate > 0.0 { - return Some((refresh_rate * 1000.0).round() as u32); + return NonZeroU32::new((refresh_rate * 1000.0).round() as u32); } let mut display_link = std::ptr::null_mut(); @@ -360,6 +360,9 @@ fn refresh_rate_millihertz(id: CGDirectDisplayID, mode: &NativeDisplayMode) -> O return None; } - (time.time_scale as i64).checked_div(time.time_value).map(|v| (v * 1000) as u32) + (time.time_scale as i64) + .checked_div(time.time_value) + .map(|v| (v * 1000) as u32) + .and_then(NonZeroU32::new) } } diff --git a/src/platform_impl/apple/uikit/monitor.rs b/src/platform_impl/apple/uikit/monitor.rs index 4485e25148..b87d5a26ce 100644 --- a/src/platform_impl/apple/uikit/monitor.rs +++ b/src/platform_impl/apple/uikit/monitor.rs @@ -1,6 +1,7 @@ #![allow(clippy::unnecessary_cast)] use std::collections::{BTreeSet, VecDeque}; +use std::num::{NonZeroU16, NonZeroU32}; use std::{fmt, hash, ptr}; use objc2::mutability::IsRetainable; @@ -44,8 +45,7 @@ impl Eq for MainThreadBoundDelegateImpls {} #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct VideoModeHandle { pub(crate) size: (u32, u32), - pub(crate) bit_depth: u16, - pub(crate) refresh_rate_millihertz: u32, + pub(crate) refresh_rate_millihertz: Option, screen_mode: MainThreadBoundDelegateImpls, pub(crate) monitor: MonitorHandle, } @@ -60,7 +60,6 @@ impl VideoModeHandle { let size = screen_mode.size(); VideoModeHandle { size: (size.width as u32, size.height as u32), - bit_depth: 32, refresh_rate_millihertz, screen_mode: MainThreadBoundDelegateImpls(MainThreadBound::new(screen_mode, mtm)), monitor: MonitorHandle::new(uiscreen), @@ -71,12 +70,12 @@ impl VideoModeHandle { self.size.into() } - pub fn bit_depth(&self) -> u16 { - self.bit_depth + pub fn bit_depth(&self) -> Option { + None } - pub fn refresh_rate_millihertz(&self) -> Option { - Some(self.refresh_rate_millihertz) + pub fn refresh_rate_millihertz(&self) -> Option { + self.refresh_rate_millihertz } pub fn monitor(&self) -> MonitorHandle { @@ -162,9 +161,9 @@ impl MonitorHandle { }) } - pub fn position(&self) -> PhysicalPosition { + pub fn position(&self) -> Option> { let bounds = self.ui_screen.get_on_main(|ui_screen| ui_screen.nativeBounds()); - (bounds.origin.x as f64, bounds.origin.y as f64).into() + Some((bounds.origin.x as f64, bounds.origin.y as f64).into()) } pub fn scale_factor(&self) -> f64 { @@ -213,7 +212,7 @@ impl MonitorHandle { } } -fn refresh_rate_millihertz(uiscreen: &UIScreen) -> u32 { +fn refresh_rate_millihertz(uiscreen: &UIScreen) -> Option { let refresh_rate_millihertz: NSInteger = { let os_capabilities = app_state::os_capabilities(); if os_capabilities.maximum_frames_per_second { @@ -234,7 +233,7 @@ fn refresh_rate_millihertz(uiscreen: &UIScreen) -> u32 { } }; - refresh_rate_millihertz as u32 * 1000 + NonZeroU32::new(refresh_rate_millihertz as u32 * 1000) } pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque { diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 039467dd67..9fbc3e8912 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -4,6 +4,7 @@ compile_error!("Please select a feature to build for unix: `x11`, `wayland`"); use std::collections::VecDeque; +use std::num::{NonZeroU16, NonZeroU32}; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::sync::Arc; use std::time::Duration; @@ -242,7 +243,7 @@ impl MonitorHandle { } #[inline] - pub fn position(&self) -> PhysicalPosition { + pub fn position(&self) -> Option> { x11_or_wayland!(match self; MonitorHandle(m) => m.position()) } @@ -277,12 +278,12 @@ impl VideoModeHandle { } #[inline] - pub fn bit_depth(&self) -> u16 { + pub fn bit_depth(&self) -> Option { x11_or_wayland!(match self; VideoModeHandle(m) => m.bit_depth()) } #[inline] - pub fn refresh_rate_millihertz(&self) -> Option { + pub fn refresh_rate_millihertz(&self) -> Option { x11_or_wayland!(match self; VideoModeHandle(m) => m.refresh_rate_millihertz()) } diff --git a/src/platform_impl/linux/wayland/output.rs b/src/platform_impl/linux/wayland/output.rs index 3f7d5da75f..ea5ba083e5 100644 --- a/src/platform_impl/linux/wayland/output.rs +++ b/src/platform_impl/linux/wayland/output.rs @@ -1,3 +1,5 @@ +use std::num::{NonZeroU16, NonZeroU32}; + use sctk::output::{Mode, OutputData}; use sctk::reexports::client::protocol::wl_output::WlOutput; use sctk::reexports::client::Proxy; @@ -29,9 +31,9 @@ impl MonitorHandle { } #[inline] - pub fn position(&self) -> PhysicalPosition { + pub fn position(&self) -> Option> { let output_data = self.proxy.data::().unwrap(); - output_data.with_output_info(|info| { + Some(output_data.with_output_info(|info| { info.logical_position.map_or_else( || { LogicalPosition::::from(info.location) @@ -42,7 +44,7 @@ impl MonitorHandle { .to_physical(info.scale_factor as f64) }, ) - }) + })) } #[inline] @@ -105,8 +107,7 @@ impl std::hash::Hash for MonitorHandle { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct VideoModeHandle { pub(crate) size: PhysicalSize, - pub(crate) bit_depth: u16, - pub(crate) refresh_rate_millihertz: u32, + pub(crate) refresh_rate_millihertz: Option, pub(crate) monitor: MonitorHandle, } @@ -114,8 +115,7 @@ impl VideoModeHandle { fn new(monitor: MonitorHandle, mode: Mode) -> Self { VideoModeHandle { size: (mode.dimensions.0 as u32, mode.dimensions.1 as u32).into(), - refresh_rate_millihertz: mode.refresh_rate as u32, - bit_depth: 32, + refresh_rate_millihertz: NonZeroU32::new(mode.refresh_rate as u32), monitor: monitor.clone(), } } @@ -126,13 +126,13 @@ impl VideoModeHandle { } #[inline] - pub fn bit_depth(&self) -> u16 { - self.bit_depth + pub fn bit_depth(&self) -> Option { + None } #[inline] - pub fn refresh_rate_millihertz(&self) -> Option { - Some(self.refresh_rate_millihertz) + pub fn refresh_rate_millihertz(&self) -> Option { + self.refresh_rate_millihertz } pub fn monitor(&self) -> MonitorHandle { diff --git a/src/platform_impl/linux/x11/monitor.rs b/src/platform_impl/linux/x11/monitor.rs index 1347b8e18a..eaf9bef37a 100644 --- a/src/platform_impl/linux/x11/monitor.rs +++ b/src/platform_impl/linux/x11/monitor.rs @@ -1,3 +1,5 @@ +use std::num::{NonZeroU16, NonZeroU32}; + use x11rb::connection::RequestConnection; use x11rb::protocol::randr::{self, ConnectionExt as _}; use x11rb::protocol::xproto; @@ -20,8 +22,8 @@ impl XConnection { pub struct VideoModeHandle { pub(crate) current: bool, pub(crate) size: (u32, u32), - pub(crate) bit_depth: u16, - pub(crate) refresh_rate_millihertz: Option, + pub(crate) bit_depth: Option, + pub(crate) refresh_rate_millihertz: Option, pub(crate) native_mode: randr::Mode, pub(crate) monitor: Option, } @@ -33,12 +35,12 @@ impl VideoModeHandle { } #[inline] - pub fn bit_depth(&self) -> u16 { + pub fn bit_depth(&self) -> Option { self.bit_depth } #[inline] - pub fn refresh_rate_millihertz(&self) -> Option { + pub fn refresh_rate_millihertz(&self) -> Option { self.refresh_rate_millihertz } @@ -55,7 +57,7 @@ pub struct MonitorHandle { /// The name of the monitor pub(crate) name: String, /// The position of the monitor in the X screen - position: (i32, i32), + pub(crate) position: (i32, i32), /// If the monitor is the primary one primary: bool, /// The DPI scale factor @@ -93,10 +95,12 @@ impl std::hash::Hash for MonitorHandle { } #[inline] -pub fn mode_refresh_rate_millihertz(mode: &randr::ModeInfo) -> Option { +pub fn mode_refresh_rate_millihertz(mode: &randr::ModeInfo) -> Option { if mode.dot_clock > 0 && mode.htotal > 0 && mode.vtotal > 0 { #[allow(clippy::unnecessary_cast)] - Some((mode.dot_clock as u64 * 1000 / (mode.htotal as u64 * mode.vtotal as u64)) as u32) + NonZeroU32::new( + (mode.dot_clock as u64 * 1000 / (mode.htotal as u64 * mode.vtotal as u64)) as u32, + ) } else { None } @@ -145,8 +149,8 @@ impl MonitorHandle { self.id as _ } - pub fn position(&self) -> PhysicalPosition { - self.position.into() + pub fn position(&self) -> Option> { + Some(self.position.into()) } #[inline] diff --git a/src/platform_impl/linux/x11/util/randr.rs b/src/platform_impl/linux/x11/util/randr.rs index 429e69acc2..f90068867c 100644 --- a/src/platform_impl/linux/x11/util/randr.rs +++ b/src/platform_impl/linux/x11/util/randr.rs @@ -1,3 +1,4 @@ +use std::num::NonZeroU16; use std::str::FromStr; use std::{env, str}; @@ -86,7 +87,7 @@ impl XConnection { current: mode.id == current_mode, size: (mode.width.into(), mode.height.into()), refresh_rate_millihertz: monitor::mode_refresh_rate_millihertz(mode), - bit_depth: bit_depth as u16, + bit_depth: NonZeroU16::new(bit_depth as u16), native_mode: mode.id, // This is populated in `MonitorHandle::video_modes` as the // video mode is returned to the user diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index f19fbb51a2..31f06fd5c3 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -822,7 +822,7 @@ impl UnownedWindow { let window_position = self.outer_position_physical(); self.shared_state_lock().restore_position = Some(window_position); - let monitor_origin: (i32, i32) = monitor.position().into(); + let monitor_origin: (i32, i32) = monitor.position; self.set_position_inner(monitor_origin.0, monitor_origin.1) .expect_then_ignore_error("Failed to set window position"); self.set_fullscreen_hint(true).map(Some) diff --git a/src/platform_impl/orbital/mod.rs b/src/platform_impl/orbital/mod.rs index 076db505d6..4ba0923cc6 100644 --- a/src/platform_impl/orbital/mod.rs +++ b/src/platform_impl/orbital/mod.rs @@ -1,6 +1,7 @@ #![cfg(target_os = "redox")] use std::fmt::{self, Display, Formatter}; +use std::num::{NonZeroU16, NonZeroU32}; use std::str; use std::sync::Arc; @@ -197,11 +198,11 @@ pub struct MonitorHandle; impl MonitorHandle { pub fn name(&self) -> Option { - Some("Redox Device".to_owned()) + None } - pub fn position(&self) -> PhysicalPosition { - (0, 0).into() + pub fn position(&self) -> Option> { + None } pub fn scale_factor(&self) -> f64 { @@ -210,11 +211,7 @@ impl MonitorHandle { pub fn current_video_mode(&self) -> Option { // (it is guaranteed to support 32 bit color though) - Some(VideoModeHandle { - size: PhysicalSize::default(), // TODO - bit_depth: 32, - monitor: self.clone(), - }) + Some(VideoModeHandle { monitor: self.clone() }) } pub fn video_modes(&self) -> impl Iterator { @@ -224,21 +221,20 @@ impl MonitorHandle { #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct VideoModeHandle { - size: PhysicalSize, - bit_depth: u16, monitor: MonitorHandle, } impl VideoModeHandle { pub fn size(&self) -> PhysicalSize { - self.size + // TODO + PhysicalSize::default() } - pub fn bit_depth(&self) -> u16 { - self.bit_depth + pub fn bit_depth(&self) -> Option { + None } - pub fn refresh_rate_millihertz(&self) -> Option { + pub fn refresh_rate_millihertz(&self) -> Option { // TODO None } diff --git a/src/platform_impl/web/monitor.rs b/src/platform_impl/web/monitor.rs index 700a4484ba..f6b1529dd4 100644 --- a/src/platform_impl/web/monitor.rs +++ b/src/platform_impl/web/monitor.rs @@ -1,9 +1,11 @@ use std::cell::{OnceCell, Ref, RefCell}; use std::cmp::Ordering; +use std::fmt::{self, Debug, Formatter}; use std::future::Future; use std::hash::{Hash, Hasher}; use std::iter::{self, Once}; use std::mem; +use std::num::{NonZeroU16, NonZeroU32}; use std::ops::{Deref, DerefMut}; use std::pin::Pin; use std::rc::{Rc, Weak}; @@ -31,7 +33,7 @@ use crate::platform::web::{ MonitorPermissionError, Orientation, OrientationData, OrientationLock, OrientationLockError, }; -#[derive(Debug, Clone, Eq)] +#[derive(Clone, Eq)] pub struct MonitorHandle { /// [`None`] means [`web_sys::Screen`], which is always the same. id: Option, @@ -45,30 +47,15 @@ impl MonitorHandle { } pub fn scale_factor(&self) -> f64 { - self.inner.queue(|inner| match &inner.screen { - Screen::Screen(_) => 0., - Screen::Detailed { screen, .. } => screen.device_pixel_ratio(), - }) + self.inner.queue(|inner| inner.scale_factor()) } - pub fn position(&self) -> PhysicalPosition { - self.inner.queue(|inner| { - if let Screen::Detailed { screen, .. } = &inner.screen { - PhysicalPosition::new(screen.left(), screen.top()) - } else { - PhysicalPosition::default() - } - }) + pub fn position(&self) -> Option> { + self.inner.queue(|inner| inner.position()) } pub fn name(&self) -> Option { - self.inner.queue(|inner| { - if let Screen::Detailed { screen, .. } = &inner.screen { - Some(screen.label()) - } else { - None - } - }) + self.inner.queue(|inner| inner.name()) } pub fn current_video_mode(&self) -> Option { @@ -80,36 +67,7 @@ impl MonitorHandle { } pub fn orientation(&self) -> OrientationData { - self.inner.queue(|inner| { - let orientation = inner.orientation(); - let angle = orientation.angle().unwrap(); - - match orientation.type_().unwrap() { - OrientationType::LandscapePrimary => OrientationData { - orientation: Orientation::Landscape, - flipped: false, - natural: angle == 0, - }, - OrientationType::LandscapeSecondary => OrientationData { - orientation: Orientation::Landscape, - flipped: true, - natural: angle == 180, - }, - OrientationType::PortraitPrimary => OrientationData { - orientation: Orientation::Portrait, - flipped: false, - natural: angle == 0, - }, - OrientationType::PortraitSecondary => OrientationData { - orientation: Orientation::Portrait, - flipped: true, - natural: angle == 180, - }, - _ => { - unreachable!("found unrecognized orientation: {}", orientation.type_string()) - }, - } - }) + self.inner.queue(|inner| inner.orientation()) } pub fn request_lock(&self, orientation_lock: OrientationLock) -> OrientationLockFuture { @@ -126,7 +84,7 @@ impl MonitorHandle { } let future = - JsFuture::from(inner.orientation().lock(orientation_lock.to_js()).unwrap()); + JsFuture::from(inner.orientation_raw().lock(orientation_lock.to_js()).unwrap()); let notifier = Notifier::new(); let notified = notifier.notified(); @@ -151,22 +109,16 @@ impl MonitorHandle { return Err(OrientationLockError::Unsupported); } - inner.orientation().unlock().map_err(OrientationLockError::from_js) + inner.orientation_raw().unlock().map_err(OrientationLockError::from_js) }) } pub fn is_internal(&self) -> Option { - self.inner.queue(|inner| { - if let Screen::Detailed { screen, .. } = &inner.screen { - Some(screen.is_internal()) - } else { - None - } - }) + self.inner.queue(|inner| inner.is_internal()) } pub fn is_detailed(&self) -> bool { - self.inner.queue(|inner| matches!(inner.screen, Screen::Detailed { .. })) + self.inner.queue(|inner| inner.is_detailed()) } pub(crate) fn detailed( @@ -187,6 +139,31 @@ impl MonitorHandle { } } +impl Debug for MonitorHandle { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let (name, position, scale_factor, orientation, is_internal, is_detailed) = + self.inner.queue(|this| { + ( + this.name(), + this.position(), + this.scale_factor(), + this.orientation(), + this.is_internal(), + this.is_detailed(), + ) + }); + + f.debug_struct("MonitorHandle") + .field("name", &name) + .field("position", &position) + .field("scale_factor", &scale_factor) + .field("orientation", &orientation) + .field("is_internal", &is_internal) + .field("is_detailed", &is_detailed) + .finish() + } +} + impl Hash for MonitorHandle { fn hash(&self, state: &mut H) { self.id.hash(state) @@ -268,29 +245,19 @@ impl OrientationLockError { } } -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct VideoModeHandle(pub(super) MonitorHandle); +#[derive(Clone, Eq, Hash, PartialEq)] +pub struct VideoModeHandle(MonitorHandle); impl VideoModeHandle { pub fn size(&self) -> PhysicalSize { - self.0.inner.queue(|inner| { - let width = inner.screen.width().unwrap(); - let height = inner.screen.height().unwrap(); - - if let Some(Engine::Chromium) = inner.engine { - PhysicalSize::new(width, height).cast() - } else { - LogicalSize::new(width, height) - .to_physical(super::web_sys::scale_factor(&inner.window)) - } - }) + self.0.inner.queue(|inner| inner.size()) } - pub fn bit_depth(&self) -> u16 { - self.0.inner.queue(|inner| inner.screen.color_depth().unwrap()).try_into().unwrap() + pub fn bit_depth(&self) -> Option { + self.0.inner.queue(|inner| inner.bit_depth()) } - pub fn refresh_rate_millihertz(&self) -> Option { + pub fn refresh_rate_millihertz(&self) -> Option { None } @@ -299,6 +266,14 @@ impl VideoModeHandle { } } +impl Debug for VideoModeHandle { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let (size, bit_depth) = self.0.inner.queue(|this| (this.size(), this.bit_depth())); + + f.debug_struct("MonitorHandle").field("size", &size).field("bit_depth", &bit_depth).finish() + } +} + struct Inner { window: WindowExt, engine: Option, @@ -311,12 +286,94 @@ impl Inner { Self { window, engine, screen, orientation: OnceCell::new() } } - fn orientation(&self) -> &ScreenOrientationExt { + fn scale_factor(&self) -> f64 { + match &self.screen { + Screen::Screen(_) => 0., + Screen::Detailed { screen, .. } => screen.device_pixel_ratio(), + } + } + + fn position(&self) -> Option> { + if let Screen::Detailed { screen, .. } = &self.screen { + Some(PhysicalPosition::new(screen.left(), screen.top())) + } else { + None + } + } + + fn name(&self) -> Option { + if let Screen::Detailed { screen, .. } = &self.screen { + Some(screen.label()) + } else { + None + } + } + + fn orientation_raw(&self) -> &ScreenOrientationExt { self.orientation.get_or_init(|| self.screen.orientation().unchecked_into()) } + fn orientation(&self) -> OrientationData { + let orientation = self.orientation_raw(); + + let angle = orientation.angle().unwrap(); + + match orientation.type_().unwrap() { + OrientationType::LandscapePrimary => OrientationData { + orientation: Orientation::Landscape, + flipped: false, + natural: angle == 0, + }, + OrientationType::LandscapeSecondary => OrientationData { + orientation: Orientation::Landscape, + flipped: true, + natural: angle == 180, + }, + OrientationType::PortraitPrimary => OrientationData { + orientation: Orientation::Portrait, + flipped: false, + natural: angle == 0, + }, + OrientationType::PortraitSecondary => OrientationData { + orientation: Orientation::Portrait, + flipped: true, + natural: angle == 180, + }, + _ => { + unreachable!("found unrecognized orientation: {}", orientation.type_string()) + }, + } + } + + fn is_internal(&self) -> Option { + if let Screen::Detailed { screen, .. } = &self.screen { + Some(screen.is_internal()) + } else { + None + } + } + + fn is_detailed(&self) -> bool { + matches!(self.screen, Screen::Detailed { .. }) + } + + fn size(&self) -> PhysicalSize { + let width = self.screen.width().unwrap(); + let height = self.screen.height().unwrap(); + + if let Some(Engine::Chromium) = self.engine { + PhysicalSize::new(width, height).cast() + } else { + LogicalSize::new(width, height).to_physical(super::web_sys::scale_factor(&self.window)) + } + } + + fn bit_depth(&self) -> Option { + NonZeroU16::new(self.screen.color_depth().unwrap().try_into().unwrap()) + } + fn has_lock_support(&self) -> bool { - *HAS_LOCK_SUPPORT.get_or_init(|| !self.orientation().has_lock().is_undefined()) + *HAS_LOCK_SUPPORT.get_or_init(|| !self.orientation_raw().has_lock().is_undefined()) } } diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index e179e85eb1..c6ab740da5 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -1,5 +1,6 @@ use std::collections::{BTreeSet, VecDeque}; use std::hash::Hash; +use std::num::{NonZeroU16, NonZeroU32}; use std::{io, mem, ptr}; use windows_sys::Win32::Foundation::{BOOL, HWND, LPARAM, POINT, RECT}; @@ -20,8 +21,8 @@ use crate::platform_impl::platform::window::Window; #[derive(Clone)] pub struct VideoModeHandle { pub(crate) size: (u32, u32), - pub(crate) bit_depth: u16, - pub(crate) refresh_rate_millihertz: u32, + pub(crate) bit_depth: Option, + pub(crate) refresh_rate_millihertz: Option, 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, @@ -66,8 +67,8 @@ impl VideoModeHandle { VideoModeHandle { size: (mode.dmPelsWidth, mode.dmPelsHeight), - bit_depth: mode.dmBitsPerPel as u16, - refresh_rate_millihertz: mode.dmDisplayFrequency * 1000, + bit_depth: NonZeroU16::new(mode.dmBitsPerPel as u16), + refresh_rate_millihertz: NonZeroU32::new(mode.dmDisplayFrequency * 1000), monitor, native_video_mode: Box::new(mode), } @@ -77,12 +78,12 @@ impl VideoModeHandle { self.size.into() } - pub fn bit_depth(&self) -> u16 { + pub fn bit_depth(&self) -> Option { self.bit_depth } - pub fn refresh_rate_millihertz(&self) -> Option { - Some(self.refresh_rate_millihertz) + pub fn refresh_rate_millihertz(&self) -> Option { + self.refresh_rate_millihertz } pub fn monitor(&self) -> MonitorHandle { @@ -189,13 +190,13 @@ impl MonitorHandle { } #[inline] - pub fn position(&self) -> PhysicalPosition { + pub fn position(&self) -> Option> { get_monitor_info(self.0) .map(|info| { let rc_monitor = info.monitorInfo.rcMonitor; PhysicalPosition { x: rc_monitor.left, y: rc_monitor.top } }) - .unwrap_or(PhysicalPosition { x: 0, y: 0 }) + .ok() } #[inline] diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 268767834d..afd399607f 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -786,7 +786,7 @@ impl Window { Fullscreen::Borderless(None) => monitor::current_monitor(window), }; - let position: (i32, i32) = monitor.position().into(); + let position: (i32, i32) = monitor.position().unwrap_or_default().into(); let size: (u32, u32) = monitor.size().into(); unsafe { From 3bab4ef4fb9f5e71b718c92422e705f729e8ea11 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 26 Jul 2024 15:59:17 +0200 Subject: [PATCH 4/4] Android: remove `MonitorHandle` support Because we don't want to force all methods on `VideoModeHandle` to return `Option`, we decided to remove the already incomplete support in Android for both types. --- src/changelog/unreleased.md | 1 + src/platform_impl/android/mod.rs | 72 ++++++++++++-------------------- 2 files changed, 27 insertions(+), 46 deletions(-) diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 8d54167e3a..6fce8a2f95 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -122,6 +122,7 @@ changelog entry. - Remove `Touch::id` in favor of `Touch::finger_id`. - Remove `MonitorHandle::size()` and `refresh_rate_millihertz()` in favor of `MonitorHandle::current_video_mode()`. +- On Android, remove all `MonitorHandle` support instead of emitting false data. ### Fixed diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index b5605f7e87..6690f38a56 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -1,6 +1,5 @@ use std::any::Any; use std::cell::Cell; -use std::collections::VecDeque; use std::hash::Hash; use std::num::{NonZeroU16, NonZeroU32}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -201,9 +200,8 @@ impl EventLoop { app.window_event(&self.window_target, window_id, event); }, MainEvent::ConfigChanged { .. } => { - let monitor = MonitorHandle::new(self.android_app.clone()); - let old_scale_factor = monitor.scale_factor(); - let scale_factor = monitor.scale_factor(); + let old_scale_factor = scale_factor(&self.android_app); + let scale_factor = scale_factor(&self.android_app); if (scale_factor - old_scale_factor).abs() < f64::EPSILON { let new_inner_size = Arc::new(Mutex::new(screen_size(&self.android_app))); let window_id = window::WindowId(WindowId); @@ -609,12 +607,11 @@ impl RootActiveEventLoop for ActiveEventLoop { } fn available_monitors(&self) -> Box> { - let handle = RootMonitorHandle { inner: MonitorHandle::new(self.app.clone()) }; - Box::new(vec![handle].into_iter()) + Box::new(std::iter::empty()) } fn primary_monitor(&self) -> Option { - Some(RootMonitorHandle { inner: MonitorHandle::new(self.app.clone()) }) + None } fn system_theme(&self) -> Option { @@ -744,21 +741,19 @@ impl Window { } pub fn primary_monitor(&self) -> Option { - Some(MonitorHandle::new(self.app.clone())) + None } - pub fn available_monitors(&self) -> VecDeque { - let mut v = VecDeque::with_capacity(1); - v.push_back(MonitorHandle::new(self.app.clone())); - v + pub fn available_monitors(&self) -> Option { + None } pub fn current_monitor(&self) -> Option { - Some(MonitorHandle::new(self.app.clone())) + None } pub fn scale_factor(&self) -> f64 { - MonitorHandle::new(self.app.clone()).scale_factor() + scale_factor(&self.app) } pub fn request_redraw(&self) { @@ -957,68 +952,49 @@ impl Display for OsError { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct MonitorHandle { - app: AndroidApp, -} -impl PartialOrd for MonitorHandle { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl Ord for MonitorHandle { - fn cmp(&self, _other: &Self) -> std::cmp::Ordering { - std::cmp::Ordering::Equal - } -} +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct MonitorHandle; impl MonitorHandle { - pub(crate) fn new(app: AndroidApp) -> Self { - Self { app } - } - pub fn name(&self) -> Option { - Some("Android Device".to_owned()) + unreachable!() } pub fn position(&self) -> Option> { - None + unreachable!() } pub fn scale_factor(&self) -> f64 { - self.app.config().density().map(|dpi| dpi as f64 / 160.0).unwrap_or(1.0) + unreachable!() } pub fn current_video_mode(&self) -> Option { - Some(VideoModeHandle { size: screen_size(&self.app), monitor: self.clone() }) + unreachable!() } - pub fn video_modes(&self) -> impl Iterator { - self.current_video_mode().into_iter() + pub fn video_modes(&self) -> std::iter::Empty { + unreachable!() } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct VideoModeHandle { - size: PhysicalSize, - monitor: MonitorHandle, -} +pub struct VideoModeHandle; impl VideoModeHandle { pub fn size(&self) -> PhysicalSize { - self.size + unreachable!() } pub fn bit_depth(&self) -> Option { - None + unreachable!() } pub fn refresh_rate_millihertz(&self) -> Option { - None + unreachable!() } pub fn monitor(&self) -> MonitorHandle { - self.monitor.clone() + unreachable!() } } @@ -1029,3 +1005,7 @@ fn screen_size(app: &AndroidApp) -> PhysicalSize { PhysicalSize::new(0, 0) } } + +fn scale_factor(app: &AndroidApp) -> f64 { + app.config().density().map(|dpi| dpi as f64 / 160.0).unwrap_or(1.0) +}