Skip to content

Commit

Permalink
api: make VideoModeHandle into VideoMode
Browse files Browse the repository at this point in the history
The video mode is generally a static data and not a reference to some
video mode. This changes the exclusive fullscreen API to match that an
accept a monitor now.
  • Loading branch information
kchibisov committed Dec 25, 2024
1 parent 6896de5 commit 6c0c47d
Show file tree
Hide file tree
Showing 28 changed files with 248 additions and 464 deletions.
2 changes: 2 additions & 0 deletions src/changelog/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ changelog entry.
- On macOS, no longer emit `Focused` upon window creation.
- On iOS, emit more events immediately, instead of queuing them.
- Update `smol_str` to version `0.3`
- `VideoModeHandle` to `VideoMode` which stores plain data.
- `Fullscreen::Exclusive` to take `(MonitorHandle, VideoMode)`.

### Removed

Expand Down
87 changes: 31 additions & 56 deletions src/monitor.rs
Original file line number Diff line number Diff line change
@@ -1,89 +1,64 @@
//! Types useful for interacting with a user's monitors.
use std::fmt;
use std::num::{NonZeroU16, NonZeroU32};

use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::platform_impl;

/// A handle to a fullscreen video mode of a specific monitor.
/// Describes a fullscreen video mode of a monitor.
///
/// This can be acquired with [`MonitorHandle::video_modes`].
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct VideoModeHandle {
pub(crate) video_mode: platform_impl::VideoModeHandle,
/// Can be acquired with [`MonitorHandle::video_modes`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VideoMode {
pub(crate) size: PhysicalSize<u32>,
pub(crate) bit_depth: Option<NonZeroU16>,
pub(crate) refresh_rate_millihertz: Option<NonZeroU32>,
}

impl std::fmt::Debug for VideoModeHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.video_mode.fmt(f)
}
}

impl PartialOrd for VideoModeHandle {
fn partial_cmp(&self, other: &VideoModeHandle) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}

impl Ord for VideoModeHandle {
fn cmp(&self, other: &VideoModeHandle) -> std::cmp::Ordering {
self.monitor().cmp(&other.monitor()).then(
self.size()
.cmp(&other.size())
.then(
self.refresh_rate_millihertz()
.cmp(&other.refresh_rate_millihertz())
.then(self.bit_depth().cmp(&other.bit_depth())),
)
.reverse(),
)
}
}

impl VideoModeHandle {
impl VideoMode {
/// Returns the resolution of this video mode. This **must not** be used to create your
/// rendering surface. Use [`Window::surface_size()`] instead.
///
/// [`Window::surface_size()`]: crate::window::Window::surface_size
#[inline]
pub fn size(&self) -> PhysicalSize<u32> {
self.video_mode.size()
self.size
}

/// 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.
#[inline]
pub fn bit_depth(&self) -> Option<NonZeroU16> {
self.video_mode.bit_depth()
self.bit_depth
}

/// Returns the refresh rate of this video mode in mHz.
#[inline]
pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
self.video_mode.refresh_rate_millihertz()
}

/// Returns the monitor that this video mode is valid for. Each monitor has
/// a separate set of valid video modes.
#[inline]
pub fn monitor(&self) -> MonitorHandle {
MonitorHandle { inner: self.video_mode.monitor() }
self.refresh_rate_millihertz
}
}

impl std::fmt::Display for VideoModeHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl fmt::Display for VideoMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}x{} {}{}",
self.size().width,
self.size().height,
self.refresh_rate_millihertz().map(|rate| format!("@ {rate} mHz ")).unwrap_or_default(),
self.bit_depth().map(|bit_depth| format!("({bit_depth} bpp)")).unwrap_or_default(),
self.size.width,
self.size.height,
self.refresh_rate_millihertz.map(|rate| format!("@ {rate} mHz ")).unwrap_or_default(),
self.bit_depth.map(|bit_depth| format!("({bit_depth} bpp)")).unwrap_or_default(),
)
}
}

/// Fullscreen modes.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Fullscreen {
Exclusive(MonitorHandle, VideoMode),

/// Providing `None` to `Borderless` will fullscreen on the current monitor.
Borderless(Option<MonitorHandle>),
}

