x11: compute resize logical size with new dpi (#668)

* x11: compute resize logical size with new dpi

Whenever a dpi change occurs, trigger a Resized event as well with the
new logical size. Given X11 primarily deals in physical pixels, a change
in DPI also changes the logical size (as the physical size remains
fixed).

* Doc tweaks
This commit is contained in:
Victor Berger 2018-10-19 22:32:57 +02:00 committed by Francesca Plebani
parent f486845f7f
commit c744b016ce
3 changed files with 54 additions and 39 deletions

View file

@ -14,8 +14,10 @@
- On Windows, `Window::set_cursor` now applies immediately instead of requiring specific events to occur first. - On Windows, `Window::set_cursor` now applies immediately instead of requiring specific events to occur first.
- On Windows, fix `Window::set_maximized`. - On Windows, fix `Window::set_maximized`.
- On Windows 10, fix transparency (#260). - On Windows 10, fix transparency (#260).
- on macOS, fix modifiers during key repeat. - On macOS, fix modifiers during key repeat.
- Implemented the `Debug` trait for `Window`, `EventsLoop`, `EventsLoopProxy` and `WindowBuilder`. - Implemented the `Debug` trait for `Window`, `EventsLoop`, `EventsLoopProxy` and `WindowBuilder`.
- On X11, now a `Resized` event will always be generated after a DPI change to ensure the window's logical size is consistent with the new DPI.
- Added further clarifications to the DPI docs.
# Version 0.17.2 (2018-08-19) # Version 0.17.2 (2018-08-19)

View file

@ -29,9 +29,9 @@
//! them entering an existential panic. Once users enter that state, they will no longer be focused on your application. //! them entering an existential panic. Once users enter that state, they will no longer be focused on your application.
//! //!
//! There are two ways to get the DPI factor: //! There are two ways to get the DPI factor:
//! - You can track the `WindowEvent::HiDpiFactorChanged` event of your windows. This event is sent any //! - You can track the [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) event of your
//! time the DPI factor changes, be it because the window moved to another monitor, or because the //! windows. This event is sent any time the DPI factor changes, either because the window moved to another monitor,
//! user changed the configuration of their screen. //! or because the user changed the configuration of their screen.
//! - You can also retrieve the DPI factor of a monitor by calling //! - You can also retrieve the DPI factor of a monitor by calling
//! [`MonitorId::get_hidpi_factor`](../struct.MonitorId.html#method.get_hidpi_factor), or the //! [`MonitorId::get_hidpi_factor`](../struct.MonitorId.html#method.get_hidpi_factor), or the
//! current DPI factor applied to a window by calling //! current DPI factor applied to a window by calling
@ -40,8 +40,8 @@
//! //!
//! Depending on the platform, the window's actual DPI factor may only be known after //! Depending on the platform, the window's actual DPI factor may only be known after
//! the event loop has started and your window has been drawn once. To properly handle these cases, //! the event loop has started and your window has been drawn once. To properly handle these cases,
//! the most robust way is to monitor the `WindowEvent::HiDpiFactorChanged` event and dynamically //! the most robust way is to monitor the [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged)
//! adapt your drawing logic to follow the DPI factor. //! event and dynamically adapt your drawing logic to follow the DPI factor.
//! //!
//! Here's an overview of what sort of DPI factors you can expect, and where they come from: //! Here's an overview of what sort of DPI factors you can expect, and where they come from:
//! - **Windows:** On Windows 8 and 10, per-monitor scaling is readily configured by users from the display settings. //! - **Windows:** On Windows 8 and 10, per-monitor scaling is readily configured by users from the display settings.
@ -53,18 +53,28 @@
//! - **X11:** On X11, we calcuate the DPI factor based on the millimeter dimensions provided by XRandR. This can //! - **X11:** On X11, we calcuate the DPI factor based on the millimeter dimensions provided by XRandR. This can
//! result in a wide range of possible values, including some interesting ones like 1.0833333333333333. This can be //! result in a wide range of possible values, including some interesting ones like 1.0833333333333333. This can be
//! overridden using the `WINIT_HIDPI_FACTOR` environment variable, though that's not recommended. //! overridden using the `WINIT_HIDPI_FACTOR` environment variable, though that's not recommended.
//! - **Wayland:** On Wayland, DPI factors are very much at the discretion of the user. //! - **Wayland:** On Wayland, DPI factors are set per-screen by the server, and are always integers (most often 1 or 2).
//! - **iOS:** DPI factors are both constant and device-specific on iOS. //! - **iOS:** DPI factors are both constant and device-specific on iOS.
//! - **Android:** This feature isn't yet implemented on Android, so the DPI factor will always be returned as 1.0. //! - **Android:** This feature isn't yet implemented on Android, so the DPI factor will always be returned as 1.0.
//! //!
//! The window's logical size is conserved across DPI changes, resulting in the physical size changing instead. This //! The window's logical size is conserved across DPI changes, resulting in the physical size changing instead. This
//! may be surprising on X11, but is quite standard elsewhere. Physical size changes produce a //! may be surprising on X11, but is quite standard elsewhere. Physical size changes always produce a
//! [`Resized`](../enum.WindowEvent.html#variant.Resized) event, even on platforms where no resize actually occurs, //! [`Resized`](../enum.WindowEvent.html#variant.Resized) event, even on platforms where no resize actually occurs,
//! such as macOS and Wayland. As a result, it's not necessary to separately handle //! such as macOS and Wayland. As a result, it's not necessary to separately handle
//! [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) if you're only listening for size. //! [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) if you're only listening for size.
//! //!
//! Your GPU has no awareness of the concept of logical pixels, and unless you like wasting pixel density, your //! Your GPU has no awareness of the concept of logical pixels, and unless you like wasting pixel density, your
//! framebuffer's size should be in physical pixels. //! framebuffer's size should be in physical pixels.
//!
//! `winit` will send [`Resized`](../enum.WindowEvent.html#variant.Resized) events whenever a window's logical size
//! changes, and [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) events
//! whenever the DPI factor changes. Receiving either of these events means that the physical size of your window has
//! changed, and you should recompute it using the latest values you received for each. If the logical size and the
//! DPI factor change simultaneously, `winit` will send both events together; thus, it's recommended to buffer
//! these events and process them at the end of the queue.
//!
//! If you never received any [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) events,
//! then your window's DPI factor is 1.
/// Checks that the DPI factor is a normal positive `f64`. /// Checks that the DPI factor is a normal positive `f64`.
/// ///

View file

@ -412,10 +412,10 @@ impl EventsLoop {
let new_inner_size = (xev.width as u32, xev.height as u32); let new_inner_size = (xev.width as u32, xev.height as u32);
let new_inner_position = (xev.x as i32, xev.y as i32); let new_inner_position = (xev.x as i32, xev.y as i32);
let monitor = window.get_current_monitor(); // This must be done *before* locking! let mut monitor = window.get_current_monitor(); // This must be done *before* locking!
let mut shared_state_lock = window.shared_state.lock(); let mut shared_state_lock = window.shared_state.lock();
let (resized, moved) = { let (mut resized, moved) = {
let resized = util::maybe_change(&mut shared_state_lock.size, new_inner_size); let resized = util::maybe_change(&mut shared_state_lock.size, new_inner_size);
let moved = if is_synthetic { let moved = if is_synthetic {
util::maybe_change(&mut shared_state_lock.inner_position, new_inner_position) util::maybe_change(&mut shared_state_lock.inner_position, new_inner_position)
@ -435,32 +435,8 @@ impl EventsLoop {
(resized, moved) (resized, moved)
}; };
// This is a hack to ensure that the DPI adjusted resize is actually applied on all WMs. KWin
// doesn't need this, but Xfwm does.
if let Some(adjusted_size) = shared_state_lock.dpi_adjusted {
let rounded_size = (adjusted_size.0.round() as u32, adjusted_size.1.round() as u32);
if new_inner_size == rounded_size {
// When this finally happens, the event will not be synthetic.
shared_state_lock.dpi_adjusted = None;
} else {
unsafe {
(self.xconn.xlib.XResizeWindow)(
self.xconn.display,
xwindow,
rounded_size.0 as c_uint,
rounded_size.1 as c_uint,
);
}
}
}
let mut events = Events::default(); let mut events = Events::default();
if resized {
let logical_size = LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor);
events.resized = Some(WindowEvent::Resized(logical_size));
}
let new_outer_position = if moved || shared_state_lock.position.is_none() { let new_outer_position = if moved || shared_state_lock.position.is_none() {
// We need to convert client area position to window position. // We need to convert client area position to window position.
let frame_extents = shared_state_lock.frame_extents let frame_extents = shared_state_lock.frame_extents
@ -497,9 +473,9 @@ impl EventsLoop {
}); });
let new_hidpi_factor = { let new_hidpi_factor = {
let window_rect = util::AaRect::new(new_outer_position, new_inner_size); let window_rect = util::AaRect::new(new_outer_position, new_inner_size);
let monitor = self.xconn.get_monitor_for_window(Some(window_rect)); monitor = self.xconn.get_monitor_for_window(Some(window_rect));
let new_hidpi_factor = monitor.hidpi_factor; let new_hidpi_factor = monitor.hidpi_factor;
shared_state_lock.last_monitor = Some(monitor); shared_state_lock.last_monitor = Some(monitor.clone());
new_hidpi_factor new_hidpi_factor
}; };
if last_hidpi_factor != new_hidpi_factor { if last_hidpi_factor != new_hidpi_factor {
@ -512,23 +488,50 @@ impl EventsLoop {
); );
flusher.queue(); flusher.queue();
shared_state_lock.dpi_adjusted = Some((new_width, new_height)); shared_state_lock.dpi_adjusted = Some((new_width, new_height));
// if the DPI factor changed, force a resize event to ensure the logical
// size is computed with the right DPI factor
resized = true;
} }
} }
// This is a hack to ensure that the DPI adjusted resize is actually applied on all WMs. KWin
// doesn't need this, but Xfwm does.
if let Some(adjusted_size) = shared_state_lock.dpi_adjusted {
let rounded_size = (adjusted_size.0.round() as u32, adjusted_size.1.round() as u32);
if new_inner_size == rounded_size {
// When this finally happens, the event will not be synthetic.
shared_state_lock.dpi_adjusted = None;
} else {
unsafe {
(self.xconn.xlib.XResizeWindow)(
self.xconn.display,
xwindow,
rounded_size.0 as c_uint,
rounded_size.1 as c_uint,
);
}
}
}
if resized {
let logical_size = LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor);
events.resized = Some(WindowEvent::Resized(logical_size));
}
events events
}); });
if let Some(events) = events { if let Some(events) = events {
let window_id = mkwid(xwindow); let window_id = mkwid(xwindow);
if let Some(event) = events.dpi_changed {
callback(Event::WindowEvent { window_id, event });
}
if let Some(event) = events.resized { if let Some(event) = events.resized {
callback(Event::WindowEvent { window_id, event }); callback(Event::WindowEvent { window_id, event });
} }
if let Some(event) = events.moved { if let Some(event) = events.moved {
callback(Event::WindowEvent { window_id, event }); callback(Event::WindowEvent { window_id, event });
} }
if let Some(event) = events.dpi_changed {
callback(Event::WindowEvent { window_id, event });
}
} }
} }