Skip to content

Commit

Permalink
Support listing available video modes for a monitor (#896)
Browse files Browse the repository at this point in the history
* Support listing available video modes for a monitor

* Use derivative for Windows `MonitorHandle`

* Update FEATURES.md

* Fix multiline if statement

* Add documentation for `VideoMode` type
  • Loading branch information
Aleksi Juvani authored and Osspial committed Jun 12, 2019
1 parent 2b89dde commit 47b5dfa
Show file tree
Hide file tree
Showing 13 changed files with 254 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
- Rename several functions to improve both internal consistency and compliance with Rust API guidelines.
- Remove `WindowBuilder::multitouch` field, since it was only implemented on a few platforms. Multitouch is always enabled now.
- **Breaking:** On macOS, change `ns` identifiers to use snake_case for consistency with iOS's `ui` identifiers.
- Add `MonitorHandle::video_modes` method for retrieving supported video modes for the given monitor.

# Version 0.19.1 (2019-04-08)

Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ lazy_static = "1"
libc = "0.2"
log = "0.4"
serde = { version = "1", optional = true, features = ["serde_derive"] }
derivative = "1.0.2"

[dev-dependencies]
image = "0.21"
Expand All @@ -33,6 +34,7 @@ objc = "0.2.3"
cocoa = "0.18.4"
core-foundation = "0.6"
core-graphics = "0.17.3"
core-video-sys = "0.1.2"
dispatch = "0.1.4"
objc = "0.2.3"

Expand Down
8 changes: 5 additions & 3 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ If your PR makes notable changes to Winit's features, please update this section

### System Information
- **Monitor list**: Retrieve the list of monitors and their metadata, including which one is primary.
- **Video mode query**: Monitors can be queried for their supported fullscreen video modes (consisting of resolution, refresh rate, and bit depth).

### Input Handling
- **Mouse events**: Generating mouse events associated with pointer motion, click, and scrolling events.
Expand Down Expand Up @@ -160,9 +161,10 @@ Legend:
|Popup windows ||||||||

### System information
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Emscripten|
|------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|Monitor list |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Emscripten|
|---------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|Monitor list |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |
|Video mode query |✔️ |✔️ |✔️ |✔️ ||✔️ ||

### Input handling
|Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |Emscripten|
Expand Down
14 changes: 14 additions & 0 deletions examples/video_modes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
extern crate winit;

use winit::event_loop::EventLoop;

fn main() {
let event_loop = EventLoop::new();
let monitor = event_loop.primary_monitor();

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

for mode in monitor.video_modes() {
println!("{:?}", mode);
}
}
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ extern crate log;
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
#[macro_use]
extern crate derivative;

#[cfg(target_os = "windows")]
extern crate winapi;
Expand All @@ -101,6 +103,8 @@ extern crate dispatch;
extern crate core_foundation;
#[cfg(target_os = "macos")]
extern crate core_graphics;
#[cfg(target_os = "macos")]
extern crate core_video_sys;
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
extern crate x11_dl;
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "windows"))]
Expand Down
45 changes: 45 additions & 0 deletions src/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,45 @@ impl Iterator for AvailableMonitorsIter {
}
}

/// Describes a fullscreen video mode of a monitor.
///
/// Can be acquired with:
/// - [`MonitorHandle::video_modes`][monitor_get].
///
/// [monitor_get]: ../monitor/struct.MonitorHandle.html#method.video_modes
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct VideoMode {
pub(crate) dimensions: (u32, u32),
pub(crate) bit_depth: u16,
pub(crate) refresh_rate: u16,
}

impl VideoMode {
/// Returns the resolution of this video mode.
pub fn dimensions(&self) -> PhysicalSize {
self.dimensions.into()
}

/// 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.
///
/// ## Platform-specific
///
/// - **Wayland:** Always returns 32.
/// - **iOS:** Always returns 32.
pub fn bit_depth(&self) -> u16 {
self.bit_depth
}

/// Returns the refresh rate of this video mode. **Note**: the returned
/// refresh rate is an integer approximation, and you shouldn't rely on this
/// value to be exact.
pub fn refresh_rate(&self) -> u16 {
self.refresh_rate
}
}

