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}