mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
terminal: bunch of junk for paged terminal
This commit is contained in:
@ -50,4 +50,8 @@ pub usingnamespace if (builtin.target.isWasm()) struct {
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
|
||||
_ = @import("new/page.zig");
|
||||
_ = @import("new/size.zig");
|
||||
_ = @import("new/style.zig");
|
||||
}
|
||||
|
95
src/terminal/new/page.zig
Normal file
95
src/terminal/new/page.zig
Normal file
@ -0,0 +1,95 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const color = @import("../color.zig");
|
||||
const sgr = @import("../sgr.zig");
|
||||
const size = @import("size.zig");
|
||||
const Offset = size.Offset;
|
||||
|
||||
/// 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
|
||||
/// serialized, copied, etc. as a convenient way to represent a section
|
||||
/// of the screen.
|
||||
///
|
||||
/// This property is useful for renderers which want to copy just the pages
|
||||
/// for the visible portion of the screen, or for infinite scrollback where
|
||||
/// we may want to serialize and store pages that are sufficiently far
|
||||
/// away from the current viewport.
|
||||
///
|
||||
/// Pages are always backed by a single contiguous block of memory that is
|
||||
/// aligned on a page boundary. This makes it easy and fast to copy pages
|
||||
/// around. Within the contiguous block of memory, the contents of a page are
|
||||
/// thoughtfully laid out to optimize primarily for terminal IO (VT streams)
|
||||
/// and to minimize memory usage.
|
||||
pub const Page = struct {
|
||||
/// 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
|
||||
/// boundary and is a multiple of the system page size.
|
||||
///
|
||||
/// The backing memory is always zero initialized, so the zero value
|
||||
/// of all data within the page must always be valid.
|
||||
memory: []align(std.mem.page_size) u8,
|
||||
|
||||
/// The array of rows in the page. The rows are always in row order
|
||||
/// (i.e. index 0 is the top row, index 1 is the row below that, etc.)
|
||||
rows: Offset(Row),
|
||||
|
||||
/// The array of cells in the page. The cells are NOT in row order,
|
||||
/// but they are in column order. To determine the mapping of cells
|
||||
/// 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.
|
||||
cells: Offset(Cell),
|
||||
};
|
||||
|
||||
pub const Row = packed struct {
|
||||
/// The cells in the row offset from the page.
|
||||
cells: Offset(Cell),
|
||||
};
|
||||
|
||||
/// A cell represents a single terminal grid cell.
|
||||
///
|
||||
/// The zero value of this struct must be a valid cell representing empty,
|
||||
/// since we zero initialize the backing memory for a page.
|
||||
pub const Cell = packed struct(u32) {
|
||||
codepoint: u21 = 0,
|
||||
};
|
||||
|
||||
/// The style attributes for a cell.
|
||||
pub const Style = struct {
|
||||
/// Various colors, all self-explanatory.
|
||||
fg_color: Color = .none,
|
||||
bg_color: Color = .none,
|
||||
underline_color: Color = .none,
|
||||
|
||||
/// On/off attributes that don't require much bit width so we use
|
||||
/// a packed struct to make this take up significantly less space.
|
||||
flags: packed struct {
|
||||
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;
|
||||
}
|
60
src/terminal/new/size.zig
Normal file
60
src/terminal/new/size.zig
Normal file
@ -0,0 +1,60 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
/// The maximum size of a page in bytes. We use a u16 here because any
|
||||
/// smaller bit size by Zig is upgraded anyways to a u16 on mainstream
|
||||
/// CPU architectures, and because 65KB is a reasonable page size. To
|
||||
/// support better configurability, we derive everything from this.
|
||||
pub const max_page_size = 65_536;
|
||||
|
||||
/// The int type that can contain the maximum memory offset in bytes,
|
||||
/// derived from the maximum terminal page size.
|
||||
pub const OffsetInt = std.math.IntFittingRange(0, max_page_size - 1);
|
||||
|
||||
/// The int type that can contain the maximum number of cells in a page.
|
||||
pub const CellCountInt = u16; // TODO: derive
|
||||
//
|
||||
/// The offset from the base address of the page to the start of some data.
|
||||
/// This is typed for ease of use.
|
||||
///
|
||||
/// This is a packed struct so we can attach methods to an int.
|
||||
pub fn Offset(comptime T: type) type {
|
||||
return packed struct(OffsetInt) {
|
||||
const Self = @This();
|
||||
|
||||
offset: OffsetInt = 0,
|
||||
|
||||
/// Returns a pointer to the start of the data, properly typed.
|
||||
pub fn ptr(self: Self, base: anytype) [*]T {
|
||||
// The offset must be properly aligned for the type since
|
||||
// our return type is naturally aligned. We COULD modify this
|
||||
// to return arbitrary alignment, but its not something we need.
|
||||
assert(@mod(self.offset, @alignOf(T)) == 0);
|
||||
return @ptrFromInt(@intFromPtr(base) + self.offset);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "Offset" {
|
||||
// This test is here so that if Offset changes, we can be very aware
|
||||
// of this effect and think about the implications of it.
|
||||
const testing = std.testing;
|
||||
try testing.expect(OffsetInt == u16);
|
||||
}
|
||||
|
||||
test "Offset ptr u8" {
|
||||
const testing = std.testing;
|
||||
const offset: Offset(u8) = .{ .offset = 42 };
|
||||
const base_int: usize = @intFromPtr(&offset);
|
||||
const actual = offset.ptr(&offset);
|
||||
try testing.expectEqual(@as(usize, base_int + 42), @intFromPtr(actual));
|
||||
}
|
||||
|
||||
test "Offset ptr structural" {
|
||||
const Struct = struct { x: u32, y: u32 };
|
||||
const testing = std.testing;
|
||||
const offset: Offset(Struct) = .{ .offset = @alignOf(Struct) * 4 };
|
||||
const base_int: usize = @intFromPtr(&offset);
|
||||
const actual = offset.ptr(&offset);
|
||||
try testing.expectEqual(@as(usize, base_int + offset.offset), @intFromPtr(actual));
|
||||
}
|
62
src/terminal/new/style.zig
Normal file
62
src/terminal/new/style.zig
Normal file
@ -0,0 +1,62 @@
|
||||
const std = @import("std");
|
||||
const color = @import("../color.zig");
|
||||
const sgr = @import("../sgr.zig");
|
||||
const size = @import("size.zig");
|
||||
|
||||
/// The unique identifier for a style. This is at most the number of cells
|
||||
/// that can fit into a terminal page.
|
||||
pub const Id = size.CellCountInt;
|
||||
|
||||
/// The style attributes for a cell.
|
||||
pub const Style = struct {
|
||||
/// Various colors, all self-explanatory.
|
||||
fg_color: Color = .none,
|
||||
bg_color: Color = .none,
|
||||
underline_color: Color = .none,
|
||||
|
||||
/// On/off attributes that don't require much bit width so we use
|
||||
/// a packed struct to make this take up significantly less space.
|
||||
flags: packed struct {
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
/// Maps a style definition to metadata about that style.
|
||||
pub const MetadataMap = std.AutoHashMapUnmanaged(Style, Metadata);
|
||||
|
||||
/// Maps the unique style ID to the concrete style definition.
|
||||
pub const IdMap = std.AutoHashMapUnmanaged(size.CellCountInt, Style);
|
||||
|
||||
/// Metadata about a style. This is used to track the reference count
|
||||
/// and the unique identifier for a style. The unique identifier is used
|
||||
/// to track the style in the full style map.
|
||||
pub const Metadata = struct {
|
||||
ref: size.CellCountInt = 0,
|
||||
id: size.CellCountInt = 0,
|
||||
};
|
||||
|
||||
test {
|
||||
_ = Style;
|
||||
}
|
Reference in New Issue
Block a user