/// Handle to a monitor.
///
/// Allows you to retrieve information about a given monitor and can be used in [`Window`] creation.
Expand Down Expand Up @@ -88,4 +127,10 @@ impl MonitorHandle {
pub fn hidpi_factor(&self) -> f64 {
self.inner.hidpi_factor()
}

/// Returns all fullscreen video modes supported by this monitor.
#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.inner.video_modes()
}
}
32 changes: 24 additions & 8 deletions src/platform_impl/ios/monitor.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
use std::{
collections::VecDeque,
collections::{HashSet, VecDeque},
fmt,
ops::{Deref, DerefMut},
};

use dpi::{PhysicalPosition, PhysicalSize};
use monitor::VideoMode;

use platform_impl::platform::ffi::{
id,
nil,
CGFloat,
CGRect,
NSUInteger,
};
use platform_impl::platform::ffi::{id, nil, CGFloat, CGRect, CGSize, NSInteger, NSUInteger};

pub struct Inner {
uiscreen: id,
Expand Down Expand Up @@ -134,6 +129,27 @@ impl Inner {
scale as f64
}
}

pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
let refresh_rate: NSInteger = unsafe { msg_send![self.uiscreen, maximumFramesPerSecond] };

let available_modes: id = unsafe { msg_send![self.uiscreen, availableModes] };
let available_mode_count: NSUInteger = unsafe { msg_send![available_modes, count] };

let mut modes = HashSet::with_capacity(available_mode_count);

for i in 0..available_mode_count {
let mode: id = unsafe { msg_send![available_modes, objectAtIndex: i] };
let size: CGSize = unsafe { msg_send![mode, size] };
modes.insert(VideoMode {
dimensions: (size.width as u32, size.height as u32),
bit_depth: 32,
refresh_rate: refresh_rate as u16,
});
}

modes.into_iter()
}
}

// MonitorHandleExtIOS
Expand Down
10 changes: 9 additions & 1 deletion src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use icon::Icon;
use error::{ExternalError, NotSupportedError, OsError as RootOsError};
use event::Event;
use event_loop::{EventLoopClosed, ControlFlow, EventLoopWindowTarget as RootELW};
use monitor::MonitorHandle as RootMonitorHandle;
use monitor::{MonitorHandle as RootMonitorHandle, VideoMode};
use window::{WindowAttributes, CursorIcon};
use self::x11::{XConnection, XError};
use self::x11::ffi::XVisualInfo;
Expand Down Expand Up @@ -142,6 +142,14 @@ impl MonitorHandle {
&MonitorHandle::Wayland(ref m) => m.hidpi_factor() as f64,
}
}

#[inline]
pub fn video_modes(&self) -> Box<dyn Iterator<Item = VideoMode>> {
match self {
MonitorHandle::X(m) => Box::new(m.video_modes()),
MonitorHandle::Wayland(m) => Box::new(m.video_modes()),
}
}
}

impl Window {
Expand Down
15 changes: 15 additions & 0 deletions src/platform_impl/linux/wayland/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}
use event::ModifiersState;
use dpi::{PhysicalPosition, PhysicalSize};
use platform_impl::platform::sticky_exit_callback;
use monitor::VideoMode;

use super::window::WindowStore;
use super::WindowId;
Expand Down Expand Up @@ -584,6 +585,20 @@ impl MonitorHandle {
.with_info(&self.proxy, |_, info| info.scale_factor)
.unwrap_or(1)
}

