Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x11: reload dpi on PropertyChangeEvent #2874

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 78 additions & 56 deletions src/platform_impl/linux/x11/event_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,17 @@ impl<T: 'static> EventProcessor<T> {
_ => {}
}
}

ffi::PropertyNotify => {
let xev: &ffi::XPropertyEvent = xev.as_ref();

// XA_RESOURCE_MANAGER received from the root window indicates that xresources
// have been changed. Check and update Xft.dpi if the dpi value was changed.
if xev.atom == ffi::XA_RESOURCE_MANAGER {
self.process_dpi_change(&mut callback)
}
}

_ => {
if event_type == self.xkbext.first_event_id {
let xev = unsafe { &*(xev as *const _ as *const ffi::XkbAnyEvent) };
Expand Down Expand Up @@ -1153,62 +1164,7 @@ impl<T: 'static> EventProcessor<T> {
}
}
if event_type == self.randr_event_offset {
// In the future, it would be quite easy to emit monitor hotplug events.
let prev_list = monitor::invalidate_cached_monitor_list();
if let Some(prev_list) = prev_list {
let new_list = wt.xconn.available_monitors();
for new_monitor in new_list {
// Previous list may be empty, in case of disconnecting and
// reconnecting the only one monitor. We still need to emit events in
// this case.
let maybe_prev_scale_factor = prev_list
.iter()
.find(|prev_monitor| prev_monitor.name == new_monitor.name)
.map(|prev_monitor| prev_monitor.scale_factor);
if Some(new_monitor.scale_factor) != maybe_prev_scale_factor {
for (window_id, window) in wt.windows.borrow().iter() {
if let Some(window) = window.upgrade() {
// Check if the window is on this monitor
let monitor = window.current_monitor();
if monitor.name == new_monitor.name {
let (width, height) = window.inner_size_physical();
let (new_width, new_height) = window.adjust_for_dpi(
// If we couldn't determine the previous scale
// factor (e.g., because all monitors were closed
// before), just pick whatever the current monitor
// has set as a baseline.
maybe_prev_scale_factor
.unwrap_or(monitor.scale_factor),
new_monitor.scale_factor,
width,
height,
&window.shared_state_lock(),
);

let window_id = crate::window::WindowId(*window_id);
let old_inner_size = PhysicalSize::new(width, height);
let mut new_inner_size =
PhysicalSize::new(new_width, new_height);

callback(Event::WindowEvent {
window_id,
event: WindowEvent::ScaleFactorChanged {
scale_factor: new_monitor.scale_factor,
new_inner_size: &mut new_inner_size,
},
});

if new_inner_size != old_inner_size {
let (new_width, new_height) = new_inner_size.into();
window
.set_inner_size_physical(new_width, new_height);
}
}
}
}
}
}
}
self.process_dpi_change(&mut callback)
}
}
}
Expand Down Expand Up @@ -1301,6 +1257,72 @@ impl<T: 'static> EventProcessor<T> {
});
}
}

fn process_dpi_change<F>(
&self,
callback: &mut F,
) where
F: FnMut(Event<'_, T>)
{
let wt = get_xtarget(&self.target);

// In the future, it would be quite easy to emit monitor hotplug events.
let prev_list = monitor::invalidate_cached_monitor_list();
if let Some(prev_list) = prev_list {
let new_list = wt.xconn.available_monitors();
for new_monitor in new_list {
// Previous list may be empty, in case of disconnecting and
// reconnecting the only one monitor. We still need to emit events in
// this case.
let maybe_prev_scale_factor = prev_list
.iter()
.find(|prev_monitor| prev_monitor.name == new_monitor.name)
.map(|prev_monitor| prev_monitor.scale_factor);
if Some(new_monitor.scale_factor) != maybe_prev_scale_factor {
for (window_id, window) in wt.windows.borrow().iter() {
if let Some(window) = window.upgrade() {
// Check if the window is on this monitor
let monitor = window.current_monitor();
if monitor.name == new_monitor.name {
let (width, height) = window.inner_size_physical();
let (new_width, new_height) = window.adjust_for_dpi(
// If we couldn't determine the previous scale
// factor (e.g., because all monitors were closed
// before), just pick whatever the current monitor
// has set as a baseline.
maybe_prev_scale_factor
.unwrap_or(monitor.scale_factor),
new_monitor.scale_factor,
width,
height,
&window.shared_state_lock(),
);

let window_id = crate::window::WindowId(*window_id);
let old_inner_size = PhysicalSize::new(width, height);
let mut new_inner_size =
PhysicalSize::new(new_width, new_height);

callback(Event::WindowEvent {
window_id,
event: WindowEvent::ScaleFactorChanged {
scale_factor: new_monitor.scale_factor,
new_inner_size: &mut new_inner_size,
},
});

if new_inner_size != old_inner_size {
let (new_width, new_height) = new_inner_size.into();
window
.set_inner_size_physical(new_width, new_height);
}
}
}
}
}
}
}
}
}

fn is_first_touch(first: &mut Option<u64>, num: &mut u32, id: u64, phase: TouchPhase) -> bool {
Expand Down
24 changes: 19 additions & 5 deletions src/platform_impl/linux/x11/util/randr.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use std::{env, slice, str::FromStr};


use super::{
ffi::{CurrentTime, RRCrtc, RRMode, Success, XRRCrtcInfo, XRRScreenResources},
*,
};
use crate::platform_impl::platform::x11::monitor;
use crate::{dpi::validate_scale_factor, platform_impl::platform::x11::VideoMode};
use x11_dl::xlib::XTextProperty;

/// Represents values of `WINIT_HIDPI_FACTOR`.
pub enum EnvVarDPI {
Expand Down Expand Up @@ -39,11 +41,23 @@ 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.is_null() {
return None;
}
if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() {

// Logic inspired by dunst
// https://github.com/dunst-project/dunst/commit/812d5a3b84c093adfbdab9939ee57545e442090c
(self.xlib.XFlush)(self.display);

let default_screen = (self.xlib.XDefaultScreen)(self.display);
let root = (self.xlib.XRootWindow)(self.display, default_screen);

let prop = &mut XTextProperty { value: ptr::null_mut(), encoding: 0, format: 0, nitems: 0 };

(self.xlib.XLockDisplay)(self.display);
(self.xlib.XGetTextProperty)(self.display, root, &mut *prop, ffi::XA_RESOURCE_MANAGER);

(self.xlib.XFlush)(self.display);
(self.xlib.XSync)(self.display, 0);

if let Ok(res) = ::std::ffi::CStr::from_ptr(prop.value as *const i8).to_str() {
let name: &str = "Xft.dpi:\t";
for pair in res.split('\n') {
if let Some(stripped) = pair.strip_prefix(name) {
Expand Down
8 changes: 8 additions & 0 deletions src/platform_impl/linux/x11/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,14 @@ impl UnownedWindow {
)
};

unsafe {
(xconn.xlib.XSelectInput)(
xconn.display,
root,
ffi::PropertyChangeMask,
);
}

#[allow(clippy::mutex_atomic)]
let mut window = UnownedWindow {
xconn: Arc::clone(xconn),
Expand Down