diff --git a/CHANGELOG.md b/CHANGELOG.md index 85f30ae569..282a4f2d6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/src/platform_impl/linux/wayland/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs index cfdc870ae3..e0d6a36d78 100644 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -393,7 +393,7 @@ impl EventLoop { 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()); }); } diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index 96c6931e77..ae6558a810 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -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 = attributes + let size: Size = attributes .inner_size - .map(|size| size.to_logical::(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> { let mut window_state = self.window_state.lock().unwrap(); - let scale_factor = window_state.scale_factor(); - window_state.resize(size.to_logical::(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. diff --git a/src/platform_impl/linux/wayland/window/state.rs b/src/platform_impl/linux/wayland/window/state.rs index ea2ff5aec1..f9f3187efe 100644 --- a/src/platform_impl/linux/wayland/window/state.rs +++ b/src/platform_impl/linux/wayland/window/state.rs @@ -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, + /// Initial window size provided by the user. Removed on the first + /// configure. + initial_size: Option, + /// The state of the frame callback. frame_callback_state: FrameCallbackState, @@ -153,7 +157,7 @@ impl WindowState { connection: Connection, queue_handle: &QueueHandle, winit_state: &WinitState, - size: LogicalSize, + initial_size: Size, window: Window, theme: Option, ) -> 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, event_sink: &mut EventSink, ) -> LogicalSize { + // 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, Option) { + 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 { + 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) { + fn resize(&mut self, inner_size: LogicalSize) { self.size = inner_size; // Update the stateless size. diff --git a/src/platform_impl/linux/x11/monitor.rs b/src/platform_impl/linux/x11/monitor.rs index 7c708613c7..9269f49ebc 100644 --- a/src/platform_impl/linux/x11/monitor.rs +++ b/src/platform_impl/linux/x11/monitor.rs @@ -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,