mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 00:36:07 +03:00
starting the new screen implementation
This commit is contained in:
167
src/terminal/Screen2.zig
Normal file
167
src/terminal/Screen2.zig
Normal file
@ -0,0 +1,167 @@
|
||||
//! Screen represents the internal storage for a terminal screen, including
|
||||
//! scrollback. This is implemented as a single continuous ring buffer.
|
||||
//!
|
||||
//! Definitions:
|
||||
//!
|
||||
//! * Screen - The full screen (active + history).
|
||||
//! * Active - The area that is the current edit-able screen (the
|
||||
//! bottom of the scrollback). This is "edit-able" because it is
|
||||
//! the only part that escape sequences such as set cursor position
|
||||
//! actually affect.
|
||||
//! * History - The area that contains the lines prior to the active
|
||||
//! area. This is the scrollback area. Escape sequences can no longer
|
||||
//! affect this area.
|
||||
//! * Viewport - The area that is currently visible to the user. This
|
||||
//! can be thought of as the current window into the screen.
|
||||
//!
|
||||
const Screen = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const color = @import("color.zig");
|
||||
const CircBuf = @import("circ_buf.zig").CircBuf;
|
||||
|
||||
const log = std.log.scoped(.screen);
|
||||
|
||||
/// This is a single item within the storage buffer. We use a union to
|
||||
/// have different types of data in a single contiguous buffer.
|
||||
///
|
||||
/// Note: the union is extern so that it follows the same memory layout
|
||||
/// semantics as C, which allows us to have a tightly packed union.
|
||||
const StorageCell = extern union {
|
||||
row_header: RowHeader,
|
||||
cell: Cell,
|
||||
|
||||
test {
|
||||
// log.warn("header={}@{} cell={}@{} storage={}@{}", .{
|
||||
// @sizeOf(RowHeader),
|
||||
// @alignOf(RowHeader),
|
||||
// @sizeOf(Cell),
|
||||
// @alignOf(Cell),
|
||||
// @sizeOf(StorageCell),
|
||||
// @alignOf(StorageCell),
|
||||
// });
|
||||
|
||||
// We want to be at most the size of a cell always. We have WAY
|
||||
// more cells than other fields, so we don't want to pay the cost
|
||||
// of padding due to other fields.
|
||||
try std.testing.expectEqual(@sizeOf(Cell), @sizeOf(StorageCell));
|
||||
}
|
||||
};
|
||||
|
||||
/// The row header is at the start of every row within the storage buffer.
|
||||
/// It can store row-specific data.
|
||||
const RowHeader = struct {
|
||||
dirty: bool,
|
||||
|
||||
/// If true, this row is soft-wrapped. The first cell of the next
|
||||
/// row is a continuous of this row.
|
||||
wrap: bool,
|
||||
};
|
||||
|
||||
/// Cell is a single cell within the screen.
|
||||
const Cell = struct {
|
||||
/// The primary unicode codepoint for this cell. Most cells (almost all)
|
||||
/// contain exactly one unicode codepoint. However, it is possible for
|
||||
/// cells to contain multiple if multiple codepoints are used to create
|
||||
/// a single grapheme cluster.
|
||||
///
|
||||
/// In the case multiple codepoints make up a single grapheme, the
|
||||
/// additional codepoints can be looked up in the hash map on the
|
||||
/// Screen. Since multi-codepoints graphemes are rare, we don't want to
|
||||
/// waste memory for every cell, so we use a side lookup for it.
|
||||
char: u32,
|
||||
|
||||
/// Foreground and background color. attrs.has_{bg/fg} must be checked
|
||||
/// to see if these are useful values.
|
||||
fg: color.RGB = undefined,
|
||||
bg: color.RGB = undefined,
|
||||
|
||||
/// On/off attributes that can be set
|
||||
attrs: packed struct {
|
||||
has_bg: bool = false,
|
||||
has_fg: bool = false,
|
||||
|
||||
bold: bool = false,
|
||||
faint: bool = false,
|
||||
underline: bool = false,
|
||||
inverse: bool = false,
|
||||
|
||||
/// True if this is a wide character. This char takes up
|
||||
/// two cells. The following cell ALWAYS is a space.
|
||||
wide: bool = false,
|
||||
|
||||
/// Notes that this only exists to be blank for a preceeding
|
||||
/// wide character (tail) or following (head).
|
||||
wide_spacer_tail: bool = false,
|
||||
wide_spacer_head: bool = false,
|
||||
} = .{},
|
||||
|
||||
/// True if the cell should be skipped for drawing
|
||||
pub fn empty(self: Cell) bool {
|
||||
return self.char == 0;
|
||||
}
|
||||
|
||||
test {
|
||||
// We use this test to ensure we always get the right size of the attrs
|
||||
// const cell: Cell = .{ .char = 0 };
|
||||
// _ = @bitCast(u8, cell.attrs);
|
||||
// try std.testing.expectEqual(1, @sizeOf(@TypeOf(cell.attrs)));
|
||||
}
|
||||
|
||||
test {
|
||||
//log.warn("CELL={} {}", .{ @sizeOf(Cell), @alignOf(Cell) });
|
||||
try std.testing.expectEqual(12, @sizeOf(Cell));
|
||||
}
|
||||
};
|
||||
|
||||
const StorageBuf = CircBuf(StorageCell);
|
||||
|
||||
/// The allocator used for all the storage operations
|
||||
alloc: Allocator,
|
||||
|
||||
/// The full set of storage.
|
||||
storage: StorageBuf,
|
||||
|
||||
/// The number of rows and columns in the visible space.
|
||||
rows: usize,
|
||||
cols: usize,
|
||||
|
||||
/// The maximum number of lines that are available in scrollback. This
|
||||
/// is in addition to the number of visible rows.
|
||||
max_scrollback: usize,
|
||||
|
||||
/// Initialize a new screen.
|
||||
pub fn init(
|
||||
alloc: Allocator,
|
||||
rows: usize,
|
||||
cols: usize,
|
||||
max_scrollback: usize,
|
||||
) !Screen {
|
||||
// * Our buffer size is preallocated to fit double our visible space
|
||||
// or the maximum scrollback whichever is smaller.
|
||||
// * We add +1 to cols to fit the row header
|
||||
const buf_size = (rows + @minimum(max_scrollback, rows)) * (cols + 1);
|
||||
|
||||
return Screen{
|
||||
.alloc = alloc,
|
||||
.storage = try StorageBuf.init(alloc, buf_size),
|
||||
.rows = rows,
|
||||
.cols = cols,
|
||||
.max_scrollback = max_scrollback,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Screen) void {
|
||||
self.storage.deinit(self.alloc);
|
||||
}
|
||||
|
||||
test {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var s = try init(alloc, 3, 5, 0);
|
||||
defer s.deinit();
|
||||
}
|
@ -27,18 +27,7 @@ pub const EraseLine = csi.EraseLine;
|
||||
pub const TabClear = csi.TabClear;
|
||||
pub const Attribute = sgr.Attribute;
|
||||
|
||||
test {
|
||||
_ = ansi;
|
||||
_ = charsets;
|
||||
_ = color;
|
||||
_ = csi;
|
||||
_ = point;
|
||||
_ = sgr;
|
||||
_ = stream;
|
||||
_ = Parser;
|
||||
_ = Selection;
|
||||
_ = Terminal;
|
||||
_ = Screen;
|
||||
pub const Screen2 = @import("Screen2.zig");
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
|
Reference in New Issue
Block a user