Skip to content

fix(wm): eliminate visual glitches across resize, drag, hover, and presentation#13

Closed
devin-ai-integration[bot] wants to merge 2 commits into
mainfrom
devin/1778275818-fix-wm-visual-glitches
Closed

fix(wm): eliminate visual glitches across resize, drag, hover, and presentation#13
devin-ai-integration[bot] wants to merge 2 commits into
mainfrom
devin/1778275818-fix-wm-visual-glitches

Conversation

@devin-ai-integration

Copy link
Copy Markdown

Description

Fixes four classes of visual glitches in the window manager that produced tearing, stale pixels through transparent surfaces, overdraw beyond dirty regions, and the wrong cursor over chrome buttons.

wm_logic.cpp — transparent-window opacity bookkeeping

  • get_window_opaque_cover_rects(w) no longer reports a transparent window's client rect as opaque coverage. Treating transparent windows as opaque caused the compositor to skip painting the dock, menubar, and stacked windows behind them, producing black or stale pixels through the transparent surface.
  • window_opaque_bounds(w) returns {0,0,0,0} for transparent windows instead of the client bounds, so callers that gate "fully covered" decisions don't falsely treat a transparent surface as an occluder.
  • window_occlusion_bounds(w) now also explicitly returns an empty rect for transparent windows, matching the opaque-bounds contract.

wm_render.cpp — clipped chrome paint

  • Added gui_fill_rounded_rect_clipped / gui_draw_rounded_rect_clipped helpers that intersect the requested rect with the dirty clip before drawing, falling back to an axis-aligned clipped fill when the rounded shape would extend past the clip. This prevents the decoration paint from overdrawing pixels owned by neighbouring windows already composited into this frame.
  • draw_window_decoration_frame now uses the clipped helpers for every shadow / outline / frame body / inner stroke fill.

wm_main.cpp — present fence

  • Inserted an sfence before present_frame so all backbuffer stores are globally visible before the display engine latches the present buffer. Without this fence, weakly-ordered cores or write-combining stores can let the scanout latch a partially composited frame, manifesting as tearing or sparkle.

wm_logic.cpp — cursor kind on chrome buttons

  • update_cursor_kind previously set the move cursor whenever hover_frame_index >= 2, even when the pointer was specifically over a close/minimize/maximize button (hover_button >= 0). The cursor now stays as the arrow when hovering a chrome button, so the click affordance isn't visually contradicted by a drag affordance.

Type of Change

  • Bug fix
  • Feature
  • Refactor / cleanup
  • Documentation
  • Tooling / build
  • Asset update

Area

  • Meridian / boot image
  • Kernel
  • Memory management
  • Filesystems / storage
  • USB / input
  • Display / window manager
  • Userspace / shell
  • App
  • Networking
  • Audio
  • Documentation / website
  • Tools / generated assets

Checklist

  • Built release: meson compile -C build/release boot-disk iso
  • Built debug: meson compile -C build/debug boot-disk iso (built wm.elf target)
  • Ran smoke tests when applicable: meson test -C build/debug --suite smoke --print-errorlogs
  • Tested the affected QEMU target or hardware path
  • Checked serial output when changing boot, kernel, drivers, shutdown, or panic paths
  • Tested /data persistence when changing storage, FAT32, USB storage, rootfs staging, or image generation
  • Included screenshots for visible desktop, app, website, cursor, icon, font, or wallpaper changes
  • Kept generated files and source assets in sync when changing runtime assets

Validation

$ ninja -C build/debug wm.elf
ninja: Entering directory `build/debug'
[1/3] Compiling C++ object libwm_objs.a.p/src_usr_wm_wm_logic.cpp.o
[2/3] Linking static target libwm_objs.a
[3/3] Generating wm with a custom command

End-to-end QEMU exercise of the resize / drag / hover / button paths was not run as part of this change; reviewer or follow-up should confirm visually.

Notes for review

Things most worth a careful eye:

  • Empty-rect contract for transparent windows. window_opaque_bounds and window_occlusion_bounds now return {0,0,0,0}. All current call sites either early-out on w.transparent (e.g. refresh_window_visible_regions, get_window_opaque_cover_rects) or are guarded by rect_intersection / rect_contains, both of which handle empty rects safely. Worth a second pass to confirm no caller relies on the old "opaque == client bounds" behaviour.
  • asm volatile("sfence" ::: "memory") is x86-only. The supported boot target is x86_64 so this is fine, but flagging it explicitly.
  • gui_fill_rounded_rect_clipped fallback. When the rounded rect is not fully contained in the dirty clip, it falls back to an axis-aligned clipped fill (square corners). In current callers the cache surface used by ensure_window_decoration_cache is sized to fully contain the rect, so the rounded path is always taken; the fallback is defensive. Please confirm no caller passes a smaller clip in the future.
  • Cursor behaviour change. Hovering a window's chrome button now leaves the cursor as the arrow rather than the move cursor. Confirm this matches the intended UX.
  • Out-of-scope painting-order observation. wm_compose.cpp paints opaque windows first then transparent windows, which can cause a transparent window to draw over an opaque window above it in z-order. The user's listed files did not include wm_compose.cpp, so this PR does not touch that path. Worth a follow-up if confirmed.

Link to Devin session: https://app.devin.ai/sessions/a59e645119c741de9eedffd4f027c160
Requested by: @unionyxx

unionyxx and others added 2 commits May 8, 2026 21:34
…d clipped chrome paint

* wm_logic.cpp: transparent windows now report no opaque coverage / no
  occlusion bounds, so the compositor keeps painting the dock, menubar
  and stacked windows underneath them instead of skipping repaint and
  leaving stale or black pixels behind transparent surfaces.
* wm_main.cpp: re-establish an sfence before submitting a present, so
  backbuffer stores are globally visible before the display engine
  latches the present buffer (prevents tearing/sparkle on weakly
  ordered cores or write-combining stores).
* wm_render.cpp: restore gui_fill_rounded_rect_clipped /
  gui_draw_rounded_rect_clipped helpers and use them for the window
  decoration shadow, frame, body fill and inner stroke so we never
  overdraw beyond the dirty region during partial repaints (which
  previously corrupted neighbouring already-composited windows).

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
When the pointer is over a window's close/minimize/maximize button the
cursor was switching to GUI_CURSOR_MOVE because update_cursor_kind only
inspected hover_frame_index without checking hover_button. The move
cursor visually implies a drag affordance, which conflicts with the
button click affordance and momentarily flickers as the pointer crosses
into the button. Keep the arrow cursor for the button case so the
control affordance is unambiguous.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@unionyxx unionyxx closed this May 8, 2026
@unionyxx unionyxx deleted the devin/1778275818-fix-wm-visual-glitches branch May 8, 2026 21:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant