Skip to content

Commit

Permalink
On Wayland, fix Window::request_inner_size during resize
Browse files Browse the repository at this point in the history
The user may change the size during the on-going resize, meaning that
the size will desync with winit's internal loop which breaks viewporter
setup with fractional scaling.

Links: alacritty/alacritty#7474
  • Loading branch information
kchibisov committed Dec 31, 2023
1 parent 5b4f97e commit 63d52aa
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 59 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Unreleased` header.
# Unreleased

- On X11, reduce the amount of time spent fetching screen resources.
- On Wayland, fix `Window::request_inner_size` being overwritten by resize.
- On Wayland, fix `Window::inner_size` not using the correct rounding.

# 0.29.7

Expand Down
43 changes: 17 additions & 26 deletions src/platform_impl/linux/wayland/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use sctk::reexports::calloop_wayland_source::WaylandSource;
use sctk::reexports::client::globals;
use sctk::reexports::client::{Connection, QueueHandle};

use crate::dpi::{LogicalSize, PhysicalSize};
use crate::dpi::LogicalSize;
use crate::error::{EventLoopError, OsError as RootOsError};
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
use crate::event_loop::{
Expand All @@ -34,7 +34,7 @@ use sink::EventSink;

use super::state::{WindowCompositorUpdate, WinitState};
use super::window::state::FrameCallbackState;
use super::{DeviceId, WaylandError, WindowId};
use super::{logical_to_physical_rounded, DeviceId, WaylandError, WindowId};

type WaylandDispatcher = calloop::Dispatcher<'static, WaylandSource<WinitState>, WinitState>;

Expand Down Expand Up @@ -356,15 +356,13 @@ impl<T: 'static> EventLoop<T> {

for mut compositor_update in compositor_updates.drain(..) {
let window_id = compositor_update.window_id;
if let Some(scale_factor) = compositor_update.scale_factor {
let physical_size = self.with_state(|state| {
if compositor_update.scale_changed {
let (physical_size, scale_factor) = self.with_state(|state| {
let windows = state.windows.get_mut();
let mut window = windows.get(&window_id).unwrap().lock().unwrap();

// Set the new scale factor.
window.set_scale_factor(scale_factor);
let window_size = compositor_update.size.unwrap_or(window.inner_size());
logical_to_physical_rounded(window_size, scale_factor)
let window = windows.get(&window_id).unwrap().lock().unwrap();
let scale_factor = window.scale_factor();
let size = logical_to_physical_rounded(window.inner_size(), scale_factor);
(size, scale_factor)
});

// Stash the old window size.
Expand All @@ -386,30 +384,30 @@ impl<T: 'static> EventLoop<T> {

let physical_size = *new_inner_size.lock().unwrap();
drop(new_inner_size);
let new_logical_size = physical_size.to_logical(scale_factor);

// Resize the window when user altered the size.
if old_physical_size != physical_size {
self.with_state(|state| {
let windows = state.windows.get_mut();
let mut window = windows.get(&window_id).unwrap().lock().unwrap();

let new_logical_size: LogicalSize<f64> =
physical_size.to_logical(scale_factor);
window.request_inner_size(new_logical_size.into());
});
}

// Make it queue resize.
compositor_update.size = Some(new_logical_size);
// Make it queue resize.
compositor_update.resized = true;
}
}

if let Some(size) = compositor_update.size.take() {
if compositor_update.resized {
let physical_size = self.with_state(|state| {
let windows = state.windows.get_mut();
let window = windows.get(&window_id).unwrap().lock().unwrap();

let scale_factor = window.scale_factor();
let physical_size = logical_to_physical_rounded(size, scale_factor);

// TODO could probably bring back size reporting optimization.
let size = logical_to_physical_rounded(window.inner_size(), scale_factor);

// Mark the window as needed a redraw.
state
Expand All @@ -420,7 +418,7 @@ impl<T: 'static> EventLoop<T> {
.redraw_requested
.store(true, Ordering::Relaxed);

physical_size
size
});

callback(
Expand Down Expand Up @@ -684,10 +682,3 @@ impl<T> EventLoopWindowTarget<T> {
.into())
}
}

// The default routine does floor, but we need round on Wayland.
fn logical_to_physical_rounded(size: LogicalSize<u32>, scale_factor: f64) -> PhysicalSize<u32> {
let width = size.width as f64 * scale_factor;
let height = size.height as f64 * scale_factor;
(width.round(), height.round()).into()
}
8 changes: 8 additions & 0 deletions src/platform_impl/linux/wayland/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use sctk::reexports::client::globals::{BindError, GlobalError};
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{self, ConnectError, DispatchError, Proxy};

use crate::dpi::{LogicalSize, PhysicalSize};
pub use crate::platform_impl::platform::{OsError, WindowId};
pub use event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget};
pub use output::{MonitorHandle, VideoMode};
Expand Down Expand Up @@ -76,3 +77,10 @@ impl DeviceId {
fn make_wid(surface: &WlSurface) -> WindowId {
WindowId(surface.id().as_ptr() as u64)
}

/// The default routine does floor, but we need round on Wayland.
fn logical_to_physical_rounded(size: LogicalSize<u32>, scale_factor: f64) -> PhysicalSize<u32> {
let width = size.width as f64 * scale_factor;
let height = size.height as f64 * scale_factor;
(width.round(), height.round()).into()
}
42 changes: 16 additions & 26 deletions src/platform_impl/linux/wayland/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,19 @@ use sctk::shell::WaylandSurface;
use sctk::shm::{Shm, ShmHandler};
use sctk::subcompositor::SubcompositorState;

use crate::dpi::LogicalSize;
use crate::platform_impl::OsError;

use super::event_loop::sink::EventSink;
use super::output::MonitorHandle;
use super::seat::{
use crate::platform_impl::wayland::event_loop::sink::EventSink;
use crate::platform_impl::wayland::output::MonitorHandle;
use crate::platform_impl::wayland::seat::{
PointerConstraintsState, RelativePointerState, TextInputState, WinitPointerData,
WinitPointerDataExt, WinitSeatState,
};
use super::types::kwin_blur::KWinBlurManager;
use super::types::wp_fractional_scaling::FractionalScalingManager;
use super::types::wp_viewporter::ViewporterState;
use super::types::xdg_activation::XdgActivationState;
use super::window::{WindowRequests, WindowState};
use super::{WaylandError, WindowId};
use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
use crate::platform_impl::wayland::types::wp_fractional_scaling::FractionalScalingManager;
use crate::platform_impl::wayland::types::wp_viewporter::ViewporterState;
use crate::platform_impl::wayland::types::xdg_activation::XdgActivationState;
use crate::platform_impl::wayland::window::{WindowRequests, WindowState};
use crate::platform_impl::wayland::{WaylandError, WindowId};
use crate::platform_impl::OsError;

/// Winit's Wayland state.
pub struct WinitState {
Expand Down Expand Up @@ -219,7 +217,7 @@ impl WinitState {

// Update the scale factor right away.
window.lock().unwrap().set_scale_factor(scale_factor);
self.window_compositor_updates[pos].scale_factor = Some(scale_factor);
self.window_compositor_updates[pos].scale_changed = true;
} else if let Some(pointer) = self.pointer_surfaces.get(&surface.id()) {
// Get the window, where the pointer resides right now.
let focused_window = match pointer.pointer().winit_data().focused_window() {
Expand Down Expand Up @@ -283,9 +281,7 @@ impl WindowHandler for WinitState {
};

// Populate the configure to the window.
//
// XXX the size on the window will be updated right before dispatching the size to the user.
let new_size = self
self.window_compositor_updates[pos].resized |= self
.windows
.get_mut()
.get_mut(&window_id)
Expand All @@ -298,12 +294,6 @@ impl WindowHandler for WinitState {
&self.subcompositor_state,
&mut self.events_sink,
);

// NOTE: Only update when the value is `Some` to not override consequent configures with
// the same sizes.
if new_size.is_some() {
self.window_compositor_updates[pos].size = new_size;
}
}
}

Expand Down Expand Up @@ -397,10 +387,10 @@ pub struct WindowCompositorUpdate {
pub window_id: WindowId,

/// New window size.
pub size: Option<LogicalSize<u32>>,
pub resized: bool,

/// New scale factor.
pub scale_factor: Option<f64>,
pub scale_changed: bool,

/// Close the window.
pub close_window: bool,
Expand All @@ -410,8 +400,8 @@ impl WindowCompositorUpdate {
fn new(window_id: WindowId) -> Self {
Self {
window_id,
size: None,
scale_factor: None,
resized: false,
scale_changed: false,
close_window: false,
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/platform_impl/linux/wayland/window/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ impl Window {
pub fn inner_size(&self) -> PhysicalSize<u32> {
let window_state = self.window_state.lock().unwrap();
let scale_factor = window_state.scale_factor();
window_state.inner_size().to_physical(scale_factor)
super::logical_to_physical_rounded(window_state.inner_size(), scale_factor)
}

#[inline]
Expand Down Expand Up @@ -308,7 +308,7 @@ impl Window {
pub fn outer_size(&self) -> PhysicalSize<u32> {
let window_state = self.window_state.lock().unwrap();
let scale_factor = window_state.scale_factor();
window_state.outer_size().to_physical(scale_factor)
super::logical_to_physical_rounded(window_state.outer_size(), scale_factor)
}

#[inline]
Expand Down
10 changes: 5 additions & 5 deletions src/platform_impl/linux/wayland/window/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ 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;
use crate::platform_impl::wayland::make_wid;
use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
use crate::platform_impl::wayland::{logical_to_physical_rounded, make_wid};
use crate::platform_impl::WindowId;
use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme};

Expand Down Expand Up @@ -256,7 +256,7 @@ impl WindowState {
shm: &Shm,
subcompositor: &Option<Arc<SubcompositorState>>,
event_sink: &mut EventSink,
) -> Option<LogicalSize<u32>> {
) -> bool {
// 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.
Expand Down Expand Up @@ -368,9 +368,9 @@ impl WindowState {

if state_change_requires_resize || new_size != self.inner_size() {
self.resize(new_size);
Some(new_size)
true
} else {
None
false
}
}

Expand Down Expand Up @@ -647,7 +647,7 @@ impl WindowState {
self.resize(inner_size.to_logical(self.scale_factor()))
}

self.inner_size().to_physical(self.scale_factor())
logical_to_physical_rounded(self.inner_size(), self.scale_factor())
}

/// Resize the window to the new inner size.
Expand Down

0 comments on commit 63d52aa

Please sign in to comment.