x11: fix client left at (0,0) inside its frame after WM restart (titlebar drawn behind the client)#841
Open
science wants to merge 1 commit into
Open
Conversation
meta_window_x11_manage seeds priv->client_rect with window->rect (root coordinates) after meta_window_ensure_frame has already reparented the client to (0, 0) inside its frame. meta_window_x11_move_resize_internal compares that cache against frame-relative coordinates (borders.total.left, borders.total.top), so when a window's pre-manage root position happens to equal exactly that offset, need_move_client stays FALSE and the client is left at (0, 0) inside its frame - the titlebar is drawn behind the client, and the unused right/bottom of the frame shows through as black strips. The state never self-heals: the cache claims the client is already at the correct offset, so no later move or resize ever sends CWX|CWY for it. This happens in practice after every WM restart (cinnamon --replace or crash recovery) to windows parked in the top-left corner of the screen: when the old WM's connection closes its frames are destroyed, and the save-set returns clients to the root window preserving absolute coordinates - for a frame that sat at the screen origin, the client lands at exactly (borders.total.left, borders.total.top). Seed client_rect.x/y with frame->child_x/child_y - where XReparentWindow actually placed the client - instead. Co-Authored-By: Claude Code <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
When muffin manages an already-mapped window — every window on the desktop after
cinnamon --replaceor a crash-recovery restart — the client can be left at (0,0) inside its frame instead of at(borders.total.left, borders.total.top). The frame is created and sized correctly and_NET_FRAME_EXTENTSadvertises the titlebar, but the client covers it: the window looks like it lost its titlebar, with black strips on the right and bottom where the oversized frame shows through. The state never self-heals — later moves only move the frame, and later resizes sendCWWidth|CWHeightwithoutCWX|CWY.Trigger: the client's root coordinates at manage time are exactly
(borders.total.left, borders.total.top)— e.g.(10, 45)with the default theme. That sounds exotic, but it is precisely where the X save-set drops any window whose frame sat at the screen's top-left corner when the previous WM died: frames are destroyed with the old WM's connection, and save-set reparenting preserves absolute coordinates (frame at (0,0) + child offset (10,45) = client at root (10,45)). In practice, a window the user keeps parked in the top-left corner (sidebars, docks) gets this on every Cinnamon restart, while every other window re-frames fine.Root cause
meta_window_x11_manageseeds the client-rect cache with root coordinates aftermeta_window_ensure_framehas already reparented the client to (0,0) inside the frame:meta_window_x11_move_resize_internalthen compares frame-relative target coordinates against that root-coordinate seed (the code comment even notes the coordinate-space switch right above the comparison):When the pre-manage root position equals
borders.total, the two compare equal,need_move_clientstays FALSE, and the client stays at (0,0) where the reparent put it. Since the cache now claims the client is at the "correct" offset, nothing ever moves it again.The fix
Seed
client_rect.x/ywithframe->child_x/child_y— whereXReparentWindowactually placed the client — when the window was framed. One hunk inmeta_window_x11_manage.For comparison: mutter no longer has this pattern since the frames rework —
meta-x11-frame.cqueries the frame borders before reparenting and passes the real child offset toXReparentWindow, so there is no fix-up move to skip.Validation
Visual demo
Screenshot filmstrip — healthy → WM killed → re-adopted, on 6.0.1 / unpatched master / patched master (5 frames, unedited scrot captures)
Two identical xterms; the only difference is position. "victim" (red) is parked with its frame at the screen's top-left corner, "control" (green) sits mid-screen. muffin is SIGKILLed and restarted. On 6.0.1 and unpatched master the victim's client is left at (0,0) inside its frame — titlebar behind the client, black strips where the oversized frame shows through — while the control re-frames correctly. With this patch both re-frame correctly.
Regression test
Self-contained regression test (attached:
muffin-restart-reframe-test.sh; runs on a nested Xvfb, no effect on the host session). It starts muffin, maps two xterms, reads the healthy child offset (theme-independent), SIGKILLs muffin, parks the "victim" xterm at exactly that offset in root coordinates, restarts muffin, and compares both windows' child offsets:b332877, unpatched, built locallyb332877+ this patchmuffin-restart-reframe-test.sh (run with MUFFIN_BIN=/path/to/muffin)
Note on a related corner
The other two
meta_window_ensure_framecallers (MOTIF-hints reload inwindow-props.c, window-type change inwindow.c) have the same theoretical exposure: the cache holds the unframed root coordinates when the reparent-to-(0,0) happens. Hitting it requires a client to toggle decorations while positioned exactly onborders.totalroot coordinates, which doesn't happen organically — and fixing those paths would needwindow-x11private access from other modules (or mutter's approach of reparenting at the real offset from the start). This PR takes the minimal fix for the path users actually hit; happy to extend it if you'd prefer the broader change.Environment
Observed on Cinnamon 6.0.4 / muffin 6.0.1 (Ubuntu 24.04.4) on two machines; root-caused and fixed against master.
🤖 Generated with Claude Code