From f1c5afd84e9719245da0ec8d8ea342bac190e43f Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sat, 21 Sep 2024 20:27:12 +0300 Subject: [PATCH] monitor: refactor `MonitorHandle` to store dyn object This also alters `VideoMode` to be a regular object and not reference the `MonitorHandle`, since it's a static data. Given that `VideoMode` set may change during runtime keeping the reference as a some sort of validity may not be idea and propagating errors when changing video mode could be more reliable. --- examples/application.rs | 5 +- src/changelog/unreleased.md | 2 + src/monitor.rs | 190 +++++++++------- src/platform/ios.rs | 13 +- src/platform/macos.rs | 11 +- src/platform/wayland.rs | 14 -- src/platform/web.rs | 35 ++- src/platform/windows.rs | 22 -- src/platform/x11.rs | 14 -- src/platform_impl/android/mod.rs | 42 +--- src/platform_impl/apple/appkit/event_loop.rs | 12 +- src/platform_impl/apple/appkit/mod.rs | 1 - src/platform_impl/apple/appkit/monitor.rs | 213 +++++++++--------- src/platform_impl/apple/appkit/window.rs | 21 +- .../apple/appkit/window_delegate.rs | 36 +-- src/platform_impl/apple/uikit/event_loop.rs | 12 +- src/platform_impl/apple/uikit/mod.rs | 1 - src/platform_impl/apple/uikit/monitor.rs | 94 ++++---- src/platform_impl/apple/uikit/window.rs | 44 ++-- src/platform_impl/linux/mod.rs | 42 ---- .../linux/wayland/event_loop/mod.rs | 11 +- src/platform_impl/linux/wayland/mod.rs | 6 +- src/platform_impl/linux/wayland/output.rs | 61 ++--- src/platform_impl/linux/wayland/window/mod.rs | 50 ++-- src/platform_impl/linux/x11/mod.rs | 14 +- src/platform_impl/linux/x11/monitor.rs | 61 ++--- src/platform_impl/linux/x11/util/randr.rs | 21 +- src/platform_impl/linux/x11/window.rs | 73 +++--- src/platform_impl/mod.rs | 37 --- src/platform_impl/orbital/event_loop.rs | 12 +- src/platform_impl/orbital/mod.rs | 28 --- src/platform_impl/orbital/window.rs | 17 +- .../web/event_loop/window_target.rs | 10 +- src/platform_impl/web/monitor.rs | 68 +++--- src/platform_impl/web/web_sys/canvas.rs | 10 +- src/platform_impl/web/web_sys/fullscreen.rs | 5 +- src/platform_impl/web/window.rs | 25 +- src/platform_impl/windows/event_loop.rs | 25 +- src/platform_impl/windows/mod.rs | 1 - src/platform_impl/windows/monitor.rs | 126 +++++------ src/platform_impl/windows/window.rs | 46 ++-- src/platform_impl/windows/window_state.rs | 3 +- src/window.rs | 18 +- 43 files changed, 726 insertions(+), 826 deletions(-) diff --git a/examples/application.rs b/examples/application.rs index c0587fab..9addc5b9 100644 --- a/examples/application.rs +++ b/examples/application.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/changelog/unreleased.md b/src/changelog/unreleased.md index e8344b53..2690fc64 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -72,6 +72,7 @@ changelog entry. - On X11, Wayland, Windows and macOS, improved scancode conversions for more obscure key codes. - Add ability to make non-activating window on macOS using `NSPanel` with `NSWindowStyleMask::NonactivatingPanel`. - On Windows, add `IconExtWindows::from_resource_name`. +- Implement `MonitorHandleProvider` for `MonitorHandle` to access common monitor API. ### Changed @@ -189,6 +190,7 @@ changelog entry. - On macOS, no longer need control of the main `NSApplication` class (which means you can now override it yourself). - Removed `KeyEventExtModifierSupplement`, and made the fields `text_with_all_modifiers` and `key_without_modifiers` public on `KeyEvent` instead. +- Move `window::Fullscreen` to `monitor::Fullscreen`. ### Removed diff --git a/src/monitor.rs b/src/monitor.rs index 2c8c58e5..91c6406a 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -1,57 +1,18 @@ //! Types useful for interacting with a user's monitors. +//! +//! If you want to get basic information about a monitor, you can use the +//! [`MonitorHandle`] type. This is retrieved from one of the following +//! 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 retrieved with [`MonitorHandle::video_modes()`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct VideoMode { - pub(crate) size: PhysicalSize, - pub(crate) bit_depth: Option, - pub(crate) refresh_rate_millihertz: Option, -} - -impl 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)?; - - if let Some(refresh_rate) = self.refresh_rate_millihertz { - write!(f, "@{refresh_rate}mHz")?; - } - - if let Some(bit_depth) = self.bit_depth { - write!(f, " ({bit_depth} bpp)")?; - } - - Ok(()) - } -} +use crate::utils::AsAny; /// Handle to a monitor. /// @@ -83,21 +44,48 @@ impl fmt::Display for VideoMode { /// 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 std::fmt::Debug for MonitorHandle { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.inner.fmt(f) +impl Deref for MonitorHandle { + type Target = dyn MonitorHandleProvider; + + fn deref(&self) -> &Self::Target { + self.0.as_ref() } } -impl MonitorHandle { +impl PartialEq for MonitorHandle { + fn eq(&self, other: &Self) -> bool { + self.0.as_ref().eq(other.0.as_ref()) + } +} + +impl Eq for MonitorHandle {} + +/// Provider of the [`MonitorHandle`]. +pub trait MonitorHandleProvider: AsAny + fmt::Debug + Send + Sync { + /// Identifier for this monitor. + /// + /// The representation of this modifier is not guaranteed and should be used only to compare + /// monitors. + fn id(&self) -> u128; + + /// Native platform identifier of this monitor. + /// + /// # Platform-specific + /// + /// - **Windows**: This is `HMONITOR`. + /// - **macOS**: This is `CGDirectDisplayID`. + /// - **iOS**: This is `UIScreen*`. + /// - **Wayland**: This is the ID of the `wl_output` device. + /// - **X11**: This is the ID of the CRTC. + /// - **Web**: This is an internal ID not meant for consumption. + 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 /// @@ -107,10 +95,7 @@ impl MonitorHandle { 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() - } + fn name(&self) -> Option>; /// Returns the top-left corner position of the monitor in desktop coordinates. /// @@ -126,10 +111,7 @@ impl MonitorHandle { 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() - } + 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`]. @@ -150,20 +132,72 @@ impl MonitorHandle { /// #[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() - } + 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() + fn video_modes(&self) -> Box>; +} + +impl PartialEq for dyn MonitorHandleProvider + '_ { + fn eq(&self, other: &Self) -> bool { + self.id() == other.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 5f5b221b..e3c09ea8 100644 --- a/src/platform/ios.rs +++ b/src/platform/ios.rs @@ -108,6 +108,7 @@ use std::os::raw::c_void; use serde::{Deserialize, Serialize}; 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`]. @@ -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::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) -> VideoMode { - self.inner.preferred_video_mode() + 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 bb0f8b1f..fec09008 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -73,6 +73,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. @@ -499,22 +500,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::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/wayland.rs b/src/platform/wayland.rs index 84af0038..bcdcccbc 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 28227662..59b7b30b 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 05172b77..bfefa729 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 2c4cef75..9e70a298 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 96306560..fe43dc5c 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -20,12 +20,11 @@ use crate::event_loop::{ EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider, OwnedDisplayHandle as CoreOwnedDisplayHandle, }; -use crate::monitor::{MonitorHandle as RootMonitorHandle, VideoMode}; +use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle}; 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; @@ -700,11 +699,11 @@ impl RootActiveEventLoop for ActiveEventLoop { Err(NotSupportedError::new("create_custom_cursor is not supported").into()) } - fn available_monitors(&self) -> Box> { + fn available_monitors(&self) -> Box> { Box::new(std::iter::empty()) } - fn primary_monitor(&self) -> Option { + fn primary_monitor(&self) -> Option { None } @@ -824,15 +823,15 @@ impl CoreWindow for Window { GLOBAL_WINDOW } - fn primary_monitor(&self) -> Option { + fn primary_monitor(&self) -> Option { None } - fn available_monitors(&self) -> Box> { + fn available_monitors(&self) -> Box> { Box::new(std::iter::empty()) } - fn current_monitor(&self) -> Option { + fn current_monitor(&self) -> Option { None } @@ -1018,31 +1017,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!() - } -} - 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 179df8f1..0ddf61b2 100644 --- a/src/platform_impl/apple/appkit/event_loop.rs +++ b/src/platform_impl/apple/appkit/event_loop.rs @@ -29,7 +29,7 @@ use crate::event_loop::{ ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopProxy as CoreEventLoopProxy, 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; @@ -114,13 +114,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 b1669c91..19cb782c 100644 --- a/src/platform_impl/apple/appkit/mod.rs +++ b/src/platform_impl/apple/appkit/mod.rs @@ -24,4 +24,3 @@ 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 a4141111..6d65bc5e 100644 --- a/src/platform_impl/apple/appkit/monitor.rs +++ b/src/platform_impl/apple/appkit/monitor.rs @@ -29,7 +29,7 @@ use objc2_foundation::{ns_string, NSNumber, NSPoint, NSRect}; use super::ffi; use super::util::cgerr; use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; -use crate::monitor::VideoMode; +use crate::monitor::{MonitorHandleProvider, VideoMode}; #[derive(Clone)] pub struct VideoModeHandle { @@ -54,7 +54,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("VideoMode") + f.debug_struct("VideoModeHandle") .field("mode", &self.mode) .field("monitor", &self.monitor) .finish() @@ -106,10 +106,113 @@ pub struct MonitorHandle(CGDirectDisplayID); impl MonitorHandle { /// Internal comparisons of [`MonitorHandle`]s are done first requesting a UUID for the handle. - fn uuid(&self) -> [u8; 16] { + fn uuid(&self) -> u128 { let ptr = unsafe { ffi::CGDisplayCreateUUIDFromDisplayID(self.0) }; let cf_uuid = unsafe { CFRetained::from_raw(NonNull::new(ptr).unwrap()) }; - unsafe { CFUUIDGetUUIDBytes(&cf_uuid) }.into() + u128::from_ne_bytes(unsafe { CFUUIDGetUUIDBytes(&cf_uuid) }.into()) + } + + pub fn new(id: CGDirectDisplayID) -> Self { + MonitorHandle(id) + } + + fn refresh_rate_millihertz(&self) -> Option { + let current_display_mode = + NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) }.unwrap()); + refresh_rate_millihertz(self.0, ¤t_display_mode) + } + + 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 = CGDisplayCopyAllDisplayModes(self.0, None) + .expect("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 CGDisplayMode; + CFRetained::retain(NonNull::new(mode).unwrap()) + }) + .collect(); + modes + }; + + modes.into_iter().map(move |mode| { + let cg_refresh_rate_hertz = CGDisplayModeGetRefreshRate(Some(&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 = self.uuid(); + NSScreen::screens(mtm).into_iter().find(|screen| { + let other_native_id = get_display_id(screen); + let other = MonitorHandle::new(other_native_id); + uuid == other.uuid() + }) + } +} + +impl MonitorHandleProvider for MonitorHandle { + fn id(&self) -> u128 { + self.uuid() + } + + fn native_id(&self) -> u64 { + self.0 as _ + } + + // TODO: Be smarter about this: + // + // + fn name(&self) -> Option> { + let screen_num = unsafe { CGDisplayModelNumber(self.0) }; + 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 mode = NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) }.unwrap()); + let refresh_rate_millihertz = refresh_rate_millihertz(self.0, &mode); + Some(VideoModeHandle::new(self.clone(), mode, refresh_rate_millihertz).mode) + } + + fn video_modes(&self) -> Box> { + Box::new(self.video_mode_handles().map(|mode| mode.mode)) } } @@ -175,113 +278,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 screen_num = unsafe { CGDisplayModelNumber(self.0) }; - 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.0) }; - 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) }.unwrap()); - refresh_rate_millihertz(self.0, ¤t_display_mode) - } - - pub fn current_video_mode(&self) -> Option { - let mode = NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) }.unwrap()); - let refresh_rate_millihertz = refresh_rate_millihertz(self.0, &mode); - Some(VideoModeHandle::new(self.clone(), mode, refresh_rate_millihertz).mode) - } - - pub fn video_modes(&self) -> impl Iterator { - self.video_modes_handles().map(|handle| handle.mode) - } - - pub(crate) fn video_modes_handles(&self) -> impl Iterator { - let refresh_rate_millihertz = self.refresh_rate_millihertz(); - let monitor = self.clone(); - - unsafe { - let modes = { - let array = CGDisplayCopyAllDisplayModes(self.0, None) - .expect("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 CGDisplayMode; - CFRetained::retain(NonNull::new(mode).unwrap()) - }) - .collect(); - modes - }; - - modes.into_iter().map(move |mode| { - let cg_refresh_rate_hertz = CGDisplayModeGetRefreshRate(Some(&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 = self.uuid(); - NSScreen::screens(mtm).into_iter().find(|screen| { - let other_native_id = get_display_id(screen); - let other = MonitorHandle::new(other_native_id); - 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 0f1c5ab1..5e66bd40 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 dispatch2::MainThreadBound; use dpi::{Position, Size}; use objc2::rc::{autoreleasepool, Retained}; @@ -10,10 +12,10 @@ use objc2_foundation::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, }; #[derive(Debug)] @@ -206,11 +208,11 @@ impl CoreWindow for Window { } fn set_fullscreen(&self, fullscreen: Option) { - self.maybe_wait_on_main(|delegate| delegate.set_fullscreen(fullscreen.map(Into::into))) + self.maybe_wait_on_main(|delegate| delegate.set_fullscreen(fullscreen)) } fn fullscreen(&self) -> Option { - self.maybe_wait_on_main(|delegate| delegate.fullscreen().map(Into::into)) + self.maybe_wait_on_main(|delegate| delegate.fullscreen()) } fn set_decorations(&self, decorations: bool) { @@ -307,21 +309,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 7ef8de41..ef469dcb 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -42,13 +42,14 @@ use super::observer::RunLoop; use super::util::cgerr; use super::view::WinitView; use super::window::{window_id, WinitPanel, WinitWindow}; -use super::{ffi, Fullscreen, MonitorHandle}; +use super::{ffi, MonitorHandle}; use crate::dpi::{ LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, 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, @@ -257,7 +258,9 @@ define_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)); }, } @@ -550,9 +553,10 @@ fn new_window( mtm: MainThreadMarker, ) -> Option> { autoreleasepool(|_| { - let screen = match attrs.fullscreen.clone().map(Into::into) { + let screen = match attrs.fullscreen.clone() { Some(Fullscreen::Borderless(Some(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), @@ -871,7 +875,7 @@ impl WindowDelegate { delegate.set_cursor(attrs.cursor); // Set fullscreen mode after we setup everything - delegate.set_fullscreen(attrs.fullscreen.map(Into::into)); + delegate.set_fullscreen(attrs.fullscreen); // Setting the window as key has to happen *after* we set the fullscreen // state, since otherwise we'll briefly see the window at normal size @@ -1455,17 +1459,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(monitor, _) => monitor.clone(), } - .ns_screen(mtm) .unwrap(); let old_screen = self.window().screen().unwrap(); @@ -1487,7 +1492,7 @@ impl WindowDelegate { // parameter, which is not consistent with the docs saying that it // takes a `NSDictionary`.. - let display_id = monitor.native_identifier(); + let display_id = monitor.native_id() as _; let mut fade_token = ffi::kCGDisplayFadeReservationInvalidToken; @@ -1514,8 +1519,9 @@ impl WindowDelegate { cgerr(CGDisplayCapture(display_id)).unwrap(); } + let monitor = monitor.as_any().downcast_ref::().unwrap(); let video_mode = - match monitor.video_modes_handles().find(|mode| &mode.mode == video_mode) { + match monitor.video_mode_handles().find(|mode| &mode.mode == video_mode) { Some(video_mode) => video_mode, None => return, }; @@ -1580,7 +1586,8 @@ impl WindowDelegate { // State is restored by `window_did_exit_fullscreen` toggle_fullscreen(self.window()); }, - (Some(Fullscreen::Exclusive(ref monitor, _)), None) => { + (Some(Fullscreen::Exclusive(monitor, _)), None) => { + let monitor = monitor.as_any().downcast_ref::().unwrap(); restore_and_release_display(monitor); toggle_fullscreen(self.window()); }, @@ -1603,7 +1610,7 @@ impl WindowDelegate { let window_level = unsafe { CGShieldingWindowLevel() } as NSWindowLevel + 1; self.window().setLevel(window_level); }, - (Some(Fullscreen::Exclusive(ref monitor, _)), Some(Fullscreen::Borderless(_))) => { + (Some(Fullscreen::Exclusive(monitor, _)), Some(Fullscreen::Borderless(_))) => { let presentation_options = self.ivars().save_presentation_opts.get().unwrap_or( NSApplicationPresentationOptions::FullScreen | NSApplicationPresentationOptions::AutoHideDock @@ -1611,6 +1618,7 @@ impl WindowDelegate { ); app.setPresentationOptions(presentation_options); + let monitor = monitor.as_any().downcast_ref::().unwrap(); restore_and_release_display(monitor); // Restore the normal window level following the Borderless fullscreen @@ -1815,11 +1823,11 @@ fn restore_and_release_display(monitor: &MonitorHandle) { if available_monitors.contains(monitor) { unsafe { CGRestorePermanentDisplayConfiguration(); - cgerr(CGDisplayRelease(monitor.native_identifier())).unwrap(); + cgerr(CGDisplayRelease(monitor.native_id() as _)).unwrap(); }; } 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 a86ceda8..97ac4212 100644 --- a/src/platform_impl/apple/uikit/event_loop.rs +++ b/src/platform_impl/apple/uikit/event_loop.rs @@ -28,7 +28,7 @@ use crate::event_loop::{ ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopProxy as CoreEventLoopProxy, 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}; @@ -56,14 +56,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 44924a67..392507cb 100644 --- a/src/platform_impl/apple/uikit/mod.rs +++ b/src/platform_impl/apple/uikit/mod.rs @@ -18,7 +18,6 @@ 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)] pub enum OsError {} diff --git a/src/platform_impl/apple/uikit/monitor.rs b/src/platform_impl/apple/uikit/monitor.rs index 81fe054d..c72b09e0 100644 --- a/src/platform_impl/apple/uikit/monitor.rs +++ b/src/platform_impl/apple/uikit/monitor.rs @@ -11,7 +11,7 @@ use objc2_foundation::NSInteger; use objc2_ui_kit::{UIScreen, UIScreenMode}; use crate::dpi::PhysicalPosition; -use crate::monitor::VideoMode; +use crate::monitor::{MonitorHandleProvider, VideoMode}; // Workaround for `MainThreadBound` implementing almost no traits #[derive(Debug)] @@ -76,6 +76,60 @@ pub struct MonitorHandle { ui_screen: MainThreadBound>, } +impl MonitorHandleProvider for MonitorHandle { + fn id(&self) -> u128 { + self.native_id() as _ + } + + fn native_id(&self) -> u64 { + // SAFETY: Only getting the pointer. + let mtm = unsafe { MainThreadMarker::new_unchecked() }; + Retained::as_ptr(self.ui_screen.get(mtm)) as u64 + } + + fn name(&self) -> Option> { + run_on_main(|mtm| { + #[allow(deprecated)] + let main = UIScreen::mainScreen(mtm); + if *self.ui_screen(mtm) == main { + Some("Primary".into()) + } else if Some(self.ui_screen(mtm)) == main.mirroredScreen().as_ref() { + Some("Mirrored".into()) + } else { + #[allow(deprecated)] + UIScreen::screens(mtm) + .iter() + .position(|rhs| rhs == *self.ui_screen(mtm)) + .map(|idx| idx.to_string().into()) + } + }) + } + + 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()) + } + + fn scale_factor(&self) -> f64 { + self.ui_screen.get_on_main(|ui_screen| ui_screen.nativeScale()) as f64 + } + + 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 + })) + } + + fn video_modes(&self) -> Box> { + Box::new(self.video_modes()) + } +} + impl Clone for MonitorHandle { fn clone(&self) -> Self { run_on_main(|mtm| Self { @@ -137,44 +191,6 @@ impl MonitorHandle { Self { ui_screen: MainThreadBound::new(ui_screen, mtm) } } - pub 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()) - } else if Some(self.ui_screen(mtm)) == main.mirroredScreen().as_ref() { - Some("Mirrored".to_string()) - } else { - #[allow(deprecated)] - UIScreen::screens(mtm) - .iter() - .position(|rhs| rhs == *self.ui_screen(mtm)) - .map(|idx| idx.to_string()) - } - }) - } - - pub 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 { - self.ui_screen.get_on_main(|ui_screen| ui_screen.nativeScale()) as f64 - } - - pub fn current_video_mode(&self) -> Option { - Some(run_on_main(|mtm| { - VideoModeHandle::new( - self.ui_screen(mtm).clone(), - self.ui_screen(mtm).currentMode().unwrap(), - mtm, - ) - .mode - })) - } - pub fn video_modes_handles(&self) -> impl Iterator { run_on_main(|mtm| { let ui_screen = self.ui_screen(mtm); diff --git a/src/platform_impl/apple/uikit/window.rs b/src/platform_impl/apple/uikit/window.rs index 85248de5..ef414af4 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 dispatch2::MainThreadBound; use objc2::rc::Retained; @@ -16,7 +17,7 @@ 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::{ LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize, @@ -25,7 +26,7 @@ use crate::dpi::{ use crate::error::{NotSupportedError, RequestError}; use crate::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, @@ -78,8 +79,9 @@ impl WinitUIWindow { this.setRootViewController(Some(view_controller)); - match window_attributes.fullscreen.clone().map(Into::into) { - Some(Fullscreen::Exclusive(ref monitor, ref video_mode)) => { + match window_attributes.fullscreen.clone() { + Some(Fullscreen::Exclusive(monitor, ref video_mode)) => { + let monitor = monitor.as_any().downcast_ref::().unwrap(); let screen = monitor.ui_screen(mtm); if let Some(video_mode) = monitor.video_modes_handles().find(|mode| &mode.mode == video_mode) @@ -89,6 +91,7 @@ impl WinitUIWindow { 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,6 +306,7 @@ impl Inner { let mtm = MainThreadMarker::new().unwrap(); let uiscreen = match &monitor { 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_modes_handles().find(|mode| &mode.mode == video_mode) @@ -311,7 +315,9 @@ impl Inner { } 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() }, @@ -349,7 +355,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 } @@ -482,10 +488,13 @@ impl Window { #[allow(deprecated)] let main_screen = UIScreen::mainScreen(mtm); - let fullscreen = window_attributes.fullscreen.clone().map(Into::into); + let fullscreen = window_attributes.fullscreen.clone(); let screen = match fullscreen { - Some(Fullscreen::Exclusive(ref monitor, _)) => 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, }; @@ -670,12 +679,12 @@ impl CoreWindow for Window { self.maybe_wait_on_main(|delegate| delegate.is_maximized()) } - fn set_fullscreen(&self, fullscreen: Option) { - self.maybe_wait_on_main(|delegate| delegate.set_fullscreen(fullscreen.map(Into::into))) + fn set_fullscreen(&self, fullscreen: Option) { + self.maybe_wait_on_main(|delegate| delegate.set_fullscreen(fullscreen)) } - fn fullscreen(&self) -> Option { - self.maybe_wait_on_main(|delegate| delegate.fullscreen().map(Into::into)) + fn fullscreen(&self) -> Option { + self.maybe_wait_on_main(|delegate| delegate.fullscreen()) } fn set_decorations(&self, decorations: bool) { @@ -771,21 +780,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 eb76a9c0..8d6d57ec 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -14,13 +14,11 @@ pub(crate) use self::common::xkb::{physicalkey_to_scancode, scancode_to_physical use self::x11::{XConnection, XError, XNotSupported}; use crate::application::ApplicationHandler; pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource; -use crate::dpi::PhysicalPosition; #[cfg(x11_platform)] use crate::dpi::Size; use crate::error::{EventLoopError, NotSupportedError}; use crate::event_loop::ActiveEventLoop; pub(crate) use crate::icon::RgbaIcon as PlatformIcon; -use crate::monitor::VideoMode; use crate::platform::pump_events::PumpStatus; #[cfg(x11_platform)] use crate::platform::x11::{WindowType as XWindowType, XlibErrorHook}; @@ -104,14 +102,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 @@ -140,38 +130,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(Clone, Debug, Eq, Hash, PartialEq)] pub(crate) enum PlatformCustomCursor { #[cfg(wayland_platform)] diff --git a/src/platform_impl/linux/wayland/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs index 9d1270ca..4c6e1d6a 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}; @@ -621,19 +623,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 856a5f19..6276a870 100644 --- a/src/platform_impl/linux/wayland/mod.rs +++ b/src/platform_impl/linux/wayland/mod.rs @@ -1,10 +1,7 @@ //! Winit's Wayland backend. -pub use event_loop::{ActiveEventLoop, EventLoop}; -pub use output::MonitorHandle; use sctk::reexports::client::protocol::wl_surface::WlSurface; use sctk::reexports::client::Proxy; -pub use window::Window; pub(super) use crate::cursor::OnlyCursorImage as CustomCursor; use crate::dpi::{LogicalSize, PhysicalSize}; @@ -17,6 +14,9 @@ mod state; mod types; mod window; +pub use event_loop::{ActiveEventLoop, EventLoop}; +pub use window::Window; + /// Get the WindowId out of the surface. #[inline] fn make_wid(surface: &WlSurface) -> WindowId { diff --git a/src/platform_impl/linux/wayland/output.rs b/src/platform_impl/linux/wayland/output.rs index eb1b8267..c4dedc5b 100644 --- a/src/platform_impl/linux/wayland/output.rs +++ b/src/platform_impl/linux/wayland/output.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::num::NonZeroU32; use sctk::output::{Mode, OutputData}; @@ -5,7 +6,7 @@ use sctk::reexports::client::protocol::wl_output::WlOutput; use sctk::reexports::client::Proxy; use crate::dpi::{LogicalPosition, PhysicalPosition}; -use crate::monitor::VideoMode; +use crate::monitor::{MonitorHandleProvider as CoreMonitorHandle, VideoMode}; #[derive(Clone, Debug)] pub struct MonitorHandle { @@ -17,21 +18,24 @@ impl MonitorHandle { pub(crate) fn new(proxy: WlOutput) -> Self { Self { proxy } } +} - #[inline] - pub fn name(&self) -> Option { - let output_data = self.proxy.data::().unwrap(); - output_data.with_output_info(|info| info.name.clone()) +impl CoreMonitorHandle for MonitorHandle { + fn id(&self) -> u128 { + self.native_id() as _ } - #[inline] - pub fn native_identifier(&self) -> u32 { + fn native_id(&self) -> u64 { let output_data = self.proxy.data::().unwrap(); - output_data.with_output_info(|info| info.id) + output_data.with_output_info(|info| info.id as u64) } - #[inline] - pub fn position(&self) -> Option> { + fn name(&self) -> Option> { + let output_data = self.proxy.data::().unwrap(); + output_data.with_output_info(|info| info.name.clone().map(Cow::Owned)) + } + + 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,56 +51,37 @@ 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(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()); - modes.into_iter().map(wayland_mode_to_core_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); - } -} - -/// Convert Wayland's [`Mode`] to winit's [`VideoMode`]. +/// 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(), diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index df449d86..cbcac9e5 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, PhysicalInsets, PhysicalPosition, PhysicalSize, Po 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,19 +139,20 @@ impl Window { window_state.set_resizable(attributes.resizable); // Set startup mode. - match attributes.fullscreen.map(Into::into) { + match attributes.fullscreen { 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(), _ => (), @@ -437,26 +438,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() @@ -468,7 +470,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 } @@ -628,8 +630,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> { @@ -639,8 +640,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 41cd999e..eb3e064e 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; @@ -672,23 +673,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 a3044bdb..28b721f8 100644 --- a/src/platform_impl/linux/x11/monitor.rs +++ b/src/platform_impl/linux/x11/monitor.rs @@ -6,7 +6,7 @@ use x11rb::protocol::xproto; use super::{util, X11Error, XConnection}; use crate::dpi::PhysicalPosition; -use crate::monitor::VideoMode; +use crate::monitor::{MonitorHandleProvider, VideoMode}; // Used for testing. This should always be committed as false. const DISABLE_MONITOR_LIST_CACHING: bool = false; @@ -23,7 +23,6 @@ pub struct VideoModeHandle { pub(crate) current: bool, pub(crate) mode: VideoMode, pub(crate) native_mode: randr::Mode, - pub(crate) monitor: Option, } impl From for VideoMode { @@ -50,6 +49,36 @@ pub struct MonitorHandle { pub(crate) video_modes: Vec, } +impl MonitorHandleProvider for MonitorHandle { + fn id(&self) -> u128 { + self.native_id() as _ + } + + 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 { fn eq(&self, other: &Self) -> bool { self.id == other.id @@ -121,34 +150,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(Into::into) - } - - #[inline] - pub fn video_modes(&self) -> impl Iterator { - self.video_modes.clone().into_iter().map(Into::into) - } } impl XConnection { diff --git a/src/platform_impl/linux/x11/util/randr.rs b/src/platform_impl/linux/x11/util/randr.rs index 5d0b37a2..5a2a1c69 100644 --- a/src/platform_impl/linux/x11/util/randr.rs +++ b/src/platform_impl/linux/x11/util/randr.rs @@ -83,19 +83,14 @@ 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, - mode: VideoMode { - 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, - // 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, + mode: VideoMode { + 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 7d7f7442..f4ee7bae 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; @@ -26,14 +27,15 @@ use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size} use crate::error::{NotSupportedError, RequestError}; use crate::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, -}; +use crate::platform_impl::{common, PlatformCustomCursor, PlatformIcon}; use crate::window::{ CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow, WindowAttributes, WindowButtons, WindowId, WindowLevel, @@ -179,12 +181,12 @@ impl CoreWindow for Window { self.0.is_maximized() } - fn set_fullscreen(&self, fullscreen: Option) { - self.0.set_fullscreen(fullscreen.map(Into::into)) + fn set_fullscreen(&self, fullscreen: Option) { + self.0.set_fullscreen(fullscreen) } - fn fullscreen(&self) -> Option { - self.0.fullscreen().map(Into::into) + fn fullscreen(&self) -> Option { + self.0.fullscreen() } fn set_decorations(&self, decorations: bool) { @@ -275,28 +277,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 { @@ -862,8 +857,7 @@ impl UnownedWindow { if window_attrs.fullscreen.is_some() { if let Some(flusher) = - leap!(window - .set_fullscreen_inner(window_attrs.fullscreen.clone().map(Into::into))) + leap!(window.set_fullscreen_inner(window_attrs.fullscreen.clone())) { flusher.ignore_error() } @@ -1039,7 +1033,7 @@ impl UnownedWindow { // to the desktop video mode as macOS and Windows do (&None, &Some(Fullscreen::Exclusive(ref monitor, _))) | (&Some(Fullscreen::Borderless(_)), &Some(Fullscreen::Exclusive(ref monitor, _))) => { - let id = monitor.native_identifier(); + let id = monitor.native_id() as _; shared_state_lock.desktop_video_mode = Some(( id, self.xconn.get_crtc_mode(id).expect("Failed to get desktop video mode"), @@ -1070,19 +1064,22 @@ impl UnownedWindow { flusher.map(Some) }, Some(fullscreen) => { - let (video_mode, monitor) = match fullscreen { - Fullscreen::Exclusive(PlatformMonitorHandle::X(monitor), video_mode) => { - (Some(video_mode), monitor.clone()) - }, - 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() { @@ -1091,7 +1088,7 @@ impl UnownedWindow { if let Some(native_mode) = video_mode.and_then(|requested| { monitor.video_modes.iter().find_map(|mode| { - if mode.mode == requested { + if &mode.mode == requested { Some(mode.native_mode) } else { None @@ -1124,7 +1121,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, 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 498c61b2..9ce2fa6f 100644 --- a/src/platform_impl/mod.rs +++ b/src/platform_impl/mod.rs @@ -1,6 +1,3 @@ -use crate::monitor::{MonitorHandle as RootMonitorHandle, VideoMode}; -use crate::window::Fullscreen as RootFullscreen; - #[cfg(android_platform)] mod android; #[cfg(target_vendor = "apple")] @@ -29,40 +26,6 @@ use self::web as platform; #[cfg(windows_platform)] use self::windows as platform; -/// Helper for converting between platform-specific and generic -/// [`VideoMode`]/[`MonitorHandle`] -#[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) enum Fullscreen { - Exclusive(MonitorHandle, VideoMode), - Borderless(Option), -} - -impl From for Fullscreen { - fn from(f: RootFullscreen) -> Self { - match f { - RootFullscreen::Exclusive(handle, video_mode) => { - Self::Exclusive(handle.inner, video_mode) - }, - RootFullscreen::Borderless(Some(handle)) => Self::Borderless(Some(handle.inner)), - RootFullscreen::Borderless(None) => Self::Borderless(None), - } - } -} - -impl From for RootFullscreen { - fn from(f: Fullscreen) -> Self { - match f { - Fullscreen::Exclusive(inner, video_mode) => { - Self::Exclusive(RootMonitorHandle { inner }, 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 a1c04a2a..29b0e430 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::{ @@ -11,9 +11,7 @@ use orbclient::{ }; use smol_str::SmolStr; -use super::{ - MonitorHandle, PlatformSpecificEventLoopAttributes, RedoxSocket, TimeSocket, WindowProperties, -}; +use super::{PlatformSpecificEventLoopAttributes, RedoxSocket, TimeSocket, WindowProperties}; use crate::application::ApplicationHandler; use crate::error::{EventLoopError, NotSupportedError, RequestError}; use crate::event::{self, Ime, Modifiers, StartCause}; @@ -711,9 +709,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 +717,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 9f717106..89b63988 100644 --- a/src/platform_impl/orbital/mod.rs +++ b/src/platform_impl/orbital/mod.rs @@ -4,8 +4,6 @@ use std::{fmt, str}; pub(crate) use self::event_loop::{ActiveEventLoop, EventLoop}; pub use self::window::Window; -use crate::dpi::PhysicalPosition; -use crate::monitor::VideoMode; mod event_loop; mod window; @@ -133,29 +131,3 @@ impl fmt::Display for WindowProperties<'_> { ) } } - -#[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) - None - } - - pub fn video_modes(&self) -> impl Iterator { - std::iter::empty() - } -} diff --git a/src/platform_impl/orbital/window.rs b/src/platform_impl/orbital/window.rs index 72e3a7f3..5a69d5bb 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::{PhysicalInsets, 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 @@ -33,7 +34,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() @@ -161,22 +162,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 a5a26685..7a766248 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -18,7 +18,7 @@ use crate::event_loop::{ EventLoopProxy as RootEventLoopProxy, OwnedDisplayHandle as CoreOwnedDisplayHandle, }; use crate::keyboard::ModifiersState; -use crate::monitor::MonitorHandle as RootMonitorHandle; +use crate::monitor::MonitorHandle as CoremMonitorHandle; use crate::platform::web::{CustomCursorFuture, PollStrategy, WaitUntilStrategy}; use crate::platform_impl::platform::cursor::CustomCursor; use crate::platform_impl::web::event_loop::proxy::EventLoopProxy; @@ -502,18 +502,18 @@ impl RootActiveEventLoop for ActiveEventLoop { Ok(RootCustomCursor { inner: CustomCursor::new(self, source.inner) }) } - fn available_monitors(&self) -> Box> { + fn available_monitors(&self) -> Box> { Box::new( self.runner .monitor() .available_monitors() .into_iter() - .map(|inner| RootMonitorHandle { inner }), + .map(|monitor| CoremMonitorHandle(Arc::new(monitor))), ) } - fn primary_monitor(&self) -> Option { - self.runner.monitor().primary_monitor().map(|inner| RootMonitorHandle { inner }) + fn primary_monitor(&self) -> Option { + self.runner.monitor().primary_monitor().map(|monitor| CoremMonitorHandle(Arc::new(monitor))) } fn listen_device_events(&self, allowed: DeviceEvents) { diff --git a/src/platform_impl/web/monitor.rs b/src/platform_impl/web/monitor.rs index 7a0612fd..71f9391f 100644 --- a/src/platform_impl/web/monitor.rs +++ b/src/platform_impl/web/monitor.rs @@ -8,7 +8,7 @@ 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; @@ -28,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, VideoMode}; +use crate::monitor::{MonitorHandle as CoreMonitorHandle, MonitorHandleProvider, VideoMode}; use crate::platform::web::{ MonitorPermissionError, Orientation, OrientationData, OrientationLock, OrientationLockError, }; @@ -46,30 +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(VideoMode { - size: self.inner.queue(|inner| inner.size()), - bit_depth: self.inner.queue(|inner| inner.bit_depth()), - refresh_rate_millihertz: None, - }) - } - - pub fn video_modes(&self) -> impl Iterator { - self.current_video_mode().into_iter() - } - pub fn orientation(&self) -> OrientationData { self.inner.queue(|inner| inner.orientation()) } @@ -143,6 +119,40 @@ impl MonitorHandle { } } +impl MonitorHandleProvider for MonitorHandle { + fn id(&self) -> u128 { + self.native_id() as _ + } + + 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) = @@ -192,9 +202,9 @@ impl PartialOrd for MonitorHandle { } } -impl From for RootMonitorHandle { - fn from(inner: MonitorHandle) -> Self { - RootMonitorHandle { inner } +impl From for CoreMonitorHandle { + fn from(monitor: MonitorHandle) -> Self { + CoreMonitorHandle(Arc::new(monitor)) } } diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 8de54112..c4ea043a 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -27,7 +27,7 @@ use crate::event::{ SurfaceSizeWriter, WindowEvent, }; use crate::keyboard::{Key, KeyLocation, ModifiersState, PhysicalKey}; -use crate::platform_impl::Fullscreen; +use crate::monitor::Fullscreen; use crate::window::{WindowAttributes, WindowId}; #[allow(dead_code)] @@ -149,13 +149,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 11255833..5c688604 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, @@ -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 9ae56121..4a31c2d1 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -13,11 +13,10 @@ use super::{backend, lock, ActiveEventLoop}; use crate::dpi::{LogicalInsets, PhysicalInsets, 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 CoremMonitorHandle}; 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 { @@ -270,20 +269,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 } @@ -396,21 +395,21 @@ impl RootWindow for Window { Err(NotSupportedError::new("set_cursor_hittest is not supported").into()) } - fn current_monitor(&self) -> Option { + fn current_monitor(&self) -> Option { Some(self.inner.queue(|inner| inner.monitor.current_monitor()).into()) } - fn available_monitors(&self) -> Box> { + fn available_monitors(&self) -> Box> { Box::new( self.inner .queue(|inner| inner.monitor.available_monitors()) .into_iter() - .map(RootMonitorHandle::from), + .map(CoremMonitorHandle::from), ) } - fn primary_monitor(&self) -> Option { - self.inner.queue(|inner| inner.monitor.primary_monitor()).map(RootMonitorHandle::from) + fn primary_monitor(&self) -> Option { + self.inner.queue(|inner| inner.monitor.primary_monitor()).map(CoremMonitorHandle::from) } fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle { diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index bb4ec8d2..4e3272f7 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -75,7 +75,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}; @@ -89,7 +89,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::{ @@ -425,16 +425,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 { @@ -1221,7 +1221,7 @@ unsafe fn public_window_callback_inner( if !new_monitor.is_null() && 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) @@ -1232,12 +1232,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 monitor, _) => { - let old_monitor = 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 6fa7f4a7..b7dc2b2d 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -11,7 +11,6 @@ pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSourc use crate::event::DeviceId; use crate::icon::Icon; 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 40304dd1..1e4179df 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -1,7 +1,7 @@ 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,7 +13,7 @@ use windows_sys::Win32::Graphics::Gdi::{ use super::util::decode_wide; use crate::dpi::{PhysicalPosition, PhysicalSize}; -use crate::monitor::VideoMode; +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; @@ -60,14 +60,6 @@ impl VideoModeHandle { } } -#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] -pub struct MonitorHandle(HMONITOR); - -// Send and Sync are not implemented for HMONITOR, we have to wrap it and implement them manually. - -unsafe impl Send for MonitorHandle {} -unsafe impl Sync for MonitorHandle {} - unsafe extern "system" fn monitor_enum_proc( hmonitor: HMONITOR, _hdc: HDC, @@ -116,27 +108,19 @@ pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result Self { 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()) - } - - #[inline] - pub fn native_identifier(&self) -> String { - self.name().unwrap() - } - - #[inline] - pub fn hmonitor(&self) -> HMONITOR { - self.0 - } - pub(crate) fn size(&self) -> PhysicalSize { let rc_monitor = get_monitor_info(self.0).unwrap().monitorInfo.rcMonitor; PhysicalSize { @@ -145,39 +129,7 @@ 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(mode).mode) - } - } - } - - pub(crate) fn video_mode_handles(&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 @@ -187,7 +139,7 @@ impl MonitorHandle { Ok(monitor_info) => monitor_info, Err(error) => { tracing::warn!("Error from get_monitor_info: {error}"); - return modes.into_iter(); + return Box::new(iter::empty()); }, }; @@ -206,10 +158,54 @@ impl MonitorHandle { i += 1; } - modes.into_iter() - } - - pub fn video_modes(&self) -> impl Iterator { - self.video_mode_handles().map(|mode| mode.mode) + Box::new(modes.into_iter()) + } +} + +impl MonitorHandleProvider for MonitorHandle { + fn id(&self) -> u128 { + self.native_id() as _ + } + + 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 3134ad05..b02c5592 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}; @@ -46,11 +47,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::{PhysicalInsets, 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::{ @@ -69,11 +71,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, }; #[derive(Clone, Copy, Debug)] @@ -352,7 +353,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); } @@ -755,13 +756,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) + window_state.fullscreen.clone() } - fn set_fullscreen(&self, fullscreen: Option) { - let fullscreen = fullscreen.map(Into::into); + fn set_fullscreen(&self, fullscreen: Option) { let window = self.window; let window_state = Arc::clone(&self.window_state); @@ -774,7 +774,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.hwnd()) => + if monitor.native_id() == monitor::current_monitor(window.hwnd()).native_id() => { return }, @@ -790,12 +790,13 @@ impl CoreWindow for Window { // fullscreen match (&old_fullscreen, &fullscreen) { (_, Some(Fullscreen::Exclusive(monitor, video_mode))) => { - let monitor_info = monitor::get_monitor_info(monitor.hmonitor()).unwrap(); + 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( @@ -880,11 +881,16 @@ impl CoreWindow for Window { window_state.lock().unwrap().saved_window = Some(SavedWindow { placement }); let monitor = match &fullscreen { - Fullscreen::Exclusive(monitor, _) => monitor.clone(), - Fullscreen::Borderless(Some(monitor)) => monitor.clone(), - Fullscreen::Borderless(None) => monitor::current_monitor(window.hwnd()), + 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.hwnd()))); + let position: (i32, i32) = monitor.position().unwrap_or_default().into(); let size: (u32, u32) = monitor.size().into(); @@ -946,15 +952,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 28b5c3bb..f1b8e022 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 3c5c2431..b19d7dd7 100644 --- a/src/window.rs +++ b/src/window.rs @@ -10,7 +10,7 @@ pub use crate::cursor::{BadImage, Cursor, CustomCursor, CustomCursorSource, MAX_ use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::RequestError; pub use crate::icon::{BadIcon, Icon}; -use crate::monitor::{MonitorHandle, VideoMode}; +use crate::monitor::{Fullscreen, MonitorHandle}; use crate::platform_impl::PlatformSpecificWindowAttributes; use crate::utils::AsAny; @@ -442,7 +442,7 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// 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 + fmt::Debug { /// [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 @@ -972,6 +973,7 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// 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. @@ -1430,18 +1432,6 @@ impl From for CursorIcon { } } -/// Fullscreen modes. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum Fullscreen { - /// This changes the video mode of the monitor for fullscreen windows and, - /// if applicable, captures the monitor for exclusive use by this - /// application. - Exclusive(MonitorHandle, VideoMode), - - /// 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))]