Skip to content

Commit

Permalink
Add MonitorHandle::current_video_mode()
Browse files Browse the repository at this point in the history
  • Loading branch information
daxpedda committed Jul 20, 2024
1 parent 73c01ff commit 521e071
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 79 deletions.
1 change: 1 addition & 0 deletions src/changelog/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ changelog entry.

- Add `ActiveEventLoop::create_proxy()`.
- On Web, implement `Error` for `platform::web::CustomCursorError`.
- Add `MonitorHandle::current_video_mode()`.

### Changed

Expand Down
6 changes: 6 additions & 0 deletions src/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ impl MonitorHandle {
self.inner.scale_factor()
}

/// Returns the currently active video mode of this monitor.
#[inline]
pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
self.inner.current_video_mode().map(|video_mode| VideoModeHandle { video_mode })
}

/// Returns all fullscreen video modes supported by this monitor.
///
/// ## Platform-specific
Expand Down
8 changes: 6 additions & 2 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1017,17 +1017,21 @@ impl MonitorHandle {
None
}

pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
let size = self.size().into();
// FIXME this is not the real refresh rate
// (it is guaranteed to support 32 bit color though)
std::iter::once(VideoModeHandle {
Some(VideoModeHandle {
size,
bit_depth: 32,
refresh_rate_millihertz: 60000,
monitor: self.clone(),
})
}

pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
self.current_video_mode().into_iter()
}
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
Expand Down
107 changes: 63 additions & 44 deletions src/platform_impl/apple/appkit/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,34 @@ impl Clone for NativeDisplayMode {
}

impl VideoModeHandle {
fn new(monitor: MonitorHandle, mode: NativeDisplayMode, refresh_rate_millihertz: u32) -> Self {
unsafe {
let pixel_encoding =
CFString::wrap_under_create_rule(ffi::CGDisplayModeCopyPixelEncoding(mode.0))
.to_string();
let bit_depth = if pixel_encoding.eq_ignore_ascii_case(ffi::IO32BitDirectPixels) {
32
} else if pixel_encoding.eq_ignore_ascii_case(ffi::IO16BitDirectPixels) {
16
} else if pixel_encoding.eq_ignore_ascii_case(ffi::kIO30BitDirectPixels) {
30
} else {
unimplemented!()
};

VideoModeHandle {
size: PhysicalSize::new(
ffi::CGDisplayModeGetPixelWidth(mode.0) as u32,
ffi::CGDisplayModeGetPixelHeight(mode.0) as u32,
),
refresh_rate_millihertz,
bit_depth,
monitor: monitor.clone(),
native_mode: mode,
}
}
}

pub fn size(&self) -> PhysicalSize<u32> {
self.size
}
Expand Down Expand Up @@ -212,29 +240,15 @@ impl MonitorHandle {
}

pub fn refresh_rate_millihertz(&self) -> Option<u32> {
unsafe {
let current_display_mode = NativeDisplayMode(CGDisplayCopyDisplayMode(self.0) as _);
let refresh_rate = ffi::CGDisplayModeGetRefreshRate(current_display_mode.0);
if refresh_rate > 0.0 {
return Some((refresh_rate * 1000.0).round() as u32);
}

let mut display_link = std::ptr::null_mut();
if ffi::CVDisplayLinkCreateWithCGDisplay(self.0, &mut display_link)
!= ffi::kCVReturnSuccess
{
return None;
}
let time = ffi::CVDisplayLinkGetNominalOutputVideoRefreshPeriod(display_link);
ffi::CVDisplayLinkRelease(display_link);

// This value is indefinite if an invalid display link was specified
if time.flags & ffi::kCVTimeIsIndefinite != 0 {
return None;
}
let current_display_mode =
NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _);
refresh_rate_millihertz(self.0, &current_display_mode)
}

(time.time_scale as i64).checked_div(time.time_value).map(|v| (v * 1000) as u32)
}
pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
let mode = NativeDisplayMode(unsafe { CGDisplayCopyDisplayMode(self.0) } as _);
let refresh_rate_millihertz = refresh_rate_millihertz(self.0, &mode).unwrap_or(0);
Some(VideoModeHandle::new(self.clone(), mode, refresh_rate_millihertz))
}

pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
Expand Down Expand Up @@ -268,29 +282,11 @@ impl MonitorHandle {
refresh_rate_millihertz
};

