Skip to content

fix: window swaps flash the desktop background + redundant alpha re-stamps flicker backdrop materials#1409

Open
KhangHLe wants to merge 1 commit into
glzr-io:mainfrom
KhangHLe:atomic-repositions
Open

fix: window swaps flash the desktop background + redundant alpha re-stamps flicker backdrop materials#1409
KhangHLe wants to merge 1 commit into
glzr-io:mainfrom
KhangHLe:atomic-repositions

Conversation

@KhangHLe

Copy link
Copy Markdown

Problem

Two independent, reproducible sources of visible flicker when tiled windows exchange positions (move --direction between two windows):

  1. The desktop flashes in the vacated slot. Each window is repositioned with its own SetWindowPos call under SWP_ASYNCWINDOWPOS, so the two moves are queued to each app's own thread and land frames apart — the region one window vacates stays exposed (showing the desktop/background) until the other window's move lands. Reproducible with transparency effects disabled; most visible with two windows of the same size swapping slots.

  2. Backdrop materials blink on effect passes. set_transparency re-fires SetLayeredWindowAttributes even when the value is unchanged. A redundant re-stamp isn't free: DWM re-evaluates the window's composition, which visibly flickers system-backdrop materials (Acrylic/Mica) on windows that carry them (e.g. Windows Terminal with acrylic, Firefox with Mica, Explorer). Effects passes re-apply transparency liberally, so any sync that touches effects blinks every glass window on screen.

Fix

  1. Atomic repositions: plain repositions (ordinary, steady-state-visible windows — no restore, no DPI fix-up, no minimize/maximize/fullscreen special-casing, no active drag) are collected during the redraw loop and committed in a single BeginDeferWindowPos/EndDeferWindowPos transaction after it, so all rects land in the same compositor frame. A lone entry keeps the historical individual async path; anything state-special keeps the individual reposition_window path unconditionally. On any batch failure it falls back to individual repositions.

  2. Idempotent alpha: set_transparency reads the current layered attributes first and returns early when the window is already layered at the target alpha.

Notes

  • WindowPosBatch is a small RAII-ish wrapper in wm-platform around the defer APIs; SWP_ASYNCWINDOWPOS is stripped inside the batch (deferred transactions commit synchronously by design).
  • The batch's synchronous commit matches the pre-existing trade-off documented for surrogate-adjacent repositioning; it only engages when 2+ plain repositions occur in one sync cycle.
  • Developed and behavior-tested on a fork carrying additional features; this branch is the two general-purpose fixes cherry-picked onto current main and compile-verified against it.

Happy to split into two PRs if preferred.

Two independent causes of visible flicker when windows exchange rects:

1. set_transparency re-fired SetLayeredWindowAttributes with unchanged
   values on every effects pass (e.g. the all-windows pass queued on
   animation completion), forcing DWM to re-evaluate composition - which
   visibly blinks system-backdrop materials (Acrylic/Mica) on windows
   that carry them. Now skipped when the window is already layered at
   the target alpha.

2. Plain repositions executed as individual SWP_ASYNCWINDOWPOS calls,
   which land frames apart per-app - so two windows exchanging rects
   expose the desktop in the vacated slot between the two moves, even
   with animations and transparency disabled. Plain (non-animated,
   non-surrogate, steady-state-visible) repositions are now collected
   during the redraw loop and committed in one atomic DeferWindowPos
   transaction, mirroring the existing surrogate batch. Single-window
   cycles keep the historical individual async path.

Known residual: a rare flash remains, suspected cross-sync-cycle split
of the two moves (needs instrumentation; not fixable by within-cycle
batching).
@github-project-automation github-project-automation Bot moved this to 📬 Needs triage in glazewm Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 📬 Needs triage

Development

Successfully merging this pull request may close these issues.

1 participant