mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-11 21:31:29 +11:00
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:
parent
f486845f7f
commit
c744b016ce
|
@ -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)
|
||||||
|
|
||||||
|
|
24
src/dpi.rs
24
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.
|
//! 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`.
|
||||||
///
|
///
|
||||||
|
|
|
@ -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 });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue