diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-11 16:26:20 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-11 16:26:20 -0300 |
| commit | 24aff2afe3cc827fe1a920a044922aeacd1ff6a3 (patch) | |
| tree | 2f82c17f196b0fbb1a89876ecc40347b7ddfebed /src | |
| parent | 4ca28745d774c232cd31a29c17878f19bbe24cf5 (diff) | |
| download | pardes-24aff2afe3cc827fe1a920a044922aeacd1ff6a3.tar.gz pardes-24aff2afe3cc827fe1a920a044922aeacd1ff6a3.zip | |
gui: the ground is the theme's own background, not a hand-agreed #121212
The SDL shell painted 18,18,18 behind the grid and inside every cell the core
left at its default background, whatever the theme was wearing. Under a light
theme that is a black line along the bottom and right edges of the window --
the strip left over when the window is not a whole number of cells -- measured
two pixels tall with acme at 1728x2102.
The ground is now core.theme().bg, read once a frame, the way PardesView reads
pardes_theme_bg: a Theme command takes hold without a relaunch, and it is the
theme's OWN background rather than the animated chrome colour, because
document backgrounds switch the instant the theme does.
The other half of the macOS fix does not port. A theme that declares no
background (the curated dark, every vendored *_transparent) goes see-through
over an NSVisualEffectView there; here it keeps the terminal-native dark,
because SDL's GPU API refuses to claim a SDL_WINDOW_TRANSPARENT window at all
-- 'The GPU API doesn't support transparent windows', SDL_gpu.c, since D3D12
has no transparent swapchain. Tried it: the shell fails at
ClaimWindowForGPUDevice and exits. bg_default now says so where the next
person will look for it.
Verified on a real window under niri: with Theme acme the bottom strip is
#ffffea where it was #121212.
Diffstat (limited to 'src')
| -rw-r--r-- | src/gui/gui.zig | 65 |
1 files changed, 47 insertions, 18 deletions
diff --git a/src/gui/gui.zig b/src/gui/gui.zig index c21ad069..7e34a928 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -68,10 +68,35 @@ const overlay_frag_glsl_es = if (is_emscripten) @embedFile("overlay.frag.es.glsl // web: no ptys — the whole state replays from a dump embedded at build time const embedded_dump = if (is_emscripten) @embedFile("embedded.dump.zon") else ""; -// default page colors — the same "terminal native dark" the prototype used +/// What the shell paints where the core painted nothing: behind the grid — the +/// strip left over when the window is not a whole number of cells tall — and +/// inside every cell the core left at its default background. The FALLBACK +/// for it, that is: the ground itself is the active theme's own background, +/// read off the core once a frame (see `ground`), and this is what a theme +/// with none of its own gets. +/// +/// A theme declares no background (the curated `dark`, every vendored +/// `*_transparent`) to mean "wear whatever the terminal is wearing", and a +/// window has nothing to wear. The AppKit shell answers that by going +/// see-through over an NSVisualEffectView — pardes_theme_bg, docs/macos.md — +/// and this shell cannot follow it there: SDL's GPU API refuses to claim a +/// SDL_WINDOW_TRANSPARENT window at all ("The GPU API doesn't support +/// transparent windows", SDL_gpu.c), because D3D12 has no transparent +/// swapchain and the API says no everywhere rather than only where it must. +/// So a themeless window keeps the terminal-native dark it always wore. const bg_default = [3]u8{ 18, 18, 18 }; const fg_default = [3]u8{ 204, 204, 204 }; +/// The ground under this frame. The theme's OWN background and not the +/// animated chrome colour: taglines fade between themes over a handful of +/// frames, document backgrounds switch the instant the theme does, and this is +/// one of those. Asked per frame, so a `Theme` command takes hold without a +/// relaunch — and asked at all because a hand-agreed constant was a black line +/// along the two edges of every light-themed window. +fn ground(theme_bg: ?[3]u8) [3]u8 { + return theme_bg orelse bg_default; +} + // the plan9 arrow cursor, bytes verbatim from 9front /sys/src/9/port/ // devmouse.c (Cursor arrow, 16x16 MSB-first): clr is the white outline, set // the black ink, offset {-1,-1} puts the hot point at (1,1) in the bitmap. @@ -1656,7 +1681,7 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options) !void { core.update(.tick); _ = frame_arena.reset(.retain_capacity); const surface = try core.render(frame_arena.allocator()); - renderFrame(&g, gpa, surface, core.crt_on, core.show_debug) catch |err| log.err("render: {t}", .{err}); + renderFrame(&g, gpa, surface, core.theme().bg, core.crt_on, core.show_debug) catch |err| log.err("render: {t}", .{err}); } } @@ -1965,7 +1990,7 @@ fn webFrame(arg: ?*anyopaque) callconv(.c) void { core.update(.tick); _ = st.frame_arena.reset(.retain_capacity); const surface = core.render(st.frame_arena.allocator()) catch return; - renderFrameGl(g, st.gpa, surface, core.show_debug) catch return; + renderFrameGl(g, st.gpa, surface, core.theme().bg, core.show_debug) catch return; } /// the canvas follows the page (CSS); the SDL window follows the canvas, @@ -3210,7 +3235,7 @@ fn stepScroll(g: *Gui, core: *pardes.Pardes, gpa: std.mem.Allocator) void { /// to the body, which is the whole of the clipping — the shell draws the grid /// in one flat pass, so without it the overhanging rows would land on the /// pane's own tag and on whatever is below it. -fn emitScrollRows(g: *Gui, instances: [*]CellInstance, base: u32, surface: *pardes.Surface, layout: CellLayout, win_w: f32, win_h: f32) u32 { +fn emitScrollRows(g: *Gui, instances: [*]CellInstance, base: u32, surface: *pardes.Surface, layout: CellLayout, win_w: f32, win_h: f32, page: [3]u8) u32 { if (g.scroll_pane == null or g.scroll_lag == 0) return 0; const r = g.scroll_rect; if (r.w <= config.GUTTER or r.h <= pardes.BOX_H) return 0; @@ -3230,7 +3255,7 @@ fn emitScrollRows(g: *Gui, instances: [*]CellInstance, base: u32, surface: *pard var col = x0; while (col < x0 + bw) : (col += 1) { const sidx: u32 = @as(u32, row) * surface.cols + col; - emitInstance(g, instances, base + n, col, row, shifted, win_w, win_h, surface.at(col, row), sidx == cursor_idx and !cursor_bar); + emitInstance(g, instances, base + n, col, row, shifted, win_w, win_h, surface.at(col, row), sidx == cursor_idx and !cursor_bar, page); n += 1; } } @@ -3240,7 +3265,7 @@ fn emitScrollRows(g: *Gui, instances: [*]CellInstance, base: u32, surface: *pard const erow: u16 = if (g.scroll_lag > 0) y0 + bh else y0 - 1; var i: u16 = 0; while (i < bw) : (i += 1) { - emitInstance(g, instances, base + n, x0 + i, erow, shifted, win_w, win_h, &g.scroll_edge[i], false); + emitInstance(g, instances, base + n, x0 + i, erow, shifted, win_w, win_h, &g.scroll_edge[i], false, page); n += 1; } } @@ -3545,7 +3570,7 @@ fn drawNativeImagesGpu(g: *Gui, rp: ?*c.SDL_GPURenderPass, surface: *pardes.Surf c.SDL_SetGPUScissor(pass, &whole); } -fn renderFrame(g: *Gui, gpa: std.mem.Allocator, surface: *pardes.Surface, crt_on: bool, debug_on: bool) !void { +fn renderFrame(g: *Gui, gpa: std.mem.Allocator, surface: *pardes.Surface, theme_bg: ?[3]u8, crt_on: bool, debug_on: bool) !void { const cmd = c.SDL_AcquireGPUCommandBuffer(g.device) orelse return; var sw: u32 = 0; var sh: u32 = 0; @@ -3592,12 +3617,13 @@ fn renderFrame(g: *Gui, gpa: std.mem.Allocator, surface: *pardes.Surface, crt_on // re-creating the buffer on every frame that starts or ends a slide. if (cells != 0) try ensureVbuf(g, cells * 2); + const page = ground(theme_bg); var color_target = std.mem.zeroes(c.SDL_GPUColorTargetInfo); color_target.texture = scene; color_target.clear_color = .{ - .r = @as(f32, @floatFromInt(bg_default[0])) / 255.0, - .g = @as(f32, @floatFromInt(bg_default[1])) / 255.0, - .b = @as(f32, @floatFromInt(bg_default[2])) / 255.0, + .r = @as(f32, @floatFromInt(page[0])) / 255.0, + .g = @as(f32, @floatFromInt(page[1])) / 255.0, + .b = @as(f32, @floatFromInt(page[2])) / 255.0, .a = 1.0, }; color_target.load_op = c.SDL_GPU_LOADOP_CLEAR; @@ -3618,10 +3644,10 @@ fn renderFrame(g: *Gui, gpa: std.mem.Allocator, surface: *pardes.Surface, crt_on var col: u16 = 0; while (col < surface.cols) : (col += 1) { const idx: u32 = @as(u32, row) * surface.cols + col; - emitInstance(g, instances, idx, col, row, layout, win_w, win_h, surface.at(col, row), idx == cursor_idx and !cursor_bar); + emitInstance(g, instances, idx, col, row, layout, win_w, win_h, surface.at(col, row), idx == cursor_idx and !cursor_bar, page); } } - const shifted = emitScrollRows(g, instances, cells, surface, layout, win_w, win_h); + const shifted = emitScrollRows(g, instances, cells, surface, layout, win_w, win_h, page); c.SDL_UnmapGPUTransferBuffer(g.device, g.vxfer.?); // emitInstance may have rasterized new glyphs into the staging atlas; @@ -3698,12 +3724,14 @@ fn emitInstance( win_h: f32, cell: *const pardes.Cell, is_cursor: bool, + /// the ground this frame: what a default background resolves to + page: [3]u8, ) void { // Resolve the cell style to concrete fg/bg. A normal block cursor reverses // the cell; the insert cursor is a separate overlay, so the glyph beneath // it remains in this base pass. var fg = fg_default; - var bg = bg_default; + var bg = page; var reverse = is_cursor; if (!cell.default) { const st = cell.style; @@ -3713,7 +3741,7 @@ fn emitInstance( .rgb => |rgb| rgb, }; bg = switch (st.bg) { - .default => bg_default, + .default => page, .index => |i| palColor(i), .rgb => |rgb| rgb, }; @@ -4065,17 +4093,18 @@ fn makeShader(device: *c.SDL_GPUDevice, code: []const u8, stage: c.SDL_GPUShader // re-upload each frame; attribs re-specified per draw (no VAO to manage). // ===================================================================== -fn renderFrameGl(g: *Gui, gpa: std.mem.Allocator, surface: *pardes.Surface, debug_on: bool) !void { +fn renderFrameGl(g: *Gui, gpa: std.mem.Allocator, surface: *pardes.Surface, theme_bg: ?[3]u8, debug_on: bool) !void { var pw: c_int = 0; var ph: c_int = 0; _ = c.SDL_GetWindowSizeInPixels(g.window, &pw, &ph); const sw: u32 = @intCast(@max(pw, 1)); const sh: u32 = @intCast(@max(ph, 1)); c.glViewport(0, 0, @intCast(sw), @intCast(sh)); + const page = ground(theme_bg); c.glClearColor( - @as(f32, @floatFromInt(bg_default[0])) / 255.0, - @as(f32, @floatFromInt(bg_default[1])) / 255.0, - @as(f32, @floatFromInt(bg_default[2])) / 255.0, + @as(f32, @floatFromInt(page[0])) / 255.0, + @as(f32, @floatFromInt(page[1])) / 255.0, + @as(f32, @floatFromInt(page[2])) / 255.0, 1.0, ); c.glClear(c.GL_COLOR_BUFFER_BIT); |
