steamcompmgr: return the nudged focus window to (0, 0)#2274
Open
KogasaPls wants to merge 1 commit into
Open
Conversation
a563226 removed the forced snap back to (0, 0), leaving the one-shot nudge parked at (1, 1) permanently. Wine records that as the WM-imposed position, and on Wine >= 11 its position reconciliation fights the stale (1, 1) endlessly once woken (e.g. by a clipboard cut), presenting as persistent black flickering. End the nudge back at (0, 0); windows that request a different position still keep it. Fixes: ValveSoftware#2241
Author
|
I had Claude Fable 5 attempt to put together a minimal reproduction based on my understanding of the bug. As far as I can tell it mostly validates my story, and validates the fix, but take it with a pile of salt of course. |
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 #2241.
Since the initial commit of steamcompmgr in 2013, newly focused windows are given a "nudge" to (1, 1) before being snapped back to (0, 0). (I assume the nudge exists to force a ConfigureNotify, so I haven't removed it.) a563226 removed the snap so that windows requesting a specific position keep it, which left every newly focused window permanently parked at (1, 1) unless it asked for something else. On Wine ≥ 11 this can set up a race where Wine repeatedly shifts the window between (0, 0) and (1, 1), visible as persistent flicker and a storm of ConfigureNotify events.
Fix
Move the window back to (0, 0) immediately after the nudge. This restores the original first-focus behavior and is strictly narrower than reverting a563226: windows that request a different position still keep it, so a563226's fix and the follow-ups built on non-zero origins (48a3be2, 23a53b9) survive.
With the nudge-back in place, the repro procedure from #2241 produced 5 ConfigureNotify events for the whole session (versus 3253 in under a minute without it) and no flicker.
Bug Mechanism
(traced with
WINEDEBUG=trace+x11drvand an instrumented gamescope)A fullscreen game maximizes its screen-sized window before first map: a same-rect no-op that sets
WS_MAXIMIZEwithout any X config request. At first focus gamescope nudges the window to (1, 1), and Wine adopts the WM-imposed position into its x11-side desired state and, viawindow_update_client_config, the win32-side rect. The window is now parked at (1, 1) while win32 believes it is maximized.The mismatch sits dormant until a window event makes Wine re-evaluate. Any window creation that runs gamescope's focus pass will do: in a real session, the 1x1 clipboard-owner window a Ctrl+X creates (in the recorded repro runs, Wine's own startup churn fires it within ~200 ms). The focus pass emits a ConfigureNotify for the game window;
window_update_client_statederivesWS_MAXIMIZEfrom_NET_WM_STATE_MAXIMIZED, which gamescope never sets, concludes the window is no longer maximized, and postsSC_RESTORE.The game re-maximizes in response, requesting (0, 0). Since it reacts from inside the restore, the restore's own transaction finishes last and submits its stale (1, 1) while the (0, 0) config is still in flight.
window_needs_config_change_delaydefers it because another config is pending (its trace message blames_NET_WM_STATE/_MOTIF_WM_HINTS, but the per-round hint rewrites change no decorations and are not the trigger). When the granted (0, 0)'s ConfigureNotify returns,window_configure_notifypreserves the stale desired rect and immediately re-requests it ("preserving delayed config", bf0813e3).gamescope grants every request verbatim and issues no moves of its own after the one-time nudge, so the granted (1, 1) parks the window again and its ConfigureNotify wakes the next round. The WoW session trace logged 1623 requests for (1, 1) and 1623 for (0, 0), exactly paired; the minimal repro cycles in roughly 200 µs per round.
Wine has a guard against exactly this kind of position fight (see the comment in
window_update_client_config), but it requires the WM-imposed rect to fully cover the monitor, which is false at (1, 1). The same classification is why a single round dies out on its own: once the window truly rests at (0, 0), no further restore can fire. The loop persists only while each round ends back at (1, 1).Wine 10.0 converges after one exchange: an unexpected ConfigureNotify is simply adopted into the desired state ("avoid requesting the same state again", the same adoption that absorbs the nudge in step 1). The 10.9 commit bf0813e3 (
winex11: Serialize managed window config change requests) made configs that arrive while another request is in flight be preserved and re-requested instead, which is what sustains the loop; Proton 10 (Wine 10.0 base) therefore shows a single-frame blip where Proton 11 loops indefinitely.Follow-ups
Produce a minimal repro of the self-sustaining reconfiguration loop I observed in World of Warcraft.Repro posted below.