From e11048c20a50b67841e648a4d5ddd7490bc9c9b7 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Mon, 28 Sep 2026 13:24:06 -0300 Subject: Paint into a target Surface instead of swapping the frame's out The painters of a pane's body and tag (renderBody and its selection, gutter, syntax, ANSI and wrap-marker passes; paintPaneTag; paintHeaders; renderPane; the image and PDF grid paths) take the Surface they paint as a parameter. The body layer and the tag layers paint straight into their own temporary surfaces, and the std.mem.swap of p.surface around those calls is gone. No behaviour change. --- src/body_layer.zig | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) (limited to 'src/body_layer.zig') diff --git a/src/body_layer.zig b/src/body_layer.zig index aa52957c..db4d9524 100644 --- a/src/body_layer.zig +++ b/src/body_layer.zig @@ -145,7 +145,7 @@ test "compact context borders separate source gaps and preserve multiline header // ---- drawing a pane body into the surface ---- -fn paintTerminalSelection(p: *Pardes, pane: *Pane, r: Rect, rows: []const Pane.PointerRow, bg: [3]u8, fg: ?[3]u8) void { +fn paintTerminalSelection(p: *Pardes, s: *Surface, pane: *Pane, r: Rect, rows: []const Pane.PointerRow, bg: [3]u8, fg: ?[3]u8) void { const body = bodyText(p, p.scratch.allocator(), pane) catch return; const raw = pane.body.mode == .tty; const tx = r.x + config.GUTTER; @@ -178,7 +178,7 @@ fn paintTerminalSelection(p: *Pardes, pane: *Pane, r: Rect, rows: []const Pane.P var col = panes.File.rawDisplayCol(line, lo); const end = panes.File.rawDisplayCol(line, hi); while (col < end and col < width) : (col += 1) { - const cell = p.surface.at(tx + @as(u16, @intCast(col)), body_y + @as(u16, @intCast(visible))); + const cell = s.at(tx + @as(u16, @intCast(col)), body_y + @as(u16, @intCast(visible))); cell.default = false; cell.style.bg = .{ .rgb = bg }; if (fg) |ink| cell.style.fg = .{ .rgb = ink }; @@ -187,8 +187,8 @@ fn paintTerminalSelection(p: *Pardes, pane: *Pane, r: Rect, rows: []const Pane.P } } -fn paintSourceSelection(p: *Pardes, pane: *Pane, r: Rect, rows: []const Pane.PointerRow, bg: [3]u8, fg: ?[3]u8) void { - if (pane.isTerminal()) return paintTerminalSelection(p, pane, r, rows, bg, fg); +fn paintSourceSelection(p: *Pardes, s: *Surface, pane: *Pane, r: Rect, rows: []const Pane.PointerRow, bg: [3]u8, fg: ?[3]u8) void { + if (pane.isTerminal()) return paintTerminalSelection(p, s, pane, r, rows, bg, fg); const terminal_lines = if (pane.file == null) panes.Terminal.cursorLines(p, pane) catch return else &.{}; const tx = r.x + config.GUTTER; const width = r.w -| config.GUTTER; @@ -209,7 +209,7 @@ fn paintSourceSelection(p: *Pardes, pane: *Pane, r: Rect, rows: []const Pane.Poi var col = prefix + @as(i32, @intCast(panes.File.rawDisplayCol(line[start..], lo - start))); const last = prefix + @as(i32, @intCast(panes.File.rawDisplayCol(line[start..], hi - start))); while (col < last and col < width) : (col += 1) { - const cell = p.surface.at(tx + @as(u16, @intCast(col)), body_y + @as(u16, @intCast(visible))); + const cell = s.at(tx + @as(u16, @intCast(col)), body_y + @as(u16, @intCast(visible))); cell.default = false; cell.style.bg = .{ .rgb = bg }; if (fg) |ink| cell.style.fg = .{ .rgb = ink }; @@ -220,6 +220,7 @@ fn paintSourceSelection(p: *Pardes, pane: *Pane, r: Rect, rows: []const Pane.Poi pub fn paintPointerSelection( p: *Pardes, + s: *Surface, pane: *Pane, r: Rect, tx: u16, @@ -230,8 +231,7 @@ pub fn paintPointerSelection( bg: [3]u8, fg: ?[3]u8, ) void { - if (pane.pointerSelection(sl)) |rows| return paintSourceSelection(p, pane, r, rows, bg, fg); - const s = &p.surface; + if (pane.pointerSelection(sl)) |rows| return paintSourceSelection(p, s, pane, r, rows, bg, fg); const r0 = @min(sl.r0, sl.r1); const r1 = @max(sl.r0, sl.r1); const c0 = @max(0, @min(sl.c0, sl.c1)); @@ -278,13 +278,8 @@ pub fn renderBodyLayer(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rec const temporary_cells = try arena.alloc(Cell, @as(usize, r.w) * (logical_rows + pane.tag_rows)); @memset(temporary_cells, .{}); var temporary: Surface = .{ .cols = r.w, .rows = @intCast(logical_rows + pane.tag_rows), .cells = temporary_cells }; - std.mem.swap(Surface, &p.surface, &temporary); const synthetic: Rect = .{ .x = 0, .y = 0, .w = r.w, .h = @intCast(logical_rows + pane.tag_rows) }; - renderBody(p, arena, pane, synthetic, id, id == p.active, false) catch |err| { - std.mem.swap(Surface, &p.surface, &temporary); - return err; - }; - std.mem.swap(Surface, &p.surface, &temporary); + try renderBody(p, &temporary, arena, pane, synthetic, id, id == p.active, false); const first_row: usize = if (p.settings.tag_bottom) 0 else pane.tag_rows; for (0..logical_rows) |row| { const start = (row + first_row) * r.w + config.GUTTER; @@ -304,8 +299,7 @@ pub fn renderBodyLayer(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rec } } -pub fn renderBody(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect, id: usize, active: bool, scrollbar: bool) !void { - const s = &p.surface; +pub fn renderBody(p: *Pardes, s: *Surface, arena: std.mem.Allocator, pane: *Pane, r: Rect, id: usize, active: bool, scrollbar: bool) !void { const th = p.theme(); const chrome = p.chromeTheme(); const tx = r.x + config.GUTTER; @@ -343,12 +337,12 @@ pub fn renderBody(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect, id const tz_color = tracy.zone(@src(), "paneRecolor"); // File layout is independent of whether its producer requests colors. if (pane.file) |*f| { - panes.File.drawGutter(p, pane, r, tx, tw, body_h, active); + panes.File.drawGutter(p, s, pane, r, tx, tw, body_h, active); if (p.settings.colors and pane.colorAlgo() != .none) - panes.File.recolorSyntax(p, pane, f, r, tx, tw, body_h); - panes.File.drawWrapMarkers(p, pane, r, tx, tw, body_h, pane_bg); + panes.File.recolorSyntax(p, s, pane, f, r, tx, tw, body_h); + panes.File.drawWrapMarkers(p, s, pane, r, tx, tw, body_h, pane_bg); } else if (pane.isTerminal() and p.settings.colors) { - panes.Terminal.recolorAnsi(p, pane, r, tx, tw, body_h, body); + panes.Terminal.recolorAnsi(p, s, pane, r, tx, tw, body_h, body); } tz_color.end(); @@ -366,9 +360,9 @@ pub fn renderBody(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect, id // macOS composes its glass overlay over exactly these. const preview_bg = colors.mix(page_rgb, colors.mix(page_rgb, th.sel_bg)); if (preview.file_word) |word| - panes.File.paintWordSelection(p, pane, r, word.row, word.lo, word.hi, preview_bg) + panes.File.paintWordSelection(p, s, pane, r, word.row, word.lo, word.hi, preview_bg) else if (preview.sel) |sel| - paintPointerSelection(p, pane, r, tx, tw, tag_y, body_y, sel, preview_bg, null); + paintPointerSelection(p, s, pane, r, tx, tw, tag_y, body_y, sel, preview_bg, null); } } const modal_hover = if (p.look_hover_preview) |preview| @@ -381,7 +375,7 @@ pub fn renderBody(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect, id // A select sweep over the tag is the tag's selection, painted there // in the tag's own selection colours. if (b == pardes.sel_slot and @min(sl.r0, sl.r1) < pane.tag_rows) continue; - paintPointerSelection(p, pane, r, tx, tw, tag_y, body_y, sl, sel_btn[b], th.sel_fg); + paintPointerSelection(p, s, pane, r, tx, tw, tag_y, body_y, sl, sel_btn[b], th.sel_fg); } // modal line selection (helix `x`): whole body rows, normal mode only if ((pane.body.mode == .normal or modal_hover) and pane.body.msel.active) { -- cgit v1.3