use crate::term::BufWrite as _;
use unicode_width::UnicodeWidthChar as _;
const MODE_APPLICATION_KEYPAD: u8 = 0b0000_0001;
const MODE_APPLICATION_CURSOR: u8 = 0b0000_0010;
const MODE_HIDE_CURSOR: u8 = 0b0000_0100;
const MODE_ALTERNATE_SCREEN: u8 = 0b0000_1000;
const MODE_BRACKETED_PASTE: u8 = 0b0001_0000;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum MouseProtocolMode {
None,
Press,
PressRelease,
ButtonMotion,
AnyMotion,
}
impl Default for MouseProtocolMode {
fn default() -> Self {
Self::None
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum MouseProtocolEncoding {
Default,
Utf8,
Sgr,
}
impl Default for MouseProtocolEncoding {
fn default() -> Self {
Self::Default
}
}
#[derive(Clone, Debug)]
pub struct Screen {
grid: crate::grid::Grid,
alternate_grid: crate::grid::Grid,
attrs: crate::attrs::Attrs,
saved_attrs: crate::attrs::Attrs,
title: String,
icon_name: String,
modes: u8,
mouse_protocol_mode: MouseProtocolMode,
mouse_protocol_encoding: MouseProtocolEncoding,
audible_bell_count: usize,
visual_bell_count: usize,
errors: usize,
}
impl Screen {
pub fn get_selected_text(
&self,
low_x: i32,
low_y: i32,
high_x: i32,
high_y: i32,
) -> String {
self.grid().get_selected_text(low_x, low_y, high_x, high_y)
}
pub(crate) fn new(size: crate::grid::Size, scrollback_len: usize) -> Self {
let mut grid = crate::grid::Grid::new(size, scrollback_len);
grid.allocate_rows();
Self {
grid,
alternate_grid: crate::grid::Grid::new(size, 0),
attrs: crate::attrs::Attrs::default(),
saved_attrs: crate::attrs::Attrs::default(),
title: String::default(),
icon_name: String::default(),
modes: 0,
mouse_protocol_mode: MouseProtocolMode::default(),
mouse_protocol_encoding: MouseProtocolEncoding::default(),
audible_bell_count: 0,
visual_bell_count: 0,
errors: 0,
}
}
pub(crate) fn set_size(&mut self, rows: u16, cols: u16) {
self.grid.set_size(crate::grid::Size { rows, cols });
self
.alternate_grid
.set_size(crate::grid::Size { rows, cols });
}
#[must_use]
pub fn size(&self) -> (u16, u16) {
let size = self.grid().size();
(size.rows, size.cols)
}
#[must_use]
pub fn scrollback(&self) -> usize {
self.grid().scrollback()
}
pub fn scrollback_len(&self) -> usize {
self.grid().scrollback_len()
}
pub fn set_scrollback(&mut self, rows: usize) {
self.grid_mut().set_scrollback(rows);
}
#[must_use]
pub fn contents(&self) -> String {
let mut contents = String::new();
self.write_contents(&mut contents);
contents
}
fn write_contents(&self, contents: &mut String) {
self.grid().write_contents(contents);
}
pub fn rows(
&self,
start: u16,
width: u16,
) -> impl Iterator<Item = String> + '_ {
self.grid().visible_rows().map(move |row| {
let mut contents = String::new();
row.write_contents(&mut contents, start, width, false);
contents
})
}
#[must_use]
pub fn contents_between(
&self,
start_row: u16,
start_col: u16,
end_row: u16,
end_col: u16,
) -> String {
match start_row.cmp(&end_row) {
std::cmp::Ordering::Less => {
let (_, cols) = self.size();
let mut contents = String::new();
for (i, row) in self
.grid()
.visible_rows()
.enumerate()
.skip(usize::from(start_row))
.take(usize::from(end_row) - usize::from(start_row) + 1)
{
if i == usize::from(start_row) {
row.write_contents(
&mut contents,
start_col,
cols - start_col,
false,
);
if !row.wrapped() {
contents.push('\n');
}
} else if i == usize::from(end_row) {
row.write_contents(&mut contents, 0, end_col, false);
} else {
row.write_contents(&mut contents, 0, cols, false);
if !row.wrapped() {
contents.push('\n');
}
}
}
contents
}
std::cmp::Ordering::Equal => {
if start_col < end_col {
self
.rows(start_col, end_col - start_col)
.nth(usize::from(start_row))
.unwrap_or_else(String::new)
} else {
String::new()
}
}
std::cmp::Ordering::Greater => String::new(),
}
}
#[must_use]
pub fn state_formatted(&self) -> Vec<u8> {
let mut contents = vec![];
self.write_contents_formatted(&mut contents);
self.write_input_mode_formatted(&mut contents);
self.write_title_formatted(&mut contents);
contents
}
#[must_use]
pub fn state_diff(&self, prev: &Self) -> Vec<u8> {
let mut contents = vec![];
self.write_contents_diff(&mut contents, prev);
self.write_input_mode_diff(&mut contents, prev);
self.write_title_diff(&mut contents, prev);
self.write_bells_diff(&mut contents, prev);
contents
}
#[must_use]
pub fn contents_formatted(&self) -> Vec<u8> {
let mut contents = vec![];
self.write_contents_formatted(&mut contents);
contents
}
fn write_contents_formatted(&self, contents: &mut Vec<u8>) {
crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
let prev_attrs = self.grid().write_contents_formatted(contents);
self.attrs.write_escape_code_diff(contents, &prev_attrs);
}
#[allow(clippy::missing_panics_doc)]
pub fn rows_formatted(
&self,
start: u16,
width: u16,
) -> impl Iterator<Item = Vec<u8>> + '_ {
let mut wrapping = false;
self.grid().visible_rows().enumerate().map(move |(i, row)| {
let i = i.try_into().unwrap();
let mut contents = vec![];
row.write_contents_formatted(
&mut contents,
start,
width,
i,
wrapping,
None,
None,
);
if start == 0 && width == self.grid.size().cols {
wrapping = row.wrapped();
}
contents
})
}
#[must_use]
pub fn contents_diff(&self, prev: &Self) -> Vec<u8> {
let mut contents = vec![];
self.write_contents_diff(&mut contents, prev);
contents
}
fn write_contents_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
if self.hide_cursor() != prev.hide_cursor() {
crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
}
let prev_attrs =
self
.grid()
.write_contents_diff(contents, prev.grid(), prev.attrs);
self.attrs.write_escape_code_diff(contents, &prev_attrs);
}
#[allow(clippy::missing_panics_doc)]
pub fn rows_diff<'a>(
&'a self,
prev: &'a Self,
start: u16,
width: u16,
) -> impl Iterator<Item = Vec<u8>> + 'a {
self
.grid()
.visible_rows()
.zip(prev.grid().visible_rows())
.enumerate()
.map(move |(i, (row, prev_row))| {
let i = i.try_into().unwrap();
let mut contents = vec![];
row.write_contents_diff(
&mut contents,
prev_row,
start,
width,
i,
false,
false,
crate::grid::Pos { row: i, col: start },
crate::attrs::Attrs::default(),
);
contents
})
}
#[must_use]
pub fn input_mode_formatted(&self) -> Vec<u8> {
let mut contents = vec![];
self.write_input_mode_formatted(&mut contents);
contents
}
fn write_input_mode_formatted(&self, contents: &mut Vec<u8>) {
crate::term::ApplicationKeypad::new(self.mode(MODE_APPLICATION_KEYPAD))
.write_buf(contents);
crate::term::ApplicationCursor::new(self.mode(MODE_APPLICATION_CURSOR))
.write_buf(contents);
crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE))
.write_buf(contents);
crate::term::MouseProtocolMode::new(
self.mouse_protocol_mode,
MouseProtocolMode::None,
)
.write_buf(contents);
crate::term::MouseProtocolEncoding::new(
self.mouse_protocol_encoding,
MouseProtocolEncoding::Default,
)
.write_buf(contents);
}
#[must_use]
pub fn input_mode_diff(&self, prev: &Self) -> Vec<u8> {
let mut contents = vec![];
self.write_input_mode_diff(&mut contents, prev);
contents
}
fn write_input_mode_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
if self.mode(MODE_APPLICATION_KEYPAD) != prev.mode(MODE_APPLICATION_KEYPAD)
{
crate::term::ApplicationKeypad::new(self.mode(MODE_APPLICATION_KEYPAD))
.write_buf(contents);
}
if self.mode(MODE_APPLICATION_CURSOR) != prev.mode(MODE_APPLICATION_CURSOR)
{
crate::term::ApplicationCursor::new(self.mode(MODE_APPLICATION_CURSOR))
.write_buf(contents);
}
if self.mode(MODE_BRACKETED_PASTE) != prev.mode(MODE_BRACKETED_PASTE) {
crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE))
.write_buf(contents);
}
crate::term::MouseProtocolMode::new(
self.mouse_protocol_mode,
prev.mouse_protocol_mode,
)
.write_buf(contents);
crate::term::MouseProtocolEncoding::new(
self.mouse_protocol_encoding,
prev.mouse_protocol_encoding,
)
.write_buf(contents);
}
#[must_use]
pub fn title_formatted(&self) -> Vec<u8> {
let mut contents = vec![];
self.write_title_formatted(&mut contents);
contents
}
fn write_title_formatted(&self, contents: &mut Vec<u8>) {
crate::term::ChangeTitle::new(&self.icon_name, &self.title, "", "")
.write_buf(contents);
}
#[must_use]
pub fn title_diff(&self, prev: &Self) -> Vec<u8> {
let mut contents = vec![];
self.write_title_diff(&mut contents, prev);
contents
}
fn write_title_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
crate::term::ChangeTitle::new(
&self.icon_name,
&self.title,
&prev.icon_name,
&prev.title,
)
.write_buf(contents);
}
#[must_use]
pub fn bells_diff(&self, prev: &Self) -> Vec<u8> {
let mut contents = vec![];
self.write_bells_diff(&mut contents, prev);
contents
}
fn write_bells_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
if self.audible_bell_count != prev.audible_bell_count {
crate::term::AudibleBell::default().write_buf(contents);
}
if self.visual_bell_count != prev.visual_bell_count {
crate::term::VisualBell::default().write_buf(contents);
}
}
#[must_use]
pub fn attributes_formatted(&self) -> Vec<u8> {
let mut contents = vec![];
self.write_attributes_formatted(&mut contents);
contents
}
fn write_attributes_formatted(&self, contents: &mut Vec<u8>) {
crate::term::ClearAttrs::default().write_buf(contents);
self
.attrs
.write_escape_code_diff(contents, &crate::attrs::Attrs::default());
}
#[must_use]
pub fn cursor_position(&self) -> (u16, u16) {
let pos = self.grid().pos();
(pos.row, pos.col)
}
#[must_use]
pub fn cursor_state_formatted(&self) -> Vec<u8> {
let mut contents = vec![];
self.write_cursor_state_formatted(&mut contents);
contents
}
fn write_cursor_state_formatted(&self, contents: &mut Vec<u8>) {
crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
self
.grid()
.write_cursor_position_formatted(contents, None, None);
}
#[must_use]
pub fn cell(&self, row: u16, col: u16) -> Option<&crate::cell::Cell> {
self.grid().visible_cell(crate::grid::Pos { row, col })
}
#[must_use]
pub fn row_wrapped(&self, row: u16) -> bool {
self
.grid()
.visible_row(row)
.map_or(false, crate::row::Row::wrapped)
}
#[must_use]
pub fn title(&self) -> &str {
&self.title
}
#[must_use]
pub fn icon_name(&self) -> &str {
&self.icon_name
}
#[must_use]
pub fn audible_bell_count(&self) -> usize {
self.audible_bell_count
}
#[must_use]
pub fn visual_bell_count(&self) -> usize {
self.visual_bell_count
}
#[must_use]
pub fn errors(&self) -> usize {
self.errors
}
#[must_use]
pub fn alternate_screen(&self) -> bool {
self.mode(MODE_ALTERNATE_SCREEN)
}
#[must_use]
pub fn application_keypad(&self) -> bool {
self.mode(MODE_APPLICATION_KEYPAD)
}
#[must_use]
pub fn application_cursor(&self) -> bool {
self.mode(MODE_APPLICATION_CURSOR)
}
#[must_use]
pub fn hide_cursor(&self) -> bool {
self.mode(MODE_HIDE_CURSOR)
}
#[must_use]
pub fn bracketed_paste(&self) -> bool {
self.mode(MODE_BRACKETED_PASTE)
}
#[must_use]
pub fn mouse_protocol_mode(&self) -> MouseProtocolMode {
self.mouse_protocol_mode
}
#[must_use]
pub fn mouse_protocol_encoding(&self) -> MouseProtocolEncoding {
self.mouse_protocol_encoding
}
#[must_use]
pub fn fgcolor(&self) -> crate::attrs::Color {
self.attrs.fgcolor
}
#[must_use]
pub fn bgcolor(&self) -> crate::attrs::Color {
self.attrs.bgcolor
}
#[must_use]
pub fn bold(&self) -> bool {
self.attrs.bold()
}
#[must_use]
pub fn italic(&self) -> bool {
self.attrs.italic()
}
#[must_use]
pub fn underline(&self) -> bool {
self.attrs.underline()
}
#[must_use]
pub fn inverse(&self) -> bool {
self.attrs.inverse()
}
fn grid(&self) -> &crate::grid::Grid {
if self.mode(MODE_ALTERNATE_SCREEN) {
&self.alternate_grid
} else {
&self.grid
}
}
fn grid_mut(&mut self) -> &mut crate::grid::Grid {
if self.mode(MODE_ALTERNATE_SCREEN) {
&mut self.alternate_grid
} else {
&mut self.grid
}
}
fn enter_alternate_grid(&mut self) {
self.grid_mut().set_scrollback(0);
self.set_mode(MODE_ALTERNATE_SCREEN);
self.alternate_grid.allocate_rows();
}
fn exit_alternate_grid(&mut self) {
self.clear_mode(MODE_ALTERNATE_SCREEN);
}
fn save_cursor(&mut self) {
self.grid_mut().save_cursor();
self.saved_attrs = self.attrs;
}
fn restore_cursor(&mut self) {
self.grid_mut().restore_cursor();
self.attrs = self.saved_attrs;
}
fn set_mode(&mut self, mode: u8) {
self.modes |= mode;
}
fn clear_mode(&mut self, mode: u8) {
self.modes &= !mode;
}
fn mode(&self, mode: u8) -> bool {
self.modes & mode != 0
}
fn set_mouse_mode(&mut self, mode: MouseProtocolMode) {
self.mouse_protocol_mode = mode;
}
fn clear_mouse_mode(&mut self, mode: MouseProtocolMode) {
if self.mouse_protocol_mode == mode {
self.mouse_protocol_mode = MouseProtocolMode::default();
}
}
fn set_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
self.mouse_protocol_encoding = encoding;
}
fn clear_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
if self.mouse_protocol_encoding == encoding {
self.mouse_protocol_encoding = MouseProtocolEncoding::default();
}
}
}
impl Screen {
fn text(&mut self, c: char) {
let pos = self.grid().pos();
let size = self.grid().size();
let attrs = self.attrs;
let width = c.width();
if width.is_none() && (u32::from(c)) < 256 {
return;
}
let width = width
.unwrap_or(1)
.try_into()
.unwrap();
let mut wrap = false;
if pos.col > size.cols - width {
let last_cell = self
.grid()
.drawing_cell(crate::grid::Pos {
row: pos.row,
col: size.cols - 1,
})
.unwrap();
if last_cell.has_contents() || last_cell.is_wide_continuation() {
wrap = true;
}
}
self.grid_mut().col_wrap(width, wrap);
let pos = self.grid().pos();
if width == 0 {
if pos.col > 0 {
let mut prev_cell = self
.grid_mut()
.drawing_cell_mut(crate::grid::Pos {
row: pos.row,
col: pos.col - 1,
})
.unwrap();
if prev_cell.is_wide_continuation() {
prev_cell = self
.grid_mut()
.drawing_cell_mut(crate::grid::Pos {
row: pos.row,
col: pos.col - 2,
})
.unwrap();
}
prev_cell.append(c);
} else if pos.row > 0 {
let prev_row = self
.grid()
.drawing_row(pos.row - 1)
.unwrap();
if prev_row.wrapped() {
let mut prev_cell = self
.grid_mut()
.drawing_cell_mut(crate::grid::Pos {
row: pos.row - 1,
col: size.cols - 1,
})
.unwrap();
if prev_cell.is_wide_continuation() {
prev_cell = self
.grid_mut()
.drawing_cell_mut(crate::grid::Pos {
row: pos.row - 1,
col: size.cols - 2,
})
.unwrap();
}
prev_cell.append(c);
}
}
} else {
if self
.grid()
.drawing_cell(pos)
.unwrap()
.is_wide_continuation()
{
let prev_cell = self
.grid_mut()
.drawing_cell_mut(crate::grid::Pos {
row: pos.row,
col: pos.col - 1,
})
.unwrap();
prev_cell.clear(attrs);
}
if self
.grid()
.drawing_cell(pos)
.unwrap()
.is_wide()
{
if let Some(next_cell) =
self.grid_mut().drawing_cell_mut(crate::grid::Pos {
row: pos.row,
col: pos.col + 1,
})
{
next_cell.set(' ', attrs);
}
}
let cell = self
.grid_mut()
.drawing_cell_mut(pos)
.unwrap();
cell.set(c, attrs);
self.grid_mut().col_inc(1);
if width > 1 {
let pos = self.grid().pos();
if self
.grid()
.drawing_cell(pos)
.unwrap()
.is_wide()
{
let next_next_pos = crate::grid::Pos {
row: pos.row,
col: pos.col + 1,
};
let next_next_cell = self
.grid_mut()
.drawing_cell_mut(next_next_pos)
.unwrap();
next_next_cell.clear(attrs);
if next_next_pos.col == size.cols - 1 {
self
.grid_mut()
.drawing_row_mut(pos.row)
.unwrap()
.wrap(false);
}
}
let next_cell = self
.grid_mut()
.drawing_cell_mut(pos)
.unwrap();
next_cell.clear(crate::attrs::Attrs::default());
next_cell.set_wide_continuation(true);
self.grid_mut().col_inc(1);
}
}
}
fn bel(&mut self) {
self.audible_bell_count += 1;
}
fn bs(&mut self) {
self.grid_mut().col_dec(1);
}
fn tab(&mut self) {
self.grid_mut().col_tab();
}
fn lf(&mut self) {
self.grid_mut().row_inc_scroll(1);
}
fn vt(&mut self) {
self.lf();
}
fn ff(&mut self) {
self.lf();
}
fn cr(&mut self) {
self.grid_mut().col_set(0);
}
fn decsc(&mut self) {
self.save_cursor();
}
fn decrc(&mut self) {
self.restore_cursor();
}
fn deckpam(&mut self) {
self.set_mode(MODE_APPLICATION_KEYPAD);
}
fn deckpnm(&mut self) {
self.clear_mode(MODE_APPLICATION_KEYPAD);
}
fn ri(&mut self) {
self.grid_mut().row_dec_scroll(1);
}
fn ris(&mut self) {
let title = self.title.clone();
let icon_name = self.icon_name.clone();
let audible_bell_count = self.audible_bell_count;
let visual_bell_count = self.visual_bell_count;
let errors = self.errors;
*self = Self::new(self.grid.size(), self.grid.scrollback_len());
self.title = title;
self.icon_name = icon_name;
self.audible_bell_count = audible_bell_count;
self.visual_bell_count = visual_bell_count;
self.errors = errors;
}
fn vb(&mut self) {
self.visual_bell_count += 1;
}
fn ich(&mut self, count: u16) {
self.grid_mut().insert_cells(count);
}
fn cuu(&mut self, offset: u16) {
self.grid_mut().row_dec_clamp(offset);
}
fn cud(&mut self, offset: u16) {
self.grid_mut().row_inc_clamp(offset);
}
fn cuf(&mut self, offset: u16) {
self.grid_mut().col_inc_clamp(offset);
}
fn cub(&mut self, offset: u16) {
self.grid_mut().col_dec(offset);
}
fn cha(&mut self, col: u16) {
self.grid_mut().col_set(col - 1);
}
fn cup(&mut self, (row, col): (u16, u16)) {
self.grid_mut().set_pos(crate::grid::Pos {
row: row - 1,
col: col - 1,
});
}
fn ed(&mut self, mode: u16) {
let attrs = self.attrs;
match mode {
0 => self.grid_mut().erase_all_forward(attrs),
1 => self.grid_mut().erase_all_backward(attrs),
2 => self.grid_mut().erase_all(attrs),
n => {
log::debug!("unhandled ED mode: {}", n);
}
}
}
fn decsed(&mut self, mode: u16) {
self.ed(mode);
}
fn el(&mut self, mode: u16) {
let attrs = self.attrs;
match mode {
0 => self.grid_mut().erase_row_forward(attrs),
1 => self.grid_mut().erase_row_backward(attrs),
2 => self.grid_mut().erase_row(attrs),
n => {
log::debug!("unhandled EL mode: {}", n);
}
}
}
fn decsel(&mut self, mode: u16) {
self.el(mode);
}
fn il(&mut self, count: u16) {
self.grid_mut().insert_lines(count);
}
fn dl(&mut self, count: u16) {
self.grid_mut().delete_lines(count);
}
fn dch(&mut self, count: u16) {
self.grid_mut().delete_cells(count);
}
fn su(&mut self, count: u16) {
self.grid_mut().scroll_up(count);
}
fn sd(&mut self, count: u16) {
self.grid_mut().scroll_down(count);
}
fn ech(&mut self, count: u16) {
let attrs = self.attrs;
self.grid_mut().erase_cells(count, attrs);
}
fn vpa(&mut self, row: u16) {
self.grid_mut().row_set(row - 1);
}
#[allow(clippy::unused_self)]
fn sm(&mut self, params: &vte::Params) {
if log::log_enabled!(log::Level::Debug) {
log::debug!("unhandled SM mode: {}", param_str(params));
}
}
fn decset(&mut self, params: &vte::Params) {
for param in params {
match param {
&[1] => self.set_mode(MODE_APPLICATION_CURSOR),
&[6] => self.grid_mut().set_origin_mode(true),
&[9] => self.set_mouse_mode(MouseProtocolMode::Press),
&[25] => self.clear_mode(MODE_HIDE_CURSOR),
&[47] => self.enter_alternate_grid(),
&[1000] => {
self.set_mouse_mode(MouseProtocolMode::PressRelease);
}
&[1002] => {
self.set_mouse_mode(MouseProtocolMode::ButtonMotion);
}
&[1003] => self.set_mouse_mode(MouseProtocolMode::AnyMotion),
&[1005] => {
self.set_mouse_encoding(MouseProtocolEncoding::Utf8);
}
&[1006] => {
self.set_mouse_encoding(MouseProtocolEncoding::Sgr);
}
&[1049] => {
self.decsc();
self.alternate_grid.clear();
self.enter_alternate_grid();
}
&[2004] => self.set_mode(MODE_BRACKETED_PASTE),
ns => {
if log::log_enabled!(log::Level::Debug) {
let n = if ns.len() == 1 {
format!(
"{}",
ns[0]
)
} else {
format!("{:?}", ns)
};
log::debug!("unhandled DECSET mode: {}", n);
}
}
}
}
}
#[allow(clippy::unused_self)]
fn rm(&mut self, params: &vte::Params) {
if log::log_enabled!(log::Level::Debug) {
log::debug!("unhandled RM mode: {}", param_str(params));
}
}
fn decrst(&mut self, params: &vte::Params) {
for param in params {
match param {
&[1] => self.clear_mode(MODE_APPLICATION_CURSOR),
&[6] => self.grid_mut().set_origin_mode(false),
&[9] => self.clear_mouse_mode(MouseProtocolMode::Press),
&[25] => self.set_mode(MODE_HIDE_CURSOR),
&[47] => {
self.exit_alternate_grid();
}
&[1000] => {
self.clear_mouse_mode(MouseProtocolMode::PressRelease);
}
&[1002] => {
self.clear_mouse_mode(MouseProtocolMode::ButtonMotion);
}
&[1003] => {
self.clear_mouse_mode(MouseProtocolMode::AnyMotion);
}
&[1005] => {
self.clear_mouse_encoding(MouseProtocolEncoding::Utf8);
}
&[1006] => {
self.clear_mouse_encoding(MouseProtocolEncoding::Sgr);
}
&[1049] => {
self.exit_alternate_grid();
self.decrc();
}
&[2004] => self.clear_mode(MODE_BRACKETED_PASTE),
ns => {
if log::log_enabled!(log::Level::Debug) {
let n = if ns.len() == 1 {
format!(
"{}",
ns[0]
)
} else {
format!("{:?}", ns)
};
log::debug!("unhandled DECRST mode: {}", n);
}
}
}
}
}
fn sgr(&mut self, params: &vte::Params) {
if params.is_empty() {
self.attrs = crate::attrs::Attrs::default();
return;
}
let mut iter = params.iter();
macro_rules! next_param {
() => {
match iter.next() {
Some(n) => n,
_ => return,
}
};
}
macro_rules! to_u8 {
($n:expr) => {
if let Some(n) = u16_to_u8($n) {
n
} else {
return;
}
};
}
macro_rules! next_param_u8 {
() => {
if let &[n] = next_param!() {
to_u8!(n)
} else {
return;
}
};
}
loop {
match next_param!() {
&[0] => self.attrs = crate::attrs::Attrs::default(),
&[1] => self.attrs.set_bold(true),
&[3] => self.attrs.set_italic(true),
&[4] => self.attrs.set_underline(true),
&[7] => self.attrs.set_inverse(true),
&[22] => self.attrs.set_bold(false),
&[23] => self.attrs.set_italic(false),
&[24] => self.attrs.set_underline(false),
&[27] => self.attrs.set_inverse(false),
&[n] if (30..=37).contains(&n) => {
self.attrs.fgcolor = crate::attrs::Color::Idx(to_u8!(n) - 30);
}
&[38, 2, r, g, b] => {
self.attrs.fgcolor =
crate::attrs::Color::Rgb(to_u8!(r), to_u8!(g), to_u8!(b));
}
&[38, 5, i] => {
self.attrs.fgcolor = crate::attrs::Color::Idx(to_u8!(i));
}
&[38] => match next_param!() {
&[2] => {
let r = next_param_u8!();
let g = next_param_u8!();
let b = next_param_u8!();
self.attrs.fgcolor = crate::attrs::Color::Rgb(r, g, b);
}
&[5] => {
self.attrs.fgcolor = crate::attrs::Color::Idx(next_param_u8!());
}
ns => {
if log::log_enabled!(log::Level::Debug) {
let n = if ns.len() == 1 {
format!(
"{}",
ns[0]
)
} else {
format!("{:?}", ns)
};
log::debug!("unhandled SGR mode: 38 {}", n);
}
return;
}
},
&[39] => {
self.attrs.fgcolor = crate::attrs::Color::Default;
}
&[n] if (40..=47).contains(&n) => {
self.attrs.bgcolor = crate::attrs::Color::Idx(to_u8!(n) - 40);
}
&[48, 2, r, g, b] => {
self.attrs.bgcolor =
crate::attrs::Color::Rgb(to_u8!(r), to_u8!(g), to_u8!(b));
}
&[48, 5, i] => {
self.attrs.bgcolor = crate::attrs::Color::Idx(to_u8!(i));
}
&[48] => match next_param!() {
&[2] => {
let r = next_param_u8!();
let g = next_param_u8!();
let b = next_param_u8!();
self.attrs.bgcolor = crate::attrs::Color::Rgb(r, g, b);
}
&[5] => {
self.attrs.bgcolor = crate::attrs::Color::Idx(next_param_u8!());
}
ns => {
if log::log_enabled!(log::Level::Debug) {
let n = if ns.len() == 1 {
format!(
"{}",
ns[0]
)
} else {
format!("{:?}", ns)
};
log::debug!("unhandled SGR mode: 48 {}", n);
}
return;
}
},
&[49] => {
self.attrs.bgcolor = crate::attrs::Color::Default;
}
&[n] if (90..=97).contains(&n) => {
self.attrs.fgcolor = crate::attrs::Color::Idx(to_u8!(n) - 82);
}
&[n] if (100..=107).contains(&n) => {
self.attrs.bgcolor = crate::attrs::Color::Idx(to_u8!(n) - 92);
}
ns => {
if log::log_enabled!(log::Level::Debug) {
let n = if ns.len() == 1 {
format!(
"{}",
ns[0]
)
} else {
format!("{:?}", ns)
};
log::debug!("unhandled SGR mode: {}", n);
}
}
}
}
}
fn decstbm(&mut self, (top, bottom): (u16, u16)) {
self.grid_mut().set_scroll_region(top - 1, bottom - 1);
}
fn osc0(&mut self, s: &[u8]) {
self.osc1(s);
self.osc2(s);
}
fn osc1(&mut self, s: &[u8]) {
if let Ok(s) = std::str::from_utf8(s) {
self.icon_name = s.to_string();
}
}
fn osc2(&mut self, s: &[u8]) {
if let Ok(s) = std::str::from_utf8(s) {
self.title = s.to_string();
}
}
}
impl vte::Perform for Screen {
fn print(&mut self, c: char) {
if c == '\u{fffd}' || ('\u{80}'..'\u{a0}').contains(&c) {
self.errors = self.errors.saturating_add(1);
}
self.text(c);
}
fn execute(&mut self, b: u8) {
match b {
7 => self.bel(),
8 => self.bs(),
9 => self.tab(),
10 => self.lf(),
11 => self.vt(),
12 => self.ff(),
13 => self.cr(),
14 | 15 => {}
_ => {
self.errors = self.errors.saturating_add(1);
log::debug!("unhandled control character: {}", b);
}
}
}
fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, b: u8) {
match intermediates.get(0) {
None => match b {
b'7' => self.decsc(),
b'8' => self.decrc(),
b'=' => self.deckpam(),
b'>' => self.deckpnm(),
b'M' => self.ri(),
b'c' => self.ris(),
b'g' => self.vb(),
_ => {
log::debug!("unhandled escape code: ESC {}", b);
}
},
Some(i) => {
log::debug!("unhandled escape code: ESC {} {}", i, b);
}
}
}
fn csi_dispatch(
&mut self,
params: &vte::Params,
intermediates: &[u8],
_ignore: bool,
c: char,
) {
match intermediates.get(0) {
None => match c {
'@' => self.ich(canonicalize_params_1(params, 1)),
'A' => self.cuu(canonicalize_params_1(params, 1)),
'B' => self.cud(canonicalize_params_1(params, 1)),
'C' => self.cuf(canonicalize_params_1(params, 1)),
'D' => self.cub(canonicalize_params_1(params, 1)),
'G' => self.cha(canonicalize_params_1(params, 1)),
'H' => self.cup(canonicalize_params_2(params, 1, 1)),
'J' => self.ed(canonicalize_params_1(params, 0)),
'K' => self.el(canonicalize_params_1(params, 0)),
'L' => self.il(canonicalize_params_1(params, 1)),
'M' => self.dl(canonicalize_params_1(params, 1)),
'P' => self.dch(canonicalize_params_1(params, 1)),
'S' => self.su(canonicalize_params_1(params, 1)),
'T' => self.sd(canonicalize_params_1(params, 1)),
'X' => self.ech(canonicalize_params_1(params, 1)),
'd' => self.vpa(canonicalize_params_1(params, 1)),
'h' => self.sm(params),
'l' => self.rm(params),
'm' => self.sgr(params),
'r' => {
self.decstbm(canonicalize_params_decstbm(params, self.grid().size()))
}
_ => {
if log::log_enabled!(log::Level::Debug) {
log::debug!(
"unhandled csi sequence: CSI {} {}",
param_str(params),
c
);
}
}
},
Some(b'?') => match c {
'J' => self.decsed(canonicalize_params_1(params, 0)),
'K' => self.decsel(canonicalize_params_1(params, 0)),
'h' => self.decset(params),
'l' => self.decrst(params),
_ => {
if log::log_enabled!(log::Level::Debug) {
log::debug!(
"unhandled csi sequence: CSI ? {} {}",
param_str(params),
c
);
}
}
},
Some(i) => {
if log::log_enabled!(log::Level::Debug) {
log::debug!(
"unhandled csi sequence: CSI {} {} {}",
i,
param_str(params),
c
);
}
}
}
}
fn osc_dispatch(&mut self, params: &[&[u8]], _bel_terminated: bool) {
match (params.get(0), params.get(1)) {
(Some(&b"0"), Some(s)) => self.osc0(s),
(Some(&b"1"), Some(s)) => self.osc1(s),
(Some(&b"2"), Some(s)) => self.osc2(s),
_ => {
if log::log_enabled!(log::Level::Debug) {
log::debug!("unhandled osc sequence: OSC {}", osc_param_str(params),);
}
}
}
}
fn hook(
&mut self,
params: &vte::Params,
intermediates: &[u8],
_ignore: bool,
action: char,
) {
if log::log_enabled!(log::Level::Debug) {
match intermediates.get(0) {
None => log::debug!(
"unhandled dcs sequence: DCS {} {}",
param_str(params),
action,
),
Some(i) => log::debug!(
"unhandled dcs sequence: DCS {} {} {}",
i,
param_str(params),
action,
),
}
}
}
}
fn canonicalize_params_1(params: &vte::Params, default: u16) -> u16 {
let first = params.iter().next().map_or(0, |x| *x.get(0).unwrap_or(&0));
if first == 0 {
default
} else {
first
}
}
fn canonicalize_params_2(
params: &vte::Params,
default1: u16,
default2: u16,
) -> (u16, u16) {
let mut iter = params.iter();
let first = iter.next().map_or(0, |x| *x.get(0).unwrap_or(&0));
let first = if first == 0 { default1 } else { first };
let second = iter.next().map_or(0, |x| *x.get(0).unwrap_or(&0));
let second = if second == 0 { default2 } else { second };
(first, second)
}
fn canonicalize_params_decstbm(
params: &vte::Params,
size: crate::grid::Size,
) -> (u16, u16) {
let mut iter = params.iter();
let top = iter.next().map_or(0, |x| *x.get(0).unwrap_or(&0));
let top = if top == 0 { 1 } else { top };
let bottom = iter.next().map_or(0, |x| *x.get(0).unwrap_or(&0));
let bottom = if bottom == 0 { size.rows } else { bottom };
(top, bottom)
}
fn u16_to_u8(i: u16) -> Option<u8> {
if i > u16::from(u8::max_value()) {
None
} else {
Some(i.try_into().unwrap())
}
}
fn param_str(params: &vte::Params) -> String {
let strs: Vec<_> = params
.iter()
.map(|subparams| {
let subparam_strs: Vec<_> = subparams
.iter()
.map(std::string::ToString::to_string)
.collect();
subparam_strs.join(" : ")
})
.collect();
strs.join(" ; ")
}
fn osc_param_str(params: &[&[u8]]) -> String {
let strs: Vec<_> = params
.iter()
.map(|b| format!("\"{}\"", std::string::String::from_utf8_lossy(*b)))
.collect();
strs.join(" ; ")
}