Skip to content

Compose the DirectComposition tree behind child HWNDs on Windows - #5

Merged
ChrisUnityArto merged 1 commit into
unity/a11y-automationfrom
unity/dcomp-behind-child-hwnds
Oct 1, 2026
Merged

ChrisUnityArto merged 1 commit into
unity/a11y-automationfrom
unity/dcomp-behind-child-hwnds

Conversation

@ChrisUnityArto

Copy link
Copy Markdown

What

One argument in gpui_windows/src/directx_renderer.rs: DirectComposition::new now calls CreateTargetForHwnd(hwnd, false) instead of true.

Why

The topmost flag decides whether the composition target's visual tree is drawn above (true) or below (false) the window's child HWNDs. With true, anything gpui paints covers every child window of the gpui window.

gpui creates no child HWNDs of its own, so this only bites an embedder that parents a native child. The Unity Hub does exactly that: it embeds a wry / WebView2 webview with build_as_child for inline video. The result on Windows:

  • the webview's frames are completely hidden under gpui's opaque frame (the dialog background and poster painted under the slot);
  • the child still receives mouse input, because hit-testing is per-HWND rather than per-pixel, so a click on the "poster" starts the hidden YouTube player;
  • so the video plays audio with no picture (HUB-7562).

false composes gpui's tree behind its children, which matches macOS, where a child NSView draws above gpui's Metal layer. The Hub's embed code (clipping, hiding under modals and popovers, poster fallback) was already written for that model, and now behaves the same on both platforms.

This change should also go upstream: gpui-ce#222's proposed webview crate, and any other build_as_child user on Windows, hits the same wall.

Risk

  • No effect on a window with no child HWNDs, which covers every gpui window gpui creates by itself.
  • The disable_direct_composition path doesn't use this code and is unchanged.

Testing

  • Windows x64: built the Hub (cargo build -p hub-gpui) against this commit through a local [patch]. In a manual run, the CLI-announcement dialog's inline video showed real frames in sync with audio, and play, pause and seek updated the picture. Before the change: audio only.
  • macOS and Linux: not affected (Windows-only file).

🤖 Generated with Claude Code

`CreateTargetForHwnd(hwnd, true)` puts gpui's visual tree on top of every
child window of the gpui window. gpui creates no child HWNDs of its own, so
this only bites an embedder that parents a native child, such as a wry /
WebView2 webview built with `build_as_child`: gpui's opaque frame covers the
child completely, while the child still receives mouse input and keeps
running. A video embed therefore plays audio with no picture.

`topmost: false` composes gpui's tree behind its children, which matches
macOS, where a child `NSView` draws above gpui's Metal layer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@toddbeauchamp toddbeauchamp left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Taude: Approving. One flag, one Windows-only file, and it fixes the audio-but-no-picture video bug.

macOS: not affected. directx_renderer.rs only builds on Windows, and test-mac passed. The change makes Windows layer child windows the way macOS already does.

Checked:

  • gpui never creates child windows itself, so normal gpui windows don't change.
  • With DirectComposition on, gpui windows get WS_EX_NOREDIRECTIONBITMAP, so false really does put gpui's drawing behind children.
  • The disable_direct_composition path doesn't use this code.

CI failures are unrelated to this PR (#4 fails the same jobs):

  • test-linux, test-windows, msrv: crates/gpui_media/build.rs uses bindgen, but Cargo.toml only lists it as a build dependency on macOS. That bug is on unity/a11y-automation. Fix it there by listing bindgen on all platforms or skipping the bindgen code when the target isn't macOS.
  • clippy: a new GOLDEN_RATIO lint at crates/gpui/src/geometry.rs:3789.

Worth checking before GA (not blocking):

  1. Child windows now draw over all gpui UI on Windows. Check that a tooltip, dropdown or toast over a playing video still hides the webview, like the Hub does on macOS.
  2. Try closing and reopening the dialog, resizing the window, and dragging it to a screen with a different DPI. The webview shouldn't be left behind.
  3. Upstream Zed uses true, so an upstream PR will probably need this to be opt-in.

@ChrisUnityArto
ChrisUnityArto merged commit 1a1b6d6 into unity/a11y-automation Oct 1, 2026
13 of 18 checks passed
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.

2 participants