diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-28 14:59:21 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:15 -0300 |
| commit | 360b72ade0acfa655a3740b638a0cc8e23238a05 (patch) | |
| tree | 4bc9464da2cdcfd6fe636979f0b457221ec92c3d /src/detached | |
| parent | d88e381793c6505bf3f2af43705bd2fa32f55ef6 (diff) | |
| download | pardes-360b72ade0acfa655a3740b638a0cc8e23238a05.tar.gz pardes-360b72ade0acfa655a3740b638a0cc8e23238a05.zip | |
The GUI draws in tiers from the regions; pane chrome moves with its pane
Stage 8 of docs/render-pipeline.md. The frame is drawn in groups: tier 0,
one per track in paint order (tiers 2 and 3), tier 4 (notices, then guides
and the debug box), tier 5 (bar cursors), each cells, images, decor. Decor
(rules, rails, thumbs, grip marks, spines, the workspace and column rules,
the bottom band, notice rules, bar cursors) is whole-pixel rects through
the new decor.frag over ui.vert, so it carries its track's transition and
clip. A closing pane's chrome comes from Surface.previous_regions.
Deleted: taglineBaseRgb, topbarPaneBorderHeight, bottomTaglinePresent,
frameChromeBg, cellBackgroundIs and the rail inference, paneGripCell's
scan, transient_on, PaintPlan. One cover map (coverFrame) is marked from
the layers and regions once a frame. New regions column, guide and debug;
Surface.chrome is the palette, carried in wire v8 (not shipped yet), so an
attached GUI draws the same chrome (test). A per-instance clip replaces the
vertical transition's scissor.
Goldens: 01-12 byte-identical. 13-17 differ only past the grid: the image
pass left its scissor at the grid's size, cutting rule ends, rail feet and
the bottom band in the leftover pixels whenever a picture was on screen.
16-debug now shows the debug box, which a context-row layer had hidden.
18-mid-transition is new: virtual clock (PARDES_TEST_CLOCK in the GUI),
PanelSlide Newcol with the picture, frame 6 of 12.
Also: the GUI sleeps when idle instead of polling every 16 ms, and a
minimized or occluded window sleeps through animation. Tracy 'gui frame
build' at 200x60: 992/1015 us median before, 989/987 us after.
Shared files touched: pardes.zig (one export), detached/client.zig,
detached/server.zig, detached/wire.zig (all additive), gui.zig. Not touched:
Messages.zig, mouse.zig, tagline.zig, colors.zig, tty.zig, host_io.zig,
dump.zig, exec.zig, panes.zig.
Diffstat (limited to 'src/detached')
| -rw-r--r-- | src/detached/client.zig | 4 | ||||
| -rw-r--r-- | src/detached/server.zig | 1 | ||||
| -rw-r--r-- | src/detached/wire.zig | 41 |
3 files changed, 37 insertions, 9 deletions
diff --git a/src/detached/client.zig b/src/detached/client.zig index 3347f609..9fa85fb4 100644 --- a/src/detached/client.zig +++ b/src/detached/client.zig @@ -107,6 +107,8 @@ pub const Client = struct { tag_layers: [pardes.MAX_TAG_LAYERS]pardes.Layer = @splat(.{}), /// The session's placed regions, as its last frame gave them. regions: std.ArrayListUnmanaged(pardes.Region) = .empty, + /// The palette its chrome is drawn in, as its last frame gave it. + chrome: pardes.Chrome = .{}, in: std.ArrayListUnmanaged(u8) = .empty, out: std.ArrayListUnmanaged(u8) = .empty, /// Bytes of `in` belonging to the message `next` returned last. Compacted at @@ -322,7 +324,7 @@ pub const Client = struct { try c.reshape(f.cols, f.rows); } try f.apply(c.grid.items); - f.applyLayers(c.gpa, &c.body_layers, &c.tag_layers, &c.regions) catch |err| return switch (err) { + f.applyLayers(c.gpa, &c.body_layers, &c.tag_layers, &c.regions, &c.chrome) catch |err| return switch (err) { error.OutOfMemory => error.Closed, else => @as(wire.Error, @errorCast(err)), }; diff --git a/src/detached/server.zig b/src/detached/server.zig index 46fb8fcf..9e18e125 100644 --- a/src/detached/server.zig +++ b/src/detached/server.zig @@ -760,6 +760,7 @@ pub const Session = struct { &surface.body_layers, &surface.tag_layers, surface.regionList(), + surface.chrome, ) catch |err| { log.debug("frame {d}x{d} not encodable: {t}", .{ surface.cols, surface.rows, err }); return; diff --git a/src/detached/wire.zig b/src/detached/wire.zig index 684c869b..32cd5229 100644 --- a/src/detached/wire.zig +++ b/src/detached/wire.zig @@ -169,8 +169,9 @@ pub const Frame = struct { nruns: u32, runs: []const u8, - pub fn applyLayers(f: Frame, gpa: std.mem.Allocator, layers: *[pardes.MAX_PANES]pardes.Layer, tags: *[pardes.MAX_TAG_LAYERS]pardes.Layer, regions: *std.ArrayListUnmanaged(pardes.Region)) !void { + pub fn applyLayers(f: Frame, gpa: std.mem.Allocator, layers: *[pardes.MAX_PANES]pardes.Layer, tags: *[pardes.MAX_TAG_LAYERS]pardes.Layer, regions: *std.ArrayListUnmanaged(pardes.Region), chrome: *pardes.Chrome) !void { regions.clearRetainingCapacity(); + chrome.* = .{}; for (layers) |*layer| { gpa.free(layer.cells); layer.* = .{}; @@ -188,6 +189,8 @@ pub const Frame = struct { gpa.free(layer.cells); layer.* = .{}; } + regions.clearRetainingCapacity(); + chrome.* = .{}; } if (f.layers.len == 0) return; var r: Reader = .init(f.layers); @@ -255,6 +258,15 @@ pub const Frame = struct { region.clip > region.rect.w or region.thumb_y > region.rect.h or region.thumb_h > region.rect.h - region.thumb_y) return error.BadValue; regions.appendAssumeCapacity(region); } + inline for (@typeInfo(pardes.Chrome).@"struct".fields) |field| { + const rgb = &@field(chrome, field.name); + if (field.type == ?[3]u8) { + if (try r.getBool()) { + rgb.* = .{ 0, 0, 0 }; + for (&rgb.*.?) |*channel| channel.* = try r.getByte(); + } + } else for (rgb) |*channel| channel.* = try r.getByte(); + } try r.end(); } @@ -697,7 +709,7 @@ pub fn encodeFramePointer( cells: []const pardes.Cell, prev: []const pardes.Cell, ) Error![]const u8 { - return encodeFrameLayers(out, cols, rows, cursor, pointer_shape, cells, prev, &.{}, &.{}, &.{}); + return encodeFrameLayers(out, cols, rows, cursor, pointer_shape, cells, prev, &.{}, &.{}, &.{}, .{}); } pub fn layersBound(layers: []const pardes.Layer, tags: []const pardes.Layer, regions: []const pardes.Region) usize { @@ -708,12 +720,15 @@ pub fn layersBound(layers: []const pardes.Layer, tags: []const pardes.Layer, reg for (tags) |layer| if (layer.rows != 0) { n += 28 + layer.cells.len * cell_max; }; - return n + regions.len * region_len; + return n + regions.len * region_len + chrome_len; } const region_len = 1 + 1 + 4 + 8 + 1 + 2 + 2 + 1 + 2; +/// The palette: each colour three bytes, a theme's optional ones a flag +/// before them. +const chrome_len = @typeInfo(pardes.Chrome).@"struct".fields.len * 4; -pub fn encodeFrameLayers(out: []u8, cols: u16, rows: u16, cursor: ?Cursor, pointer_shape: pardes.Surface.PointerShape, cells: []const pardes.Cell, prev: []const pardes.Cell, layers: []const pardes.Layer, tags: []const pardes.Layer, regions: []const pardes.Region) Error![]const u8 { +pub fn encodeFrameLayers(out: []u8, cols: u16, rows: u16, cursor: ?Cursor, pointer_shape: pardes.Surface.PointerShape, cells: []const pardes.Cell, prev: []const pardes.Cell, layers: []const pardes.Layer, tags: []const pardes.Layer, regions: []const pardes.Region, chrome: pardes.Chrome) Error![]const u8 { // The protocol's ceiling, enforced by the ENCODER too, and BEFORE the // assert below so a caller can be told rather than tripped. `max_payload` // is derived from these two, so a larger grid is a frame this decoder @@ -793,6 +808,13 @@ pub fn encodeFrameLayers(out: []u8, cols: u16, rows: u16, cursor: ?Cursor, point try w.putByte(region.notice); try w.putU16(region.clip); } + inline for (@typeInfo(pardes.Chrome).@"struct".fields) |field| { + const rgb = @field(chrome, field.name); + if (field.type == ?[3]u8) { + try w.putBool(rgb != null); + if (rgb) |value| for (value) |channel| try w.putByte(channel); + } else for (rgb) |channel| try w.putByte(channel); + } } std.mem.writeInt(u32, w.buf[layers_len_at..][0..4], @intCast(w.n - layers_start), .little); const nruns_at = w.n; @@ -1871,7 +1893,8 @@ test "detached wire carries compact body rows and exact pointer metrics" { const placed = [_]pardes.Region{.{ .kind = .rail, .owner = 3, .serial = 42, .rect = .{ .x = 0, .y = 1, .w = 2, .h = 3 }, .active = true, .thumb_y = 1, .thumb_h = 2, .clip = 1 }}; const out = try gpa.alloc(u8, frameBound(2, 4) + layersBound(&.{layer}, &.{tag}, &placed)); defer gpa.free(out); - const encoded = try encodeFrameLayers(out, 2, 4, null, .link, &cells, &cells, &.{layer}, &.{tag}, &placed); + const palette: pardes.Chrome = .{ .page = .{ 1, 2, 3 }, .border = .{ 4, 5, 6 }, .tag_focus_bg = .{ 7, 8, 9 }, .scroll_thumb = .{ 10, 11, 12 } }; + const encoded = try encodeFrameLayers(out, 2, 4, null, .link, &cells, &cells, &.{layer}, &.{tag}, &placed, palette); const packet = (try framed(encoded)).?; const frame = (try decodeServer(packet.tag, packet.payload)).frame; var decoded: [pardes.MAX_PANES]pardes.Layer = @splat(.{}); @@ -1881,8 +1904,10 @@ test "detached wire carries compact body rows and exact pointer metrics" { try frame.apply(&cells); var regions: std.ArrayListUnmanaged(pardes.Region) = .empty; defer regions.deinit(gpa); - try frame.applyLayers(gpa, &decoded, &tags, ®ions); + var chrome: pardes.Chrome = .{}; + try frame.applyLayers(gpa, &decoded, &tags, ®ions, &chrome); try std.testing.expectEqualDeep(placed[0..], regions.items); + try std.testing.expectEqualDeep(palette, chrome); try std.testing.expectEqual(@as(u16, 6), decoded[0].rows); try std.testing.expect(decoded[0].context_separators.isSet(0)); try std.testing.expect(!decoded[0].context_separators.isSet(1)); @@ -1908,7 +1933,7 @@ test "detached wire carries compact body rows and exact pointer metrics" { try std.testing.expect(mouse_encoded.len <= clientBound(message)); var truncated = frame; truncated.layers = frame.layers[0 .. frame.layers.len - 1]; - try std.testing.expectError(error.Truncated, truncated.applyLayers(gpa, &decoded, &tags, ®ions)); + try std.testing.expectError(error.Truncated, truncated.applyLayers(gpa, &decoded, &tags, ®ions, &chrome)); for (decoded) |item| { try std.testing.expectEqual(@as(u16, 0), item.rows); try std.testing.expectEqual(@as(usize, 0), item.cells.len); @@ -1922,6 +1947,6 @@ test "detached wire carries compact body rows and exact pointer metrics" { const no_layers = try encodeFramePointer(out, 2, 4, null, .arrow, &cells, &cells); const cleared_packet = (try framed(no_layers)).?; const cleared = (try decodeServer(cleared_packet.tag, cleared_packet.payload)).frame; - try cleared.applyLayers(gpa, &decoded, &tags, ®ions); + try cleared.applyLayers(gpa, &decoded, &tags, ®ions, &chrome); try std.testing.expectEqual(@as(u16, 0), decoded[0].rows); } |
