//! Render state: a snapshot of the terminal state required to render a frame. //! //! Port of `ZIG/src/terminal/render.zig`. //! //! Developer note: this is in the terminal crate and not the renderer crate //! because the goal is that this remains generic to multiple renderers. //! //! Contains the state required to render the screen, including optimizing //! for repeated render calls and only rendering dirty regions. //! //! Previously, our renderer would use `clone` to clone the screen within //! the viewport to perform rendering. This worked well enough that we kept //! it all the way up through the Ghostty 1.2.x series, but the clone time //! was repeatedly a bottleneck blocking IO. //! //! Rather than a generic clone that tries to clone all screen state per call //! (within a region), a stateful approach that optimizes for only what a //! renderer needs to do makes more sense. //! //! To use this, initialize the render state to empty, then call `update` //! on each frame to update the state to the latest terminal state. //! //! ``` //! # use ghostty_terminal::{render::RenderState, terminal::{Terminal, Options}}; //! # let mut terminal = Terminal::new(Options::new(80, 24))?; //! let mut state = RenderState::new(); //! state.update(&mut terminal)?; //! # Ok::<(), ghostty_terminal::page::Error>(()) //! ``` //! //! Note: the render state retains as much memory as possible between updates //! to prevent future allocations. If a very large frame is rendered once, //! the render state will retain that much memory until dropped. To avoid //! waste, it is recommended that the caller drop and start with an //! empty render state every so often. use std::collections::HashSet; use ghostty_vt::{ color::{self, Palette, Rgb}, modes::Mode, }; use crate::{ cursor, highlight::Flattened, page::{self, ContentTag, Error, Wide}, page_list::{Direction, PageList, Pin}, point::{Coordinate, Point, Tag}, screen_set::Key as ScreenKey, selection::Selection, size, style::{Color, Style}, terminal::{Dirty as TerminalDirty, Terminal}, }; /// Contains the state required to render the screen. See the module docs. #[derive(Debug, Clone)] pub struct RenderState { /// The current screen dimensions. It is possible that these don't match /// the renderer's current dimensions in grid cells because resizing /// can happen asynchronously. This can lead to a one or two frame /// mismatch a renderer needs to handle. /// /// The viewport is always exactly equal to the active area size so this /// is also the viewport size. pub rows: size::CellCountInt, pub cols: size::CellCountInt, /// The color state for the terminal. pub colors: Colors, /// Cursor state within the viewport. pub cursor: Cursor, /// The rows (y=0 is top) of the viewport. Guaranteed to be `rows` length. pub row_data: Vec, /// The dirty state of the render state. This is set by the update method. /// The renderer/caller should set this to false when it has handled /// the dirty state. pub dirty: Dirty, /// The screen type that this state represents. This is used primarily /// to detect changes. pub screen: ScreenKey, /// The last viewport pin used to generate this state. This is NOT /// a tracked pin and is generally NOT safe to read other than the direct /// values for comparison. pub viewport_pin: Option, /// The cached selection so we can avoid expensive selection calculations /// if possible. selection_cache: Option, } /// The color state for the terminal. /// /// The background/foreground will be reversed if the terminal reverse /// color mode is on! You do not need to handle that manually! #[derive(Debug, Clone, Copy)] pub struct Colors { pub background: Rgb, pub foreground: Rgb, pub cursor: Option, pub palette: Palette, } #[derive(Debug, Clone, Copy)] pub struct Cursor { /// The x/y position of the cursor within the active area. pub active: Coordinate, /// The x/y position of the cursor within the viewport. This /// may be None if the cursor is not visible within the viewport. pub viewport: Option, /// The cell data for the cursor position. Managed memory is not /// safe to access from this. pub cell: page::Cell, /// The style, always valid even if the cell is default style. pub style: Style, /// The visual style of the cursor itself, such as a block or bar. pub visual_style: cursor::Style, /// True if the cursor is detected to be at a password input field. pub password_input: bool, /// Cursor visibility state determined by the terminal mode. pub visible: bool, /// Cursor blink state determined by the terminal mode. pub blinking: bool, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct CursorViewport { /// The x/y position of the cursor within the viewport. pub x: size::CellCountInt, pub y: size::CellCountInt, /// Whether the cursor is part of a wide character and /// on the tail of it. If so, some renderers may use this /// to move the cursor back one. pub wide_tail: bool, } /// A row within the viewport. #[derive(Debug, Clone)] pub struct Row { /// The page pin. This is not safe to read unless you can guarantee /// the terminal state hasn't changed since the last `update` call. pub pin: Pin, /// Raw row data. pub raw: page::Row, /// The cells in this row. Guaranteed to be `cols` length. pub cells: Cells, /// A dirty flag that can be used by the renderer to track /// its own draw state. `update` will mark this true whenever /// this row is changed, too. pub dirty: bool, /// The x range of the selection within this row. pub selection: Option<[size::CellCountInt; 2]>, /// The highlights within this row. pub highlights: Vec, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Highlight { /// A special tag that can be used by the caller to differentiate /// different highlight types. The value is opaque to the RenderState. pub tag: u8, /// The x ranges of highlights within this row. pub range: [size::CellCountInt; 2], } /// One cell of a row (the Zig `RenderState.Cell`), by reference. #[derive(Debug, Clone, Copy)] pub struct Cell<'a> { /// Always set, this is the raw copied cell data from page.Cell. /// The managed memory (hyperlinks, graphemes, etc.) is NOT safe /// to access from here. It is duplicated into the other fields if /// it exists. pub raw: page::Cell, /// Grapheme data for the cell (the codepoints after the first). Empty /// unless the raw cell's content_tag is `CodepointGrapheme`. pub grapheme: &'a [u32], /// The style data for the cell. The default style unless the style_id /// is non-default on raw (or the cell is a bg-color-only cell). pub style: Style, } /// The cells of a row, stored as a struct of arrays (the Zig /// `MultiArrayList(Cell)`) so that the common plain-text case only copies the /// raw cells. /// /// `raw` always has exactly `cols` entries. `grapheme` and `style_ids` either /// have exactly `cols` entries, or are empty when the source row has no /// managed memory (in which case every cell has no grapheme and the default /// style). Styles are stored sparsely: `style_ids[x]` is 0 for the default /// style, otherwise `styles[style_ids[x] - 1]`. Always use the accessors /// rather than indexing those directly. #[derive(Debug, Clone, Default)] pub struct Cells { pub raw: Vec, pub grapheme: Vec>, pub style_ids: Vec, pub styles: Vec