summaryrefslogtreecommitdiff
path: root/build.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-07-02 20:06:25 -0300
committerGabriel Schneider <[email protected]>2026-08-01 15:02:07 -0300
commitbead9f0b78380d1428f40fdab339c1c8f88fa626 (patch)
tree960255de5a2cef661ec5d0ad3220a4b2b1738842 /build.zig
parent62ef1882ab7878210f05f06504796849e412c318 (diff)
downloadpardes-bead9f0b78380d1428f40fdab339c1c8f88fa626.tar.gz
pardes-bead9f0b78380d1428f40fdab339c1c8f88fa626.zip
sdl ui backend
Diffstat (limited to 'build.zig')
-rw-r--r--build.zig81
1 files changed, 81 insertions, 0 deletions
diff --git a/build.zig b/build.zig
index 34450fa9..ca8560fd 100644
--- a/build.zig
+++ b/build.zig
@@ -16,6 +16,12 @@ pub fn build(b: *std.Build) void {
const static_link = b.option(bool, "static", "link all build executables and test binaries statically") orelse false;
const exe_linkage: ?std.builtin.LinkMode = if (static_link) .static else null;
const strip = b.option(bool, "strip", "strip debug info from the compiled pardes binary") orelse false;
+ // -Dsdl: build the SDL3 GPU UI frontend (a windowed cell-grid renderer)
+ // instead of the default libvaxis terminal backend. The sdl/stb deps are
+ // lazy, so a plain `zig build` never fetches or compiles them; they're only
+ // pulled in when this flag is set. Use `zig build test-sdl -Dsdl` for the
+ // rendered-image SDL e2e suite.
+ const sdl = b.option(bool, "sdl", "build the SDL3 GPU UI frontend instead of the terminal backend") orelse false;
const exe = b.addExecutable(.{
.name = "pardes",
@@ -93,6 +99,50 @@ pub fn build(b: *std.Build) void {
vaxis_mod.addImport("uucode", uucode_mod);
exe.root_module.addImport("vaxis", vaxis_mod);
+ // The UI backend module (`-Dui`). main.zig + image.zig import `"ui"` instead of
+ // `"vaxis"` directly; the chosen backend file re-exports the shared UI types
+ // (Key/Mouse/Window/Cell/…) and provides the renderer/loop (`Vaxis`/`Tty`/
+ // `Loop`/`init`). vaxis = transparent passthrough; sdl = SDL3 GPU frontend.
+ const ui_root = if (sdl) b.path("ui_sdl.zig") else b.path("ui_vaxis.zig");
+ const ui_mod = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ .root_source_file = ui_root,
+ });
+ ui_mod.addImport("vaxis", vaxis_mod);
+ exe.root_module.addImport("ui", ui_mod);
+
+ // SDL3 GPU backend wiring (`-Dsdl`). The sdl package builds SDL3 from source
+ // as a static lib + emits the SDL3/ headers; we @cImport those headers in
+ // ui_sdl.zig, link the lib, and pull in stb_truetype (for the glyph atlas) and
+ // the embedded DejaVuSansMono font. All gated on the flag so a plain `zig build`
+ // (vaxis) never fetches or compiles SDL.
+ if (sdl) {
+ const sdl_dep = b.lazyDependency("sdl", .{
+ .target = target,
+ .optimize = optimize,
+ }) orelse return; // not fetched (offline?) — skip SDL wiring
+ const sdl_lib = sdl_dep.artifact("SDL3");
+ ui_mod.linkLibrary(sdl_lib);
+ ui_mod.addIncludePath(sdl_dep.path("include"));
+ ui_mod.link_libc = true;
+
+ // stb_truetype: the header (declarations, via include path) + the impl TU
+ // + a small C shim (font.c) so ui_sdl.zig doesn't feed the whole
+ // single-header lib to translate-c.
+ ui_mod.addIncludePath(b.path("vendor/stb"));
+ ui_mod.addCSourceFile(.{ .file = b.path("vendor/stb/stb_truetype_impl.c") });
+ ui_mod.addCSourceFile(.{ .file = b.path("vendor/stb/font.c") });
+
+ // GLSL -> SPIR-V at build time, embedded into ui_sdl.zig via @embedFile.
+ ui_mod.addAnonymousImport("ui.vert.spv", .{
+ .root_source_file = compileGlsl(b, "shaders/ui.vert.glsl", "vertex", "ui.vert.spv"),
+ });
+ ui_mod.addAnonymousImport("ui.frag.spv", .{
+ .root_source_file = compileGlsl(b, "shaders/ui.frag.glsl", "fragment", "ui.frag.spv"),
+ });
+ }
+
// zstbi: C (stb_image)-backed image decoder for the image-viewer panes. We use
// it instead of zigimg because zigimg's pure-Zig decoder crashes the Zig 0.16
// compiler here; stb_image is plain C (extern fns), so it compiles fine, decodes
@@ -201,6 +251,28 @@ pub fn build(b: *std.Build) void {
const test_step = b.step("test", "run deterministic tests");
test_step.dependOn(&run_tests.step);
+ // `test-sdl`: e2e tests for the SDL3 GPU backend. The SDL pardes exe runs in
+ // test mode (PARDES_TEST=1): stdin drives app events through a pty, while the
+ // real GPU renderer writes the latest framebuffer capture to a temp PPM file.
+ // Requires `-Dsdl` so the SDL exe is the one under test.
+ const sdl_tests = b.addExecutable(.{
+ .name = "pardes-e2e-sdl",
+ .linkage = exe_linkage,
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ .root_source_file = b.path("tests_sdl.zig"),
+ .link_libc = true,
+ }),
+ });
+ const run_sdl_tests = b.addRunArtifact(sdl_tests);
+ run_sdl_tests.addArtifactArg(exe);
+ const sdl_test_opts = b.addOptions();
+ sdl_test_opts.addOption(bool, "trace", trace);
+ sdl_tests.root_module.addOptions("config", sdl_test_opts);
+ const test_sdl_step = b.step("test-sdl", "run SDL3 GPU e2e tests (requires -Dsdl)");
+ test_sdl_step.dependOn(&run_sdl_tests.step);
+
// Unit tests for the pure modal-editing math (modal.zig has no vaxis/ghostty
// deps, so `zig test` on it is fast and needs no wired-in modules).
const unit_tests = b.addTest(.{
@@ -254,3 +326,12 @@ pub fn build(b: *std.Build) void {
tutor_step.dependOn(&run_tutor.step);
test_step.dependOn(&run_tutor.step);
}
+
+// glslc <src> -fshader-stage=<stage> -o <out> -> .spv, returned as a LazyPath to
+// @embedFile. Only used by the SDL3 GPU UI backend (`-Dui=sdl`).
+fn compileGlsl(b: *std.Build, src: []const u8, stage: []const u8, out: []const u8) std.Build.LazyPath {
+ const cmd = b.addSystemCommand(&.{ "glslc", b.fmt("-fshader-stage={s}", .{stage}) });
+ cmd.addFileArg(b.path(src));
+ cmd.addArg("-o");
+ return cmd.addOutputFileArg(out);
+}