Browse Source

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
Manos Pitsidianakis 3 months ago
parent
commit
592339bdca
Signed by: epilys GPG Key ID: 73627C2F690DF710
  1. 20
      src/components/mail/compose.rs
  2. 15
      src/terminal/embed.rs
  3. 196
      src/terminal/embed/grid.rs

20
src/components/mail/compose.rs

@ -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";

15
src/terminal/embed.rs

@ -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)
}

196
src/terminal/embed/grid.rs

@ -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,6 +66,62 @@ 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)]
pub struct EmbedTerminal {
pub grid: EmbedGrid,
pub stdin: std::fs::File,
/// Pid of the embed process
pub child_pid: nix::unistd::Pid,
}
impl EmbedTerminal {
pub fn new(stdin: std::fs::File, child_pid: nix::unistd::Pid) -> Self {
EmbedTerminal {
grid: EmbedGrid::new(),
stdin,
child_pid,
}
}
pub fn set_terminal_size(&mut self, new_val: (usize, usize)) {
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);
}
pub fn wake_up(&self) {
let _ = nix::sys::signal::kill(self.child_pid, nix::sys::signal::SIGCONT);
}
pub fn stop(&self) {
//debug!("stopping");
let _ = nix::sys::signal::kill(debug!(self.child_pid), nix::sys::signal::SIGSTOP);
}
pub fn is_active(&self) -> Result<WaitStatus> {
debug!(waitpid(self.child_pid, Some(WaitPidFlag::WNOHANG),))
.map_err(|e| MeliError::new(e.to_string()))
}
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)]
@ -73,7 +133,9 @@ enum CodepointBuf {
}
impl EmbedGrid {
pub fn new(stdin: std::fs::File, child_pid: nix::unistd::Pid) -> Self {
pub fn new() -> Self {
let mut normal_screen = CellBuffer::default();
normal_screen.set_growable(true);
EmbedGrid {
cursor: (0, 0),
scroll_region: ScrollRegion {
@ -83,10 +145,9 @@ impl EmbedGrid {
..Default::default()
},
terminal_size: (0, 0),
grid: CellBuffer::default(),
initialized: false,
alternate_screen: CellBuffer::default(),
state: State::Normal,
stdin,
child_pid,
fg_color: Color::Default,
bg_color: Color::Default,
prev_fg_color: None,
@ -96,61 +157,60 @@ impl EmbedGrid {
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 {
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.grid.resize(new_val.0, new_val.1, None) {
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.grid.clear(Some(Cell::default()));
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;
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);
}
pub fn wake_up(&self) {
let _ = nix::sys::signal::kill(self.child_pid, nix::sys::signal::SIGCONT);
}
pub fn stop(&self) {
//debug!("stopping");
let _ = nix::sys::signal::kill(debug!(self.child_pid), nix::sys::signal::SIGSTOP);
}
pub fn is_active(&self) -> Result<WaitStatus> {
debug!(waitpid(self.child_pid, Some(WaitPidFlag::WNOHANG),))
.map_err(|e| MeliError::new(e.to_string()))
}
pub fn process_byte(&mut self, byte: u8) {
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…
Cancel
Save