Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
fix(xwayland): anchor a left/top resize on the LOGICAL committed size
Dragging an X11 window's left or top handle at output scale 2 threw the
window across the desktop: on the first motion the origin jumped a whole
window-width left, then tracked the pointer at double speed while the
opposite edge — the one the anchoring exists to pin — slid along with it.
Reported against Houdini; it was every X11 client under xwayland_hidpi.
anchor_resize_commit works in box_geom units, which are logical, and
computes origin = start origin + (start size - committed size). But
handle_window_commit fed it the raw committed surface extent, and an X11
surface commits PHYSICAL pixels — twice the logical box under
xwayland_hidpi, as the scale_only_render_finish call ten lines below
already says. So the subtraction was W - 2W, off by a window-width with
twice the gain.
The anchoring (506ec54) predates HiDPI X11 (da95c6f) by three days; that
change routed every other xsurface read-back through from_x11 and missed
this one. Convert the same way, and take the wine frame off after the
conversion in logical WINE_MARGIN units rather than as 32 raw physical
px, the way render_finish reads the size back.
Verified headless at scale 2 with an X11 zenity, before and after: the
pre-fix binary moves a 250-wide window's origin 195 -> -35 -> 25 over a
40px left-edge drag, the fixed one 195 -> 235 with the right edge held at
445 throughout. Grow-leftward, top-edge and right-edge drags all hold
their opposite edge too.
Co-Authored-By: Claude Opus 5 <[email protected]>
src/server/window.rs | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/src/server/window.rs b/src/server/window.rs
index e103d6f..7cfd96e 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -5322,16 +5322,27 @@ unsafe extern "C" fn handle_window_commit(listener: *mut ffi::wl_listener, _data
// size it just committed, ahead of the render_finish below that places
// the tree at `rendering_requested`. (xdg toplevels do the same in their
// own commit handler, where the toplevel geometry is the authority.)
+ //
+ // The committed surface is PHYSICAL pixels — under `xwayland_hidpi` twice
+ // the logical box, as the scale pass below says — while
+ // `anchor_resize_commit` works in box_geom's logical units. Convert first,
+ // and take the wine frame off after, the way `render_finish` reads the
+ // xsurface size back. Feeding it the raw buffer width put the origin a
+ // whole window-width to the left and made it track the pointer at double
+ // speed, every X11 left/top drag at scale 2.
if let WindowImpl::Xwayland(xwindow) = (*window).impl_type {
if !xwindow.is_null() && !(*xwindow).xsurface.is_null() && (*window).resize_edges.is_some() {
let surface = (*(*xwindow).xsurface).surface;
if !surface.is_null() {
- let mut w = ffi::river_wlr_surface_get_width(surface);
- let mut h = ffi::river_wlr_surface_get_height(surface);
+ let s = crate::xwayland_window::x11_scale_for((*window).server, (*xwindow).xsurface);
+ let mut w = crate::xwayland_window::from_x11(
+ ffi::river_wlr_surface_get_width(surface), s);
+ let mut h = crate::xwayland_window::from_x11(
+ ffi::river_wlr_surface_get_height(surface), s);
let has_parent = !(*(*xwindow).xsurface).parent.is_null();
if (*window).is_wine() && !has_parent && !(*window).is_fullscreen() {
- w = (w - 32).max(0);
- h = (h - 32).max(0);
+ w = (w - crate::xwayland_window::WINE_MARGIN * 2).max(0);
+ h = (h - crate::xwayland_window::WINE_MARGIN * 2).max(0);
}
(*window).anchor_resize_commit(w, h);
}