Dear Maintainer,
On Wayland sessions with a fractional display scale factor (125%, 150%, ...),
KWin 6.3.6 can get into a state where every geometry change applied to a
window - including plain same-size moves - is silently deferred behind an
xdg_surface configure that the client can never satisfy, making the window
permanently invisible and unmovable.
Root cause (src/xdgshellwindow.cpp, XdgSurfaceWindow::moveResizeInternal):
whether a geometry change can be applied immediately is decided by comparing
the requested client size with the committed client size, truncated via
QSizeF::toSize(). The two values are derived differently, and at a fractional
scale they can disagree permanently. Example at 125%: logical width 1041 is
1301.25 device pixels; KWin's side becomes 1040.8 -> 1040 while the client's
committed size rounds to 1041. 1040 != 1041, forever. Every placement of that
window then takes the configure path - even a move to a different position at
the same size. Clients that suspend rendering while fully off-screen
(Chromium/Electron-based applications, Firefox) never acknowledge the
configure, so the window is wedged: it cannot be moved on-screen because KWin
is waiting for the client, and the client will not draw because it is
off-screen.
Impact: any application window can end up invisible with no user-visible
recovery short of killing the application. Scrolling/tiling KWin scripts
(e.g. Karousel) move windows off-screen as part of normal operation and
trigger this within minutes of ordinary use; user reports of the symptom:
https://github.com/peterfajdiga/karousel/issues/166
Reproduction (Plasma Wayland session, any output at 125% scale):
1. Open a Chromium-family browser (Wayland-native, e.g. chromium
--ozone-platform=wayland) and note its window is e.g. 1041 px wide
(any width whose product with the scale factor is non-integer).
2. Move the window fully outside all outputs via the KWin scripting console
(assigning frameGeometry), let it idle a few seconds.
3. Attempt to move it back on-screen the same way, same size.
The assignment is never applied; reading frameGeometry back shows the old
position indefinitely. At 100% scale the same steps behave normally.
Fixed upstream in Plasma 6.7.0 - incidentally, as part of scale-override
preparation work, which is presumably why it was never identified as a bugfix
or backported to 6.4-6.6:
https://invent.kde.org/plasma/kwin/-/commit/16affa41f2877799edaf073ddb440ef1e7c38d9f
(the relevant part: both sides of the comparison now go through
SurfaceInterface::snappedSize(), rounding both to the surface's pixel grid
before comparing).
The attached patch is a minimal equivalent of that comparison change for
6.3.6, applying on top of the existing
upstream_e8541cf2_wayland-Fix-resizing-with-fractional-increments.patch
(which reworked the surrounding lines but kept the truncated comparison).
DEP-3 headers included.
Tested: built in a trixie chroot and deployed on a trixie system running
Plasma Wayland at 125% scale where the deadlock was reproducible at will;
with the patch it cannot be reproduced at all, and previously-affected
setups (tiling script + Chromium/Electron apps) behave normally. I am
currently running the patched package, so reportbug may show my version as
4:6.3.6-1+snapfix1; the bug applies to stock 4:6.3.6-1.
Given the small, targeted nature of the fix and that the failure mode is
windows becoming permanently invisible, this seems like a candidate for a
trixie point release; happy to provide any further testing.