let pixel_encoding =
CFString::wrap_under_create_rule(ffi::CGDisplayModeCopyPixelEncoding(mode))
.to_string();
let bit_depth = if pixel_encoding.eq_ignore_ascii_case(ffi::IO32BitDirectPixels) {
32
} else if pixel_encoding.eq_ignore_ascii_case(ffi::IO16BitDirectPixels) {
16
} else if pixel_encoding.eq_ignore_ascii_case(ffi::kIO30BitDirectPixels) {
30
} else {
unimplemented!()
};

VideoModeHandle {
size: PhysicalSize::new(
ffi::CGDisplayModeGetPixelWidth(mode) as u32,
ffi::CGDisplayModeGetPixelHeight(mode) as u32,
),
VideoModeHandle::new(
monitor.clone(),
NativeDisplayMode(mode),
refresh_rate_millihertz,
bit_depth,
monitor: monitor.clone(),
native_mode: NativeDisplayMode(mode),
}
)
})
}
}
Expand Down Expand Up @@ -349,3 +345,26 @@ pub(crate) fn flip_window_screen_coordinates(frame: NSRect) -> NSPoint {
let y = main_screen_height - frame.size.height - frame.origin.y;
NSPoint::new(frame.origin.x, y)
}

fn refresh_rate_millihertz(id: CGDirectDisplayID, mode: &NativeDisplayMode) -> Option<u32> {
unsafe {
let refresh_rate = ffi::CGDisplayModeGetRefreshRate(mode.0);
if refresh_rate > 0.0 {
return Some((refresh_rate * 1000.0).round() as u32);
}

let mut display_link = std::ptr::null_mut();
if ffi::CVDisplayLinkCreateWithCGDisplay(id, &mut display_link) != ffi::kCVReturnSuccess {
return None;
}
let time = ffi::CVDisplayLinkGetNominalOutputVideoRefreshPeriod(display_link);
ffi::CVDisplayLinkRelease(display_link);

// This value is indefinite if an invalid display link was specified
if time.flags & ffi::kCVTimeIsIndefinite != 0 {
return None;
}

(time.time_scale as i64).checked_div(time.time_value).map(|v| (v * 1000) as u32)
}
}
10 changes: 10 additions & 0 deletions src/platform_impl/apple/uikit/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,16 @@ impl MonitorHandle {
Some(self.ui_screen.get_on_main(|ui_screen| refresh_rate_millihertz(ui_screen)))
}

pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
Some(run_on_main(|mtm| {
VideoModeHandle::new(
self.ui_screen(mtm).clone(),
self.ui_screen(mtm).currentMode().unwrap(),
mtm,
)
}))
}

pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
run_on_main(|mtm| {
let ui_screen = self.ui_screen(mtm);
Expand Down
5 changes: 5 additions & 0 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,11 @@ impl MonitorHandle {
x11_or_wayland!(match self; MonitorHandle(m) => m.scale_factor() as _)
}

#[inline]
pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
x11_or_wayland!(match self; MonitorHandle(m) => m.current_video_mode())
}

#[inline]
pub fn video_modes(&self) -> Box<dyn Iterator<Item = VideoModeHandle>> {
x11_or_wayland!(match self; MonitorHandle(m) => Box::new(m.video_modes()))
Expand Down
30 changes: 23 additions & 7 deletions src/platform_impl/linux/wayland/output.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use sctk::output::OutputData;
use sctk::output::{Mode, OutputData};
use sctk::reexports::client::protocol::wl_output::WlOutput;
use sctk::reexports::client::Proxy;

Expand Down Expand Up @@ -87,6 +87,18 @@ impl MonitorHandle {
output_data.scale_factor()
}

#[inline]
pub fn current_video_mode(&self) -> Option<PlatformVideoModeHandle> {
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(|mode| {
PlatformVideoModeHandle::Wayland(VideoModeHandle::new(self.clone(), mode))
})
})
}

#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = PlatformVideoModeHandle> {
let output_data = self.proxy.data::<OutputData>().unwrap();
Expand All @@ -95,12 +107,7 @@ impl MonitorHandle {
let monitor = self.clone();

modes.into_iter().map(move |mode| {
PlatformVideoModeHandle::Wayland(VideoModeHandle {
size: (mode.dimensions.0 as u32, mode.dimensions.1 as u32).into(),
refresh_rate_millihertz: mode.refresh_rate as u32,
bit_depth: 32,
monitor: monitor.clone(),
})
PlatformVideoModeHandle::Wayland(VideoModeHandle::new(monitor.clone(), mode))
})
}
}
Expand Down Expand Up @@ -140,6 +147,15 @@ pub struct VideoModeHandle {
}

