Sokol backend for DVUI.
  • Zig 83.3%
  • Objective-C 15.6%
  • GLSL 1.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Mikael Säker da1945e926 backend: read a render target back, and a way to dump one
`textureReadTarget` was the last stub. sokol has no readback and a target
it created is GPU-private, so getBytes on it fails outright; the working
route is a blit into a shared-storage staging texture, a wait, then
getBytes from the copy. That is what dvui's Picture needs to export a
PNG.

It synchronises with the GPU, unavoidably — the pixels do not exist until
the pass that writes them has run — so it belongs in export and debugging
paths, not in a frame with a deadline.

DVUI_SOKOL_DUMP_TARGETS=<prefix> writes every render target of one frame
to raw RGBA files. That is what finally settled a blur nobody could see:
the dumps showed the capture was a perfect image of the editor pane and
the blur chain was working exactly as intended, which moved the question
from "is it broken" to "is there anything in a blurred white page to
look at".

The dump holds its targets one extra frame on purpose. Nothing is on the
GPU when `end()` returns — it records passes and `sg.commit()` runs later
in the frame callback — so reading there returns uninitialised memory.
The first version did, and every target came back a uniform magenta.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015nVTZMZW74ebw9bzNPcu2w
2026-09-10 16:44:26 +02:00
src backend: read a render target back, and a way to dump one 2026-09-10 16:44:26 +02:00
.gitignore Initial commit: shared sokol backend for DVUI 2026-05-06 15:33:36 +02:00
build.zig backend: read a render target back, and a way to dump one 2026-09-10 16:44:26 +02:00
build.zig.zon platform: expose consumeReopenRequest, and track sokol/sokey 2026-08-06 10:19:34 +02:00
LICENSE Add MIT license 2026-07-25 12:00:17 +02:00
README.md docs: repoint links to git.urverk.org 2026-07-25 11:41:27 +02:00

dvui-sokol

Shared sokol backend for DVUI, used by cue, quote, and wordandkey.

Modules

  • dvui_sokol — the DVUI backend itself (kind = .custom).
  • runtime — small std-shim helpers (clock, getenv, fs, sleep, mutex) for code that does not want to thread std.Io through every call.
  • shader — generated GLSL → backend-specific shader bytecode (compiled by sokol-shdc at build time).

Consuming this package

In your build.zig.zon, declare both this package and dvui (matching the pinned version below — Zig's package manager deduplicates by hash):

.dependencies = .{
    .dvui_sokol = .{
        .url = "git+https://git.urverk.org/urverk/dvui-sokol#<rev>",
        .hash = "...",
    },
    .dvui = .{
        .url = "git+https://github.com/david-vanderson/dvui#2459d676a26c11627593aad5e9ad4cbda42e2f1e",
        .hash = "dvui-0.5.0-dev-AQFJmT4I3wAC_MQuAnjx7tZe7iRao6cN7E7vcdOlItYa",
    },
},

In build.zig:

const ds_dep = b.dependency("dvui_sokol", .{ .target = target, .optimize = optimize });
const backend_mod = ds_dep.module("dvui_sokol");
const runtime_mod = ds_dep.module("runtime");

const dvui_dep = b.dependency("dvui", .{
    .target = target,
    .optimize = optimize,
    .backend = .custom,
    .libc = true,
    .@"stb-image" = true,
});
const dvui_mod = dvui_dep.module("dvui");

@import("dvui").linkBackend(dvui_mod, backend_mod);

// Use dvui_mod and runtime_mod from your app modules.

In your main.zig:

const dvui = @import("dvui");
const sokol = @import("sokol");
const dvui_sokol = @import("dvui_sokol");

pub const dvui_app: dvui.App = .{
    .config = .{ .startFn = &startOptions },
    .frameFn = frame,
    .initFn = init,
    .deinitFn = deinit,
};
pub const main = dvui.App.main;

File-open handling (macOS)

The backend wires three file-delivery paths into a single per-frame drain so consumers don't have to think about which event a file arrived on:

  • Window drag-and-drop — sokol's FILES_DROPPED event.
  • Dock-icon drops / "Open With" — macOS's application:openFiles: delegate method (legacy).
  • Terminal open -a App -- file and modern open flows — macOS's application:openURLs: delegate method.

The two delegate methods are installed at dyld static-init phase by a tiny companion src/load_hook.m (a DvuiSokolDelegateGraft class with a +load method). This timing is mandatory: macOS dispatches the launch-time kAEOpenDocuments Apple Event before applicationDidFinishLaunching: runs, so any later injection loses the race and AppKit shows its "cannot open files in the X format" fallback dialog. +load is the earliest hook the Obj-C runtime exposes, and that's how this implementation wins.

In your frame loop, call drainDroppedFiles once per frame:

for (dvui_sokol.drainDroppedFiles()) |path| {
    try editor.openFile(path);
}

Returned slices live in static buffers — valid until the next event of the same type replaces them, which in practice means valid for the rest of the current frame. Copy if you need to keep them.

CFBundleDocumentTypes in your Info.plist is not required — open -a App is explicit about the target. (It is still useful if you want Finder's "Open With…" menu to list your app, but it's orthogonal to whether files actually open.)

Other macOS extras

  • Press-and-hold suppression — macOS's accent-picker popup is disabled at startup so holding a printable key delivers repeated keyDown events (what text editors want). Same flag Terminal.app, VS Code, and Sublime set.
  • Glass / blur compositing — opt-in via the -Dglass=true build option. Off by default; consumers that don't render frosted popups pay zero overhead.

License

MIT