a73x

ae38f777

Render actual cell colors

a73x   2026-04-08 11:53

Commit message
Render actual cell colors

src/main.zig
Old New
@@ -198,13 +198,15 @@ fn runTerminal(alloc: std.mem.Allocator) !void {
198 const term_rows = term.render_state.row_data.items(.cells); 198 const term_rows = term.render_state.row_data.items(.cells);
199 var row_idx: u32 = 0; 199 var row_idx: u32 = 0;
200 while (row_idx < term_rows.len) : (row_idx += 1) { 200 while (row_idx < term_rows.len) : (row_idx += 1) {
201 const raw_cells = term_rows[row_idx].items(.raw); 201 const row_cells = term_rows[row_idx];
202 const raw_cells = row_cells.items(.raw);
202 var col_idx: u32 = 0; 203 var col_idx: u32 = 0;
203 while (col_idx < raw_cells.len) : (col_idx += 1) { 204 while (col_idx < raw_cells.len) : (col_idx += 1) {
204 const cp = raw_cells[col_idx].codepoint(); 205 const cp = raw_cells[col_idx].codepoint();
205 if (cp == 0 or cp == ' ') continue; 206 if (cp == 0 or cp == ' ') continue;
206 207
207 const uv = atlas.getOrInsert(&face, @intCast(cp)) catch continue; 208 const uv = atlas.getOrInsert(&face, @intCast(cp)) catch continue;
209 const colors = term.cellColors(row_cells.get(col_idx));
208 210
209 try instances.append(alloc, .{ 211 try instances.append(alloc, .{
210 .cell_pos = .{ @floatFromInt(col_idx), @floatFromInt(row_idx) }, 212 .cell_pos = .{ @floatFromInt(col_idx), @floatFromInt(row_idx) },
@@ -216,8 +218,8 @@ fn runTerminal(alloc: std.mem.Allocator) !void {
216 @as(f32, @floatFromInt(cell_h)) - @as(f32, @floatFromInt(uv.bearing_y)), 218 @as(f32, @floatFromInt(cell_h)) - @as(f32, @floatFromInt(uv.bearing_y)),
217 }, 219 },
218 .uv_rect = .{ uv.u0, uv.v0, uv.u1, uv.v1 }, 220 .uv_rect = .{ uv.u0, uv.v0, uv.u1, uv.v1 },
219 .fg = .{ 0.9, 0.9, 0.9, 1.0 }, 221 .fg = colors.fg,
220 .bg = .{ 0.08, 0.08, 0.08, 1.0 }, 222 .bg = colors.bg,
221 }); 223 });
222 } 224 }
223 } 225 }
src/vt.zig
Old New
@@ -53,6 +53,10 @@ const ghostty_vt = @import("ghostty-vt");
53 pub const InputAction = ghostty_vt.input.KeyAction; 53 pub const InputAction = ghostty_vt.input.KeyAction;
54 pub const InputKey = ghostty_vt.input.Key; 54 pub const InputKey = ghostty_vt.input.Key;
55 pub const InputMods = ghostty_vt.input.KeyMods; 55 pub const InputMods = ghostty_vt.input.KeyMods;
56 pub const CellColors = struct {
57 fg: [4]f32,
58 bg: [4]f32,
59 };
56 60
57 pub const Terminal = struct { 61 pub const Terminal = struct {
58 alloc: std.mem.Allocator, 62 alloc: std.mem.Allocator,
@@ -126,6 +130,26 @@ pub const Terminal = struct {
126 return writer.buffered(); 130 return writer.buffered();
127 } 131 }
128 132
133 pub fn cellColors(
134 self: *const Terminal,
135 cell: ghostty_vt.RenderState.Cell,
136 ) CellColors {
137 const style: ghostty_vt.Style = switch (cell.raw.content_tag) {
138 .bg_color_palette, .bg_color_rgb => cell.style,
139 else => if (cell.raw.style_id != 0) cell.style else .{},
140 };
141 const colors = self.render_state.colors;
142 const fg = style.fg(.{
143 .default = colors.foreground,
144 .palette = &colors.palette,
145 });
146 const bg = style.bg(&cell.raw, &colors.palette) orelse colors.background;
147 return .{
148 .fg = rgbToFloat4(fg),
149 .bg = rgbToFloat4(bg),
150 };
151 }
152
129 pub fn resize(self: *Terminal, new_cols: u16, new_rows: u16) !void { 153 pub fn resize(self: *Terminal, new_cols: u16, new_rows: u16) !void {
130 try self.inner.resize(self.alloc, @intCast(new_cols), @intCast(new_rows)); 154 try self.inner.resize(self.alloc, @intCast(new_cols), @intCast(new_rows));
131 } 155 }
@@ -145,6 +169,15 @@ pub const Terminal = struct {
145 } 169 }
146 }; 170 };
147 171
172 fn rgbToFloat4(rgb: ghostty_vt.color.RGB) [4]f32 {
173 return .{
174 @as(f32, @floatFromInt(rgb.r)) / 255.0,
175 @as(f32, @floatFromInt(rgb.g)) / 255.0,
176 @as(f32, @floatFromInt(rgb.b)) / 255.0,
177 1.0,
178 };
179 }
180
148 test "Terminal init/deinit" { 181 test "Terminal init/deinit" {
149 var term = try Terminal.init(std.testing.allocator, .{ 182 var term = try Terminal.init(std.testing.allocator, .{
150 .cols = 80, 183 .cols = 80,
@@ -191,3 +224,21 @@ test "Terminal encodes non-text arrow keys" {
191 const encoded = try term.encodeKey(.arrow_left, .{}, .press, &buf); 224 const encoded = try term.encodeKey(.arrow_left, .{}, .press, &buf);
192 try std.testing.expectEqualStrings("\x1b[D", encoded); 225 try std.testing.expectEqualStrings("\x1b[D", encoded);
193 } 226 }
227
228 test "Terminal resolves ANSI fg and bg colors from render state" {
229 var term = try Terminal.init(std.testing.allocator, .{
230 .cols = 80,
231 .rows = 24,
232 });
233 defer term.deinit();
234
235 term.write("\x1b[31;44mA\x1b[0m");
236 try term.snapshot();
237
238 const cell = term.render_state.row_data.get(0).cells.get(0);
239 const colors = term.cellColors(cell);
240 const palette = term.render_state.colors.palette;
241
242 try std.testing.expectEqualDeep(rgbToFloat4(palette[1]), colors.fg);
243 try std.testing.expectEqualDeep(rgbToFloat4(palette[4]), colors.bg);
244 }