a0db41f87c
Add a renderer for applications running on the primary screen. This renderer uses relative cursor positioning, and works with all the same primitives as the alternate screen. Fix a bug where repositioning was never turned back to false. This was a nasty one with huge perf implications. Set internal cells to a space and default on init. This prevents us from writing them. As a result, we now issue a hardware clear on resize.
123 lines
3.6 KiB
Zig
123 lines
3.6 KiB
Zig
const std = @import("std");
|
|
const assert = std.debug.assert;
|
|
const Style = @import("Cell.zig").Style;
|
|
const Cell = @import("Cell.zig");
|
|
const MouseShape = @import("Mouse.zig").Shape;
|
|
const CursorShape = Cell.CursorShape;
|
|
|
|
const log = std.log.scoped(.internal_screen);
|
|
|
|
const InternalScreen = @This();
|
|
|
|
pub const InternalCell = struct {
|
|
char: std.ArrayList(u8) = undefined,
|
|
style: Style = .{},
|
|
uri: std.ArrayList(u8) = undefined,
|
|
uri_id: std.ArrayList(u8) = undefined,
|
|
// if we got skipped because of a wide character
|
|
skipped: bool = false,
|
|
default: bool = true,
|
|
|
|
pub fn eql(self: InternalCell, cell: Cell) bool {
|
|
// fastpath when both cells are default
|
|
if (self.default and cell.default) return true;
|
|
// this is actually faster than std.meta.eql on the individual items.
|
|
// Our strings are always small, usually less than 4 bytes so the simd
|
|
// usage in std.mem.eql has too much overhead vs looping the bytes
|
|
if (!std.mem.eql(u8, self.char.items, cell.char.grapheme)) return false;
|
|
if (!Style.eql(self.style, cell.style)) return false;
|
|
if (!std.mem.eql(u8, self.uri.items, cell.link.uri)) return false;
|
|
if (!std.mem.eql(u8, self.uri_id.items, cell.link.params)) return false;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
width: usize = 0,
|
|
height: usize = 0,
|
|
|
|
buf: []InternalCell = undefined,
|
|
|
|
cursor_row: usize = 0,
|
|
cursor_col: usize = 0,
|
|
cursor_vis: bool = false,
|
|
cursor_shape: CursorShape = .default,
|
|
|
|
mouse_shape: MouseShape = .default,
|
|
|
|
/// sets each cell to the default cell
|
|
pub fn init(alloc: std.mem.Allocator, w: usize, h: usize) !InternalScreen {
|
|
var screen = InternalScreen{
|
|
.buf = try alloc.alloc(InternalCell, w * h),
|
|
};
|
|
for (screen.buf, 0..) |_, i| {
|
|
screen.buf[i] = .{
|
|
.char = try std.ArrayList(u8).initCapacity(alloc, 1),
|
|
.uri = std.ArrayList(u8).init(alloc),
|
|
.uri_id = std.ArrayList(u8).init(alloc),
|
|
};
|
|
try screen.buf[i].char.append(' ');
|
|
}
|
|
screen.width = w;
|
|
screen.height = h;
|
|
return screen;
|
|
}
|
|
|
|
pub fn deinit(self: *InternalScreen, alloc: std.mem.Allocator) void {
|
|
for (self.buf, 0..) |_, i| {
|
|
self.buf[i].char.deinit();
|
|
self.buf[i].uri.deinit();
|
|
self.buf[i].uri_id.deinit();
|
|
}
|
|
|
|
alloc.free(self.buf);
|
|
}
|
|
|
|
/// writes a cell to a location. 0 indexed
|
|
pub fn writeCell(
|
|
self: *InternalScreen,
|
|
col: usize,
|
|
row: usize,
|
|
cell: Cell,
|
|
) void {
|
|
if (self.width < col) {
|
|
// column out of bounds
|
|
return;
|
|
}
|
|
if (self.height < row) {
|
|
// height out of bounds
|
|
return;
|
|
}
|
|
const i = (row * self.width) + col;
|
|
assert(i < self.buf.len);
|
|
self.buf[i].char.clearRetainingCapacity();
|
|
self.buf[i].char.appendSlice(cell.char.grapheme) catch {
|
|
log.warn("couldn't write grapheme", .{});
|
|
};
|
|
self.buf[i].uri.clearRetainingCapacity();
|
|
self.buf[i].uri.appendSlice(cell.link.uri) catch {
|
|
log.warn("couldn't write uri", .{});
|
|
};
|
|
self.buf[i].uri_id.clearRetainingCapacity();
|
|
self.buf[i].uri_id.appendSlice(cell.link.params) catch {
|
|
log.warn("couldn't write uri_id", .{});
|
|
};
|
|
self.buf[i].style = cell.style;
|
|
self.buf[i].default = cell.default;
|
|
}
|
|
|
|
pub fn readCell(self: *InternalScreen, col: usize, row: usize) ?Cell {
|
|
if (self.width < col) {
|
|
// column out of bounds
|
|
return null;
|
|
}
|
|
if (self.height < row) {
|
|
// height out of bounds
|
|
return null;
|
|
}
|
|
const i = (row * self.width) + col;
|
|
assert(i < self.buf.len);
|
|
return .{
|
|
.char = .{ .grapheme = self.buf[i].char.items },
|
|
.style = self.buf[i].style,
|
|
};
|
|
}
|