Repository navigation
build: fizzy builds on dvui's SDL3 backend again, and CI keeps it - #328
Merged
Merged
Conversation
`zig build -Dnative-backend=sdl3` had stopped compiling: the platform module reached into fields and symbols only fizzy's own backend has (the floats' window slots, `window_skin`, `present_hook`, and the Core Animation transaction calls in `macos_monitor.m`). They are now behind one flag, `platform.own_backend`, and on dvui's backend those parts do nothing. A macOS CI step compiles the mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The monitor takes full screen and zooms over from AppKit and draws their steps itself, which needs fizzy's backend's frame hooks; on dvui's backend its steps were drawn out of time with the window. There it now installs only the guard that keeps the window's content under the title bar through SDL's styles (`fizzy_macos_window_keep_full_size_content`): fizzy's chrome stays, and without that guard the window came back from each full-screen round trip a title bar shorter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 of 15 tasks
foxnne
added a commit
that referenced
this pull request
Oct 10, 2026
…324) Part of #281: step 1 of "Popout's generic half moves into the backend". A dvui app on fizzy's backend can now open OS windows through `@import("backend").viewports`, the same API fizzy's Popout uses. Before this PR it lived in fizzy's `src/backend/backend_native.zig`, out of reach of any other app. That is what `run-dvui` in example-app will build on. ## What moved - **`backend/src/viewports.zig`** is the `viewports` namespace from `backend_native.zig`, moved as it was. The `if (comptime !supported) return …` guards are gone: this backend always supports viewports, so `supported` is just `true`. Each function still calls `dvui.currentWindow().backend.impl.viewport*`. - **`WindowGlassLook`** moves into `platform.window`, beside the `WindowGlass` it converts to. Both the main window's glass and a viewport's take it. - **`SDLBackend.viewports`** exports the API. The backend's slot array, also named `viewports`, is renamed `viewport_slots` to free that name (a mechanical rename, 23 sites). - **Fizzy** re-exports it: `pub const viewports = @import("backend").viewports;`. Popout and Editor are unchanged. ## A stand-in for backends without OS windows `backend/src/viewports_none.zig` has every name `viewports` has, and each one reports that there are no OS windows: `available()` is false and `open` returns null. An app's floats, menus and dialogs then stay in the main window. It is the web's former stand-in, moved into the package, so these all share it: - fizzy on the web, on dvui's testing backend (integration tests) and on dvui's own SDL3 backend; - any dvui app built on either kind of backend. That app imports it beside the backend (`viewportsNoneModule` in `backend/build.zig`, which needs only dvui) and picks the backend's own where it has one: ```zig const viewports = if (@hasDecl(backend, "viewports")) backend.viewports else @import("viewports_none"); ``` `backend_native.zig` does exactly that. `backend_web.zig` uses the stand-in, and its `WindowGlassLook` is the stand-in's. The stand-in's `windowGlass` takes any look: on dvui's SDL3 backend fizzy still dresses its main window with `platform`'s. **With #328** (`-Dnative-backend=sdl3` builds again, with a CI step): whichever lands second renames the two `impl.viewports` loops that #328 guards in `platform/window.zig` to `impl.viewport_slots`. I trial-merged the two locally with that rename. Both `zig build` and `zig build -Dnative-backend=sdl3` succeed, and the SDL3 build starts and plays the soak tape (no verdict there, since verdicts read fizzy's backend's counters). ## Verification On macOS, on this branch: `zig build`, `test` (514/515, 1 skipped), `test-integration` (358/358), `check-web`, `test-sdk-version`, the backend's own tests, and the Windows and Linux cross-builds all pass, with and without the stand-in commit. No behaviour changes on fizzy's backend: the same functions run, reached by a different path. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The two loops own_backend guards in platform/window.zig read viewport_slots, as #324 renamed them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
foxnne
marked this pull request as ready for review
October 10, 2026 13:34
foxnne
enabled auto-merge (squash)
October 10, 2026 13:36
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.
What changes
zig build -Dnative-backend=sdl3(fizzy on dvui's own SDL3 backend) compiles and runs again, and CI now builds it on macOS so it can't break unnoticed again.On
mainit stopped compiling. The backend package'splatformmodule reached into things only fizzy's own backend (backend/src/SDLBackend.zig) has:win.backend.impl.viewports, inplatform/window.zig: restyling and reskinning each float's own window.win.backend.impl.window_skin, inplatform/window.zig.back.present_hook, inplatform/macos_monitor.zig.fizzy_native_transaction_begin/fizzy_native_viewport_transact, inplatform/macos_monitor.zig. These are defined in the backend'smacos_monitor.m, so the mode failed at link time once the first three compiled.All of these now sit behind one comptime flag,
platform.own_backend, which is true only when@import("backend")is the package's own backend: that is, the one that declaresplatform. On dvui's backend those parts do nothing.SDLBackendasserts the flag at comptime, so fizzy's own build fails rather than silently losing them.Why one flag instead of a
@hasFieldper field: #324 renamesviewportstoviewport_slots. With a per-field@hasField("viewports"), that rename would turn the guard false on fizzy's own backend, and floats would quietly stop being reskinned. With the flag, a renamed field still breaks fizzy's build.The transaction calls aren't just a link fix.
macosAppPreBeginSyncopens a Core Animation transaction that only the present hook closes. Without the guard, dvui's backend would have left that transaction open through a full-screen transition.Full screen on dvui's backend: AppKit's own, with fizzy's chrome
Full screen was broken on this mode. The macOS monitor (
platform/macos_monitor.zig+macos/window_monitor.m) takes full screen and zooms over from AppKit. It animates the window itself and draws each step from the backend's begin and present hooks inside a Core Animation transaction, which only fizzy's backend has. On dvui's backend those steps were drawn out of time with the window.macos_monitor.installnow leaves that out on dvui's backend. The window goes in and out as AppKit and SDL take it, and every query (in_fullscreen_space,zoomed, titlebar inset) answers from AppKit's own state.One piece of the monitor is about fizzy's chrome, not its backend: the
setStyleMask:guard. SDL puts its own style on the window at will-enter and did-exit, andstyleTitledputs fizzy's back. Without the guard, AppKit keeps the content size across each change. Measured with the monitor left out entirely, the window came back from each round trip 40 pt shorter: 800 → 760 → 720.fizzy_macos_window_keep_full_size_contentinstalls that guard alone, and on dvui's backend it is allinstalldoes.What stays on dvui's backend: the frosted or Liquid Glass window, the transparent title-less titlebar and the titlebar hit test. They are plain AppKit calls that work on any SDL window. What goes: floats as OS windows, the window skin on float windows, custom GPU programs, fizzy's own full-screen and zoom animation, live-resize sync and the health counters.
Option considered: drop the mode
The other way to fix this was to remove
-Dnative-backend=sdl3entirely:NativeBackend, the build option, and thesupported/@hasDeclfallbacks. Fizzy's own app depends on its backend's viewports, glass and window skin, and nothing shipped on the sdl3 mode. I built that version and it passed every gate.The decision went to keeping the mode. A dvui app that uses fizzy's backend package on the stock dvui backend is in the same position as fizzy in this mode (example-app's coming
run-dvui). Building fizzy this way is the cheapest proof that the platform pieces still work for such an app. It costs one guard per backend-only reach, plus one CI step.CI
The macOS leg of the
testmatrix gets two steps: fetch with-Dnative-backend=sdl3, thenzig build -Dnative-backend=sdl3 -Dno-emit. It runs on macOS only because all four breakages were in macOS-only branches, which a Linux build never analyses. Cost: dvui's own SDL is compiled on that leg. It isn't in the leg's saved Zig cache until the nextbuild.zig.zonchange re-keys it.Coordination
#324 landed first, carrying the stand-in
viewportsfor dvui's backend (viewports_none). Main is merged into this branch (1dca6885): the twoown_backend-guarded loops inplatform/window.zignow readimpl.viewport_slots, andzig build,-Dnative-backend=sdl3and the gates pass on the merge.SDK impact
platformaren't part of the plugin boundary, andtest-sdk-versionpasses.Verified
Gates pass:
zig build,zig build test(514/515, 1 skipped),zig build test-integration(44/44 steps, 358/358),zig build check-web,zig build test-sdk-version.zig build -Dnative-backend=sdl3builds, links and runs.Full screen, driven through the agent plugin (
fizzyedit/agent'sfizzy-mcp, in an isolated--profile).fizzy.toggleFullScreenran four times; after each toggle, fizzy's layout (fizzy_screen) and AppKit's window frames (CGWindowList) were sampled for 3.5 s. The start frame is 300,204 1200×800 in every run:The 39 pt at the top in full screen is the area below the notch, the same on both backends. Fizzy's layout follows each way: full width with the titlebar strip collapsed in full screen, the strip back after.
Screenshots after a round trip on the sdl3 build show the frosted window with its transparent titlebar and traffic lights.
The
own_backendassert works: pointing the flag at a declaration that doesn't exist fails fizzy's own build.zig build -Dtarget=x86_64-windows-gnu -Dno-emitcross-compiles. It compiles; whether it runs wasn't tested.linux_titlebar.zigis relevant there, and it is unchanged.check-web.Follow-ups
impl.window, which dvui's backend also has.FIZZY_VERDICTwrites nothing on dvui's backend: verdicts read fizzy's backend's health counters, which dvui's backend doesn't have. The soak tape itself plays there (found by the "Floating glass windows" session).🤖 Generated with Claude Code