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 /// The underline colour (SGR 58, #520): what an underline / strikethrough draws
17 /// in, independent of `fg`. `Default` means "follow the fg". It is *not* packed
18 /// into the printed `Cell` (the 12-byte cell is full); the print path stamps a
19 /// non-default value into the row's ucolor map. See `term.rs::write_glyph`.
20 pub underline_color: Color,
21}
22
23impl Pen {
24 /// Reset to default appearance (SGR 0).
25 pub fn reset(&mut self) {
26 *self = Pen::default();
27 }
28
29 /// Build a cell carrying this pen's appearance and the given glyph.
30 pub fn cell(&self, c: char) -> Cell {
31 Cell::from_parts(c, self.fg, self.bg, self.flags)
32 }
33}
34
35/// The cursor's drawn shape (DECSCUSR / the renderer's caret glyph). The engine
36/// reports it on the frame; the renderer draws it. Default `Block` (#81).
37#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
38pub enum CursorShape {
39 #[default]
40 Block,
41 Underline,
42 Bar,
43}
44
45/// The input position, its pending-wrap state, and the current pen.
46#[derive(Clone, Copy, Debug)]
47pub struct Cursor {
48 pub row: usize,
49 pub col: usize,
50 /// Deferred last-column wrap (xterm's "wrapnext"). Set when a print fills the
51 /// last column: the cursor stays put and the actual line wrap happens on the
52 /// *next* print. Eager wrapping here is the classic off-by-one that shifts
53 /// lines (see `docs/architecture.md` "Hidden VT state").
54 pub pending_wrap: bool,
55 pub pen: Pen,
56 /// Whether the cursor is shown (DEC ?25). The engine only reports it.
57 pub visible: bool,
58 /// The caret shape (DECSCUSR, #89) — reported on the frame, drawn by the
59 /// renderer.
60 pub shape: CursorShape,
61 /// Whether the caret blinks (att610 ?12, #81). The engine reports the *mode*;
62 /// the actual animation is the renderer's.
63 pub blink: bool,
64}
65
66impl Cursor {
67 /// The cursor's `(row, col)` position.
68 pub(crate) fn point(&self) -> (usize, usize) {
69 (self.row, self.col)
70 }
71
72 /// Set the position, clamped to a `rows` x `cols` screen.
73 pub(crate) fn set_point(&mut self, point: (usize, usize), rows: usize, cols: usize) {
74 self.row = point.0.min(rows - 1);
75 self.col = point.1.min(cols - 1);
76 }
77}
78
79impl Default for Cursor {
80 fn default() -> Self {
81 // The cursor starts visible; a manual impl is needed because `bool`'s
82 // derived default is `false`.
83 Cursor {
84 row: 0,
85 col: 0,
86 pending_wrap: false,
87 pen: Pen::default(),
88 visible: true,
89 shape: CursorShape::Block,
90 blink: false,
91 }
92 }
93}