/// Handle to a monitor.
///
/// Allows you to retrieve basic information and metadata about a monitor.
Expand Down Expand Up @@ -188,13 +163,13 @@ impl MonitorHandle {

/// Returns the currently active video mode of this monitor.
#[inline]
pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
self.inner.current_video_mode().map(|video_mode| VideoModeHandle { video_mode })
pub fn current_video_mode(&self) -> Option<VideoMode> {
self.inner.current_video_mode()
}

/// Returns all fullscreen video modes supported by this monitor.
#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
self.inner.video_modes().map(|video_mode| VideoModeHandle { video_mode })
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.inner.video_modes()
}
}
10 changes: 5 additions & 5 deletions src/platform/ios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ use std::os::raw::c_void;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

use crate::monitor::{MonitorHandle, VideoModeHandle};
use crate::monitor::{MonitorHandle, VideoMode};
use crate::window::{Window, WindowAttributes};

/// Additional methods on [`Window`] that are specific to iOS.
Expand Down Expand Up @@ -384,10 +384,10 @@ pub trait MonitorHandleExtIOS {
/// [`UIScreen`]: https://developer.apple.com/documentation/uikit/uiscreen?language=objc
fn ui_screen(&self) -> *mut c_void;

/// Returns the preferred [`VideoModeHandle`] for this monitor.
/// Returns the preferred [`VideoMode`] for this monitor.
///
/// This translates to a call to [`-[UIScreen preferredMode]`](https://developer.apple.com/documentation/uikit/uiscreen/1617823-preferredmode?language=objc).
fn preferred_video_mode(&self) -> VideoModeHandle;
fn preferred_video_mode(&self) -> VideoMode;
}

