Skip to main content

justerm_core/
cursor.rs

1//! The cursor and its drawing pen.
2
3use crate::cell::{Cell, CellFlags};
4use crate::color::Color;
5
6/// The current SGR state — the appearance copied into each printed cell.
7///
8/// Modelling it as a "template cell" mirrors Alacritty: a later slice can make
9/// erase (ED/EL) fill cleared cells with `bg` instead of `Default` and that
10/// *is* Background Color Erase (BCE), no structural change. See `term.rs`.
11#[derive(Clone, Copy, Debug, Default)]
12pub struct Pen {
13    pub fg: Color,
14    pub bg: Color,
15    pub flags: CellFlags,
16}
17
18impl Pen {
19    /// Reset to default appearance (SGR 0).
20    pub fn reset(&mut self) {
21        *self = Pen::default();
22    }
23
24    /// Build a cell carrying this pen's appearance and the given glyph.
25    pub fn cell(&self, c: char) -> Cell {
26        Cell::from_parts(c, self.fg, self.bg, self.flags)
27    }
28}
29
30/// The cursor's drawn shape (DECSCUSR / the renderer's caret glyph). The engine
31/// reports it on the frame; the renderer draws it. Default `Block` (#81).
32#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
33pub enum CursorShape {
34    #[default]
35    Block,
36    Underline,
37    Bar,
38}
39
40/// The input position, its pending-wrap state, and the current pen.
41#[derive(Clone, Copy, Debug)]
42pub struct Cursor {
43    pub row: usize,
44    pub col: usize,
45    /// Deferred last-column wrap (xterm's "wrapnext"). Set when a print fills the
46    /// last column: the cursor stays put and the actual line wrap happens on the
47    /// *next* print. Eager wrapping here is the classic off-by-one that shifts
48    /// lines (see `docs/architecture.md` "Hidden VT state").
49    pub pending_wrap: bool,
50    pub pen: Pen,
51    /// Whether the cursor is shown (DEC ?25). The engine only reports it.
52    pub visible: bool,
53    /// The caret shape (DECSCUSR, #89) — reported on the frame, drawn by the
54    /// renderer.
55    pub shape: CursorShape,
56    /// Whether the caret blinks (att610 ?12, #81). The engine reports the *mode*;
57    /// the actual animation is the renderer's.
58    pub blink: bool,
59}
60
61impl Cursor {
62    /// The cursor's `(row, col)` position.
63    pub(crate) fn point(&self) -> (usize, usize) {
64        (self.row, self.col)
65    }
66
67    /// Set the position, clamped to a `rows` x `cols` screen.
68    pub(crate) fn set_point(&mut self, point: (usize, usize), rows: usize, cols: usize) {
69        self.row = point.0.min(rows - 1);
70        self.col = point.1.min(cols - 1);
71    }
72}
73
74impl Default for Cursor {
75    fn default() -> Self {
76        // The cursor starts visible; a manual impl is needed because `bool`'s
77        // derived default is `false`.
78        Cursor {
79            row: 0,
80            col: 0,
81            pending_wrap: false,
82            pen: Pen::default(),
83            visible: true,
84            shape: CursorShape::Block,
85            blink: false,
86        }
87    }
88}