api: make VideoModeHandle into VideoMode

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.
This commit is contained in:
Kirill Chibisov 2025-01-02 03:29:42 +03:00 committed by GitHub
parent 5462f27dda
commit ee245c569d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 247 additions and 475 deletions

View file

@ -162,6 +162,8 @@ changelog entry.
- On macOS, no longer emit `Focused` upon window creation. - On macOS, no longer emit `Focused` upon window creation.
- On iOS, emit more events immediately, instead of queuing them. - On iOS, emit more events immediately, instead of queuing them.
- Update `smol_str` to version `0.3` - Update `smol_str` to version `0.3`
- Rename `VideoModeHandle` to `VideoMode`, now it only stores plain data.
- Make `Fullscreen::Exclusive` contain `(MonitorHandle, VideoMode)`.
### Removed ### Removed

View file

@ -1,86 +1,55 @@
//! Types useful for interacting with a user's monitors. //! Types useful for interacting with a user's monitors.
use std::fmt;
use std::num::{NonZeroU16, NonZeroU32}; use std::num::{NonZeroU16, NonZeroU32};
use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::platform_impl; 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`]. /// Can be retrieved with [`MonitorHandle::video_modes()`].
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VideoModeHandle { pub struct VideoMode {
pub(crate) video_mode: platform_impl::VideoModeHandle, pub(crate) size: PhysicalSize<u32>,
pub(crate) bit_depth: Option<NonZeroU16>,
pub(crate) refresh_rate_millihertz: Option<NonZeroU32>,
} }
impl std::fmt::Debug for VideoModeHandle { impl VideoMode {
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 {
/// Returns the resolution of this video mode. This **must not** be used to create your /// Returns the resolution of this video mode. This **must not** be used to create your
/// rendering surface. Use [`Window::surface_size()`] instead. /// rendering surface. Use [`Window::surface_size()`] instead.
/// ///
/// [`Window::surface_size()`]: crate::window::Window::surface_size /// [`Window::surface_size()`]: crate::window::Window::surface_size
#[inline]
pub fn size(&self) -> PhysicalSize<u32> { 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 /// 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 /// available per color. This is generally 24 bits or 32 bits on modern
/// systems, depending on whether the alpha channel is counted or not. /// systems, depending on whether the alpha channel is counted or not.
#[inline]
pub fn bit_depth(&self) -> Option<NonZeroU16> { 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. /// Returns the refresh rate of this video mode in mHz.
#[inline]
pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> { pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
self.video_mode.refresh_rate_millihertz() self.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() }
} }
} }
impl std::fmt::Display for VideoModeHandle { impl fmt::Display for VideoMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(f, "{}x{}", self.size.width, self.size.height)?;
f,
"{}x{} {}{}", if let Some(refresh_rate) = self.refresh_rate_millihertz {
self.size().width, write!(f, "@{refresh_rate}mHz")?;
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(), if let Some(bit_depth) = self.bit_depth {
) write!(f, " ({bit_depth} bpp)")?;
}
Ok(())
} }
} }
@ -188,13 +157,13 @@ impl MonitorHandle {
/// Returns the currently active video mode of this monitor. /// Returns the currently active video mode of this monitor.
#[inline] #[inline]
pub fn current_video_mode(&self) -> Option<VideoModeHandle> { pub fn current_video_mode(&self) -> Option<VideoMode> {
self.inner.current_video_mode().map(|video_mode| VideoModeHandle { video_mode }) self.inner.current_video_mode()
} }
/// Returns all fullscreen video modes supported by this monitor. /// Returns all fullscreen video modes supported by this monitor.
#[inline] #[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> { pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.inner.video_modes().map(|video_mode| VideoModeHandle { video_mode }) self.inner.video_modes()
} }
} }

View file

@ -107,7 +107,7 @@ use std::os::raw::c_void;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::monitor::{MonitorHandle, VideoModeHandle}; use crate::monitor::{MonitorHandle, VideoMode};
use crate::window::{Window, WindowAttributes}; use crate::window::{Window, WindowAttributes};
/// Additional methods on [`Window`] that are specific to iOS. /// 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 /// [`UIScreen`]: https://developer.apple.com/documentation/uikit/uiscreen?language=objc
fn ui_screen(&self) -> *mut c_void; 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). /// 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 { impl MonitorHandleExtIOS for MonitorHandle {
@ -399,8 +399,8 @@ impl MonitorHandleExtIOS for MonitorHandle {
} }
#[inline] #[inline]
fn preferred_video_mode(&self) -> VideoModeHandle { fn preferred_video_mode(&self) -> VideoMode {
VideoModeHandle { video_mode: self.inner.preferred_video_mode() } self.inner.preferred_video_mode()
} }
} }

View file

@ -1,6 +1,5 @@
use std::cell::Cell; use std::cell::Cell;
use std::hash::Hash; use std::hash::Hash;
use std::num::{NonZeroU16, NonZeroU32};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -21,7 +20,7 @@ use crate::event_loop::{
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider, EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
OwnedDisplayHandle as CoreOwnedDisplayHandle, OwnedDisplayHandle as CoreOwnedDisplayHandle,
}; };
use crate::monitor::MonitorHandle as RootMonitorHandle; use crate::monitor::{MonitorHandle as RootMonitorHandle, VideoMode};
use crate::platform::pump_events::PumpStatus; use crate::platform::pump_events::PumpStatus;
use crate::window::{ use crate::window::{
self, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, ImePurpose, self, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, ImePurpose,
@ -1021,32 +1020,11 @@ impl MonitorHandle {
unreachable!() unreachable!()
} }
pub fn current_video_mode(&self) -> Option<VideoModeHandle> { pub fn current_video_mode(&self) -> Option<VideoMode> {
unreachable!() unreachable!()
} }
pub fn video_modes(&self) -> std::iter::Empty<VideoModeHandle> { pub fn video_modes(&self) -> std::iter::Empty<VideoMode> {
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 {
unreachable!() unreachable!()
} }
} }

View file

@ -19,7 +19,7 @@ pub(crate) use self::event::{physicalkey_to_scancode, scancode_to_physicalkey, K
pub(crate) use self::event_loop::{ pub(crate) use self::event_loop::{
ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes, 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::Window;
pub(crate) use self::window_delegate::PlatformSpecificWindowAttributes; pub(crate) use self::window_delegate::PlatformSpecificWindowAttributes;
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource; pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;

View file

@ -17,22 +17,18 @@ use objc2_foundation::{ns_string, run_on_main, MainThreadMarker, NSNumber, NSPoi
use super::ffi; use super::ffi;
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use crate::monitor::VideoMode;
#[derive(Clone)] #[derive(Clone)]
pub struct VideoModeHandle { pub struct VideoModeHandle {
size: PhysicalSize<u32>, pub(crate) mode: VideoMode,
bit_depth: Option<NonZeroU16>,
refresh_rate_millihertz: Option<NonZeroU32>,
pub(crate) monitor: MonitorHandle, pub(crate) monitor: MonitorHandle,
pub(crate) native_mode: NativeDisplayMode, pub(crate) native_mode: NativeDisplayMode,
} }
impl PartialEq for VideoModeHandle { impl PartialEq for VideoModeHandle {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.size == other.size self.monitor == other.monitor && self.mode == other.mode
&& self.bit_depth == other.bit_depth
&& self.refresh_rate_millihertz == other.refresh_rate_millihertz
&& self.monitor == other.monitor
} }
} }
@ -40,19 +36,14 @@ impl Eq for VideoModeHandle {}
impl std::hash::Hash for VideoModeHandle { impl std::hash::Hash for VideoModeHandle {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { 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); self.monitor.hash(state);
} }
} }
impl std::fmt::Debug for VideoModeHandle { impl std::fmt::Debug for VideoModeHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VideoModeHandle") f.debug_struct("VideoMode")
.field("size", &self.size) .field("mode", &self.mode)
.field("bit_depth", &self.bit_depth)
.field("refresh_rate_millihertz", &self.refresh_rate_millihertz)
.field("monitor", &self.monitor) .field("monitor", &self.monitor)
.finish() .finish()
} }
@ -83,13 +74,14 @@ impl Clone for NativeDisplayMode {
impl VideoModeHandle { impl VideoModeHandle {
fn new( fn new(
monitor: MonitorHandle, monitor: MonitorHandle,
mode: NativeDisplayMode, native_mode: NativeDisplayMode,
refresh_rate_millihertz: Option<NonZeroU32>, refresh_rate_millihertz: Option<NonZeroU32>,
) -> Self { ) -> Self {
unsafe { unsafe {
let pixel_encoding = let pixel_encoding = CFString::wrap_under_create_rule(
CFString::wrap_under_create_rule(ffi::CGDisplayModeCopyPixelEncoding(mode.0)) ffi::CGDisplayModeCopyPixelEncoding(native_mode.0),
.to_string(); )
.to_string();
let bit_depth = if pixel_encoding.eq_ignore_ascii_case(ffi::IO32BitDirectPixels) { let bit_depth = if pixel_encoding.eq_ignore_ascii_case(ffi::IO32BitDirectPixels) {
32 32
} else if pixel_encoding.eq_ignore_ascii_case(ffi::IO16BitDirectPixels) { } else if pixel_encoding.eq_ignore_ascii_case(ffi::IO16BitDirectPixels) {
@ -100,34 +92,18 @@ impl VideoModeHandle {
unimplemented!() unimplemented!()
}; };
VideoModeHandle { let mode = VideoMode {
size: PhysicalSize::new( size: PhysicalSize::new(
ffi::CGDisplayModeGetPixelWidth(mode.0) as u32, ffi::CGDisplayModeGetPixelWidth(native_mode.0) as u32,
ffi::CGDisplayModeGetPixelHeight(mode.0) as u32, ffi::CGDisplayModeGetPixelHeight(native_mode.0) as u32,
), ),
refresh_rate_millihertz, refresh_rate_millihertz,
bit_depth: NonZeroU16::new(bit_depth), bit_depth: NonZeroU16::new(bit_depth),
monitor: monitor.clone(), };
native_mode: mode,
} VideoModeHandle { mode, monitor: monitor.clone(), native_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()
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -240,13 +216,17 @@ impl MonitorHandle {
refresh_rate_millihertz(self.0, &current_display_mode) 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 mode = NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _);
let refresh_rate_millihertz = refresh_rate_millihertz(self.0, &mode); 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 = VideoModeHandle> { pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.video_modes_handles().map(|handle| handle.mode)
}
pub(crate) fn video_modes_handles(&self) -> impl Iterator<Item = VideoModeHandle> {
let refresh_rate_millihertz = self.refresh_rate_millihertz(); let refresh_rate_millihertz = self.refresh_rate_millihertz();
let monitor = self.clone(); let monitor = self.clone();

View file

@ -64,7 +64,7 @@ impl Window {
impl Drop for Window { impl Drop for Window {
fn drop(&mut self) { fn drop(&mut self) {
// Restore the video mode. // Restore the video mode.
if matches!(self.fullscreen(), Some(Fullscreen::Exclusive(_))) { if matches!(self.fullscreen(), Some(Fullscreen::Exclusive(_, _))) {
self.set_fullscreen(None); self.set_fullscreen(None);
} }

View file

@ -7,7 +7,6 @@ use std::rc::Rc;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use core_graphics::display::CGDisplay; use core_graphics::display::CGDisplay;
use monitor::VideoModeHandle;
use objc2::rc::{autoreleasepool, Retained}; use objc2::rc::{autoreleasepool, Retained};
use objc2::runtime::{AnyObject, ProtocolObject}; use objc2::runtime::{AnyObject, ProtocolObject};
use objc2::{declare_class, msg_send_id, mutability, sel, ClassType, DeclaredClass}; 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 // Exclusive mode sets the state in `set_fullscreen` as the user
// can't enter exclusive mode by other means (like the // can't enter exclusive mode by other means (like the
// fullscreen button on the window decorations) // fullscreen button on the window decorations)
Some(Fullscreen::Exclusive(_)) => (), Some(Fullscreen::Exclusive(_, _)) => (),
// `window_will_enter_fullscreen` was triggered and we're already // `window_will_enter_fullscreen` was triggered and we're already
// in fullscreen, so we must've reached here by `set_fullscreen` // in fullscreen, so we must've reached here by `set_fullscreen`
// as it updates the state // as it updates the state
@ -287,7 +286,7 @@ declare_class!(
// user-provided options are ignored in exclusive fullscreen. // user-provided options are ignored in exclusive fullscreen.
let mut options = proposed_options; let mut options = proposed_options;
let fullscreen = self.ivars().fullscreen.borrow(); let fullscreen = self.ivars().fullscreen.borrow();
if let Some(Fullscreen::Exclusive(_)) = &*fullscreen { if let Some(Fullscreen::Exclusive(_, _)) = &*fullscreen {
options = NSApplicationPresentationOptions::NSApplicationPresentationFullScreen options = NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock | NSApplicationPresentationOptions::NSApplicationPresentationHideDock
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar; | NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
@ -507,7 +506,7 @@ fn new_window(
autoreleasepool(|_| { autoreleasepool(|_| {
let screen = match attrs.fullscreen.clone().map(Into::into) { let screen = match attrs.fullscreen.clone().map(Into::into) {
Some(Fullscreen::Borderless(Some(monitor))) Some(Fullscreen::Borderless(Some(monitor)))
| Some(Fullscreen::Exclusive(VideoModeHandle { monitor, .. })) => { | Some(Fullscreen::Exclusive(monitor, _)) => {
monitor.ns_screen(mtm).or_else(|| NSScreen::mainScreen(mtm)) monitor.ns_screen(mtm).or_else(|| NSScreen::mainScreen(mtm))
}, },
Some(Fullscreen::Borderless(None)) => NSScreen::mainScreen(mtm), Some(Fullscreen::Borderless(None)) => NSScreen::mainScreen(mtm),
@ -1426,7 +1425,7 @@ impl WindowDelegate {
return; return;
} }
}, },
Fullscreen::Exclusive(video_mode) => video_mode.monitor(), Fullscreen::Exclusive(monitor, _) => monitor.clone(),
} }
.ns_screen(mtm) .ns_screen(mtm)
.unwrap(); .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 // Note: `enterFullScreenMode:withOptions:` seems to do the exact
// same thing as we're doing here (captures the display, sets the // same thing as we're doing here (captures the display, sets the
// video mode, and hides the menu bar and dock), with the exception // 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 // parameter, which is not consistent with the docs saying that it
// takes a `NSDictionary`.. // takes a `NSDictionary`..
let display_id = video_mode.monitor().native_identifier(); let display_id = monitor.native_identifier();
let mut fade_token = ffi::kCGDisplayFadeReservationInvalidToken; let mut fade_token = ffi::kCGDisplayFadeReservationInvalidToken;
@ -1479,6 +1478,12 @@ impl WindowDelegate {
assert_eq!(ffi::CGDisplayCapture(display_id), ffi::kCGErrorSuccess); 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 { unsafe {
let result = ffi::CGDisplaySetDisplayMode( let result = ffi::CGDisplaySetDisplayMode(
display_id, display_id,
@ -1543,11 +1548,11 @@ impl WindowDelegate {
// State is restored by `window_did_exit_fullscreen` // State is restored by `window_did_exit_fullscreen`
toggle_fullscreen(self.window()); toggle_fullscreen(self.window());
}, },
(Some(Fullscreen::Exclusive(ref video_mode)), None) => { (Some(Fullscreen::Exclusive(ref monitor, _)), None) => {
restore_and_release_display(&video_mode.monitor()); restore_and_release_display(monitor);
toggle_fullscreen(self.window()); toggle_fullscreen(self.window());
}, },
(Some(Fullscreen::Borderless(_)), Some(Fullscreen::Exclusive(_))) => { (Some(Fullscreen::Borderless(_)), Some(Fullscreen::Exclusive(..))) => {
// If we're already in fullscreen mode, calling // If we're already in fullscreen mode, calling
// `CGDisplayCapture` will place the shielding window on top of // `CGDisplayCapture` will place the shielding window on top of
// our window, which results in a black display and is not what // 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; let window_level = unsafe { ffi::CGShieldingWindowLevel() } as NSWindowLevel + 1;
self.window().setLevel(window_level); 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( let presentation_options = self.ivars().save_presentation_opts.get().unwrap_or(
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock | NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock
@ -1575,7 +1580,7 @@ impl WindowDelegate {
); );
app.setPresentationOptions(presentation_options); 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 // Restore the normal window level following the Borderless fullscreen
// `CGShieldingWindowLevel() + 1` hack. // `CGShieldingWindowLevel() + 1` hack.

View file

@ -12,7 +12,7 @@ use std::fmt;
pub(crate) use self::event_loop::{ pub(crate) use self::event_loop::{
ActiveEventLoop, EventLoop, EventLoopProxy, PlatformSpecificEventLoopAttributes, 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 self::window::{PlatformSpecificWindowAttributes, Window};
pub(crate) use crate::cursor::{ pub(crate) use crate::cursor::{
NoCustomCursor as PlatformCustomCursor, NoCustomCursor as PlatformCustomCursorSource, NoCustomCursor as PlatformCustomCursor, NoCustomCursor as PlatformCustomCursorSource,

View file

@ -1,7 +1,7 @@
#![allow(clippy::unnecessary_cast)] #![allow(clippy::unnecessary_cast)]
use std::collections::{BTreeSet, VecDeque}; use std::collections::VecDeque;
use std::num::{NonZeroU16, NonZeroU32}; use std::num::NonZeroU32;
use std::{fmt, hash, ptr}; use std::{fmt, hash, ptr};
use objc2::mutability::IsRetainable; use objc2::mutability::IsRetainable;
@ -11,8 +11,8 @@ use objc2_foundation::{run_on_main, MainThreadBound, MainThreadMarker, NSInteger
use objc2_ui_kit::{UIScreen, UIScreenMode}; use objc2_ui_kit::{UIScreen, UIScreenMode};
use super::app_state; use super::app_state;
use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::dpi::PhysicalPosition;
use crate::monitor::VideoModeHandle as RootVideoModeHandle; use crate::monitor::VideoMode;
// Workaround for `MainThreadBound` implementing almost no traits // Workaround for `MainThreadBound` implementing almost no traits
#[derive(Debug)] #[derive(Debug)]
@ -44,10 +44,8 @@ impl<T: IsRetainable + Message> Eq for MainThreadBoundDelegateImpls<T> {}
#[derive(Debug, PartialEq, Eq, Hash, Clone)] #[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct VideoModeHandle { pub struct VideoModeHandle {
pub(crate) size: (u32, u32), pub(crate) mode: VideoMode,
pub(crate) refresh_rate_millihertz: Option<NonZeroU32>,
screen_mode: MainThreadBoundDelegateImpls<UIScreenMode>, screen_mode: MainThreadBoundDelegateImpls<UIScreenMode>,
pub(crate) monitor: MonitorHandle,
} }
impl VideoModeHandle { impl VideoModeHandle {
@ -58,30 +56,18 @@ impl VideoModeHandle {
) -> VideoModeHandle { ) -> VideoModeHandle {
let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen); let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen);
let size = screen_mode.size(); let size = screen_mode.size();
VideoModeHandle { let mode = VideoMode {
size: (size.width as u32, size.height as u32), size: (size.width as u32, size.height as u32).into(),
bit_depth: None,
refresh_rate_millihertz, refresh_rate_millihertz,
};
VideoModeHandle {
mode,
screen_mode: MainThreadBoundDelegateImpls(MainThreadBound::new(screen_mode, mtm)), screen_mode: MainThreadBoundDelegateImpls(MainThreadBound::new(screen_mode, mtm)),
monitor: MonitorHandle::new(uiscreen),
} }
} }
pub fn size(&self) -> PhysicalSize<u32> {
self.size.into()
}
pub fn bit_depth(&self) -> Option<NonZeroU16> {
None
}
pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
self.refresh_rate_millihertz
}
pub fn monitor(&self) -> MonitorHandle {
self.monitor.clone()
}
pub(super) fn screen_mode(&self, mtm: MainThreadMarker) -> &Retained<UIScreenMode> { pub(super) fn screen_mode(&self, mtm: MainThreadMarker) -> &Retained<UIScreenMode> {
self.screen_mode.0.get(mtm) 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 self.ui_screen.get_on_main(|ui_screen| ui_screen.nativeScale()) as f64
} }
pub fn current_video_mode(&self) -> Option<VideoModeHandle> { pub fn current_video_mode(&self) -> Option<VideoMode> {
Some(run_on_main(|mtm| { Some(run_on_main(|mtm| {
VideoModeHandle::new( VideoModeHandle::new(
self.ui_screen(mtm).clone(), self.ui_screen(mtm).clone(),
self.ui_screen(mtm).currentMode().unwrap(), self.ui_screen(mtm).currentMode().unwrap(),
mtm, mtm,
) )
.mode
})) }))
} }
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> { pub fn video_modes_handles(&self) -> impl Iterator<Item = VideoModeHandle> {
run_on_main(|mtm| { run_on_main(|mtm| {
let ui_screen = self.ui_screen(mtm); let ui_screen = self.ui_screen(mtm);
// Use Ord impl of RootVideoModeHandle
let modes: BTreeSet<_> = ui_screen ui_screen
.availableModes() .availableModes()
.into_iter() .into_iter()
.map(|mode| RootVideoModeHandle { .map(|mode| VideoModeHandle::new(ui_screen.clone(), mode, mtm))
video_mode: VideoModeHandle::new(ui_screen.clone(), mode, mtm), .collect::<Vec<_>>()
}) .into_iter()
.collect();
modes.into_iter().map(|mode| mode.video_mode)
}) })
} }
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.video_modes_handles().map(|handle| handle.mode)
}
pub(crate) fn ui_screen(&self, mtm: MainThreadMarker) -> &Retained<UIScreen> { pub(crate) fn ui_screen(&self, mtm: MainThreadMarker) -> &Retained<UIScreen> {
self.ui_screen.get(mtm) self.ui_screen.get(mtm)
} }
pub fn preferred_video_mode(&self) -> VideoModeHandle { pub fn preferred_video_mode(&self) -> VideoMode {
run_on_main(|mtm| { run_on_main(|mtm| {
VideoModeHandle::new( VideoModeHandle::new(
self.ui_screen(mtm).clone(), self.ui_screen(mtm).clone(),
self.ui_screen(mtm).preferredMode().unwrap(), self.ui_screen(mtm).preferredMode().unwrap(),
mtm, mtm,
) )
.mode
}) })
} }
} }

View file

@ -91,10 +91,13 @@ impl WinitUIWindow {
this.setRootViewController(Some(view_controller)); this.setRootViewController(Some(view_controller));
match window_attributes.fullscreen.clone().map(Into::into) { match window_attributes.fullscreen.clone().map(Into::into) {
Some(Fullscreen::Exclusive(ref video_mode)) => { Some(Fullscreen::Exclusive(ref monitor, ref video_mode)) => {
let monitor = video_mode.monitor();
let screen = monitor.ui_screen(mtm); 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); this.setScreen(screen);
}, },
Some(Fullscreen::Borderless(Some(ref monitor))) => { Some(Fullscreen::Borderless(Some(ref monitor))) => {
@ -312,9 +315,13 @@ impl Inner {
pub(crate) fn set_fullscreen(&self, monitor: Option<Fullscreen>) { pub(crate) fn set_fullscreen(&self, monitor: Option<Fullscreen>) {
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
let uiscreen = match &monitor { let uiscreen = match &monitor {
Some(Fullscreen::Exclusive(video_mode)) => { Some(Fullscreen::Exclusive(monitor, video_mode)) => {
let uiscreen = video_mode.monitor.ui_screen(mtm); let uiscreen = monitor.ui_screen(mtm);
uiscreen.setCurrentMode(Some(video_mode.screen_mode(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() uiscreen.clone()
}, },
Some(Fullscreen::Borderless(Some(monitor))) => monitor.ui_screen(mtm).clone(), Some(Fullscreen::Borderless(Some(monitor))) => monitor.ui_screen(mtm).clone(),
@ -489,7 +496,7 @@ impl Window {
let main_screen = UIScreen::mainScreen(mtm); let main_screen = UIScreen::mainScreen(mtm);
let fullscreen = window_attributes.fullscreen.clone().map(Into::into); let fullscreen = window_attributes.fullscreen.clone().map(Into::into);
let screen = match fullscreen { 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(Some(ref monitor))) => monitor.ui_screen(mtm),
Some(Fullscreen::Borderless(None)) | None => &main_screen, Some(Fullscreen::Borderless(None)) | None => &main_screen,
}; };

View file

@ -4,7 +4,6 @@
compile_error!("Please select a feature to build for unix: `x11`, `wayland`"); compile_error!("Please select a feature to build for unix: `x11`, `wayland`");
use std::env; use std::env;
use std::num::{NonZeroU16, NonZeroU32};
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::time::Duration; use std::time::Duration;
#[cfg(x11_platform)] #[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 self::x11::{XConnection, XError, XNotSupported};
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource; pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
use crate::dpi::PhysicalPosition;
#[cfg(x11_platform)] #[cfg(x11_platform)]
use crate::dpi::Size; use crate::dpi::Size;
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::error::{EventLoopError, NotSupportedError}; use crate::error::{EventLoopError, NotSupportedError};
use crate::event_loop::ActiveEventLoop; use crate::event_loop::ActiveEventLoop;
pub(crate) use crate::icon::RgbaIcon as PlatformIcon; pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
use crate::keyboard::Key; use crate::keyboard::Key;
use crate::monitor::VideoMode;
use crate::platform::pump_events::PumpStatus; use crate::platform::pump_events::PumpStatus;
#[cfg(x11_platform)] #[cfg(x11_platform)]
use crate::platform::x11::{WindowType as XWindowType, XlibErrorHook}; use crate::platform::x11::{WindowType as XWindowType, XlibErrorHook};
@ -165,46 +165,16 @@ impl MonitorHandle {
} }
#[inline] #[inline]
pub fn current_video_mode(&self) -> Option<VideoModeHandle> { pub fn current_video_mode(&self) -> Option<VideoMode> {
x11_or_wayland!(match self; MonitorHandle(m) => m.current_video_mode()) x11_or_wayland!(match self; MonitorHandle(m) => m.current_video_mode())
} }
#[inline] #[inline]
pub fn video_modes(&self) -> Box<dyn Iterator<Item = VideoModeHandle>> { pub fn video_modes(&self) -> Box<dyn Iterator<Item = VideoMode>> {
x11_or_wayland!(match self; MonitorHandle(m) => Box::new(m.video_modes())) 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<u32> {
x11_or_wayland!(match self; VideoModeHandle(m) => m.size())
}
#[inline]
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<NonZeroU32> {
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)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct KeyEventExtra { pub struct KeyEventExtra {
pub text_with_all_modifiers: Option<SmolStr>, pub text_with_all_modifiers: Option<SmolStr>,

View file

@ -1,7 +1,7 @@
//! Winit's Wayland backend. //! Winit's Wayland backend.
pub use event_loop::{ActiveEventLoop, EventLoop}; 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::protocol::wl_surface::WlSurface;
use sctk::reexports::client::Proxy; use sctk::reexports::client::Proxy;
pub use window::Window; pub use window::Window;

View file

@ -1,11 +1,11 @@
use std::num::{NonZeroU16, NonZeroU32}; use std::num::NonZeroU32;
use sctk::output::{Mode, OutputData}; use sctk::output::{Mode, OutputData};
use sctk::reexports::client::protocol::wl_output::WlOutput; use sctk::reexports::client::protocol::wl_output::WlOutput;
use sctk::reexports::client::Proxy; use sctk::reexports::client::Proxy;
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; use crate::dpi::{LogicalPosition, PhysicalPosition};
use crate::platform_impl::platform::VideoModeHandle as PlatformVideoModeHandle; use crate::monitor::VideoMode;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MonitorHandle { pub struct MonitorHandle {
@ -54,27 +54,19 @@ impl MonitorHandle {
} }
#[inline] #[inline]
pub fn current_video_mode(&self) -> Option<PlatformVideoModeHandle> { pub fn current_video_mode(&self) -> Option<VideoMode> {
let output_data = self.proxy.data::<OutputData>().unwrap(); let output_data = self.proxy.data::<OutputData>().unwrap();
output_data.with_output_info(|info| { output_data.with_output_info(|info| {
let mode = info.modes.iter().find(|mode| mode.current).cloned(); let mode = info.modes.iter().find(|mode| mode.current).cloned();
mode.map(wayland_mode_to_core_mode)
mode.map(|mode| {
PlatformVideoModeHandle::Wayland(VideoModeHandle::new(self.clone(), mode))
})
}) })
} }
#[inline] #[inline]
pub fn video_modes(&self) -> impl Iterator<Item = PlatformVideoModeHandle> { pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
let output_data = self.proxy.data::<OutputData>().unwrap(); let output_data = self.proxy.data::<OutputData>().unwrap();
let modes = output_data.with_output_info(|info| info.modes.clone()); let modes = output_data.with_output_info(|info| info.modes.clone());
modes.into_iter().map(wayland_mode_to_core_mode)
let monitor = self.clone();
modes.into_iter().map(move |mode| {
PlatformVideoModeHandle::Wayland(VideoModeHandle::new(monitor.clone(), mode))
})
} }
} }
@ -104,38 +96,11 @@ impl std::hash::Hash for MonitorHandle {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] /// Convert Wayland's [`Mode`] to winit's [`VideoMode`].
pub struct VideoModeHandle { fn wayland_mode_to_core_mode(mode: Mode) -> VideoMode {
pub(crate) size: PhysicalSize<u32>, VideoMode {
pub(crate) refresh_rate_millihertz: Option<NonZeroU32>, size: (mode.dimensions.0, mode.dimensions.1).into(),
pub(crate) monitor: MonitorHandle, bit_depth: None,
} refresh_rate_millihertz: NonZeroU32::new(mode.refresh_rate as u32),
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<u32> {
self.size
}
#[inline]
pub fn bit_depth(&self) -> Option<NonZeroU16> {
None
}
#[inline]
pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
self.refresh_rate_millihertz
}
pub fn monitor(&self) -> MonitorHandle {
self.monitor.clone()
} }
} }

View file

@ -139,7 +139,7 @@ impl Window {
// Set startup mode. // Set startup mode.
match attributes.fullscreen.map(Into::into) { match attributes.fullscreen.map(Into::into) {
Some(Fullscreen::Exclusive(_)) => { Some(Fullscreen::Exclusive(..)) => {
warn!("`Fullscreen::Exclusive` is ignored on Wayland"); warn!("`Fullscreen::Exclusive` is ignored on Wayland");
}, },
#[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))] #[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<CoreFullscreen>) { fn set_fullscreen(&self, fullscreen: Option<CoreFullscreen>) {
match fullscreen { match fullscreen {
Some(CoreFullscreen::Exclusive(_)) => { Some(CoreFullscreen::Exclusive(..)) => {
warn!("`Fullscreen::Exclusive` is ignored on Wayland"); warn!("`Fullscreen::Exclusive` is ignored on Wayland");
}, },
#[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))] #[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))]

View file

@ -1,12 +1,12 @@
use std::num::{NonZeroU16, NonZeroU32}; use std::num::NonZeroU32;
use x11rb::connection::RequestConnection; use x11rb::connection::RequestConnection;
use x11rb::protocol::randr::{self, ConnectionExt as _}; use x11rb::protocol::randr::{self, ConnectionExt as _};
use x11rb::protocol::xproto; use x11rb::protocol::xproto;
use super::{util, X11Error, XConnection}; use super::{util, X11Error, XConnection};
use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::dpi::PhysicalPosition;
use crate::platform_impl::VideoModeHandle as PlatformVideoModeHandle; use crate::monitor::VideoMode;
// Used for testing. This should always be committed as false. // Used for testing. This should always be committed as false.
const DISABLE_MONITOR_LIST_CACHING: bool = false; const DISABLE_MONITOR_LIST_CACHING: bool = false;
@ -21,32 +21,14 @@ impl XConnection {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VideoModeHandle { pub struct VideoModeHandle {
pub(crate) current: bool, pub(crate) current: bool,
pub(crate) size: (u32, u32), pub(crate) mode: VideoMode,
pub(crate) bit_depth: Option<NonZeroU16>,
pub(crate) refresh_rate_millihertz: Option<NonZeroU32>,
pub(crate) native_mode: randr::Mode, pub(crate) native_mode: randr::Mode,
pub(crate) monitor: Option<MonitorHandle>, pub(crate) monitor: Option<MonitorHandle>,
} }
impl VideoModeHandle { impl From<VideoModeHandle> for VideoMode {
#[inline] fn from(handle: VideoModeHandle) -> Self {
pub fn size(&self) -> PhysicalSize<u32> { handle.mode
self.size.into()
}
#[inline]
pub fn bit_depth(&self) -> Option<NonZeroU16> {
self.bit_depth
}
#[inline]
pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
self.refresh_rate_millihertz
}
#[inline]
pub fn monitor(&self) -> MonitorHandle {
self.monitor.clone().unwrap()
} }
} }
@ -65,7 +47,7 @@ pub struct MonitorHandle {
/// Used to determine which windows are on this monitor /// Used to determine which windows are on this monitor
pub(crate) rect: util::AaRect, pub(crate) rect: util::AaRect,
/// Supported video modes on this monitor /// Supported video modes on this monitor
video_modes: Vec<VideoModeHandle>, pub(crate) video_modes: Vec<VideoModeHandle>,
} }
impl PartialEq for MonitorHandle { impl PartialEq for MonitorHandle {
@ -159,17 +141,13 @@ impl MonitorHandle {
} }
#[inline] #[inline]
pub fn current_video_mode(&self) -> Option<PlatformVideoModeHandle> { pub fn current_video_mode(&self) -> Option<VideoMode> {
self.video_modes.iter().find(|mode| mode.current).cloned().map(PlatformVideoModeHandle::X) self.video_modes.iter().find(|mode| mode.current).cloned().map(Into::into)
} }
#[inline] #[inline]
pub fn video_modes(&self) -> impl Iterator<Item = PlatformVideoModeHandle> { pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
let monitor = self.clone(); self.video_modes.clone().into_iter().map(Into::into)
self.video_modes.clone().into_iter().map(move |mut x| {
x.monitor = Some(monitor.clone());
PlatformVideoModeHandle::X(x)
})
} }
} }

View file

@ -7,6 +7,7 @@ use x11rb::protocol::randr::{self, ConnectionExt as _};
use super::*; use super::*;
use crate::dpi::validate_scale_factor; use crate::dpi::validate_scale_factor;
use crate::monitor::VideoMode;
use crate::platform_impl::platform::x11::{monitor, VideoModeHandle}; use crate::platform_impl::platform::x11::{monitor, VideoModeHandle};
/// Represents values of `WINIT_HIDPI_FACTOR`. /// Represents values of `WINIT_HIDPI_FACTOR`.
@ -85,9 +86,11 @@ impl XConnection {
.map(|mode| { .map(|mode| {
VideoModeHandle { VideoModeHandle {
current: mode.id == current_mode, current: mode.id == current_mode,
size: (mode.width.into(), mode.height.into()), mode: VideoMode {
refresh_rate_millihertz: monitor::mode_refresh_rate_millihertz(mode), size: (mode.width as u32, mode.height as u32).into(),
bit_depth: NonZeroU16::new(bit_depth as u16), refresh_rate_millihertz: monitor::mode_refresh_rate_millihertz(mode),
bit_depth: NonZeroU16::new(bit_depth as u16),
},
native_mode: mode.id, native_mode: mode.id,
// This is populated in `MonitorHandle::video_modes` as the // This is populated in `MonitorHandle::video_modes` as the
// video mode is returned to the user // video mode is returned to the user

View file

@ -32,7 +32,6 @@ use crate::platform_impl::x11::{
}; };
use crate::platform_impl::{ use crate::platform_impl::{
common, Fullscreen, MonitorHandle as PlatformMonitorHandle, PlatformCustomCursor, PlatformIcon, common, Fullscreen, MonitorHandle as PlatformMonitorHandle, PlatformCustomCursor, PlatformIcon,
VideoModeHandle as PlatformVideoModeHandle,
}; };
use crate::window::{ use crate::window::{
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow, CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow,
@ -327,7 +326,7 @@ impl Drop for Window {
let xconn = &window.xconn; let xconn = &window.xconn;
// Restore the video mode on drop. // Restore the video mode on drop.
if let Some(Fullscreen::Exclusive(_)) = window.fullscreen() { if let Some(Fullscreen::Exclusive(..)) = window.fullscreen() {
window.set_fullscreen(None); window.set_fullscreen(None);
} }
@ -1035,20 +1034,17 @@ impl UnownedWindow {
// fullscreen, so we can restore it upon exit, as XRandR does not // fullscreen, so we can restore it upon exit, as XRandR does not
// provide a mechanism to set this per app-session or restore this // provide a mechanism to set this per app-session or restore this
// to the desktop video mode as macOS and Windows do // to the desktop video mode as macOS and Windows do
(&None, &Some(Fullscreen::Exclusive(PlatformVideoModeHandle::X(ref video_mode)))) (&None, &Some(Fullscreen::Exclusive(ref monitor, _)))
| ( | (&Some(Fullscreen::Borderless(_)), &Some(Fullscreen::Exclusive(ref monitor, _))) => {
&Some(Fullscreen::Borderless(_)), let id = monitor.native_identifier();
&Some(Fullscreen::Exclusive(PlatformVideoModeHandle::X(ref video_mode))),
) => {
let monitor = video_mode.monitor.as_ref().unwrap();
shared_state_lock.desktop_video_mode = Some(( shared_state_lock.desktop_video_mode = Some((
monitor.id, id,
self.xconn.get_crtc_mode(monitor.id).expect("Failed to get desktop video mode"), self.xconn.get_crtc_mode(id).expect("Failed to get desktop video mode"),
)); ));
}, },
// Restore desktop video mode upon exiting exclusive fullscreen // Restore desktop video mode upon exiting exclusive fullscreen
(&Some(Fullscreen::Exclusive(_)), &None) (&Some(Fullscreen::Exclusive(..)), &None)
| (&Some(Fullscreen::Exclusive(_)), &Some(Fullscreen::Borderless(_))) => { | (&Some(Fullscreen::Exclusive(..)), &Some(Fullscreen::Borderless(_))) => {
let (monitor_id, mode_id) = shared_state_lock.desktop_video_mode.take().unwrap(); let (monitor_id, mode_id) = shared_state_lock.desktop_video_mode.take().unwrap();
self.xconn self.xconn
.set_crtc_config(monitor_id, mode_id) .set_crtc_config(monitor_id, mode_id)
@ -1072,8 +1068,8 @@ impl UnownedWindow {
}, },
Some(fullscreen) => { Some(fullscreen) => {
let (video_mode, monitor) = match fullscreen { let (video_mode, monitor) = match fullscreen {
Fullscreen::Exclusive(PlatformVideoModeHandle::X(ref video_mode)) => { Fullscreen::Exclusive(PlatformMonitorHandle::X(monitor), video_mode) => {
(Some(video_mode), video_mode.monitor.clone().unwrap()) (Some(video_mode), monitor.clone())
}, },
Fullscreen::Borderless(Some(PlatformMonitorHandle::X(monitor))) => { Fullscreen::Borderless(Some(PlatformMonitorHandle::X(monitor))) => {
(None, monitor) (None, monitor)
@ -1090,7 +1086,15 @@ impl UnownedWindow {
return Ok(None); 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.mode == requested {
Some(mode.native_mode)
} else {
None
}
})
}) {
// FIXME: this is actually not correct if we're setting the // FIXME: this is actually not correct if we're setting the
// video mode to a resolution higher than the current // video mode to a resolution higher than the current
// desktop resolution, because XRandR does not automatically // desktop resolution, because XRandR does not automatically
@ -1117,7 +1121,7 @@ impl UnownedWindow {
// this will make someone unhappy, but it's very unusual for // this will make someone unhappy, but it's very unusual for
// games to want to do this anyway). // games to want to do this anyway).
self.xconn self.xconn
.set_crtc_config(monitor.id, video_mode.native_mode) .set_crtc_config(monitor.id, native_mode)
.expect("failed to set video mode"); .expect("failed to set video mode");
} }

View file

@ -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; use crate::window::Fullscreen as RootFullscreen;
#[cfg(android_platform)] #[cfg(android_platform)]
@ -30,17 +30,19 @@ use self::web as platform;
use self::windows as platform; use self::windows as platform;
/// Helper for converting between platform-specific and generic /// Helper for converting between platform-specific and generic
/// [`VideoModeHandle`]/[`MonitorHandle`] /// [`VideoMode`]/[`MonitorHandle`]
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum Fullscreen { pub(crate) enum Fullscreen {
Exclusive(VideoModeHandle), Exclusive(MonitorHandle, VideoMode),
Borderless(Option<MonitorHandle>), Borderless(Option<MonitorHandle>),
} }
impl From<RootFullscreen> for Fullscreen { impl From<RootFullscreen> for Fullscreen {
fn from(f: RootFullscreen) -> Self { fn from(f: RootFullscreen) -> Self {
match f { 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(Some(handle)) => Self::Borderless(Some(handle.inner)),
RootFullscreen::Borderless(None) => Self::Borderless(None), RootFullscreen::Borderless(None) => Self::Borderless(None),
} }
@ -50,8 +52,8 @@ impl From<RootFullscreen> for Fullscreen {
impl From<Fullscreen> for RootFullscreen { impl From<Fullscreen> for RootFullscreen {
fn from(f: Fullscreen) -> Self { fn from(f: Fullscreen) -> Self {
match f { match f {
Fullscreen::Exclusive(video_mode) => { Fullscreen::Exclusive(inner, video_mode) => {
Self::Exclusive(RootVideoModeHandle { video_mode }) Self::Exclusive(RootMonitorHandle { inner }, video_mode)
}, },
Fullscreen::Borderless(Some(inner)) => { Fullscreen::Borderless(Some(inner)) => {
Self::Borderless(Some(RootMonitorHandle { inner })) Self::Borderless(Some(RootMonitorHandle { inner }))

View file

@ -1,16 +1,16 @@
#![cfg(target_os = "redox")] #![cfg(target_os = "redox")]
use std::num::{NonZeroU16, NonZeroU32};
use std::{fmt, str}; use std::{fmt, str};
use smol_str::SmolStr; use smol_str::SmolStr;
pub(crate) use self::event_loop::{ActiveEventLoop, EventLoop}; pub(crate) use self::event_loop::{ActiveEventLoop, EventLoop};
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::keyboard::Key;
mod event_loop;
pub use self::window::Window; pub use self::window::Window;
use crate::dpi::PhysicalPosition;
use crate::keyboard::Key;
use crate::monitor::VideoMode;
mod event_loop;
mod window; mod window;
pub(crate) use crate::cursor::{ pub(crate) use crate::cursor::{
@ -151,38 +151,13 @@ impl MonitorHandle {
1.0 // TODO 1.0 // TODO
} }
pub fn current_video_mode(&self) -> Option<VideoModeHandle> { pub fn current_video_mode(&self) -> Option<VideoMode> {
// (it is guaranteed to support 32 bit color though) // (it is guaranteed to support 32 bit color though)
Some(VideoModeHandle { monitor: self.clone() })
}
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
self.current_video_mode().into_iter()
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct VideoModeHandle {
monitor: MonitorHandle,
}
impl VideoModeHandle {
pub fn size(&self) -> PhysicalSize<u32> {
// TODO
PhysicalSize::default()
}
pub fn bit_depth(&self) -> Option<NonZeroU16> {
None None
} }
pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> { pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
// TODO std::iter::empty()
None
}
pub fn monitor(&self) -> MonitorHandle {
self.monitor.clone()
} }
} }

View file

@ -43,7 +43,6 @@ pub(crate) use self::event_loop::{
pub(crate) use self::keyboard::KeyEventExtra; pub(crate) use self::keyboard::KeyEventExtra;
pub(crate) use self::monitor::{ pub(crate) use self::monitor::{
HasMonitorPermissionFuture, MonitorHandle, MonitorPermissionFuture, OrientationLockFuture, HasMonitorPermissionFuture, MonitorHandle, MonitorPermissionFuture, OrientationLockFuture,
VideoModeHandle,
}; };
use self::web_sys as backend; use self::web_sys as backend;
pub use self::window::{PlatformSpecificWindowAttributes, Window}; pub use self::window::{PlatformSpecificWindowAttributes, Window};

View file

@ -3,9 +3,8 @@ use std::cmp::Ordering;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::future::Future; use std::future::Future;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::iter::{self, Once};
use std::mem; use std::mem;
use std::num::{NonZeroU16, NonZeroU32}; use std::num::NonZeroU16;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::pin::Pin; use std::pin::Pin;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
@ -29,7 +28,7 @@ use super::main_thread::MainThreadMarker;
use super::r#async::{Dispatcher, Notified, Notifier}; use super::r#async::{Dispatcher, Notified, Notifier};
use super::web_sys::{Engine, EventListenerHandle}; use super::web_sys::{Engine, EventListenerHandle};
use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::monitor::MonitorHandle as RootMonitorHandle; use crate::monitor::{MonitorHandle as RootMonitorHandle, VideoMode};
use crate::platform::web::{ use crate::platform::web::{
MonitorPermissionError, Orientation, OrientationData, OrientationLock, OrientationLockError, MonitorPermissionError, Orientation, OrientationData, OrientationLock, OrientationLockError,
}; };
@ -59,12 +58,16 @@ impl MonitorHandle {
self.inner.queue(|inner| inner.name()) self.inner.queue(|inner| inner.name())
} }
pub fn current_video_mode(&self) -> Option<VideoModeHandle> { pub fn current_video_mode(&self) -> Option<VideoMode> {
Some(VideoModeHandle(self.clone())) 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<VideoModeHandle> { pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
iter::once(VideoModeHandle(self.clone())) self.current_video_mode().into_iter()
} }
pub fn orientation(&self) -> OrientationData { 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<u32> {
self.0.inner.queue(|inner| inner.size())
}
pub fn bit_depth(&self) -> Option<NonZeroU16> {
self.0.inner.queue(|inner| inner.bit_depth())
}
pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
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 { struct Inner {
window: WindowExt, window: WindowExt,
engine: Option<Engine>, engine: Option<Engine>,

View file

@ -55,7 +55,7 @@ pub(crate) fn request_fullscreen(
let canvas: &RequestFullscreen = canvas.unchecked_ref(); let canvas: &RequestFullscreen = canvas.unchecked_ref();
match fullscreen { 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)) => { Fullscreen::Borderless(Some(monitor)) => {
if !monitor::has_screen_details_support(window) { if !monitor::has_screen_details_support(window) {
error!( error!(

View file

@ -1311,8 +1311,8 @@ unsafe fn public_window_callback_inner(
*fullscreen_monitor = Some(MonitorHandle::new(new_monitor)); *fullscreen_monitor = Some(MonitorHandle::new(new_monitor));
} }
}, },
Fullscreen::Exclusive(ref video_mode) => { Fullscreen::Exclusive(ref monitor, _) => {
let old_monitor = video_mode.monitor.hmonitor(); let old_monitor = monitor.hmonitor();
if let Ok(old_monitor_info) = monitor::get_monitor_info(old_monitor) { if let Ok(old_monitor_info) = monitor::get_monitor_info(old_monitor) {
let old_monitor_rect = old_monitor_info.monitorInfo.rcMonitor; let old_monitor_rect = old_monitor_info.monitorInfo.rcMonitor;
window_pos.x = old_monitor_rect.left; window_pos.x = old_monitor_rect.left;

View file

@ -6,7 +6,7 @@ pub(crate) use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes
pub use self::icon::WinIcon as PlatformIcon; pub use self::icon::WinIcon as PlatformIcon;
pub(crate) use self::icon::{SelectedCursor, WinCursor as PlatformCustomCursor, WinIcon}; 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::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 self::window::Window;
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource; pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
use crate::event::DeviceId; use crate::event::DeviceId;

View file

@ -1,4 +1,4 @@
use std::collections::{BTreeSet, VecDeque}; use std::collections::{HashSet, VecDeque};
use std::hash::Hash; use std::hash::Hash;
use std::num::{NonZeroU16, NonZeroU32}; use std::num::{NonZeroU16, NonZeroU32};
use std::{io, mem, ptr}; use std::{io, mem, ptr};
@ -13,26 +13,20 @@ use windows_sys::Win32::Graphics::Gdi::{
use super::util::decode_wide; use super::util::decode_wide;
use crate::dpi::{PhysicalPosition, PhysicalSize}; 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::dpi::{dpi_to_scale_factor, get_monitor_dpi};
use crate::platform_impl::platform::util::has_flag; use crate::platform_impl::platform::util::has_flag;
#[derive(Clone)] #[derive(Clone)]
pub struct VideoModeHandle { pub struct VideoModeHandle {
pub(crate) size: (u32, u32), pub(crate) mode: VideoMode,
pub(crate) bit_depth: Option<NonZeroU16>,
pub(crate) refresh_rate_millihertz: Option<NonZeroU32>,
pub(crate) monitor: MonitorHandle,
// DEVMODEW is huge so we box it to avoid blowing up the size of winit::window::Fullscreen // DEVMODEW is huge so we box it to avoid blowing up the size of winit::window::Fullscreen
pub(crate) native_video_mode: Box<DEVMODEW>, pub(crate) native_video_mode: Box<DEVMODEW>,
} }
impl PartialEq for VideoModeHandle { impl PartialEq for VideoModeHandle {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.size == other.size self.mode == other.mode
&& self.bit_depth == other.bit_depth
&& self.refresh_rate_millihertz == other.refresh_rate_millihertz
&& self.monitor == other.monitor
} }
} }
@ -40,53 +34,29 @@ impl Eq for VideoModeHandle {}
impl std::hash::Hash for VideoModeHandle { impl std::hash::Hash for VideoModeHandle {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.size.hash(state); self.mode.hash(state);
self.bit_depth.hash(state);
self.refresh_rate_millihertz.hash(state);
self.monitor.hash(state);
} }
} }
impl std::fmt::Debug for VideoModeHandle { impl std::fmt::Debug for VideoModeHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VideoModeHandle") f.debug_struct("VideoMode").field("mode", &self.mode).finish()
.field("size", &self.size)
.field("bit_depth", &self.bit_depth)
.field("refresh_rate_millihertz", &self.refresh_rate_millihertz)
.field("monitor", &self.monitor)
.finish()
} }
} }
impl VideoModeHandle { impl VideoModeHandle {
fn new(monitor: MonitorHandle, mode: DEVMODEW) -> Self { fn new(native_video_mode: DEVMODEW) -> Self {
const REQUIRED_FIELDS: u32 = const REQUIRED_FIELDS: u32 =
DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
assert!(has_flag(mode.dmFields, REQUIRED_FIELDS)); assert!(has_flag(native_video_mode.dmFields, REQUIRED_FIELDS));
VideoModeHandle { let mode = VideoMode {
size: (mode.dmPelsWidth, mode.dmPelsHeight), size: (native_video_mode.dmPelsWidth, native_video_mode.dmPelsHeight).into(),
bit_depth: NonZeroU16::new(mode.dmBitsPerPel as u16), bit_depth: NonZeroU16::new(native_video_mode.dmBitsPerPel as u16),
refresh_rate_millihertz: NonZeroU32::new(mode.dmDisplayFrequency * 1000), refresh_rate_millihertz: NonZeroU32::new(native_video_mode.dmDisplayFrequency * 1000),
monitor, };
native_video_mode: Box::new(mode),
}
}
pub fn size(&self) -> PhysicalSize<u32> { VideoModeHandle { mode, native_video_mode: Box::new(native_video_mode) }
self.size.into()
}
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()
} }
} }
@ -193,7 +163,7 @@ impl MonitorHandle {
} }
#[inline] #[inline]
pub fn current_video_mode(&self) -> Option<VideoModeHandle> { pub fn current_video_mode(&self) -> Option<VideoMode> {
let monitor_info = get_monitor_info(self.0).ok()?; let monitor_info = get_monitor_info(self.0).ok()?;
let device_name = monitor_info.szDevice.as_ptr(); let device_name = monitor_info.szDevice.as_ptr();
unsafe { unsafe {
@ -204,29 +174,26 @@ impl MonitorHandle {
{ {
None None
} else { } else {
Some(VideoModeHandle::new(self.clone(), mode)) Some(VideoModeHandle::new(mode).mode)
} }
} }
} }
#[inline] pub(crate) fn video_mode_handles(&self) -> impl Iterator<Item = VideoModeHandle> {
pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
// EnumDisplaySettingsExW can return duplicate values (or some of the // EnumDisplaySettingsExW can return duplicate values (or some of the
// fields are probably changing, but we aren't looking at those fields // fields are probably changing, but we aren't looking at those fields
// anyway), so we're using a BTreeSet deduplicate // anyway), so we're using a BTreeSet deduplicate
let mut modes = BTreeSet::<RootVideoModeHandle>::new(); let mut modes = HashSet::<VideoModeHandle>::new();
let mod_map = |mode: RootVideoModeHandle| mode.video_mode;
let monitor_info = match get_monitor_info(self.0) { let monitor_info = match get_monitor_info(self.0) {
Ok(monitor_info) => monitor_info, Ok(monitor_info) => monitor_info,
Err(error) => { Err(error) => {
tracing::warn!("Error from get_monitor_info: {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 device_name = monitor_info.szDevice.as_ptr();
let mut i = 0; let mut i = 0;
loop { loop {
let mut mode: DEVMODEW = unsafe { mem::zeroed() }; let mut mode: DEVMODEW = unsafe { mem::zeroed() };
@ -236,13 +203,15 @@ impl MonitorHandle {
} }
// Use Ord impl of RootVideoModeHandle // Use Ord impl of RootVideoModeHandle
modes.insert(RootVideoModeHandle { modes.insert(VideoModeHandle::new(mode));
video_mode: VideoModeHandle::new(self.clone(), mode),
});
i += 1; i += 1;
} }
modes.into_iter().map(mod_map) modes.into_iter()
}
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.video_mode_handles().map(|mode| mode.mode)
} }
} }

View file

@ -334,7 +334,7 @@ impl Window {
impl Drop for Window { impl Drop for Window {
fn drop(&mut self) { fn drop(&mut self) {
// Restore fullscreen video mode on exit. // Restore fullscreen video mode on exit.
if matches!(self.fullscreen(), Some(CoreFullscreen::Exclusive(_))) { if matches!(self.fullscreen(), Some(CoreFullscreen::Exclusive(_, _))) {
self.set_fullscreen(None); self.set_fullscreen(None);
} }
@ -771,9 +771,13 @@ impl CoreWindow for Window {
// Change video mode if we're transitioning to or from exclusive // Change video mode if we're transitioning to or from exclusive
// fullscreen // fullscreen
match (&old_fullscreen, &fullscreen) { match (&old_fullscreen, &fullscreen) {
(_, Some(Fullscreen::Exclusive(video_mode))) => { (_, Some(Fullscreen::Exclusive(monitor, video_mode))) => {
let monitor = video_mode.monitor();
let monitor_info = monitor::get_monitor_info(monitor.hmonitor()).unwrap(); 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 { let res = unsafe {
ChangeDisplaySettingsExW( ChangeDisplaySettingsExW(
@ -791,7 +795,7 @@ impl CoreWindow for Window {
debug_assert!(res != DISP_CHANGE_FAILED); debug_assert!(res != DISP_CHANGE_FAILED);
assert_eq!(res, DISP_CHANGE_SUCCESSFUL); assert_eq!(res, DISP_CHANGE_SUCCESSFUL);
}, },
(Some(Fullscreen::Exclusive(_)), _) => { (Some(Fullscreen::Exclusive(..)), _) => {
let res = unsafe { let res = unsafe {
ChangeDisplaySettingsExW( ChangeDisplaySettingsExW(
ptr::null(), ptr::null(),
@ -828,7 +832,7 @@ impl CoreWindow for Window {
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| { WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
f.set( f.set(
WindowFlags::MARKER_EXCLUSIVE_FULLSCREEN, WindowFlags::MARKER_EXCLUSIVE_FULLSCREEN,
matches!(fullscreen, Some(Fullscreen::Exclusive(_))), matches!(fullscreen, Some(Fullscreen::Exclusive(_, _))),
); );
f.set( f.set(
WindowFlags::MARKER_BORDERLESS_FULLSCREEN, WindowFlags::MARKER_BORDERLESS_FULLSCREEN,
@ -858,7 +862,7 @@ impl CoreWindow for Window {
window_state.lock().unwrap().saved_window = Some(SavedWindow { placement }); window_state.lock().unwrap().saved_window = Some(SavedWindow { placement });
let monitor = match &fullscreen { let monitor = match &fullscreen {
Fullscreen::Exclusive(video_mode) => video_mode.monitor(), Fullscreen::Exclusive(monitor, _) => monitor.clone(),
Fullscreen::Borderless(Some(monitor)) => monitor.clone(), Fullscreen::Borderless(Some(monitor)) => monitor.clone(),
Fullscreen::Borderless(None) => monitor::current_monitor(window), Fullscreen::Borderless(None) => monitor::current_monitor(window),
}; };

View file

@ -10,7 +10,7 @@ pub use crate::cursor::{BadImage, Cursor, CustomCursor, CustomCursorSource, MAX_
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size}; use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::RequestError; use crate::error::RequestError;
pub use crate::icon::{BadIcon, Icon}; pub use crate::icon::{BadIcon, Icon};
use crate::monitor::{MonitorHandle, VideoModeHandle}; use crate::monitor::{MonitorHandle, VideoMode};
use crate::platform_impl::PlatformSpecificWindowAttributes; use crate::platform_impl::PlatformSpecificWindowAttributes;
use crate::utils::AsAny; 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. /// - **Wayland:** Does not support exclusive fullscreen mode and will no-op a request.
/// - **Windows:** Screen saver is disabled in fullscreen mode. /// - **Windows:** Screen saver is disabled in fullscreen mode.
/// - **Android / Orbital:** Unsupported. /// - **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( #[cfg_attr(
any(web_platform, docsrs), any(web_platform, docsrs),
doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]" 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, /// This changes the video mode of the monitor for fullscreen windows and,
/// if applicable, captures the monitor for exclusive use by this /// if applicable, captures the monitor for exclusive use by this
/// application. /// application.
Exclusive(VideoModeHandle), Exclusive(MonitorHandle, VideoMode),
/// Providing `None` to `Borderless` will fullscreen on the current monitor. /// Providing `None` to `Borderless` will fullscreen on the current monitor.
Borderless(Option<MonitorHandle>), Borderless(Option<MonitorHandle>),