Skip to content

Commit

Permalink
Add MonitorGone error case
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Jan 23, 2023
1 parent 1851222 commit 44d5ca8
Show file tree
Hide file tree
Showing 23 changed files with 363 additions and 288 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ And please only add new entries to the top of this list, right below the `# Unre

# Unreleased

- Added `monitor::MonitorGone`.
- **Breaking:** Changed the following methods to return `Result<T, MonitorGone>`, allowing handling errors that occur if the monitor disconnects:
- `MonitorHandle::name`
- `MonitorHandle::size`
- `MonitorHandle::position`
- `MonitorHandle::refresh_rate_millihertz`
- `MonitorHandle::scale_factor`
- `MonitorHandle::video_modes`
- Add `Window::is_minimized`.
- On X11, fix errors handled during `register_xlib_error_hook` invocation bleeding into winit.
- Add `Window::has_focus`.
Expand Down
26 changes: 22 additions & 4 deletions examples/monitor_list.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
#![allow(clippy::single_match)]
use std::thread;
use std::time::Duration;

use simple_logger::SimpleLogger;
use winit::{event_loop::EventLoop, window::WindowBuilder};
use winit::event_loop::EventLoop;

fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();

dbg!(window.available_monitors().collect::<Vec<_>>());
dbg!(window.primary_monitor());
println!("Primary monitor: {:#?}", event_loop.primary_monitor());

let mut monitors = event_loop.available_monitors().collect::<Vec<_>>();
println!("Monitor list: {:#?}", monitors);

loop {
let new_monitors = event_loop.available_monitors().collect::<Vec<_>>();
if new_monitors != monitors {
println!(
"Monitor list changed from {:#?} to {:#?}",
monitors, new_monitors
);
monitors = new_monitors;
}

// Sleep for the example; in practice, you should not need to listen
// for monitor changes.
thread::sleep(Duration::from_secs(1));
}
}
2 changes: 1 addition & 1 deletion examples/video_modes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn main() {

println!("Listing available video modes:");

for mode in monitor.video_modes() {
for mode in monitor.video_modes().expect("monitor video modes") {
println!("{}", mode);
}
}
99 changes: 60 additions & 39 deletions src/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,21 @@
//! methods, which return an iterator of [`MonitorHandle`]:
//! - [`EventLoopWindowTarget::available_monitors`](crate::event_loop::EventLoopWindowTarget::available_monitors).
//! - [`Window::available_monitors`](crate::window::Window::available_monitors).
use std::error;
use std::fmt;

use crate::{
dpi::{PhysicalPosition, PhysicalSize},
platform_impl,
};

/// Describes a fullscreen video mode of a monitor.
///
/// Can be acquired with [`MonitorHandle::video_modes`].
/// A list of these can be acquired with [`MonitorHandle::video_modes`].
///
/// `VideoMode` is essentially just a static blob of data, and it's properties
/// will _not_ be updated automatically if a video mode changes - refetch the
/// modes instead.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct VideoMode {
pub(crate) video_mode: platform_impl::VideoMode,
Expand Down Expand Up @@ -59,10 +66,10 @@ impl VideoMode {
/// available per color. This is generally 24 bits or 32 bits on modern
/// systems, depending on whether the alpha channel is counted or not.
///
///
/// ## Platform-specific
///
/// - **Wayland / Orbital:** Always returns 32.
/// - **iOS:** Always returns 32.
/// - **Wayland / Orbital / iOS:** Always returns 32.
#[inline]
pub fn bit_depth(&self) -> u16 {
self.video_mode.bit_depth()
Expand Down Expand Up @@ -97,85 +104,99 @@ impl std::fmt::Display for VideoMode {
}
}

/// Handle to a monitor.
/// A handle to a monitor.
///
/// Allows you to retrieve information about a given monitor and can be used in [`Window`] creation.
/// Allows you to retrieve information about a given monitor, and can be used
/// in [`Window`] creation to set the monitor that the window will go
/// fullscreen on.
///
/// Since a monitor can be removed by the user at any time, all methods on
/// this return a [`Result`] with a [`MonitorGone`] as the error case.
///
/// [`Window`]: crate::window::Window
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct MonitorHandle {
pub(crate) inner: platform_impl::MonitorHandle,
}

/// Signifies that the monitor isn't connected to the system anymore.
///
/// This might sometimes also happen if the monitors parameters changed enough
/// that the system deemed it should destroy and recreate the handle to it.
///
/// Finally, it may happen spuriously around the time when a monitor is
/// reconnected.
#[derive(Debug, Clone, PartialEq)]
pub struct MonitorGone {
_inner: (),
}

impl MonitorGone {
#[allow(dead_code)]
pub(crate) fn new() -> Self {
Self { _inner: () }
}
}

impl fmt::Display for MonitorGone {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"the monitor could not be found (it might have been disconnected)"
)
}
}

impl error::Error for MonitorGone {}

impl MonitorHandle {
/// Returns a human-readable name of the monitor.
///
/// Returns `None` if the monitor doesn't exist anymore.
///
/// ## Platform-specific
///
/// - **Web:** Always returns None
/// A human-readable name of the monitor.
#[inline]
pub fn name(&self) -> Option<String> {
pub fn name(&self) -> Result<String, MonitorGone> {
self.inner.name()
}

/// Returns the monitor's resolution.
///
/// ## Platform-specific
///
/// - **Web:** Always returns (0,0)
/// The monitor's currently configured resolution.
#[inline]
pub fn size(&self) -> PhysicalSize<u32> {
pub fn size(&self) -> Result<PhysicalSize<u32>, MonitorGone> {
self.inner.size()
}

/// Returns the top-left corner position of the monitor relative to the larger full
/// screen area.
///
/// ## Platform-specific
/// The current position of the monitor in the desktop at large.
///
/// - **Web:** Always returns (0,0)
/// The position has origin in the top-left of the monitor.
#[inline]
pub fn position(&self) -> PhysicalPosition<i32> {
pub fn position(&self) -> Result<PhysicalPosition<i32>, MonitorGone> {
self.inner.position()
}

/// The monitor refresh rate used by the system.
///
/// Return `Some` if succeed, or `None` if failed, which usually happens when the monitor
/// the window is on is removed.
/// The refresh rate currently in use on the monitor.
///
/// When using exclusive fullscreen, the refresh rate of the [`VideoMode`] that was used to
/// enter fullscreen should be used instead.
#[inline]
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
pub fn refresh_rate_millihertz(&self) -> Result<u32, MonitorGone> {
self.inner.refresh_rate_millihertz()
}

/// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
///
/// See the [`dpi`](crate::dpi) module for more information.
///
///
/// ## Platform-specific
///
/// - **X11:** Can be overridden using the `WINIT_X11_SCALE_FACTOR` environment variable.
/// - **Android:** Always returns 1.0.
/// - **Web:** Always returns 1.0
#[inline]
pub fn scale_factor(&self) -> f64 {
pub fn scale_factor(&self) -> Result<f64, MonitorGone> {
self.inner.scale_factor()
}

/// Returns all fullscreen video modes supported by this monitor.
///
/// ## Platform-specific
///
/// - **Web:** Always returns an empty iterator
#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
pub fn video_modes(&self) -> Result<impl Iterator<Item = VideoMode>, MonitorGone> {
self.inner
.video_modes()
.map(|video_mode| VideoMode { video_mode })
.map(|modes| modes.map(|video_mode| VideoMode { video_mode }))
}
}
12 changes: 6 additions & 6 deletions src/platform/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::os::raw::c_void;

use crate::{
event_loop::{EventLoopBuilder, EventLoopWindowTarget},
monitor::MonitorHandle,
monitor::{MonitorGone, MonitorHandle},
window::{Window, WindowBuilder},
};

Expand Down Expand Up @@ -278,18 +278,18 @@ impl<T> EventLoopBuilderExtMacOS for EventLoopBuilder<T> {
/// 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;
fn native_id(&self) -> Result<u32, MonitorGone>;
/// Returns a pointer to the NSScreen representing this monitor.
fn ns_screen(&self) -> Option<*mut c_void>;
fn ns_screen(&self) -> Result<*mut c_void, MonitorGone>;
}

impl MonitorHandleExtMacOS for MonitorHandle {
#[inline]
fn native_id(&self) -> u32 {
self.inner.native_identifier()
fn native_id(&self) -> Result<u32, MonitorGone> {
Ok(self.inner.native_identifier())
}

fn ns_screen(&self) -> Option<*mut c_void> {
fn ns_screen(&self) -> Result<*mut c_void, MonitorGone> {
self.inner.ns_screen().map(|s| Id::as_ptr(&s) as _)
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/platform/wayland.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::os::raw;

use crate::{
event_loop::{EventLoopBuilder, EventLoopWindowTarget},
monitor::MonitorHandle,
monitor::{MonitorGone, MonitorHandle},
window::{Window, WindowBuilder},
};

Expand Down Expand Up @@ -133,12 +133,12 @@ impl WindowBuilderExtWayland for WindowBuilder {
/// Additional methods on `MonitorHandle` that are specific to Wayland.
pub trait MonitorHandleExtWayland {
/// Returns the inner identifier of the monitor.
fn native_id(&self) -> u32;
fn native_id(&self) -> Result<u32, MonitorGone>;
}

impl MonitorHandleExtWayland for MonitorHandle {
#[inline]
fn native_id(&self) -> u32 {
fn native_id(&self) -> Result<u32, MonitorGone> {
self.inner.native_identifier()
}
}
12 changes: 6 additions & 6 deletions src/platform/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
dpi::PhysicalSize,
event::DeviceId,
event_loop::EventLoopBuilder,
monitor::MonitorHandle,
monitor::{MonitorGone, MonitorHandle},
platform_impl::WinIcon,
window::{BadIcon, Icon, Window, WindowBuilder},
};
Expand Down Expand Up @@ -275,21 +275,21 @@ impl WindowBuilderExtWindows for WindowBuilder {
/// 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;
fn native_id(&self) -> Result<String, MonitorGone>;

/// Returns the handle of the monitor - `HMONITOR`.
fn hmonitor(&self) -> HMONITOR;
fn hmonitor(&self) -> Result<HMONITOR, MonitorGone>;
}

impl MonitorHandleExtWindows for MonitorHandle {
#[inline]
fn native_id(&self) -> String {
fn native_id(&self) -> Result<String, MonitorGone> {
self.inner.native_identifier()
}

#[inline]
fn hmonitor(&self) -> HMONITOR {
self.inner.hmonitor()
fn hmonitor(&self) -> Result<HMONITOR, MonitorGone> {
Ok(self.inner.hmonitor())
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/platform/x11.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::ptr;

use crate::{
event_loop::{EventLoopBuilder, EventLoopWindowTarget},
monitor::MonitorHandle,
monitor::{MonitorGone, MonitorHandle},
window::{Window, WindowBuilder},
};

Expand Down Expand Up @@ -222,12 +222,12 @@ impl WindowBuilderExtX11 for WindowBuilder {
/// Additional methods on `MonitorHandle` that are specific to X11.
pub trait MonitorHandleExtX11 {
/// Returns the inner identifier of the monitor.
fn native_id(&self) -> u32;
fn native_id(&self) -> Result<u32, MonitorGone>;
}

impl MonitorHandleExtX11 for MonitorHandle {
#[inline]
fn native_id(&self) -> u32 {
fn native_id(&self) -> Result<u32, MonitorGone> {
self.inner.native_identifier()
}
}
Loading

0 comments on commit 44d5ca8

Please sign in to comment.