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.
This commit is contained in:
Kirill Chibisov 2024-09-21 20:27:12 +03:00
parent be1baf164c
commit f1c5afd84e
43 changed files with 726 additions and 826 deletions

View file

@ -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

View file

@ -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<u32>,
pub(crate) bit_depth: Option<NonZeroU16>,
pub(crate) refresh_rate_millihertz: Option<NonZeroU32>,
}
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<u32> {
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<NonZeroU16> {
self.bit_depth
}
/// Returns the refresh rate of this video mode in mHz.
pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
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<dyn MonitorHandleProvider>);
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<String> {
self.inner.name()
}
fn name(&self) -> Option<Cow<'_, str>>;
/// 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<PhysicalPosition<i32>> {
self.inner.position()
}
fn position(&self) -> Option<PhysicalPosition<i32>>;
/// 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<VideoMode> {
self.inner.current_video_mode()
}
fn current_video_mode(&self) -> Option<VideoMode>;
/// Returns all fullscreen video modes supported by this monitor.
#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.inner.video_modes()
fn video_modes(&self) -> Box<dyn Iterator<Item = VideoMode>>;
}
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<u32>,
pub(crate) bit_depth: Option<NonZeroU16>,
pub(crate) refresh_rate_millihertz: Option<NonZeroU32>,
}
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<u32> {
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<NonZeroU16> {
self.bit_depth
}
/// Returns the refresh rate of this video mode in mHz.
pub fn refresh_rate_millihertz(&self) -> Option<NonZeroU32> {
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<MonitorHandle>),
}

View file

@ -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::<IosMonitorHandle>().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::<IosMonitorHandle>().unwrap();
monitor.preferred_video_mode()
}
}

View file

@ -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::<MacOsMonitorHandle>().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 _)
}
}

View file

@ -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()
}
}

View file

@ -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<bool> {
self.inner.is_internal()
self.as_any().downcast_ref::<WebMonitorHandle>().unwrap().is_internal()
}
fn orientation(&self) -> OrientationData {
self.inner.orientation()
self.as_any().downcast_ref::<WebMonitorHandle>().unwrap().orientation()
}
fn request_lock(&self, orientation_lock: OrientationLock) -> OrientationLockFuture {
OrientationLockFuture(self.inner.request_lock(orientation_lock))
let future = self
.as_any()
.downcast_ref::<WebMonitorHandle>()
.unwrap()
.request_lock(orientation_lock);
OrientationLockFuture(future)
}
fn unlock(&self) -> Result<(), OrientationLockError> {
self.inner.unlock()
self.as_any().downcast_ref::<WebMonitorHandle>().unwrap().unlock()
}
fn is_detailed(&self) -> bool {
self.inner.is_detailed()
self.as_any().downcast_ref::<WebMonitorHandle>().unwrap().is_detailed()
}
}

View file

@ -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.

View file

@ -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()
}
}

View file

