const std = @import("std"); const Allocator = std.mem.Allocator; const lib = @import("lib.zig"); const size = @import("size.zig"); /// The possible reference locations for a point. When someone says "(42, 80)" /// in the context of a terminal, that could mean multiple things: it is in the /// current visible viewport? the current active area of the screen where the /// cursor is? the entire scrollback history? etc. /// /// This tag is used to differentiate those cases. pub const Tag = lib.Enum(lib.target, &.{ // Top-left is part of the active area where a running program can // jump the cursor and make changes. The active area is the "editable" // part of the screen. // // The bottom-right of the active tag differs from all other tags // because it includes the full height (rows) of the screen, including // rows that may not be written yet. This is required because the active // area is fully "addressable" by the running program (see below) whereas // the other tags are used primarily for reading/modifying past-written // data so they can't address unwritten rows. // // Note for those less familiar with terminal functionality: there // are escape sequences to move the cursor to any position on // the screen, but it is limited to the size of the viewport and // the bottommost part of the screen. Terminal programs can't -- // with sequences at the time of writing this comment -- modify // anything in the scrollback, visible viewport (if it differs // from the active area), etc. "active", // Top-left is the visible viewport. This means that if the user // has scrolled in any direction, top-left changes. The bottom-right // is the last written row from the top-left. "viewport", // Top-left is the furthest back in the scrollback history // supported by the screen and the bottom-right is the bottom-right // of the last written row. Note this last point is important: the // bottom right is NOT necessarily the same as "active" because // "active" always allows referencing the full rows tall of the // screen whereas "screen" only contains written rows. "screen", // The top-left is the same as "screen" but the bottom-right is // the line just before the top of "active". This contains only // the scrollback history. "history", }); /// An x/y point in the terminal for some definition of location (tag). pub const Point = union(Tag) { active: Coordinate, viewport: Coordinate, screen: Coordinate, history: Coordinate, pub inline fn coord(self: Point) Coordinate { return switch (self) { .active, .viewport, .screen, .history, => |v| v, }; } const c_union = lib.TaggedUnion( lib.target, @This(), // Padding: largest variant is Coordinate (u16 + u32 = 6 bytes). // Use [2]u64 (16 bytes) for future expansion. [2]u64, ); pub const C = c_union.C; pub const CValue = c_union.CValue; pub const cval = c_union.cval; }; pub const Coordinate = extern struct { /// x can use size.CellCountInt because the number of columns /// can't ever be more than a valid number of columns in a Page. x: size.CellCountInt = 0, /// y does not use size.CellCountInt because certain coordinate /// usage such as screen/history can have more rows than are possible /// in a single page. y: u32 = 0, pub fn eql(self: Coordinate, other: Coordinate) bool { return self.x == other.x and self.y == other.y; } };