a73x

52554a17

Render terminal cursor

a73x   2026-04-08 11:44

Commit message
Render terminal cursor

src/font.zig
Old New
@@ -166,14 +166,15 @@ pub const Atlas = struct {
166 pub fn init(alloc: std.mem.Allocator, width: u32, height: u32) !Atlas { 166 pub fn init(alloc: std.mem.Allocator, width: u32, height: u32) !Atlas {
167 const pixels = try alloc.alloc(u8, @as(usize, width) * @as(usize, height)); 167 const pixels = try alloc.alloc(u8, @as(usize, width) * @as(usize, height));
168 @memset(pixels, 0); 168 @memset(pixels, 0);
169 pixels[0] = 255;
169 return .{ 170 return .{
170 .alloc = alloc, 171 .alloc = alloc,
171 .width = width, 172 .width = width,
172 .height = height, 173 .height = height,
173 .pixels = pixels, 174 .pixels = pixels,
174 .cursor_x = 0, 175 .cursor_x = 1,
175 .cursor_y = 0, 176 .cursor_y = 0,
176 .row_height = 0, 177 .row_height = 1,
177 .cache = std.AutoHashMap(u21, GlyphUV).init(alloc), 178 .cache = std.AutoHashMap(u21, GlyphUV).init(alloc),
178 .dirty = true, 179 .dirty = true,
179 }; 180 };
@@ -184,6 +185,20 @@ pub const Atlas = struct {
184 self.cache.deinit(); 185 self.cache.deinit();
185 } 186 }
186 187
188 pub fn cursorUV(self: *const Atlas) GlyphUV {
189 return .{
190 .u0 = 0,
191 .v0 = 0,
192 .u1 = 1.0 / @as(f32, @floatFromInt(self.width)),
193 .v1 = 1.0 / @as(f32, @floatFromInt(self.height)),
194 .width = 1,
195 .height = 1,
196 .bearing_x = 0,
197 .bearing_y = 0,
198 .advance_x = 1,
199 };
200 }
201
187 pub fn getOrInsert(self: *Atlas, face: *Face, codepoint: u21) !GlyphUV { 202 pub fn getOrInsert(self: *Atlas, face: *Face, codepoint: u21) !GlyphUV {
188 if (self.cache.get(codepoint)) |uv| return uv; 203 if (self.cache.get(codepoint)) |uv| return uv;
189 204
@@ -270,3 +285,13 @@ test "Atlas packs multiple glyphs and returns UVs" {
270 try std.testing.expectEqual(uv_m.u0, uv_m2.u0); 285 try std.testing.expectEqual(uv_m.u0, uv_m2.u0);
271 try std.testing.expectEqual(uv_m.v0, uv_m2.v0); 286 try std.testing.expectEqual(uv_m.v0, uv_m2.v0);
272 } 287 }
288
289 test "Atlas reserves a white pixel for cursor rendering" {
290 var atlas = try Atlas.init(std.testing.allocator, 16, 16);
291 defer atlas.deinit();
292
293 const uv = atlas.cursorUV();
294 try std.testing.expectEqual(@as(u8, 255), atlas.pixels[0]);
295 try std.testing.expectEqual(@as(f32, 0), uv.u0);
296 try std.testing.expectEqual(@as(f32, 0), uv.v0);
297 }
src/main.zig
Old New
@@ -222,6 +222,31 @@ fn runTerminal(alloc: std.mem.Allocator) !void {
222 } 222 }
223 } 223 }
224 224
225 if (term.render_state.cursor.visible) {
226 if (term.render_state.cursor.viewport) |cursor| {
227 const cursor_uv = atlas.cursorUV();
228 try instances.append(alloc, .{
229 .cell_pos = .{
230 @floatFromInt(cursor.x),
231 @floatFromInt(cursor.y),
232 },
233 .glyph_size = .{
234 @floatFromInt(cell_w),
235 @floatFromInt(cell_h),
236 },
237 .glyph_bearing = .{ 0, 0 },
238 .uv_rect = .{
239 cursor_uv.u0,
240 cursor_uv.v0,
241 cursor_uv.u1,
242 cursor_uv.v1,
243 },
244 .fg = .{ 1.0, 1.0, 1.0, 0.5 },
245 .bg = .{ 0, 0, 0, 0 },
246 });
247 }
248 }
249
225 // Re-upload atlas if new glyphs were added 250 // Re-upload atlas if new glyphs were added
226 if (atlas.dirty) { 251 if (atlas.dirty) {
227 try ctx.uploadAtlas(atlas.pixels); 252 try ctx.uploadAtlas(atlas.pixels);