On Wayland, improve initial user size handling

Keep the user provided size in the original values and convert only
when we're getting a `configure` event. On some compositors will
have a scale available, so it'll work, however with some we'll
still have old 'pick 1` as default.

Also configure_bounds when compositor tells the user to pick the size,
that will ensure that initial `with_inner_size` won't grow beyond the
working area.

Fixes #3187.
This commit is contained in:
Kirill Chibisov 2023-10-27 00:56:23 +04:00 committed by GitHub
parent 53ca5af48f
commit 12dbbf8012
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 92 additions and 26 deletions

View file

@ -11,7 +11,10 @@ Unreleased` header.
# Unreleased # Unreleased
- On Wayland, apply correct scale to `PhysicalSize` passed in `WindowBuilder::with_inner_size` when possible.
- On Wayland, fix `RedrawRequsted` being always sent without decorations and `sctk-adwaita` feature. - On Wayland, fix `RedrawRequsted` being always sent without decorations and `sctk-adwaita` feature.
- On Wayland, ignore resize requests when the window is fully tiled.
- On Wayland, use `configure_bounds` to constrain `with_inner_size` when compositor wants users to pick size.
- On Windows, fix deadlock when accessing the state during `Cursor{Enter,Leave}`. - On Windows, fix deadlock when accessing the state during `Cursor{Enter,Leave}`.
- On macOS, fix deadlock when entering a nested event loop from an event handler. - On macOS, fix deadlock when entering a nested event loop from an event handler.

View file

@ -393,7 +393,7 @@ impl<T: 'static> EventLoop<T> {
self.with_state(|state| { self.with_state(|state| {
let windows = state.windows.get_mut(); let windows = state.windows.get_mut();
let mut window = windows.get(&window_id).unwrap().lock().unwrap(); let mut window = windows.get(&window_id).unwrap().lock().unwrap();
window.resize(new_logical_size); window.request_inner_size(new_logical_size.into());
}); });
} }

View file

@ -97,11 +97,9 @@ impl Window {
.map(|activation_state| activation_state.global().clone()); .map(|activation_state| activation_state.global().clone());
let display = event_loop_window_target.connection.display(); let display = event_loop_window_target.connection.display();
// XXX The initial scale factor must be 1, but it might cause sizing issues on HiDPI. let size: Size = attributes
let size: LogicalSize<u32> = attributes
.inner_size .inner_size
.map(|size| size.to_logical::<u32>(1.)) .unwrap_or(LogicalSize::new(800., 600.).into());
.unwrap_or((800, 600).into());
// We prefer server side decorations, however to not have decorations we ask for client // We prefer server side decorations, however to not have decorations we ask for client
// side decorations instead. // side decorations instead.
@ -141,7 +139,8 @@ impl Window {
// Set the window title. // Set the window title.
window_state.set_title(attributes.title); window_state.set_title(attributes.title);
// Set the min and max sizes. // Set the min and max sizes. We must set the hints upon creating a window, so
// we use the default `1.` scaling...
let min_size = attributes.min_inner_size.map(|size| size.to_logical(1.)); let min_size = attributes.min_inner_size.map(|size| size.to_logical(1.));
let max_size = attributes.max_inner_size.map(|size| size.to_logical(1.)); let max_size = attributes.max_inner_size.map(|size| size.to_logical(1.));
window_state.set_min_inner_size(min_size); window_state.set_min_inner_size(min_size);
@ -315,12 +314,9 @@ impl Window {
#[inline] #[inline]
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> { pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
let mut window_state = self.window_state.lock().unwrap(); let mut window_state = self.window_state.lock().unwrap();
let scale_factor = window_state.scale_factor(); let new_size = window_state.request_inner_size(size);
window_state.resize(size.to_logical::<u32>(scale_factor));
self.request_redraw(); self.request_redraw();
Some(new_size)
Some(window_state.inner_size().to_physical(scale_factor))
} }
/// Set the minimum inner size for the window. /// Set the minimum inner size for the window.

View file

@ -28,7 +28,7 @@ use sctk::shm::Shm;
use sctk::subcompositor::SubcompositorState; use sctk::subcompositor::SubcompositorState;
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur; use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
use crate::dpi::{LogicalPosition, LogicalSize}; use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size};
use crate::error::{ExternalError, NotSupportedError}; use crate::error::{ExternalError, NotSupportedError};
use crate::event::WindowEvent; use crate::event::WindowEvent;
use crate::platform_impl::wayland::event_loop::sink::EventSink; use crate::platform_impl::wayland::event_loop::sink::EventSink;
@ -133,6 +133,10 @@ pub struct WindowState {
/// sends `None` for the new size in the configure. /// sends `None` for the new size in the configure.
stateless_size: LogicalSize<u32>, stateless_size: LogicalSize<u32>,
/// Initial window size provided by the user. Removed on the first
/// configure.
initial_size: Option<Size>,
/// The state of the frame callback. /// The state of the frame callback.
frame_callback_state: FrameCallbackState, frame_callback_state: FrameCallbackState,
@ -153,7 +157,7 @@ impl WindowState {
connection: Connection, connection: Connection,
queue_handle: &QueueHandle<WinitState>, queue_handle: &QueueHandle<WinitState>,
winit_state: &WinitState, winit_state: &WinitState,
size: LogicalSize<u32>, initial_size: Size,
window: Window, window: Window,
theme: Option<Theme>, theme: Option<Theme>,
) -> Self { ) -> Self {
@ -194,8 +198,9 @@ impl WindowState {
resizable: true, resizable: true,
scale_factor: 1., scale_factor: 1.,
shm: winit_state.shm.wl_shm().clone(), shm: winit_state.shm.wl_shm().clone(),
size, size: initial_size.to_logical(1.),
stateless_size: size, stateless_size: initial_size.to_logical(1.),
initial_size: Some(initial_size),
text_inputs: Vec::new(), text_inputs: Vec::new(),
theme, theme,
title: String::default(), title: String::default(),
@ -253,6 +258,14 @@ impl WindowState {
subcompositor: &Arc<SubcompositorState>, subcompositor: &Arc<SubcompositorState>,
event_sink: &mut EventSink, event_sink: &mut EventSink,
) -> LogicalSize<u32> { ) -> LogicalSize<u32> {
// NOTE: when using fractional scaling or wl_compositor@v6 the scaling
// should be delivered before the first configure, thus apply it to
// properly scale the physical sizes provided by the users.
if let Some(initial_size) = self.initial_size.take() {
self.size = initial_size.to_logical(self.scale_factor());
self.stateless_size = self.size;
}
if configure.decoration_mode == DecorationMode::Client if configure.decoration_mode == DecorationMode::Client
&& self.frame.is_none() && self.frame.is_none()
&& !self.csd_fails && !self.csd_fails
@ -297,7 +310,7 @@ impl WindowState {
event_sink.push_window_event(WindowEvent::Occluded(occluded), window_id); event_sink.push_window_event(WindowEvent::Occluded(occluded), window_id);
} }
let new_size = if let Some(frame) = self.frame.as_mut() { let (mut new_size, constrain) = if let Some(frame) = self.frame.as_mut() {
// Configure the window states. // Configure the window states.
frame.update_state(configure.state); frame.update_state(configure.state);
@ -305,22 +318,38 @@ impl WindowState {
(Some(width), Some(height)) => { (Some(width), Some(height)) => {
let (width, height) = frame.subtract_borders(width, height); let (width, height) = frame.subtract_borders(width, height);
( (
width.map(|w| w.get()).unwrap_or(1), (
height.map(|h| h.get()).unwrap_or(1), width.map(|w| w.get()).unwrap_or(1),
height.map(|h| h.get()).unwrap_or(1),
)
.into(),
false,
) )
.into()
} }
(_, _) if stateless => self.stateless_size, (_, _) if stateless => (self.stateless_size, true),
_ => self.size, _ => (self.size, true),
} }
} else { } else {
match configure.new_size { match configure.new_size {
(Some(width), Some(height)) => (width.get(), height.get()).into(), (Some(width), Some(height)) => ((width.get(), height.get()).into(), false),
_ if stateless => self.stateless_size, _ if stateless => (self.stateless_size, true),
_ => self.size, _ => (self.size, true),
} }
}; };
// Apply configure bounds only when compositor let the user decide what size to pick.
if constrain {
let bounds = self.inner_size_bounds(&configure);
new_size.width = bounds
.0
.map(|bound_w| new_size.width.min(bound_w.get()))
.unwrap_or(new_size.width);
new_size.height = bounds
.1
.map(|bound_h| new_size.height.min(bound_h.get()))
.unwrap_or(new_size.height);
}
// XXX Set the configure before doing a resize. // XXX Set the configure before doing a resize.
self.last_configure = Some(configure); self.last_configure = Some(configure);
@ -330,6 +359,30 @@ impl WindowState {
new_size new_size
} }
/// Compute the bounds for the inner size of the surface.
fn inner_size_bounds(
&self,
configure: &WindowConfigure,
) -> (Option<NonZeroU32>, Option<NonZeroU32>) {
let configure_bounds = match configure.suggested_bounds {
Some((width, height)) => (NonZeroU32::new(width), NonZeroU32::new(height)),
None => (None, None),
};
if let Some(frame) = self.frame.as_ref() {
let (width, height) = frame.subtract_borders(
configure_bounds.0.unwrap_or(NonZeroU32::new(1).unwrap()),
configure_bounds.1.unwrap_or(NonZeroU32::new(1).unwrap()),
);
(
configure_bounds.0.and(width),
configure_bounds.1.and(height),
)
} else {
configure_bounds
}
}
#[inline] #[inline]
fn is_stateless(configure: &WindowConfigure) -> bool { fn is_stateless(configure: &WindowConfigure) -> bool {
!(configure.is_maximized() || configure.is_fullscreen() || configure.is_tiled()) !(configure.is_maximized() || configure.is_fullscreen() || configure.is_tiled())
@ -568,8 +621,22 @@ impl WindowState {
} }
} }
/// Try to resize the window when the user can do so.
pub fn request_inner_size(&mut self, inner_size: Size) -> PhysicalSize<u32> {
if self
.last_configure
.as_ref()
.map(Self::is_stateless)
.unwrap_or(true)
{
self.resize(inner_size.to_logical(self.scale_factor()))
}
self.inner_size().to_physical(self.scale_factor())
}
/// Resize the window to the new inner size. /// Resize the window to the new inner size.
pub fn resize(&mut self, inner_size: LogicalSize<u32>) { fn resize(&mut self, inner_size: LogicalSize<u32>) {
self.size = inner_size; self.size = inner_size;
// Update the stateless size. // Update the stateless size.

View file

@ -212,7 +212,7 @@ impl XConnection {
return Ok(MonitorHandle::dummy()); return Ok(MonitorHandle::dummy());
} }
let default = monitors.get(0).unwrap(); let default = monitors.first().unwrap();
let window_rect = match window_rect { let window_rect = match window_rect {
Some(rect) => rect, Some(rect) => rect,