2020-05-31 14:29:32 -04:00
|
|
|
/// A very light abstraction around the XCB connection.
|
|
|
|
///
|
|
|
|
/// Keeps track of the xcb connection itself and the xlib display ID that was used to connect.
|
2020-09-11 12:32:55 -05:00
|
|
|
use std::ffi::{CStr, CString};
|
2020-09-11 15:44:01 +02:00
|
|
|
|
2020-09-11 18:02:22 +02:00
|
|
|
pub(crate) struct Atoms {
|
|
|
|
pub wm_protocols: Option<u32>,
|
2020-09-11 12:32:55 -05:00
|
|
|
pub wm_delete_window: Option<u32>,
|
2020-09-11 18:02:22 +02:00
|
|
|
}
|
|
|
|
|
2020-05-31 14:29:32 -04:00
|
|
|
pub struct XcbConnection {
|
|
|
|
pub conn: xcb::Connection,
|
|
|
|
pub xlib_display: i32,
|
2020-09-11 18:02:22 +02:00
|
|
|
|
2020-09-11 12:32:55 -05:00
|
|
|
pub(crate) atoms: Atoms,
|
2020-05-31 14:29:32 -04:00
|
|
|
}
|
|
|
|
|
2020-09-11 18:02:22 +02:00
|
|
|
macro_rules! intern_atoms {
|
|
|
|
($conn:expr, $( $name:ident ),+ ) => {{
|
|
|
|
$(
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
let $name = xcb::intern_atom($conn, true, stringify!($name));
|
|
|
|
)+
|
|
|
|
|
|
|
|
// splitting request and reply to improve throughput
|
|
|
|
|
|
|
|
(
|
|
|
|
$( $name.get_reply()
|
|
|
|
.map(|r| r.atom())
|
|
|
|
.ok()),+
|
|
|
|
)
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
2020-05-31 14:29:32 -04:00
|
|
|
impl XcbConnection {
|
2020-09-11 15:47:00 +02:00
|
|
|
pub fn new() -> Result<Self, xcb::base::ConnError> {
|
2020-09-11 18:02:22 +02:00
|
|
|
let (conn, xlib_display) = xcb::Connection::connect_with_xlib_display()?;
|
|
|
|
|
2020-09-11 12:32:55 -05:00
|
|
|
let (wm_protocols, wm_delete_window) = intern_atoms!(&conn, WM_PROTOCOLS, WM_DELETE_WINDOW);
|
2020-09-11 18:02:22 +02:00
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
conn,
|
|
|
|
xlib_display,
|
|
|
|
|
|
|
|
atoms: Atoms {
|
|
|
|
wm_protocols,
|
2020-09-11 12:32:55 -05:00
|
|
|
wm_delete_window,
|
|
|
|
},
|
2020-09-11 18:02:22 +02:00
|
|
|
})
|
2020-05-31 14:29:32 -04:00
|
|
|
}
|
2020-09-11 15:44:01 +02:00
|
|
|
|
|
|
|
// Try to get the scaling with this function first.
|
|
|
|
// If this gives you `None`, fall back to `get_scaling_screen_dimensions`.
|
|
|
|
// If neither work, I guess just assume 96.0 and don't do any scaling.
|
2020-09-11 15:47:00 +02:00
|
|
|
fn get_scaling_xft(&self) -> Option<f64> {
|
2020-09-11 15:44:01 +02:00
|
|
|
use x11::xlib::{
|
2020-09-11 12:32:55 -05:00
|
|
|
XResourceManagerString, XrmDestroyDatabase, XrmGetResource, XrmGetStringDatabase,
|
|
|
|
XrmValue,
|
2020-09-11 15:44:01 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
let display = self.conn.get_raw_dpy();
|
|
|
|
unsafe {
|
|
|
|
let rms = XResourceManagerString(display);
|
|
|
|
if !rms.is_null() {
|
|
|
|
let db = XrmGetStringDatabase(rms);
|
|
|
|
if !db.is_null() {
|
|
|
|
let mut value = XrmValue {
|
|
|
|
size: 0,
|
|
|
|
addr: std::ptr::null_mut(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut value_type: *mut libc::c_char = std::ptr::null_mut();
|
|
|
|
let name_c_str = CString::new("Xft.dpi").unwrap();
|
|
|
|
let c_str = CString::new("Xft.Dpi").unwrap();
|
|
|
|
|
|
|
|
let dpi = if XrmGetResource(
|
|
|
|
db,
|
|
|
|
name_c_str.as_ptr(),
|
|
|
|
c_str.as_ptr(),
|
|
|
|
&mut value_type,
|
|
|
|
&mut value,
|
|
|
|
) != 0
|
|
|
|
&& !value.addr.is_null()
|
|
|
|
{
|
|
|
|
let value_addr: &CStr = CStr::from_ptr(value.addr);
|
|
|
|
value_addr.to_str().ok();
|
|
|
|
let value_str = value_addr.to_str().ok()?;
|
|
|
|
let value_f64: f64 = value_str.parse().ok()?;
|
|
|
|
let dpi_to_scale = value_f64 / 96.0;
|
|
|
|
Some(dpi_to_scale)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
XrmDestroyDatabase(db);
|
|
|
|
|
|
|
|
return dpi;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to get the scaling with `get_scaling_xft` first.
|
|
|
|
// Only use this function as a fallback.
|
|
|
|
// If neither work, I guess just assume 96.0 and don't do any scaling.
|
2020-09-11 15:47:00 +02:00
|
|
|
fn get_scaling_screen_dimensions(&self) -> Option<f64> {
|
2020-09-11 15:44:01 +02:00
|
|
|
// Figure out screen information
|
|
|
|
let setup = self.conn.get_setup();
|
2020-09-11 12:32:55 -05:00
|
|
|
let screen = setup.roots().nth(self.xlib_display as usize).unwrap();
|
2020-09-11 15:44:01 +02:00
|
|
|
|
|
|
|
// Get the DPI from the screen struct
|
|
|
|
//
|
|
|
|
// there are 2.54 centimeters to an inch; so there are 25.4 millimeters.
|
|
|
|
// dpi = N pixels / (M millimeters / (25.4 millimeters / 1 inch))
|
|
|
|
// = N pixels / (M inch / 25.4)
|
|
|
|
// = N * 25.4 pixels / M inch
|
|
|
|
let width_px = screen.width_in_pixels() as f64;
|
|
|
|
let width_mm = screen.width_in_millimeters() as f64;
|
|
|
|
let height_px = screen.height_in_pixels() as f64;
|
|
|
|
let height_mm = screen.height_in_millimeters() as f64;
|
|
|
|
let _xres = width_px * 25.4 / width_mm;
|
|
|
|
let yres = height_px * 25.4 / height_mm;
|
|
|
|
|
|
|
|
let yscale = yres / 96.0;
|
|
|
|
|
|
|
|
// TODO: choose between `xres` and `yres`? (probably both are the same?)
|
|
|
|
Some(yscale)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn get_scaling(&self) -> Option<f64> {
|
|
|
|
self.get_scaling_xft()
|
|
|
|
.or(self.get_scaling_screen_dimensions())
|
|
|
|
}
|
2020-05-31 14:29:32 -04:00
|
|
|
}
|