this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 4320ec29d03415eba80a14b2eaaff8cefa6822e8 149 lines 4.5 kB view raw
1const std = @import("std"); 2const assert = std.debug.assert; 3const Style = @import("Cell.zig").Style; 4const Cell = @import("Cell.zig"); 5const MouseShape = @import("Mouse.zig").Shape; 6const CursorShape = Cell.CursorShape; 7 8const log = std.log.scoped(.vaxis); 9 10const InternalScreen = @This(); 11 12pub const InternalCell = struct { 13 char: std.ArrayList(u8) = .empty, 14 style: Style = .{}, 15 uri: std.ArrayList(u8) = .empty, 16 uri_id: std.ArrayList(u8) = .empty, 17 // if we got skipped because of a wide character 18 skipped: bool = false, 19 default: bool = true, 20 21 // If we should skip rendering *this* round due to being printed over previously (from a scaled 22 // cell, for example) 23 skip: bool = false, 24 25 scale: Cell.Scale = .{}, 26 27 pub fn eql(self: InternalCell, cell: Cell) bool { 28 29 // fastpath when both cells are default 30 if (self.default and cell.default) return true; 31 32 return std.mem.eql(u8, self.char.items, cell.char.grapheme) and 33 Style.eql(self.style, cell.style) and 34 std.mem.eql(u8, self.uri.items, cell.link.uri) and 35 std.mem.eql(u8, self.uri_id.items, cell.link.params); 36 } 37}; 38 39arena: *std.heap.ArenaAllocator, 40width: u16 = 0, 41height: u16 = 0, 42 43buf: []InternalCell, 44 45cursor_vis: bool = false, 46cursor_shape: CursorShape = .default, 47 48mouse_shape: MouseShape = .default, 49 50/// sets each cell to the default cell 51pub fn init(gpa: std.mem.Allocator, w: u16, h: u16) !InternalScreen { 52 const arena = try gpa.create(std.heap.ArenaAllocator); 53 arena.* = .init(gpa); 54 var screen = InternalScreen{ 55 .arena = arena, 56 .buf = try arena.allocator().alloc(InternalCell, @as(usize, @intCast(w)) * h), 57 }; 58 for (screen.buf, 0..) |_, i| { 59 screen.buf[i] = .{ 60 .char = try .initCapacity(arena.allocator(), 1), 61 .uri = .empty, 62 .uri_id = .empty, 63 }; 64 screen.buf[i].char.appendAssumeCapacity(' '); 65 } 66 screen.width = w; 67 screen.height = h; 68 return screen; 69} 70 71pub fn deinit(self: *InternalScreen, alloc: std.mem.Allocator) void { 72 self.arena.deinit(); 73 alloc.destroy(self.arena); 74 self.* = undefined; 75} 76 77/// writes a cell to a location. 0 indexed 78pub fn writeCell( 79 self: *InternalScreen, 80 col: u16, 81 row: u16, 82 cell: Cell, 83) void { 84 if (self.width <= col) { 85 // column out of bounds 86 return; 87 } 88 if (self.height <= row) { 89 // height out of bounds 90 return; 91 } 92 const i = (@as(usize, @intCast(row)) * self.width) + col; 93 assert(i < self.buf.len); 94 self.buf[i].char.clearRetainingCapacity(); 95 self.buf[i].char.appendSlice(self.arena.allocator(), cell.char.grapheme) catch |err| { 96 log.warn("couldn't write grapheme: {t}", .{err}); 97 }; 98 self.buf[i].uri.clearRetainingCapacity(); 99 self.buf[i].uri.appendSlice(self.arena.allocator(), cell.link.uri) catch |err| { 100 log.warn("couldn't write uri: {t}", .{err}); 101 }; 102 self.buf[i].uri_id.clearRetainingCapacity(); 103 self.buf[i].uri_id.appendSlice(self.arena.allocator(), cell.link.params) catch |err| { 104 log.warn("couldn't write uri_id: {t}", .{err}); 105 }; 106 self.buf[i].style = cell.style; 107 self.buf[i].default = cell.default; 108} 109 110pub fn readCell(self: *InternalScreen, col: u16, row: u16) ?Cell { 111 if (self.width <= col) { 112 // column out of bounds 113 return null; 114 } 115 if (self.height <= row) { 116 // height out of bounds 117 return null; 118 } 119 const i = (row * self.width) + col; 120 assert(i < self.buf.len); 121 const cell = self.buf[i]; 122 return .{ 123 .char = .{ .grapheme = cell.char.items }, 124 .style = cell.style, 125 .link = .{ 126 .uri = cell.uri.items, 127 .params = cell.uri_id.items, 128 }, 129 .default = cell.default, 130 }; 131} 132 133test "InternalScreen: out-of-bounds read/write are ignored" { 134 var screen = try InternalScreen.init(std.testing.allocator, 2, 2); 135 defer screen.deinit(std.testing.allocator); 136 137 const sentinel: Cell = .{ .char = .{ .grapheme = "A", .width = 1 } }; 138 screen.writeCell(0, 1, sentinel); 139 140 const oob_cell: Cell = .{ .char = .{ .grapheme = "X", .width = 1 } }; 141 screen.writeCell(2, 0, oob_cell); 142 const read_back = screen.readCell(0, 1) orelse return error.TestUnexpectedResult; 143 try std.testing.expect(std.mem.eql(u8, read_back.char.grapheme, "A")); 144 try std.testing.expect(screen.readCell(2, 0) == null); 145} 146 147test { 148 std.testing.refAllDecls(@This()); 149}