1use crate::ansi::TermEvent;
18use crate::palette::Rgb;
19use crate::screen::{Cell, Color, Screen, flags};
20
21pub const COLS: usize = 40;
22pub const ROWS: usize = 24;
23pub const FONT: u8 = 250;
25
26pub const PALETTE: [Rgb; 8] = [0x000000, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF];
28
29fn color(i: u8) -> Color {
30 Color::Rgb(PALETTE[(i & 7) as usize])
31}
32
33pub fn glyph(c: u8) -> u8 {
36 match c {
37 b'#' => GLYPH_POUND,
38 b'[' => 0x1B, b'\\' => GLYPH_HALF,
40 b']' => 0x1A, b'^' => 0x18, b'_' => b'#',
43 b'`' => GLYPH_DASH,
44 b'{' => GLYPH_QUARTER,
45 b'|' => GLYPH_BARS,
46 b'}' => GLYPH_THREE_QUARTERS,
47 b'~' => GLYPH_DIVIDE,
48 0x7F => GLYPH_BLOCK,
49 c => c,
50 }
51}
52
53pub const GLYPH_POUND: u8 = 0x01;
55pub const GLYPH_HALF: u8 = 0x02;
56pub const GLYPH_QUARTER: u8 = 0x03;
57pub const GLYPH_BARS: u8 = 0x04;
58pub const GLYPH_DASH: u8 = 0x05;
59pub const GLYPH_BLOCK: u8 = 0x06;
60pub const GLYPH_THREE_QUARTERS: u8 = 0x07;
61pub const GLYPH_DIVIDE: u8 = 0x0E;
62
63fn is_code(b: u8) -> bool {
65 b < 0x20
66}
67
68#[derive(Debug)]
69pub struct Viewdata {
70 page: [[u8; COLS]; ROWS],
73 x: usize,
74 y: usize,
75 escape: bool,
77}
78
79impl Default for Viewdata {
80 fn default() -> Viewdata {
81 Viewdata { page: [[b' '; COLS]; ROWS], x: 0, y: 0, escape: false }
82 }
83}
84
85impl Viewdata {
86 pub fn start(&mut self, s: &mut Screen) {
87 *self = Viewdata::default();
88 s.look.fonts[0] = FONT;
89 s.base_font = FONT;
90 s.autowrap = false;
91 s.ice_colors = false;
92 self.draw(s);
93 }
94
95 pub fn feed(&mut self, s: &mut Screen, data: &[u8], events: &mut Vec<TermEvent>) {
97 for &b in data {
98 self.byte(s, b, events);
99 }
100 self.draw(s);
101 }
102
103 fn byte(&mut self, s: &mut Screen, b: u8, events: &mut Vec<TermEvent>) {
104 let b = if b >= 0xA0 { b & 0x7F } else { b };
105 if self.escape {
106 self.escape = false;
107 if (0x40..=0x5F).contains(&b) {
110 self.put(b - 0x40);
111 }
112 return;
113 }
114 match b {
115 0x07 => events.push(TermEvent::Bell),
116 0x08 => self.back(),
117 0x09 => self.forward(),
118 0x0A => self.y = (self.y + 1) % ROWS,
119 0x0B => self.y = (self.y + ROWS - 1) % ROWS,
120 0x0C => {
121 self.page = [[b' '; COLS]; ROWS];
122 (self.x, self.y) = (0, 0);
123 }
124 0x0D => self.x = 0,
125 0x11 => s.cursor_visible = true,
126 0x14 => s.cursor_visible = false,
127 0x1B => self.escape = true,
128 0x1E => (self.x, self.y) = (0, 0),
129 0x20..=0x7F => self.put(b),
130 0x80..=0x9F => self.put(b - 0x80),
132 _ => {}
133 }
134 }
135
136 fn put(&mut self, b: u8) {
137 self.page[self.y][self.x] = b;
138 self.forward();
139 }
140
141 fn forward(&mut self) {
143 self.x += 1;
144 if self.x == COLS {
145 self.x = 0;
146 self.y = (self.y + 1) % ROWS;
147 }
148 }
149
150 fn back(&mut self) {
151 if self.x == 0 {
152 self.x = COLS - 1;
153 self.y = (self.y + ROWS - 1) % ROWS;
154 } else {
155 self.x -= 1;
156 }
157 }
158
159 pub fn draw(&self, s: &mut Screen) {
161 let mut above: Option<[Cell; COLS]> = None;
162 for y in 0..ROWS.min(s.rows()) {
163 let (row, top) = draw_row(&self.page[y]);
164 let cells = match above.take() {
165 Some(top_cells) => top_cells.map(|c| {
167 if c.flags & flags::DOUBLE_TOP != 0 {
168 Cell { flags: (c.flags & !flags::DOUBLE_TOP) | flags::DOUBLE_BOTTOM, ..c }
169 } else {
170 Cell { ch: b' ', flags: c.flags & !flags::BLINK, ..c }
171 }
172 }),
173 None => {
174 if top && y + 1 < ROWS {
175 above = Some(row);
176 }
177 if top && y + 1 == ROWS {
178 row.map(|c| Cell { flags: c.flags & !flags::DOUBLE_TOP, ..c })
179 } else {
180 row
181 }
182 }
183 };
184 for (x, c) in cells.iter().enumerate() {
185 if let Some(cell) = s.cell_mut(x, y) {
186 *cell = *c;
187 }
188 }
189 }
190 s.move_to(self.x, self.y);
191 }
192}
193
194fn draw_row(page: &[u8; COLS]) -> ([Cell; COLS], bool) {
197 let (mut fg, mut bg) = (7u8, 0u8);
198 let (mut mosaic, mut separated, mut hold, mut flash, mut conceal, mut double) =
199 (false, false, false, false, false, false);
200 let mut held = b' ';
201 let mut any_double = false;
202 let mut cells = [Cell::default(); COLS];
203 for (x, &b) in page.iter().enumerate() {
204 let ch = if is_code(b) {
205 match b {
207 0x09 => flash = false,
208 0x0C => {
209 double = false;
210 hold = false;
211 held = b' ';
212 }
213 0x18 => conceal = true,
214 0x19 => separated = false,
215 0x1A => separated = true,
216 0x1C => bg = 0,
217 0x1D => bg = fg,
218 0x1E => hold = true,
219 _ => {}
220 }
221 if hold && mosaic { held } else { b' ' }
222 } else if mosaic && (b & 0x20 != 0) {
223 let pattern = (b & 0x1F) | ((b & 0x40) >> 1);
225 let g = if separated { 0xC0 } else { 0x80 } | pattern;
226 held = g;
227 g
228 } else {
229 glyph(b)
230 };
231 let mut f = 0;
232 if flash {
233 f |= flags::BLINK;
234 }
235 if conceal {
236 f |= flags::CONCEAL;
237 }
238 if double {
239 f |= flags::DOUBLE_TOP;
240 }
241 cells[x] = Cell { ch, fg: color(fg), bg: color(bg), flags: f, tile: 0 };
242 if is_code(b) {
243 match b {
245 0x01..=0x07 => {
246 fg = b;
247 mosaic = false;
248 conceal = false;
249 hold = false;
250 held = b' ';
251 }
252 0x11..=0x17 => {
253 fg = b - 0x10;
254 mosaic = true;
255 conceal = false;
256 }
257 0x08 => flash = true,
258 0x0D => {
259 double = true;
260 any_double = true;
261 hold = false;
262 held = b' ';
263 }
264 0x1F => {
265 hold = false;
266 held = b' ';
267 }
268 _ => {}
269 }
270 }
271 }
272 (cells, any_double)
273}
274
275pub fn encode_char(c: char) -> Option<u8> {
278 match c {
279 '\r' | '#' => Some(0x5F),
280 '£' => Some(0x23),
281 c if c.is_ascii() && !c.is_ascii_control() => Some(c as u8),
282 _ => None,
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 fn screen(data: &[u8]) -> Screen {
291 let mut s = Screen::new(COLS, ROWS, 0);
292 let mut v = Viewdata::default();
293 v.start(&mut s);
294 v.feed(&mut s, data, &mut Vec::new());
295 s
296 }
297
298 #[test]
299 fn serial_attributes_take_a_cell_and_color_the_rest() {
300 let s = screen(b"\x1bAHi\x1bD\x1b]X");
302 let row = s.row(0);
303 assert_eq!(row[0].ch, b' ', "the code's own cell is a space");
304 assert_eq!((row[1].ch, row[1].fg), (b'H', color(1)));
305 assert_eq!(row[3].ch, b' ');
306 assert_eq!(row[4].bg, color(4));
308 assert_eq!((row[5].ch, row[5].fg, row[5].bg), (b'X', color(4), color(4)));
309 assert_eq!(s.row(1)[0].fg, color(7));
311 }
312
313 #[test]
314 fn mosaics_hold_and_separation() {
315 let s = screen(b"\x1bR\x7f!\x1bZ\x7f\x1b^\x1bS");
318 let row = s.row(0);
319 assert_eq!(row[1].ch, 0x80 | 0x3F);
320 assert_eq!(row[2].ch, 0x80 | 0x01);
321 assert_eq!(row[4].ch, 0xC0 | 0x3F, "separated");
322 assert_eq!(row[6].ch, 0xC0 | 0x3F);
324 let s = screen(b"\x1bQA");
326 assert_eq!(s.row(0)[1].ch, b'A');
327 }
328
329 #[test]
330 fn overwriting_a_code_recolors_the_row() {
331 let mut s = Screen::new(COLS, ROWS, 0);
332 let mut v = Viewdata::default();
333 v.start(&mut s);
334 v.feed(&mut s, b"\x1bAred text", &mut Vec::new());
335 assert_eq!(s.row(0)[5].fg, color(1));
336 v.feed(&mut s, b"\r\x1bB", &mut Vec::new());
338 assert_eq!(s.row(0)[5].fg, color(2));
339 }
340
341 #[test]
342 fn double_height_and_the_page_wrapping() {
343 let s = screen(b"\x1bMBig\r\nsmall");
344 assert!(s.row(0)[1].flags & flags::DOUBLE_TOP != 0);
345 assert_eq!(s.row(1)[1].ch, b'B');
347 assert!(s.row(1)[1].flags & flags::DOUBLE_BOTTOM != 0);
348 let s = screen(b"\x0b#");
350 assert_eq!(s.row(ROWS - 1)[0].ch, GLYPH_POUND);
351 assert_eq!(s.cursor(), (1, ROWS - 1));
352 let mut data = vec![0x1E, 0x0B];
354 data.extend([b'x'; COLS]);
355 assert_eq!(screen(&data).cursor(), (0, 0));
356 }
357}