DEV Community

Batuhan Demirbilek
Batuhan Demirbilek

Posted on

Putting your live windows on an infinite canvas: the "park & swap" trick (C++ / WGC / D3D11)

The problem

Alt-tab and multiple monitors are how we've juggled windows for 30 years. I wanted something spatial: zoom out, see every window at once; zoom into one, work in it for real; zoom back out. Like a map for your desktop.

On Linux there's niri and driftwm (they're compositors). On Windows you can't replace the compositor — so the question was: can you do this on top of Windows, with the real windows, in a single exe?

Attempt 1 (and why it fails)

The naive idea: capture every window, hide the originals off-screen, and draw the captures on a fullscreen surface. When the user "dives" into one, show the real window again.

This breaks immediately. The moment a window is fully occluded or moved off the visible desktop, DWM stops compositing it — Windows.Graphics.Capture then returns black or stale frames. Your beautiful canvas fills with black tiles.

The park & swap trick

The fix is to never fully hide a window:

  1. Each window is captured with Windows.Graphics.Capture (FreeThreaded frame pool, poll-based) into a persistent D3D11 texture.
  2. The window is "parked" in a 2px-tall visible strip at the bottom of the primary monitor. 2px is enough that DWM keeps compositing it, so the capture stays live — but it's invisible behind the canvas.
  3. The canvas is a borderless fullscreen D3D11 swapchain. Each window is a 1:1-pixel textured quad placed in world space; pan/zoom is just a camera transform. A D2D/DWrite overlay draws labels, a world-anchored dot grid, the minimap, docks.
  4. When you zoom past a threshold (or double-click), the real HWND is moved onto its quad's screen rect and given focus. Now you're typing into the actual window — no input simulation, no proxying. Pull back (a thumb-button press) and it returns to the park strip.

So there are two states per window: parked (you see its live texture on the canvas) and swapped-in (you see and use the real window). The transition is a SetWindowPos.

Things that bit me

  • IsBorderRequired(false) and the borderless-capture path black out capture on some Windows builds — removed.
  • CopySubresourceRegion with content-sized copies + a drain-to-newest loop also produced black tiles; a single TryGetNextFrame + full CopyResource is the proven path.
  • Added a constant-buffer field the pixel shader reads, but only bound the cbuffer to the vertex stage — every tile came out alpha=0 (invisible). If a shader stage reads a cbuffer, bind it to that stage.

Result

A single 559 KB exe (statically linked, no .NET/redist), ~3300 lines in one translation unit. Idle-throttled so it doesn't cook the GPU. It has grown search-and-fly, sticky notes, a minimap, an app launcher, named workspaces.

Demo + download: https://github.com/13auth/spatial-canvas

Happy to answer questions about the capture/compositing pipeline.

Top comments (0)