2018-05-14 08:14:57 -04:00
|
|
|
use std::os::raw::*;
|
2014-09-19 15:42:47 +02:00
|
|
|
|
2022-06-08 11:50:26 -07:00
|
|
|
use once_cell::sync::Lazy;
|
2018-05-14 08:14:57 -04:00
|
|
|
use parking_lot::Mutex;
|
2014-09-19 15:42:47 +02:00
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
use super::{
|
|
|
|
|
ffi::{
|
2019-07-29 21:16:14 +03:00
|
|
|
RRCrtc, RRCrtcChangeNotifyMask, RRMode, RROutputPropertyNotifyMask,
|
|
|
|
|
RRScreenChangeNotifyMask, True, Window, XRRCrtcInfo, XRRScreenResources,
|
2019-06-21 11:33:15 -04:00
|
|
|
},
|
|
|
|
|
util, XConnection, XError,
|
|
|
|
|
};
|
|
|
|
|
use crate::{
|
|
|
|
|
dpi::{PhysicalPosition, PhysicalSize},
|
2019-07-29 21:16:14 +03:00
|
|
|
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
|
|
|
|
|
platform_impl::{MonitorHandle as PlatformMonitorHandle, VideoMode as PlatformVideoMode},
|
2018-05-14 08:14:57 -04:00
|
|
|
};
|
|
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
// Used for testing. This should always be committed as false.
|
2018-05-14 08:14:57 -04:00
|
|
|
const DISABLE_MONITOR_LIST_CACHING: bool = false;
|
|
|
|
|
|
2022-06-08 11:50:26 -07:00
|
|
|
static MONITORS: Lazy<Mutex<Option<Vec<MonitorHandle>>>> = Lazy::new(Mutex::default);
|
2018-05-22 09:07:46 -04:00
|
|
|
|
2019-02-05 10:30:33 -05:00
|
|
|
pub fn invalidate_cached_monitor_list() -> Option<Vec<MonitorHandle>> {
|
2018-05-14 08:14:57 -04:00
|
|
|
// We update this lazily.
|
|
|
|
|
(*MONITORS.lock()).take()
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
|
pub struct VideoMode {
|
|
|
|
|
pub(crate) size: (u32, u32),
|
|
|
|
|
pub(crate) bit_depth: u16,
|
|
|
|
|
pub(crate) refresh_rate: u16,
|
|
|
|
|
pub(crate) native_mode: RRMode,
|
|
|
|
|
pub(crate) monitor: Option<MonitorHandle>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl VideoMode {
|
|
|
|
|
#[inline]
|
2020-01-04 01:33:07 -05:00
|
|
|
pub fn size(&self) -> PhysicalSize<u32> {
|
2019-07-29 21:16:14 +03:00
|
|
|
self.size.into()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn bit_depth(&self) -> u16 {
|
|
|
|
|
self.bit_depth
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn refresh_rate(&self) -> u16 {
|
|
|
|
|
self.refresh_rate
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn monitor(&self) -> RootMonitorHandle {
|
|
|
|
|
RootMonitorHandle {
|
|
|
|
|
inner: PlatformMonitorHandle::X(self.monitor.clone().unwrap()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-14 08:14:57 -04:00
|
|
|
#[derive(Debug, Clone)]
|
2019-02-05 10:30:33 -05:00
|
|
|
pub struct MonitorHandle {
|
2017-09-07 09:33:46 +01:00
|
|
|
/// The actual id
|
2019-07-29 21:16:14 +03:00
|
|
|
pub(crate) id: RRCrtc,
|
2017-09-07 09:33:46 +01:00
|
|
|
/// The name of the monitor
|
2018-06-14 19:42:18 -04:00
|
|
|
pub(crate) name: String,
|
2017-09-07 09:33:46 +01:00
|
|
|
/// The size of the monitor
|
|
|
|
|
dimensions: (u32, u32),
|
|
|
|
|
/// The position of the monitor in the X screen
|
2017-10-19 20:08:05 +03:00
|
|
|
position: (i32, i32),
|
2017-09-07 09:33:46 +01:00
|
|
|
/// If the monitor is the primary one
|
|
|
|
|
primary: bool,
|
2018-05-14 08:14:57 -04:00
|
|
|
/// The DPI scale factor
|
2020-01-03 14:52:27 -05:00
|
|
|
pub(crate) scale_factor: f64,
|
2018-05-14 08:14:57 -04:00
|
|
|
/// Used to determine which windows are on this monitor
|
2018-07-01 11:01:46 -04:00
|
|
|
pub(crate) rect: util::AaRect,
|
2019-06-12 21:07:25 +03:00
|
|
|
/// Supported video modes on this monitor
|
|
|
|
|
video_modes: Vec<VideoMode>,
|
2017-09-07 09:33:46 +01:00
|
|
|
}
|
2015-05-07 13:14:09 +02:00
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
impl PartialEq for MonitorHandle {
|
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
|
self.id == other.id
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Eq for MonitorHandle {}
|
|
|
|
|
|
|
|
|
|
impl PartialOrd for MonitorHandle {
|
|
|
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
2022-01-01 13:00:11 +11:00
|
|
|
Some(self.cmp(other))
|
2019-07-29 21:16:14 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Ord for MonitorHandle {
|
|
|
|
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
|
|
|
self.id.cmp(&other.id)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl std::hash::Hash for MonitorHandle {
|
|
|
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
|
|
|
self.id.hash(state);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-05 10:30:33 -05:00
|
|
|
impl MonitorHandle {
|
2019-07-29 21:16:14 +03:00
|
|
|
fn new(
|
2018-06-14 19:42:18 -04:00
|
|
|
xconn: &XConnection,
|
2018-05-14 08:14:57 -04:00
|
|
|
resources: *mut XRRScreenResources,
|
2019-07-29 21:16:14 +03:00
|
|
|
id: RRCrtc,
|
|
|
|
|
crtc: *mut XRRCrtcInfo,
|
2018-05-14 08:14:57 -04:00
|
|
|
primary: bool,
|
2018-11-17 15:51:39 -05:00
|
|
|
) -> Option<Self> {
|
2020-01-03 14:52:27 -05:00
|
|
|
let (name, scale_factor, video_modes) = unsafe { xconn.get_output_info(resources, crtc)? };
|
2019-07-29 21:16:14 +03:00
|
|
|
let dimensions = unsafe { ((*crtc).width as u32, (*crtc).height as u32) };
|
|
|
|
|
let position = unsafe { ((*crtc).x as i32, (*crtc).y as i32) };
|
2018-07-01 11:01:46 -04:00
|
|
|
let rect = util::AaRect::new(position, dimensions);
|
2019-02-05 10:30:33 -05:00
|
|
|
Some(MonitorHandle {
|
2018-05-14 08:14:57 -04:00
|
|
|
id,
|
|
|
|
|
name,
|
2020-01-03 14:52:27 -05:00
|
|
|
scale_factor,
|
2018-05-14 08:14:57 -04:00
|
|
|
dimensions,
|
|
|
|
|
position,
|
|
|
|
|
primary,
|
|
|
|
|
rect,
|
2019-06-12 21:07:25 +03:00
|
|
|
video_modes,
|
2018-11-17 15:51:39 -05:00
|
|
|
})
|
2017-09-07 09:33:46 +01:00
|
|
|
}
|
2014-09-19 15:42:47 +02:00
|
|
|
|
2019-11-10 13:55:29 -07:00
|
|
|
pub fn dummy() -> Self {
|
2019-09-23 11:45:29 -07:00
|
|
|
MonitorHandle {
|
|
|
|
|
id: 0,
|
|
|
|
|
name: "<dummy monitor>".into(),
|
2020-01-03 14:52:27 -05:00
|
|
|
scale_factor: 1.0,
|
2019-09-23 11:45:29 -07:00
|
|
|
dimensions: (1, 1),
|
|
|
|
|
position: (0, 0),
|
|
|
|
|
primary: true,
|
|
|
|
|
rect: util::AaRect::new((0, 0), (1, 1)),
|
|
|
|
|
video_modes: Vec::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-04 10:18:20 -07:00
|
|
|
pub(crate) fn is_dummy(&self) -> bool {
|
|
|
|
|
// Zero is an invalid XID value; no real monitor will have it
|
|
|
|
|
self.id == 0
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn name(&self) -> Option<String> {
|
2017-09-07 09:33:46 +01:00
|
|
|
Some(self.name.clone())
|
2014-09-19 15:42:47 +02:00
|
|
|
}
|
|
|
|
|
|
2015-09-21 14:42:05 +02:00
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn native_identifier(&self) -> u32 {
|
2017-09-07 09:33:46 +01:00
|
|
|
self.id as u32
|
2015-03-16 13:52:58 -07:00
|
|
|
}
|
|
|
|
|
|
2020-01-04 01:33:07 -05:00
|
|
|
pub fn size(&self) -> PhysicalSize<u32> {
|
2018-06-14 19:42:18 -04:00
|
|
|
self.dimensions.into()
|
2017-09-07 09:33:46 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-04 01:33:07 -05:00
|
|
|
pub fn position(&self) -> PhysicalPosition<i32> {
|
2018-06-14 19:42:18 -04:00
|
|
|
self.position.into()
|
2014-09-19 15:42:47 +02:00
|
|
|
}
|
2017-10-17 14:56:38 +03:00
|
|
|
|
|
|
|
|
#[inline]
|
2020-01-03 14:52:27 -05:00
|
|
|
pub fn scale_factor(&self) -> f64 {
|
|
|
|
|
self.scale_factor
|
2017-10-17 14:56:38 +03:00
|
|
|
}
|
2019-06-12 21:07:25 +03:00
|
|
|
|
|
|
|
|
#[inline]
|
2019-07-29 21:16:14 +03:00
|
|
|
pub fn video_modes(&self) -> impl Iterator<Item = RootVideoMode> {
|
|
|
|
|
let monitor = self.clone();
|
|
|
|
|
self.video_modes.clone().into_iter().map(move |mut x| {
|
|
|
|
|
x.monitor = Some(monitor.clone());
|
|
|
|
|
RootVideoMode {
|
|
|
|
|
video_mode: PlatformVideoMode::X(x),
|
|
|
|
|
}
|
|
|
|
|
})
|
2019-06-12 21:07:25 +03:00
|
|
|
}
|
2014-09-19 15:42:47 +02:00
|
|
|
}
|
2018-05-14 08:14:57 -04:00
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
impl XConnection {
|
2019-02-05 10:30:33 -05:00
|
|
|
pub fn get_monitor_for_window(&self, window_rect: Option<util::AaRect>) -> MonitorHandle {
|
2019-05-29 21:29:54 -04:00
|
|
|
let monitors = self.available_monitors();
|
2019-09-23 11:45:29 -07:00
|
|
|
|
|
|
|
|
if monitors.is_empty() {
|
|
|
|
|
// Return a dummy monitor to avoid panicking
|
|
|
|
|
return MonitorHandle::dummy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let default = monitors.get(0).unwrap();
|
2018-05-14 08:14:57 -04:00
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
let window_rect = match window_rect {
|
|
|
|
|
Some(rect) => rect,
|
|
|
|
|
None => return default.to_owned(),
|
|
|
|
|
};
|
2018-05-14 08:14:57 -04:00
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
let mut largest_overlap = 0;
|
|
|
|
|
let mut matched_monitor = default;
|
|
|
|
|
for monitor in &monitors {
|
|
|
|
|
let overlapping_area = window_rect.get_overlapping_area(&monitor.rect);
|
|
|
|
|
if overlapping_area > largest_overlap {
|
|
|
|
|
largest_overlap = overlapping_area;
|
2022-01-01 13:00:11 +11:00
|
|
|
matched_monitor = monitor;
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
2018-05-14 08:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
matched_monitor.to_owned()
|
|
|
|
|
}
|
2018-05-14 08:14:57 -04:00
|
|
|
|
2019-02-05 10:30:33 -05:00
|
|
|
fn query_monitor_list(&self) -> Vec<MonitorHandle> {
|
2018-06-14 19:42:18 -04:00
|
|
|
unsafe {
|
2019-07-29 21:16:14 +03:00
|
|
|
let mut major = 0;
|
|
|
|
|
let mut minor = 0;
|
|
|
|
|
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
|
|
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
let root = (self.xlib.XDefaultRootWindow)(self.display);
|
2019-07-29 21:16:14 +03:00
|
|
|
let resources = if (major == 1 && minor >= 3) || major > 1 {
|
2019-03-05 18:58:14 -07:00
|
|
|
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root)
|
|
|
|
|
} else {
|
|
|
|
|
// WARNING: this function is supposedly very slow, on the order of hundreds of ms.
|
|
|
|
|
// Upon failure, `resources` will be null.
|
|
|
|
|
(self.xrandr.XRRGetScreenResources)(self.display, root)
|
|
|
|
|
};
|
|
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
if resources.is_null() {
|
|
|
|
|
panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist.");
|
|
|
|
|
}
|
2018-05-14 08:14:57 -04:00
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
let mut has_primary = false;
|
2018-05-14 08:14:57 -04:00
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
let primary = (self.xrandr.XRRGetOutputPrimary)(self.display, root);
|
2022-06-10 13:43:33 +03:00
|
|
|
let mut available = Vec::with_capacity((*resources).ncrtc as usize);
|
|
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
for crtc_index in 0..(*resources).ncrtc {
|
|
|
|
|
let crtc_id = *((*resources).crtcs.offset(crtc_index as isize));
|
|
|
|
|
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
|
|
|
|
|
let is_active = (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0;
|
|
|
|
|
if is_active {
|
|
|
|
|
let is_primary = *(*crtc).outputs.offset(0) == primary;
|
2018-05-22 09:07:46 -04:00
|
|
|
has_primary |= is_primary;
|
2022-01-01 13:00:11 +11:00
|
|
|
if let Some(monitor_id) =
|
|
|
|
|
MonitorHandle::new(self, resources, crtc_id, crtc, is_primary)
|
|
|
|
|
{
|
|
|
|
|
available.push(monitor_id)
|
|
|
|
|
}
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
2019-07-29 21:16:14 +03:00
|
|
|
(self.xrandr.XRRFreeCrtcInfo)(crtc);
|
2018-05-14 08:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
// If no monitors were detected as being primary, we just pick one ourselves!
|
|
|
|
|
if !has_primary {
|
|
|
|
|
if let Some(ref mut fallback) = available.first_mut() {
|
|
|
|
|
// Setting this here will come in handy if we ever add an `is_primary` method.
|
|
|
|
|
fallback.primary = true;
|
|
|
|
|
}
|
2018-05-22 09:07:46 -04:00
|
|
|
}
|
|
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
(self.xrandr.XRRFreeScreenResources)(resources);
|
|
|
|
|
available
|
|
|
|
|
}
|
2018-05-14 08:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn available_monitors(&self) -> Vec<MonitorHandle> {
|
2018-06-14 19:42:18 -04:00
|
|
|
let mut monitors_lock = MONITORS.lock();
|
|
|
|
|
(*monitors_lock)
|
|
|
|
|
.as_ref()
|
|
|
|
|
.cloned()
|
|
|
|
|
.or_else(|| {
|
|
|
|
|
let monitors = Some(self.query_monitor_list());
|
|
|
|
|
if !DISABLE_MONITOR_LIST_CACHING {
|
|
|
|
|
(*monitors_lock) = monitors.clone();
|
|
|
|
|
}
|
|
|
|
|
monitors
|
|
|
|
|
})
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
2018-05-14 08:14:57 -04:00
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn primary_monitor(&self) -> MonitorHandle {
|
|
|
|
|
self.available_monitors()
|
2018-06-14 19:42:18 -04:00
|
|
|
.into_iter()
|
|
|
|
|
.find(|monitor| monitor.primary)
|
2019-09-23 11:45:29 -07:00
|
|
|
.unwrap_or_else(MonitorHandle::dummy)
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
2018-05-14 08:14:57 -04:00
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
pub fn select_xrandr_input(&self, root: Window) -> Result<c_int, XError> {
|
2019-07-29 21:16:14 +03:00
|
|
|
let has_xrandr = unsafe {
|
|
|
|
|
let mut major = 0;
|
|
|
|
|
let mut minor = 0;
|
|
|
|
|
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor)
|
|
|
|
|
};
|
|
|
|
|
assert!(
|
|
|
|
|
has_xrandr == True,
|
|
|
|
|
"[winit] XRandR extension not available."
|
|
|
|
|
);
|
2018-05-14 08:14:57 -04:00
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
let mut event_offset = 0;
|
|
|
|
|
let mut error_offset = 0;
|
|
|
|
|
let status = unsafe {
|
2019-06-21 11:33:15 -04:00
|
|
|
(self.xrandr.XRRQueryExtension)(self.display, &mut event_offset, &mut error_offset)
|
2018-06-14 19:42:18 -04:00
|
|
|
};
|
2018-05-14 08:14:57 -04:00
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
if status != True {
|
|
|
|
|
self.check_errors()?;
|
|
|
|
|
unreachable!("[winit] `XRRQueryExtension` failed but no error was received.");
|
|
|
|
|
}
|
2018-05-14 08:14:57 -04:00
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
let mask = RRCrtcChangeNotifyMask | RROutputPropertyNotifyMask | RRScreenChangeNotifyMask;
|
2018-06-14 19:42:18 -04:00
|
|
|
unsafe { (self.xrandr.XRRSelectInput)(self.display, root, mask) };
|
2018-05-14 08:14:57 -04:00
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
Ok(event_offset)
|
|
|
|
|
}
|
2018-05-14 08:14:57 -04:00
|
|
|
}
|