embed: split EmbedGrid to EmbedTerminal and EmbedGrid
An embedded pseudoterminal was enclosed in the EmbedGrid struct. This commit splits it into EmbedTerminal and EmbedGrid, with EmbedGrid containing only the CellBuffer grid logic. With this change we can reuse EmbedGrid to parse ANSI output from external programs into meli's CellBuffer's.master
parent
ae8c2addab
commit
592339bdca
|
@ -26,7 +26,7 @@ use melib::Draft;
|
|||
|
||||
use crate::conf::accounts::JobRequest;
|
||||
use crate::jobs::JoinHandle;
|
||||
use crate::terminal::embed::EmbedGrid;
|
||||
use crate::terminal::embed::EmbedTerminal;
|
||||
use indexmap::IndexSet;
|
||||
use nix::sys::wait::WaitStatus;
|
||||
use std::convert::TryInto;
|
||||
|
@ -53,13 +53,13 @@ enum Cursor {
|
|||
|
||||
#[derive(Debug)]
|
||||
enum EmbedStatus {
|
||||
Stopped(Arc<Mutex<EmbedGrid>>, File),
|
||||
Running(Arc<Mutex<EmbedGrid>>, File),
|
||||
Stopped(Arc<Mutex<EmbedTerminal>>, File),
|
||||
Running(Arc<Mutex<EmbedTerminal>>, File),
|
||||
}
|
||||
|
||||
impl std::ops::Deref for EmbedStatus {
|
||||
type Target = Arc<Mutex<EmbedGrid>>;
|
||||
fn deref(&self) -> &Arc<Mutex<EmbedGrid>> {
|
||||
type Target = Arc<Mutex<EmbedTerminal>>;
|
||||
fn deref(&self) -> &Arc<Mutex<EmbedTerminal>> {
|
||||
use EmbedStatus::*;
|
||||
match self {
|
||||
Stopped(ref e, _) | Running(ref e, _) => e,
|
||||
|
@ -68,7 +68,7 @@ impl std::ops::Deref for EmbedStatus {
|
|||
}
|
||||
|
||||
impl std::ops::DerefMut for EmbedStatus {
|
||||
fn deref_mut(&mut self) -> &mut Arc<Mutex<EmbedGrid>> {
|
||||
fn deref_mut(&mut self) -> &mut Arc<Mutex<EmbedTerminal>> {
|
||||
use EmbedStatus::*;
|
||||
match self {
|
||||
Stopped(ref mut e, _) | Running(ref mut e, _) => e,
|
||||
|
@ -794,9 +794,9 @@ impl Component for Composer {
|
|||
clear_area(grid, embed_area, theme_default);
|
||||
copy_area(
|
||||
grid,
|
||||
&guard.grid,
|
||||
&guard.grid.buffer(),
|
||||
embed_area,
|
||||
((0, 0), pos_dec(guard.terminal_size, (1, 1))),
|
||||
((0, 0), pos_dec(guard.grid.terminal_size, (1, 1))),
|
||||
);
|
||||
guard.set_terminal_size((width!(embed_area), height!(embed_area)));
|
||||
context.dirty_areas.push_back(area);
|
||||
|
@ -807,9 +807,9 @@ impl Component for Composer {
|
|||
let guard = embed_pty.lock().unwrap();
|
||||
copy_area(
|
||||
grid,
|
||||
&guard.grid,
|
||||
&guard.grid.buffer(),
|
||||
embed_area,
|
||||
((0, 0), pos_dec(guard.terminal_size, (1, 1))),
|
||||
((0, 0), pos_dec(guard.grid.terminal_size, (1, 1))),
|
||||
);
|
||||
change_colors(grid, embed_area, Color::Byte(8), theme_default.bg);
|
||||
const STOPPED_MESSAGE: &str = "process has stopped, press 'e' to re-activate";
|
||||
|
|
|
@ -37,7 +37,7 @@ use std::os::unix::{
|
|||
|
||||
mod grid;
|
||||
|
||||
pub use grid::EmbedGrid;
|
||||
pub use grid::{EmbedGrid, EmbedTerminal};
|
||||
|
||||
// ioctl request code to "Make the given terminal the controlling terminal of the calling process"
|
||||
use libc::TIOCSCTTY;
|
||||
|
@ -55,7 +55,11 @@ ioctl_write_ptr_bad!(set_window_size, TIOCSWINSZ, Winsize);
|
|||
|
||||
ioctl_none_bad!(set_controlling_terminal, TIOCSCTTY);
|
||||
|
||||
pub fn create_pty(width: usize, height: usize, command: String) -> Result<Arc<Mutex<EmbedGrid>>> {
|
||||
pub fn create_pty(
|
||||
width: usize,
|
||||
height: usize,
|
||||
command: String,
|
||||
) -> Result<Arc<Mutex<EmbedTerminal>>> {
|
||||
// Open a new PTY master
|
||||
let master_fd = posix_openpt(OFlag::O_RDWR)?;
|
||||
|
||||
|
@ -149,7 +153,7 @@ pub fn create_pty(width: usize, height: usize, command: String) -> Result<Arc<Mu
|
|||
};
|
||||
|
||||
let stdin = unsafe { std::fs::File::from_raw_fd(master_fd.clone().into_raw_fd()) };
|
||||
let mut embed_grid = EmbedGrid::new(stdin, child_pid);
|
||||
let mut embed_grid = EmbedTerminal::new(stdin, child_pid);
|
||||
embed_grid.set_terminal_size((width, height));
|
||||
let grid = Arc::new(Mutex::new(embed_grid));
|
||||
let grid_ = grid.clone();
|
||||
|
@ -164,7 +168,7 @@ pub fn create_pty(width: usize, height: usize, command: String) -> Result<Arc<Mu
|
|||
Ok(grid)
|
||||
}
|
||||
|
||||
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<EmbedTerminal>>) {
|
||||
let mut bytes_iter = pty_fd.bytes();
|
||||
//debug!("waiting for bytes");
|
||||
while let Some(Ok(byte)) = bytes_iter.next() {
|
||||
|
@ -391,6 +395,9 @@ impl std::fmt::Display for EscCode<'_> {
|
|||
EscCode(CsiQ(ref buf), c) => {
|
||||
write!(f, "ESC[?{}{}\t\tCSI [UNKNOWN]", unsafestr!(buf), *c as char)
|
||||
}
|
||||
EscCode(Normal, c) => {
|
||||
write!(f, "{} as char: {} Normal", c, *c as char)
|
||||
}
|
||||
EscCode(unknown, c) => {
|
||||
write!(f, "{:?}{} [UNKNOWN]", unknown, c)
|
||||
}
|
||||
|
|
|
@ -35,18 +35,22 @@ use nix::sys::wait::{waitpid, WaitPidFlag};
|
|||
* The main process copies the grid whenever the actual terminal is redrawn.
|
||||
**/
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ScreenBuffer {
|
||||
Normal,
|
||||
Alternate,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EmbedGrid {
|
||||
cursor: (usize, usize),
|
||||
/// [top;bottom]
|
||||
scroll_region: ScrollRegion,
|
||||
pub grid: CellBuffer,
|
||||
pub alternate_screen: CellBuffer,
|
||||
pub state: State,
|
||||
pub stdin: std::fs::File,
|
||||
/// Pid of the embed process
|
||||
pub child_pid: nix::unistd::Pid,
|
||||
/// (width, height)
|
||||
pub terminal_size: (usize, usize),
|
||||
initialized: bool,
|
||||
fg_color: Color,
|
||||
bg_color: Color,
|
||||
/// Store the fg/bg color when highlighting the cell where the cursor is so that it can be
|
||||
|
@ -62,68 +66,35 @@ pub struct EmbedGrid {
|
|||
wrap_next: bool,
|
||||
/// Store state in case a multi-byte character is encountered
|
||||
codepoints: CodepointBuf,
|
||||
pub normal_screen: CellBuffer,
|
||||
screen_buffer: ScreenBuffer,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum CodepointBuf {
|
||||
None,
|
||||
TwoCodepoints(u8),
|
||||
ThreeCodepoints(u8, Option<u8>),
|
||||
FourCodepoints(u8, Option<u8>, Option<u8>),
|
||||
#[derive(Debug)]
|
||||
pub struct EmbedTerminal {
|
||||
pub grid: EmbedGrid,
|
||||
pub stdin: std::fs::File,
|
||||
/// Pid of the embed process
|
||||
pub child_pid: nix::unistd::Pid,
|
||||
}
|
||||
|
||||
impl EmbedGrid {
|
||||
impl EmbedTerminal {
|
||||
pub fn new(stdin: std::fs::File, child_pid: nix::unistd::Pid) -> Self {
|
||||
EmbedGrid {
|
||||
cursor: (0, 0),
|
||||
scroll_region: ScrollRegion {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
..Default::default()
|
||||
},
|
||||
terminal_size: (0, 0),
|
||||
grid: CellBuffer::default(),
|
||||
state: State::Normal,
|
||||
EmbedTerminal {
|
||||
grid: EmbedGrid::new(),
|
||||
stdin,
|
||||
child_pid,
|
||||
fg_color: Color::Default,
|
||||
bg_color: Color::Default,
|
||||
prev_fg_color: None,
|
||||
prev_bg_color: None,
|
||||
show_cursor: true,
|
||||
auto_wrap_mode: true,
|
||||
wrap_next: false,
|
||||
origin_mode: false,
|
||||
codepoints: CodepointBuf::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_terminal_size(&mut self, new_val: (usize, usize)) {
|
||||
if new_val == self.terminal_size {
|
||||
return;
|
||||
}
|
||||
//debug!("resizing to {:?}", new_val);
|
||||
self.scroll_region.top = 0;
|
||||
self.scroll_region.bottom = new_val.1.saturating_sub(1);
|
||||
|
||||
self.terminal_size = new_val;
|
||||
if !self.grid.resize(new_val.0, new_val.1, None) {
|
||||
panic!(
|
||||
"Terminal size too big: ({} cols, {} rows)",
|
||||
new_val.0, new_val.1
|
||||
);
|
||||
}
|
||||
self.grid.clear(Some(Cell::default()));
|
||||
self.cursor = (0, 0);
|
||||
self.wrap_next = false;
|
||||
self.grid.set_terminal_size(new_val);
|
||||
let winsize = Winsize {
|
||||
ws_row: <u16>::try_from(new_val.1).unwrap(),
|
||||
ws_col: <u16>::try_from(new_val.0).unwrap(),
|
||||
ws_xpixel: 0,
|
||||
ws_ypixel: 0,
|
||||
};
|
||||
|
||||
let master_fd = self.stdin.as_raw_fd();
|
||||
let _ = unsafe { set_window_size(master_fd, &winsize) };
|
||||
let _ = nix::sys::signal::kill(self.child_pid, nix::sys::signal::SIGWINCH);
|
||||
|
@ -144,13 +115,102 @@ impl EmbedGrid {
|
|||
}
|
||||
|
||||
pub fn process_byte(&mut self, byte: u8) {
|
||||
let Self {
|
||||
ref mut grid,
|
||||
ref mut stdin,
|
||||
child_pid: _,
|
||||
} = self;
|
||||
grid.process_byte(stdin, byte);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum CodepointBuf {
|
||||
None,
|
||||
TwoCodepoints(u8),
|
||||
ThreeCodepoints(u8, Option<u8>),
|
||||
FourCodepoints(u8, Option<u8>, Option<u8>),
|
||||
}
|
||||
|
||||
impl EmbedGrid {
|
||||
pub fn new() -> Self {
|
||||
let mut normal_screen = CellBuffer::default();
|
||||
normal_screen.set_growable(true);
|
||||
EmbedGrid {
|
||||
cursor: (0, 0),
|
||||
scroll_region: ScrollRegion {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
..Default::default()
|
||||
},
|
||||
terminal_size: (0, 0),
|
||||
initialized: false,
|
||||
alternate_screen: CellBuffer::default(),
|
||||
state: State::Normal,
|
||||
fg_color: Color::Default,
|
||||
bg_color: Color::Default,
|
||||
prev_fg_color: None,
|
||||
prev_bg_color: None,
|
||||
show_cursor: true,
|
||||
auto_wrap_mode: true,
|
||||
wrap_next: false,
|
||||
origin_mode: false,
|
||||
codepoints: CodepointBuf::None,
|
||||
normal_screen,
|
||||
screen_buffer: ScreenBuffer::Normal,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buffer(&self) -> &CellBuffer {
|
||||
match self.screen_buffer {
|
||||
ScreenBuffer::Normal => &self.normal_screen,
|
||||
ScreenBuffer::Alternate => &self.alternate_screen,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buffer_mut(&mut self) -> &mut CellBuffer {
|
||||
match self.screen_buffer {
|
||||
ScreenBuffer::Normal => &mut self.normal_screen,
|
||||
ScreenBuffer::Alternate => &mut self.alternate_screen,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_terminal_size(&mut self, new_val: (usize, usize)) {
|
||||
if new_val == self.terminal_size && self.initialized {
|
||||
return;
|
||||
}
|
||||
self.initialized = true;
|
||||
//debug!("resizing to {:?}", new_val);
|
||||
self.scroll_region.top = 0;
|
||||
self.scroll_region.bottom = new_val.1.saturating_sub(1);
|
||||
|
||||
self.terminal_size = new_val;
|
||||
if !self.alternate_screen.resize(new_val.0, new_val.1, None) {
|
||||
panic!(
|
||||
"Terminal size too big: ({} cols, {} rows)",
|
||||
new_val.0, new_val.1
|
||||
);
|
||||
}
|
||||
self.alternate_screen.clear(Some(Cell::default()));
|
||||
if !self.normal_screen.resize(new_val.0, new_val.1, None) {
|
||||
panic!(
|
||||
"Terminal size too big: ({} cols, {} rows)",
|
||||
new_val.0, new_val.1
|
||||
);
|
||||
}
|
||||
self.normal_screen.clear(Some(Cell::default()));
|
||||
self.cursor = (0, 0);
|
||||
self.wrap_next = false;
|
||||
}
|
||||
|
||||
pub fn process_byte(&mut self, stdin: &mut std::fs::File, byte: u8) {
|
||||
let EmbedGrid {
|
||||
ref mut cursor,
|
||||
ref mut scroll_region,
|
||||
ref terminal_size,
|
||||
ref mut grid,
|
||||
ref mut terminal_size,
|
||||
ref mut alternate_screen,
|
||||
ref mut state,
|
||||
ref mut stdin,
|
||||
ref mut fg_color,
|
||||
ref mut bg_color,
|
||||
ref mut prev_fg_color,
|
||||
|
@ -160,15 +220,45 @@ impl EmbedGrid {
|
|||
ref mut auto_wrap_mode,
|
||||
ref mut wrap_next,
|
||||
ref mut origin_mode,
|
||||
child_pid: _,
|
||||
ref mut screen_buffer,
|
||||
ref mut normal_screen,
|
||||
initialized: _,
|
||||
} = self;
|
||||
let mut grid = normal_screen;
|
||||
|
||||
let is_alternate = match *screen_buffer {
|
||||
ScreenBuffer::Normal => false,
|
||||
_ => {
|
||||
grid = alternate_screen;
|
||||
true
|
||||
}
|
||||
};
|
||||
|
||||
macro_rules! increase_cursor_y {
|
||||
() => {
|
||||
cursor.1 += 1;
|
||||
if !is_alternate {
|
||||
cursor.0 = 0;
|
||||
if cursor.1 >= terminal_size.1 {
|
||||
if !grid.resize(std::cmp::max(1, grid.cols()), grid.rows() + 2, None) {
|
||||
return;
|
||||
}
|
||||
scroll_region.bottom += 1;
|
||||
terminal_size.1 += 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! increase_cursor_x {
|
||||
() => {
|
||||
if cursor.0 + 1 < terminal_size.0 {
|
||||
cursor.0 += 1;
|
||||
} else if *auto_wrap_mode {
|
||||
} else if is_alternate && *auto_wrap_mode {
|
||||
*wrap_next = true;
|
||||
} else if !is_alternate {
|
||||
cursor.0 = 0;
|
||||
increase_cursor_y!();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -183,10 +273,14 @@ impl EmbedGrid {
|
|||
}
|
||||
macro_rules! cursor_y {
|
||||
() => {
|
||||
std::cmp::min(
|
||||
cursor.1 + scroll_region.top,
|
||||
terminal_size.1.saturating_sub(1),
|
||||
)
|
||||
if is_alternate {
|
||||
std::cmp::min(
|
||||
cursor.1 + scroll_region.top,
|
||||
terminal_size.1.saturating_sub(1),
|
||||
)
|
||||
} else {
|
||||
cursor.1
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! cursor_val {
|
||||
|
@ -274,11 +368,11 @@ impl EmbedGrid {
|
|||
//debug!("setting cell {:?} char '{}'", cursor, c as char);
|
||||
//debug!("newline y-> y+1, cursor was: {:?}", cursor);
|
||||
|
||||
if cursor.1 + 1 < terminal_size.1 {
|
||||
if cursor.1 == scroll_region.bottom {
|
||||
if cursor.1 + 1 < terminal_size.1 || !is_alternate {
|
||||
if cursor.1 == scroll_region.bottom && is_alternate {
|
||||
grid.scroll_up(scroll_region, cursor.1, 1);
|
||||
} else {
|
||||
cursor.1 += 1;
|
||||
increase_cursor_y!();
|
||||
}
|
||||
}
|
||||
*wrap_next = false;
|
||||
|
@ -436,6 +530,9 @@ impl EmbedGrid {
|
|||
grid[cursor_val!()].set_fg(Color::Black);
|
||||
grid[cursor_val!()].set_bg(Color::White);
|
||||
}
|
||||
b"1047" | b"1049" => {
|
||||
*screen_buffer = ScreenBuffer::Alternate;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
@ -463,6 +560,9 @@ impl EmbedGrid {
|
|||
grid[cursor_val!()].set_bg(*bg_color);
|
||||
}
|
||||
}
|
||||
b"1047" | b"1049" => {
|
||||
*screen_buffer = ScreenBuffer::Normal;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
//debug!("{}", EscCode::from((&(*state), byte)));
|
||||
|
|
Loading…
Reference in New Issue