Skip to content

Commit

Permalink
On Wayland, improve initial user size handling
Browse files Browse the repository at this point in the history
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 rust-windowing#3187.
  • Loading branch information
kchibisov authored Oct 26, 2023
1 parent 53ca5af commit 12dbbf8
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 26 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion src/platform_impl/linux/wayland/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
});
}

Expand Down
16 changes: 6 additions & 10 deletions src/platform_impl/linux/wayland/window/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down
95 changes: 81 additions & 14 deletions src/platform_impl/linux/wayland/window/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,

Expand All @@ -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 {
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -297,30 +310,46 @@ 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);

match configure.new_size {
(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);

Expand All @@ -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())
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/platform_impl/linux/x11/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 12dbbf8

Please sign in to comment.