fix(cua-driver): Linux agent cursor never painted under reparenting WMs — z-order BadMatch killed the overlay thread - #2957
Merged
Conversation
f-trycua
force-pushed
the
fix/overlay-zorder-reparenting-wm
branch
from
August 6, 2026 20:36
b5c013b to
b08ce82
Compare
…e Linux agent cursor can paint The first element click on a session pins the overlay above the target window and restacks with ConfigureWindow(sibling=<client xid>, Above). Under a reparenting WM (xfwm4, Mutter, KWin) the client window lives inside a WM frame, so it does not share the root-child overlay's parent and the server answers BadMatch. classify_x11_overlay_event treated that as fatal: the overlay thread exited, RENDER was cleared, and every later cursor command was silently rejected — the agent cursor never painted a single frame on such desktops and set_agent_cursor_enabled appeared to succeed while get_agent_cursor_state read enabled:false. Three changes: - reassert() resolves the pin target to its top-level root-child ancestor via query_tree before using it as the restack sibling — the frame is the window that actually occupies a stacking slot, and the walk doubles as the liveness probe. - recoverable_x11_z_order_error also accepts BadMatch (a reparent can still race the restack); only errors naming the overlay window itself stay fatal. - send_command_for logs a warning when the X11 channel rejects a command, so a dead render thread is diagnosable instead of silent. Fixes #2955 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
f-trycua
force-pushed
the
fix/overlay-zorder-reparenting-wm
branch
from
August 7, 2026 08:57
b08ce82 to
6be766e
Compare
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.
Fixes #2955.
Root cause
The first element click in a session sends
PinAbove(<target window>), and the z-order enforcer restacks the overlay withConfigureWindow(sibling=<client xid>, StackMode=Above). Under a reparenting WM (xfwm4, Mutter, KWin, …) the client window is a child of a WM frame window, not of the root — and X11 requires a restack sibling to share the configured window's parent, so the server answers BadMatch.classify_x11_overlay_eventtreated any non-BadWindow error as fatal: the overlay thread broke its loop,RENDERwas cleared, and every later cursor command was rejected by the dead channel — silently. Net effect on such desktops (which includes the xorgxrdp/XFCE box from #2955, but is not xrdp-specific):0x0forever);set_agent_cursor_enabledreturned{"enabled": true}whileget_agent_cursor_statereadenabled: false(the fallback whenRENDERisNone).Instrumented trace of the death, seconds after the first click:
Fix
reassert()resolves the pin target to its top-level root-child ancestor (query_treewalk) before using it as the restack sibling — the WM frame is the window that actually occupies a stacking slot, so the restack now lands wherePinAboveintended. The walk doubles as the liveness probe thatget_window_attributesprovided before. On non-reparenting setups the walk terminates on the first iteration and behavior is unchanged.recoverable_x11_z_order_erroralso accepts BadMatch (a reparent can still race between the ancestor walk and the restack). Errors naming the overlay window itself remain fatal, matching the existing BadWindow rule.send_command_fornow logs a warning when the X11 channel rejects a command — previously a dead render thread was completely silent, which is what made this so hard to see.Testing
cargo test -p platform-linux --lib— 263 passed. The oldnon_badwindow_configure_error_remains_fataltest asserted the buggy behavior; it is replaced byz_order_sibling_badmatch_is_recoverableplus a newoverlay_badmatch_remains_fatalguard for the overlay-window case.0x0 → 38x39 (arrow) → 98x90 (arrow + click pulse + session badge)at the clicked button, thread survives, and a desktop screenshot shows the rendered cursor with its session badge.get_agent_cursor_statenow agrees withset_agent_cursor_enabled.🤖 Generated with Claude Code