diff --git a/CHANGELOG.md b/CHANGELOG.md index fde3d44b..050abcc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,10 @@ - On Windows, `Window::set_cursor` now applies immediately instead of requiring specific events to occur first. - On Windows, fix `Window::set_maximized`. - 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`. +- 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) diff --git a/src/dpi.rs b/src/dpi.rs index 8dd498b3..c836d27b 100644 --- a/src/dpi.rs +++ b/src/dpi.rs @@ -29,9 +29,9 @@ //! 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: -//! - You can track the `WindowEvent::HiDpiFactorChanged` event of your windows. This event is sent any -//! time the DPI factor changes, be it because the window moved to another monitor, or because the -//! user changed the configuration of their screen. +//! - You can track the [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) event of your +//! windows. This event is sent any time the DPI factor changes, either because the window moved to another monitor, +//! or because the user changed the configuration of their screen. //! - 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 //! 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 //! 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 -//! adapt your drawing logic to follow the DPI factor. +//! the most robust way is to monitor the [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) +//! 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: //! - **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 //! 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. -//! - **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. //! - **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 -//! 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, //! 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. //! //! 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. +//! +//! `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`. /// diff --git a/src/platform/linux/x11/mod.rs b/src/platform/linux/x11/mod.rs index 1d82414c..f8cd69fc 100644 --- a/src/platform/linux/x11/mod.rs +++ b/src/platform/linux/x11/mod.rs @@ -412,10 +412,10 @@ impl EventsLoop { 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 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 (resized, moved) = { + let (mut resized, moved) = { let resized = util::maybe_change(&mut shared_state_lock.size, new_inner_size); let moved = if is_synthetic { util::maybe_change(&mut shared_state_lock.inner_position, new_inner_position) @@ -435,32 +435,8 @@ impl EventsLoop { (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(); - 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() { // We need to convert client area position to window position. let frame_extents = shared_state_lock.frame_extents @@ -497,9 +473,9 @@ impl EventsLoop { }); let new_hidpi_factor = { 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; - shared_state_lock.last_monitor = Some(monitor); + shared_state_lock.last_monitor = Some(monitor.clone()); new_hidpi_factor }; if last_hidpi_factor != new_hidpi_factor { @@ -512,23 +488,50 @@ impl EventsLoop { ); flusher.queue(); 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 }); if let Some(events) = events { let window_id = mkwid(xwindow); + if let Some(event) = events.dpi_changed { + callback(Event::WindowEvent { window_id, event }); + } if let Some(event) = events.resized { callback(Event::WindowEvent { window_id, event }); } if let Some(event) = events.moved { callback(Event::WindowEvent { window_id, event }); } - if let Some(event) = events.dpi_changed { - callback(Event::WindowEvent { window_id, event }); - } } }