this repo has no description
13
fork

Configure Feed

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

at 8a9c2d5e1b3778f1ea43c9bd5d325cfa72016584 147 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.ArrayListUnmanaged(u8) = .empty, 14 style: Style = .{}, 15 uri: std.ArrayListUnmanaged(u8) = .empty, 16 uri_id: std.ArrayListUnmanaged(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_row: u16 = 0, 46cursor_col: u16 = 0, 47cursor_vis: bool = false, 48cursor_shape: CursorShape = .default, 49 50mouse_shape: MouseShape = .default, 51 52/// sets each cell to the default cell 53pub fn init(alloc: std.mem.Allocator, w: u16, h: u16) !InternalScreen { 54 const arena = try alloc.create(std.heap.ArenaAllocator); 55 arena.* = .init(alloc); 56 var screen = InternalScreen{ 57 .arena = arena, 58 .buf = try arena.allocator().alloc(InternalCell, @as(usize, @intCast(w)) * h), 59 }; 60 for (screen.buf, 0..) |_, i| { 61 screen.buf[i] = .{ 62 .char = try std.ArrayListUnmanaged(u8).initCapacity(arena.allocator(), 1), 63 .uri = .empty, 64 .uri_id = .empty, 65 }; 66 screen.buf[i].char.appendAssumeCapacity(' '); 67 } 68 screen.width = w; 69 screen.height = h; 70 return screen; 71} 72 73pub fn deinit(self: *InternalScreen, alloc: std.mem.Allocator) void { 74 self.arena.deinit(); 75 alloc.destroy(self.arena); 76 self.* = undefined; 77} 78 79/// writes a cell to a location. 0 indexed 80pub fn writeCell( 81 self: *InternalScreen, 82 col: u16, 83 row: u16, 84 cell: Cell, 85) void { 86 if (self.width <= col) { 87 // column out of bounds 88 return; 89 } 90 if (self.height <= row) { 91 // height out of bounds 92 return; 93 } 94 const i = (@as(usize, @intCast(row)) * self.width) + col; 95 assert(i < self.buf.len); 96 self.buf[i].char.clearRetainingCapacity(); 97 self.buf[i].char.appendSlice(self.arena.allocator(), cell.char.grapheme) catch { 98 log.warn("couldn't write grapheme", .{}); 99 }; 100 self.buf[i].uri.clearRetainingCapacity(); 101 self.buf[i].uri.appendSlice(self.arena.allocator(), cell.link.uri) catch { 102 log.warn("couldn't write uri", .{}); 103 }; 104 self.buf[i].uri_id.clearRetainingCapacity(); 105 self.buf[i].uri_id.appendSlice(self.arena.allocator(), cell.link.params) catch { 106 log.warn("couldn't write uri_id", .{}); 107 }; 108 self.buf[i].style = cell.style; 109 self.buf[i].default = cell.default; 110} 111 112pub fn readCell(self: *InternalScreen, col: u16, row: u16) ?Cell { 113 if (self.width <= col) { 114 // column out of bounds 115 return null; 116 } 117 if (self.height <= row) { 118 // height out of bounds 119 return null; 120 } 121 const i = (row * self.width) + col; 122 assert(i < self.buf.len); 123 const cell = self.buf[i]; 124 return .{ 125 .char = .{ .grapheme = cell.char.items }, 126 .style = cell.style, 127 .link = .{ 128 .uri = cell.uri.items, 129 .params = cell.uri_id.items, 130 }, 131 .default = cell.default, 132 }; 133} 134 135test "InternalScreen: out-of-bounds read/write are ignored" { 136 var screen = try InternalScreen.init(std.testing.allocator, 2, 2); 137 defer screen.deinit(std.testing.allocator); 138 139 const sentinel: Cell = .{ .char = .{ .grapheme = "A", .width = 1 } }; 140 screen.writeCell(0, 1, sentinel); 141 142 const oob_cell: Cell = .{ .char = .{ .grapheme = "X", .width = 1 } }; 143 screen.writeCell(2, 0, oob_cell); 144 const read_back = screen.readCell(0, 1) orelse return error.TestUnexpectedResult; 145 try std.testing.expect(std.mem.eql(u8, read_back.char.grapheme, "A")); 146 try std.testing.expect(screen.readCell(2, 0) == null); 147}