aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/terminal/size.zig
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-04-04 19:22:04 +0300
committerCan Celik <ogulcancelik@gmail.com>2026-04-05 01:23:06 +0300
commit41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch)
tree3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/terminal/size.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/terminal/size.zig')
-rw-r--r--vendor/libghostty-vt/src/terminal/size.zig227
1 files changed, 227 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/terminal/size.zig b/vendor/libghostty-vt/src/terminal/size.zig
new file mode 100644
index 00000000..7be09739
--- /dev/null
+++ b/vendor/libghostty-vt/src/terminal/size.zig
@@ -0,0 +1,227 @@
+const std = @import("std");
+const assert = @import("../quirks.zig").inlineAssert;
+
+/// 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 = std.math.maxInt(u32);
+
+/// 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);
+
+/// Int types for maximum values of things. A lot of these sizes are
+/// based on "X is enough for any reasonable use case" principles.
+// The goal is that a user can have the maxInt amount of all of these
+// present at one time and be able to address them in a single Page.zig.
+
+// Total number of cells that are possible in each dimension (row/col).
+// Based on 2^16 being enough for any reasonable terminal size and allowing
+// IDs to remain 16-bit.
+pub const CellCountInt = u16;
+
+// Total number of styles and hyperlinks that are possible in a page.
+// We match CellCountInt here because each cell in a single row can have at
+// most one style, making it simple to split a page by splitting rows.
+//
+// Note due to the way RefCountedSet works, we are short one value, but
+// this is a theoretical limit we accept. A page with a single row max
+// columns wide would be one short of having every cell have a unique style.
+pub const StyleCountInt = CellCountInt;
+pub const HyperlinkCountInt = CellCountInt;
+
+// Total number of bytes that can be taken up by grapheme data and string
+// data. Both of these technically unlimited with malicious input, but
+// we choose a reasonable limit of 2^32 (4GB) per.
+pub const GraphemeBytesInt = u32;
+pub const StringBytesInt = u32;
+
+/// 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,
+
+ /// A slice of type T that stores via a base offset and len.
+ pub const Slice = struct {
+ offset: Self = .{},
+ len: usize = 0,
+
+ /// Returns a slice for the data, properly typed.
+ pub inline fn slice(self: Slice, base: anytype) []T {
+ return self.offset.ptr(base)[0..self.len];
+ }
+ };
+
+ /// Returns a pointer to the start of the data, properly typed.
+ pub inline 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.
+ const addr = intFromBase(base) + self.offset;
+ assert(addr % @alignOf(T) == 0);
+ return @ptrFromInt(addr);
+ }
+ };
+}
+
+/// Represents a buffer that is offset from some base pointer.
+/// Offset-based structures should use this as their initialization
+/// parameter so that they can know what segment of memory they own
+/// while at the same time initializing their offset fields to be
+/// against the true base.
+///
+/// The term "true base" is used to describe the base address of
+/// the allocation, which i.e. can include memory that you do NOT
+/// own and is used by some other structures. All offsets are against
+/// this "true base" so that to determine addresses structures don't
+/// need to add up all the intermediary offsets.
+pub const OffsetBuf = struct {
+ /// The true base pointer to the backing memory. This is
+ /// "byte zero" of the allocation. This plus the offset make
+ /// it easy to pass in the base pointer in all usage to this
+ /// structure and the offsets are correct.
+ base: [*]u8,
+
+ /// Offset from base where the beginning of /this/ data
+ /// structure is located. We use this so that we can slowly
+ /// build up a chain of offset-based structures but always
+ /// have the base pointer sent into functions be the true base.
+ offset: usize = 0,
+
+ /// Initialize a zero-offset buffer from a base.
+ pub fn init(base: anytype) OffsetBuf {
+ return initOffset(base, 0);
+ }
+
+ /// Initialize from some base pointer and offset.
+ pub fn initOffset(base: anytype, offset: usize) OffsetBuf {
+ return .{
+ .base = @ptrFromInt(intFromBase(base)),
+ .offset = offset,
+ };
+ }
+
+ /// The base address for the start of the data for the user
+ /// of this OffsetBuf. This is where your data structure should
+ /// begin; anything before this is NOT your memory.
+ pub fn start(self: OffsetBuf) [*]u8 {
+ const ptr = self.base + self.offset;
+ return @ptrCast(ptr);
+ }
+
+ /// Returns an Offset calculation for some child member of
+ /// your struct. The offset is against the true base pointer
+ /// so that future callers can pass that in as the base.
+ pub fn member(
+ self: OffsetBuf,
+ comptime T: type,
+ len: usize,
+ ) Offset(T) {
+ return .{ .offset = @intCast(self.offset + len) };
+ }
+
+ /// Add an offset to the current offset.
+ pub fn add(self: OffsetBuf, offset: usize) OffsetBuf {
+ return .{
+ .base = self.base,
+ .offset = self.offset + offset,
+ };
+ }
+
+ /// Rebase the offset to have a zero offset by rebasing onto start.
+ /// This is similar to `add` but all of the offsets are merged into base.
+ pub fn rebase(self: OffsetBuf, offset: usize) OffsetBuf {
+ return .{
+ .base = self.start() + offset,
+ .offset = 0,
+ };
+ }
+};
+
+/// Get the offset for a given type from some base pointer to the
+/// actual pointer to the type.
+pub inline fn getOffset(
+ comptime T: type,
+ base: anytype,
+ ptr: *const T,
+) Offset(T) {
+ const base_int = intFromBase(base);
+ const ptr_int = @intFromPtr(ptr);
+ const offset = ptr_int - base_int;
+ return .{ .offset = @intCast(offset) };
+}
+
+inline fn intFromBase(base: anytype) usize {
+ const T = @TypeOf(base);
+ return switch (@typeInfo(T)) {
+ .pointer => |v| switch (v.size) {
+ .one,
+ .many,
+ .c,
+ => @intFromPtr(base),
+
+ .slice => @intFromPtr(base.ptr),
+ },
+
+ else => switch (T) {
+ OffsetBuf => @intFromPtr(base.base),
+
+ else => @compileError("invalid base type"),
+ },
+ };
+}
+
+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 == u32);
+}
+
+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 = std.mem.alignForward(usize, @intFromPtr(&offset), @alignOf(Struct));
+ const base: [*]u8 = @ptrFromInt(base_int);
+ const actual = offset.ptr(base);
+ try testing.expectEqual(@as(usize, base_int + offset.offset), @intFromPtr(actual));
+}
+
+test "getOffset bytes" {
+ const testing = std.testing;
+ var widgets: []const u8 = "ABCD";
+ const offset = getOffset(u8, widgets.ptr, &widgets[2]);
+ try testing.expectEqual(@as(OffsetInt, 2), offset.offset);
+}
+
+test "getOffset structs" {
+ const testing = std.testing;
+ const Widget = struct { x: u32, y: u32 };
+ const widgets: []const Widget = &.{
+ .{ .x = 1, .y = 2 },
+ .{ .x = 3, .y = 4 },
+ .{ .x = 5, .y = 6 },
+ .{ .x = 7, .y = 8 },
+ .{ .x = 9, .y = 10 },
+ };
+ const offset = getOffset(Widget, widgets.ptr, &widgets[2]);
+ try testing.expectEqual(
+ @as(OffsetInt, @sizeOf(Widget) * 2),
+ offset.offset,
+ );
+}