diff --git a/examples/window.rs b/examples/window.rs index 6f5589f5ce..2cb84f1be3 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -21,6 +21,7 @@ use winit::error::RequestError; use winit::event::{DeviceEvent, DeviceId, Ime, MouseButton, MouseScrollDelta, WindowEvent}; use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::keyboard::{Key, ModifiersState}; +use winit::monitor::Fullscreen; #[cfg(macos_platform)] use winit::platform::macos::{ ApplicationHandlerExtMacOS, OptionAsAlt, WindowAttributesExtMacOS, WindowExtMacOS, @@ -34,8 +35,8 @@ use winit::platform::web::{ActiveEventLoopExtWeb, CustomCursorExtWeb, WindowAttr #[cfg(x11_platform)] use winit::platform::x11::WindowAttributesExtX11; use winit::window::{ - Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, Icon, ResizeDirection, - Theme, Window, WindowAttributes, WindowId, + Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, Icon, ResizeDirection, Theme, Window, + WindowAttributes, WindowId, }; #[path = "util/tracing.rs"] diff --git a/src/application.rs b/src/application.rs index 320c654e24..91013fb20e 100644 --- a/src/application.rs +++ b/src/application.rs @@ -2,7 +2,7 @@ use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent}; use crate::event_loop::ActiveEventLoop; -#[cfg(any(docsrs, macos_platform))] +#[cfg(macos_platform)] use crate::platform::macos::ApplicationHandlerExtMacOS; use crate::window::WindowId; @@ -349,7 +349,7 @@ pub trait ApplicationHandler { /// The macOS-specific handler. /// /// The return value from this should not change at runtime. - #[cfg(any(docsrs, macos_platform))] + #[cfg(macos_platform)] #[inline(always)] fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> { None @@ -423,7 +423,7 @@ impl ApplicationHandler for &mut A { (**self).memory_warning(event_loop); } - #[cfg(any(docsrs, macos_platform))] + #[cfg(macos_platform)] #[inline] fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> { (**self).macos_handler() @@ -497,7 +497,7 @@ impl ApplicationHandler for Box { (**self).memory_warning(event_loop); } - #[cfg(any(docsrs, macos_platform))] + #[cfg(macos_platform)] #[inline] fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> { (**self).macos_handler() diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 2b04d1a69d..53f7ca15b8 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -158,6 +158,8 @@ changelog entry. - In the same spirit rename `DeviceEvent::MouseMotion` to `PointerMotion`. - Remove `Force::Calibrated::altitude_angle`. - On X11, use bottom-right corner for IME hotspot in `Window::set_ime_cursor_area`. +- `VideoModeHandle` to `VideoMode` which stores plain data. +- `Fullscreen::Exclusive` to take `(MonitorHandle, VideoMode)`. ### Removed diff --git a/src/event_loop.rs b/src/event_loop.rs index 059613459b..2f884cf2de 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -209,10 +209,10 @@ impl EventLoop { /// /// Web applications are recommended to use #[cfg_attr( - any(web_platform, docsrs), + web_platform, doc = " [`EventLoopExtWeb::spawn_app()`][crate::platform::web::EventLoopExtWeb::spawn_app()]" )] - #[cfg_attr(not(any(web_platform, docsrs)), doc = " `EventLoopExtWeb::spawn_app()`")] + #[cfg_attr(not(web_platform), doc = " `EventLoopExtWeb::spawn_app()`")] /// [^1] instead of [`run_app()`] to avoid the need for the Javascript exception trick, and to /// make it clearer that the event loop runs asynchronously (via the browser's own, /// internal, event loop) and doesn't block the current thread of execution like it does @@ -343,10 +343,10 @@ pub trait ActiveEventLoop: AsAny { /// /// **Web:** Only returns the current monitor without #[cfg_attr( - any(web_platform, docsrs), + web_platform, doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." )] - #[cfg_attr(not(any(web_platform, docsrs)), doc = "detailed monitor permissions.")] + #[cfg_attr(not(web_platform), doc = "detailed monitor permissions.")] fn available_monitors(&self) -> Box>; /// Returns the primary monitor of the system. @@ -358,10 +358,10 @@ pub trait ActiveEventLoop: AsAny { /// - **Wayland:** Always returns `None`. /// - **Web:** Always returns `None` without #[cfg_attr( - any(web_platform, docsrs), + web_platform, doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." )] - #[cfg_attr(not(any(web_platform, docsrs)), doc = " detailed monitor permissions.")] + #[cfg_attr(not(web_platform), doc = " detailed monitor permissions.")] fn primary_monitor(&self) -> Option; /// Change if or when [`DeviceEvent`]s are captured. diff --git a/src/monitor.rs b/src/monitor.rs index 898f9b2003..4b48cccf81 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -5,90 +5,14 @@ //! methods, which return an iterator of [`MonitorHandle`]: //! - [`ActiveEventLoop::available_monitors`][crate::event_loop::ActiveEventLoop::available_monitors]. //! - [`Window::available_monitors`][crate::window::Window::available_monitors]. +use std::borrow::Cow; +use std::fmt; use std::num::{NonZeroU16, NonZeroU32}; +use std::ops::Deref; +use std::sync::Arc; use crate::dpi::{PhysicalPosition, PhysicalSize}; -use crate::platform_impl; - -/// Describes a fullscreen video mode of a monitor. -/// -/// Can be acquired with [`MonitorHandle::video_modes`]. -#[derive(Clone, PartialEq, Eq, Hash)] -pub struct VideoModeHandle { - pub(crate) video_mode: platform_impl::VideoModeHandle, -} - -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 { - /// 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() - } - - /// 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() - } - - /// 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() } - } -} - -impl std::fmt::Display for VideoModeHandle { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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(), - ) - } -} +use crate::utils::AsAny; /// Handle to a monitor. /// @@ -98,48 +22,57 @@ impl std::fmt::Display for VideoModeHandle { /// /// **Web:** A [`MonitorHandle`] created without #[cfg_attr( - any(web_platform, docsrs), + web_platform, doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." )] -#[cfg_attr(not(any(web_platform, docsrs)), doc = "detailed monitor permissions.")] +#[cfg_attr(not(web_platform), doc = "detailed monitor permissions.")] /// will always represent the current monitor the browser window is in instead of a specific /// monitor. See #[cfg_attr( - any(web_platform, docsrs), + web_platform, doc = "[`MonitorHandleExtWeb::is_detailed()`][crate::platform::web::MonitorHandleExtWeb::is_detailed]" )] -#[cfg_attr(not(any(web_platform, docsrs)), doc = "`MonitorHandleExtWeb::is_detailed()`")] +#[cfg_attr(not(web_platform), doc = "`MonitorHandleExtWeb::is_detailed()`")] /// to check. /// /// [`Window`]: crate::window::Window -#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct MonitorHandle { - pub(crate) inner: platform_impl::MonitorHandle, +#[derive(Debug, Clone)] +pub struct MonitorHandle(pub(crate) Arc); + +impl Deref for MonitorHandle { + type Target = dyn MonitorHandleProvider; + + fn deref(&self) -> &Self::Target { + self.0.as_ref() + } } -impl std::fmt::Debug for MonitorHandle { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.inner.fmt(f) +impl PartialEq for MonitorHandle { + fn eq(&self, other: &Self) -> bool { + self.0.as_ref().eq(other.0.as_ref()) } } -impl MonitorHandle { +impl Eq for MonitorHandle {} + +/// Provider of the [`MonitorHandle`]. +pub trait MonitorHandleProvider: AsAny + fmt::Debug + Send + Sync { + /// Native platform identifier of this monitor. + fn native_id(&self) -> u64; + /// Returns a human-readable name of the monitor. /// - /// Returns `None` if the monitor doesn't exist anymore. + /// Returns `None` if the monitor doesn't exist anymore or the name couldn't be obtained. /// /// ## Platform-specific /// /// **Web:** Always returns [`None`] without #[cfg_attr( - any(web_platform, docsrs), + web_platform, doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." )] - #[cfg_attr(not(any(web_platform, docsrs)), doc = "detailed monitor permissions.")] - #[inline] - pub fn name(&self) -> Option { - self.inner.name() - } + #[cfg_attr(not(web_platform), doc = "detailed monitor permissions.")] + fn name(&self) -> Option>; /// Returns the top-left corner position of the monitor relative to the larger full /// screen area. @@ -148,14 +81,11 @@ impl MonitorHandle { /// /// **Web:** Always returns [`None`] without #[cfg_attr( - any(web_platform, docsrs), + web_platform, doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." )] - #[cfg_attr(not(any(web_platform, docsrs)), doc = "detailed monitor permissions.")] - #[inline] - pub fn position(&self) -> Option> { - self.inner.position() - } + #[cfg_attr(not(web_platform), doc = "detailed monitor permissions.")] + fn position(&self) -> Option>; /// Returns the scale factor of the underlying monitor. To map logical pixels to physical /// pixels and vice versa, use [`Window::scale_factor`]. @@ -169,27 +99,80 @@ impl MonitorHandle { /// - **Android:** Always returns 1.0. /// - **Web:** Always returns `0.0` without #[cfg_attr( - any(web_platform, docsrs), + web_platform, doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." )] - #[cfg_attr(not(any(web_platform, docsrs)), doc = " detailed monitor permissions.")] + #[cfg_attr(not(web_platform), doc = " detailed monitor permissions.")] /// #[rustfmt::skip] /// [`Window::scale_factor`]: crate::window::Window::scale_factor - #[inline] - pub fn scale_factor(&self) -> f64 { - self.inner.scale_factor() - } + fn scale_factor(&self) -> f64; /// 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 }) - } + fn current_video_mode(&self) -> Option; /// 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 }) + fn video_modes(&self) -> Box>; +} + +impl PartialEq for dyn MonitorHandleProvider + '_ { + fn eq(&self, other: &Self) -> bool { + self.native_id() == other.native_id() + } +} + +impl Eq for dyn MonitorHandleProvider + '_ {} + +/// Describes a fullscreen video mode of a monitor. +/// +/// Can be acquired with [`MonitorHandleProvider::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 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 + pub fn size(&self) -> PhysicalSize { + 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. + pub fn bit_depth(&self) -> Option { + self.bit_depth + } + + /// Returns the refresh rate of this video mode in mHz. + pub fn refresh_rate_millihertz(&self) -> Option { + self.refresh_rate_millihertz } } + +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(), + ) + } +} + +/// 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), +} diff --git a/src/platform/ios.rs b/src/platform/ios.rs index 9b55b0c4b2..b83c9974ff 100644 --- a/src/platform/ios.rs +++ b/src/platform/ios.rs @@ -107,7 +107,8 @@ 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::platform_impl::MonitorHandle as IosMonitorHandle; use crate::window::{Window, WindowAttributes}; /// Additional methods on [`Window`] that are specific to iOS. @@ -115,10 +116,11 @@ pub trait WindowExtIOS { /// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`. /// /// The default value is device dependent, and it's recommended GLES or Metal applications set - /// this to [`MonitorHandle::scale_factor()`]. + /// this to [`MonitorHandleProvider::scale_factor()`]. /// /// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc /// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc + /// [`MonitorHandleProvider::scale_factor()`]: crate::monitor::MonitorHandleProvider::scale_factor() fn set_scale_factor(&self, scale_factor: f64); /// Sets the valid orientations for the [`Window`]. @@ -289,10 +291,11 @@ pub trait WindowAttributesExtIOS { /// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`. /// /// The default value is device dependent, and it's recommended GLES or Metal applications set - /// this to [`MonitorHandle::scale_factor()`]. + /// this to [`MonitorHandleProvider::scale_factor()`]. /// /// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc /// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc + /// [`MonitorHandleProvider::scale_factor()`]: crate::monitor::MonitorHandleProvider::scale_factor() fn with_scale_factor(self, scale_factor: f64) -> Self; /// Sets the valid orientations for the [`Window`]. @@ -384,10 +387,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 { @@ -395,12 +398,14 @@ impl MonitorHandleExtIOS for MonitorHandle { fn ui_screen(&self) -> *mut c_void { // SAFETY: The marker is only used to get the pointer of the screen let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() }; - objc2::rc::Retained::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void + let monitor = self.as_any().downcast_ref::().unwrap(); + objc2::rc::Retained::as_ptr(monitor.ui_screen(mtm)) as *mut c_void } #[inline] - fn preferred_video_mode(&self) -> VideoModeHandle { - VideoModeHandle { video_mode: self.inner.preferred_video_mode() } + fn preferred_video_mode(&self) -> VideoMode { + let monitor = self.as_any().downcast_ref::().unwrap(); + monitor.preferred_video_mode() } } diff --git a/src/platform/macos.rs b/src/platform/macos.rs index beeee0148d..94eddb3e21 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -78,6 +78,7 @@ use serde::{Deserialize, Serialize}; use crate::application::ApplicationHandler; use crate::event_loop::{ActiveEventLoop, EventLoopBuilder}; use crate::monitor::MonitorHandle; +use crate::platform_impl::MonitorHandle as MacOsMonitorHandle; use crate::window::{Window, WindowAttributes, WindowId}; /// Additional methods on [`Window`] that are specific to MacOS. @@ -488,22 +489,16 @@ impl EventLoopBuilderExtMacOS for EventLoopBuilder { /// Additional methods on [`MonitorHandle`] that are specific to MacOS. pub trait MonitorHandleExtMacOS { - /// Returns the identifier of the monitor for Cocoa. - fn native_id(&self) -> u32; /// Returns a pointer to the NSScreen representing this monitor. fn ns_screen(&self) -> Option<*mut c_void>; } impl MonitorHandleExtMacOS for MonitorHandle { - #[inline] - fn native_id(&self) -> u32 { - self.inner.native_identifier() - } - fn ns_screen(&self) -> Option<*mut c_void> { + let monitor = self.as_any().downcast_ref::().unwrap(); // SAFETY: We only use the marker to get a pointer let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() }; - self.inner.ns_screen(mtm).map(|s| objc2::rc::Retained::as_ptr(&s) as _) + monitor.ns_screen(mtm).map(|s| objc2::rc::Retained::as_ptr(&s) as _) } } diff --git a/src/platform/mod.rs b/src/platform/mod.rs index c6b0e1b45c..7ab821f1d8 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -2,23 +2,23 @@ //! //! Only the modules corresponding to the platform you're compiling to will be available. -#[cfg(any(android_platform, docsrs))] +#[cfg(android_platform)] pub mod android; -#[cfg(any(ios_platform, docsrs))] +#[cfg(ios_platform)] pub mod ios; -#[cfg(any(macos_platform, docsrs))] +#[cfg(macos_platform)] pub mod macos; -#[cfg(any(orbital_platform, docsrs))] +#[cfg(orbital_platform)] pub mod orbital; -#[cfg(any(x11_platform, wayland_platform, docsrs))] +#[cfg(any(x11_platform, wayland_platform))] pub mod startup_notify; -#[cfg(any(wayland_platform, docsrs))] +#[cfg(wayland_platform)] pub mod wayland; -#[cfg(any(web_platform, docsrs))] +#[cfg(web_platform)] pub mod web; -#[cfg(any(windows_platform, docsrs))] +#[cfg(windows_platform)] pub mod windows; -#[cfg(any(x11_platform, docsrs))] +#[cfg(x11_platform)] pub mod x11; #[allow(unused_imports)] diff --git a/src/platform/run_on_demand.rs b/src/platform/run_on_demand.rs index 851e3450fc..e24aa7d808 100644 --- a/src/platform/run_on_demand.rs +++ b/src/platform/run_on_demand.rs @@ -33,10 +33,10 @@ pub trait EventLoopExtRunOnDemand { /// to the caller (specifically this is impossible on iOS and Web - though with the Web /// backend it is possible to use #[cfg_attr( - any(web_platform, docsrs), + web_platform, doc = " [`EventLoopExtWeb::spawn_app()`][crate::platform::web::EventLoopExtWeb::spawn_app()]" )] - #[cfg_attr(not(any(web_platform, docsrs)), doc = " `EventLoopExtWeb::spawn_app()`")] + #[cfg_attr(not(web_platform), doc = " `EventLoopExtWeb::spawn_app()`")] /// [^1] more than once instead). /// - No [`Window`] state can be carried between separate runs of the event loop. /// diff --git a/src/platform/wayland.rs b/src/platform/wayland.rs index 84af00388b..bcdcccbc46 100644 --- a/src/platform/wayland.rs +++ b/src/platform/wayland.rs @@ -14,7 +14,6 @@ //! * `wayland-csd-adwaita-crossfont`. //! * `wayland-csd-adwaita-notitle`. use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder}; -use crate::monitor::MonitorHandle; pub use crate::window::Theme; use crate::window::{Window as CoreWindow, WindowAttributes}; @@ -97,16 +96,3 @@ impl WindowAttributesExtWayland for WindowAttributes { self } } - -/// Additional methods on `MonitorHandle` that are specific to Wayland. -pub trait MonitorHandleExtWayland { - /// Returns the inner identifier of the monitor. - fn native_id(&self) -> u32; -} - -impl MonitorHandleExtWayland for MonitorHandle { - #[inline] - fn native_id(&self) -> u32 { - self.inner.native_identifier() - } -} diff --git a/src/platform/web.rs b/src/platform/web.rs index 28227662d8..59b7b30b2e 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -58,8 +58,7 @@ use crate::application::ApplicationHandler; use crate::cursor::CustomCursorSource; use crate::error::NotSupportedError; use crate::event_loop::{ActiveEventLoop, EventLoop}; -use crate::monitor::MonitorHandle; -use crate::platform_impl::PlatformCustomCursorSource; +use crate::monitor::MonitorHandleProvider; #[cfg(web_platform)] use crate::platform_impl::{ CustomCursorFuture as PlatformCustomCursorFuture, @@ -67,6 +66,7 @@ use crate::platform_impl::{ MonitorPermissionFuture as PlatformMonitorPermissionFuture, OrientationLockFuture as PlatformOrientationLockFuture, }; +use crate::platform_impl::{MonitorHandle as WebMonitorHandle, PlatformCustomCursorSource}; use crate::window::{CustomCursor, Window, WindowAttributes}; #[cfg(not(web_platform))] @@ -253,12 +253,18 @@ pub trait EventLoopExtWeb { /// /// [`MonitorHandle`]s don't automatically make use of this after permission is granted. New /// [`MonitorHandle`]s have to be created instead. + /// + /// [`MonitorHandle`]: crate::monitor::MonitorHandle fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture; /// Returns whether the user has given permission to access detailed monitor information. /// /// [`MonitorHandle`]s don't automatically make use of detailed monitor information after /// permission is granted. New [`MonitorHandle`]s have to be created instead. + /// + /// [`MonitorHandle`]: crate::monitor::MonitorHandle + /// + /// [`MonitorHandle`]: crate::monitor::MonitorHandle fn has_detailed_monitor_permission(&self) -> HasMonitorPermissionFuture; } @@ -348,12 +354,16 @@ pub trait ActiveEventLoopExtWeb { /// /// [`MonitorHandle`]s don't automatically make use of this after permission is granted. New /// [`MonitorHandle`]s have to be created instead. + /// + /// [`MonitorHandle`]: crate::monitor::MonitorHandle fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture; /// Returns whether the user has given permission to access detailed monitor information. /// /// [`MonitorHandle`]s don't automatically make use of detailed monitor information after /// permission is granted. New [`MonitorHandle`]s have to be created instead. + /// + /// [`MonitorHandle`]: crate::monitor::MonitorHandle fn has_detailed_monitor_permission(&self) -> bool; } @@ -650,6 +660,8 @@ impl Future for HasMonitorPermissionFuture { } /// Additional methods on [`MonitorHandle`] that are specific to the Web. +/// +/// [`MonitorHandle`]: crate::monitor::MonitorHandle pub trait MonitorHandleExtWeb { /// Returns whether the screen is internal to the device or external. /// @@ -677,28 +689,35 @@ pub trait MonitorHandleExtWeb { /// specific monitor. /// /// See [`ActiveEventLoopExtWeb::request_detailed_monitor_permission()`]. + /// + /// [`MonitorHandle`]: crate::monitor::MonitorHandle fn is_detailed(&self) -> bool; } -impl MonitorHandleExtWeb for MonitorHandle { +impl MonitorHandleExtWeb for dyn MonitorHandleProvider + '_ { fn is_internal(&self) -> Option { - self.inner.is_internal() + self.as_any().downcast_ref::().unwrap().is_internal() } fn orientation(&self) -> OrientationData { - self.inner.orientation() + self.as_any().downcast_ref::().unwrap().orientation() } fn request_lock(&self, orientation_lock: OrientationLock) -> OrientationLockFuture { - OrientationLockFuture(self.inner.request_lock(orientation_lock)) + let future = self + .as_any() + .downcast_ref::() + .unwrap() + .request_lock(orientation_lock); + OrientationLockFuture(future) } fn unlock(&self) -> Result<(), OrientationLockError> { - self.inner.unlock() + self.as_any().downcast_ref::().unwrap().unlock() } fn is_detailed(&self) -> bool { - self.inner.is_detailed() + self.as_any().downcast_ref::().unwrap().is_detailed() } } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index c0ab94f47d..6a01ca8e56 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -15,7 +15,6 @@ use windows_sys::Win32::Foundation::HANDLE; use crate::dpi::PhysicalSize; use crate::event::DeviceId; use crate::event_loop::EventLoopBuilder; -use crate::monitor::MonitorHandle; use crate::window::{BadIcon, Icon, Window, WindowAttributes}; /// Window Handle type used by Win32 API @@ -618,27 +617,6 @@ impl WindowAttributesExtWindows for WindowAttributes { } } -/// Additional methods on `MonitorHandle` that are specific to Windows. -pub trait MonitorHandleExtWindows { - /// Returns the name of the monitor adapter specific to the Win32 API. - fn native_id(&self) -> String; - - /// Returns the handle of the monitor - `HMONITOR`. - fn hmonitor(&self) -> HMONITOR; -} - -impl MonitorHandleExtWindows for MonitorHandle { - #[inline] - fn native_id(&self) -> String { - self.inner.native_identifier() - } - - #[inline] - fn hmonitor(&self) -> HMONITOR { - self.inner.hmonitor() - } -} - /// Additional methods on `DeviceId` that are specific to Windows. pub trait DeviceIdExtWindows { /// Returns an identifier that persistently refers to this specific device. diff --git a/src/platform/x11.rs b/src/platform/x11.rs index 2c4cef7576..9e70a298af 100644 --- a/src/platform/x11.rs +++ b/src/platform/x11.rs @@ -4,7 +4,6 @@ use serde::{Deserialize, Serialize}; use crate::dpi::Size; use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder}; -use crate::monitor::MonitorHandle; use crate::window::{Window as CoreWindow, WindowAttributes}; /// X window type. Maps directly to @@ -240,16 +239,3 @@ impl WindowAttributesExtX11 for WindowAttributes { self } } - -/// Additional methods on `MonitorHandle` that are specific to X11. -pub trait MonitorHandleExtX11 { - /// Returns the inner identifier of the monitor. - fn native_id(&self) -> u32; -} - -impl MonitorHandleExtX11 for MonitorHandle { - #[inline] - fn native_id(&self) -> u32 { - self.inner.native_identifier() - } -} diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 713c7cb700..0a6abf97f3 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,12 +20,11 @@ use crate::event_loop::{ EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider, OwnedDisplayHandle as CoreOwnedDisplayHandle, }; -use crate::monitor::MonitorHandle as RootMonitorHandle; +use crate::monitor::{Fullscreen, MonitorHandle as RootMonitorHandle}; use crate::platform::pump_events::PumpStatus; use crate::window::{ - self, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, ImePurpose, - ResizeDirection, Theme, Window as CoreWindow, WindowAttributes, WindowButtons, WindowId, - WindowLevel, + self, CursorGrabMode, CustomCursor, CustomCursorSource, ImePurpose, ResizeDirection, Theme, + Window as CoreWindow, WindowAttributes, WindowButtons, WindowId, WindowLevel, }; mod keycodes; @@ -1001,52 +999,6 @@ impl Display for OsError { } } -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct MonitorHandle; - -impl MonitorHandle { - pub fn name(&self) -> Option { - unreachable!() - } - - pub fn position(&self) -> Option> { - unreachable!() - } - - pub fn scale_factor(&self) -> f64 { - unreachable!() - } - - 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 { - unreachable!() - } -} - fn screen_size(app: &AndroidApp) -> PhysicalSize { if let Some(native_window) = app.native_window() { PhysicalSize::new(native_window.width() as _, native_window.height() as _) diff --git a/src/platform_impl/apple/appkit/event_loop.rs b/src/platform_impl/apple/appkit/event_loop.rs index 4e8648d0f8..fe85141374 100644 --- a/src/platform_impl/apple/appkit/event_loop.rs +++ b/src/platform_impl/apple/appkit/event_loop.rs @@ -36,7 +36,7 @@ use crate::event_loop::{ EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider, OwnedDisplayHandle as CoreOwnedDisplayHandle, }; -use crate::monitor::MonitorHandle as RootMonitorHandle; +use crate::monitor::MonitorHandle as CoreMonitorHandle; use crate::platform::macos::ActivationPolicy; use crate::platform::pump_events::PumpStatus; use crate::platform_impl::Window; @@ -115,13 +115,17 @@ impl RootActiveEventLoop for ActiveEventLoop { Ok(RootCustomCursor { inner: CustomCursor::new(source.inner)? }) } - fn available_monitors(&self) -> Box> { - Box::new(monitor::available_monitors().into_iter().map(|inner| RootMonitorHandle { inner })) + fn available_monitors(&self) -> Box> { + Box::new( + monitor::available_monitors() + .into_iter() + .map(|monitor| CoreMonitorHandle(Arc::new(monitor))), + ) } fn primary_monitor(&self) -> Option { let monitor = monitor::primary_monitor(); - Some(RootMonitorHandle { inner: monitor }) + Some(CoreMonitorHandle(Arc::new(monitor))) } fn listen_device_events(&self, _allowed: DeviceEvents) {} diff --git a/src/platform_impl/apple/appkit/mod.rs b/src/platform_impl/apple/appkit/mod.rs index 4aacb90103..4987f6d968 100644 --- a/src/platform_impl/apple/appkit/mod.rs +++ b/src/platform_impl/apple/appkit/mod.rs @@ -19,9 +19,8 @@ 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; pub(crate) use crate::icon::NoIcon as PlatformIcon; -pub(crate) use crate::platform_impl::Fullscreen; diff --git a/src/platform_impl/apple/appkit/monitor.rs b/src/platform_impl/apple/appkit/monitor.rs index 8917c36a07..91b720c8bb 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::{MonitorHandleProvider, 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.mode == other.mode && self.monitor == other.monitor } } @@ -40,9 +36,7 @@ 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.mode.hash(state); self.monitor.hash(state); } } @@ -50,9 +44,7 @@ impl std::hash::Hash for VideoModeHandle { 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("mode", &self.mode) .field("monitor", &self.monitor) .finish() } @@ -83,13 +75,15 @@ 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,38 +94,125 @@ 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, - } + }; + + VideoModeHandle { mode, monitor: monitor.clone(), native_mode } } } +} - pub fn size(&self) -> PhysicalSize { - self.size +#[derive(Clone)] +pub struct MonitorHandle(CGDirectDisplayID); + +impl MonitorHandle { + pub fn new(id: CGDirectDisplayID) -> Self { + MonitorHandle(id) } - pub fn bit_depth(&self) -> Option { - self.bit_depth + fn refresh_rate_millihertz(&self) -> Option { + let current_display_mode = + NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _); + refresh_rate_millihertz(self.0, ¤t_display_mode) } - pub fn refresh_rate_millihertz(&self) -> Option { - self.refresh_rate_millihertz + pub fn video_mode_handles(&self) -> impl Iterator { + let refresh_rate_millihertz = self.refresh_rate_millihertz(); + let monitor = self.clone(); + + unsafe { + let modes = { + let array = ffi::CGDisplayCopyAllDisplayModes(self.0, std::ptr::null()); + assert!(!array.is_null(), "failed to get list of display modes"); + let array_count = CFArrayGetCount(array); + let modes: Vec<_> = (0..array_count) + .map(move |i| { + let mode = CFArrayGetValueAtIndex(array, i) as *mut _; + ffi::CGDisplayModeRetain(mode); + mode + }) + .collect(); + CFRelease(array as *const _); + modes + }; + + modes.into_iter().map(move |mode| { + let cg_refresh_rate_hertz = ffi::CGDisplayModeGetRefreshRate(mode).round() as i64; + + // CGDisplayModeGetRefreshRate returns 0.0 for any display that + // isn't a CRT + let refresh_rate_millihertz = if cg_refresh_rate_hertz > 0 { + NonZeroU32::new((cg_refresh_rate_hertz * 1000) as u32) + } else { + refresh_rate_millihertz + }; + + VideoModeHandle::new( + monitor.clone(), + NativeDisplayMode(mode), + refresh_rate_millihertz, + ) + }) + } } - pub fn monitor(&self) -> MonitorHandle { - self.monitor.clone() + pub(crate) fn ns_screen(&self, mtm: MainThreadMarker) -> Option> { + let uuid = unsafe { ffi::CGDisplayCreateUUIDFromDisplayID(self.0) }; + NSScreen::screens(mtm).into_iter().find(|screen| { + let other_native_id = get_display_id(screen); + let other_uuid = unsafe { + ffi::CGDisplayCreateUUIDFromDisplayID(other_native_id as CGDirectDisplayID) + }; + uuid == other_uuid + }) } } -#[derive(Clone)] -pub struct MonitorHandle(CGDirectDisplayID); +impl MonitorHandleProvider for MonitorHandle { + fn native_id(&self) -> u64 { + self.0 as _ + } + + fn name(&self) -> Option> { + let MonitorHandle(display_id) = *self; + let screen_num = CGDisplay::new(display_id).model_number(); + Some(format!("Monitor #{screen_num}").into()) + } + + fn position(&self) -> Option> { + // This is already in screen coordinates. If we were using `NSScreen`, + // then a conversion would've been needed: + // flip_window_screen_coordinates(self.ns_screen(mtm)?.frame()) + let bounds = unsafe { CGDisplayBounds(self.0) }; + let position = LogicalPosition::new(bounds.origin.x, bounds.origin.y); + Some(position.to_physical(self.scale_factor())) + } + + fn scale_factor(&self) -> f64 { + run_on_main(|mtm| { + match self.ns_screen(mtm) { + Some(screen) => screen.backingScaleFactor() as f64, + None => 1.0, // default to 1.0 when we can't find the screen + } + }) + } + + fn current_video_mode(&self) -> Option { + let native_ode = NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _); + let refresh_rate_millihertz = refresh_rate_millihertz(self.0, &native_ode); + Some(VideoModeHandle::new(self.clone(), native_ode, refresh_rate_millihertz).mode) + } + + fn video_modes(&self) -> Box> { + Box::new(self.video_mode_handles().map(|mode| mode.mode)) + } +} // `CGDirectDisplayID` changes on video mode change, so we cannot rely on that // for comparisons, but we can use `CGDisplayCreateUUIDFromDisplayID` to get an @@ -190,114 +271,13 @@ impl fmt::Debug for MonitorHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("MonitorHandle") .field("name", &self.name()) - .field("native_identifier", &self.native_identifier()) + .field("native_id", &self.native_id()) .field("position", &self.position()) .field("scale_factor", &self.scale_factor()) .finish_non_exhaustive() } } -impl MonitorHandle { - pub fn new(id: CGDirectDisplayID) -> Self { - MonitorHandle(id) - } - - // TODO: Be smarter about this: - // - pub fn name(&self) -> Option { - let MonitorHandle(display_id) = *self; - let screen_num = CGDisplay::new(display_id).model_number(); - Some(format!("Monitor #{screen_num}")) - } - - #[inline] - pub fn native_identifier(&self) -> u32 { - self.0 - } - - #[inline] - pub fn position(&self) -> Option> { - // This is already in screen coordinates. If we were using `NSScreen`, - // then a conversion would've been needed: - // flip_window_screen_coordinates(self.ns_screen(mtm)?.frame()) - let bounds = unsafe { CGDisplayBounds(self.native_identifier()) }; - let position = LogicalPosition::new(bounds.origin.x, bounds.origin.y); - Some(position.to_physical(self.scale_factor())) - } - - pub fn scale_factor(&self) -> f64 { - run_on_main(|mtm| { - match self.ns_screen(mtm) { - Some(screen) => screen.backingScaleFactor() as f64, - None => 1.0, // default to 1.0 when we can't find the screen - } - }) - } - - fn refresh_rate_millihertz(&self) -> Option { - let current_display_mode = - NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _); - refresh_rate_millihertz(self.0, ¤t_display_mode) - } - - 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)) - } - - pub fn video_modes(&self) -> impl Iterator { - let refresh_rate_millihertz = self.refresh_rate_millihertz(); - let monitor = self.clone(); - - unsafe { - let modes = { - let array = ffi::CGDisplayCopyAllDisplayModes(self.0, std::ptr::null()); - assert!(!array.is_null(), "failed to get list of display modes"); - let array_count = CFArrayGetCount(array); - let modes: Vec<_> = (0..array_count) - .map(move |i| { - let mode = CFArrayGetValueAtIndex(array, i) as *mut _; - ffi::CGDisplayModeRetain(mode); - mode - }) - .collect(); - CFRelease(array as *const _); - modes - }; - - modes.into_iter().map(move |mode| { - let cg_refresh_rate_hertz = ffi::CGDisplayModeGetRefreshRate(mode).round() as i64; - - // CGDisplayModeGetRefreshRate returns 0.0 for any display that - // isn't a CRT - let refresh_rate_millihertz = if cg_refresh_rate_hertz > 0 { - NonZeroU32::new((cg_refresh_rate_hertz * 1000) as u32) - } else { - refresh_rate_millihertz - }; - - VideoModeHandle::new( - monitor.clone(), - NativeDisplayMode(mode), - refresh_rate_millihertz, - ) - }) - } - } - - pub(crate) fn ns_screen(&self, mtm: MainThreadMarker) -> Option> { - let uuid = unsafe { ffi::CGDisplayCreateUUIDFromDisplayID(self.0) }; - NSScreen::screens(mtm).into_iter().find(|screen| { - let other_native_id = get_display_id(screen); - let other_uuid = unsafe { - ffi::CGDisplayCreateUUIDFromDisplayID(other_native_id as CGDirectDisplayID) - }; - uuid == other_uuid - }) - } -} - pub(crate) fn get_display_id(screen: &NSScreen) -> u32 { let key = ns_string!("NSScreenNumber"); diff --git a/src/platform_impl/apple/appkit/window.rs b/src/platform_impl/apple/appkit/window.rs index e70f5934b8..ec378ffe19 100644 --- a/src/platform_impl/apple/appkit/window.rs +++ b/src/platform_impl/apple/appkit/window.rs @@ -1,5 +1,7 @@ #![allow(clippy::unnecessary_cast)] +use std::sync::Arc; + use dpi::{Position, Size}; use objc2::rc::{autoreleasepool, Retained}; use objc2::{declare_class, mutability, ClassType, DeclaredClass}; @@ -9,10 +11,10 @@ use objc2_foundation::{MainThreadBound, MainThreadMarker, NSObject}; use super::event_loop::ActiveEventLoop; use super::window_delegate::WindowDelegate; use crate::error::RequestError; -use crate::monitor::MonitorHandle as CoreMonitorHandle; +use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle}; use crate::window::{ - Cursor, Fullscreen, Icon, ImePurpose, Theme, UserAttentionType, Window as CoreWindow, - WindowAttributes, WindowButtons, WindowId, WindowLevel, + Cursor, Icon, ImePurpose, Theme, UserAttentionType, Window as CoreWindow, WindowAttributes, + WindowButtons, WindowId, WindowLevel, }; pub(crate) struct Window { @@ -64,7 +66,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); } @@ -301,21 +303,24 @@ impl CoreWindow for Window { fn current_monitor(&self) -> Option { self.maybe_wait_on_main(|delegate| { - delegate.current_monitor().map(|inner| CoreMonitorHandle { inner }) + delegate.current_monitor().map(|monitor| CoreMonitorHandle(Arc::new(monitor))) }) } fn available_monitors(&self) -> Box> { self.maybe_wait_on_main(|delegate| { Box::new( - delegate.available_monitors().into_iter().map(|inner| CoreMonitorHandle { inner }), + delegate + .available_monitors() + .into_iter() + .map(|monitor| CoreMonitorHandle(Arc::new(monitor))), ) }) } fn primary_monitor(&self) -> Option { self.maybe_wait_on_main(|delegate| { - delegate.primary_monitor().map(|inner| CoreMonitorHandle { inner }) + delegate.primary_monitor().map(|monitor| CoreMonitorHandle(Arc::new(monitor))) }) } diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index ddbc371bda..0cb579311b 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, CGPoint}; -use monitor::VideoModeHandle; use objc2::rc::{autoreleasepool, Retained}; use objc2::runtime::{AnyObject, ProtocolObject}; use objc2::{declare_class, msg_send_id, mutability, sel, ClassType, DeclaredClass}; @@ -34,10 +33,11 @@ use super::monitor::{self, flip_window_screen_coordinates, get_display_id}; use super::observer::RunLoop; use super::view::WinitView; use super::window::WinitWindow; -use super::{ffi, Fullscreen, MonitorHandle}; +use super::{ffi, MonitorHandle}; use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::{NotSupportedError, RequestError}; use crate::event::{SurfaceSizeWriter, WindowEvent}; +use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider}; use crate::platform::macos::{OptionAsAlt, WindowExtMacOS}; use crate::window::{ Cursor, CursorGrabMode, Icon, ImePurpose, ResizeDirection, Theme, UserAttentionType, @@ -242,7 +242,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 @@ -250,7 +250,7 @@ declare_class!( // Otherwise, we must've reached fullscreen by the user clicking // on the green fullscreen button. Update state! None => { - let current_monitor = self.current_monitor_inner(); + let current_monitor = self.current_monitor_inner().map(|monitor| CoreMonitorHandle(Arc::new(monitor))); *fullscreen = Some(Fullscreen::Borderless(current_monitor)); }, } @@ -282,7 +282,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; @@ -496,7 +496,8 @@ 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, _)) => { + let monitor = monitor.as_any().downcast_ref::().unwrap(); monitor.ns_screen(mtm).or_else(|| NSScreen::mainScreen(mtm)) }, Some(Fullscreen::Borderless(None)) => NSScreen::mainScreen(mtm), @@ -1334,17 +1335,18 @@ impl WindowDelegate { // does not take a screen parameter, but uses the current screen) if let Some(ref fullscreen) = fullscreen { let new_screen = match fullscreen { - Fullscreen::Borderless(Some(monitor)) => monitor.clone(), + Fullscreen::Borderless(Some(monitor)) | Fullscreen::Exclusive(monitor, _) => { + let monitor = monitor.as_any().downcast_ref::().unwrap(); + monitor.ns_screen(mtm) + }, Fullscreen::Borderless(None) => { if let Some(monitor) = self.current_monitor_inner() { - monitor + monitor.ns_screen(mtm) } else { return; } }, - Fullscreen::Exclusive(video_mode) => video_mode.monitor(), } - .ns_screen(mtm) .unwrap(); let old_screen = self.window().screen().unwrap(); @@ -1353,7 +1355,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 @@ -1366,7 +1368,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_id() as _; let mut fade_token = ffi::kCGDisplayFadeReservationInvalidToken; @@ -1396,6 +1398,13 @@ impl WindowDelegate { } unsafe { + let monitor = monitor.as_any().downcast_ref::().unwrap(); + let video_mode = + match monitor.video_mode_handles().find(|mode| &mode.mode == video_mode) { + Some(video_mode) => video_mode, + None => return, + }; + let result = ffi::CGDisplaySetDisplayMode( display_id, video_mode.native_mode.0, @@ -1459,11 +1468,12 @@ 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(monitor, _)), None) => { + let monitor = monitor.as_any().downcast_ref::().unwrap(); + 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 @@ -1483,7 +1493,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(monitor, _)), Some(Fullscreen::Borderless(_))) => { let presentation_options = self.ivars().save_presentation_opts.get().unwrap_or( NSApplicationPresentationOptions::NSApplicationPresentationFullScreen | NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock @@ -1491,7 +1501,8 @@ impl WindowDelegate { ); app.setPresentationOptions(presentation_options); - restore_and_release_display(&video_mode.monitor()); + let monitor = monitor.as_any().downcast_ref::().unwrap(); + restore_and_release_display(monitor); // Restore the normal window level following the Borderless fullscreen // `CGShieldingWindowLevel() + 1` hack. @@ -1695,11 +1706,11 @@ fn restore_and_release_display(monitor: &MonitorHandle) { if available_monitors.contains(monitor) { unsafe { ffi::CGRestorePermanentDisplayConfiguration(); - assert_eq!(ffi::CGDisplayRelease(monitor.native_identifier()), ffi::kCGErrorSuccess); + assert_eq!(ffi::CGDisplayRelease(monitor.native_id() as _), ffi::kCGErrorSuccess); }; } else { warn!( - monitor = monitor.name(), + monitor = monitor.name().map(|name| name.to_string()), "Tried to restore exclusive fullscreen on a monitor that is no longer available" ); } diff --git a/src/platform_impl/apple/uikit/event_loop.rs b/src/platform_impl/apple/uikit/event_loop.rs index 4f93a61d90..6fbc98a8f5 100644 --- a/src/platform_impl/apple/uikit/event_loop.rs +++ b/src/platform_impl/apple/uikit/event_loop.rs @@ -33,7 +33,7 @@ use crate::event_loop::{ EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider, OwnedDisplayHandle as CoreOwnedDisplayHandle, }; -use crate::monitor::MonitorHandle as RootMonitorHandle; +use crate::monitor::MonitorHandle as CoreMonitorHandle; use crate::platform_impl::Window; use crate::window::{CustomCursor, CustomCursorSource, Theme, Window as CoreWindow}; @@ -61,14 +61,18 @@ impl RootActiveEventLoop for ActiveEventLoop { Err(NotSupportedError::new("create_custom_cursor is not supported").into()) } - fn available_monitors(&self) -> Box> { - Box::new(monitor::uiscreens(self.mtm).into_iter().map(|inner| RootMonitorHandle { inner })) + fn available_monitors(&self) -> Box> { + Box::new( + monitor::uiscreens(self.mtm) + .into_iter() + .map(|monitor| CoreMonitorHandle(Arc::new(monitor))), + ) } fn primary_monitor(&self) -> Option { #[allow(deprecated)] let monitor = MonitorHandle::new(UIScreen::mainScreen(self.mtm)); - Some(RootMonitorHandle { inner: monitor }) + Some(CoreMonitorHandle(Arc::new(monitor))) } fn listen_device_events(&self, _allowed: DeviceEvents) {} diff --git a/src/platform_impl/apple/uikit/mod.rs b/src/platform_impl/apple/uikit/mod.rs index d5560c7e73..91d8968b93 100644 --- a/src/platform_impl/apple/uikit/mod.rs +++ b/src/platform_impl/apple/uikit/mod.rs @@ -12,13 +12,12 @@ 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, }; pub(crate) use crate::icon::NoIcon as PlatformIcon; -pub(crate) use crate::platform_impl::Fullscreen; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct KeyEventExtra {} diff --git a/src/platform_impl/apple/uikit/monitor.rs b/src/platform_impl/apple/uikit/monitor.rs index b87d5a26ce..b218f0cfdc 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::{MonitorHandleProvider, 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) } @@ -91,124 +77,132 @@ pub struct MonitorHandle { ui_screen: MainThreadBound>, } -impl Clone for MonitorHandle { - fn clone(&self) -> Self { - run_on_main(|mtm| Self { - ui_screen: MainThreadBound::new(self.ui_screen.get(mtm).clone(), mtm), - }) - } -} - -impl hash::Hash for MonitorHandle { - fn hash(&self, state: &mut H) { - (self as *const Self).hash(state); - } -} - -impl PartialEq for MonitorHandle { - fn eq(&self, other: &Self) -> bool { - ptr::eq(self, other) +impl MonitorHandle { + pub(crate) fn new(ui_screen: Retained) -> Self { + // Holding `Retained` implies we're on the main thread. + let mtm = MainThreadMarker::new().unwrap(); + Self { ui_screen: MainThreadBound::new(ui_screen, mtm) } } -} -impl Eq for MonitorHandle {} + pub fn video_mode_handless(&self) -> impl Iterator { + run_on_main(|mtm| { + let ui_screen = self.ui_screen(mtm); -impl PartialOrd for MonitorHandle { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) + ui_screen + .availableModes() + .into_iter() + .map(|mode| VideoModeHandle::new(ui_screen.clone(), mode, mtm)) + .collect::>() + .into_iter() + }) } -} -impl Ord for MonitorHandle { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - // TODO: Make a better ordering - (self as *const Self).cmp(&(other as *const Self)) + pub(crate) fn ui_screen(&self, mtm: MainThreadMarker) -> &Retained { + self.ui_screen.get(mtm) } -} -impl fmt::Debug for MonitorHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MonitorHandle") - .field("name", &self.name()) - .field("position", &self.position()) - .field("scale_factor", &self.scale_factor()) - .finish_non_exhaustive() + 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 + }) } } -impl MonitorHandle { - pub(crate) fn new(ui_screen: Retained) -> Self { - // Holding `Retained` implies we're on the main thread. - let mtm = MainThreadMarker::new().unwrap(); - Self { ui_screen: MainThreadBound::new(ui_screen, mtm) } +impl MonitorHandleProvider for MonitorHandle { + fn native_id(&self) -> u64 { + todo!() } - pub fn name(&self) -> Option { + fn name(&self) -> Option> { run_on_main(|mtm| { #[allow(deprecated)] let main = UIScreen::mainScreen(mtm); if *self.ui_screen(mtm) == main { - Some("Primary".to_string()) + Some("Primary".to_string().into()) } else if Some(self.ui_screen(mtm)) == main.mirroredScreen().as_ref() { - Some("Mirrored".to_string()) + Some("Mirrored".to_string().into()) } else { #[allow(deprecated)] UIScreen::screens(mtm) .iter() .position(|rhs| rhs == &**self.ui_screen(mtm)) - .map(|idx| idx.to_string()) + .map(|idx| idx.to_string().into()) } }) } - pub fn position(&self) -> Option> { + fn position(&self) -> Option> { let bounds = self.ui_screen.get_on_main(|ui_screen| ui_screen.nativeBounds()); Some((bounds.origin.x as f64, bounds.origin.y as f64).into()) } - pub fn scale_factor(&self) -> f64 { + fn scale_factor(&self) -> f64 { self.ui_screen.get_on_main(|ui_screen| ui_screen.nativeScale()) as f64 } - pub fn current_video_mode(&self) -> Option { + 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 { - run_on_main(|mtm| { - let ui_screen = self.ui_screen(mtm); - // Use Ord impl of RootVideoModeHandle - - let modes: BTreeSet<_> = ui_screen - .availableModes() - .into_iter() - .map(|mode| RootVideoModeHandle { - video_mode: VideoModeHandle::new(ui_screen.clone(), mode, mtm), - }) - .collect(); + fn video_modes(&self) -> Box> { + Box::new(self.video_mode_handless().map(|mode| mode.mode)) + } +} - modes.into_iter().map(|mode| mode.video_mode) +impl Clone for MonitorHandle { + fn clone(&self) -> Self { + run_on_main(|mtm| Self { + ui_screen: MainThreadBound::new(self.ui_screen.get(mtm).clone(), mtm), }) } +} - pub(crate) fn ui_screen(&self, mtm: MainThreadMarker) -> &Retained { - self.ui_screen.get(mtm) +impl hash::Hash for MonitorHandle { + fn hash(&self, state: &mut H) { + (self as *const Self).hash(state); } +} - pub fn preferred_video_mode(&self) -> VideoModeHandle { - run_on_main(|mtm| { - VideoModeHandle::new( - self.ui_screen(mtm).clone(), - self.ui_screen(mtm).preferredMode().unwrap(), - mtm, - ) - }) +impl PartialEq for MonitorHandle { + fn eq(&self, other: &Self) -> bool { + ptr::eq(self, other) + } +} + +impl Eq for MonitorHandle {} + +impl PartialOrd for MonitorHandle { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for MonitorHandle { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + // TODO: Make a better ordering + (self as *const Self).cmp(&(other as *const Self)) + } +} + +impl fmt::Debug for MonitorHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MonitorHandle") + .field("name", &self.name()) + .field("position", &self.position()) + .field("scale_factor", &self.scale_factor()) + .finish_non_exhaustive() } } diff --git a/src/platform_impl/apple/uikit/window.rs b/src/platform_impl/apple/uikit/window.rs index be4bef4536..dc0c10a682 100644 --- a/src/platform_impl/apple/uikit/window.rs +++ b/src/platform_impl/apple/uikit/window.rs @@ -1,6 +1,7 @@ #![allow(clippy::unnecessary_cast)] use std::collections::VecDeque; +use std::sync::Arc; use objc2::rc::Retained; use objc2::{class, declare_class, msg_send, msg_send_id, mutability, ClassType, DeclaredClass}; @@ -16,13 +17,13 @@ use tracing::{debug, warn}; use super::app_state::EventWrapper; use super::view::WinitView; use super::view_controller::WinitViewController; -use super::{app_state, monitor, ActiveEventLoop, Fullscreen, MonitorHandle}; +use super::{app_state, monitor, ActiveEventLoop, MonitorHandle}; use crate::cursor::Cursor; use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::{NotSupportedError, RequestError}; use crate::event::{Event, WindowEvent}; use crate::icon::Icon; -use crate::monitor::MonitorHandle as CoreMonitorHandle; +use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle}; use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations}; use crate::window::{ CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow, @@ -88,13 +89,18 @@ 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(monitor, ref video_mode)) => { + let monitor = monitor.as_any().downcast_ref::().unwrap(); let screen = monitor.ui_screen(mtm); - screen.setCurrentMode(Some(video_mode.screen_mode(mtm))); + if let Some(video_mode) = + monitor.video_mode_handless().find(|mode| &mode.mode == video_mode) + { + screen.setCurrentMode(Some(video_mode.screen_mode(mtm))); + } this.setScreen(screen); }, Some(Fullscreen::Borderless(Some(ref monitor))) => { + let monitor = monitor.as_any().downcast_ref::().unwrap(); let screen = monitor.ui_screen(mtm); this.setScreen(screen); }, @@ -303,12 +309,19 @@ 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 monitor = monitor.as_any().downcast_ref::().unwrap(); + let uiscreen = monitor.ui_screen(mtm); + if let Some(video_mode) = + monitor.video_mode_handless().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(), + Some(Fullscreen::Borderless(Some(monitor))) => { + monitor.as_any().downcast_ref::().unwrap().ui_screen(mtm).clone() + }, Some(Fullscreen::Borderless(None)) => { self.current_monitor_inner().ui_screen(mtm).clone() }, @@ -346,7 +359,7 @@ impl Inner { && screen_space_bounds.size.width == screen_bounds.size.width && screen_space_bounds.size.height == screen_bounds.size.height { - Some(Fullscreen::Borderless(Some(monitor))) + Some(Fullscreen::Borderless(Some(CoreMonitorHandle(Arc::new(monitor))))) } else { None } @@ -480,8 +493,11 @@ 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::Borderless(Some(ref monitor))) => monitor.ui_screen(mtm), + Some(Fullscreen::Exclusive(ref monitor, _)) + | Some(Fullscreen::Borderless(Some(ref monitor))) => { + let monitor = monitor.as_any().downcast_ref::().unwrap(); + monitor.ui_screen(mtm) + }, Some(Fullscreen::Borderless(None)) | None => &main_screen, }; @@ -691,11 +707,11 @@ impl CoreWindow for Window { self.maybe_wait_on_main(|delegate| delegate.is_maximized()) } - fn set_fullscreen(&self, fullscreen: Option) { + fn set_fullscreen(&self, fullscreen: Option) { self.maybe_wait_on_main(|delegate| delegate.set_fullscreen(fullscreen.map(Into::into))) } - fn fullscreen(&self) -> Option { + fn fullscreen(&self) -> Option { self.maybe_wait_on_main(|delegate| delegate.fullscreen().map(Into::into)) } @@ -792,21 +808,24 @@ impl CoreWindow for Window { fn current_monitor(&self) -> Option { self.maybe_wait_on_main(|delegate| { - delegate.current_monitor().map(|inner| CoreMonitorHandle { inner }) + delegate.current_monitor().map(|monitor| CoreMonitorHandle(Arc::new(monitor))) }) } fn available_monitors(&self) -> Box> { self.maybe_wait_on_main(|delegate| { Box::new( - delegate.available_monitors().into_iter().map(|inner| CoreMonitorHandle { inner }), + delegate + .available_monitors() + .into_iter() + .map(|monitor| CoreMonitorHandle(Arc::new(monitor))), ) }) } fn primary_monitor(&self) -> Option { self.maybe_wait_on_main(|delegate| { - delegate.primary_monitor().map(|inner| CoreMonitorHandle { inner }) + delegate.primary_monitor().map(|monitor| CoreMonitorHandle(Arc::new(monitor))) }) } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 55ba7e2e02..5fbac681b9 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)] @@ -19,7 +18,6 @@ use crate::application::ApplicationHandler; pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource; #[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; @@ -107,14 +105,6 @@ impl Default for PlatformSpecificWindowAttributes { pub(crate) static X11_BACKEND: Lazy, XNotSupported>>> = Lazy::new(|| Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new))); -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub enum MonitorHandle { - #[cfg(x11_platform)] - X(x11::MonitorHandle), - #[cfg(wayland_platform)] - Wayland(wayland::MonitorHandle), -} - /// `x11_or_wayland!(match expr; Enum(foo) => foo.something())` /// expands to the equivalent of /// ```ignore @@ -143,68 +133,6 @@ macro_rules! x11_or_wayland { }; } -impl MonitorHandle { - #[inline] - pub fn name(&self) -> Option { - x11_or_wayland!(match self; MonitorHandle(m) => m.name()) - } - - #[inline] - pub fn native_identifier(&self) -> u32 { - x11_or_wayland!(match self; MonitorHandle(m) => m.native_identifier()) - } - - #[inline] - pub fn position(&self) -> Option> { - x11_or_wayland!(match self; MonitorHandle(m) => m.position()) - } - - #[inline] - pub fn scale_factor(&self) -> f64 { - x11_or_wayland!(match self; MonitorHandle(m) => m.scale_factor() as _) - } - - #[inline] - pub fn current_video_mode(&self) -> Option { - x11_or_wayland!(match self; MonitorHandle(m) => m.current_video_mode()) - } - - #[inline] - pub fn video_modes(&self) -> Box> { - x11_or_wayland!(match self; MonitorHandle(m) => Box::new(m.video_modes())) - } -} - -#[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/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs index 0ce019f6b1..a663092605 100644 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -20,6 +20,7 @@ use crate::event_loop::{ ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, OwnedDisplayHandle as CoreOwnedDisplayHandle, }; +use crate::monitor::MonitorHandle as CoreMonitorHandle; use crate::platform::pump_events::PumpStatus; use crate::platform_impl::platform::min_timeout; use crate::platform_impl::PlatformCustomCursor; @@ -31,6 +32,7 @@ pub mod sink; use proxy::EventLoopProxy; use sink::EventSink; +use super::output::MonitorHandle; use super::state::{WindowCompositorUpdate, WinitState}; use super::window::state::FrameCallbackState; use super::{logical_to_physical_rounded, WindowId}; @@ -615,19 +617,18 @@ impl RootActiveEventLoop for ActiveEventLoop { Ok(Box::new(window)) } - fn available_monitors(&self) -> Box> { + fn available_monitors(&self) -> Box> { Box::new( self.state .borrow() .output_state .outputs() - .map(crate::platform_impl::wayland::output::MonitorHandle::new) - .map(crate::platform_impl::MonitorHandle::Wayland) - .map(|inner| crate::monitor::MonitorHandle { inner }), + .map(MonitorHandle::new) + .map(|inner| CoreMonitorHandle(Arc::new(inner))), ) } - fn primary_monitor(&self) -> Option { + fn primary_monitor(&self) -> Option { // There's no primary monitor on Wayland. None } diff --git a/src/platform_impl/linux/wayland/mod.rs b/src/platform_impl/linux/wayland/mod.rs index 91aad463c7..6636ab1d91 100644 --- a/src/platform_impl/linux/wayland/mod.rs +++ b/src/platform_impl/linux/wayland/mod.rs @@ -1,7 +1,6 @@ //! Winit's Wayland backend. pub use event_loop::{ActiveEventLoop, EventLoop}; -pub use output::{MonitorHandle, VideoModeHandle}; 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..87fd6c1176 100644 --- a/src/platform_impl/linux/wayland/output.rs +++ b/src/platform_impl/linux/wayland/output.rs @@ -1,11 +1,12 @@ -use std::num::{NonZeroU16, NonZeroU32}; +use std::borrow::Cow; +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::{MonitorHandleProvider as CoreMonitorHandle, VideoMode}; #[derive(Clone, Debug)] pub struct MonitorHandle { @@ -17,21 +18,20 @@ impl MonitorHandle { pub(crate) fn new(proxy: WlOutput) -> Self { Self { proxy } } +} - #[inline] - pub fn name(&self) -> Option { +impl CoreMonitorHandle for MonitorHandle { + fn native_id(&self) -> u64 { let output_data = self.proxy.data::().unwrap(); - output_data.with_output_info(|info| info.name.clone()) + output_data.with_output_info(|info| info.id as u64) } - #[inline] - pub fn native_identifier(&self) -> u32 { + fn name(&self) -> Option> { let output_data = self.proxy.data::().unwrap(); - output_data.with_output_info(|info| info.id) + output_data.with_output_info(|info| info.name.clone().map(Cow::Owned)) } - #[inline] - pub fn position(&self) -> Option> { + fn position(&self) -> Option> { let output_data = self.proxy.data::().unwrap(); Some(output_data.with_output_info(|info| { info.logical_position.map_or_else( @@ -47,95 +47,41 @@ impl MonitorHandle { })) } - #[inline] - pub fn scale_factor(&self) -> i32 { + fn scale_factor(&self) -> f64 { let output_data = self.proxy.data::().unwrap(); - output_data.scale_factor() + output_data.scale_factor() as f64 } - #[inline] - pub fn current_video_mode(&self) -> Option { + 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 { + fn video_modes(&self) -> Box> { 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)) - }) + Box::new(modes.into_iter().map(wayland_mode_to_core_mode)) } } impl PartialEq for MonitorHandle { fn eq(&self, other: &Self) -> bool { - self.native_identifier() == other.native_identifier() + self.native_id() == other.native_id() } } impl Eq for MonitorHandle {} -impl PartialOrd for MonitorHandle { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for MonitorHandle { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.native_identifier().cmp(&other.native_identifier()) - } -} - -impl std::hash::Hash for MonitorHandle { - fn hash(&self, state: &mut H) { - self.native_identifier().hash(state); - } -} - -#[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 725cb72bcb..f2161936b7 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -21,12 +21,12 @@ use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::{NotSupportedError, RequestError}; use crate::event::{Ime, WindowEvent}; use crate::event_loop::AsyncRequestSerial; -use crate::monitor::MonitorHandle as CoreMonitorHandle; -use crate::platform_impl::{Fullscreen, MonitorHandle as PlatformMonitorHandle}; +use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle}; +use crate::platform_impl::wayland::output; +use crate::utils::AsAny; use crate::window::{ - Cursor, CursorGrabMode, Fullscreen as CoreFullscreen, ImePurpose, ResizeDirection, Theme, - UserAttentionType, Window as CoreWindow, WindowAttributes, WindowButtons, WindowId, - WindowLevel, + Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, + Window as CoreWindow, WindowAttributes, WindowButtons, WindowId, WindowLevel, }; pub(crate) mod state; @@ -139,18 +139,19 @@ 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))] Some(Fullscreen::Borderless(monitor)) => { - let output = monitor.and_then(|monitor| match monitor { - PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy), - #[cfg(x11_platform)] - PlatformMonitorHandle::X(_) => None, + let output = monitor.as_ref().and_then(|monitor| { + monitor + .as_any() + .downcast_ref::() + .map(|handle| &handle.proxy) }); - window.set_fullscreen(output.as_ref()) + window.set_fullscreen(output) }, _ if attributes.maximized => window.set_maximized(), _ => (), @@ -433,26 +434,27 @@ impl CoreWindow for Window { .unwrap_or_default() } - fn set_fullscreen(&self, fullscreen: Option) { + fn set_fullscreen(&self, fullscreen: Option) { match fullscreen { - Some(CoreFullscreen::Exclusive(_)) => { + Some(Fullscreen::Exclusive(..)) => { warn!("`Fullscreen::Exclusive` is ignored on Wayland"); }, #[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))] - Some(CoreFullscreen::Borderless(monitor)) => { - let output = monitor.and_then(|monitor| match monitor.inner { - PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy), - #[cfg(x11_platform)] - PlatformMonitorHandle::X(_) => None, + Some(Fullscreen::Borderless(monitor)) => { + let output = monitor.as_ref().and_then(|monitor| { + monitor + .as_any() + .downcast_ref::() + .map(|handle| &handle.proxy) }); - self.window.set_fullscreen(output.as_ref()) + self.window.set_fullscreen(output) }, None => self.window.unset_fullscreen(), } } - fn fullscreen(&self) -> Option { + fn fullscreen(&self) -> Option { let is_fullscreen = self .window_state .lock() @@ -464,7 +466,7 @@ impl CoreWindow for Window { if is_fullscreen { let current_monitor = self.current_monitor(); - Some(CoreFullscreen::Borderless(current_monitor)) + Some(Fullscreen::Borderless(current_monitor)) } else { None } @@ -624,8 +626,7 @@ impl CoreWindow for Window { data.outputs() .next() .map(MonitorHandle::new) - .map(crate::platform_impl::MonitorHandle::Wayland) - .map(|inner| CoreMonitorHandle { inner }) + .map(|monitor| CoreMonitorHandle(Arc::new(monitor))) } fn available_monitors(&self) -> Box> { @@ -635,8 +636,7 @@ impl CoreWindow for Window { .unwrap() .clone() .into_iter() - .map(crate::platform_impl::MonitorHandle::Wayland) - .map(|inner| CoreMonitorHandle { inner }), + .map(|inner| CoreMonitorHandle(Arc::new(inner))), ) } diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 6f1dae2aa2..bce3eb96cd 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -30,6 +30,7 @@ use crate::event_loop::{ EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider, OwnedDisplayHandle as CoreOwnedDisplayHandle, }; +use crate::monitor::MonitorHandle as CoreMonitorHandle; use crate::platform::pump_events::PumpStatus; use crate::platform_impl::common::xkb::Context; use crate::platform_impl::platform::min_timeout; @@ -682,23 +683,18 @@ impl RootActiveEventLoop for ActiveEventLoop { }) } - fn available_monitors(&self) -> Box> { + fn available_monitors(&self) -> Box> { Box::new( self.xconn .available_monitors() .into_iter() .flatten() - .map(crate::platform_impl::MonitorHandle::X) - .map(|inner| crate::monitor::MonitorHandle { inner }), + .map(|monitor| CoreMonitorHandle(Arc::new(monitor))), ) } - fn primary_monitor(&self) -> Option { - self.xconn - .primary_monitor() - .ok() - .map(crate::platform_impl::MonitorHandle::X) - .map(|inner| crate::monitor::MonitorHandle { inner }) + fn primary_monitor(&self) -> Option { + self.xconn.primary_monitor().ok().map(|monitor| CoreMonitorHandle(Arc::new(monitor))) } fn system_theme(&self) -> Option { diff --git a/src/platform_impl/linux/x11/monitor.rs b/src/platform_impl/linux/x11/monitor.rs index eaf9bef37a..41746730c6 100644 --- a/src/platform_impl/linux/x11/monitor.rs +++ b/src/platform_impl/linux/x11/monitor.rs @@ -1,12 +1,13 @@ use std::num::{NonZeroU16, NonZeroU32}; +use dpi::PhysicalSize; use x11rb::connection::RequestConnection; use x11rb::protocol::randr::{self, ConnectionExt as _}; use x11rb::protocol::xproto; use super::{util, X11Error, XConnection}; -use crate::dpi::{PhysicalPosition, PhysicalSize}; -use crate::platform_impl::VideoModeHandle as PlatformVideoModeHandle; +use crate::dpi::PhysicalPosition; +use crate::monitor::{MonitorHandleProvider, VideoMode}; // Used for testing. This should always be committed as false. const DISABLE_MONITOR_LIST_CACHING: bool = false; @@ -21,32 +22,19 @@ 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 { + VideoMode { + size: mode.size, + bit_depth: mode.bit_depth, + refresh_rate_millihertz: mode.refresh_rate_millihertz, + } } } @@ -65,7 +53,33 @@ 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 MonitorHandleProvider for MonitorHandle { + fn native_id(&self) -> u64 { + self.id as _ + } + + fn name(&self) -> Option> { + Some(self.name.as_str().into()) + } + + fn position(&self) -> Option> { + Some(self.position.into()) + } + + fn scale_factor(&self) -> f64 { + self.scale_factor + } + + fn current_video_mode(&self) -> Option { + self.video_modes.iter().find_map(|mode| mode.current.then(|| mode.clone().into())) + } + + fn video_modes(&self) -> Box> { + Box::new(self.video_modes.clone().into_iter().map(|mode| mode.into())) + } } impl PartialEq for MonitorHandle { @@ -139,38 +153,6 @@ impl MonitorHandle { // Zero is an invalid XID value; no real monitor will have it self.id == 0 } - - pub fn name(&self) -> Option { - Some(self.name.clone()) - } - - #[inline] - pub fn native_identifier(&self) -> u32 { - self.id as _ - } - - pub fn position(&self) -> Option> { - Some(self.position.into()) - } - - #[inline] - pub fn scale_factor(&self) -> f64 { - self.scale_factor - } - - #[inline] - pub fn current_video_mode(&self) -> Option { - self.video_modes.iter().find(|mode| mode.current).cloned().map(PlatformVideoModeHandle::X) - } - - #[inline] - pub fn video_modes(&self) -> impl Iterator { - let monitor = self.clone(); - self.video_modes.clone().into_iter().map(move |mut x| { - x.monitor = Some(monitor.clone()); - PlatformVideoModeHandle::X(x) - }) - } } impl XConnection { diff --git a/src/platform_impl/linux/x11/util/randr.rs b/src/platform_impl/linux/x11/util/randr.rs index f90068867c..c5227620d8 100644 --- a/src/platform_impl/linux/x11/util/randr.rs +++ b/src/platform_impl/linux/x11/util/randr.rs @@ -82,17 +82,12 @@ impl XConnection { // XRROutputInfo contains an array of mode ids that correspond to // modes in the array in XRRScreenResources .filter(|x| output_modes.iter().any(|id| x.id == *id)) - .map(|mode| { - VideoModeHandle { - current: mode.id == current_mode, - size: (mode.width.into(), mode.height.into()), - refresh_rate_millihertz: monitor::mode_refresh_rate_millihertz(mode), - bit_depth: NonZeroU16::new(bit_depth as u16), - native_mode: mode.id, - // This is populated in `MonitorHandle::video_modes` as the - // video mode is returned to the user - monitor: None, - } + .map(|mode| VideoModeHandle { + current: mode.id == current_mode, + 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, }) .collect(); diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 1866bf8d4c..c125a1843b 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::ffi::CString; use std::mem::replace; use std::num::NonZeroU32; @@ -25,15 +26,15 @@ use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::{NotSupportedError, RequestError}; use crate::event::{Event, SurfaceSizeWriter, WindowEvent}; use crate::event_loop::AsyncRequestSerial; +use crate::monitor::{ + Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider, VideoMode, +}; use crate::platform::x11::WindowType; use crate::platform_impl::x11::atoms::*; use crate::platform_impl::x11::{ xinput_fp1616_to_float, MonitorHandle as X11MonitorHandle, WakeSender, X11Error, }; -use crate::platform_impl::{ - common, Fullscreen, MonitorHandle as PlatformMonitorHandle, PlatformCustomCursor, PlatformIcon, - VideoModeHandle as PlatformVideoModeHandle, -}; +use crate::platform_impl::{common, PlatformCustomCursor, PlatformIcon}; use crate::window::{ CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow, WindowAttributes, WindowButtons, WindowId, WindowLevel, @@ -174,11 +175,11 @@ impl CoreWindow for Window { self.0.is_maximized() } - fn set_fullscreen(&self, fullscreen: Option) { + fn set_fullscreen(&self, fullscreen: Option) { self.0.set_fullscreen(fullscreen.map(Into::into)) } - fn fullscreen(&self) -> Option { + fn fullscreen(&self) -> Option { self.0.fullscreen().map(Into::into) } @@ -270,28 +271,21 @@ impl CoreWindow for Window { self.0.set_cursor_hittest(hittest) } - fn current_monitor(&self) -> Option { - self.0 - .current_monitor() - .map(crate::platform_impl::MonitorHandle::X) - .map(|inner| crate::monitor::MonitorHandle { inner }) + fn current_monitor(&self) -> Option { + self.0.current_monitor().map(|monitor| CoreMonitorHandle(Arc::new(monitor))) } - fn available_monitors(&self) -> Box> { + fn available_monitors(&self) -> Box> { Box::new( self.0 .available_monitors() .into_iter() - .map(crate::platform_impl::MonitorHandle::X) - .map(|inner| crate::monitor::MonitorHandle { inner }), + .map(|monitor| CoreMonitorHandle(Arc::new(monitor))), ) } - fn primary_monitor(&self) -> Option { - self.0 - .primary_monitor() - .map(crate::platform_impl::MonitorHandle::X) - .map(|inner| crate::monitor::MonitorHandle { inner }) + fn primary_monitor(&self) -> Option { + self.0.primary_monitor().map(|monitor| CoreMonitorHandle(Arc::new(monitor))) } fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle { @@ -323,7 +317,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); } @@ -1031,20 +1025,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_id() as _; 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) @@ -1067,26 +1058,40 @@ impl UnownedWindow { flusher.map(Some) }, Some(fullscreen) => { - let (video_mode, monitor) = match fullscreen { - Fullscreen::Exclusive(PlatformVideoModeHandle::X(ref video_mode)) => { - (Some(video_mode), video_mode.monitor.clone().unwrap()) - }, - Fullscreen::Borderless(Some(PlatformMonitorHandle::X(monitor))) => { - (None, monitor) - }, - Fullscreen::Borderless(None) => { - (None, self.shared_state_lock().last_monitor.clone()) - }, - #[cfg(wayland_platform)] - _ => unreachable!(), - }; + let (monitor, video_mode): (Cow<'_, X11MonitorHandle>, Option<&VideoMode>) = + match &fullscreen { + Fullscreen::Exclusive(monitor, video_mode) => { + let monitor = + monitor.as_any().downcast_ref::().unwrap(); + (Cow::Borrowed(monitor), Some(video_mode)) + }, + Fullscreen::Borderless(Some(monitor)) => { + let monitor = + monitor.as_any().downcast_ref::().unwrap(); + (Cow::Borrowed(monitor), None) + }, + Fullscreen::Borderless(None) => { + (Cow::Owned(self.shared_state_lock().last_monitor.clone()), None) + }, + }; // Don't set fullscreen on an invalid dummy monitor handle if monitor.is_dummy() { 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 @@ -1113,7 +1118,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.native_id() as _, native_mode) .expect("failed to set video mode"); } diff --git a/src/platform_impl/mod.rs b/src/platform_impl/mod.rs index 1425c1b151..9ce2fa6f02 100644 --- a/src/platform_impl/mod.rs +++ b/src/platform_impl/mod.rs @@ -1,6 +1,3 @@ -use crate::monitor::{MonitorHandle as RootMonitorHandle, VideoModeHandle as RootVideoModeHandle}; -use crate::window::Fullscreen as RootFullscreen; - #[cfg(android_platform)] mod android; #[cfg(target_vendor = "apple")] @@ -29,38 +26,6 @@ use self::web as platform; #[cfg(windows_platform)] use self::windows as platform; -/// Helper for converting between platform-specific and generic -/// [`VideoModeHandle`]/[`MonitorHandle`] -#[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) enum Fullscreen { - Exclusive(VideoModeHandle), - Borderless(Option), -} - -impl From for Fullscreen { - fn from(f: RootFullscreen) -> Self { - match f { - RootFullscreen::Exclusive(mode) => Self::Exclusive(mode.video_mode), - RootFullscreen::Borderless(Some(handle)) => Self::Borderless(Some(handle.inner)), - RootFullscreen::Borderless(None) => Self::Borderless(None), - } - } -} - -impl From for RootFullscreen { - fn from(f: Fullscreen) -> Self { - match f { - Fullscreen::Exclusive(video_mode) => { - Self::Exclusive(RootVideoModeHandle { video_mode }) - }, - Fullscreen::Borderless(Some(inner)) => { - Self::Borderless(Some(RootMonitorHandle { inner })) - }, - Fullscreen::Borderless(None) => Self::Borderless(None), - } - } -} - #[cfg(all( not(ios_platform), not(windows_platform), diff --git a/src/platform_impl/orbital/event_loop.rs b/src/platform_impl/orbital/event_loop.rs index 1b9cc6042c..7716f7659e 100644 --- a/src/platform_impl/orbital/event_loop.rs +++ b/src/platform_impl/orbital/event_loop.rs @@ -2,7 +2,7 @@ use std::cell::Cell; use std::collections::VecDeque; use std::sync::{mpsc, Arc, Mutex}; use std::time::Instant; -use std::{mem, slice}; +use std::{iter, mem, slice}; use bitflags::bitflags; use orbclient::{ @@ -12,8 +12,7 @@ use orbclient::{ use smol_str::SmolStr; use super::{ - KeyEventExtra, MonitorHandle, PlatformSpecificEventLoopAttributes, RedoxSocket, TimeSocket, - WindowProperties, + KeyEventExtra, PlatformSpecificEventLoopAttributes, RedoxSocket, TimeSocket, WindowProperties, }; use crate::application::ApplicationHandler; use crate::error::{EventLoopError, NotSupportedError, RequestError}; @@ -711,9 +710,7 @@ impl RootActiveEventLoop for ActiveEventLoop { } fn available_monitors(&self) -> Box> { - let mut v = VecDeque::with_capacity(1); - v.push_back(crate::monitor::MonitorHandle { inner: MonitorHandle }); - Box::new(v.into_iter()) + Box::new(iter::empty()) } fn system_theme(&self) -> Option { @@ -721,7 +718,7 @@ impl RootActiveEventLoop for ActiveEventLoop { } fn primary_monitor(&self) -> Option { - Some(crate::monitor::MonitorHandle { inner: MonitorHandle }) + None } fn listen_device_events(&self, _allowed: DeviceEvents) {} diff --git a/src/platform_impl/orbital/mod.rs b/src/platform_impl/orbital/mod.rs index 593e1e5215..38917ce88f 100644 --- a/src/platform_impl/orbital/mod.rs +++ b/src/platform_impl/orbital/mod.rs @@ -1,12 +1,10 @@ #![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}; use crate::keyboard::Key; mod event_loop; @@ -135,57 +133,6 @@ impl<'a> fmt::Display for WindowProperties<'a> { } } -#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct MonitorHandle; - -impl MonitorHandle { - pub fn name(&self) -> Option { - None - } - - pub fn position(&self) -> Option> { - None - } - - pub fn scale_factor(&self) -> f64 { - 1.0 // TODO - } - - 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() - } -} - #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeyEventExtra { pub key_without_modifiers: Key, diff --git a/src/platform_impl/orbital/window.rs b/src/platform_impl/orbital/window.rs index 76c698363d..59198913b0 100644 --- a/src/platform_impl/orbital/window.rs +++ b/src/platform_impl/orbital/window.rs @@ -1,13 +1,14 @@ use std::collections::VecDeque; +use std::iter; use std::sync::{Arc, Mutex}; use super::event_loop::EventLoopProxy; -use super::{ActiveEventLoop, MonitorHandle, RedoxSocket, WindowProperties}; +use super::{ActiveEventLoop, RedoxSocket, WindowProperties}; use crate::cursor::Cursor; use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::{NotSupportedError, RequestError}; -use crate::monitor::MonitorHandle as CoreMonitorHandle; -use crate::window::{self, Fullscreen, ImePurpose, Window as CoreWindow, WindowId}; +use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle}; +use crate::window::{self, ImePurpose, Window as CoreWindow, WindowId}; // These values match the values uses in the `window_new` function in orbital: // https://gitlab.redox-os.org/redox-os/orbital/-/blob/master/src/scheme.rs @@ -32,7 +33,7 @@ impl Window { el: &ActiveEventLoop, attrs: window::WindowAttributes, ) -> Result { - let scale = MonitorHandle.scale_factor(); + let scale = 1.; let (x, y) = if let Some(pos) = attrs.position { pos.to_physical::(scale).into() @@ -160,22 +161,22 @@ impl CoreWindow for Window { #[inline] fn primary_monitor(&self) -> Option { - Some(CoreMonitorHandle { inner: MonitorHandle }) + None } #[inline] fn available_monitors(&self) -> Box> { - Box::new(vec![CoreMonitorHandle { inner: MonitorHandle }].into_iter()) + Box::new(iter::empty()) } #[inline] fn current_monitor(&self) -> Option { - Some(CoreMonitorHandle { inner: MonitorHandle }) + None } #[inline] fn scale_factor(&self) -> f64 { - MonitorHandle.scale_factor() + 1. } #[inline] diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 2ab0e78f8c..bcfd32fa22 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -509,12 +509,12 @@ impl RootActiveEventLoop for ActiveEventLoop { .monitor() .available_monitors() .into_iter() - .map(|inner| RootMonitorHandle { inner }), + .map(|monitor| RootMonitorHandle(Arc::new(monitor))), ) } fn primary_monitor(&self) -> Option { - self.runner.monitor().primary_monitor().map(|inner| RootMonitorHandle { inner }) + self.runner.monitor().primary_monitor().map(|monitor| RootMonitorHandle(Arc::new(monitor))) } fn listen_device_events(&self, allowed: DeviceEvents) { 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..27c8c7119b 100644 --- a/src/platform_impl/web/monitor.rs +++ b/src/platform_impl/web/monitor.rs @@ -3,13 +3,12 @@ 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}; -use std::sync::OnceLock; +use std::sync::{Arc, OnceLock}; use std::task::{ready, Context, Poll}; use dpi::LogicalSize; @@ -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, MonitorHandleProvider, VideoMode}; use crate::platform::web::{ MonitorPermissionError, Orientation, OrientationData, OrientationLock, OrientationLockError, }; @@ -47,26 +46,6 @@ impl MonitorHandle { Self { id, inner: Dispatcher::new(main_thread, inner).0 } } - pub fn scale_factor(&self) -> f64 { - self.inner.queue(|inner| inner.scale_factor()) - } - - pub fn position(&self) -> Option> { - self.inner.queue(|inner| inner.position()) - } - - pub fn name(&self) -> Option { - self.inner.queue(|inner| inner.name()) - } - - pub fn current_video_mode(&self) -> Option { - Some(VideoModeHandle(self.clone())) - } - - pub fn video_modes(&self) -> Once { - iter::once(VideoModeHandle(self.clone())) - } - pub fn orientation(&self) -> OrientationData { self.inner.queue(|inner| inner.orientation()) } @@ -140,6 +119,36 @@ impl MonitorHandle { } } +impl MonitorHandleProvider for MonitorHandle { + fn native_id(&self) -> u64 { + self.id.unwrap_or_default() + } + + fn scale_factor(&self) -> f64 { + self.inner.queue(|inner| inner.scale_factor()) + } + + fn position(&self) -> Option> { + self.inner.queue(|inner| inner.position()) + } + + fn name(&self) -> Option> { + self.inner.queue(|inner| inner.name().map(Into::into)) + } + + 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, + }) + } + + fn video_modes(&self) -> Box> { + Box::new(self.current_video_mode().into_iter()) + } +} + impl Debug for MonitorHandle { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let (name, position, scale_factor, orientation, is_internal, is_detailed) = @@ -190,8 +199,8 @@ impl PartialOrd for MonitorHandle { } impl From for RootMonitorHandle { - fn from(inner: MonitorHandle) -> Self { - RootMonitorHandle { inner } + fn from(monitor: MonitorHandle) -> Self { + RootMonitorHandle(Arc::new(monitor)) } } @@ -252,35 +261,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/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 8ab4f7e846..71fa7e4da8 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -26,7 +26,7 @@ use crate::event::{ SurfaceSizeWriter, }; use crate::keyboard::{Key, KeyLocation, ModifiersState, PhysicalKey}; -use crate::platform_impl::Fullscreen; +use crate::monitor::Fullscreen; use crate::window::{WindowAttributes, WindowId}; #[allow(dead_code)] @@ -148,13 +148,7 @@ impl Canvas { } if let Some(fullscreen) = attr.fullscreen { - fullscreen::request_fullscreen( - main_thread, - &window, - &document, - &canvas, - fullscreen.into(), - ); + fullscreen::request_fullscreen(main_thread, &window, &document, &canvas, fullscreen); } if attr.active { diff --git a/src/platform_impl/web/web_sys/fullscreen.rs b/src/platform_impl/web/web_sys/fullscreen.rs index a7e847ad2b..5c688604b7 100644 --- a/src/platform_impl/web/web_sys/fullscreen.rs +++ b/src/platform_impl/web/web_sys/fullscreen.rs @@ -9,7 +9,8 @@ use web_sys::{console, Document, Element, HtmlCanvasElement, Window}; use super::super::main_thread::MainThreadMarker; use super::super::monitor::{self, ScreenDetailed}; -use crate::platform_impl::Fullscreen; +use crate::monitor::Fullscreen; +use crate::platform_impl::MonitorHandle; pub(crate) fn request_fullscreen( main_thread: MainThreadMarker, @@ -55,7 +56,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!( @@ -64,6 +65,8 @@ pub(crate) fn request_fullscreen( return; } + let monitor = monitor.as_any().downcast_ref::().unwrap(); + if let Some(monitor) = monitor.detailed(main_thread) { let options: FullscreenOptions = Object::new().unchecked_into(); options.set_screen(&monitor); diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index 639dcfe49e..162b081e74 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -11,11 +11,10 @@ use super::{backend, lock, ActiveEventLoop}; use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::{NotSupportedError, RequestError}; use crate::icon::Icon; -use crate::monitor::MonitorHandle as RootMonitorHandle; +use crate::monitor::{Fullscreen, MonitorHandle as RootMonitorHandle}; use crate::window::{ - Cursor, CursorGrabMode, Fullscreen as RootFullscreen, ImePurpose, ResizeDirection, Theme, - UserAttentionType, Window as RootWindow, WindowAttributes, WindowButtons, WindowId, - WindowLevel, + Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, + Window as RootWindow, WindowAttributes, WindowButtons, WindowId, WindowLevel, }; pub struct Window { @@ -232,20 +231,20 @@ impl RootWindow for Window { false } - fn set_fullscreen(&self, fullscreen: Option) { + fn set_fullscreen(&self, fullscreen: Option) { self.inner.dispatch(move |inner| { if let Some(fullscreen) = fullscreen { - inner.canvas.request_fullscreen(fullscreen.into()); + inner.canvas.request_fullscreen(fullscreen); } else { inner.canvas.exit_fullscreen() } }) } - fn fullscreen(&self) -> Option { + fn fullscreen(&self) -> Option { self.inner.queue(|inner| { if inner.canvas.is_fullscreen() { - Some(RootFullscreen::Borderless(None)) + Some(Fullscreen::Borderless(None)) } else { None } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 874ae06a8c..e106a8baf8 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -73,7 +73,7 @@ use crate::event_loop::{ OwnedDisplayHandle as CoreOwnedDisplayHandle, }; use crate::keyboard::ModifiersState; -use crate::monitor::MonitorHandle as RootMonitorHandle; +use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle}; use crate::platform::pump_events::PumpStatus; use crate::platform_impl::platform::dark_mode::try_theme; use crate::platform_impl::platform::dpi::{become_dpi_aware, dpi_to_scale_factor}; @@ -87,7 +87,7 @@ use crate::platform_impl::platform::window::InitData; use crate::platform_impl::platform::window_state::{ CursorFlags, ImeState, WindowFlags, WindowState, }; -use crate::platform_impl::platform::{raw_input, util, wrap_device_id, Fullscreen}; +use crate::platform_impl::platform::{raw_input, util, wrap_device_id}; use crate::platform_impl::Window; use crate::utils::Lazy; use crate::window::{ @@ -480,16 +480,16 @@ impl RootActiveEventLoop for ActiveEventLoop { Ok(RootCustomCursor { inner: WinCursor::new(&source.inner.0)? }) } - fn available_monitors(&self) -> Box> { + fn available_monitors(&self) -> Box> { Box::new( monitor::available_monitors() .into_iter() - .map(|inner| crate::monitor::MonitorHandle { inner }), + .map(|monitor| CoreMonitorHandle(Arc::new(monitor))), ) } - fn primary_monitor(&self) -> Option { - Some(RootMonitorHandle { inner: monitor::primary_monitor() }) + fn primary_monitor(&self) -> Option { + Some(CoreMonitorHandle(Arc::new(monitor::primary_monitor()))) } fn exiting(&self) -> bool { @@ -1296,7 +1296,7 @@ unsafe fn public_window_callback_inner( if new_monitor != 0 && fullscreen_monitor .as_ref() - .map(|monitor| new_monitor != monitor.hmonitor()) + .map(|monitor| new_monitor != monitor.native_id() as _) .unwrap_or(true) { if let Ok(new_monitor_info) = monitor::get_monitor_info(new_monitor) @@ -1307,12 +1307,15 @@ unsafe fn public_window_callback_inner( window_pos.cx = new_monitor_rect.right - new_monitor_rect.left; window_pos.cy = new_monitor_rect.bottom - new_monitor_rect.top; } - *fullscreen_monitor = Some(MonitorHandle::new(new_monitor)); + *fullscreen_monitor = Some(CoreMonitorHandle(Arc::new( + MonitorHandle::new(new_monitor), + ))); } }, - Fullscreen::Exclusive(ref video_mode) => { - let old_monitor = video_mode.monitor.hmonitor(); - if let Ok(old_monitor_info) = monitor::get_monitor_info(old_monitor) { + Fullscreen::Exclusive(monitor, _) => { + if let Ok(old_monitor_info) = + monitor::get_monitor_info(monitor.native_id() as _) + { let old_monitor_rect = old_monitor_info.monitorInfo.rcMonitor; window_pos.x = old_monitor_rect.left; window_pos.y = old_monitor_rect.top; diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 0bf9fe8660..ed73ac6232 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -6,14 +6,13 @@ 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; use crate::icon::Icon; use crate::keyboard::Key; use crate::platform::windows::{BackdropType, Color, CornerPreference}; -use crate::platform_impl::Fullscreen; #[derive(Clone, Debug, PartialEq)] pub struct PlatformSpecificWindowAttributes { diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index d98d54f71b..2b6a0a791a 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -1,7 +1,7 @@ -use std::collections::{BTreeSet, VecDeque}; +use std::collections::{HashSet, VecDeque}; use std::hash::Hash; use std::num::{NonZeroU16, NonZeroU32}; -use std::{io, mem, ptr}; +use std::{io, iter, mem, ptr}; use windows_sys::Win32::Foundation::{BOOL, HWND, LPARAM, POINT, RECT}; use windows_sys::Win32::Graphics::Gdi::{ @@ -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::{MonitorHandleProvider, 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,66 +34,32 @@ 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("VideoModeHandle").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) } } } -#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] -pub struct MonitorHandle(HMONITOR); - -// Send is not implemented for HMONITOR, we have to wrap it and implement it manually. -// For more info see: -// https://github.com/retep998/winapi-rs/issues/360 -// https://github.com/retep998/winapi-rs/issues/396 - -unsafe impl Send for MonitorHandle {} - unsafe extern "system" fn monitor_enum_proc( hmonitor: HMONITOR, _hdc: HDC, @@ -148,25 +108,19 @@ pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result Self { - MonitorHandle(hmonitor) - } +#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] +pub struct MonitorHandle(HMONITOR); - #[inline] - pub fn name(&self) -> Option { - let monitor_info = get_monitor_info(self.0).unwrap(); - Some(decode_wide(&monitor_info.szDevice).to_string_lossy().to_string()) - } +// Send is not implemented for HMONITOR, we have to wrap it and implement it manually. +// For more info see: +// https://github.com/retep998/winapi-rs/issues/360 +// https://github.com/retep998/winapi-rs/issues/396 - #[inline] - pub fn native_identifier(&self) -> String { - self.name().unwrap() - } +unsafe impl Send for MonitorHandle {} - #[inline] - pub fn hmonitor(&self) -> HMONITOR { - self.0 +impl MonitorHandle { + pub(crate) fn new(hmonitor: HMONITOR) -> Self { + MonitorHandle(hmonitor) } pub(crate) fn size(&self) -> PhysicalSize { @@ -177,51 +131,17 @@ impl MonitorHandle { } } - #[inline] - pub fn position(&self) -> Option> { - get_monitor_info(self.0) - .map(|info| { - let rc_monitor = info.monitorInfo.rcMonitor; - PhysicalPosition { x: rc_monitor.left, y: rc_monitor.top } - }) - .ok() - } - - #[inline] - pub fn scale_factor(&self) -> f64 { - dpi_to_scale_factor(get_monitor_dpi(self.0).unwrap_or(96)) - } - - #[inline] - pub fn current_video_mode(&self) -> Option { - let monitor_info = get_monitor_info(self.0).ok()?; - let device_name = monitor_info.szDevice.as_ptr(); - unsafe { - let mut mode: DEVMODEW = mem::zeroed(); - mode.dmSize = mem::size_of_val(&mode) as u16; - if EnumDisplaySettingsExW(device_name, ENUM_CURRENT_SETTINGS, &mut mode, 0) - == false.into() - { - None - } else { - Some(VideoModeHandle::new(self.clone(), mode)) - } - } - } - - #[inline] - pub fn video_modes(&self) -> impl Iterator { + pub(crate) fn video_mode_handles(&self) -> Box> { // 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 Box::new(iter::empty()); }, }; @@ -236,13 +156,55 @@ 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) + Box::new(modes.into_iter()) + } +} + +impl MonitorHandleProvider for MonitorHandle { + fn native_id(&self) -> u64 { + self.0 as _ + } + + fn name(&self) -> Option> { + let monitor_info = get_monitor_info(self.0).unwrap(); + Some(decode_wide(&monitor_info.szDevice).to_string_lossy().to_string().into()) + } + + fn position(&self) -> Option> { + get_monitor_info(self.0) + .map(|info| { + let rc_monitor = info.monitorInfo.rcMonitor; + PhysicalPosition { x: rc_monitor.left, y: rc_monitor.top } + }) + .ok() + } + + fn scale_factor(&self) -> f64 { + dpi_to_scale_factor(get_monitor_dpi(self.0).unwrap_or(96)) + } + + fn current_video_mode(&self) -> Option { + let monitor_info = get_monitor_info(self.0).ok()?; + let device_name = monitor_info.szDevice.as_ptr(); + unsafe { + let mut mode: DEVMODEW = mem::zeroed(); + mode.dmSize = mem::size_of_val(&mode) as u16; + if EnumDisplaySettingsExW(device_name, ENUM_CURRENT_SETTINGS, &mut mode, 0) + == false.into() + { + None + } else { + Some(VideoModeHandle::new(mode).mode) + } + } + } + + fn video_modes(&self) -> Box> { + Box::new(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 a9a2c7612d..3be469a9fa 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -1,5 +1,6 @@ #![cfg(windows_platform)] +use std::borrow::Cow; use std::cell::Cell; use std::ffi::c_void; use std::mem::{self, MaybeUninit}; @@ -45,11 +46,12 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{ WDA_EXCLUDEFROMCAPTURE, WDA_NONE, WM_NCLBUTTONDOWN, WM_SYSCOMMAND, WNDCLASSEXW, }; +use super::MonitorHandle; use crate::cursor::Cursor; use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::{NotSupportedError, RequestError}; use crate::icon::Icon; -use crate::monitor::MonitorHandle as CoreMonitorHandle; +use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider}; use crate::platform::windows::{BackdropType, Color, CornerPreference}; use crate::platform_impl::platform::dark_mode::try_theme; use crate::platform_impl::platform::definitions::{ @@ -66,11 +68,10 @@ use crate::platform_impl::platform::keyboard::KeyEventBuilder; use crate::platform_impl::platform::window_state::{ CursorFlags, SavedWindow, WindowFlags, WindowState, }; -use crate::platform_impl::platform::{monitor, util, Fullscreen, SelectedCursor}; +use crate::platform_impl::platform::{monitor, util, SelectedCursor}; use crate::window::{ - CursorGrabMode, Fullscreen as CoreFullscreen, ImePurpose, ResizeDirection, Theme, - UserAttentionType, Window as CoreWindow, WindowAttributes, WindowButtons, WindowId, - WindowLevel, + CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow, + WindowAttributes, WindowButtons, WindowId, WindowLevel, }; /// The Win32 implementation of the main `Window` object. @@ -334,7 +335,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(Fullscreen::Exclusive(_, _))) { self.set_fullscreen(None); } @@ -733,12 +734,12 @@ impl CoreWindow for Window { window_state.window_flags.contains(WindowFlags::MAXIMIZED) } - fn fullscreen(&self) -> Option { + fn fullscreen(&self) -> Option { let window_state = self.window_state_lock(); window_state.fullscreen.clone().map(Into::into) } - fn set_fullscreen(&self, fullscreen: Option) { + fn set_fullscreen(&self, fullscreen: Option) { let fullscreen = fullscreen.map(Into::into); let window = self.window; let window_state = Arc::clone(&self.window_state); @@ -752,7 +753,7 @@ impl CoreWindow for Window { // Return if saved Borderless(monitor) is the same as current monitor when requested // fullscreen is Borderless(None) (Some(Fullscreen::Borderless(Some(monitor))), Some(Fullscreen::Borderless(None))) - if *monitor == monitor::current_monitor(window) => + if monitor.native_id() == monitor::current_monitor(window).native_id() => { return }, @@ -767,9 +768,14 @@ 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(); - let monitor_info = monitor::get_monitor_info(monitor.hmonitor()).unwrap(); + (_, Some(Fullscreen::Exclusive(monitor, video_mode))) => { + let monitor = monitor.as_any().downcast_ref::().unwrap(); + let video_mode = + match monitor.video_mode_handles().find(|mode| &mode.mode == video_mode) { + Some(monitor) => monitor, + None => return, + }; + let monitor_info = monitor::get_monitor_info(monitor.native_id() as _).unwrap(); let res = unsafe { ChangeDisplaySettingsExW( @@ -787,7 +793,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(), @@ -824,7 +830,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, @@ -854,11 +860,16 @@ 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::Borderless(Some(monitor)) => monitor.clone(), - Fullscreen::Borderless(None) => monitor::current_monitor(window), + Fullscreen::Exclusive(monitor, _) + | Fullscreen::Borderless(Some(monitor)) => Some(Cow::Borrowed( + monitor.as_any().downcast_ref::().unwrap(), + )), + Fullscreen::Borderless(None) => None, }; + let monitor = + monitor.unwrap_or_else(|| Cow::Owned(monitor::current_monitor(window))); + let position: (i32, i32) = monitor.position().unwrap_or_default().into(); let size: (u32, u32) = monitor.size().into(); @@ -920,15 +931,19 @@ impl CoreWindow for Window { } fn current_monitor(&self) -> Option { - Some(CoreMonitorHandle { inner: monitor::current_monitor(self.hwnd()) }) + Some(CoreMonitorHandle(Arc::new(monitor::current_monitor(self.hwnd())))) } fn available_monitors(&self) -> Box> { - Box::new(monitor::available_monitors().into_iter().map(|inner| CoreMonitorHandle { inner })) + Box::new( + monitor::available_monitors() + .into_iter() + .map(|monitor| CoreMonitorHandle(Arc::new(monitor))), + ) } fn primary_monitor(&self) -> Option { - Some(CoreMonitorHandle { inner: monitor::primary_monitor() }) + Some(CoreMonitorHandle(Arc::new(monitor::primary_monitor()))) } fn set_window_icon(&self, window_icon: Option) { diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index f84dd2cfdb..5c223042c9 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -19,7 +19,8 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{ use crate::dpi::{PhysicalPosition, PhysicalSize, Size}; use crate::icon::Icon; use crate::keyboard::ModifiersState; -use crate::platform_impl::platform::{event_loop, util, Fullscreen, SelectedCursor}; +use crate::monitor::Fullscreen; +use crate::platform_impl::platform::{event_loop, util, SelectedCursor}; use crate::window::{Theme, WindowAttributes}; /// Contains information about states and the window that the callback is going to use. diff --git a/src/window.rs b/src/window.rs index 22af8cbc1d..1412ce75f8 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::{PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::RequestError; pub use crate::icon::{BadIcon, Icon}; -use crate::monitor::{MonitorHandle, VideoModeHandle}; +use crate::monitor::{Fullscreen, MonitorHandle}; use crate::platform_impl::PlatformSpecificWindowAttributes; use crate::utils::AsAny; @@ -46,7 +46,7 @@ impl fmt::Debug for WindowId { } /// Attributes used when creating a window. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub struct WindowAttributes { pub surface_size: Option, pub min_surface_size: Option, @@ -442,7 +442,7 @@ pub trait Window: AsAny + Send + Sync { /// moved to another screen); as such, tracking [`WindowEvent::ScaleFactorChanged`] events is /// the most robust way to track the DPI you need to use to draw. /// - /// This value may differ from [`MonitorHandle::scale_factor`]. + /// This value may differ from [`MonitorHandleProvider::scale_factor`]. /// /// See the [`dpi`] crate for more information. /// @@ -496,6 +496,7 @@ pub trait Window: AsAny + Send + Sync { /// [android_1]: https://developer.android.com/training/multiscreen/screendensities /// [web_1]: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio /// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc + /// [`MonitorHandleProvider::scale_factor`]: crate::monitor::MonitorHandleProvider::scale_factor. fn scale_factor(&self) -> f64; /// Queues a [`WindowEvent::RedrawRequested`] event to be emitted that aligns with the windowing @@ -906,7 +907,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]" @@ -915,6 +916,7 @@ pub trait Window: AsAny + Send + Sync { /// or calling without a [transient activation] does nothing. /// /// [transient activation]: https://developer.mozilla.org/en-US/docs/Glossary/Transient_activation + /// [`VideoMode`]: crate::monitor::VideoMode fn set_fullscreen(&self, fullscreen: Option); /// Gets the window's current fullscreen state. @@ -1373,15 +1375,6 @@ impl From for CursorIcon { } } -/// Fullscreen modes. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum Fullscreen { - Exclusive(VideoModeHandle), - - /// Providing `None` to `Borderless` will fullscreen on the current monitor. - Borderless(Option), -} - /// The theme variant to use. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]