a73x

9c669d38

Draw cell backgrounds

a73x   2026-04-08 11:57

Commit message
Draw cell backgrounds

build.zig
Old New
@@ -182,6 +182,8 @@ pub fn build(b: *std.Build) void {
182 renderer_mod.addImport("vulkan", vulkan_module); 182 renderer_mod.addImport("vulkan", vulkan_module);
183 renderer_mod.linkSystemLibrary("dl", .{}); 183 renderer_mod.linkSystemLibrary("dl", .{});
184 exe_mod.addImport("renderer", renderer_mod); 184 exe_mod.addImport("renderer", renderer_mod);
185 main_test_mod.addImport("renderer", renderer_mod);
186 main_test_mod.addImport("font", font_mod);
185 187
186 // Test renderer.zig 188 // Test renderer.zig
187 const renderer_test_mod = b.createModule(.{ 189 const renderer_test_mod = b.createModule(.{
src/main.zig
Old New
@@ -194,6 +194,8 @@ fn runTerminal(alloc: std.mem.Allocator) !void {
194 try term.snapshot(); 194 try term.snapshot();
195 195
196 instances.clearRetainingCapacity(); 196 instances.clearRetainingCapacity();
197 const default_bg = term.backgroundColor();
198 const bg_uv = atlas.cursorUV();
197 199
198 const term_rows = term.render_state.row_data.items(.cells); 200 const term_rows = term.render_state.row_data.items(.cells);
199 var row_idx: u32 = 0; 201 var row_idx: u32 = 0;
@@ -203,24 +205,24 @@ fn runTerminal(alloc: std.mem.Allocator) !void {
203 var col_idx: u32 = 0; 205 var col_idx: u32 = 0;
204 while (col_idx < raw_cells.len) : (col_idx += 1) { 206 while (col_idx < raw_cells.len) : (col_idx += 1) {
205 const cp = raw_cells[col_idx].codepoint(); 207 const cp = raw_cells[col_idx].codepoint();
206 if (cp == 0 or cp == ' ') continue;
207
208 const uv = atlas.getOrInsert(&face, @intCast(cp)) catch continue;
209 const colors = term.cellColors(row_cells.get(col_idx)); 208 const colors = term.cellColors(row_cells.get(col_idx));
210 209 const glyph_uv = if (cp == 0 or cp == ' ')
211 try instances.append(alloc, .{ 210 null
212 .cell_pos = .{ @floatFromInt(col_idx), @floatFromInt(row_idx) }, 211 else
213 .glyph_size = .{ @floatFromInt(uv.width), @floatFromInt(uv.height) }, 212 atlas.getOrInsert(&face, @intCast(cp)) catch null;
214 .glyph_bearing = .{ 213
215 @floatFromInt(uv.bearing_x), 214 try appendCellInstances(
216 // bearing_y in freetype is from baseline going up; our quad origin is top-left going down 215 alloc,
217 // cell_h - bearing_y gives the offset from the top of the cell 216 &instances,
218 @as(f32, @floatFromInt(cell_h)) - @as(f32, @floatFromInt(uv.bearing_y)), 217 row_idx,
219 }, 218 col_idx,
220 .uv_rect = .{ uv.u0, uv.v0, uv.u1, uv.v1 }, 219 cell_w,
221 .fg = colors.fg, 220 cell_h,
222 .bg = colors.bg, 221 glyph_uv,
223 }); 222 bg_uv,
223 colors,
224 default_bg,
225 );
224 } 226 }
225 } 227 }
226 228
@@ -277,6 +279,43 @@ fn gridSizeForWindow(window_w: u32, window_h: u32, cell_w: u32, cell_h: u32) Gri
277 }; 279 };
278 } 280 }
279 281
282 fn appendCellInstances(
283 alloc: std.mem.Allocator,
284 instances: *std.ArrayListUnmanaged(renderer.Instance),
285 row_idx: u32,
286 col_idx: u32,
287 cell_w: u32,
288 cell_h: u32,
289 glyph_uv: ?font.GlyphUV,
290 bg_uv: font.GlyphUV,
291 colors: vt.CellColors,
292 default_bg: [4]f32,
293 ) !void {
294 if (!std.meta.eql(colors.bg, default_bg)) {
295 try instances.append(alloc, .{
296 .cell_pos = .{ @floatFromInt(col_idx), @floatFromInt(row_idx) },
297 .glyph_size = .{ @floatFromInt(cell_w), @floatFromInt(cell_h) },
298 .glyph_bearing = .{ 0, 0 },
299 .uv_rect = .{ bg_uv.u0, bg_uv.v0, bg_uv.u1, bg_uv.v1 },
300 .fg = colors.bg,
301 .bg = colors.bg,
302 });
303 }
304
305 const uv = glyph_uv orelse return;
306 try instances.append(alloc, .{
307 .cell_pos = .{ @floatFromInt(col_idx), @floatFromInt(row_idx) },
308 .glyph_size = .{ @floatFromInt(uv.width), @floatFromInt(uv.height) },
309 .glyph_bearing = .{
310 @floatFromInt(uv.bearing_x),
311 @as(f32, @floatFromInt(cell_h)) - @as(f32, @floatFromInt(uv.bearing_y)),
312 },
313 .uv_rect = .{ uv.u0, uv.v0, uv.u1, uv.v1 },
314 .fg = colors.fg,
315 .bg = colors.bg,
316 });
317 }
318
280 fn encodeKeyboardEvent( 319 fn encodeKeyboardEvent(
281 term: *const vt.Terminal, 320 term: *const vt.Terminal,
282 ev: wayland_client.KeyboardEvent, 321 ev: wayland_client.KeyboardEvent,
@@ -441,6 +480,94 @@ test "gridSizeForWindow clamps to at least one cell" {
441 try std.testing.expectEqual(@as(u16, 1), grid.rows); 480 try std.testing.expectEqual(@as(u16, 1), grid.rows);
442 } 481 }
443 482
483 test "appendCellInstances emits a background quad for colored space" {
484 var instances: std.ArrayListUnmanaged(renderer.Instance) = .empty;
485 defer instances.deinit(std.testing.allocator);
486
487 const bg_uv: font.GlyphUV = .{
488 .u0 = 0.0,
489 .v0 = 0.0,
490 .u1 = 0.1,
491 .v1 = 0.1,
492 .width = 1,
493 .height = 1,
494 .bearing_x = 0,
495 .bearing_y = 0,
496 .advance_x = 1,
497 };
498
499 try appendCellInstances(
500 std.testing.allocator,
501 &instances,
502 2,
503 3,
504 8,
505 16,
506 null,
507 bg_uv,
508 .{
509 .fg = .{ 1.0, 1.0, 1.0, 1.0 },
510 .bg = .{ 0.2, 0.3, 0.4, 1.0 },
511 },
512 .{ 0.0, 0.0, 0.0, 1.0 },
513 );
514
515 try std.testing.expectEqual(@as(usize, 1), instances.items.len);
516 try std.testing.expectEqualDeep([4]f32{ 0.2, 0.3, 0.4, 1.0 }, instances.items[0].fg);
517 try std.testing.expectEqualDeep([4]f32{ 0.2, 0.3, 0.4, 1.0 }, instances.items[0].bg);
518 try std.testing.expectEqualDeep([2]f32{ 8.0, 16.0 }, instances.items[0].glyph_size);
519 }
520
521 test "appendCellInstances emits background before glyph" {
522 var instances: std.ArrayListUnmanaged(renderer.Instance) = .empty;
523 defer instances.deinit(std.testing.allocator);
524
525 const bg_uv: font.GlyphUV = .{
526 .u0 = 0.0,
527 .v0 = 0.0,
528 .u1 = 0.1,
529 .v1 = 0.1,
530 .width = 1,
531 .height = 1,
532 .bearing_x = 0,
533 .bearing_y = 0,
534 .advance_x = 1,
535 };
536 const glyph_uv: font.GlyphUV = .{
537 .u0 = 0.2,
538 .v0 = 0.3,
539 .u1 = 0.4,
540 .v1 = 0.5,
541 .width = 7,
542 .height = 11,
543 .bearing_x = 1,
544 .bearing_y = 13,
545 .advance_x = 8,
546 };
547
548 try appendCellInstances(
549 std.testing.allocator,
550 &instances,
551 0,
552 1,
553 8,
554 16,
555 glyph_uv,
556 bg_uv,
557 .{
558 .fg = .{ 0.9, 0.8, 0.7, 1.0 },
559 .bg = .{ 0.1, 0.2, 0.3, 1.0 },
560 },
561 .{ 0.0, 0.0, 0.0, 1.0 },
562 );
563
564 try std.testing.expectEqual(@as(usize, 2), instances.items.len);
565 try std.testing.expectEqualDeep([2]f32{ 8.0, 16.0 }, instances.items[0].glyph_size);
566 try std.testing.expectEqualDeep([2]f32{ 7.0, 11.0 }, instances.items[1].glyph_size);
567 try std.testing.expectEqualDeep([4]f32{ 0.1, 0.2, 0.3, 1.0 }, instances.items[0].fg);
568 try std.testing.expectEqualDeep([4]f32{ 0.9, 0.8, 0.7, 1.0 }, instances.items[1].fg);
569 }
570
444 fn runRenderSmokeTest(alloc: std.mem.Allocator) !void { 571 fn runRenderSmokeTest(alloc: std.mem.Allocator) !void {
445 var conn = try wayland_client.Connection.init(); 572 var conn = try wayland_client.Connection.init();
446 defer conn.deinit(); 573 defer conn.deinit();
src/vt.zig
Old New
@@ -158,6 +158,10 @@ pub const Terminal = struct {
158 try self.render_state.update(self.alloc, &self.inner); 158 try self.render_state.update(self.alloc, &self.inner);
159 } 159 }
160 160
161 pub fn backgroundColor(self: *const Terminal) [4]f32 {
162 return rgbToFloat4(self.render_state.colors.background);
163 }
164
161 /// Number of columns in the terminal grid. 165 /// Number of columns in the terminal grid.
162 pub fn cols(self: *const Terminal) u16 { 166 pub fn cols(self: *const Terminal) u16 {
163 return @intCast(self.render_state.cols); 167 return @intCast(self.render_state.cols);