Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_tui/src/lib_tui/term/emu.rs

23.8 KiB, 31 runs

created by r1870400018:20805, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! The emulator: the parser and the screen, joined.
2//!
3//! [`Terminal`] is the type a caller holds. Bytes go in through [`Terminal::feed`] and a screen
4//! comes out through [`Terminal::screen`]. Everything else on the type exists because a terminal is
5//! not only a screen: it answers some questions the application asks of it, it reports a window
6//! title, it rings a bell, and it holds the mode flags that tell an input layer how to encode a key
7//! press.
8
9use crate::lib_tui::term::{
10 cell::{
11 NamedColour,
12 Pen,
13 TermColour,
14 ATTR_BLINK,
15 ATTR_BOLD,
16 ATTR_DIM,
17 ATTR_HIDDEN,
18 ATTR_ITALIC,
19 ATTR_REVERSE,
20 ATTR_STRIKE,
21 ATTR_UNDERLINE,
22 },
23 charset::{
24 Charset,
25 Charsets,
26 },
27 parse::{
28 Act,
29 Csi,
30 Esc,
31 Osc,
32 Params,
33 Parser,
34 C0,
35 },
36 screen::{
37 Damage,
38 Erase,
39 Screen,
40 Surface,
41 },
42};
43
44use oxedyne_fe2o3_core::prelude::*;
45
46
47/// The mode flags an input layer needs in order to encode what the user does.
48///
49/// None of these change the grid, which is why they live here rather than on the screen. They are
50/// recorded because an application that asks for them and does not get them behaves badly: a mouse
51/// click sent in the wrong encoding lands in the wrong place, and a paste sent without its
52/// brackets is executed line by line.
53#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
54pub struct Modes {
55 /// `DECCKM`, which asks for cursor keys as `SS3` rather than `CSI`.
56 pub app_cursor: bool,
57 /// `DECKPAM`, which asks for the keypad in application mode.
58 pub app_keypad: bool,
59 /// `?1000`, reporting button presses and releases.
60 pub mouse_button: bool,
61 /// `?1002`, reporting motion while a button is down.
62 pub mouse_drag: bool,
63 /// `?1003`, reporting all motion.
64 pub mouse_any: bool,
65 /// `?1006`, the `SGR` mouse encoding.
66 pub mouse_sgr: bool,
67 /// `?2004`, bracketed paste.
68 pub bracketed_paste: bool,
69 /// `?1004`, focus in and out reporting.
70 pub focus_events: bool,
71 /// `LNM`, which makes a line feed also return the carriage.
72 pub newline: bool,
73}
74
75/// A terminal: a byte stream in, a screen out.
76#[derive(Clone, Debug)]
77pub struct Terminal {
78 /// The byte stream state machine.
79 parser: Parser,
80 /// The grid and the cursor.
81 screen: Screen,
82 /// The buffer the parser writes into, reused between feeds.
83 acts: Vec<Act>,
84 /// The mode flags.
85 modes: Modes,
86 /// The window title the stream last asked for.
87 title: String,
88 /// Whether the title changed since a caller last took it.
89 title_dirty: bool,
90 /// How many bells have rung since a caller last took them.
91 bells: usize,
92 /// Bytes the application asked for, to be written back to the pseudoterminal.
93 replies: Vec<u8>,
94 /// The last character printed, which `REP` repeats.
95 last_print: Option<char>,
96 /// The designated character sets and which of them is in front.
97 sets: Charsets,
98 /// The character sets `DECSC` saved.
99 saved_sets: Option<Charsets>,
100}
101
102impl Terminal {
103
104 /// A terminal of the given size with the default scrollback bound.
105 ///
106 /// # Errors
107 /// Fails if either dimension is zero.
108 pub fn new(cols: usize, rows: usize) -> Outcome<Self> {
109 Ok(Self::from_screen(res!(Screen::new(cols, rows))))
110 }
111
112 /// A terminal of the given size, keeping at most `scrollback` lines.
113 ///
114 /// # Errors
115 /// Fails if either dimension is zero.
116 pub fn with_scrollback(cols: usize, rows: usize, scrollback: usize) -> Outcome<Self> {
117 Ok(Self::from_screen(res!(Screen::with_scrollback(cols, rows, scrollback))))
118 }
119
120 /// Wraps a screen that has already been made.
121 fn from_screen(screen: Screen) -> Self {
122 Self {
123 parser: Parser::new(),
124 screen,
125 acts: Vec::new(),
126 modes: Modes::default(),
127 title: String::new(),
128 title_dirty: false,
129 bells: 0,
130 replies: Vec::new(),
131 last_print: None,
132 sets: Charsets::new(),
133 saved_sets: None,
134 }
135 }
136
137 // ┌─────────────────────────────┐
138 // │ INSPECTION │
139 // └─────────────────────────────┘
140
141 /// The screen.
142 pub fn screen(&self) -> &Screen {
143 &self.screen
144 }
145
146 /// The screen, for a caller that wants to drive it directly.
147 pub fn screen_mut(&mut self) -> &mut Screen {
148 &mut self.screen
149 }
150
151 /// What has changed since a renderer last looked.
152 pub fn damage(&self) -> &Damage {
153 self.screen.damage()
154 }
155
156 /// Declares the screen drawn, so that damage accumulates afresh.
157 pub fn clear_damage(&mut self) {
158 self.screen.clear_damage();
159 }
160
161 /// The mode flags.
162 pub fn modes(&self) -> &Modes {
163 &self.modes
164 }
165
166 /// The designated character sets and which of them is mapped over the printable range.
167 pub fn charsets(&self) -> &Charsets {
168 &self.sets
169 }
170
171 /// The window title the stream last asked for.
172 pub fn title(&self) -> &str {
173 &self.title
174 }
175
176 /// The window title, if it has changed since this was last called.
177 pub fn take_title(&mut self) -> Option<String> {
178 if self.title_dirty {
179 self.title_dirty = false;
180 Some(self.title.clone())
181 } else {
182 None
183 }
184 }
185
186 /// How many bells have rung since this was last called.
187 pub fn take_bells(&mut self) -> usize {
188 let n = self.bells;
189 self.bells = 0;
190 n
191 }
192
193 /// The bytes the application has asked to be sent back to it, which the caller must write to
194 /// the pseudoterminal.
195 ///
196 /// An application that asks where the cursor is and never hears back will wait for the answer,
197 /// so a caller that never drains this will eventually hang something.
198 pub fn take_replies(&mut self) -> Vec<u8> {
199 std::mem::take(&mut self.replies)
200 }
201
202 /// Whether a character or sequence is half collected, waiting on more bytes.
203 pub fn is_partial(&self) -> bool {
204 self.parser.is_partial()
205 }
206
207 // ┌─────────────────────────────┐
208 // │ FEEDING │
209 // └─────────────────────────────┘
210
211 /// Consumes a slice of the byte stream.
212 ///
213 /// The slice may end anywhere, including inside a character or a control sequence; what is
214 /// incomplete is held over until the next call.
215 pub fn feed(&mut self, bytes: &[u8]) -> Outcome<()> {
216 let mut acts = std::mem::take(&mut self.acts);
217 acts.clear();
218 self.parser.advance(bytes, &mut acts);
219 for act in &acts {
220 self.act(act);
221 }
222 acts.clear();
223 self.acts = acts;
224 Ok(())
225 }
226
227 /// Changes the size of the screen.
228 ///
229 /// # Errors
230 /// Fails if either dimension is zero.
231 pub fn resize(&mut self, cols: usize, rows: usize) -> Outcome<()> {
232 res!(self.screen.resize(cols, rows));
233 Ok(())
234 }
235
236 /// Carries out one thing the parser decided the stream was asking for.
237 fn act(&mut self, act: &Act) {
238 match act {
239 Act::Print(c) => {
240 // A designated set stands in front of the printable ASCII range only, so a
241 // character that arrived as UTF-8 passes through whatever is designated.
242 let c = self.sets.map(*c);
243 self.screen.print(c);
244 self.last_print = Some(c);
245 }
246 Act::Ctrl(c) => self.ctrl(*c),
247 Act::Csi(seq) => self.csi(seq),
248 Act::Esc(seq) => self.esc(seq),
249 Act::Osc(seq) => self.osc(seq),
250 }
251 }
252
253 /// Carries out a C0 control.
254 fn ctrl(&mut self, c: C0) {
255 match c {
256 C0::Bell => self.bells += 1,
257 C0::Backspace => self.screen.backspace(),
258 C0::Tab => self.screen.tab(),
259 C0::LineFeed => {
260 if self.modes.newline {
261 self.screen.next_line();
262 } else {
263 self.screen.line_feed();
264 }
265 }
266 C0::CarriageReturn => self.screen.carriage_return(),
267 C0::ShiftOut => self.sets.shift_to(1),
268 C0::ShiftIn => self.sets.shift_to(0),
269 }
270 }
271
272 // ┌─────────────────────────────┐
273 // │ ESCAPE SEQUENCES │
274 // └─────────────────────────────┘
275
276 /// Carries out an escape sequence, ignoring any it does not recognise.
277 fn esc(&mut self, seq: &Esc) {
278 match (seq.inter, seq.fin) {
279 // Character set designation into G0 to G3. The `-`, `.` and `/` forms designate a 96
280 // character set into G1 to G3, which no set this model knows how to be, so they are
281 // consumed and dropped rather than being allowed to shadow the ASCII range.
282 (Some(b'('), c) => self.designate(0, c),
283 (Some(b')'), c) => self.designate(1, c),
284 (Some(b'*'), c) => self.designate(2, c),
285 (Some(b'+'), c) => self.designate(3, c),
286 (Some(b'-' | b'.' | b'/'), _) => {}
287 // DECALN, the alignment pattern.
288 (Some(b'#'), b'8') => self.screen.fill_alignment(),
289 (Some(_), _) => {}
290 (None, b'7') => self.save_cursor(),
291 (None, b'8') => self.restore_cursor(),
292 (None, b'D') => self.screen.line_feed(),
293 (None, b'E') => self.screen.next_line(),
294 (None, b'M') => self.screen.reverse_index(),
295 (None, b'H') => self.screen.set_tab(),
296 (None, b'c') => self.hard_reset(),
297 (None, b'=') => self.modes.app_keypad = true,
298 (None, b'>') => self.modes.app_keypad = false,
299 // ST on its own, and everything else, is consumed and dropped.
300 (None, _) => {}
301 }
302 }
303
304 /// Carries out an operating system command, ignoring any it does not recognise.
305 ///
306 /// Only the title commands mean anything to a screen model. `OSC 0` sets both the window and
307 /// the icon title, `OSC 1` the icon title alone and `OSC 2` the window title alone; the icon
308 /// title has no place to go here, so `OSC 1` is consumed and dropped.
309 fn osc(&mut self, seq: &Osc) {
310 match seq.ident {
311 Some(0) | Some(2) => {
312 self.title = seq.text.clone();
313 self.title_dirty = true;
314 }
315 _ => {}
316 }
317 }
318
319 /// Designates a character set into one of G0 to G3.
320 ///
321 /// A designator this model does not know names ASCII, so an unrecognised set leaves the bytes
322 /// as they arrived rather than substituting something invented here.
323 fn designate(&mut self, g: usize, designator: u8) {
324 self.sets.designate(g, Charset::from_designator(designator));
325 }
326
327 /// Saves the cursor, the pen, origin mode and the character sets, as `DECSC` asks.
328 ///
329 /// The character sets are part of what `DECSC` saves, which matters to a programme that draws a
330 /// box, saves, prints a label in ASCII and restores expecting to go on drawing the box.
331 fn save_cursor(&mut self) {
332 self.screen.save_cursor();
333 self.saved_sets = Some(self.sets);
334 }
335
336 /// Restores what [`Terminal::save_cursor`] saved.
337 fn restore_cursor(&mut self) {
338 self.screen.restore_cursor();
339 if let Some(sets) = self.saved_sets {
340 self.sets = sets;
341 }
342 }
343
344 /// Returns the terminal to its power on state.
345 fn hard_reset(&mut self) {
346 self.screen.reset();
347 self.modes = Modes::default();
348 self.last_print = None;
349 self.sets.reset();
350 self.saved_sets = None;
351 }
352
353 /// Returns the terminal to a known state without clearing the grid, as `DECSTR` asks.
354 ///
355 /// The cursor does not move. Several accounts of `DECSTR` say that it homes the cursor, but
356 /// xterm and tmux both leave it where it was, and an application that sends a soft reset in the
357 /// middle of drawing would be badly served by a terminal that moved it.
358 ///
359 /// The character sets are returned to ASCII. This is one of the few places where tmux is not
360 /// followed: tmux 3.6 leaves a designated graphics set in place across `DECSTR`, while the
361 /// VT510 manual lists the character sets among what a soft reset restores, and xterm resets
362 /// them. The manual is followed here, since a terminal that keeps the line drawing set through
363 /// a reset shows a screenful of `qqqq` to the next programme that prints plain text.
364 fn soft_reset(&mut self) {
365 let cur = *self.screen.cursor();
366 self.screen.set_pen(Pen::plain());
367 self.screen.set_origin(false);
368 self.screen.set_autowrap(true);
369 self.screen.set_insert(false);
370 self.screen.set_cursor_visible(true);
371 let rows = self.screen.rows();
372 self.screen.set_region(0, rows - 1);
373 self.sets.reset();
374 // Setting the region has homed the cursor, which is what the saved position becomes.
375 self.save_cursor();
376 self.screen.move_to(cur.col, cur.row);
377 self.modes = Modes::default();
378 }
379
380 // ┌─────────────────────────────┐
381 // │ CONTROL SEQUENCES │
382 // └─────────────────────────────┘
383
384 /// Carries out a control sequence, ignoring any it does not recognise.
385 fn csi(&mut self, seq: &Csi) {
386 let p = &seq.params;
387 match (seq.private, seq.inter, seq.fin) {
388 // ── Private sequences ──────────────────────────────
389 (Some(b'?'), None, b'h') => self.dec_modes(p, true),
390 (Some(b'?'), None, b'l') => self.dec_modes(p, false),
391 (Some(b'?'), None, b'n') => self.dec_report(p),
392 (Some(b'?'), None, b'J') => self.erase_display(p),
393 (Some(b'?'), None, b'K') => self.erase_line(p),
394 (Some(_), _, _) => {}
395 // ── Intermediates ──────────────────────────────────
396 (None, Some(b'!'), b'p') => self.soft_reset(),
397 (None, Some(_), _) => {}
398 // ── Cursor movement ────────────────────────────────
399 (None, None, b'A') => self.screen.move_up(Self::count(p)),
400 (None, None, b'B') | (None, None, b'e') => self.screen.move_down(Self::count(p)),
401 (None, None, b'C') | (None, None, b'a') => self.screen.move_right(Self::count(p)),
402 (None, None, b'D') => self.screen.move_left(Self::count(p)),
403 (None, None, b'E') => {
404 self.screen.move_down(Self::count(p));
405 self.screen.move_to_col(0);
406 }
407 (None, None, b'F') => {
408 self.screen.move_up(Self::count(p));
409 self.screen.move_to_col(0);
410 }
411 (None, None, b'G') | (None, None, b'`') => {
412 self.screen.move_to_col(Self::index(p, 0));
413 }
414 (None, None, b'd') => self.screen.move_to_row(Self::index(p, 0)),
415 (None, None, b'H') | (None, None, b'f') => {
416 self.screen.move_to(Self::index(p, 1), Self::index(p, 0));
417 }
418 (None, None, b'I') => self.screen.tab_forward(Self::count(p)),
419 (None, None, b'Z') => self.screen.tab_back(Self::count(p)),
420 // ── Erasing ────────────────────────────────────────
421 (None, None, b'J') => self.erase_display(p),
422 (None, None, b'K') => self.erase_line(p),
423 (None, None, b'X') => self.screen.erase_chars(Self::count(p)),
424 // ── Insertion and deletion ─────────────────────────
425 (None, None, b'@') => self.screen.insert_chars(Self::count(p)),
426 (None, None, b'P') => self.screen.delete_chars(Self::count(p)),
427 (None, None, b'L') => self.screen.insert_lines(Self::count(p)),
428 (None, None, b'M') => self.screen.delete_lines(Self::count(p)),
429 // ── Scrolling ──────────────────────────────────────
430 (None, None, b'S') => self.screen.scroll_up(Self::count(p)),
431 (None, None, b'T') => self.screen.scroll_down(Self::count(p)),
432 (None, None, b'r') => {
433 let rows = self.screen.rows();
434 let top = Self::index(p, 0);
435 let bottom = match p.get(1) {
436 Some(0) | None => rows - 1,
437 Some(v) => (v as usize).saturating_sub(1),
438 };
439 self.screen.set_region(top, bottom);
440 }
441 // ── Attributes ─────────────────────────────────────
442 (None, None, b'm') => self.sgr(p),
443 // ── Modes ──────────────────────────────────────────
444 (None, None, b'h') => self.ansi_modes(p, true),
445 (None, None, b'l') => self.ansi_modes(p, false),
446 // ── Reports ────────────────────────────────────────
447 (None, None, b'n') => self.report(p),
448 (None, None, b'c') => self.reply(b"\x1b[?1;2c"),
449 // ── Tab stops ──────────────────────────────────────
450 (None, None, b'g') => match p.get_or(0, 0) {
451 0 => self.screen.clear_tab(),
452 3 => self.screen.clear_all_tabs(),
453 _ => {}
454 },
455 // ── Repetition ─────────────────────────────────────
456 (None, None, b'b') => {
457 if let Some(c) = self.last_print {
458 for _ in 0..Self::count(p) {
459 self.screen.print(c);
460 }
461 }
462 }
463 // ── Cursor save and restore ────────────────────────
464 // tmux makes these the same thing as `DECSC` and `DECRC`, character sets included,
465 // rather than the position only save of ANSI.SYS.
466 (None, None, b's') => self.save_cursor(),
467 (None, None, b'u') => self.restore_cursor(),
468 // Everything else is consumed and dropped.
469 (None, None, _) => {}
470 }
471 }
472
473 /// A repeat count parameter, which is one when absent or zero.
474 fn count(p: &Params) -> usize {
475 p.get_or(0, 1) as usize
476 }
477
478 /// A one based position parameter, converted to a zero based index.
479 fn index(p: &Params, i: usize) -> usize {
480 (p.get_or(i, 1) as usize).saturating_sub(1)
481 }
482
483 /// Carries out an erase of the display.
484 fn erase_display(&mut self, p: &Params) {
485 match p.get_or(0, 0) {
486 3 => self.screen.erase_scrollback(),
487 v => {
488 if let Some(what) = Erase::from_param(v) {
489 self.screen.erase_display(what);
490 }
491 }
492 }
493 }
494
495 /// Carries out an erase of the line.
496 fn erase_line(&mut self, p: &Params) {
497 if let Some(what) = Erase::from_param(p.get_or(0, 0)) {
498 self.screen.erase_line(what);
499 }
500 }
501
502 // ┌─────────────────────────────┐
503 // │ MODES │
504 // └─────────────────────────────┘
505
506 /// Sets or clears the private DEC modes.
507 fn dec_modes(&mut self, p: &Params, on: bool) {
508 for i in 0..p.len().max(1) {
509 match p.get_or(i, 0) {
510 1 => self.modes.app_cursor = on,
511 6 => self.screen.set_origin(on),
512 7 => self.screen.set_autowrap(on),
513 25 => self.screen.set_cursor_visible(on),
514 47 | 1047 => self.surface(on, false),
515 1049 => self.surface(on, true),
516 1000 => self.modes.mouse_button = on,
517 1002 => self.modes.mouse_drag = on,
518 1003 => self.modes.mouse_any = on,
519 1004 => self.modes.focus_events = on,
520 1006 => self.modes.mouse_sgr = on,
521 2004 => self.modes.bracketed_paste = on,
522 _ => {}
523 }
524 }
525 }
526
527 /// Brings the alternate or the ordinary grid forward.
528 fn surface(&mut self, alt: bool, save: bool) {
529 let want = if alt { Surface::Alternate } else { Surface::Primary };
530 self.screen.set_surface(want, save);
531 }
532
533 /// Sets or clears the ANSI modes.
534 fn ansi_modes(&mut self, p: &Params, on: bool) {
535 for i in 0..p.len().max(1) {
536 match p.get_or(i, 0) {
537 4 => self.screen.set_insert(on),
538 20 => self.modes.newline = on,
539 _ => {}
540 }
541 }
542 }
543
544 // ┌─────────────────────────────┐
545 // │ REPORTS │
546 // └─────────────────────────────┘
547
548 /// Answers a device status report.
549 fn report(&mut self, p: &Params) {
550 match p.get_or(0, 0) {
551 5 => self.reply(b"\x1b[0n"),
552 6 => {
553 let (col, row) = self.cursor_1based();
554 let s = fmt!("\x1b[{};{}R", row, col);
555 self.reply(s.as_bytes());
556 }
557 _ => {}
558 }
559 }
560
561 /// Answers a private device status report.
562 fn dec_report(&mut self, p: &Params) {
563 match p.get_or(0, 0) {
564 6 => {
565 let (col, row) = self.cursor_1based();
566 let s = fmt!("\x1b[?{};{}R", row, col);
567 self.reply(s.as_bytes());
568 }
569 _ => {}
570 }
571 }
572
573 /// The cursor position as a report gives it, counting from one and never past the final column.
574 fn cursor_1based(&self) -> (usize, usize) {
575 let cur = self.screen.cursor();
576 let col = cur.reported_col().min(self.screen.cols() - 1) + 1;
577 (col, cur.row + 1)
578 }
579
580 /// Queues bytes to be written back to the pseudoterminal.
581 fn reply(&mut self, bytes: &[u8]) {
582 self.replies.extend_from_slice(bytes);
583 }
584
585 // ┌─────────────────────────────┐
586 // │ SELECT GRAPHIC RENDITION │
587 // └─────────────────────────────┘
588
589 /// Applies a select graphic rendition sequence to the pen.
590 fn sgr(&mut self, p: &Params) {
591 let mut pen = *self.screen.pen();
592 if p.is_empty() {
593 self.screen.set_pen(Pen::plain());
594 return;
595 }
596 let mut i = 0;
597 while i < p.len() {
598 let v = match p.get(i) {
599 Some(v) => v,
600 None => break,
601 };
602 match v {
603 0 => pen = Pen::plain(),
604 1 => pen.attrs.set(ATTR_BOLD),
605 2 => pen.attrs.set(ATTR_DIM),
606 3 => pen.attrs.set(ATTR_ITALIC),
607 4 => {
608 // The modern form `4:0` turns the underline off; every other subvalue
609 // picks a style this model does not distinguish.
610 if p.is_sub(i + 1) && p.get(i + 1) == Some(0) {
611 pen.attrs.clear(ATTR_UNDERLINE);
612 } else {
613 pen.attrs.set(ATTR_UNDERLINE);
614 }
615 while p.is_sub(i + 1) {
616 i += 1;
617 }
618 }
619 5 | 6 => pen.attrs.set(ATTR_BLINK),
620 7 => pen.attrs.set(ATTR_REVERSE),
621 8 => pen.attrs.set(ATTR_HIDDEN),
622 9 => pen.attrs.set(ATTR_STRIKE),
623 21 | 22 => pen.attrs.clear(ATTR_BOLD | ATTR_DIM),
624 23 => pen.attrs.clear(ATTR_ITALIC),
625 24 => pen.attrs.clear(ATTR_UNDERLINE),
626 25 => pen.attrs.clear(ATTR_BLINK),
627 27 => pen.attrs.clear(ATTR_REVERSE),
628 28 => pen.attrs.clear(ATTR_HIDDEN),
629 29 => pen.attrs.clear(ATTR_STRIKE),
630 30..=37 => pen.fore = Self::named(v - 30),
631 38 => {
632 let (col, next) = Self::extended(p, i);
633 if let Some(c) = col {
634 pen.fore = c;
635 }
636 i = next;
637 }
638 39 => pen.fore = TermColour::Default,
639 40..=47 => pen.back = Self::named(v - 40),
640 48 => {
641 let (col, next) = Self::extended(p, i);
642 if let Some(c) = col {
643 pen.back = c;
644 }
645 i = next;
646 }
647 49 => pen.back = TermColour::Default,
648 90..=97 => pen.fore = bright(Self::named(v - 90)),
649 100..=107 => pen.back = bright(Self::named(v - 100)),
650 _ => {}
651 }
652 i += 1;
653 }
654 self.screen.set_pen(pen);
655 }
656
657 /// The named colour for an offset of zero to seven.
658 fn named(i: u32) -> TermColour {
659 match NamedColour::from_index(i as u8) {
660 Some(c) => TermColour::Named(c),
661 None => TermColour::Default,
662 }
663 }
664
665 /// Reads an extended colour selection, returning the colour and the index of its last
666 /// parameter.
667 ///
668 /// Both spellings are accepted. The original uses semicolons throughout, as in `38;5;196` and
669 /// `38;2;10;20;30`. The later one uses colons and may carry a colour space identifier that
670 /// nobody fills in, as in `38:2::10:20:30`; the empty field is what distinguishes the two colon
671 /// forms from each other.
672 fn extended(p: &Params, at: usize) -> (Option<TermColour>, usize) {
673 // The length of the colon run that starts here, counting the selector itself.
674 let mut run = 1;
675 while p.is_sub(at + run) {
676 run += 1;
677 }
678 let colon = run > 1;
679 let kind = match p.get(at + 1) {
680 Some(k) => k,
681 None => return (None, at),
682 };
683 match kind {
684 5 => {
685 let idx = match p.get(at + 2) {
686 Some(v) => v,
687 None => return (None, at + 1),
688 };
689 let end = if colon { at + run - 1 } else { at + 2 };
690 (Some(TermColour::Indexed(idx as u8)), end)
691 }
692 2 => {
693 // With a colour space identifier the run is six long, without it five.
694 let base = if colon && run >= 6 { at + 3 } else { at + 2 };
695 let r = p.get(base);
696 let g = p.get(base + 1);
697 let b = p.get(base + 2);
698 let end = if colon { at + run - 1 } else { base + 2 };
699 match (r, g, b) {
700 (Some(r), Some(g), Some(b)) => (
701 Some(TermColour::Rgb(r as u8, g as u8, b as u8)),
702 end,
703 ),
704 _ => (None, end),
705 }
706 }
707 _ => (None, if colon { at + run - 1 } else { at + 1 }),
708 }
709 }
710}
711
712/// The bright variant of a named colour, or the colour unchanged if it is not a named one.
713fn bright(c: TermColour) -> TermColour {
714 match c {
715 TermColour::Named(n) => TermColour::Named(n.brighten()),
716 other => other,
717 }
718}