Repository navigation
docs: native windows and glass on Windows and Linux, the research - #236
Merged
Merged
Conversation
`Popout.envSwitch` cached its answer in a struct declared inside the function, and the struct did not name the variable. Zig gives a struct like that one type for every comptime argument, so every switch shared one cache: whichever was read first in a run answered for all of them. In practice `FIZZY_POPOUT` is read first (`beginFrame`), so `FIZZY_NATIVE_GLASS=0`, `FIZZY_NATIVE_MENUS` and `FIZZY_NATIVE_DIALOGS` were ignored and took `FIZZY_POPOUT`'s value instead (unset: the settings). The struct now holds the name, which makes it a type of its own per switch. Seen while testing a new switch in the Ubuntu VM: it never turned on. Reproduced outside fizzy with the same function in a 15-line program on Zig 0.16.0 (`ENV_B=1` read as null, `ENV_A=1` made B true), fixed there the same way.
What the macOS pop-out and Liquid Glass work rests on, taken apart into the pieces each OS has to supply, and how Windows and Linux can supply them, with sources. No OS but macOS 26 merges or refracts glass (Microsoft has said Windows is not getting it), so elsewhere the shape stays fizzy's own (LiquidField's smooth union) and the OS supplies only the blur under it: on Windows the visual layer's host backdrop clipped to the field's outline (a backdrop cannot be a mask brush's source), on Wayland ext-background-effect-v1, which GNOME 51 and Plasma 6.7 now ship, its region applied with the surface commit. Carried views on Windows go into one overlay per display rather than a window moved under the pointer. X11 is deprioritised as its sessions are removed. Step 0 for both: the SPIR-V and DXIL glass programs predate #227 and still draw the earlier glass. It works under docs/NATIVE_WINDOWS_PLAN.md (#226), whose Windows decisions stand: the slider along DWM's backdrop types, one activation group, an Acrylic carry window, the drop zones in the app's glass. This file is the research for that plan's Phase 2 (per-shape glass through Windows.UI.Composition, which waits on a go-ahead) and for its Linux blur region. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 8, 2026
foxnne
marked this pull request as ready for review
October 8, 2026 14:54
foxnne
pushed a commit
that referenced
this pull request
Oct 8, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Euk6iCavQdW7MG5FN2vvFw
2 of 8 tasks
foxnne
added a commit
that referenced
this pull request
Oct 8, 2026
Part of #226 ## What changes Two pieces pixi's dropper magnifier (fizzyedit/pixi#3) needs to be a clear liquid orb over its zoom, with no frost and strong refraction: native Liquid Glass on macOS, the app's glass everywhere else. And the SDK release that ships them, so the orb can be tried against `main` as soon as this merges. **`core.native_glass.offered`** - The app publishes each frame whether the OS draws glass declared outside a view drag (`publishOffered`, from `Popout.beginFrame`'s `nativeGlass()`). - A plugin that sees it declares its glass with `native_glass.add`. Fizzy's overlay of Liquid Glass already stays up while any glass is declared. - Pixi declares a clear piece (`frost = 0`, the lens alone) over a zoom it draws in the window, so the OS's lens refracts the window beneath. - Off macOS 26, or with native glass off, `offered()` is false. **`LiquidField.drawPicture` / `pictureMargin` / `lens`, and `glass_look.forLens`** - The app's glass as a lens over a picture the plugin drew. The middle is the picture as it is, with no frost, colour or lift; the rim bends and lights it. - It reads no capture, so the middle stays pixel-exact. - On the web, where no look is published, it is the earlier glass. `pictureMargin` says how far past the shapes the picture should reach for that glass's rim, at the strongest shape lens. **Tried and dropped:** pixi tried the drop zones' frosted glass with its zoom laid over it. On a Mac it read as a thick grey border or a flat rim. **Merged with main** (#234–#236, #239, #240). ## SDK impact - [ ] None - [ ] Core-only or additive: reaches plugins at the next SDK release - [ ] Fingerprint moved: recorded in `sdk/src/version.zig`, `sdk_version` untouched, PR labelled `sdk` - [x] SDK release: bumps `sdk_version`, lists the `sdk` PRs since the last `sdk-v*` tag `sdk_version` goes 0.2.17 → 0.2.18 (ef95301). Since `sdk-v0.2.17` this release carries: - this PR's core additions; - #223, #224 and #227's core (the one-slider glass, `glass_look`, `core.native_glass`, `core.screens`'s menus and dialogs). The fingerprint has not moved, so installed plugins keep loading. Merging tags `sdk-v0.2.18` and, per #240, asks the store plugins to repin. ## Verified - `zig test core/gfx/glass_look.zig`: 13 pass. - `zig fmt --check` and `zig ast-check` on the changed files. - `tests/integration.zig` compiles `drawPicture` and checks `pictureMargin`. - CI: Linux, macOS, Windows, integration and the Windows cross-build on this head. - [x] macOS: pixi's orb tried locally against this branch by the maintainer, who approved it. - [ ] Windows: - [ ] Linux: - [ ] Web: ## Follow-ups - pixi#3 repinned to `sdk-v0.2.18` once it's tagged. - The orb pinching off pixi's sample button and merging with it (the plan's next step for this consumer). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Euk6iCavQdW7MG5FN2vvFw --------- Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 8, 2026
foxnne
added a commit
that referenced
this pull request
Oct 8, 2026
) Part of #236 (`docs/WINDOWS_LINUX_GLASS_PLAN.md`, step 0; no plan issue) ## What changes On Linux (Vulkan) and Windows (D3D12) the in-window glass — dialogs, menus, a drag's drops — now draws Apple's lens and follows the one window-opacity slider (`glass_look`), as it already does on macOS. The SPIR-V and DXIL embedded in `LiquidField` were last compiled in #206; the HLSL changed in #227, so those two platforms kept drawing the earlier glass whatever `publishLook` said. - **The HLSL needed nothing.** Apart from each language's declarations it is `liquid_glass.glsl` and `liquid_glass.metal` line for line, with three deliberate, documented differences (the loop form, samples at level 0, the dither taking `uv`). `LiquidField.sample` agrees with them on the field. - **Compiled with the commands at the top of the HLSL**, by SDL_shadercross with the DXC it vendors (libsdl-org/DirectXShaderCompiler at 2c84a1c5) — the toolchain #206 used: it compiles #206's HLSL to #206's SPIR-V and DXIL byte for byte. The DXIL is signed (validator 1.9, as before) and passes `dxv`; the SPIR-V passes `spirv-val`; the interface is unchanged (two samplers, one uniform buffer, colour at location 0, uv at 1). That DXC wants clang on Linux: built with GCC 13 it corrupts its heap compiling DXIL. - `publishLook`'s comment says every form of the program draws the lens now, and that the web publishes none (`Editor.zig` passes null on wasm). - Step 0 of `docs/WINDOWS_LINUX_GLASS_PLAN.md` and the "still to do" in `docs/NATIVE_WINDOWS_PLAN.md`'s materials library are marked done. ## SDK impact - [ ] None - [x] Core-only or additive: reaches plugins at the next SDK release — the programs are embedded in `core`. 0.2.18 (#228, `LiquidField.drawPicture` for pixi's dropper magnifier) shipped without them, so the magnifier's lens shows on Vulkan/D3D12 from the next release. - [ ] Fingerprint moved: recorded in `sdk/src/version.zig`, `sdk_version` untouched, PR labelled `sdk` - [ ] SDK release: bumps `sdk_version`, lists the `sdk` PRs since the last `sdk-v*` tag ## Verified Not yet seen in the app: the cloud session this was built in could not build fizzy (its network policy denies gitlab.freedesktop.org and sndio.org, which sdl_zig fetches from). What was checked instead (and CI on fb422e9 builds the app and passes its tests on macOS, Windows and Linux, the headless integration tests on Linux, and the Windows cross-build of fizzy's backend — that shows the new programs embed and link, not how they draw): - [ ] macOS: unchanged (Metal source untouched). - [ ] Windows: `core`, with `LiquidField` and the new DXIL embedded, compiles for x86_64-windows-gnu; the DXIL validates (`dxv`). **Not run on D3D12** — wants a Windows machine (WARP is enough). - [ ] Linux: the new SPIR-V drawn through SDL_GPU on lavapipe, as `GpuRenderer` draws a program (frost at sampler 0, sharp at 1, the punch and add passes), with `pack` + `applyLook` uniforms and the look from `glass_look.inApp` at window opacity 0, 0.35, 0.7, 1 — a dialog (`forText`) and three drops beside a carried card (`forDrops`), over an opaque and a translucent window. Within 1/255 of `liquid_glass.glsl` (built with glslang) at every pixel of every case; with no look published, identical to the old SPIR-V; with one published, the old SPIR-V was off by up to 204/255. `core` compiles and its check runs on x86_64-linux. **Not seen in the app.** - [ ] Web: unchanged (GLSL untouched; the web publishes no look). ## Follow-ups - See it in the app on Linux and Windows (a dialog, and a drag's drops, at a few window opacities). - The next SDK release carries these to plugins that build `core` in. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Vci2bBhv2EH5QxygzWxmx8 Co-authored-by: Claude <noreply@anthropic.com>
foxnne
added a commit
that referenced
this pull request
Oct 8, 2026
…=1 (#237) Part of #226 (`docs/NATIVE_WINDOWS_PLAN.md`, "Linux — the compositor's blur"); step 4 of `docs/WINDOWS_LINUX_GLASS_PLAN.md` (#236). ## What changes With `FIZZY_BLUR_BEHIND=1`, on a Wayland compositor that blurs, the window's base goes translucent at the window opacity over the desktop's blur, as it already does over macOS's vibrancy and Windows' Acrylic. Elsewhere, and without the switch, nothing changes: the window stays opaque. - **`platform/wayland_blur.zig`:** binds `ext-background-effect-v1` (GNOME 51, Plasma 6.7, niri) or KDE's `org_kde_kwin_blur`. - It uses SDL's own libwayland-client (`RTLD_NOLOAD`), with both protocols declared by hand, on an event queue of its own. - The region is sent only when it changes, and it lands with the commit the frame's present makes. - "No blur" is an empty region on the ext protocol, and the blur object unset and released on KDE's protocol (KWin blurs the whole window behind an empty one). - The first probe logs what it found. - **`platform/blur_region.zig`** (std-only, unit tested): steps the rounded frame into rectangles. - **`linux_titlebar.blurBehind` and `Editor.tick`:** the blur covers the frame inside the shadow's margin while the window is translucent, and is square when maximized. Off by default until someone sees it on a desktop that blurs. ## SDK impact - [x] None ## Verified - [x] Linux, Ubuntu 26.04 / GNOME 50 (aarch64 UTM VM, native Wayland, `FIZZY_BLUR_BEHIND=1 WAYLAND_DEBUG=client`). GNOME 50 has neither protocol, so this covers the "absent" path only: - one `get_registry` on fizzy's own unnamed queue; - the log line `ext-background-effect-v1 absent, org_kde_kwin_blur absent`; - no `wl_display.error`; - fizzy alive after 12 s, window opaque. - [x] Protocol details: - opcodes checked against wayland.app (`ext-background-effect-v1`) and plasma-wayland-protocols' `blur.xml`; - KWin's empty-region-means-whole-window rule read in `src/plugins/blur/blur.cpp` on `Plasma/6.6`. Master only takes a non-empty region. - [x] A stub compositor speaking both protocols, in the cloud session that wrote this: the region applied on commit, unchanged frames sent nothing, cleared when opaque, KDE's unset, nothing asked when capabilities = 0. - [x] `fizzy-blur-region-tests` (5 tests). - [x] Gates: `zig build`, `test`, `test-integration`, `check-web`, `test-sdk-version`, and the Windows and Linux cross-builds. - [ ] Not seen where the blur would actually show: GNOME 51, Plasma 6.7+, or Plasma ≤6.6 (KDE's protocol and the "off" path). Please try one of those before this goes default-on: - the blur sits inside the frame, not in the shadow margin; - the corners line up; - maximized is square and full; - opacity 1 gives no blur. - [ ] macOS / Windows / Web: no change there (the code is `comptime`-gated to Linux; it builds on all of them). Relies on the `envSwitch` fix (#235, merged): before it, `FIZZY_BLUR_BEHIND` read as `FIZZY_POPOUT`. ## Follow-ups - Default-on once seen on GNOME 51 or Plasma. - Wayland popups (menus and dialogs as `xdg_popup`s) are the other half of step 4, not built. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
foxnne
added a commit
that referenced
this pull request
Oct 8, 2026
Part of #226 (`docs/NATIVE_WINDOWS_PLAN.md`, Phase 2); step 5 of `docs/WINDOWS_LINUX_GLASS_PLAN.md` (#236). ## What changes Adds `spikes/windows-composition/`, a standalone Windows program answering the four questions the plan's Phase 2 (the drag's glass as an overlay over the desktop's blur) waits on. Nothing in fizzy changes. The research doc's step 5 records the answers. How it works: - **It uses fizzy's own pins** of fizzyedit/SDL and zigwin32. - **The window is fizzy's kind:** a transparent SDL window presenting on D3D12 through the fork's topmost DirectComposition target. - **Under it:** a Windows.UI.Composition target that isn't topmost, holding the host backdrop, cut to a pill (∪ an orbiting circle) that sweeps across the window. Five modes: - no clip; - a rounded-rect clip; - a Direct2D path rebuilt every frame; - an AlphaMask effect over a swapchain the app presents; - the app's picture hosted in composition's own tree (the fallback for question 1). - **An overlay over the whole display** tries four click-through styles. - **WinRT is bound by hand.** Every GUID and vtable slot was read from the OS's own WinMetadata with System.Reflection.Metadata, and each is cited in `src/winrt.zig`. `IGeometrySource2D` and `IGraphicsEffect` are COM objects written in Zig. At about 1,700 lines this is over CONTRIBUTING's 800-line guide. It's throwaway code in `spikes/`, built by nothing else. ## SDK impact - [x] None ## Verified Windows 11 ARM VM (`fizzy-win11`, build 26300, D3D12 on WARP, 60 Hz), aarch64-windows-gnu, driven by `SendInput`. Offsets were measured from screenshots: the clip's glass against the red box SDL presents round the pill, six samples per run, with a direction mark telling lead from lag. 1. **Two targets on one HWND: yes.** `CreateDesktopWindowTarget(hwnd, FALSE)` on SDL's claimed window succeeds and draws under SDL's picture. Static, the two align to the pixel. 2. **Path clip rebuilt every frame: works and is antialiased** (1–2 px). Composition calls `GetGeometry` once per frame. With SDL's VSYNC present the clip **leads** the picture by the present queue: | SDL present | Clip delay | Glass vs SDL's rim | |---|---|---| | VSYNC (frames in flight 1 or 2) | 0 | ~2.2 frames ahead (35 ms) | | VSYNC | 1 | 1.3 frames ahead | | VSYNC | **2** | **0 ±0.5 px** | | MAILBOX / IMMEDIATE | 0 | **0 ±0.5 px** | So fizzy either gives composition the shape from as many frames back as the present queue is deep (measured, not assumed), or has the fork present through a frame-latency waitable swapchain. 3. **Effect-graph mask: yes.** D2D's AlphaMask with a `CompositionEffectSourceParameter` over `CreateCompositionSurfaceForSwapChain` shows the glass exactly where the presented mask is. 4. **Overlay:** | Style | Same-thread click | Another process | Tree shows | |---|---|---|---| | `TRANSPARENT \| LAYERED` | passes | passes | yes | | `TRANSPARENT \| LAYERED`, alpha 255 | passes | passes | yes | | `TRANSPARENT` alone | blocked | blocked | yes | | `HTTRANSPARENT` | passes | **blocked** (though `WindowFromPoint` says it passes) | yes | A drag under the layered overlay keeps its capture (30/30 motion events). `SDL_SetWindowOpacity(0.6)` (layered) leaves both SDL's picture and the composition tree drawing. - [ ] **The blur on a hardware GPU.** The VM draws the host backdrop black. Run it, press `3` (path) with the backdrop on, and check the blur and the edge, at clip delay 0 and 2 (`D`). - [ ] **The present queue's depth at 60/120/144 Hz** on hardware, and Windows 11 23H2/24H2. - [ ] macOS / Linux / Web: n/a (Windows only; not part of fizzy's build). ## Follow-ups - If the blur holds on hardware: the overlay (step 6), with the clip delayed by the measured present-queue depth, or a waitable swapchain in the SDL fork. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
foxnne
added a commit
that referenced
this pull request
Oct 8, 2026
…#238) Part of #226 (`docs/NATIVE_WINDOWS_PLAN.md`); step 3 of `docs/WINDOWS_LINUX_GLASS_PLAN.md` (#236). ## What changes With floats as windows (`FIZZY_POPOUT=1` on Windows), menus and dialogs now leave the main window as they do on macOS (`viewports.menus`). Each one is a borderless window owned by the window it opens from, never activated, and dressed by DWM (`win32_titlebar.viewportMenuChrome`): - Acrylic (`DWMSBT_TRANSIENTWINDOW`, the material of Windows 11's own menus); - DWM's rounded corners and shadow; - no border, no system menu, no DWM transitions. DWM draws a never-activated window's backdrop as its solid fallback, so the menu's window is kept looking active (`WM_NCACTIVATE`). An owned window isn't carried when its owner moves, so a menu's window rides on nothing and is placed each frame. ## SDK impact - [x] None ## Verified Windows 11 ARM VM (`fizzy-win11`, WARP, aarch64-windows-gnu Debug), driven with real input (`SendInput`) and checked against the window list (`EnumWindows` plus DWM attributes) and screenshots: - [x] The File and Help menus and the "Unsaved changes" dialog each open in a window of their own: - owned by the main window and directly above it in the z-order; - `WS_EX_NOACTIVATE`, no `WS_SYSMENU`; - backdrop type Acrylic (3), corner preference round; - the main window stays the foreground window. - [x] The first click on an item acts: New File opens `untitled-1`, and Open Files opens the Open dialog. A temporary log showed the press arriving on the menu's window and mapping into the menu's dvui subwindow, not what lies under it. - [x] The dialog's window hides when the main window is minimized and comes back on restore. - [x] Gates: `zig build`, `test`, `test-integration`, `check-web`, `test-sdk-version`, and the Windows and Linux cross-builds. - [ ] **The look on a hardware GPU.** The VM's DWM draws every backdrop as its fallback, so these need a real machine: - Acrylic rather than a solid colour; - rounded corners and a shadow; - whether the `WM_NCACTIVATE` trick keeps the Acrylic. - [ ] A menu opened over a float's window. Not exercised; creating a float needs a view drag. - [ ] macOS / Linux / Web: unchanged (`viewports.menus` was already on for macOS, and this is the Windows branch). Seen while testing, not caused by this change: - A menu closes when fizzy is deactivated. My harness's own process start deactivated it, which made a later click fall through to the main window. - A dialog drifts a few pixels left over each minimize/restore. It does the same drawn in-window (`FIZZY_NATIVE_DIALOGS=0`). ## Follow-ups - NATIVE_WINDOWS_PLAN's Phase 1 on Windows: the slider along DWM's backdrop types, one activation group, and a policy probe. - Caption buttons in a Windows float's header (in `app/layout/Floats.zig`). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
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.
Part of #226 (
docs/NATIVE_WINDOWS_PLAN.md)What changes
Adds
docs/WINDOWS_LINUX_GLASS_PLAN.md, the research behind the plan's Windows Phase 2 and its Linux blur region. It takes apart what the macOS pop-out and Liquid Glass work rest on, piece by piece per OS, and how Windows and Linux can supply each piece, with sources.The findings:
ext-background-effect-v1(GNOME 51, Plasma 6.7, niri), withorg_kde_kwin_bluras the fallback for older Plasma.The plan's Windows decisions stand: the slider along DWM's backdrop types, one activation group, an Acrylic carry window, and the drop zones in the app's glass. Per-shape glass through Windows.UI.Composition is still the plan's "Phase 2, only with a go-ahead".
Steps 3 (Windows menus as Acrylic popups) and 4 (the Linux blur) are built in the two PRs stacked on this one.
This is a cloud session's research. Its claims marked (spike) were not run.
SDK impact
Verified
Docs only.
Follow-ups
The two step PRs stacked on this one.
🤖 Generated with Claude Code