diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index e9c550ffe3..5b85dc75ad 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -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 diff --git a/src/monitor.rs b/src/monitor.rs index 7cc7cb8fb3..a23b5f1b42 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -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, + pub(crate) bit_depth: Option, + pub(crate) refresh_rate_millihertz: Option, } -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 { - 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 { - 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 { - 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 { - 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), +} + /// Handle to a monitor. /// /// Allows you to retrieve basic information and metadata about a monitor. @@ -188,13 +163,13 @@ impl MonitorHandle { /// 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 }) + pub fn current_video_mode(&self) -> Option { + self.inner.current_video_mode() } /// Returns all fullscreen video modes supported by this monitor. #[inline] - pub fn video_modes(&self) -> impl Iterator { - self.inner.video_modes().map(|video_mode| VideoModeHandle { video_mode }) + pub fn video_modes(&self) -> impl Iterator { + self.inner.video_modes() } } diff --git a/src/platform/ios.rs b/src/platform/ios.rs index 9b55b0c4b2..f7e9f95346 100644 --- a/src/platform/ios.rs +++ b/src/platform/ios.rs @@ -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. @@ -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 { @@ -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() } } diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 0d07506a02..2ba3f2ca2d 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -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}; @@ -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, @@ -1021,32 +1020,11 @@ impl MonitorHandle { unreachable!() } - pub fn current_video_mode(&self) -> Option { + pub fn current_video_mode(&self) -> Option { unreachable!() } - pub fn video_modes(&self) -> std::iter::Empty { - unreachable!() - } -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct VideoModeHandle; - -impl VideoModeHandle { - pub fn size(&self) -> PhysicalSize { - unreachable!() - } - - pub fn bit_depth(&self) -> Option { - unreachable!() - } - - pub fn refresh_rate_millihertz(&self) -> Option { - unreachable!() - } - - pub fn monitor(&self) -> MonitorHandle { + pub fn video_modes(&self) -> std::iter::Empty { unreachable!() } } diff --git a/src/platform_impl/apple/appkit/mod.rs b/src/platform_impl/apple/appkit/mod.rs index 4aacb90103..88cba336d2 100644 --- a/src/platform_impl/apple/appkit/mod.rs +++ b/src/platform_impl/apple/appkit/mod.rs @@ -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; diff --git a/src/platform_impl/apple/appkit/monitor.rs b/src/platform_impl/apple/appkit/monitor.rs index 8917c36a07..8338123527 100644 --- a/src/platform_impl/apple/appkit/monitor.rs +++ b/src/platform_impl/apple/appkit/monitor.rs @@ -17,22 +17,18 @@ 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, - bit_depth: Option, - refresh_rate_millihertz: Option, + 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 } } @@ -40,19 +36,14 @@ impl Eq for VideoModeHandle {} impl std::hash::Hash for VideoModeHandle { fn hash(&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() } @@ -83,13 +74,14 @@ impl Clone for NativeDisplayMode { impl VideoModeHandle { fn new( monitor: MonitorHandle, - mode: NativeDisplayMode, + native_mode: NativeDisplayMode, refresh_rate_millihertz: Option, ) -> 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) { @@ -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 { - self.size - } - - pub fn bit_depth(&self) -> Option { - self.bit_depth - } - - pub fn refresh_rate_millihertz(&self) -> Option { - self.refresh_rate_millihertz - } + }; - pub fn monitor(&self) -> MonitorHandle { - self.monitor.clone() + VideoModeHandle { mode, monitor: monitor.clone(), native_mode } + } } } @@ -240,13 +216,17 @@ impl MonitorHandle { refresh_rate_millihertz(self.0, ¤t_display_mode) } - pub fn current_video_mode(&self) -> Option { + 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); - 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 { + self.video_modes_handles().map(|handle| handle.mode) } - pub fn video_modes(&self) -> impl Iterator { + pub(crate) fn video_modes_handles(&self) -> impl Iterator { let refresh_rate_millihertz = self.refresh_rate_millihertz(); let monitor = self.clone(); diff --git a/src/platform_impl/apple/appkit/window.rs b/src/platform_impl/apple/appkit/window.rs index 006b19a31d..03c35052d3 100644 --- a/src/platform_impl/apple/appkit/window.rs +++ b/src/platform_impl/apple/appkit/window.rs @@ -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); } diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index fecadf4cec..8bb04f4337 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -7,7 +7,6 @@ use std::rc::Rc; use std::sync::{Arc, Mutex}; use core_graphics::display::CGDisplay; -use monitor::VideoModeHandle; use objc2::rc::{autoreleasepool, Retained}; use objc2::runtime::{AnyObject, ProtocolObject}; use objc2::{declare_class, msg_send_id, mutability, sel, ClassType, DeclaredClass}; @@ -247,7 +246,7 @@ declare_class!( // Exclusive mode sets the state in `set_fullscreen` as the user // can't enter exclusive mode by other means (like the // fullscreen button on the window decorations) - Some(Fullscreen::Exclusive(_)) => (), + Some(Fullscreen::Exclusive(_, _)) => (), // `window_will_enter_fullscreen` was triggered and we're already // in fullscreen, so we must've reached here by `set_fullscreen` // as it updates the state @@ -287,7 +286,7 @@ declare_class!( // user-provided options are ignored in exclusive fullscreen. let mut options = proposed_options; let fullscreen = self.ivars().fullscreen.borrow(); - if let Some(Fullscreen::Exclusive(_)) = &*fullscreen { + if let Some(Fullscreen::Exclusive(_, _)) = &*fullscreen { options = NSApplicationPresentationOptions::NSApplicationPresentationFullScreen | NSApplicationPresentationOptions::NSApplicationPresentationHideDock | NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar; @@ -507,7 +506,7 @@ fn new_window( autoreleasepool(|_| { let screen = match attrs.fullscreen.clone().map(Into::into) { Some(Fullscreen::Borderless(Some(monitor))) - | Some(Fullscreen::Exclusive(VideoModeHandle { monitor, .. })) => { + | Some(Fullscreen::Exclusive(monitor, _)) => { monitor.ns_screen(mtm).or_else(|| NSScreen::mainScreen(mtm)) }, Some(Fullscreen::Borderless(None)) => NSScreen::mainScreen(mtm), @@ -1426,7 +1425,7 @@ impl WindowDelegate { return; } }, - Fullscreen::Exclusive(video_mode) => video_mode.monitor(), + Fullscreen::Exclusive(monitor, _) => monitor.clone(), } .ns_screen(mtm) .unwrap(); @@ -1437,7 +1436,7 @@ impl WindowDelegate { } } - if let Some(Fullscreen::Exclusive(ref video_mode)) = fullscreen { + if let Some(Fullscreen::Exclusive(ref monitor, ref video_mode)) = fullscreen { // Note: `enterFullScreenMode:withOptions:` seems to do the exact // same thing as we're doing here (captures the display, sets the // video mode, and hides the menu bar and dock), with the exception @@ -1450,7 +1449,7 @@ impl WindowDelegate { // parameter, which is not consistent with the docs saying that it // takes a `NSDictionary`.. - let display_id = video_mode.monitor().native_identifier(); + let display_id = monitor.native_identifier(); let mut fade_token = ffi::kCGDisplayFadeReservationInvalidToken; @@ -1479,6 +1478,12 @@ impl WindowDelegate { assert_eq!(ffi::CGDisplayCapture(display_id), ffi::kCGErrorSuccess); } + let video_mode = + match monitor.video_modes_handles().find(|mode| &mode.mode == video_mode) { + Some(video_mode) => video_mode, + None => return, + }; + unsafe { let result = ffi::CGDisplaySetDisplayMode( display_id, @@ -1543,11 +1548,11 @@ impl WindowDelegate { // State is restored by `window_did_exit_fullscreen` toggle_fullscreen(self.window()); }, - (Some(Fullscreen::Exclusive(ref video_mode)), None) => { - restore_and_release_display(&video_mode.monitor()); + (Some(Fullscreen::Exclusive(ref monitor, _)), None) => { + restore_and_release_display(monitor); toggle_fullscreen(self.window()); }, - (Some(Fullscreen::Borderless(_)), Some(Fullscreen::Exclusive(_))) => { + (Some(Fullscreen::Borderless(_)), Some(Fullscreen::Exclusive(..))) => { // If we're already in fullscreen mode, calling // `CGDisplayCapture` will place the shielding window on top of // our window, which results in a black display and is not what @@ -1567,7 +1572,7 @@ impl WindowDelegate { let window_level = unsafe { ffi::CGShieldingWindowLevel() } as NSWindowLevel + 1; self.window().setLevel(window_level); }, - (Some(Fullscreen::Exclusive(ref video_mode)), Some(Fullscreen::Borderless(_))) => { + (Some(Fullscreen::Exclusive(ref monitor, _)), Some(Fullscreen::Borderless(_))) => { let presentation_options = self.ivars().save_presentation_opts.get().unwrap_or( NSApplicationPresentationOptions::NSApplicationPresentationFullScreen | NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock @@ -1575,7 +1580,7 @@ impl WindowDelegate { ); app.setPresentationOptions(presentation_options); - restore_and_release_display(&video_mode.monitor()); + restore_and_release_display(monitor); // Restore the normal window level following the Borderless fullscreen // `CGShieldingWindowLevel() + 1` hack. diff --git a/src/platform_impl/apple/uikit/mod.rs b/src/platform_impl/apple/uikit/mod.rs index d5560c7e73..fbc8e4f390 100644 --- a/src/platform_impl/apple/uikit/mod.rs +++ b/src/platform_impl/apple/uikit/mod.rs @@ -12,7 +12,7 @@ use std::fmt; pub(crate) use self::event_loop::{ ActiveEventLoop, EventLoop, EventLoopProxy, PlatformSpecificEventLoopAttributes, }; -pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle}; +pub(crate) use self::monitor::MonitorHandle; pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window}; pub(crate) use crate::cursor::{ NoCustomCursor as PlatformCustomCursor, NoCustomCursor as PlatformCustomCursorSource, diff --git a/src/platform_impl/apple/uikit/monitor.rs b/src/platform_impl/apple/uikit/monitor.rs index 7973ed5669..ec3fca9614 100644 --- a/src/platform_impl/apple/uikit/monitor.rs +++ b/src/platform_impl/apple/uikit/monitor.rs @@ -1,7 +1,7 @@ #![allow(clippy::unnecessary_cast)] -use std::collections::{BTreeSet, VecDeque}; -use std::num::{NonZeroU16, NonZeroU32}; +use std::collections::VecDeque; +use std::num::NonZeroU32; use std::{fmt, hash, ptr}; use objc2::mutability::IsRetainable; @@ -11,8 +11,8 @@ use objc2_foundation::{run_on_main, MainThreadBound, MainThreadMarker, NSInteger use objc2_ui_kit::{UIScreen, UIScreenMode}; use super::app_state; -use crate::dpi::{PhysicalPosition, PhysicalSize}; -use crate::monitor::VideoModeHandle as RootVideoModeHandle; +use crate::dpi::PhysicalPosition; +use crate::monitor::VideoMode; // Workaround for `MainThreadBound` implementing almost no traits #[derive(Debug)] @@ -44,10 +44,8 @@ impl Eq for MainThreadBoundDelegateImpls {} #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct VideoModeHandle { - pub(crate) size: (u32, u32), - pub(crate) refresh_rate_millihertz: Option, + pub(crate) mode: VideoMode, screen_mode: MainThreadBoundDelegateImpls, - pub(crate) monitor: MonitorHandle, } impl VideoModeHandle { @@ -58,30 +56,18 @@ impl VideoModeHandle { ) -> VideoModeHandle { let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen); let size = screen_mode.size(); - VideoModeHandle { - size: (size.width as u32, size.height as u32), + let mode = VideoMode { + size: (size.width as u32, size.height as u32).into(), + bit_depth: None, refresh_rate_millihertz, + }; + + VideoModeHandle { + mode, screen_mode: MainThreadBoundDelegateImpls(MainThreadBound::new(screen_mode, mtm)), - monitor: MonitorHandle::new(uiscreen), } } - pub fn size(&self) -> PhysicalSize { - self.size.into() - } - - pub fn bit_depth(&self) -> Option { - None - } - - pub fn refresh_rate_millihertz(&self) -> Option { - self.refresh_rate_millihertz - } - - pub fn monitor(&self) -> MonitorHandle { - self.monitor.clone() - } - pub(super) fn screen_mode(&self, mtm: MainThreadMarker) -> &Retained { self.screen_mode.0.get(mtm) } @@ -179,44 +165,46 @@ impl MonitorHandle { self.ui_screen.get_on_main(|ui_screen| ui_screen.nativeScale()) as f64 } - pub fn current_video_mode(&self) -> Option { + 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, ) + .mode })) } - pub fn video_modes(&self) -> impl Iterator { + pub fn video_modes_handles(&self) -> impl Iterator { run_on_main(|mtm| { let ui_screen = self.ui_screen(mtm); - // Use Ord impl of RootVideoModeHandle - let modes: BTreeSet<_> = ui_screen + ui_screen .availableModes() .into_iter() - .map(|mode| RootVideoModeHandle { - video_mode: VideoModeHandle::new(ui_screen.clone(), mode, mtm), - }) - .collect(); - - modes.into_iter().map(|mode| mode.video_mode) + .map(|mode| VideoModeHandle::new(ui_screen.clone(), mode, mtm)) + .collect::>() + .into_iter() }) } + pub fn video_modes(&self) -> impl Iterator { + self.video_modes_handles().map(|handle| handle.mode) + } + pub(crate) fn ui_screen(&self, mtm: MainThreadMarker) -> &Retained { self.ui_screen.get(mtm) } - pub fn preferred_video_mode(&self) -> VideoModeHandle { + pub fn preferred_video_mode(&self) -> VideoMode { run_on_main(|mtm| { VideoModeHandle::new( self.ui_screen(mtm).clone(), self.ui_screen(mtm).preferredMode().unwrap(), mtm, ) + .mode }) } } diff --git a/src/platform_impl/apple/uikit/window.rs b/src/platform_impl/apple/uikit/window.rs index fcbe2cf3ba..381b7d9cc9 100644 --- a/src/platform_impl/apple/uikit/window.rs +++ b/src/platform_impl/apple/uikit/window.rs @@ -91,10 +91,13 @@ impl WinitUIWindow { this.setRootViewController(Some(view_controller)); match window_attributes.fullscreen.clone().map(Into::into) { - Some(Fullscreen::Exclusive(ref video_mode)) => { - let monitor = video_mode.monitor(); + Some(Fullscreen::Exclusive(ref monitor, ref video_mode)) => { let screen = monitor.ui_screen(mtm); - screen.setCurrentMode(Some(video_mode.screen_mode(mtm))); + if let Some(video_mode) = + monitor.video_modes_handles().find(|mode| &mode.mode == video_mode) + { + screen.setCurrentMode(Some(video_mode.screen_mode(mtm))); + } this.setScreen(screen); }, Some(Fullscreen::Borderless(Some(ref monitor))) => { @@ -312,9 +315,13 @@ impl Inner { pub(crate) fn set_fullscreen(&self, monitor: Option) { let mtm = MainThreadMarker::new().unwrap(); let uiscreen = match &monitor { - Some(Fullscreen::Exclusive(video_mode)) => { - let uiscreen = video_mode.monitor.ui_screen(mtm); - uiscreen.setCurrentMode(Some(video_mode.screen_mode(mtm))); + Some(Fullscreen::Exclusive(monitor, video_mode)) => { + let uiscreen = monitor.ui_screen(mtm); + if let Some(video_mode) = + monitor.video_modes_handles().find(|mode| &mode.mode == video_mode) + { + uiscreen.setCurrentMode(Some(video_mode.screen_mode(mtm))); + } uiscreen.clone() }, Some(Fullscreen::Borderless(Some(monitor))) => monitor.ui_screen(mtm).clone(), @@ -489,7 +496,7 @@ impl Window { let main_screen = UIScreen::mainScreen(mtm); let fullscreen = window_attributes.fullscreen.clone().map(Into::into); let screen = match fullscreen { - Some(Fullscreen::Exclusive(ref video_mode)) => video_mode.monitor.ui_screen(mtm), + Some(Fullscreen::Exclusive(ref monitor, _)) => monitor.ui_screen(mtm), Some(Fullscreen::Borderless(Some(ref monitor))) => monitor.ui_screen(mtm), Some(Fullscreen::Borderless(None)) | None => &main_screen, }; diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 55ba7e2e02..5600282f4d 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -4,7 +4,6 @@ compile_error!("Please select a feature to build for unix: `x11`, `wayland`"); use std::env; -use std::num::{NonZeroU16, NonZeroU32}; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::time::Duration; #[cfg(x11_platform)] @@ -17,13 +16,14 @@ pub(crate) use self::common::xkb::{physicalkey_to_scancode, scancode_to_physical use self::x11::{XConnection, XError, XNotSupported}; use crate::application::ApplicationHandler; pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource; +use crate::dpi::PhysicalPosition; #[cfg(x11_platform)] use crate::dpi::Size; -use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::error::{EventLoopError, NotSupportedError}; use crate::event_loop::ActiveEventLoop; pub(crate) use crate::icon::RgbaIcon as PlatformIcon; use crate::keyboard::Key; +use crate::monitor::VideoMode; use crate::platform::pump_events::PumpStatus; #[cfg(x11_platform)] use crate::platform::x11::{WindowType as XWindowType, XlibErrorHook}; @@ -165,46 +165,16 @@ impl MonitorHandle { } #[inline] - pub fn current_video_mode(&self) -> Option { + 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> { + pub fn video_modes(&self) -> Box> { x11_or_wayland!(match self; MonitorHandle(m) => Box::new(m.video_modes())) } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum VideoModeHandle { - #[cfg(x11_platform)] - X(x11::VideoModeHandle), - #[cfg(wayland_platform)] - Wayland(wayland::VideoModeHandle), -} - -impl VideoModeHandle { - #[inline] - pub fn size(&self) -> PhysicalSize { - x11_or_wayland!(match self; VideoModeHandle(m) => m.size()) - } - - #[inline] - pub fn bit_depth(&self) -> Option { - x11_or_wayland!(match self; VideoModeHandle(m) => m.bit_depth()) - } - - #[inline] - pub fn refresh_rate_millihertz(&self) -> Option { - x11_or_wayland!(match self; VideoModeHandle(m) => m.refresh_rate_millihertz()) - } - - #[inline] - pub fn monitor(&self) -> MonitorHandle { - x11_or_wayland!(match self; VideoModeHandle(m) => m.monitor(); as MonitorHandle) - } -} - #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeyEventExtra { pub text_with_all_modifiers: Option, diff --git a/src/platform_impl/linux/wayland/mod.rs b/src/platform_impl/linux/wayland/mod.rs index 91aad463c7..856a5f1928 100644 --- a/src/platform_impl/linux/wayland/mod.rs +++ b/src/platform_impl/linux/wayland/mod.rs @@ -1,7 +1,7 @@ //! Winit's Wayland backend. pub use event_loop::{ActiveEventLoop, EventLoop}; -pub use output::{MonitorHandle, VideoModeHandle}; +pub use output::MonitorHandle; use sctk::reexports::client::protocol::wl_surface::WlSurface; use sctk::reexports::client::Proxy; pub use window::Window; diff --git a/src/platform_impl/linux/wayland/output.rs b/src/platform_impl/linux/wayland/output.rs index ea5ba083e5..2a1b49dd6b 100644 --- a/src/platform_impl/linux/wayland/output.rs +++ b/src/platform_impl/linux/wayland/output.rs @@ -1,11 +1,11 @@ -use std::num::{NonZeroU16, NonZeroU32}; +use std::num::NonZeroU32; use sctk::output::{Mode, OutputData}; use sctk::reexports::client::protocol::wl_output::WlOutput; use sctk::reexports::client::Proxy; -use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; -use crate::platform_impl::platform::VideoModeHandle as PlatformVideoModeHandle; +use crate::dpi::{LogicalPosition, PhysicalPosition}; +use crate::monitor::VideoMode; #[derive(Clone, Debug)] pub struct MonitorHandle { @@ -54,27 +54,19 @@ impl MonitorHandle { } #[inline] - pub fn current_video_mode(&self) -> Option { + 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)) - }) + mode.map(wayland_mode_to_core_mode) }) } #[inline] - pub fn video_modes(&self) -> impl Iterator { + pub fn video_modes(&self) -> impl Iterator { let output_data = self.proxy.data::().unwrap(); let modes = output_data.with_output_info(|info| info.modes.clone()); - - let monitor = self.clone(); - - modes.into_iter().map(move |mode| { - PlatformVideoModeHandle::Wayland(VideoModeHandle::new(monitor.clone(), mode)) - }) + modes.into_iter().map(wayland_mode_to_core_mode) } } @@ -104,38 +96,11 @@ impl std::hash::Hash for MonitorHandle { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct VideoModeHandle { - pub(crate) size: PhysicalSize, - pub(crate) refresh_rate_millihertz: Option, - pub(crate) monitor: MonitorHandle, -} - -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: NonZeroU32::new(mode.refresh_rate as u32), - monitor: monitor.clone(), - } - } - - #[inline] - pub fn size(&self) -> PhysicalSize { - self.size - } - - #[inline] - pub fn bit_depth(&self) -> Option { - None - } - - #[inline] - pub fn refresh_rate_millihertz(&self) -> Option { - self.refresh_rate_millihertz - } - - pub fn monitor(&self) -> MonitorHandle { - self.monitor.clone() +/// Convert the wayland's [`Mode`] to winit's [`VideoMode`]. +fn wayland_mode_to_core_mode(mode: Mode) -> VideoMode { + VideoMode { + size: (mode.dimensions.0, mode.dimensions.1).into(), + bit_depth: None, + refresh_rate_millihertz: NonZeroU32::new(mode.refresh_rate as u32), } } diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index cb7317737a..2c02318385 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -139,7 +139,7 @@ impl Window { // Set startup mode. match attributes.fullscreen.map(Into::into) { - Some(Fullscreen::Exclusive(_)) => { + Some(Fullscreen::Exclusive(..)) => { warn!("`Fullscreen::Exclusive` is ignored on Wayland"); }, #[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))] @@ -438,7 +438,7 @@ impl CoreWindow for Window { fn set_fullscreen(&self, fullscreen: Option) { match fullscreen { - Some(CoreFullscreen::Exclusive(_)) => { + Some(CoreFullscreen::Exclusive(..)) => { warn!("`Fullscreen::Exclusive` is ignored on Wayland"); }, #[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))] diff --git a/src/platform_impl/linux/x11/monitor.rs b/src/platform_impl/linux/x11/monitor.rs index f3bbc26a18..1bb02942bc 100644 --- a/src/platform_impl/linux/x11/monitor.rs +++ b/src/platform_impl/linux/x11/monitor.rs @@ -6,7 +6,7 @@ use x11rb::protocol::xproto; use super::{util, X11Error, XConnection}; use crate::dpi::{PhysicalPosition, PhysicalSize}; -use crate::platform_impl::VideoModeHandle as PlatformVideoModeHandle; +use crate::monitor::VideoMode; // Used for testing. This should always be committed as false. const DISABLE_MONITOR_LIST_CACHING: bool = false; @@ -21,32 +21,20 @@ impl XConnection { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct VideoModeHandle { pub(crate) current: bool, - pub(crate) size: (u32, u32), + pub(crate) size: PhysicalSize, pub(crate) bit_depth: Option, pub(crate) refresh_rate_millihertz: Option, pub(crate) native_mode: randr::Mode, pub(crate) monitor: Option, } -impl VideoModeHandle { - #[inline] - pub fn size(&self) -> PhysicalSize { - self.size.into() - } - - #[inline] - pub fn bit_depth(&self) -> Option { - self.bit_depth - } - - #[inline] - pub fn refresh_rate_millihertz(&self) -> Option { - self.refresh_rate_millihertz - } - - #[inline] - pub fn monitor(&self) -> MonitorHandle { - self.monitor.clone().unwrap() +impl From for VideoMode { + fn from(mode: VideoModeHandle) -> Self { + Self { + size: mode.size, + bit_depth: mode.bit_depth, + refresh_rate_millihertz: mode.refresh_rate_millihertz, + } } } @@ -65,7 +53,7 @@ pub struct MonitorHandle { /// Used to determine which windows are on this monitor pub(crate) rect: util::AaRect, /// Supported video modes on this monitor - video_modes: Vec, + pub(crate) video_modes: Vec, } impl PartialEq for MonitorHandle { @@ -159,17 +147,13 @@ impl MonitorHandle { } #[inline] - pub fn current_video_mode(&self) -> Option { - self.video_modes.iter().find(|mode| mode.current).cloned().map(PlatformVideoModeHandle::X) + pub fn current_video_mode(&self) -> Option { + self.video_modes.iter().find(|mode| mode.current).cloned().map(Into::into) } #[inline] - pub fn video_modes(&self) -> impl Iterator { - let monitor = self.clone(); - self.video_modes.clone().into_iter().map(move |mut x| { - x.monitor = Some(monitor.clone()); - PlatformVideoModeHandle::X(x) - }) + pub fn video_modes(&self) -> impl Iterator { + self.video_modes.clone().into_iter().map(Into::into) } } diff --git a/src/platform_impl/linux/x11/util/randr.rs b/src/platform_impl/linux/x11/util/randr.rs index f90068867c..e29e60118d 100644 --- a/src/platform_impl/linux/x11/util/randr.rs +++ b/src/platform_impl/linux/x11/util/randr.rs @@ -85,7 +85,7 @@ impl XConnection { .map(|mode| { VideoModeHandle { current: mode.id == current_mode, - size: (mode.width.into(), mode.height.into()), + size: (mode.width as u32, mode.height as u32).into(), refresh_rate_millihertz: monitor::mode_refresh_rate_millihertz(mode), bit_depth: NonZeroU16::new(bit_depth as u16), native_mode: mode.id, diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index e3a72ad228..9712d679ac 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -32,7 +32,6 @@ use crate::platform_impl::x11::{ }; use crate::platform_impl::{ common, Fullscreen, MonitorHandle as PlatformMonitorHandle, PlatformCustomCursor, PlatformIcon, - VideoModeHandle as PlatformVideoModeHandle, }; use crate::window::{ CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow, @@ -327,7 +326,7 @@ impl Drop for Window { let xconn = &window.xconn; // Restore the video mode on drop. - if let Some(Fullscreen::Exclusive(_)) = window.fullscreen() { + if let Some(Fullscreen::Exclusive(..)) = window.fullscreen() { window.set_fullscreen(None); } @@ -1035,20 +1034,17 @@ impl UnownedWindow { // fullscreen, so we can restore it upon exit, as XRandR does not // provide a mechanism to set this per app-session or restore this // to the desktop video mode as macOS and Windows do - (&None, &Some(Fullscreen::Exclusive(PlatformVideoModeHandle::X(ref video_mode)))) - | ( - &Some(Fullscreen::Borderless(_)), - &Some(Fullscreen::Exclusive(PlatformVideoModeHandle::X(ref video_mode))), - ) => { - let monitor = video_mode.monitor.as_ref().unwrap(); + (&None, &Some(Fullscreen::Exclusive(ref monitor, _))) + | (&Some(Fullscreen::Borderless(_)), &Some(Fullscreen::Exclusive(ref monitor, _))) => { + let id = monitor.native_identifier(); shared_state_lock.desktop_video_mode = Some(( - monitor.id, - self.xconn.get_crtc_mode(monitor.id).expect("Failed to get desktop video mode"), + id, + self.xconn.get_crtc_mode(id).expect("Failed to get desktop video mode"), )); }, // Restore desktop video mode upon exiting exclusive fullscreen - (&Some(Fullscreen::Exclusive(_)), &None) - | (&Some(Fullscreen::Exclusive(_)), &Some(Fullscreen::Borderless(_))) => { + (&Some(Fullscreen::Exclusive(..)), &None) + | (&Some(Fullscreen::Exclusive(..)), &Some(Fullscreen::Borderless(_))) => { let (monitor_id, mode_id) = shared_state_lock.desktop_video_mode.take().unwrap(); self.xconn .set_crtc_config(monitor_id, mode_id) @@ -1072,8 +1068,8 @@ impl UnownedWindow { }, Some(fullscreen) => { let (video_mode, monitor) = match fullscreen { - Fullscreen::Exclusive(PlatformVideoModeHandle::X(ref video_mode)) => { - (Some(video_mode), video_mode.monitor.clone().unwrap()) + Fullscreen::Exclusive(PlatformMonitorHandle::X(monitor), video_mode) => { + (Some(video_mode), monitor.clone()) }, Fullscreen::Borderless(Some(PlatformMonitorHandle::X(monitor))) => { (None, monitor) @@ -1090,7 +1086,18 @@ impl UnownedWindow { return Ok(None); } - if let Some(video_mode) = video_mode { + if let Some(native_mode) = video_mode.and_then(|requested| { + monitor.video_modes.iter().find_map(|mode| { + if mode.refresh_rate_millihertz == requested.refresh_rate_millihertz + && mode.size == requested.size + && mode.bit_depth == requested.bit_depth + { + Some(mode.native_mode) + } else { + None + } + }) + }) { // FIXME: this is actually not correct if we're setting the // video mode to a resolution higher than the current // desktop resolution, because XRandR does not automatically @@ -1117,7 +1124,7 @@ impl UnownedWindow { // this will make someone unhappy, but it's very unusual for // games to want to do this anyway). self.xconn - .set_crtc_config(monitor.id, video_mode.native_mode) + .set_crtc_config(monitor.id, native_mode) .expect("failed to set video mode"); } diff --git a/src/platform_impl/mod.rs b/src/platform_impl/mod.rs index 1425c1b151..498c61b281 100644 --- a/src/platform_impl/mod.rs +++ b/src/platform_impl/mod.rs @@ -1,4 +1,4 @@ -use crate::monitor::{MonitorHandle as RootMonitorHandle, VideoModeHandle as RootVideoModeHandle}; +use crate::monitor::{MonitorHandle as RootMonitorHandle, VideoMode}; use crate::window::Fullscreen as RootFullscreen; #[cfg(android_platform)] @@ -30,17 +30,19 @@ use self::web as platform; use self::windows as platform; /// Helper for converting between platform-specific and generic -/// [`VideoModeHandle`]/[`MonitorHandle`] +/// [`VideoMode`]/[`MonitorHandle`] #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum Fullscreen { - Exclusive(VideoModeHandle), + Exclusive(MonitorHandle, VideoMode), Borderless(Option), } impl From for Fullscreen { fn from(f: RootFullscreen) -> Self { match f { - RootFullscreen::Exclusive(mode) => Self::Exclusive(mode.video_mode), + RootFullscreen::Exclusive(handle, video_mode) => { + Self::Exclusive(handle.inner, video_mode) + }, RootFullscreen::Borderless(Some(handle)) => Self::Borderless(Some(handle.inner)), RootFullscreen::Borderless(None) => Self::Borderless(None), } @@ -50,8 +52,8 @@ impl From for Fullscreen { impl From for RootFullscreen { fn from(f: Fullscreen) -> Self { match f { - Fullscreen::Exclusive(video_mode) => { - Self::Exclusive(RootVideoModeHandle { video_mode }) + Fullscreen::Exclusive(inner, video_mode) => { + Self::Exclusive(RootMonitorHandle { inner }, video_mode) }, Fullscreen::Borderless(Some(inner)) => { Self::Borderless(Some(RootMonitorHandle { inner })) diff --git a/src/platform_impl/orbital/mod.rs b/src/platform_impl/orbital/mod.rs index 379a5cea15..ad53b77234 100644 --- a/src/platform_impl/orbital/mod.rs +++ b/src/platform_impl/orbital/mod.rs @@ -1,16 +1,16 @@ #![cfg(target_os = "redox")] -use std::num::{NonZeroU16, NonZeroU32}; use std::{fmt, str}; use smol_str::SmolStr; pub(crate) use self::event_loop::{ActiveEventLoop, EventLoop}; -use crate::dpi::{PhysicalPosition, PhysicalSize}; +pub use self::window::Window; +use crate::dpi::PhysicalPosition; use crate::keyboard::Key; -mod event_loop; +use crate::monitor::VideoMode; -pub use self::window::Window; +mod event_loop; mod window; pub(crate) use crate::cursor::{ @@ -151,38 +151,13 @@ impl MonitorHandle { 1.0 // TODO } - pub fn current_video_mode(&self) -> Option { + pub fn current_video_mode(&self) -> Option { // (it is guaranteed to support 32 bit color though) - Some(VideoModeHandle { monitor: self.clone() }) - } - - pub fn video_modes(&self) -> impl Iterator { - self.current_video_mode().into_iter() - } -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct VideoModeHandle { - monitor: MonitorHandle, -} - -impl VideoModeHandle { - pub fn size(&self) -> PhysicalSize { - // TODO - PhysicalSize::default() - } - - pub fn bit_depth(&self) -> Option { - None - } - - pub fn refresh_rate_millihertz(&self) -> Option { - // TODO None } - pub fn monitor(&self) -> MonitorHandle { - self.monitor.clone() + pub fn video_modes(&self) -> impl Iterator { + std::iter::empty() } } diff --git a/src/platform_impl/web/mod.rs b/src/platform_impl/web/mod.rs index e7f8f9d644..2023db2aeb 100644 --- a/src/platform_impl/web/mod.rs +++ b/src/platform_impl/web/mod.rs @@ -43,7 +43,6 @@ pub(crate) use self::event_loop::{ pub(crate) use self::keyboard::KeyEventExtra; pub(crate) use self::monitor::{ HasMonitorPermissionFuture, MonitorHandle, MonitorPermissionFuture, OrientationLockFuture, - VideoModeHandle, }; use self::web_sys as backend; pub use self::window::{PlatformSpecificWindowAttributes, Window}; diff --git a/src/platform_impl/web/monitor.rs b/src/platform_impl/web/monitor.rs index 644a8749c3..7a0612fd4d 100644 --- a/src/platform_impl/web/monitor.rs +++ b/src/platform_impl/web/monitor.rs @@ -3,9 +3,8 @@ 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::num::NonZeroU16; use std::ops::{Deref, DerefMut}; use std::pin::Pin; use std::rc::{Rc, Weak}; @@ -29,7 +28,7 @@ use super::main_thread::MainThreadMarker; use super::r#async::{Dispatcher, Notified, Notifier}; use super::web_sys::{Engine, EventListenerHandle}; use crate::dpi::{PhysicalPosition, PhysicalSize}; -use crate::monitor::MonitorHandle as RootMonitorHandle; +use crate::monitor::{MonitorHandle as RootMonitorHandle, VideoMode}; use crate::platform::web::{ MonitorPermissionError, Orientation, OrientationData, OrientationLock, OrientationLockError, }; @@ -59,12 +58,16 @@ impl MonitorHandle { self.inner.queue(|inner| inner.name()) } - pub fn current_video_mode(&self) -> Option { - Some(VideoModeHandle(self.clone())) + pub fn current_video_mode(&self) -> Option { + Some(VideoMode { + size: self.inner.queue(|inner| inner.size()), + bit_depth: self.inner.queue(|inner| inner.bit_depth()), + refresh_rate_millihertz: None, + }) } - pub fn video_modes(&self) -> Once { - iter::once(VideoModeHandle(self.clone())) + pub fn video_modes(&self) -> impl Iterator { + self.current_video_mode().into_iter() } pub fn orientation(&self) -> OrientationData { @@ -252,35 +255,6 @@ impl OrientationLockError { } } -#[derive(Clone, Eq, Hash, PartialEq)] -pub struct VideoModeHandle(MonitorHandle); - -impl VideoModeHandle { - pub fn size(&self) -> PhysicalSize { - self.0.inner.queue(|inner| inner.size()) - } - - pub fn bit_depth(&self) -> Option { - self.0.inner.queue(|inner| inner.bit_depth()) - } - - pub fn refresh_rate_millihertz(&self) -> Option { - None - } - - pub fn monitor(&self) -> MonitorHandle { - self.0.clone() - } -} - -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, diff --git a/src/platform_impl/web/web_sys/fullscreen.rs b/src/platform_impl/web/web_sys/fullscreen.rs index a7e847ad2b..11255833db 100644 --- a/src/platform_impl/web/web_sys/fullscreen.rs +++ b/src/platform_impl/web/web_sys/fullscreen.rs @@ -55,7 +55,7 @@ pub(crate) fn request_fullscreen( let canvas: &RequestFullscreen = canvas.unchecked_ref(); match fullscreen { - Fullscreen::Exclusive(_) => error!("Exclusive full screen mode is not supported"), + Fullscreen::Exclusive(..) => error!("Exclusive full screen mode is not supported"), Fullscreen::Borderless(Some(monitor)) => { if !monitor::has_screen_details_support(window) { error!( diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index d453814c5f..abbaeaa3b9 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1311,8 +1311,8 @@ unsafe fn public_window_callback_inner( *fullscreen_monitor = Some(MonitorHandle::new(new_monitor)); } }, - Fullscreen::Exclusive(ref video_mode) => { - let old_monitor = video_mode.monitor.hmonitor(); + Fullscreen::Exclusive(ref monitor, _) => { + let old_monitor = monitor.hmonitor(); if let Ok(old_monitor_info) = monitor::get_monitor_info(old_monitor) { let old_monitor_rect = old_monitor_info.monitorInfo.rcMonitor; window_pos.x = old_monitor_rect.left; diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 0bf9fe8660..ecffced0a7 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -6,7 +6,7 @@ pub(crate) use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes pub use self::icon::WinIcon as PlatformIcon; pub(crate) use self::icon::{SelectedCursor, WinCursor as PlatformCustomCursor, WinIcon}; pub(crate) use self::keyboard::{physicalkey_to_scancode, scancode_to_physicalkey}; -pub(crate) use self::monitor::{MonitorHandle, VideoModeHandle}; +pub(crate) use self::monitor::MonitorHandle; pub(crate) use self::window::Window; pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource; use crate::event::DeviceId; diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index d98d54f71b..764f8c6473 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeSet, VecDeque}; +use std::collections::{HashSet, VecDeque}; use std::hash::Hash; use std::num::{NonZeroU16, NonZeroU32}; use std::{io, mem, ptr}; @@ -13,26 +13,20 @@ use windows_sys::Win32::Graphics::Gdi::{ use super::util::decode_wide; use crate::dpi::{PhysicalPosition, PhysicalSize}; -use crate::monitor::VideoModeHandle as RootVideoModeHandle; +use crate::monitor::VideoMode; use crate::platform_impl::platform::dpi::{dpi_to_scale_factor, get_monitor_dpi}; use crate::platform_impl::platform::util::has_flag; #[derive(Clone)] pub struct VideoModeHandle { - pub(crate) size: (u32, u32), - pub(crate) bit_depth: Option, - pub(crate) refresh_rate_millihertz: Option, - pub(crate) monitor: MonitorHandle, + pub(crate) mode: VideoMode, // DEVMODEW is huge so we box it to avoid blowing up the size of winit::window::Fullscreen pub(crate) native_video_mode: Box, } 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.mode == other.mode } } @@ -40,53 +34,29 @@ impl Eq for VideoModeHandle {} impl std::hash::Hash for VideoModeHandle { fn hash(&self, state: &mut H) { - self.size.hash(state); - self.bit_depth.hash(state); - self.refresh_rate_millihertz.hash(state); - self.monitor.hash(state); + self.mode.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) - .field("monitor", &self.monitor) - .finish() + f.debug_struct("VideoMode").field("mode", &self.mode).finish() } } impl VideoModeHandle { - fn new(monitor: MonitorHandle, mode: DEVMODEW) -> Self { + fn new(native_video_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: NonZeroU16::new(mode.dmBitsPerPel as u16), - refresh_rate_millihertz: NonZeroU32::new(mode.dmDisplayFrequency * 1000), - monitor, - native_video_mode: Box::new(mode), - } - } - - pub fn size(&self) -> PhysicalSize { - self.size.into() - } - - pub fn bit_depth(&self) -> Option { - self.bit_depth - } + assert!(has_flag(native_video_mode.dmFields, REQUIRED_FIELDS)); - pub fn refresh_rate_millihertz(&self) -> Option { - self.refresh_rate_millihertz - } + let mode = VideoMode { + size: (native_video_mode.dmPelsWidth, native_video_mode.dmPelsHeight).into(), + bit_depth: NonZeroU16::new(native_video_mode.dmBitsPerPel as u16), + refresh_rate_millihertz: NonZeroU32::new(native_video_mode.dmDisplayFrequency * 1000), + }; - pub fn monitor(&self) -> MonitorHandle { - self.monitor.clone() + VideoModeHandle { mode, native_video_mode: Box::new(native_video_mode) } } } @@ -193,7 +163,7 @@ impl MonitorHandle { } #[inline] - pub fn current_video_mode(&self) -> Option { + 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 { @@ -204,29 +174,26 @@ impl MonitorHandle { { None } else { - Some(VideoModeHandle::new(self.clone(), mode)) + Some(VideoModeHandle::new(mode).mode) } } } - #[inline] - pub fn video_modes(&self) -> impl Iterator { + pub(crate) fn video_mode_handles(&self) -> impl Iterator { // EnumDisplaySettingsExW can return duplicate values (or some of the // fields are probably changing, but we aren't looking at those fields // anyway), so we're using a BTreeSet deduplicate - let mut modes = BTreeSet::::new(); - let mod_map = |mode: RootVideoModeHandle| mode.video_mode; + let mut modes = HashSet::::new(); let monitor_info = match get_monitor_info(self.0) { Ok(monitor_info) => monitor_info, Err(error) => { tracing::warn!("Error from get_monitor_info: {error}"); - return modes.into_iter().map(mod_map); + return modes.into_iter(); }, }; let device_name = monitor_info.szDevice.as_ptr(); - let mut i = 0; loop { let mut mode: DEVMODEW = unsafe { mem::zeroed() }; @@ -236,13 +203,15 @@ impl MonitorHandle { } // Use Ord impl of RootVideoModeHandle - modes.insert(RootVideoModeHandle { - video_mode: VideoModeHandle::new(self.clone(), mode), - }); + modes.insert(VideoModeHandle::new(mode)); i += 1; } - modes.into_iter().map(mod_map) + modes.into_iter() + } + + pub fn video_modes(&self) -> impl Iterator { + self.video_mode_handles().map(|mode| mode.mode) } } diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index ef292015c1..55799699d5 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -334,7 +334,7 @@ impl Window { impl Drop for Window { fn drop(&mut self) { // Restore fullscreen video mode on exit. - if matches!(self.fullscreen(), Some(CoreFullscreen::Exclusive(_))) { + if matches!(self.fullscreen(), Some(CoreFullscreen::Exclusive(_, _))) { self.set_fullscreen(None); } @@ -771,9 +771,13 @@ impl CoreWindow for Window { // Change video mode if we're transitioning to or from exclusive // fullscreen match (&old_fullscreen, &fullscreen) { - (_, Some(Fullscreen::Exclusive(video_mode))) => { - let monitor = video_mode.monitor(); + (_, Some(Fullscreen::Exclusive(monitor, video_mode))) => { let monitor_info = monitor::get_monitor_info(monitor.hmonitor()).unwrap(); + let video_mode = + match monitor.video_mode_handles().find(|mode| &mode.mode == video_mode) { + Some(monitor) => monitor, + None => return, + }; let res = unsafe { ChangeDisplaySettingsExW( @@ -791,7 +795,7 @@ impl CoreWindow for Window { debug_assert!(res != DISP_CHANGE_FAILED); assert_eq!(res, DISP_CHANGE_SUCCESSFUL); }, - (Some(Fullscreen::Exclusive(_)), _) => { + (Some(Fullscreen::Exclusive(..)), _) => { let res = unsafe { ChangeDisplaySettingsExW( ptr::null(), @@ -828,7 +832,7 @@ impl CoreWindow for Window { WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| { f.set( WindowFlags::MARKER_EXCLUSIVE_FULLSCREEN, - matches!(fullscreen, Some(Fullscreen::Exclusive(_))), + matches!(fullscreen, Some(Fullscreen::Exclusive(_, _))), ); f.set( WindowFlags::MARKER_BORDERLESS_FULLSCREEN, @@ -858,7 +862,7 @@ impl CoreWindow for Window { window_state.lock().unwrap().saved_window = Some(SavedWindow { placement }); let monitor = match &fullscreen { - Fullscreen::Exclusive(video_mode) => video_mode.monitor(), + Fullscreen::Exclusive(monitor, _) => monitor.clone(), Fullscreen::Borderless(Some(monitor)) => monitor.clone(), Fullscreen::Borderless(None) => monitor::current_monitor(window), }; diff --git a/src/window.rs b/src/window.rs index ff698af2c3..dd2749dbed 100644 --- a/src/window.rs +++ b/src/window.rs @@ -10,7 +10,7 @@ pub use crate::cursor::{BadImage, Cursor, CustomCursor, CustomCursorSource, MAX_ use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::RequestError; pub use crate::icon::{BadIcon, Icon}; -use crate::monitor::{MonitorHandle, VideoModeHandle}; +use crate::monitor::{MonitorHandle, VideoMode}; use crate::platform_impl::PlatformSpecificWindowAttributes; use crate::utils::AsAny; @@ -963,7 +963,7 @@ pub trait Window: AsAny + Send + Sync { /// - **Wayland:** Does not support exclusive fullscreen mode and will no-op a request. /// - **Windows:** Screen saver is disabled in fullscreen mode. /// - **Android / Orbital:** Unsupported. - /// - **Web:** Passing a [`MonitorHandle`] or [`VideoModeHandle`] that was not created with + /// - **Web:** Passing a [`MonitorHandle`] or [`VideoMode`] that was not created with #[cfg_attr( any(web_platform, docsrs), doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]" @@ -1436,7 +1436,7 @@ pub enum Fullscreen { /// This changes the video mode of the monitor for fullscreen windows and, /// if applicable, captures the monitor for exclusive use by this /// application. - Exclusive(VideoModeHandle), + Exclusive(MonitorHandle, VideoMode), /// Providing `None` to `Borderless` will fullscreen on the current monitor. Borderless(Option),