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

@ -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");
}