impl MonitorHandleExtIOS for MonitorHandle {
Expand All @@ -399,8 +399,8 @@ impl MonitorHandleExtIOS for MonitorHandle {
}

#[inline]
fn preferred_video_mode(&self) -> VideoModeHandle {
VideoModeHandle { video_mode: self.inner.preferred_video_mode() }
fn preferred_video_mode(&self) -> VideoMode {
self.inner.preferred_video_mode()
}
}

Expand Down
28 changes: 3 additions & 25 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::cell::Cell;
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};
Expand All @@ -21,7 +20,7 @@ use crate::event_loop::{
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use crate::monitor::MonitorHandle as RootMonitorHandle;
use crate::monitor::{MonitorHandle as RootMonitorHandle, VideoMode};
use crate::platform::pump_events::PumpStatus;
use crate::window::{
self, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, ImePurpose,
Expand Down Expand Up @@ -1021,32 +1020,11 @@ impl MonitorHandle {
unreachable!()
}

pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
pub fn current_video_mode(&self) -> Option<VideoMode> {
unreachable!()
}

pub fn video_modes(&self) -> std::iter::Empty<VideoModeHandle> {
unreachable!()
}
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct VideoModeHandle;

impl VideoModeHandle {
pub fn size(&self) -> PhysicalSize<u32> {
unreachable!()
}

pub fn bit_depth(&self) -> Option<NonZeroU16> {
unreachable!()
}

pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
unreachable!()
}

pub fn monitor(&self) -> MonitorHandle {
pub fn video_modes(&self) -> std::iter::Empty<VideoMode> {
unreachable!()
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/platform_impl/apple/appkit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub(crate) use self::event::{physicalkey_to_scancode, scancode_to_physicalkey, K
pub(crate) use self::event_loop::{
ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes,
};
pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle};
pub(crate) use self::monitor::MonitorHandle;
pub(crate) use self::window::Window;
pub(crate) use self::window_delegate::PlatformSpecificWindowAttributes;
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
Expand Down
66 changes: 23 additions & 43 deletions src/platform_impl/apple/appkit/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,42 +17,33 @@ use objc2_foundation::{ns_string, run_on_main, MainThreadMarker, NSNumber, NSPoi

use super::ffi;
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use crate::monitor::VideoMode;

#[derive(Clone)]
pub struct VideoModeHandle {
size: PhysicalSize<u32>,
bit_depth: Option<NonZeroU16>,
refresh_rate_millihertz: Option<NonZeroU32>,
pub(crate) mode: VideoMode,
pub(crate) monitor: MonitorHandle,
pub(crate) native_mode: NativeDisplayMode,
}

impl PartialEq for VideoModeHandle {
fn eq(&self, other: &Self) -> bool {
self.size == other.size
&& self.bit_depth == other.bit_depth
&& self.refresh_rate_millihertz == other.refresh_rate_millihertz
&& self.monitor == other.monitor
self.monitor == other.monitor && self.mode == other.mode
}
}

impl Eq for VideoModeHandle {}

impl std::hash::Hash for VideoModeHandle {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.size.hash(state);
self.bit_depth.hash(state);
self.refresh_rate_millihertz.hash(state);
self.monitor.hash(state);
}
}

impl std::fmt::Debug for VideoModeHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VideoModeHandle")
.field("size", &self.size)
.field("bit_depth", &self.bit_depth)
.field("refresh_rate_millihertz", &self.refresh_rate_millihertz)
f.debug_struct("VideoMode")
.field("mode", &self.mode)
.field("monitor", &self.monitor)
.finish()
}
Expand Down Expand Up @@ -83,13 +74,14 @@ impl Clone for NativeDisplayMode {
impl VideoModeHandle {
fn new(
monitor: MonitorHandle,
mode: NativeDisplayMode,
native_mode: NativeDisplayMode,
refresh_rate_millihertz: Option<NonZeroU32>,
) -> Self {
unsafe {
let pixel_encoding =
CFString::wrap_under_create_rule(ffi::CGDisplayModeCopyPixelEncoding(mode.0))
.to_string();
let pixel_encoding = CFString::wrap_under_create_rule(
ffi::CGDisplayModeCopyPixelEncoding(native_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) {
Expand All @@ -100,33 +92,17 @@ impl VideoModeHandle {
unimplemented!()
};

VideoModeHandle {
let mode = VideoMode {
size: PhysicalSize::new(
ffi::CGDisplayModeGetPixelWidth(mode.0) as u32,
ffi::CGDisplayModeGetPixelHeight(mode.0) as u32,
ffi::CGDisplayModeGetPixelWidth(native_mode.0) as u32,
ffi::CGDisplayModeGetPixelHeight(native_mode.0) as u32,
),
refresh_rate_millihertz,
bit_depth: NonZeroU16::new(bit_depth),
monitor: monitor.clone(),
native_mode: mode,
}
}
}

pub fn size(&self) -> PhysicalSize<u32> {
self.size
}

pub fn bit_depth(&self) -> Option<NonZeroU16> {
self.bit_depth
}

pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
self.refresh_rate_millihertz
}
};

pub fn monitor(&self) -> MonitorHandle {
self.monitor.clone()
VideoModeHandle { mode, monitor: monitor.clone(), native_mode }
}
}
}

Expand Down Expand Up @@ -240,13 +216,17 @@ impl MonitorHandle {
refresh_rate_millihertz(self.0, &current_display_mode)
}

pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
pub fn current_video_mode(&self) -> Option<VideoMode> {
let mode = NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _);
let refresh_rate_millihertz = refresh_rate_millihertz(self.0, &mode);
Some(VideoModeHandle::new(self.clone(), mode, refresh_rate_millihertz))
Some(VideoModeHandle::new(self.clone(), mode, refresh_rate_millihertz).mode)
}

pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.video_modes_handles().map(|handle| handle.mode)
}

pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
pub(crate) fn video_modes_handles(&self) -> impl Iterator<Item = VideoModeHandle> {
let refresh_rate_millihertz = self.refresh_rate_millihertz();
let monitor = self.clone();

Expand Down
2 changes: 1 addition & 1 deletion src/platform_impl/apple/appkit/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl Window {
impl Drop for Window {
fn drop(&mut self) {
// Restore the video mode.
if matches!(self.fullscreen(), Some(Fullscreen::Exclusive(_))) {
if matches!(self.fullscreen(), Some(Fullscreen::Exclusive(_, _))) {
self.set_fullscreen(None);
}

Expand Down
Loading

0 comments on commit 6c0c47d

Please sign in to comment.