@ -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<dyn Iterator<Item = RootMonitorHandle>> {
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
Box::new(std::iter::empty())
}
fn primary_monitor(&self) -> Option<RootMonitorHandle> {
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
None
}
@ -824,15 +823,15 @@ impl CoreWindow for Window {
GLOBAL_WINDOW
}
fn primary_monitor(&self) -> Option<RootMonitorHandle> {
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
None
}
fn available_monitors(&self) -> Box<dyn Iterator<Item = RootMonitorHandle>> {
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
Box::new(std::iter::empty())
}
fn current_monitor(&self) -> Option<RootMonitorHandle> {
fn current_monitor(&self) -> Option<CoreMonitorHandle> {
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<String> {
unreachable!()
}
pub fn position(&self) -> Option<PhysicalPosition<i32>> {
unreachable!()
}
pub fn scale_factor(&self) -> f64 {
unreachable!()
}
pub fn current_video_mode(&self) -> Option<VideoMode> {
unreachable!()
}
pub fn video_modes(&self) -> std::iter::Empty<VideoMode> {
unreachable!()
}
}
fn screen_size(app: &AndroidApp) -> PhysicalSize<u32> {
if let Some(native_window) = app.native_window() {
PhysicalSize::new(native_window.width() as _, native_window.height() as _)

View file

@ -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<dyn Iterator<Item = RootMonitorHandle>> {
Box::new(monitor::available_monitors().into_iter().map(|inner| RootMonitorHandle { inner }))
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
Box::new(
monitor::available_monitors()
.into_iter()
.map(|monitor| CoreMonitorHandle(Arc::new(monitor))),
)
}
fn primary_monitor(&self) -> Option<crate::monitor::MonitorHandle> {
let monitor = monitor::primary_monitor();
Some(RootMonitorHandle { inner: monitor })
Some(CoreMonitorHandle(Arc::new(monitor)))
}
fn listen_device_events(&self, _allowed: DeviceEvents) {}

View file

@ -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;

View file

@ -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<NonZeroU32> {
let current_display_mode =
NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) }.unwrap());
refresh_rate_millihertz(self.0, &current_display_mode)
}
pub fn video_mode_handles(&self) -> impl Iterator<Item = VideoModeHandle> {
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<Retained<NSScreen>> {
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:
//
// <https://github.com/glfw/glfw/blob/57cbded0760a50b9039ee0cb3f3c14f60145567c/src/cocoa_monitor.m#L44-L126>
fn name(&self) -> Option<std::borrow::Cow<'_, str>> {
let screen_num = unsafe { CGDisplayModelNumber(self.0) };
Some(format!("Monitor #{screen_num}").into())
}
fn position(&self) -> Option<PhysicalPosition<i32>> {
// 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<VideoMode> {
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<dyn Iterator<Item = VideoMode>> {
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:
// <https://github.com/glfw/glfw/blob/57cbded0760a50b9039ee0cb3f3c14f60145567c/src/cocoa_monitor.m#L44-L126>
pub fn name(&self) -> Option<String> {
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<PhysicalPosition<i32>> {
// 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<NonZeroU32> {
let current_display_mode =
NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) }.unwrap());
refresh_rate_millihertz(self.0, &current_display_mode)
}
pub fn current_video_mode(&self) -> Option<VideoMode> {
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<Item = VideoMode> {
self.video_modes_handles().map(|handle| handle.mode)
}
pub(crate) fn video_modes_handles(&self) -> impl Iterator<Item = VideoModeHandle> {
let refresh_rate_millihertz = self.refresh_rate_millihertz();
let 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<Retained<NSScreen>> {
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");

View file

@ -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<Fullscreen>) {
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<Fullscreen> {
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<CoreMonitorHandle> {
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<dyn Iterator<Item = CoreMonitorHandle>> {
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<CoreMonitorHandle> {
self.maybe_wait_on_main(|delegate| {
delegate.primary_monitor().map(|inner| CoreMonitorHandle { inner })
delegate.primary_monitor().map(|monitor| CoreMonitorHandle(Arc::new(monitor)))
})
}

View file

@ -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<Retained<NSWindow>> {
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::<MonitorHandle>().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::<MonitorHandle>().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::<MonitorHandle>().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::<MonitorHandle>().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::<MonitorHandle>().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"
);
}

View file

@ -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<dyn Iterator<Item = RootMonitorHandle>> {
Box::new(monitor::uiscreens(self.mtm).into_iter().map(|inner| RootMonitorHandle { inner }))
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
Box::new(
monitor::uiscreens(self.mtm)
.into_iter()
.map(|monitor| CoreMonitorHandle(Arc::new(monitor))),
)
}
fn primary_monitor(&self) -> Option<crate::monitor::MonitorHandle> {
#[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) {}

View file

@ -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 {}

View file

@ -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<Retained<UIScreen>>,
}
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<std::borrow::Cow<'_, str>> {
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<PhysicalPosition<i32>> {
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<VideoMode> {
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<dyn Iterator<Item = VideoMode>> {
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<String> {
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<PhysicalPosition<i32>> {
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<VideoMode> {
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<Item = VideoModeHandle> {
run_on_main(|mtm| {
let ui_screen = self.ui_screen(mtm);

View file

@ -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::<MonitorHandle>().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::<MonitorHandle>().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::<MonitorHandle>().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::<MonitorHandle>().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::<MonitorHandle>().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<crate::window::Fullscreen>) {
self.maybe_wait_on_main(|delegate| delegate.set_fullscreen(fullscreen.map(Into::into)))
fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
self.maybe_wait_on_main(|delegate| delegate.set_fullscreen(fullscreen))
}
fn fullscreen(&self) -> Option<crate::window::Fullscreen> {
self.maybe_wait_on_main(|delegate| delegate.fullscreen().map(Into::into))
fn fullscreen(&self) -> Option<Fullscreen> {
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<CoreMonitorHandle> {
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<dyn Iterator<Item = CoreMonitorHandle>> {
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<CoreMonitorHandle> {
self.maybe_wait_on_main(|delegate| {
delegate.primary_monitor().map(|inner| CoreMonitorHandle { inner })
delegate.primary_monitor().map(|monitor| CoreMonitorHandle(Arc::new(monitor)))
})
}

View file

@ -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<Mutex<Result<Arc<XConnection>, 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<String> {
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<PhysicalPosition<i32>> {
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<VideoMode> {
x11_or_wayland!(match self; MonitorHandle(m) => m.current_video_mode())
}
#[inline]
pub fn video_modes(&self) -> Box<dyn Iterator<Item = VideoMode>> {
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)]

View file

@ -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<dyn Iterator<Item = crate::monitor::MonitorHandle>> {
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
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<crate::monitor::MonitorHandle> {
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
// There's no primary monitor on Wayland.
None
}

View file

@ -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 {

View file

@ -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<String> {
let output_data = self.proxy.data::<OutputData>().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::<OutputData>().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<PhysicalPosition<i32>> {
fn name(&self) -> Option<Cow<'_, str>> {
let output_data = self.proxy.data::<OutputData>().unwrap();
output_data.with_output_info(|info| info.name.clone().map(Cow::Owned))
}
fn position(&self) -> Option<PhysicalPosition<i32>> {
let output_data = self.proxy.data::<OutputData>().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::<OutputData>().unwrap();
output_data.scale_factor()
output_data.scale_factor() as f64
}
#[inline]
pub fn current_video_mode(&self) -> Option<VideoMode> {
fn current_video_mode(&self) -> Option<crate::monitor::VideoMode> {
let output_data = self.proxy.data::<OutputData>().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<Item = VideoMode> {
fn video_modes(&self) -> Box<dyn Iterator<Item = VideoMode>> {
let output_data = self.proxy.data::<OutputData>().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<std::cmp::Ordering> {
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<H: std::hash::Hasher>(&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(),

View file

@ -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::<output::MonitorHandle>()
.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<CoreFullscreen>) {
fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
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::<output::MonitorHandle>()
.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<CoreFullscreen> {
fn fullscreen(&self) -> Option<Fullscreen> {
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<dyn Iterator<Item = CoreMonitorHandle>> {
@ -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))),
)
}

View file

@ -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<dyn Iterator<Item = crate::monitor::MonitorHandle>> {
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
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<crate::monitor::MonitorHandle> {
self.xconn
.primary_monitor()
.ok()
.map(crate::platform_impl::MonitorHandle::X)
.map(|inner| crate::monitor::MonitorHandle { inner })
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
self.xconn.primary_monitor().ok().map(|monitor| CoreMonitorHandle(Arc::new(monitor)))
}
fn system_theme(&self) -> Option<Theme> {

View file

@ -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<MonitorHandle>,
}
impl From<VideoModeHandle> for VideoMode {
@ -50,6 +49,36 @@ pub struct MonitorHandle {
pub(crate) video_modes: Vec<VideoModeHandle>,
}
impl MonitorHandleProvider for MonitorHandle {
fn id(&self) -> u128 {
self.native_id() as _
}
fn native_id(&self) -> u64 {
self.id as _
}
fn name(&self) -> Option<std::borrow::Cow<'_, str>> {
Some(self.name.as_str().into())
}
fn position(&self) -> Option<PhysicalPosition<i32>> {
Some(self.position.into())
}
fn scale_factor(&self) -> f64 {
self.scale_factor
}
fn current_video_mode(&self) -> Option<VideoMode> {
self.video_modes.iter().find_map(|mode| mode.current.then(|| mode.clone().into()))
}
fn video_modes(&self) -> Box<dyn Iterator<Item = VideoMode>> {
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<String> {
Some(self.name.clone())
}
#[inline]
pub fn native_identifier(&self) -> u32 {
self.id as _
}
pub fn position(&self) -> Option<PhysicalPosition<i32>> {
Some(self.position.into())
}
#[inline]
pub fn scale_factor(&self) -> f64 {
self.scale_factor
}
#[inline]
pub fn current_video_mode(&self) -> Option<VideoMode> {
self.video_modes.iter().find(|mode| mode.current).cloned().map(Into::into)
}
#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.video_modes.clone().into_iter().map(Into::into)
}
}
impl XConnection {

View file

@ -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();

View file

@ -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<crate::window::Fullscreen>) {
self.0.set_fullscreen(fullscreen.map(Into::into))
fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
self.0.set_fullscreen(fullscreen)
}
fn fullscreen(&self) -> Option<crate::window::Fullscreen> {
self.0.fullscreen().map(Into::into)
fn fullscreen(&self) -> Option<Fullscreen> {
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<crate::monitor::MonitorHandle> {
self.0
.current_monitor()
.map(crate::platform_impl::MonitorHandle::X)
.map(|inner| crate::monitor::MonitorHandle { inner })
fn current_monitor(&self) -> Option<CoreMonitorHandle> {
self.0.current_monitor().map(|monitor| CoreMonitorHandle(Arc::new(monitor)))
}
fn available_monitors(&self) -> Box<dyn Iterator<Item = crate::monitor::MonitorHandle>> {
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
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<crate::monitor::MonitorHandle> {
self.0
.primary_monitor()
.map(crate::platform_impl::MonitorHandle::X)
.map(|inner| crate::monitor::MonitorHandle { inner })
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
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::<X11MonitorHandle>().unwrap();
(Cow::Borrowed(monitor), Some(video_mode))
},
Fullscreen::Borderless(Some(monitor)) => {
let monitor =
monitor.as_any().downcast_ref::<X11MonitorHandle>().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");
}

View file

@ -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<MonitorHandle>),
}
impl From<RootFullscreen> 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<Fullscreen> 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),

View file

@ -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<dyn Iterator<Item = crate::monitor::MonitorHandle>> {
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<Theme> {
@ -721,7 +717,7 @@ impl RootActiveEventLoop for ActiveEventLoop {
}
fn primary_monitor(&self) -> Option<crate::monitor::MonitorHandle> {
Some(crate::monitor::MonitorHandle { inner: MonitorHandle })
None
}
fn listen_device_events(&self, _allowed: DeviceEvents) {}

View file

@ -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<String> {
None
}
pub fn position(&self) -> Option<PhysicalPosition<i32>> {
None
}
pub fn scale_factor(&self) -> f64 {
1.0 // TODO
}
pub fn current_video_mode(&self) -> Option<VideoMode> {
// (it is guaranteed to support 32 bit color though)
None
}
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
std::iter::empty()
}
}

View file

@ -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<Self, RequestError> {
let scale = MonitorHandle.scale_factor();
let scale = 1.;
let (x, y) = if let Some(pos) = attrs.position {
pos.to_physical::<i32>(scale).into()
@ -161,22 +162,22 @@ impl CoreWindow for Window {
#[inline]
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
Some(CoreMonitorHandle { inner: MonitorHandle })
None
}
#[inline]
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
Box::new(vec![CoreMonitorHandle { inner: MonitorHandle }].into_iter())
Box::new(iter::empty())
}
#[inline]
fn current_monitor(&self) -> Option<CoreMonitorHandle> {
Some(CoreMonitorHandle { inner: MonitorHandle })
None
}
#[inline]
fn scale_factor(&self) -> f64 {
MonitorHandle.scale_factor()
1.
}
#[inline]

View file

@ -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<dyn Iterator<Item = RootMonitorHandle>> {
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoremMonitorHandle>> {
Box::new(
self.runner
.monitor()
.available_monitors()
.into_iter()
.map(|inner| RootMonitorHandle { inner }),
.map(|monitor| CoremMonitorHandle(Arc::new(monitor))),
)
}
fn primary_monitor(&self) -> Option<RootMonitorHandle> {
self.runner.monitor().primary_monitor().map(|inner| RootMonitorHandle { inner })
fn primary_monitor(&self) -> Option<CoremMonitorHandle> {
self.runner.monitor().primary_monitor().map(|monitor| CoremMonitorHandle(Arc::new(monitor)))
}
fn listen_device_events(&self, allowed: DeviceEvents) {

View file

@ -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<PhysicalPosition<i32>> {
self.inner.queue(|inner| inner.position())
}
pub fn name(&self) -> Option<String> {
self.inner.queue(|inner| inner.name())
}
pub fn current_video_mode(&self) -> Option<VideoMode> {
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<Item = VideoMode> {
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<PhysicalPosition<i32>> {
self.inner.queue(|inner| inner.position())
}
fn name(&self) -> Option<std::borrow::Cow<'_, str>> {
self.inner.queue(|inner| inner.name().map(Into::into))
}
fn current_video_mode(&self) -> Option<VideoMode> {
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<dyn Iterator<Item = VideoMode>> {
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<MonitorHandle> for RootMonitorHandle {
fn from(inner: MonitorHandle) -> Self {
RootMonitorHandle { inner }
impl From<MonitorHandle> for CoreMonitorHandle {
fn from(monitor: MonitorHandle) -> Self {
CoreMonitorHandle(Arc::new(monitor))
}
}

View file

@ -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 {

View file

@ -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::<MonitorHandle>().unwrap();
if let Some(monitor) = monitor.detailed(main_thread) {
let options: FullscreenOptions = Object::new().unchecked_into();
options.set_screen(&monitor);

View file

@ -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<RootFullscreen>) {
fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
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<RootFullscreen> {
fn fullscreen(&self) -> Option<Fullscreen> {
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<RootMonitorHandle> {
fn current_monitor(&self) -> Option<CoremMonitorHandle> {
Some(self.inner.queue(|inner| inner.monitor.current_monitor()).into())
}
fn available_monitors(&self) -> Box<dyn Iterator<Item = RootMonitorHandle>> {
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoremMonitorHandle>> {
Box::new(
self.inner
.queue(|inner| inner.monitor.available_monitors())
.into_iter()
.map(RootMonitorHandle::from),
.map(CoremMonitorHandle::from),
)
}
fn primary_monitor(&self) -> Option<RootMonitorHandle> {
self.inner.queue(|inner| inner.monitor.primary_monitor()).map(RootMonitorHandle::from)
fn primary_monitor(&self) -> Option<CoremMonitorHandle> {
self.inner.queue(|inner| inner.monitor.primary_monitor()).map(CoremMonitorHandle::from)
}
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {

View file

@ -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<dyn Iterator<Item = crate::monitor::MonitorHandle>> {
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
Box::new(
monitor::available_monitors()
.into_iter()
.map(|inner| crate::monitor::MonitorHandle { inner }),
.map(|monitor| CoreMonitorHandle(Arc::new(monitor))),
)
}
fn primary_monitor(&self) -> Option<crate::monitor::MonitorHandle> {
Some(RootMonitorHandle { inner: monitor::primary_monitor() })
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
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;

View file

@ -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 {

View file

@ -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<MONITORINFOEXW, io:
}
}
#[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 {}
impl MonitorHandle {
pub(crate) fn new(hmonitor: HMONITOR) -> Self {
MonitorHandle(hmonitor)
}
#[inline]
pub fn name(&self) -> Option<String> {
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<u32> {
let rc_monitor = get_monitor_info(self.0).unwrap().monitorInfo.rcMonitor;
PhysicalSize {
@ -145,39 +129,7 @@ impl MonitorHandle {
}
}
#[inline]
pub fn position(&self) -> Option<PhysicalPosition<i32>> {
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<VideoMode> {
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<Item = VideoModeHandle> {
pub(crate) fn video_mode_handles(&self) -> Box<dyn Iterator<Item = VideoModeHandle>> {
// 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<Item = VideoMode> {
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<std::borrow::Cow<'_, str>> {
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<PhysicalPosition<i32>> {
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<crate::monitor::VideoMode> {
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<dyn Iterator<Item = VideoMode>> {
Box::new(self.video_mode_handles().map(|mode| mode.mode))
}
}

View file

@ -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<CoreFullscreen> {
fn fullscreen(&self) -> Option<Fullscreen> {
let window_state = self.window_state_lock();
window_state.fullscreen.clone().map(Into::into)
window_state.fullscreen.clone()
}
fn set_fullscreen(&self, fullscreen: Option<CoreFullscreen>) {
let fullscreen = fullscreen.map(Into::into);
fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
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::<MonitorHandle>().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::<MonitorHandle>().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<CoreMonitorHandle> {
Some(CoreMonitorHandle { inner: monitor::current_monitor(self.hwnd()) })
Some(CoreMonitorHandle(Arc::new(monitor::current_monitor(self.hwnd()))))
}
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
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<CoreMonitorHandle> {
Some(CoreMonitorHandle { inner: monitor::primary_monitor() })
Some(CoreMonitorHandle(Arc::new(monitor::primary_monitor())))
}
fn set_window_icon(&self, window_icon: Option<Icon>) {

View file

@ -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.

View file

@ -10,7 +10,7 @@ pub use crate::cursor::{BadImage, Cursor, CustomCursor, CustomCursorSource, MAX_
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::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<Fullscreen>);
/// Gets the window's current fullscreen state.
@ -1430,18 +1432,6 @@ impl From<ResizeDirection> 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<MonitorHandle>),
}
/// The theme variant to use.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]