summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gui/gui.zig65
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);