* Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
181 lines
6.7 KiB
Rust
181 lines
6.7 KiB
Rust
use std::{env, slice, str::FromStr};
|
|
|
|
use super::{
|
|
ffi::{CurrentTime, RRCrtc, RRMode, Success, XRRCrtcInfo, XRRScreenResources},
|
|
*,
|
|
};
|
|
use crate::{dpi::validate_hidpi_factor, platform_impl::platform::x11::VideoMode};
|
|
|
|
pub fn calc_dpi_factor(
|
|
(width_px, height_px): (u32, u32),
|
|
(width_mm, height_mm): (u64, u64),
|
|
) -> f64 {
|
|
// Override DPI if `WINIT_HIDPI_FACTOR` variable is set
|
|
let dpi_override = env::var("WINIT_HIDPI_FACTOR")
|
|
.ok()
|
|
.and_then(|var| f64::from_str(&var).ok());
|
|
if let Some(dpi_override) = dpi_override {
|
|
if !validate_hidpi_factor(dpi_override) {
|
|
panic!(
|
|
"`WINIT_HIDPI_FACTOR` invalid; DPI factors must be normal floats greater than 0. Got `{}`",
|
|
dpi_override,
|
|
);
|
|
}
|
|
return dpi_override;
|
|
}
|
|
|
|
// See http://xpra.org/trac/ticket/728 for more information.
|
|
if width_mm == 0 || height_mm == 0 {
|
|
warn!("XRandR reported that the display's 0mm in size, which is certifiably insane");
|
|
return 1.0;
|
|
}
|
|
|
|
let ppmm = ((width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)).sqrt();
|
|
// Quantize 1/12 step size
|
|
let dpi_factor = ((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0);
|
|
assert!(validate_hidpi_factor(dpi_factor));
|
|
dpi_factor
|
|
}
|
|
|
|
impl XConnection {
|
|
// Retrieve DPI from Xft.dpi property
|
|
pub unsafe fn get_xft_dpi(&self) -> Option<f64> {
|
|
(self.xlib.XrmInitialize)();
|
|
let resource_manager_str = (self.xlib.XResourceManagerString)(self.display);
|
|
if resource_manager_str == ptr::null_mut() {
|
|
return None;
|
|
}
|
|
if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() {
|
|
let name: &str = "Xft.dpi:\t";
|
|
for pair in res.split("\n") {
|
|
if pair.starts_with(&name) {
|
|
let res = &pair[name.len()..];
|
|
return f64::from_str(&res).ok();
|
|
}
|
|
}
|
|
}
|
|
None
|
|
}
|
|
pub unsafe fn get_output_info(
|
|
&self,
|
|
resources: *mut XRRScreenResources,
|
|
crtc: *mut XRRCrtcInfo,
|
|
) -> Option<(String, f64, Vec<VideoMode>)> {
|
|
let output_info =
|
|
(self.xrandr.XRRGetOutputInfo)(self.display, resources, *(*crtc).outputs.offset(0));
|
|
if output_info.is_null() {
|
|
// When calling `XRRGetOutputInfo` on a virtual monitor (versus a physical display)
|
|
// it's possible for it to return null.
|
|
// https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816596
|
|
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 {
|
|
size: (x.width, x.height),
|
|
refresh_rate: (refresh_rate as f32 / 1000.0).round() as u16,
|
|
bit_depth: bit_depth as u16,
|
|
native_mode: x.id,
|
|
// This is populated in `MonitorHandle::video_modes` as the
|
|
// video mode is returned to the user
|
|
monitor: None,
|
|
}
|
|
});
|
|
|
|
let name_slice = slice::from_raw_parts(
|
|
(*output_info).name as *mut u8,
|
|
(*output_info).nameLen as usize,
|
|
);
|
|
let name = String::from_utf8_lossy(name_slice).into();
|
|
let hidpi_factor = if let Some(dpi) = self.get_xft_dpi() {
|
|
dpi / 96.
|
|
} else {
|
|
calc_dpi_factor(
|
|
((*crtc).width as u32, (*crtc).height as u32),
|
|
(
|
|
(*output_info).mm_width as u64,
|
|
(*output_info).mm_height as u64,
|
|
),
|
|
)
|
|
};
|
|
|
|
(self.xrandr.XRRFreeOutputInfo)(output_info);
|
|
Some((name, hidpi_factor, modes.collect()))
|
|
}
|
|
pub fn set_crtc_config(&self, crtc_id: RRCrtc, mode_id: RRMode) -> Result<(), ()> {
|
|
unsafe {
|
|
let mut major = 0;
|
|
let mut minor = 0;
|
|
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
|
|
|
|
let root = (self.xlib.XDefaultRootWindow)(self.display);
|
|
let resources = if (major == 1 && minor >= 3) || major > 1 {
|
|
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root)
|
|
} else {
|
|
(self.xrandr.XRRGetScreenResources)(self.display, root)
|
|
};
|
|
|
|
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
|
|
let status = (self.xrandr.XRRSetCrtcConfig)(
|
|
self.display,
|
|
resources,
|
|
crtc_id,
|
|
CurrentTime,
|
|
(*crtc).x,
|
|
(*crtc).y,
|
|
mode_id,
|
|
(*crtc).rotation,
|
|
(*crtc).outputs.offset(0),
|
|
1,
|
|
);
|
|
|
|
(self.xrandr.XRRFreeCrtcInfo)(crtc);
|
|
(self.xrandr.XRRFreeScreenResources)(resources);
|
|
|
|
if status == Success as i32 {
|
|
Ok(())
|
|
} else {
|
|
Err(())
|
|
}
|
|
}
|
|
}
|
|
pub fn get_crtc_mode(&self, crtc_id: RRCrtc) -> RRMode {
|
|
unsafe {
|
|
let mut major = 0;
|
|
let mut minor = 0;
|
|
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
|
|
|
|
let root = (self.xlib.XDefaultRootWindow)(self.display);
|
|
let resources = if (major == 1 && minor >= 3) || major > 1 {
|
|
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root)
|
|
} else {
|
|
(self.xrandr.XRRGetScreenResources)(self.display, root)
|
|
};
|
|
|
|
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
|
|
let mode = (*crtc).mode;
|
|
(self.xrandr.XRRFreeCrtcInfo)(crtc);
|
|
(self.xrandr.XRRFreeScreenResources)(resources);
|
|
mode
|
|
}
|
|
}
|
|
}
|