impl VideoModeHandle {
fn new(monitor: MonitorHandle, mode: Mode) -> Self {
VideoModeHandle {
size: (mode.dimensions.0 as u32, mode.dimensions.1 as u32).into(),
refresh_rate_millihertz: mode.refresh_rate as u32,
bit_depth: 32,
monitor: monitor.clone(),
}
}

#[inline]
pub fn size(&self) -> PhysicalSize<u32> {
self.size
Expand Down
22 changes: 9 additions & 13 deletions src/platform_impl/linux/x11/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ impl XConnection {

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VideoModeHandle {
pub(crate) current: bool,
pub(crate) size: (u32, u32),
pub(crate) bit_depth: u16,
pub(crate) refresh_rate_millihertz: u32,
Expand Down Expand Up @@ -59,8 +60,6 @@ pub struct MonitorHandle {
position: (i32, i32),
/// If the monitor is the primary one
primary: bool,
/// The refresh rate used by monitor.
refresh_rate_millihertz: Option<u32>,
/// The DPI scale factor
pub(crate) scale_factor: f64,
/// Used to determine which windows are on this monitor
Expand Down Expand Up @@ -117,20 +116,11 @@ impl MonitorHandle {
let dimensions = (crtc.width as u32, crtc.height as u32);
let position = (crtc.x as i32, crtc.y as i32);

// Get the refresh rate of the current video mode.
let current_mode = crtc.mode;
let screen_modes = resources.modes();
let refresh_rate_millihertz = screen_modes
.iter()
.find(|mode| mode.id == current_mode)
.and_then(mode_refresh_rate_millihertz);

let rect = util::AaRect::new(position, dimensions);

Some(MonitorHandle {
id,
name,
refresh_rate_millihertz,
scale_factor,
dimensions,
position,
Expand All @@ -147,7 +137,6 @@ impl MonitorHandle {
scale_factor: 1.0,
dimensions: (1, 1),
position: (0, 0),
refresh_rate_millihertz: None,
primary: true,
rect: util::AaRect::new((0, 0), (1, 1)),
video_modes: Vec::new(),
Expand Down Expand Up @@ -177,14 +166,21 @@ impl MonitorHandle {
}

pub fn refresh_rate_millihertz(&self) -> Option<u32> {
self.refresh_rate_millihertz
self.video_modes
.iter()
.find_map(|mode| mode.current.then_some(mode.refresh_rate_millihertz))
}

#[inline]
pub fn scale_factor(&self) -> f64 {
self.scale_factor
}

#[inline]
pub fn current_video_mode(&self) -> Option<PlatformVideoModeHandle> {
self.video_modes.iter().find(|mode| mode.current).cloned().map(PlatformVideoModeHandle::X)
}

#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = PlatformVideoModeHandle> {
let monitor = self.clone();
Expand Down
2 changes: 2 additions & 0 deletions src/platform_impl/linux/x11/util/randr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ impl XConnection {
let bit_depth = self.default_root().root_depth;
let output_modes = &output_info.modes;
let resource_modes = resources.modes();
let current_mode = crtc.mode;

let modes = resource_modes
.iter()
Expand All @@ -82,6 +83,7 @@ impl XConnection {
.filter(|x| output_modes.iter().any(|id| x.id == *id))
.map(|mode| {
VideoModeHandle {
current: mode.id == current_mode,
size: (mode.width.into(), mode.height.into()),
refresh_rate_millihertz: monitor::mode_refresh_rate_millihertz(mode)
.unwrap_or(0),
Expand Down
8 changes: 6 additions & 2 deletions src/platform_impl/orbital/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,17 +208,21 @@ impl MonitorHandle {
None
}

pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
let size = self.size().into();
// FIXME this is not the real refresh rate
// (it is guaranteed to support 32 bit color though)
std::iter::once(VideoModeHandle {
Some(VideoModeHandle {
size,
bit_depth: 32,
refresh_rate_millihertz: 60000,
monitor: self.clone(),
})
}

pub fn video_modes(&self) -> impl Iterator<Item = VideoModeHandle> {
self.current_video_mode().into_iter()
}
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
Expand Down
4 changes: 4 additions & 0 deletions src/platform_impl/web/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ impl MonitorHandle {
unreachable!()
}

pub fn current_video_mode(&self) -> Option<VideoModeHandle> {
unreachable!()
}

pub fn video_modes(&self) -> Empty<VideoModeHandle> {
unreachable!()
}
Expand Down
Loading

0 comments on commit 521e071

Please sign in to comment.