sericom_core/screen_buffer/
mod.rs1mod cell;
21mod cursor;
22mod escape;
23mod line;
24mod render;
25mod ui_command;
26use cell::*;
27use cursor::*;
28use escape::{EscapeSequence, EscapeState};
29use line::*;
30pub use ui_command::*;
31
32use std::collections::VecDeque;
33
34#[derive(Debug)]
38pub struct ScreenBuffer {
39 width: u16,
41 height: u16,
43 lines: VecDeque<Line>,
46 view_start: usize,
49 cursor_pos: Position,
51 selection_start: Option<(u16, usize)>,
53 selection_end: Option<(u16, usize)>,
55 max_scrollback: usize,
57 escape_state: EscapeState,
60 escape_sequence: EscapeSequence,
63 last_render: Option<tokio::time::Instant>,
65 needs_render: bool,
67}
68
69impl ScreenBuffer {
70 pub fn new(width: u16, height: u16, max_scrollback: usize) -> Self {
73 let mut buffer = Self {
74 width,
75 height,
76 lines: VecDeque::new(),
77 view_start: 0,
78 cursor_pos: Position::home(),
79 selection_start: None,
80 selection_end: None,
81 max_scrollback,
82 last_render: None,
83 needs_render: false,
84 escape_state: EscapeState::Normal,
85 escape_sequence: EscapeSequence::new(),
86 };
87 buffer.lines.push_back(Line::new(width as usize));
89 buffer
90 }
91
92 fn set_char_at_cursor(&mut self, ch: char) {
93 while self.cursor_pos.y >= self.lines.len() {
94 self.lines.push_back(Line::new(self.width as usize));
95 }
96
97 if let Some(line) = self.lines.get_mut(self.cursor_pos.y)
98 && (self.cursor_pos.x as usize) < line.len()
99 {
100 line.set_char(self.cursor_pos.x as usize, ch);
101 }
102 }
103
104 fn clear_from_cursor_to_sol(&mut self) {
105 if let Some(line) = self.lines.get_mut(self.cursor_pos.y) {
106 line.reset_to_idx(self.cursor_pos.x as usize);
107 }
108 }
109
110 fn clear_from_cursor_to_sos(&mut self) {
111 self.clear_from_cursor_to_sol();
112 for line in self
113 .lines
114 .range_mut(self.view_start..=self.cursor_pos.y - 1)
115 {
116 line.reset();
117 }
118 }
119
120 fn clear_from_cursor_to_eol(&mut self) {
121 if let Some(line) = self.lines.get_mut(self.cursor_pos.y) {
122 line.reset_from_idx(self.cursor_pos.x as usize);
123 }
124 }
125
126 fn clear_from_cursor_to_eos(&mut self) {
127 self.clear_from_cursor_to_eol();
128 for line in self.lines.range_mut(self.cursor_pos.y + 1..) {
129 line.reset();
130 }
131 }
132
133 fn clear_whole_line(&mut self) {
134 if let Some(line) = self.lines.get_mut(self.cursor_pos.y) {
135 line.reset();
136 }
137 }
138
139 fn new_line(&mut self) {
140 self.set_cursor_pos((0, self.cursor_pos.y + 1));
141
142 if self.cursor_pos.y >= self.lines.len() {
143 self.lines.push_back(Line::new(self.width as usize));
144 }
145
146 while self.lines.len() > self.max_scrollback {
148 self.lines.pop_front();
149 if self.cursor_pos.y > 0 {
151 self.cursor_pos.y -= 1;
152 }
153 if self.view_start > 0 {
154 self.view_start -= 1;
155 }
156 }
157 }
158
159 #[allow(dead_code)]
160 pub fn get_stats(&self) -> BufferStats {
161 let total_lines = self.lines.len();
162 BufferStats {
163 total_lines,
164 view_start: self.view_start,
165 view_end: (self.view_start + self.height as usize).min(total_lines),
166 cursor_line: self.cursor_pos.y,
167 has_selection: self.selection_start.is_some() && self.selection_end.is_some(),
168 }
169 }
170}
171
172#[allow(dead_code)]
173#[derive(Debug)]
174pub struct BufferStats {
175 pub total_lines: usize,
176 pub view_start: usize,
177 pub view_end: usize,
178 pub cursor_line: usize,
179 pub has_selection: bool,
180}