From cb0679b51a3da4c9a380f457958140818ba0238b Mon Sep 17 00:00:00 2001 From: John Nunley Date: Sat, 11 Nov 2023 16:31:08 -0800 Subject: [PATCH] Reload DPI on PropertyChange Supersedes #2874, fixes #1228 Signed-off-by: John Nunley --- .../linux/x11/event_processor.rs | 144 ++++++++++-------- src/platform_impl/linux/x11/window.rs | 3 +- 2 files changed, 80 insertions(+), 67 deletions(-) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 3e3c0f5e91..a832e1b347 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -570,6 +570,14 @@ impl EventProcessor { event: WindowEvent::Destroyed, }); } + ffi::PropertyNotify => { + let xev: &ffi::XPropertyEvent = xev.as_ref(); + let atom = xev.atom as xproto::Atom; + + if atom == xproto::Atom::from(xproto::AtomEnum::RESOURCE_MANAGER) { + self.process_dpi_change(&mut callback); + } + } ffi::VisibilityNotify => { let xev: &ffi::XVisibilityEvent = xev.as_ref(); @@ -1306,72 +1314,7 @@ impl EventProcessor { } } if event_type == self.randr_event_offset as c_int { - // In the future, it would be quite easy to emit monitor hotplug events. - let prev_list = wt.xconn.invalidate_cached_monitor_list(); - if let Some(prev_list) = prev_list { - let new_list = wt - .xconn - .available_monitors() - .expect("Failed to get monitor list"); - 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.shared_state_lock().last_monitor.clone(); - 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 inner_size = Arc::new(Mutex::new( - PhysicalSize::new(new_width, new_height), - )); - callback(Event::WindowEvent { - window_id, - event: WindowEvent::ScaleFactorChanged { - scale_factor: new_monitor.scale_factor, - inner_size_writer: InnerSizeWriter::new( - Arc::downgrade(&inner_size), - ), - }, - }); - - let new_inner_size = *inner_size.lock().unwrap(); - drop(inner_size); - - if new_inner_size != old_inner_size { - let (new_width, new_height) = new_inner_size.into(); - window.request_inner_size_physical( - new_width, new_height, - ); - } - } - } - } - } - } - } + self.process_dpi_change(&mut callback); } } } @@ -1464,6 +1407,75 @@ impl EventProcessor { }); } } + + fn process_dpi_change(&self, callback: &mut F) + where + F: FnMut(Event), + { + let wt = get_xtarget(&self.target); + + // In the future, it would be quite easy to emit monitor hotplug events. + let prev_list = wt.xconn.invalidate_cached_monitor_list(); + if let Some(prev_list) = prev_list { + let new_list = wt + .xconn + .available_monitors() + .expect("Failed to get monitor list"); + 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.shared_state_lock().last_monitor.clone(); + 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 inner_size = + Arc::new(Mutex::new(PhysicalSize::new(new_width, new_height))); + callback(Event::WindowEvent { + window_id, + event: WindowEvent::ScaleFactorChanged { + scale_factor: new_monitor.scale_factor, + inner_size_writer: InnerSizeWriter::new(Arc::downgrade( + &inner_size, + )), + }, + }); + + let new_inner_size = *inner_size.lock().unwrap(); + drop(inner_size); + + if new_inner_size != old_inner_size { + let (new_width, new_height) = new_inner_size.into(); + window.request_inner_size_physical(new_width, new_height); + } + } + } + } + } + } + } + } } fn is_first_touch(first: &mut Option, num: &mut u32, id: u64, phase: TouchPhase) -> bool { diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 1b08869f5f..cad9ab700e 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -276,7 +276,8 @@ impl UnownedWindow { | EventMask::KEYMAP_STATE | EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE - | EventMask::POINTER_MOTION; + | EventMask::POINTER_MOTION + | EventMask::PROPERTY_CHANGE; aux = aux.event_mask(event_mask).border_pixel(0);