this repo has no description
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 pub fn eql(self: InternalCell, cell: Cell) bool {
22
23 // fastpath when both cells are default
24 if (self.default and cell.default) return true;
25
26 return std.mem.eql(u8, self.char.items, cell.char.grapheme) and
27 Style.eql(self.style, cell.style) and
28 std.mem.eql(u8, self.uri.items, cell.link.uri) and
29 std.mem.eql(u8, self.uri_id.items, cell.link.params);
30 }
31};
32
33arena: *std.heap.ArenaAllocator = undefined,
34width: u16 = 0,
35height: u16 = 0,
36
37buf: []InternalCell = undefined,
38
39cursor_row: u16 = 0,
40cursor_col: u16 = 0,
41cursor_vis: bool = false,
42cursor_shape: CursorShape = .default,
43
44mouse_shape: MouseShape = .default,
45
46/// sets each cell to the default cell
47pub fn init(alloc: std.mem.Allocator, w: u16, h: u16) !InternalScreen {
48 const arena = try alloc.create(std.heap.ArenaAllocator);
49 arena.* = .init(alloc);
50 var screen = InternalScreen{
51 .arena = arena,
52 .buf = try arena.allocator().alloc(InternalCell, @as(usize, @intCast(w)) * h),
53 };
54 for (screen.buf, 0..) |_, i| {
55 screen.buf[i] = .{
56 .char = try std.ArrayListUnmanaged(u8).initCapacity(arena.allocator(), 1),
57 .uri = .empty,
58 .uri_id = .empty,
59 };
60 screen.buf[i].char.appendAssumeCapacity(' ');
61 }
62 screen.width = w;
63 screen.height = h;
64 return screen;
65}
66
67pub fn deinit(self: *InternalScreen, alloc: std.mem.Allocator) void {
68 self.arena.deinit();
69 alloc.destroy(self.arena);
70 self.* = undefined;
71}
72
73/// writes a cell to a location. 0 indexed
74pub fn writeCell(
75 self: *InternalScreen,
76 col: u16,
77 row: u16,
78 cell: Cell,
79) void {
80 if (self.width < col) {
81 // column out of bounds
82 return;
83 }
84 if (self.height < row) {
85 // height out of bounds
86 return;
87 }
88 const i = (@as(usize, @intCast(row)) * self.width) + col;
89 assert(i < self.buf.len);
90 self.buf[i].char.clearRetainingCapacity();
91 self.buf[i].char.appendSlice(self.arena.allocator(), cell.char.grapheme) catch {
92 log.warn("couldn't write grapheme", .{});
93 };
94 self.buf[i].uri.clearRetainingCapacity();
95 self.buf[i].uri.appendSlice(self.arena.allocator(), cell.link.uri) catch {
96 log.warn("couldn't write uri", .{});
97 };
98 self.buf[i].uri_id.clearRetainingCapacity();
99 self.buf[i].uri_id.appendSlice(self.arena.allocator(), cell.link.params) catch {
100 log.warn("couldn't write uri_id", .{});
101 };
102 self.buf[i].style = cell.style;
103 self.buf[i].default = cell.default;
104}
105
106pub fn readCell(self: *InternalScreen, col: u16, row: u16) ?Cell {
107 if (self.width < col) {
108 // column out of bounds
109 return null;
110 }
111 if (self.height < row) {
112 // height out of bounds
113 return null;
114 }
115 const i = (row * self.width) + col;
116 assert(i < self.buf.len);
117 const cell = self.buf[i];
118 return .{
119 .char = .{ .grapheme = cell.char.items },
120 .style = cell.style,
121 .link = .{
122 .uri = cell.uri.items,
123 .params = cell.uri_id.items,
124 },
125 .default = cell.default,
126 };
127}