#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode>
{
self.mgr
.with_info(&self.proxy, |_, info| info.modes.clone())
.unwrap_or(vec![])
.into_iter()
.map(|x| VideoMode {
dimensions: (x.dimensions.0 as u32, x.dimensions.1 as u32),
refresh_rate: (x.refresh_rate as f32 / 1000.0).round() as u16,
bit_depth: 32
})
}
}

pub fn primary_monitor(outputs: &OutputMgr) -> MonitorHandle {
Expand Down
11 changes: 10 additions & 1 deletion src/platform_impl/linux/x11/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::os::raw::*;
use parking_lot::Mutex;

use dpi::{PhysicalPosition, PhysicalSize};
use monitor::VideoMode;
use super::{util, XConnection, XError};
use super::ffi::{
RRCrtcChangeNotifyMask,
Expand Down Expand Up @@ -56,6 +57,8 @@ pub struct MonitorHandle {
pub(crate) hidpi_factor: f64,
/// Used to determine which windows are on this monitor
pub(crate) rect: util::AaRect,
/// Supported video modes on this monitor
video_modes: Vec<VideoMode>,
}

impl MonitorHandle {
Expand All @@ -66,7 +69,7 @@ impl MonitorHandle {
repr: util::MonitorRepr,
primary: bool,
) -> Option<Self> {
let (name, hidpi_factor) = unsafe { xconn.get_output_info(resources, &repr)? };
let (name, hidpi_factor, video_modes) = unsafe { xconn.get_output_info(resources, &repr)? };
let (dimensions, position) = unsafe { (repr.dimensions(), repr.position()) };
let rect = util::AaRect::new(position, dimensions);
Some(MonitorHandle {
Expand All @@ -77,6 +80,7 @@ impl MonitorHandle {
position,
primary,
rect,
video_modes,
})
}

Expand All @@ -101,6 +105,11 @@ impl MonitorHandle {
pub fn hidpi_factor(&self) -> f64 {
self.hidpi_factor
}

#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.video_modes.clone().into_iter()
}
}

impl XConnection {
Expand Down
32 changes: 30 additions & 2 deletions src/platform_impl/linux/x11/util/randr.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{env, slice};
use std::str::FromStr;

use monitor::VideoMode;
use dpi::validate_hidpi_factor;
use super::*;

Expand Down Expand Up @@ -101,7 +102,7 @@ impl XConnection {
&self,
resources: *mut ffi::XRRScreenResources,
repr: &MonitorRepr,
) -> Option<(String, f64)> {
) -> Option<(String, f64, Vec<VideoMode>)> {
let output_info = (self.xrandr.XRRGetOutputInfo)(
self.display,
resources,
Expand All @@ -114,6 +115,33 @@ impl XConnection {
let _ = self.check_errors(); // discard `BadRROutput` error
return None;
}

let screen = (self.xlib.XDefaultScreen)(self.display);
let bit_depth = (self.xlib.XDefaultDepth)(self.display, screen);

let output_modes =
slice::from_raw_parts((*output_info).modes, (*output_info).nmode as usize);
let resource_modes = slice::from_raw_parts((*resources).modes, (*resources).nmode as usize);

let modes = resource_modes
.iter()
// 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(|x| {
let refresh_rate = if x.dotClock > 0 && x.hTotal > 0 && x.vTotal > 0 {
x.dotClock as u64 * 1000 / (x.hTotal as u64 * x.vTotal as u64)
} else {
0
};

VideoMode {
dimensions: (x.width, x.height),
refresh_rate: (refresh_rate as f32 / 1000.0).round() as u16,
bit_depth: bit_depth as u16,
}
});

let name_slice = slice::from_raw_parts(
(*output_info).name as *mut u8,
(*output_info).nameLen as usize,
Expand All @@ -129,6 +157,6 @@ impl XConnection {
};

(self.xrandr.XRRFreeOutputInfo)(output_info);
Some((name, hidpi_factor))
Some((name, hidpi_factor, modes.collect()))
}
}
Loading

0 comments on commit 47b5dfa

Please sign in to comment.