terminal: convert Point to lib.Enum/lib.TaggedUnion with C header

This commit is contained in:
Mitchell Hashimoto
2026-03-19 13:59:48 -07:00
parent 5c8b9f3f43
commit 057f227145
2 changed files with 139 additions and 36 deletions

View File

@@ -0,0 +1,88 @@
/**
* @file point.h
*
* Terminal point types for referencing locations in the terminal grid.
*/
#ifndef GHOSTTY_VT_POINT_H
#define GHOSTTY_VT_POINT_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/** @defgroup point Point
*
* Types for referencing x/y positions in the terminal grid under
* different coordinate systems (active area, viewport, full screen,
* scrollback history).
*
* @{
*/
/**
* A coordinate in the terminal grid.
*
* @ingroup point
*/
typedef struct {
/** Column (0-indexed). */
uint16_t x;
/** Row (0-indexed). May exceed page size for screen/history tags. */
uint32_t y;
} GhosttyPointCoordinate;
/**
* Point reference tag.
*
* Determines which coordinate system a point uses.
*
* @ingroup point
*/
typedef enum {
/** Active area where the cursor can move. */
GHOSTTY_POINT_TAG_ACTIVE = 0,
/** Visible viewport (changes when scrolled). */
GHOSTTY_POINT_TAG_VIEWPORT = 1,
/** Full screen including scrollback. */
GHOSTTY_POINT_TAG_SCREEN = 2,
/** Scrollback history only (before active area). */
GHOSTTY_POINT_TAG_HISTORY = 3,
} GhosttyPointTag;
/**
* Point value union.
*
* @ingroup point
*/
typedef union {
/** Coordinate (used for all tag variants). */
GhosttyPointCoordinate coordinate;
/** Padding for ABI compatibility. Do not use. */
uint64_t _padding[2];
} GhosttyPointValue;
/**
* Tagged union for a point in the terminal grid.
*
* @ingroup point
*/
typedef struct {
GhosttyPointTag tag;
GhosttyPointValue value;
} GhosttyPoint;
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* GHOSTTY_VT_POINT_H */

View File

@@ -1,52 +1,56 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const build_options = @import("terminal_options");
const lib = @import("../lib/main.zig");
const size = @import("size.zig");
const lib_target: lib.Target = if (build_options.c_abi) .c else .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 = enum {
/// 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,
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 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,
// 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,
};
// 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) {
@@ -64,6 +68,17 @@ pub const Point = union(Tag) {
=> |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 {