mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-23 04:06:13 +03:00
terminal/new: page init
This commit is contained in:
@ -1,12 +1,15 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const color = @import("../color.zig");
|
const color = @import("../color.zig");
|
||||||
const sgr = @import("../sgr.zig");
|
const sgr = @import("../sgr.zig");
|
||||||
const style = @import("style.zig");
|
const style = @import("style.zig");
|
||||||
const size = @import("size.zig");
|
const size = @import("size.zig");
|
||||||
const Offset = size.Offset;
|
const Offset = size.Offset;
|
||||||
|
const OffsetBuf = size.OffsetBuf;
|
||||||
const hash_map = @import("hash_map.zig");
|
const hash_map = @import("hash_map.zig");
|
||||||
const AutoOffsetHashMap = hash_map.AutoOffsetHashMap;
|
const AutoOffsetHashMap = hash_map.AutoOffsetHashMap;
|
||||||
|
const alignForward = std.mem.alignForward;
|
||||||
|
|
||||||
/// A page represents a specific section of terminal screen. The primary
|
/// A page represents a specific section of terminal screen. The primary
|
||||||
/// idea of a page is that it is a fully self-contained unit that can be
|
/// idea of a page is that it is a fully self-contained unit that can be
|
||||||
@ -24,6 +27,16 @@ const AutoOffsetHashMap = hash_map.AutoOffsetHashMap;
|
|||||||
/// thoughtfully laid out to optimize primarily for terminal IO (VT streams)
|
/// thoughtfully laid out to optimize primarily for terminal IO (VT streams)
|
||||||
/// and to minimize memory usage.
|
/// and to minimize memory usage.
|
||||||
pub const Page = struct {
|
pub const Page = struct {
|
||||||
|
comptime {
|
||||||
|
// The alignment of our members. We want to ensure that the page
|
||||||
|
// alignment is always divisible by this.
|
||||||
|
assert(std.mem.page_size % @max(
|
||||||
|
@alignOf(Row),
|
||||||
|
@alignOf(Cell),
|
||||||
|
style.Set.base_align,
|
||||||
|
) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
/// The backing memory for the page. A page is always made up of a
|
/// The backing memory for the page. A page is always made up of a
|
||||||
/// a single contiguous block of memory that is aligned on a page
|
/// a single contiguous block of memory that is aligned on a page
|
||||||
/// boundary and is a multiple of the system page size.
|
/// boundary and is a multiple of the system page size.
|
||||||
@ -41,6 +54,78 @@ pub const Page = struct {
|
|||||||
/// to row, you must use the `rows` field. From the pointer to the
|
/// to row, you must use the `rows` field. From the pointer to the
|
||||||
/// first column, all cells in that row are laid out in column order.
|
/// first column, all cells in that row are laid out in column order.
|
||||||
cells: Offset(Cell),
|
cells: Offset(Cell),
|
||||||
|
|
||||||
|
/// The available set of styles in use on this page.
|
||||||
|
styles: style.Set,
|
||||||
|
|
||||||
|
/// Capacity of this page.
|
||||||
|
pub const Capacity = struct {
|
||||||
|
/// Number of columns and rows we can know about.
|
||||||
|
cols: usize,
|
||||||
|
rows: usize,
|
||||||
|
|
||||||
|
/// Number of unique styles that can be used on this page.
|
||||||
|
styles: u16,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Initialize a new page, allocating the required backing memory.
|
||||||
|
/// It is HIGHLY RECOMMENDED you use a page_allocator as the allocator
|
||||||
|
/// but any allocator is allowed.
|
||||||
|
pub fn init(alloc: Allocator, cap: Capacity) !Page {
|
||||||
|
const l = layout(cap);
|
||||||
|
const backing = try alloc.alignedAlloc(u8, std.mem.page_size, l.total_size);
|
||||||
|
errdefer alloc.free(backing);
|
||||||
|
|
||||||
|
const buf = OffsetBuf.init(backing);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.memory = backing,
|
||||||
|
.rows = buf.member(Row, l.rows_start),
|
||||||
|
.cells = buf.member(Cell, l.cells_start),
|
||||||
|
.styles = style.Set.init(
|
||||||
|
buf.add(l.styles_start),
|
||||||
|
l.styles_layout,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Page, alloc: Allocator) void {
|
||||||
|
alloc.free(self.memory);
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Layout = struct {
|
||||||
|
total_size: usize,
|
||||||
|
rows_start: usize,
|
||||||
|
cells_start: usize,
|
||||||
|
styles_start: usize,
|
||||||
|
styles_layout: style.Set.Layout,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The memory layout for a page given a desired minimum cols
|
||||||
|
/// and rows size.
|
||||||
|
pub fn layout(cap: Capacity) Layout {
|
||||||
|
const rows_start = 0;
|
||||||
|
const rows_end = rows_start + (cap.rows * @sizeOf(Row));
|
||||||
|
|
||||||
|
const cells_count = cap.cols * cap.rows;
|
||||||
|
const cells_start = alignForward(usize, rows_end, @alignOf(Cell));
|
||||||
|
const cells_end = cells_start + (cells_count * @sizeOf(Cell));
|
||||||
|
|
||||||
|
const styles_layout = style.Set.layout(cap.styles);
|
||||||
|
const styles_start = alignForward(usize, cells_end, style.Set.base_align);
|
||||||
|
const styles_end = styles_start + styles_layout.total_size;
|
||||||
|
|
||||||
|
const total_size = styles_end;
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.total_size = total_size,
|
||||||
|
.rows_start = rows_start,
|
||||||
|
.cells_start = cells_start,
|
||||||
|
.styles_start = styles_start,
|
||||||
|
.styles_layout = styles_layout,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Row = packed struct {
|
pub const Row = packed struct {
|
||||||
@ -54,56 +139,34 @@ pub const Row = packed struct {
|
|||||||
/// since we zero initialize the backing memory for a page.
|
/// since we zero initialize the backing memory for a page.
|
||||||
pub const Cell = packed struct(u32) {
|
pub const Cell = packed struct(u32) {
|
||||||
codepoint: u21 = 0,
|
codepoint: u21 = 0,
|
||||||
|
_padding: u11 = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The style attributes for a cell.
|
// Uncomment this when you want to do some math.
|
||||||
pub const Style = struct {
|
// test "Page size calculator" {
|
||||||
/// Various colors, all self-explanatory.
|
// const total_size = alignForward(
|
||||||
fg_color: Color = .none,
|
// usize,
|
||||||
bg_color: Color = .none,
|
// Page.layout(.{
|
||||||
underline_color: Color = .none,
|
// .cols = 333,
|
||||||
|
// .rows = 81,
|
||||||
/// On/off attributes that don't require much bit width so we use
|
// .styles = 32,
|
||||||
/// a packed struct to make this take up significantly less space.
|
// }).total_size,
|
||||||
flags: packed struct {
|
// std.mem.page_size,
|
||||||
bold: bool = false,
|
// );
|
||||||
italic: bool = false,
|
|
||||||
faint: bool = false,
|
|
||||||
blink: bool = false,
|
|
||||||
inverse: bool = false,
|
|
||||||
invisible: bool = false,
|
|
||||||
strikethrough: bool = false,
|
|
||||||
underline: sgr.Attribute.Underline = .none,
|
|
||||||
} = .{},
|
|
||||||
|
|
||||||
/// The color for an SGR attribute. A color can come from multiple
|
|
||||||
/// sources so we use this to track the source plus color value so that
|
|
||||||
/// we can properly react to things like palette changes.
|
|
||||||
pub const Color = union(enum) {
|
|
||||||
none: void,
|
|
||||||
palette: u8,
|
|
||||||
rgb: color.RGB,
|
|
||||||
};
|
|
||||||
|
|
||||||
test {
|
|
||||||
// The size of the struct so we can be aware of changes.
|
|
||||||
const testing = std.testing;
|
|
||||||
try testing.expectEqual(@as(usize, 14), @sizeOf(Style));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
test {
|
|
||||||
_ = Page;
|
|
||||||
_ = Style;
|
|
||||||
}
|
|
||||||
|
|
||||||
// test {
|
|
||||||
// const testing = std.testing;
|
|
||||||
// const cap = try std.math.ceilPowerOfTwo(usize, 350);
|
|
||||||
// const StyleIdMap = AutoOffsetHashMap(size.CellCountInt, style.Style);
|
|
||||||
// const StyleMetadataMap = AutoOffsetHashMap(style.Style, style.Metadata);
|
|
||||||
//
|
//
|
||||||
// var len = StyleIdMap.bufferSize(@intCast(cap));
|
// std.log.warn("total_size={} pages={}", .{
|
||||||
// len += StyleMetadataMap.bufferSize(@intCast(cap));
|
// total_size,
|
||||||
// try testing.expectEqual(@as(usize, 0), len);
|
// total_size / std.mem.page_size,
|
||||||
|
// });
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
test "Page" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var page = try Page.init(alloc, .{
|
||||||
|
.cols = 120,
|
||||||
|
.rows = 80,
|
||||||
|
.styles = 32,
|
||||||
|
});
|
||||||
|
defer page.deinit(alloc);
|
||||||
|
}
|
||||||
|
@ -132,13 +132,25 @@ pub const Set = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Possible errors for upsert.
|
||||||
|
pub const UpsertError = error{
|
||||||
|
/// No more space in the backing buffer. Remove styles or
|
||||||
|
/// grow and reinitialize.
|
||||||
|
OutOfMemory,
|
||||||
|
|
||||||
|
/// No more available IDs. Perform a garbage collection
|
||||||
|
/// operation to compact ID space.
|
||||||
|
/// TODO: implement gc operation
|
||||||
|
Overflow,
|
||||||
|
};
|
||||||
|
|
||||||
/// Upsert a style into the set and return a pointer to the metadata
|
/// Upsert a style into the set and return a pointer to the metadata
|
||||||
/// for that style. The pointer is valid for the lifetime of the set
|
/// for that style. The pointer is valid for the lifetime of the set
|
||||||
/// so long as the style is not removed.
|
/// so long as the style is not removed.
|
||||||
///
|
///
|
||||||
/// The ref count for new styles is initialized to zero and
|
/// The ref count for new styles is initialized to zero and
|
||||||
/// for existing styles remains unmodified.
|
/// for existing styles remains unmodified.
|
||||||
pub fn upsert(self: *Set, base: anytype, style: Style) !*Metadata {
|
pub fn upsert(self: *Set, base: anytype, style: Style) UpsertError!*Metadata {
|
||||||
// If we already have the style in the map, this is fast.
|
// If we already have the style in the map, this is fast.
|
||||||
var map = self.styles.map(base);
|
var map = self.styles.map(base);
|
||||||
const gop = try map.getOrPut(style);
|
const gop = try map.getOrPut(style);
|
||||||
|
Reference in New Issue
Block a user