use vte::{
Parser,
Params,
Perform,
};
use std::iter;
use crate::glyph::GlyphAttr;
use crate::charset::{
CharsetIndex,
Charset,
};
use crate::term::{
TermMode,
Term,
};
use crate::win::{
WinMode,
Win,
};
pub struct Vte {
parser: Parser,
last_c: Option<char>,
}
impl Vte {
pub fn new() -> Self {
Vte {
parser: Parser::new(),
last_c: None,
}
}
pub fn process_input(
&mut self, buf: &[u8], win: &mut Win, term: &mut Term
) {
let mut performer = Performer::new(win, term, self.last_c.take());
buf.iter().for_each(|&b| self.parser.advance(&mut performer, b));
self.last_c = performer.last_c.take();
}
}
const VTIDEN: &[u8] = b"\x1B[?6c";
struct Performer<'a> {
win: &'a mut Win,
term: &'a mut Term,
last_c: Option<char>,
}
impl<'a> Performer<'a> {
pub fn new(
win: &'a mut Win, term: &'a mut Term, last_c: Option<char>
) -> Self {
Self { win, term, last_c }
}
fn set_glyph_attr(&mut self, params: &Params) {
let prop = &mut self.term.c.glyph.prop;
if params.is_empty() {
prop.reset();
return;
}
for param in params.iter() {
match param[0] {
0 => prop.reset(),
1 => prop.attr.insert(GlyphAttr::BOLD),
2 => prop.attr.insert(GlyphAttr::FAINT),
3 => prop.attr.insert(GlyphAttr::ITALIC),
4 => prop.attr.insert(GlyphAttr::UNDERLINE),
5 | 6 => prop.attr.insert(GlyphAttr::BLINK),
7 => prop.attr.insert(GlyphAttr::REVERSE),
8 => prop.attr.insert(GlyphAttr::INVISIBLE),
9 => prop.attr.insert(GlyphAttr::STRUCK),
22 => prop.attr.remove(GlyphAttr::BOLD_FAINT),
23 => prop.attr.remove(GlyphAttr::ITALIC),
24 => prop.attr.remove(GlyphAttr::UNDERLINE),
25 => prop.attr.remove(GlyphAttr::BLINK),
27 => prop.attr.remove(GlyphAttr::REVERSE),
28 => prop.attr.remove(GlyphAttr::INVISIBLE),
29 => prop.attr.remove(GlyphAttr::STRUCK),
30..=37 => prop.fg = (param[0] - 30) as usize,
38 => (), 39 => prop.reset_fg(),
40..=47 => prop.bg = (param[0] - 40) as usize,
48 => (), 49 => prop.reset_bg(),
90..=97 => prop.fg = (param[0] - 90 + 8) as usize,
100..=107 => prop.bg = (param[0] - 100 + 8) as usize,
_ => println!("unknown glyph attr {}", param[0]),
}
}
}
fn set_mode(
&mut self, intermediate: Option<&u8>, params: &Params, val: bool
) {
let private = match intermediate {
Some(b'?') => true,
None => false,
_ => return,
};
if private {
for param in params.iter() {
match param[0] {
1 => self.win.set_mode(WinMode::APPCURSOR, val),
5 => self.win.set_mode(WinMode::REVERSE, val),
6 => {
self.term.set_mode(TermMode::ORIGIN, val);
self.term.move_ato(0, 0);
},
7 => self.term.set_mode(TermMode::WRAP, val),
25 => self.win.set_mode(WinMode::HIDE, !val),
1034 =>
self.win.set_mode(WinMode::EIGHT_BIT, val),
1048 =>
{
if val {
self.term.save_cursor();
} else {
self.term.load_cursor();
}
},
_ => (),
}
}
} else {
for param in params.iter() {
match param[0] {
4 => self.term.set_mode(TermMode::INSERT, val),
12 => self.win.set_mode(WinMode::ECHO, !val),
20 => self.term.set_mode(TermMode::CRLF, val),
_ => (),
}
}
}
}
}
impl<'a> Perform for Performer<'a> {
fn print(&mut self, c: char) {
let c = self.term.charset.map(c);
self.term.put_char(c);
self.last_c = Some(c);
}
fn execute(&mut self, byte: u8) {
let win = &mut *self.win;
let term = &mut *self.term;
match byte {
0x07 => win.bell(),
0x08 => term.move_to(term.c.x.saturating_sub(1), term.c.y),
0x09 => term.put_tabs(1),
0x0D => term.move_to(0, term.c.y),
0x0A | 0x0B | 0x0C => term.new_line(false),
0x0E => term.charset.set_current(CharsetIndex::G1),
0x0F => term.charset.set_current(CharsetIndex::G0),
_ => println!("unknown control {:02x}", byte),
}
}
fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8) {
let win = &mut *self.win;
let term = &mut *self.term;
let intermediate = intermediates.get(0);
match (byte, intermediate) {
(b'B', Some(b'(')) =>
term.charset.setup(CharsetIndex::G0, Charset::Ascii),
(b'B', Some(b')')) =>
term.charset.setup(CharsetIndex::G1, Charset::Ascii),
(b'B', Some(b'*')) =>
term.charset.setup(CharsetIndex::G2, Charset::Ascii),
(b'B', Some(b'+')) =>
term.charset.setup(CharsetIndex::G3, Charset::Ascii),
(b'D', None) => term.new_line(false),
(b'E', None) => term.new_line(true),
(b'H', None) => term.set_tab(term.c.x),
(b'M', None) => {
if term.c.y == term.scroll_top {
term.scroll_down(term.scroll_top, 1);
} else {
term.move_to(term.c.x, term.c.y-1);
}
},
(b'Z', None) => term.pty.write(VTIDEN.to_vec()),
(b'c', None) => term.reset(), (b'0', Some(b'(')) =>
term.charset.setup(CharsetIndex::G0, Charset::Graphic0),
(b'0', Some(b')')) =>
term.charset.setup(CharsetIndex::G1, Charset::Graphic0),
(b'0', Some(b'*')) =>
term.charset.setup(CharsetIndex::G2, Charset::Graphic0),
(b'0', Some(b'+')) =>
term.charset.setup(CharsetIndex::G3, Charset::Graphic0),
(b'7', None) => term.save_cursor(),
(b'8', None) => term.load_cursor(),
(b'=', None) => win.set_mode(WinMode::APPKEYPAD, true),
(b'>', None) => win.set_mode(WinMode::APPKEYPAD, false),
(b'\\', None) => (),
_ => println!("unknown esc {:?} {}", intermediate, byte as char),
}
}
fn csi_dispatch(
&mut self,
params: &Params,
intermediates: &[u8],
has_ignored_intermediates: bool,
action: char,
) {
let term = &mut *self.term;
if has_ignored_intermediates || intermediates.len() > 1 {
println!("invalid csi intermediates {:?}", intermediates);
return;
}
let intermediate = intermediates.get(0);
let mut params_iter = params.iter();
let arg0 = params_iter.next();
let arg1 = params_iter.next();
let arg0_or = |default: usize| {
arg0.map(|x| x[0] as usize).filter(|&x| x != 0).unwrap_or(default)
};
let arg1_or = |default: usize| {
arg1.map(|x| x[0] as usize).filter(|&x| x != 0).unwrap_or(default)
};
match (action, intermediate) {
('@', None) => term.insert_blanks(arg0_or(1)),
('A', None) => term.move_to(term.c.x, term.c.y.saturating_sub(arg0_or(1))),
('B', None) | ('e', None) => term.move_to(term.c.x, term.c.y+arg0_or(1)),
('b', None) => {
if let Some(c) = self.last_c {
iter::repeat(c)
.take(arg0_or(1))
.for_each(|c| term.put_char(c));
}
},
('C', None) | ('a', None) => term.move_to(term.c.x+arg0_or(1), term.c.y),
('c', _) if arg0_or(0) == 0 => term.pty.write(VTIDEN.to_vec()),
('D', None) => term.move_to(term.c.x.saturating_sub(arg0_or(1)), term.c.y),
('d', None) => term.move_ato(term.c.x, arg0_or(1)-1),
('E', None) => term.move_to(0, term.c.y+arg0_or(1)),
('F', None) => term.move_to(0, term.c.y.saturating_sub(arg0_or(1))),
('G', None) | ('`', None) => term.move_to(arg0_or(1)-1, term.c.y),
('g', None) => {
match arg0_or(0) {
0 => term.clear_tabs(iter::once(term.c.x)),
3 => term.clear_tabs(0..term.cols),
x => println!("unknown TBC {}", x),
}
},
('H', None) | ('f', None) => term.move_ato(arg1_or(1)-1, arg0_or(1)-1),
('h', intermediate) => self.set_mode(intermediate, params, true),
('I', None) => term.put_tabs(arg0_or(1) as i32),
('J', None) => {
let y = term.c.y;
match arg0_or(0) {
0 => {
term.clear_region(term.c.x..term.cols, iter::once(y));
term.clear_region(0..term.cols, y+1..term.rows);
},
1 => {
term.clear_region(0..term.cols, 0..y);
term.clear_region(0..=term.c.x, iter::once(y));
},
2 => term.clear_region(0..term.cols, 0..term.rows),
x => println!("unknown ED {}", x),
}
},
('K', None) => {
let y = term.c.y;
match arg0_or(0) {
0 => term.clear_region(term.c.x..term.cols, iter::once(y)),
1 => term.clear_region(0..=term.c.x, iter::once(y)),
2 => term.clear_region(0..term.cols, iter::once(y)),
x => println!("unknown EL {}", x),
}
},
('L', None) => term.insert_lines(arg0_or(1)),
('l', intermediate) => self.set_mode(intermediate, params, false),
('M', None) => term.delete_lines(arg0_or(1)),
('m', None) => self.set_glyph_attr(params),
('n', None) if arg0_or(0) == 6 => {
let s = format!("\x1B[{};{}R", term.c.y+1, term.c.x+1);
term.pty.write(s.as_bytes().to_vec());
}
('P', None) => term.delete_chars(arg0_or(1)),
('r', None) => {
let top = arg0_or(1) - 1;
let bot = arg1_or(term.rows) - 1;
term.set_scroll(top, bot);
term.move_ato(0, 0);
},
('S', None) => term.scroll_up(term.scroll_top, arg0_or(1)),
('s', None) => term.save_cursor(),
('T', None) => term.scroll_down(term.scroll_top, arg0_or(1)),
('u', None) => term.load_cursor(),
('X', None) => term.clear_region(
term.c.x..term.c.x+arg0_or(1), iter::once(term.c.y)
),
('Z', None) => term.put_tabs(-(arg0_or(1) as i32)),
_ => println!(
"unknown csi {:?} {:?} {}", intermediates, params, action
),
}
}
}