terminal/embed: remove some allocations and unwraps

jmap-eventsource
Manos Pitsidianakis 2020-10-18 15:02:18 +03:00
parent 89940dd606
commit 7919e95ddd
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
2 changed files with 178 additions and 169 deletions

View File

@ -20,7 +20,8 @@
*/ */
use crate::terminal::position::*; use crate::terminal::position::*;
use melib::{log, ERROR, error::*}; use melib::{error::*, log, ERROR};
use smallvec::SmallVec;
use nix::fcntl::{open, OFlag}; use nix::fcntl::{open, OFlag};
use nix::libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use nix::libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
@ -29,7 +30,10 @@ use nix::sys::stat;
use nix::unistd::{dup2, fork, ForkResult}; use nix::unistd::{dup2, fork, ForkResult};
use nix::{ioctl_none_bad, ioctl_write_ptr_bad}; use nix::{ioctl_none_bad, ioctl_write_ptr_bad};
use std::ffi::{CString, OsStr}; use std::ffi::{CString, OsStr};
use std::os::unix::{ffi::OsStrExt, io::{AsRawFd, FromRawFd, IntoRawFd}}; use std::os::unix::{
ffi::OsStrExt,
io::{AsRawFd, FromRawFd, IntoRawFd},
};
mod grid; mod grid;
@ -51,11 +55,7 @@ ioctl_write_ptr_bad!(set_window_size, TIOCSWINSZ, Winsize);
ioctl_none_bad!(set_controlling_terminal, TIOCSCTTY); ioctl_none_bad!(set_controlling_terminal, TIOCSCTTY);
pub fn create_pty( pub fn create_pty(width: usize, height: usize, command: String) -> Result<Arc<Mutex<EmbedGrid>>> {
width: usize,
height: usize,
command: String,
) -> Result<Arc<Mutex<EmbedGrid>>> {
// Open a new PTY master // Open a new PTY master
let master_fd = posix_openpt(OFlag::O_RDWR)?; let master_fd = posix_openpt(OFlag::O_RDWR)?;
@ -77,11 +77,11 @@ pub fn create_pty(
}; };
let master_fd = master_fd.clone().into_raw_fd(); let master_fd = master_fd.clone().into_raw_fd();
unsafe { set_window_size(master_fd, &winsize).unwrap() }; unsafe { set_window_size(master_fd, &winsize)? };
} }
let child_pid = match fork() { let child_pid = match fork()? {
Ok(ForkResult::Child) => { ForkResult::Child => {
/* Open slave end for pseudoterminal */ /* Open slave end for pseudoterminal */
let slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty())?; let slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty())?;
@ -115,16 +115,19 @@ pub fn create_pty(
} }
} }
/* Find posix sh location, because POSIX shell is not always at /bin/sh */ /* Find posix sh location, because POSIX shell is not always at /bin/sh */
let path_var = std::process::Command::new("getconf").args(&["PATH"]).output()?.stdout; let path_var = std::process::Command::new("getconf")
.args(&["PATH"])
.output()?
.stdout;
for mut p in std::env::split_paths(&OsStr::from_bytes(&path_var[..])) { for mut p in std::env::split_paths(&OsStr::from_bytes(&path_var[..])) {
p.push("sh"); p.push("sh");
if p.exists() { if p.exists() {
if let Err(e) = nix::unistd::execv( if let Err(e) = nix::unistd::execv(
&CString::new(p.as_os_str().as_bytes()).unwrap(), &CString::new(p.as_os_str().as_bytes()).unwrap(),
&[ &[
&CString::new("sh").unwrap(), &CString::new("sh").unwrap(),
&CString::new("-c").unwrap(), &CString::new("-c").unwrap(),
&CString::new(command.as_bytes()).unwrap(), &CString::new(command.as_bytes()).unwrap(),
], ],
) { ) {
log(format!("Could not execute `{}`: {}", command, e,), ERROR); log(format!("Could not execute `{}`: {}", command, e,), ERROR);
@ -135,14 +138,14 @@ pub fn create_pty(
log( log(
format!( format!(
"Could not execute `{}`: did not find the standard POSIX sh shell in PATH = {}", "Could not execute `{}`: did not find the standard POSIX sh shell in PATH = {}",
command, String::from_utf8_lossy(&path_var), command,
String::from_utf8_lossy(&path_var),
), ),
ERROR, ERROR,
); );
std::process::exit(-1); std::process::exit(-1);
} }
Ok(ForkResult::Parent { child }) => child, ForkResult::Parent { child } => child,
Err(e) => panic!(e),
}; };
let stdin = unsafe { std::fs::File::from_raw_fd(master_fd.clone().into_raw_fd()) }; let stdin = unsafe { std::fs::File::from_raw_fd(master_fd.clone().into_raw_fd()) };
@ -163,9 +166,9 @@ pub fn create_pty(
fn forward_pty_translate_escape_codes(pty_fd: std::fs::File, grid: Arc<Mutex<EmbedGrid>>) { fn forward_pty_translate_escape_codes(pty_fd: std::fs::File, grid: Arc<Mutex<EmbedGrid>>) {
let mut bytes_iter = pty_fd.bytes(); let mut bytes_iter = pty_fd.bytes();
debug!("waiting for bytes"); //debug!("waiting for bytes");
while let Some(Ok(byte)) = bytes_iter.next() { while let Some(Ok(byte)) = bytes_iter.next() {
debug!("got a byte? {:?}", byte as char); //debug!("got a byte? {:?}", byte as char);
/* Drink deep, and descend. */ /* Drink deep, and descend. */
grid.lock().unwrap().process_byte(byte); grid.lock().unwrap().process_byte(byte);
} }
@ -174,14 +177,14 @@ fn forward_pty_translate_escape_codes(pty_fd: std::fs::File, grid: Arc<Mutex<Emb
#[derive(Debug)] #[derive(Debug)]
pub enum State { pub enum State {
ExpectingControlChar, ExpectingControlChar,
G0, // Designate G0 Character Set G0, // Designate G0 Character Set
Osc1(Vec<u8>), //ESC ] Operating System Command (OSC is 0x9d). Osc1(SmallVec<[u8; 8]>), //ESC ] Operating System Command (OSC is 0x9d).
Osc2(Vec<u8>, Vec<u8>), Osc2(SmallVec<[u8; 8]>, SmallVec<[u8; 8]>),
Csi, // ESC [ Control Sequence Introducer (CSI is 0x9b). Csi, // ESC [ Control Sequence Introducer (CSI is 0x9b).
Csi1(Vec<u8>), Csi1(SmallVec<[u8; 8]>),
Csi2(Vec<u8>, Vec<u8>), Csi2(SmallVec<[u8; 8]>, SmallVec<[u8; 8]>),
Csi3(Vec<u8>, Vec<u8>, Vec<u8>), Csi3(SmallVec<[u8; 8]>, SmallVec<[u8; 8]>, SmallVec<[u8; 8]>),
CsiQ(Vec<u8>), CsiQ(SmallVec<[u8; 8]>),
Normal, Normal,
} }
@ -264,7 +267,7 @@ impl std::fmt::Display for EscCode<'_> {
"ESC[{}n\t\tCSI Device Status Report (DSR)| Report Cursor Position", "ESC[{}n\t\tCSI Device Status Report (DSR)| Report Cursor Position",
unsafestr!(buf) unsafestr!(buf)
), ),
EscCode(Csi1(ref buf), b't') if buf == b"18" => write!( EscCode(Csi1(ref buf), b't') if buf.as_ref() == b"18" => write!(
f, f,
"ESC[18t\t\tReport the size of the text area in characters", "ESC[18t\t\tReport the size of the text area in characters",
), ),
@ -364,11 +367,11 @@ impl std::fmt::Display for EscCode<'_> {
"ESC[?{}r\t\tCSI Restore DEC Private Mode Values", "ESC[?{}r\t\tCSI Restore DEC Private Mode Values",
unsafestr!(buf) unsafestr!(buf)
), ),
EscCode(CsiQ(ref buf), b'h') if buf == b"25" => write!( EscCode(CsiQ(ref buf), b'h') if buf.as_ref() == b"25" => write!(
f, f,
"ESC[?25h\t\tCSI DEC Private Mode Set (DECSET) show cursor", "ESC[?25h\t\tCSI DEC Private Mode Set (DECSET) show cursor",
), ),
EscCode(CsiQ(ref buf), b'h') if buf == b"12" => write!( EscCode(CsiQ(ref buf), b'h') if buf.as_ref() == b"12" => write!(
f, f,
"ESC[?12h\t\tCSI DEC Private Mode Set (DECSET) Start Blinking Cursor.", "ESC[?12h\t\tCSI DEC Private Mode Set (DECSET) Start Blinking Cursor.",
), ),
@ -377,11 +380,11 @@ impl std::fmt::Display for EscCode<'_> {
"ESC[?{}h\t\tCSI DEC Private Mode Set (DECSET). [UNKNOWN]", "ESC[?{}h\t\tCSI DEC Private Mode Set (DECSET). [UNKNOWN]",
unsafestr!(buf) unsafestr!(buf)
), ),
EscCode(CsiQ(ref buf), b'l') if buf == b"12" => write!( EscCode(CsiQ(ref buf), b'l') if buf.as_ref() == b"12" => write!(
f, f,
"ESC[?12l\t\tCSI DEC Private Mode Set (DECSET) Stop Blinking Cursor", "ESC[?12l\t\tCSI DEC Private Mode Set (DECSET) Stop Blinking Cursor",
), ),
EscCode(CsiQ(ref buf), b'l') if buf == b"25" => write!( EscCode(CsiQ(ref buf), b'l') if buf.as_ref() == b"25" => write!(
f, f,
"ESC[?25l\t\tCSI DEC Private Mode Set (DECSET) hide cursor", "ESC[?25l\t\tCSI DEC Private Mode Set (DECSET) hide cursor",
), ),

View File

@ -67,9 +67,9 @@ pub struct EmbedGrid {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
enum CodepointBuf { enum CodepointBuf {
None, None,
TwoCodepoints(Vec<u8>), TwoCodepoints(u8),
ThreeCodepoints(Vec<u8>), ThreeCodepoints(u8, Option<u8>),
FourCodepoints(Vec<u8>), FourCodepoints(u8, Option<u8>, Option<u8>),
} }
impl EmbedGrid { impl EmbedGrid {
@ -103,7 +103,7 @@ impl EmbedGrid {
if new_val == self.terminal_size { if new_val == self.terminal_size {
return; return;
} }
debug!("resizing to {:?}", new_val); //debug!("resizing to {:?}", new_val);
self.scroll_region.top = 0; self.scroll_region.top = 0;
self.scroll_region.bottom = new_val.1.saturating_sub(1); self.scroll_region.bottom = new_val.1.saturating_sub(1);
@ -129,7 +129,7 @@ impl EmbedGrid {
} }
pub fn stop(&self) { pub fn stop(&self) {
debug!("stopping"); //debug!("stopping");
let _ = nix::sys::signal::kill(debug!(self.child_pid), nix::sys::signal::SIGSTOP); let _ = nix::sys::signal::kill(debug!(self.child_pid), nix::sys::signal::SIGSTOP);
} }
@ -196,7 +196,7 @@ impl EmbedGrid {
*state = State::ExpectingControlChar; *state = State::ExpectingControlChar;
} }
(b']', State::ExpectingControlChar) => { (b']', State::ExpectingControlChar) => {
let buf1 = Vec::new(); let buf1 = SmallVec::new();
*state = State::Osc1(buf1); *state = State::Osc1(buf1);
} }
(b'[', State::ExpectingControlChar) => { (b'[', State::ExpectingControlChar) => {
@ -207,7 +207,7 @@ impl EmbedGrid {
} }
(b'D', State::ExpectingControlChar) => { (b'D', State::ExpectingControlChar) => {
// ESCD Linefeed // ESCD Linefeed
debug!("{}", EscCode::from((&(*state), byte))); //debug!("{}", EscCode::from((&(*state), byte)));
if cursor.1 == scroll_region.bottom { if cursor.1 == scroll_region.bottom {
grid.scroll_up(scroll_region, scroll_region.top, 1); grid.scroll_up(scroll_region, scroll_region.top, 1);
} else { } else {
@ -218,8 +218,8 @@ impl EmbedGrid {
} }
(b'J', State::ExpectingControlChar) => { (b'J', State::ExpectingControlChar) => {
// ESCJ Erase from the cursor to the end of the screen // ESCJ Erase from the cursor to the end of the screen
debug!("sending {}", EscCode::from((&(*state), byte))); //debug!("sending {}", EscCode::from((&(*state), byte)));
debug!("erasing from {:?} to {:?}", cursor, terminal_size); //debug!("erasing from {:?} to {:?}", cursor, terminal_size);
for y in cursor.1..terminal_size.1 { for y in cursor.1..terminal_size.1 {
for x in cursor.0..terminal_size.0 { for x in cursor.0..terminal_size.0 {
grid[(x, y)] = Cell::default(); grid[(x, y)] = Cell::default();
@ -229,21 +229,21 @@ impl EmbedGrid {
} }
(b'K', State::ExpectingControlChar) => { (b'K', State::ExpectingControlChar) => {
// ESCK Erase from the cursor to the end of the line // ESCK Erase from the cursor to the end of the line
debug!("sending {}", EscCode::from((&(*state), byte))); //debug!("sending {}", EscCode::from((&(*state), byte)));
for x in cursor.0..terminal_size.0 { for x in cursor.0..terminal_size.0 {
grid[(x, cursor.1)] = Cell::default(); grid[(x, cursor.1)] = Cell::default();
} }
*state = State::Normal; *state = State::Normal;
} }
(_, State::ExpectingControlChar) => { (_, State::ExpectingControlChar) => {
debug!( //debug!(
"unrecognised: byte is {} and state is {:?}", // "unrecognised: byte is {} and state is {:?}",
byte as char, state // byte as char, state
); //);
*state = State::Normal; *state = State::Normal;
} }
(b'?', State::Csi) => { (b'?', State::Csi) => {
let buf1 = Vec::new(); let buf1 = SmallVec::new();
*state = State::CsiQ(buf1); *state = State::CsiQ(buf1);
} }
/* OSC stuff */ /* OSC stuff */
@ -251,8 +251,8 @@ impl EmbedGrid {
buf.push(c); buf.push(c);
} }
(b';', State::Osc1(ref mut buf1_p)) => { (b';', State::Osc1(ref mut buf1_p)) => {
let buf1 = std::mem::replace(buf1_p, Vec::new()); let buf1 = std::mem::replace(buf1_p, SmallVec::new());
let buf2 = Vec::new(); let buf2 = SmallVec::new();
*state = State::Osc2(buf1, buf2); *state = State::Osc2(buf1, buf2);
} }
(c, State::Osc2(_, ref mut buf)) if (c >= b'0' && c <= b'9') || c == b'?' => { (c, State::Osc2(_, ref mut buf)) if (c >= b'0' && c <= b'9') || c == b'?' => {
@ -260,14 +260,14 @@ impl EmbedGrid {
} }
/* Normal */ /* Normal */
(b'\r', State::Normal) => { (b'\r', State::Normal) => {
debug!("carriage return x-> 0, cursor was: {:?}", cursor); //debug!("carriage return x-> 0, cursor was: {:?}", cursor);
cursor.0 = 0; cursor.0 = 0;
*wrap_next = false; *wrap_next = false;
debug!("cursor became: {:?}", cursor); //debug!("cursor became: {:?}", cursor);
} }
(b'\n', State::Normal) => { (b'\n', State::Normal) => {
//debug!("setting cell {:?} char '{}'", cursor, c as char); //debug!("setting cell {:?} char '{}'", cursor, c as char);
debug!("newline y-> y+1, cursor was: {:?}", cursor); //debug!("newline y-> y+1, cursor was: {:?}", cursor);
if cursor.1 + 1 < terminal_size.1 { if cursor.1 + 1 < terminal_size.1 {
if cursor.1 == scroll_region.bottom { if cursor.1 == scroll_region.bottom {
@ -277,18 +277,18 @@ impl EmbedGrid {
} }
} }
*wrap_next = false; *wrap_next = false;
debug!("cursor became: {:?}", cursor); //debug!("cursor became: {:?}", cursor);
} }
(b'', State::Normal) => { (b'', State::Normal) => {
debug!("Visual bell ^G, ignoring {:?}", cursor); //debug!("Visual bell ^G, ignoring {:?}", cursor);
} }
(0x08, State::Normal) => { (0x08, State::Normal) => {
/* Backspace */ /* Backspace */
debug!("backspace x-> x-1, cursor was: {:?}", cursor); //debug!("backspace x-> x-1, cursor was: {:?}", cursor);
if cursor.0 > 0 { if cursor.0 > 0 {
cursor.0 -= 1; cursor.0 -= 1;
} }
debug!("cursor became: {:?}", cursor); //debug!("cursor became: {:?}", cursor);
} }
(c, State::Normal) => { (c, State::Normal) => {
/* Character to be printed. */ /* Character to be printed. */
@ -298,57 +298,61 @@ impl EmbedGrid {
} else { } else {
match codepoints { match codepoints {
CodepointBuf::None if c & 0b1110_0000 == 0b1100_0000 => { CodepointBuf::None if c & 0b1110_0000 == 0b1100_0000 => {
*codepoints = CodepointBuf::TwoCodepoints(vec![c]); *codepoints = CodepointBuf::TwoCodepoints(c);
return; return;
} }
CodepointBuf::None if c & 0b1111_0000 == 0b1110_0000 => { CodepointBuf::None if c & 0b1111_0000 == 0b1110_0000 => {
*codepoints = CodepointBuf::ThreeCodepoints(vec![c]); *codepoints = CodepointBuf::ThreeCodepoints(c, None);
return; return;
} }
CodepointBuf::None if c & 0b1111_1000 == 0b1111_0000 => { CodepointBuf::None if c & 0b1111_1000 == 0b1111_0000 => {
*codepoints = CodepointBuf::FourCodepoints(vec![c]); *codepoints = CodepointBuf::FourCodepoints(c, None, None);
return; return;
} }
CodepointBuf::TwoCodepoints(buf) => { CodepointBuf::TwoCodepoints(b) => {
debug!("two byte char = "); //debug!("two byte char = ");
unsafe { std::str::from_utf8_unchecked(&[buf[0], c]) } unsafe { std::str::from_utf8_unchecked(&[*b, c]) }
.chars() .chars()
.next() .next()
.unwrap() .unwrap()
} }
CodepointBuf::ThreeCodepoints(buf) if buf.len() == 2 => { CodepointBuf::ThreeCodepoints(b, Some(b1)) => {
debug!("three byte char = ",); //debug!("three byte char = ",);
unsafe { std::str::from_utf8_unchecked(&[buf[0], buf[1], c]) } unsafe { std::str::from_utf8_unchecked(&[*b, *b1, c]) }
.chars() .chars()
.next() .next()
.unwrap() .unwrap()
} }
CodepointBuf::ThreeCodepoints(buf) => { CodepointBuf::ThreeCodepoints(_, ref mut b @ None) => {
buf.push(c); *b = Some(c);
return; return;
} }
CodepointBuf::FourCodepoints(buf) if buf.len() == 3 => { CodepointBuf::FourCodepoints(b, Some(b1), Some(b2)) => {
debug!("four byte char = ",); //debug!("four byte char = ",);
unsafe { std::str::from_utf8_unchecked(&[buf[0], buf[1], buf[2], c]) } unsafe { std::str::from_utf8_unchecked(&[*b, *b1, *b2, c]) }
.chars() .chars()
.next() .next()
.unwrap() .unwrap()
} }
CodepointBuf::FourCodepoints(buf) => { CodepointBuf::FourCodepoints(_, ref mut b1 @ None, None) => {
buf.push(c); *b1 = Some(c);
return;
}
CodepointBuf::FourCodepoints(_, _, ref mut b2 @ None) => {
*b2 = Some(c);
return; return;
} }
_ => { _ => {
debug!( //debug!(
"invalid utf8 sequence: codepoints = {:?} and c={}", // "invalid utf8 sequence: codepoints = {:?} and c={}",
codepoints, c // codepoints, c
); //);
*codepoints = CodepointBuf::None; *codepoints = CodepointBuf::None;
return; return;
} }
} }
}; };
debug!("c = {:?}\tcursor={:?}", c, cursor); //debug!("c = {:?}\tcursor={:?}", c, cursor);
*codepoints = CodepointBuf::None; *codepoints = CodepointBuf::None;
if *auto_wrap_mode && *wrap_next { if *auto_wrap_mode && *wrap_next {
*wrap_next = false; *wrap_next = false;
@ -360,9 +364,9 @@ impl EmbedGrid {
cursor.0 = 0; cursor.0 = 0;
} }
if c == '↪' { //if c == '↪' {
debug!("↪ cursor is {:?}", cursor_val!()); //debug!("↪ cursor is {:?}", cursor_val!());
} //}
grid[cursor_val!()].set_ch(c); grid[cursor_val!()].set_ch(c);
grid[cursor_val!()].set_fg(*fg_color); grid[cursor_val!()].set_fg(*fg_color);
grid[cursor_val!()].set_bg(*bg_color); grid[cursor_val!()].set_bg(*bg_color);
@ -387,13 +391,13 @@ impl EmbedGrid {
} }
(b'u', State::Csi) => { (b'u', State::Csi) => {
/* restore cursor */ /* restore cursor */
debug!("restore cursor {}", EscCode::from((&(*state), byte))); //debug!("restore cursor {}", EscCode::from((&(*state), byte)));
*show_cursor = true; *show_cursor = true;
*state = State::Normal; *state = State::Normal;
} }
(b'm', State::Csi) => { (b'm', State::Csi) => {
/* Reset character Attributes (SGR). Ps = 0 -> Normal (default), VT100 */ /* Reset character Attributes (SGR). Ps = 0 -> Normal (default), VT100 */
debug!("{}", EscCode::from((&(*state), byte))); //debug!("{}", EscCode::from((&(*state), byte)));
*fg_color = Color::Default; *fg_color = Color::Default;
*bg_color = Color::Default; *bg_color = Color::Default;
grid[cursor_val!()].set_fg(Color::Default); grid[cursor_val!()].set_fg(Color::Default);
@ -402,10 +406,10 @@ impl EmbedGrid {
} }
(b'C', State::Csi) => { (b'C', State::Csi) => {
// ESC[C CSI Cursor Forward one Time // ESC[C CSI Cursor Forward one Time
debug!("cursor forward one time, cursor was: {:?}", cursor); //debug!("cursor forward one time, cursor was: {:?}", cursor);
cursor.0 = std::cmp::min(cursor.0 + 1, terminal_size.0.saturating_sub(1)); cursor.0 = std::cmp::min(cursor.0 + 1, terminal_size.0.saturating_sub(1));
*wrap_next = false; *wrap_next = false;
debug!("cursor became: {:?}", cursor); //debug!("cursor became: {:?}", cursor);
*state = State::Normal; *state = State::Normal;
} }
/* CSI ? stuff */ /* CSI ? stuff */
@ -430,7 +434,7 @@ impl EmbedGrid {
_ => {} _ => {}
} }
debug!("{}", EscCode::from((&(*state), byte))); //debug!("{}", EscCode::from((&(*state), byte)));
*state = State::Normal; *state = State::Normal;
} }
(b'l', State::CsiQ(ref mut buf)) => { (b'l', State::CsiQ(ref mut buf)) => {
@ -456,12 +460,12 @@ impl EmbedGrid {
} }
_ => {} _ => {}
} }
debug!("{}", EscCode::from((&(*state), byte))); //debug!("{}", EscCode::from((&(*state), byte)));
*state = State::Normal; *state = State::Normal;
} }
/* END OF CSI ? stuff */ /* END OF CSI ? stuff */
(c, State::Csi) if c >= b'0' && c <= b'9' => { (c, State::Csi) if c >= b'0' && c <= b'9' => {
let mut buf1 = Vec::new(); let mut buf1 = SmallVec::new();
buf1.push(c); buf1.push(c);
*state = State::Csi1(buf1); *state = State::Csi1(buf1);
} }
@ -485,13 +489,13 @@ impl EmbedGrid {
), ),
Default::default(), Default::default(),
); );
debug!("{}", EscCode::from((&(*state), byte))); //debug!("{}", EscCode::from((&(*state), byte)));
*state = State::Normal; *state = State::Normal;
} }
(b'K', State::Csi) => { (b'K', State::Csi) => {
/* Erase in Line (ED), VT100.*/ /* Erase in Line (ED), VT100.*/
/* Erase to right (Default) */ /* Erase to right (Default) */
debug!("{}", EscCode::from((&(*state), byte))); //debug!("{}", EscCode::from((&(*state), byte)));
for x in cursor.0..terminal_size.0 { for x in cursor.0..terminal_size.0 {
grid[(x, cursor.1)] = Cell::default(); grid[(x, cursor.1)] = Cell::default();
} }
@ -509,7 +513,7 @@ impl EmbedGrid {
grid.scroll_down(scroll_region, cursor.1, n); grid.scroll_down(scroll_region, cursor.1, n);
debug!("{}", EscCode::from((&(*state), byte))); //debug!("{}", EscCode::from((&(*state), byte)));
*state = State::Normal; *state = State::Normal;
} }
(b'M', State::Csi) | (b'M', State::Csi1(_)) => { (b'M', State::Csi) | (b'M', State::Csi1(_)) => {
@ -524,40 +528,40 @@ impl EmbedGrid {
grid.scroll_up(scroll_region, cursor.1, n); grid.scroll_up(scroll_region, cursor.1, n);
debug!("{}", EscCode::from((&(*state), byte))); //debug!("{}", EscCode::from((&(*state), byte)));
*state = State::Normal; *state = State::Normal;
} }
(b'A', State::Csi) => { (b'A', State::Csi) => {
// Move cursor up 1 line // Move cursor up 1 line
debug!("cursor up 1 times, cursor was: {:?}", cursor); //debug!("cursor up 1 times, cursor was: {:?}", cursor);
if cursor.1 > 0 { if cursor.1 > 0 {
cursor.1 -= 1; cursor.1 -= 1;
} else { } else {
debug!("cursor.1 == 0"); //debug!("cursor.1 == 0");
} }
*wrap_next = false; *wrap_next = false;
debug!("cursor became: {:?}", cursor); //debug!("cursor became: {:?}", cursor);
*state = State::Normal; *state = State::Normal;
} }
(b'K', State::Csi1(buf)) if buf == b"0" => { (b'K', State::Csi1(buf)) if buf.as_ref() == b"0" => {
/* Erase in Line (ED), VT100.*/ /* Erase in Line (ED), VT100.*/
/* Erase to right (Default) */ /* Erase to right (Default) */
debug!("{}", EscCode::from((&(*state), byte))); //debug!("{}", EscCode::from((&(*state), byte)));
for x in cursor.0..terminal_size.0 { for x in cursor.0..terminal_size.0 {
grid[(x, cursor.1)] = Cell::default(); grid[(x, cursor.1)] = Cell::default();
} }
*state = State::Normal; *state = State::Normal;
} }
(b'K', State::Csi1(buf)) if buf == b"1" => { (b'K', State::Csi1(buf)) if buf.as_ref() == b"1" => {
/* Erase in Line (ED), VT100.*/ /* Erase in Line (ED), VT100.*/
/* Erase to left (Default) */ /* Erase to left (Default) */
for x in 0..=cursor.0 { for x in 0..=cursor.0 {
grid[(x, cursor.1)] = Cell::default(); grid[(x, cursor.1)] = Cell::default();
} }
debug!("{}", EscCode::from((&(*state), byte))); //debug!("{}", EscCode::from((&(*state), byte)));
*state = State::Normal; *state = State::Normal;
} }
(b'K', State::Csi1(buf)) if buf == b"2" => { (b'K', State::Csi1(buf)) if buf.as_ref() == b"2" => {
/* Erase in Line (ED), VT100.*/ /* Erase in Line (ED), VT100.*/
/* Erase all */ /* Erase all */
for y in 0..terminal_size.1 { for y in 0..terminal_size.1 {
@ -565,7 +569,7 @@ impl EmbedGrid {
grid[(x, y)] = Cell::default(); grid[(x, y)] = Cell::default();
} }
} }
debug!("{}", EscCode::from((&(*state), byte))); //debug!("{}", EscCode::from((&(*state), byte)));
clear_area( clear_area(
grid, grid,
((0, 0), pos_dec(*terminal_size, (1, 1))), ((0, 0), pos_dec(*terminal_size, (1, 1))),
@ -573,7 +577,7 @@ impl EmbedGrid {
); );
*state = State::Normal; *state = State::Normal;
} }
(b'J', State::Csi1(ref buf)) if buf == b"0" => { (b'J', State::Csi1(ref buf)) if buf.as_ref() == b"0" => {
/* Erase in Display (ED), VT100.*/ /* Erase in Display (ED), VT100.*/
/* Erase Below (default). */ /* Erase Below (default). */
clear_area( clear_area(
@ -593,10 +597,10 @@ impl EmbedGrid {
), ),
Default::default(), Default::default(),
); );
debug!("{}", EscCode::from((&(*state), byte))); //debug!("{}", EscCode::from((&(*state), byte)));
*state = State::Normal; *state = State::Normal;
} }
(b'J', State::Csi1(ref buf)) if buf == b"1" => { (b'J', State::Csi1(ref buf)) if buf.as_ref() == b"1" => {
/* Erase in Display (ED), VT100.*/ /* Erase in Display (ED), VT100.*/
/* Erase Above */ /* Erase Above */
clear_area( clear_area(
@ -610,10 +614,10 @@ impl EmbedGrid {
), ),
Default::default(), Default::default(),
); );
debug!("{}", EscCode::from((&(*state), byte))); //debug!("{}", EscCode::from((&(*state), byte)));
*state = State::Normal; *state = State::Normal;
} }
(b'J', State::Csi1(ref buf)) if buf == b"2" => { (b'J', State::Csi1(ref buf)) if buf.as_ref() == b"2" => {
/* Erase in Display (ED), VT100.*/ /* Erase in Display (ED), VT100.*/
/* Erase All */ /* Erase All */
clear_area( clear_area(
@ -621,7 +625,7 @@ impl EmbedGrid {
((0, 0), pos_dec(*terminal_size, (1, 1))), ((0, 0), pos_dec(*terminal_size, (1, 1))),
Default::default(), Default::default(),
); );
debug!("{}", EscCode::from((&(*state), byte))); //debug!("{}", EscCode::from((&(*state), byte)));
*state = State::Normal; *state = State::Normal;
} }
(b'X', State::Csi1(ref buf)) => { (b'X', State::Csi1(ref buf)) => {
@ -644,15 +648,15 @@ impl EmbedGrid {
cur_x += 1; cur_x += 1;
ctr += 1; ctr += 1;
} }
debug!("Erased {} Character(s)", ps); //debug!("Erased {} Character(s)", ps);
*state = State::Normal; *state = State::Normal;
} }
(b't', State::Csi1(buf)) => { (b't', State::Csi1(buf)) => {
/* Window manipulation */ /* Window manipulation */
if buf == b"18" || buf == b"19" { if buf.as_ref() == b"18" || buf.as_ref() == b"19" {
// Ps = 18 → Report the size of the text area in characters as CSI 8 ; height ; width t // Ps = 18 → Report the size of the text area in characters as CSI 8 ; height ; width t
debug!("report size of the text area"); //debug!("report size of the text area");
debug!("got {}", EscCode::from((&(*state), byte))); //debug!("got {}", EscCode::from((&(*state), byte)));
stdin.write_all(b"\x1b[8;").unwrap(); stdin.write_all(b"\x1b[8;").unwrap();
stdin stdin
.write_all((terminal_size.1).to_string().as_bytes()) .write_all((terminal_size.1).to_string().as_bytes())
@ -664,15 +668,15 @@ impl EmbedGrid {
stdin.write_all(&[b't']).unwrap(); stdin.write_all(&[b't']).unwrap();
stdin.flush().unwrap(); stdin.flush().unwrap();
} else { } else {
debug!("ignoring unknown code {}", EscCode::from((&(*state), byte))); //debug!("ignoring unknown code {}", EscCode::from((&(*state), byte)));
} }
*state = State::Normal; *state = State::Normal;
} }
(b'n', State::Csi1(_)) => { (b'n', State::Csi1(_)) => {
// Ps = 6 ⇒ Report Cursor Position (CPR) [row;column]. // Ps = 6 ⇒ Report Cursor Position (CPR) [row;column].
// Result is CSI r ; c R // Result is CSI r ; c R
debug!("report cursor position"); //debug!("report cursor position");
debug!("got {}", EscCode::from((&(*state), byte))); //debug!("got {}", EscCode::from((&(*state), byte)));
stdin.write_all(&[b'\x1b', b'[']).unwrap(); stdin.write_all(&[b'\x1b', b'[']).unwrap();
stdin stdin
.write_all((cursor.1 + 1).to_string().as_bytes()) .write_all((cursor.1 + 1).to_string().as_bytes())
@ -690,13 +694,13 @@ impl EmbedGrid {
let offset = unsafe { std::str::from_utf8_unchecked(buf) } let offset = unsafe { std::str::from_utf8_unchecked(buf) }
.parse::<usize>() .parse::<usize>()
.unwrap(); .unwrap();
debug!("cursor up {} times, cursor was: {:?}", offset, cursor); //debug!("cursor up {} times, cursor was: {:?}", offset, cursor);
if cursor.1 >= offset { if cursor.1 >= offset {
cursor.1 -= offset; cursor.1 -= offset;
} else { } else {
debug!("offset > cursor.1"); //debug!("offset > cursor.1");
} }
debug!("cursor became: {:?}", cursor); //debug!("cursor became: {:?}", cursor);
*state = State::Normal; *state = State::Normal;
} }
(b'B', State::Csi1(buf)) => { (b'B', State::Csi1(buf)) => {
@ -704,7 +708,7 @@ impl EmbedGrid {
let offset = unsafe { std::str::from_utf8_unchecked(buf) } let offset = unsafe { std::str::from_utf8_unchecked(buf) }
.parse::<usize>() .parse::<usize>()
.unwrap(); .unwrap();
debug!("cursor down {} times, cursor was: {:?}", offset, cursor); //debug!("cursor down {} times, cursor was: {:?}", offset, cursor);
if cursor.1 == scroll_region.bottom { if cursor.1 == scroll_region.bottom {
/* scroll down */ /* scroll down */
for y in scroll_region.top..scroll_region.bottom { for y in scroll_region.top..scroll_region.bottom {
@ -722,7 +726,7 @@ impl EmbedGrid {
cursor.1 = terminal_size.1.saturating_sub(1); cursor.1 = terminal_size.1.saturating_sub(1);
} }
*wrap_next = false; *wrap_next = false;
debug!("cursor became: {:?}", cursor); //debug!("cursor became: {:?}", cursor);
*state = State::Normal; *state = State::Normal;
} }
(b'D', State::Csi1(buf)) => { (b'D', State::Csi1(buf)) => {
@ -733,10 +737,10 @@ impl EmbedGrid {
if cursor.0 >= offset { if cursor.0 >= offset {
cursor.0 -= offset; cursor.0 -= offset;
} }
debug!( //debug!(
"ESC[ {} D cursor backwards cursor became: {:?}", // "ESC[ {} D cursor backwards cursor became: {:?}",
offset, cursor // offset, cursor
); // );
*state = State::Normal; *state = State::Normal;
} }
(b'E', State::Csi1(buf)) => { (b'E', State::Csi1(buf)) => {
@ -744,10 +748,10 @@ impl EmbedGrid {
let offset = unsafe { std::str::from_utf8_unchecked(buf) } let offset = unsafe { std::str::from_utf8_unchecked(buf) }
.parse::<usize>() .parse::<usize>()
.unwrap(); .unwrap();
debug!( //debug!(
"cursor next line {} times, cursor was: {:?}", // "cursor next line {} times, cursor was: {:?}",
offset, cursor // offset, cursor
); //);
if offset + cursor.1 < terminal_size.1 { if offset + cursor.1 < terminal_size.1 {
cursor.1 += offset; cursor.1 += offset;
} }
@ -756,7 +760,7 @@ impl EmbedGrid {
} }
cursor.0 = 0; cursor.0 = 0;
*wrap_next = false; *wrap_next = false;
debug!("cursor became: {:?}", cursor); //debug!("cursor became: {:?}", cursor);
*state = State::Normal; *state = State::Normal;
} }
(b'F', State::Csi1(buf)) => { (b'F', State::Csi1(buf)) => {
@ -764,14 +768,14 @@ impl EmbedGrid {
let offset = unsafe { std::str::from_utf8_unchecked(buf) } let offset = unsafe { std::str::from_utf8_unchecked(buf) }
.parse::<usize>() .parse::<usize>()
.unwrap(); .unwrap();
debug!( //debug!(
"cursor previous line {} times, cursor was: {:?}", // "cursor previous line {} times, cursor was: {:?}",
offset, cursor // offset, cursor
); //);
cursor.1 = cursor.1.saturating_sub(offset); cursor.1 = cursor.1.saturating_sub(offset);
cursor.0 = 0; cursor.0 = 0;
*wrap_next = false; *wrap_next = false;
debug!("cursor became: {:?}", cursor); //debug!("cursor became: {:?}", cursor);
*state = State::Normal; *state = State::Normal;
} }
(b'G', State::Csi1(_)) | (b'G', State::Csi) => { (b'G', State::Csi1(_)) | (b'G', State::Csi) => {
@ -783,17 +787,17 @@ impl EmbedGrid {
} else { } else {
1 1
}; };
debug!("cursor absolute {}, cursor was: {:?}", new_col, cursor); //debug!("cursor absolute {}, cursor was: {:?}", new_col, cursor);
if new_col < terminal_size.0 { if new_col < terminal_size.0 {
cursor.0 = new_col.saturating_sub(1); cursor.0 = new_col.saturating_sub(1);
} else { } else {
debug!( //debug!(
"error: new_cal = {} > terminal.size.0 = {}\nterminal_size = {:?}", // "error: new_cal = {} > terminal.size.0 = {}\nterminal_size = {:?}",
new_col, terminal_size.0, terminal_size // new_col, terminal_size.0, terminal_size
); //);
} }
*wrap_next = false; *wrap_next = false;
debug!("cursor became: {:?}", cursor); //debug!("cursor became: {:?}", cursor);
*state = State::Normal; *state = State::Normal;
} }
(b'C', State::Csi1(buf)) => { (b'C', State::Csi1(buf)) => {
@ -801,11 +805,11 @@ impl EmbedGrid {
let offset = unsafe { std::str::from_utf8_unchecked(buf) } let offset = unsafe { std::str::from_utf8_unchecked(buf) }
.parse::<usize>() .parse::<usize>()
.unwrap(); .unwrap();
debug!("cursor forward {} times, cursor was: {:?}", offset, cursor); //debug!("cursor forward {} times, cursor was: {:?}", offset, cursor);
if cursor.0 + offset < terminal_size.0 { if cursor.0 + offset < terminal_size.0 {
cursor.0 += offset; cursor.0 += offset;
} }
debug!("cursor became: {:?}", cursor); //debug!("cursor became: {:?}", cursor);
*state = State::Normal; *state = State::Normal;
} }
(b'P', State::Csi1(_)) | (b'P', State::Csi) => { (b'P', State::Csi1(_)) | (b'P', State::Csi) => {
@ -824,10 +828,10 @@ impl EmbedGrid {
for x in (terminal_size.0 - offset)..terminal_size.0 { for x in (terminal_size.0 - offset)..terminal_size.0 {
grid[(x, cursor.1)].set_ch(' '); grid[(x, cursor.1)].set_ch(' ');
} }
debug!( //debug!(
"Delete {} Character(s) with cursor at {:?} ", // "Delete {} Character(s) with cursor at {:?} ",
offset, cursor // offset, cursor
); //);
*state = State::Normal; *state = State::Normal;
} }
(b'd', State::Csi1(_)) | (b'd', State::Csi) => { (b'd', State::Csi1(_)) | (b'd', State::Csi) => {
@ -839,21 +843,21 @@ impl EmbedGrid {
} else { } else {
1 1
}; };
debug!( //debug!(
"Line position absolute row {} with cursor at {:?}", // "Line position absolute row {} with cursor at {:?}",
row, cursor // row, cursor
); //);
cursor.1 = row.saturating_sub(1); cursor.1 = row.saturating_sub(1);
if scroll_region.top + cursor.1 >= terminal_size.1 { if scroll_region.top + cursor.1 >= terminal_size.1 {
cursor.1 = terminal_size.1.saturating_sub(1); cursor.1 = terminal_size.1.saturating_sub(1);
} }
*wrap_next = false; *wrap_next = false;
debug!("cursor became: {:?}", cursor); //debug!("cursor became: {:?}", cursor);
*state = State::Normal; *state = State::Normal;
} }
(b';', State::Csi1(ref mut buf1_p)) => { (b';', State::Csi1(ref mut buf1_p)) => {
let buf1 = std::mem::replace(buf1_p, Vec::new()); let buf1 = std::mem::replace(buf1_p, SmallVec::new());
let buf2 = Vec::new(); let buf2 = SmallVec::new();
*state = State::Csi2(buf1, buf2); *state = State::Csi2(buf1, buf2);
} }
(b'm', State::Csi1(ref buf1)) => { (b'm', State::Csi1(ref buf1)) => {
@ -989,13 +993,13 @@ impl EmbedGrid {
buf.push(c); buf.push(c);
} }
(b';', State::Csi2(ref mut buf1_p, ref mut buf2_p)) => { (b';', State::Csi2(ref mut buf1_p, ref mut buf2_p)) => {
let buf1 = std::mem::replace(buf1_p, Vec::new()); let buf1 = std::mem::replace(buf1_p, SmallVec::new());
let buf2 = std::mem::replace(buf2_p, Vec::new()); let buf2 = std::mem::replace(buf2_p, SmallVec::new());
let buf3 = Vec::new(); let buf3 = SmallVec::new();
*state = State::Csi3(buf1, buf2, buf3); *state = State::Csi3(buf1, buf2, buf3);
} }
(b't', State::Csi2(_, _)) => { (b't', State::Csi2(_, _)) => {
debug!("ignoring {}", EscCode::from((&(*state), byte))); //debug!("ignoring {}", EscCode::from((&(*state), byte)));
// Window manipulation, skip it // Window manipulation, skip it
*state = State::Normal; *state = State::Normal;
} }
@ -1015,7 +1019,7 @@ impl EmbedGrid {
}; };
let (min_y, max_y) = if *origin_mode { let (min_y, max_y) = if *origin_mode {
debug!(*origin_mode); //debug!(*origin_mode);
orig_y += scroll_region.top; orig_y += scroll_region.top;
(scroll_region.top, scroll_region.bottom) (scroll_region.top, scroll_region.bottom)
} else { } else {
@ -1026,13 +1030,13 @@ impl EmbedGrid {
cursor.1 = std::cmp::max(min_y, std::cmp::min(max_y, orig_y - 1)); cursor.1 = std::cmp::max(min_y, std::cmp::min(max_y, orig_y - 1));
*wrap_next = false; *wrap_next = false;
debug!("{}", EscCode::from((&(*state), byte)),); //debug!("{}", EscCode::from((&(*state), byte)),);
debug!( //debug!(
"cursor set to ({},{}), cursor was: {:?}", // "cursor set to ({},{}), cursor was: {:?}",
orig_x, orig_y, cursor // orig_x, orig_y, cursor
); //);
debug!("cursor became: {:?}", cursor); //debug!("cursor became: {:?}", cursor);
*state = State::Normal; *state = State::Normal;
} }
(c, State::Csi2(_, ref mut buf)) if c >= b'0' && c <= b'9' => { (c, State::Csi2(_, ref mut buf)) if c >= b'0' && c <= b'9' => {
@ -1059,11 +1063,11 @@ impl EmbedGrid {
*cursor = (0, 0); *cursor = (0, 0);
*wrap_next = false; *wrap_next = false;
} }
debug!("set scrolling region to {:?}", scroll_region); //debug!("set scrolling region to {:?}", scroll_region);
*state = State::Normal; *state = State::Normal;
} }
(b't', State::Csi3(_, _, _)) => { (b't', State::Csi3(_, _, _)) => {
debug!("ignoring {}", EscCode::from((&(*state), byte))); //debug!("ignoring {}", EscCode::from((&(*state), byte)));
// Window manipulation, skip it // Window manipulation, skip it
*state = State::Normal; *state = State::Normal;
} }
@ -1071,32 +1075,34 @@ impl EmbedGrid {
(c, State::Csi3(_, _, ref mut buf)) if c >= b'0' && c <= b'9' => { (c, State::Csi3(_, _, ref mut buf)) if c >= b'0' && c <= b'9' => {
buf.push(c); buf.push(c);
} }
(b'm', State::Csi3(ref buf1, ref buf2, ref buf3)) if buf1 == b"38" && buf2 == b"5" => { (b'm', State::Csi3(ref buf1, ref buf2, ref buf3))
if buf1.as_ref() == b"38" && buf2.as_ref() == b"5" =>
{
/* Set character attributes | foreground color */ /* Set character attributes | foreground color */
*fg_color = if let Ok(byte) = *fg_color = if let Ok(byte) =
u8::from_str_radix(unsafe { std::str::from_utf8_unchecked(buf3) }, 10) u8::from_str_radix(unsafe { std::str::from_utf8_unchecked(buf3) }, 10)
{ {
debug!("parsed buf as {}", byte); //debug!("parsed buf as {}", byte);
Color::Byte(byte) Color::Byte(byte)
} else { } else {
Color::Default Color::Default
}; };
grid[cursor_val!()].set_fg(*fg_color); grid[cursor_val!()].set_fg(*fg_color);
debug!("{}", EscCode::from((&(*state), byte)));
*state = State::Normal; *state = State::Normal;
} }
(b'm', State::Csi3(ref buf1, ref buf2, ref buf3)) if buf1 == b"48" && buf2 == b"5" => { (b'm', State::Csi3(ref buf1, ref buf2, ref buf3))
if buf1.as_ref() == b"48" && buf2.as_ref() == b"5" =>
{
/* Set character attributes | background color */ /* Set character attributes | background color */
*bg_color = if let Ok(byte) = *bg_color = if let Ok(byte) =
u8::from_str_radix(unsafe { std::str::from_utf8_unchecked(buf3) }, 10) u8::from_str_radix(unsafe { std::str::from_utf8_unchecked(buf3) }, 10)
{ {
debug!("parsed buf as {}", byte); //debug!("parsed buf as {}", byte);
Color::Byte(byte) Color::Byte(byte)
} else { } else {
Color::Default Color::Default
}; };
grid[cursor_val!()].set_bg(*bg_color); grid[cursor_val!()].set_bg(*bg_color);
debug!("{}", EscCode::from((&(*state), byte)));
*state = State::Normal; *state = State::Normal;
} }
(_, State::Csi) => { (_, State::Csi) => {