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
- 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, 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 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| {
let windows = state.windows.get_mut();
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());
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: LogicalSize<u32> = attributes
let size: Size = attributes
.inner_size
.map(|size| size.to_logical::<u32>(1.))
.unwrap_or((800, 600).into());
.unwrap_or(LogicalSize::new(800., 600.).into());
// We prefer server side decorations, however to not have decorations we ask for client
// side decorations instead.
@ -141,7 +139,8 @@ impl Window {
// Set the window 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 max_size = attributes.max_inner_size.map(|size| size.to_logical(1.));
window_state.set_min_inner_size(min_size);
@ -315,12 +314,9 @@ impl Window {
#[inline]
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
let mut window_state = self.window_state.lock().unwrap();
let scale_factor = window_state.scale_factor();
window_state.resize(size.to_logical::<u32>(scale_factor));
let new_size = window_state.request_inner_size(size);
self.request_redraw();
Some(window_state.inner_size().to_physical(scale_factor))
Some(new_size)
}
/// Set the minimum inner size for the window.

View file

@ -28,7 +28,7 @@ use sctk::shm::Shm;
use sctk::subcompositor::SubcompositorState;
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::event::WindowEvent;
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.
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.
frame_callback_state: FrameCallbackState,
@ -153,7 +157,7 @@ impl WindowState {
connection: Connection,
queue_handle: &QueueHandle<WinitState>,
winit_state: &WinitState,
size: LogicalSize<u32>,
initial_size: Size,
window: Window,
theme: Option<Theme>,
) -> Self {
@ -194,8 +198,9 @@ impl WindowState {
resizable: true,
scale_factor: 1.,
shm: winit_state.shm.wl_shm().clone(),
size,
stateless_size: size,
size: initial_size.to_logical(1.),
stateless_size: initial_size.to_logical(1.),
initial_size: Some(initial_size),
text_inputs: Vec::new(),
theme,
title: String::default(),
@ -253,6 +258,14 @@ impl WindowState {
subcompositor: &Arc<SubcompositorState>,
event_sink: &mut EventSink,
) -> 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
&& self.frame.is_none()
&& !self.csd_fails
@ -297,7 +310,7 @@ impl WindowState {
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.
frame.update_state(configure.state);
@ -305,22 +318,38 @@ impl WindowState {
(Some(width), Some(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,
_ => self.size,
(_, _) if stateless => (self.stateless_size, true),
_ => (self.size, true),
}
} else {
match configure.new_size {
(Some(width), Some(height)) => (width.get(), height.get()).into(),
_ if stateless => self.stateless_size,
_ => self.size,
(Some(width), Some(height)) => ((width.get(), height.get()).into(), false),
_ if stateless => (self.stateless_size, true),
_ => (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.
self.last_configure = Some(configure);
@ -330,6 +359,30 @@ impl WindowState {
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]
fn is_stateless(configure: &WindowConfigure) -> bool {
!(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.
pub fn resize(&mut self, inner_size: LogicalSize<u32>) {
fn resize(&mut self, inner_size: LogicalSize<u32>) {
self.size = inner_size;
// Update the stateless size.

View file

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