const std = @import("std"); const ghostty_vt = @import("ghostty-vt"); /// One text selection (block-shaped), in pane-local coords; the row carries the /// scrollback offset. .none == nothing selected. pub const Sel = struct { state: enum { none, dragging, done } = .none, c0: i32 = 0, c1: i32 = 0, r0: i32 = 0, r1: i32 = 0, }; pub const EDIT_CAP = 128; // max chars in one insertion run pub const MAX_EDITS = 16; // max concurrent insertion runs per window /// A run of inserted ("char mode") text. `col` is the ORIGINAL tty column it is /// spliced in *before* — the run shifts the underlying row right, it doesn't /// overwrite. `row`/`col` are pane-local (row carries the BOX_H tag offset). pub const Edit = struct { row: i32 = 0, col: i32 = 0, len: u16 = 0, text: [EDIT_CAP]u8 = undefined, }; /// Persistent per-terminal state: the pty handle, the ghostty emulator and its /// (stateful) VT parser, the background reader task, plus its layout weight and /// text selection. /// /// Heap-allocate these and never move them: `stream`'s handler holds a pointer /// back into `term`, so the address must stay stable for parser state split /// across pty reads to keep working. pub const Term = @This(); pty: std.Io.File, pid: std.posix.pid_t, // the shell child; used to read /proc//cwd for the tag term: ghostty_vt.Terminal, stream: ghostty_vt.TerminalStream, reader: std.Io.Future(anyerror!void), /// current emulator grid size; we only push a resize when it actually changes. cols: u16, rows: u16, /// vertical weight within the column (siblings split the column height by this). vweight: f32 = 1, /// One independent selection per mouse button: 0=left, 1=middle, 2=right. sel: [3]Sel = .{ .{}, .{}, .{} }, /// modal input. true = "text"/char mode (DEFAULT; keys insert text at the /// cursor, shell prompts hidden), false = "tty"/sym mode (keys -> pty as /// terminal sequences, prompts shown). Esc toggles to tty; a left-click returns /// to text mode and anchors the cursor. /// /// The cursor and edits are anchored at ABSOLUTE body rows (visual row + scroll /// offset) so they ride the scrollback, not the screen. `cur_off` is the offset /// into the run sitting at (cur_row, cur_col). `cur_pinned` is false until the /// user clicks or types, while which the cursor tracks the shell's cursor. editing: bool = true, cur_pinned: bool = false, cur_row: i32 = 0, cur_col: i32 = 0, cur_off: u16 = 0, nedits: usize = 0, edits: [MAX_EDITS]Edit = @splat(.{}), pub fn deinit(self: *Term, io: std.Io, gpa: std.mem.Allocator) void { _ = self.reader.cancel(io) catch {}; self.stream.deinit(); self.term.deinit(gpa); self.pty.close(io); }