From 78e69d69fabcef7665f2112975399f931d2f0170 Mon Sep 17 00:00:00 2001
From: daxpedda <daxpedda@gmail.com>
Date: Tue, 23 Jul 2024 19:59:37 +0200
Subject: [PATCH] Various `Monitor/VideoModeHandle` methods now return an
 `Option`

`VideoModeHandle::refresh_rate_millihertz()` and `bit_depth()` now return a `Option<NonZero*>`.
`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/window.rs       |   2 +-
 14 files changed, 249 insertions(+), 186 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<CustomCursor, ExternalError>`.
 - 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<NonZero*>`.
+- `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<u32> {
         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<NonZeroU16> {
         self.video_mode.bit_depth()
     }
 
     /// Returns the refresh rate of this video mode in mHz.
     #[inline]
-    pub fn refresh_rate_millihertz(&self) -> Option<u32> {
+    pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
         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<i32> {
+    pub fn position(&self) -> Option<PhysicalPosition<i32>> {
         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<i32> {
-        (0, 0).into()
+    pub fn position(&self) -> Option<PhysicalPosition<i32>> {
+        None
     }
 
     pub fn scale_factor(&self) -> f64 {
@@ -989,12 +990,7 @@ impl MonitorHandle {
     }
 
     pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
-        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<Item = VideoModeHandle> {
@@ -1005,7 +1001,6 @@ impl MonitorHandle {
 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
 pub struct VideoModeHandle {
     size: PhysicalSize<u32>,
-    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<NonZeroU16> {
+        None
     }
 
-    pub fn refresh_rate_millihertz(&self) -> Option<u32> {
+    pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
         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<u32>,
-    bit_depth: u16,
-    refresh_rate_millihertz: Option<u32>,
+    bit_depth: Option<NonZeroU16>,
+    refresh_rate_millihertz: Option<NonZeroU32>,
     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<u32>,
+        refresh_rate_millihertz: Option<NonZeroU32>,
     ) -> 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<NonZeroU16> {
         self.bit_depth
     }
 
-    pub fn refresh_rate_millihertz(&self) -> Option<u32> {
+    pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
         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<i32> {
+    pub fn position(&self) -> Option<PhysicalPosition<i32>> {
         // 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<u32> {
+    fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
         let current_display_mode =
             NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _);
         refresh_rate_millihertz(self.0, &current_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<u32> {
+fn refresh_rate_millihertz(id: CGDirectDisplayID, mode: &NativeDisplayMode) -> Option<NonZeroU32> {
     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<T: IsRetainable + Message> Eq for MainThreadBoundDelegateImpls<T> {}
 #[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<NonZeroU32>,
     screen_mode: MainThreadBoundDelegateImpls<UIScreenMode>,
     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<NonZeroU16> {
+        None
     }
 
-    pub fn refresh_rate_millihertz(&self) -> Option<u32> {
-        Some(self.refresh_rate_millihertz)
+    pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
+        self.refresh_rate_millihertz
     }
 
     pub fn monitor(&self) -> MonitorHandle {
@@ -162,9 +161,9 @@ impl MonitorHandle {
         })
     }
 
-    pub fn position(&self) -> PhysicalPosition<i32> {
+    pub fn position(&self) -> Option<PhysicalPosition<i32>> {
         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<NonZeroU32> {
     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<MonitorHandle> {
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<i32> {
+    pub fn position(&self) -> Option<PhysicalPosition<i32>> {
         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<NonZeroU16> {
         x11_or_wayland!(match self; VideoModeHandle(m) => m.bit_depth())
     }
 
     #[inline]
-    pub fn refresh_rate_millihertz(&self) -> Option<u32> {
+    pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
         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<i32> {
+    pub fn position(&self) -> Option<PhysicalPosition<i32>> {
         let output_data = self.proxy.data::<OutputData>().unwrap();
-        output_data.with_output_info(|info| {
+        Some(output_data.with_output_info(|info| {
             info.logical_position.map_or_else(
                 || {
                     LogicalPosition::<i32>::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<u32>,
-    pub(crate) bit_depth: u16,
-    pub(crate) refresh_rate_millihertz: u32,
+    pub(crate) refresh_rate_millihertz: Option<NonZeroU32>,
     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<NonZeroU16> {
+        None
     }
 
     #[inline]
-    pub fn refresh_rate_millihertz(&self) -> Option<u32> {
-        Some(self.refresh_rate_millihertz)
+    pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
+        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<u32>,
+    pub(crate) bit_depth: Option<NonZeroU16>,
+    pub(crate) refresh_rate_millihertz: Option<NonZeroU32>,
     pub(crate) native_mode: randr::Mode,
     pub(crate) monitor: Option<MonitorHandle>,
 }
@@ -33,12 +35,12 @@ impl VideoModeHandle {
     }
 
     #[inline]
-    pub fn bit_depth(&self) -> u16 {
+    pub fn bit_depth(&self) -> Option<NonZeroU16> {
         self.bit_depth
     }
 
     #[inline]
-    pub fn refresh_rate_millihertz(&self) -> Option<u32> {
+    pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
         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<u32> {
+pub fn mode_refresh_rate_millihertz(mode: &randr::ModeInfo) -> Option<NonZeroU32> {
     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<i32> {
-        self.position.into()
+    pub fn position(&self) -> Option<PhysicalPosition<i32>> {
+        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<String> {
-        Some("Redox Device".to_owned())
+        None
     }
 
-    pub fn position(&self) -> PhysicalPosition<i32> {
-        (0, 0).into()
+    pub fn position(&self) -> Option<PhysicalPosition<i32>> {
+        None
     }
 
     pub fn scale_factor(&self) -> f64 {
@@ -210,11 +211,7 @@ impl MonitorHandle {
 
     pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
         // (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<Item = VideoModeHandle> {
@@ -224,21 +221,20 @@ impl MonitorHandle {
 
 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
 pub struct VideoModeHandle {
-    size: PhysicalSize<u32>,
-    bit_depth: u16,
     monitor: MonitorHandle,
 }
 
 impl VideoModeHandle {
     pub fn size(&self) -> PhysicalSize<u32> {
-        self.size
+        // TODO
+        PhysicalSize::default()
     }
 
-    pub fn bit_depth(&self) -> u16 {
-        self.bit_depth
+    pub fn bit_depth(&self) -> Option<NonZeroU16> {
+        None
     }
 
-    pub fn refresh_rate_millihertz(&self) -> Option<u32> {
+    pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
         // 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<u64>,
@@ -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<i32> {
-        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<PhysicalPosition<i32>> {
+        self.inner.queue(|inner| inner.position())
     }
 
     pub fn name(&self) -> Option<String> {
-        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<VideoModeHandle> {
@@ -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<bool> {
-        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<H: Hasher>(&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<u32> {
-        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<NonZeroU16> {
+        self.0.inner.queue(|inner| inner.bit_depth())
     }
 
-    pub fn refresh_rate_millihertz(&self) -> Option<u32> {
+    pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
         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<Engine>,
@@ -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<PhysicalPosition<i32>> {
+        if let Screen::Detailed { screen, .. } = &self.screen {
+            Some(PhysicalPosition::new(screen.left(), screen.top()))
+        } else {
+            None
+        }
+    }
+
+    fn name(&self) -> Option<String> {
+        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<bool> {
+        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<u32> {
+        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> {
+        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/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 {