1 use std::{env, slice};
2 use std::str::FromStr;
3 
4 use validate_hidpi_factor;
5 use super::*;
6 
calc_dpi_factor( (width_px, height_px): (u32, u32), (width_mm, height_mm): (u64, u64), ) -> f647 pub fn calc_dpi_factor(
8     (width_px, height_px): (u32, u32),
9     (width_mm, height_mm): (u64, u64),
10 ) -> f64 {
11     // Override DPI if `WINIT_HIDPI_FACTOR` variable is set
12     let dpi_override = env::var("WINIT_HIDPI_FACTOR")
13         .ok()
14         .and_then(|var| f64::from_str(&var).ok());
15     if let Some(dpi_override) = dpi_override {
16         if !validate_hidpi_factor(dpi_override) {
17             panic!(
18                 "`WINIT_HIDPI_FACTOR` invalid; DPI factors must be normal floats greater than 0. Got `{}`",
19                 dpi_override,
20             );
21         }
22         return dpi_override;
23     }
24 
25     // See http://xpra.org/trac/ticket/728 for more information.
26     if width_mm == 0 || height_mm == 0 {
27         warn!("XRandR reported that the display's 0mm in size, which is certifiably insane");
28         return 1.0;
29     }
30 
31     let ppmm = (
32         (width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)
33     ).sqrt();
34     // Quantize 1/12 step size
35     let dpi_factor = ((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0);
36     assert!(validate_hidpi_factor(dpi_factor));
37     dpi_factor
38 }
39 
40 pub enum MonitorRepr {
41     Monitor(*mut ffi::XRRMonitorInfo),
42     Crtc(*mut ffi::XRRCrtcInfo),
43 }
44 
45 impl MonitorRepr {
get_output(&self) -> ffi::RROutput46     pub unsafe fn get_output(&self) -> ffi::RROutput {
47         match *self {
48             // Same member names, but different locations within the struct...
49             MonitorRepr::Monitor(monitor) => *((*monitor).outputs.offset(0)),
50             MonitorRepr::Crtc(crtc) => *((*crtc).outputs.offset(0)),
51         }
52     }
53 
get_dimensions(&self) -> (u32, u32)54     pub unsafe fn get_dimensions(&self) -> (u32, u32) {
55         match *self {
56             MonitorRepr::Monitor(monitor) => ((*monitor).width as u32, (*monitor).height as u32),
57             MonitorRepr::Crtc(crtc) => ((*crtc).width as u32, (*crtc).height as u32),
58         }
59     }
60 
get_position(&self) -> (i32, i32)61     pub unsafe fn get_position(&self) -> (i32, i32) {
62         match *self {
63             MonitorRepr::Monitor(monitor) => ((*monitor).x as i32, (*monitor).y as i32),
64             MonitorRepr::Crtc(crtc) => ((*crtc).x as i32, (*crtc).y as i32),
65         }
66     }
67 }
68 
69 impl From<*mut ffi::XRRMonitorInfo> for MonitorRepr {
from(monitor: *mut ffi::XRRMonitorInfo) -> Self70     fn from(monitor: *mut ffi::XRRMonitorInfo) -> Self {
71         MonitorRepr::Monitor(monitor)
72     }
73 }
74 
75 impl From<*mut ffi::XRRCrtcInfo> for MonitorRepr {
from(crtc: *mut ffi::XRRCrtcInfo) -> Self76     fn from(crtc: *mut ffi::XRRCrtcInfo) -> Self {
77         MonitorRepr::Crtc(crtc)
78     }
79 }
80 
81 impl XConnection {
82     // Retrieve DPI from Xft.dpi property
get_xft_dpi(&self) -> Option<f64>83     pub unsafe fn get_xft_dpi(&self) -> Option<f64> {
84         (self.xlib.XrmInitialize)();
85         let resource_manager_str = (self.xlib.XResourceManagerString)(self.display);
86         if resource_manager_str == ptr::null_mut() {
87             return None;
88         }
89         if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() {
90             let name : &str = "Xft.dpi:\t";
91             for pair in res.split("\n") {
92                 if pair.starts_with(&name) {
93                     let res = &pair[name.len()..];
94                     return f64::from_str(&res).ok();
95                 }
96             }
97         }
98         None
99     }
get_output_info( &self, resources: *mut ffi::XRRScreenResources, repr: &MonitorRepr, ) -> Option<(String, f64)>100     pub unsafe fn get_output_info(
101         &self,
102         resources: *mut ffi::XRRScreenResources,
103         repr: &MonitorRepr,
104     ) -> Option<(String, f64)> {
105         let output_info = (self.xrandr.XRRGetOutputInfo)(
106             self.display,
107             resources,
108             repr.get_output(),
109         );
110         if output_info.is_null() {
111             // When calling `XRRGetOutputInfo` on a virtual monitor (versus a physical display)
112             // it's possible for it to return null.
113             // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816596
114             let _ = self.check_errors(); // discard `BadRROutput` error
115             return None;
116         }
117         let name_slice = slice::from_raw_parts(
118             (*output_info).name as *mut u8,
119             (*output_info).nameLen as usize,
120         );
121         let name = String::from_utf8_lossy(name_slice).into();
122         let hidpi_factor = if let Some(dpi) = self.get_xft_dpi() {
123             dpi / 96.
124         } else {
125             calc_dpi_factor(
126                 repr.get_dimensions(),
127                 ((*output_info).mm_width as u64, (*output_info).mm_height as u64),
128             )
129         };
130 
131         (self.xrandr.XRRFreeOutputInfo)(output_info);
132         Some((name, hidpi_factor))
133     }
134 }
135