use shpool_protocol::TtySize;
use tracing::info;
use crate::config::{self, SessionRestoreEngine, SessionRestoreMode};
const VTERM_WIDTH: u16 = 1024;
trait ConfigExt {
fn vterm_width(&self) -> u16;
}
impl ConfigExt for config::Manager {
fn vterm_width(&self) -> u16 {
let config = self.get();
config.vt100_output_spool_width.unwrap_or(VTERM_WIDTH)
}
}
pub trait SessionSpool {
fn resize(&mut self, size: TtySize);
fn restore_buffer(&self) -> Vec<u8>;
fn process(&mut self, bytes: &[u8]);
}
pub struct NullSpool;
impl SessionSpool for NullSpool {
fn resize(&mut self, _: TtySize) {}
fn restore_buffer(&self) -> Vec<u8> {
info!("generating null restore buf");
vec![]
}
fn process(&mut self, _: &[u8]) {}
}
pub struct Vt100Screen {
parser: shpool_vt100::Parser,
config: config::Manager,
}
impl SessionSpool for Vt100Screen {
fn resize(&mut self, size: TtySize) {
self.parser.screen_mut().set_size(size.rows, self.config.vterm_width());
}
fn restore_buffer(&self) -> Vec<u8> {
let (rows, cols) = self.parser.screen().size();
info!("computing screen restore buf with (rows={}, cols={})", rows, cols);
self.parser.screen().contents_formatted()
}
fn process(&mut self, bytes: &[u8]) {
self.parser.process(bytes)
}
}
pub struct Vt100Lines {
parser: shpool_vt100::Parser,
nlines: u16,
config: config::Manager,
}
impl SessionSpool for Vt100Lines {
fn resize(&mut self, size: TtySize) {
self.parser.screen_mut().set_size(size.rows, self.config.vterm_width());
}
fn restore_buffer(&self) -> Vec<u8> {
let (rows, cols) = self.parser.screen().size();
info!("computing lines({}) restore buf with (rows={}, cols={})", self.nlines, rows, cols);
self.parser.screen().last_n_rows_contents_formatted(self.nlines)
}
fn process(&mut self, bytes: &[u8]) {
self.parser.process(bytes)
}
}
pub struct Vterm {
term: shpool_vterm::Term,
mode: SessionRestoreMode,
}
impl SessionSpool for Vterm {
fn resize(&mut self, size: TtySize) {
self.term
.resize(shpool_vterm::Size { height: size.rows as usize, width: size.cols as usize });
}
fn restore_buffer(&self) -> Vec<u8> {
match self.mode {
SessionRestoreMode::Simple => vec![],
SessionRestoreMode::Screen => self.term.contents(shpool_vterm::ContentRegion::Screen),
SessionRestoreMode::Lines(nlines) => {
self.term.contents(shpool_vterm::ContentRegion::BottomLines(nlines as usize))
}
}
}
fn process(&mut self, bytes: &[u8]) {
self.term.process(bytes);
}
}
pub fn new(
config: config::Manager,
size: &TtySize,
scrollback_lines: usize,
) -> Box<dyn SessionSpool + 'static> {
let restore_engine = config.get().session_restore_engine.clone().unwrap_or_default();
let mode = config.get().session_restore_mode.clone().unwrap_or_default();
let vterm_width = config.vterm_width();
match (mode, restore_engine) {
(SessionRestoreMode::Simple, _) => Box::new(NullSpool),
(SessionRestoreMode::Screen, SessionRestoreEngine::Vt100) => Box::new(Vt100Screen {
parser: shpool_vt100::Parser::new(size.rows, vterm_width, scrollback_lines),
config,
}),
(SessionRestoreMode::Lines(nlines), SessionRestoreEngine::Vt100) => Box::new(Vt100Lines {
parser: shpool_vt100::Parser::new(size.rows, vterm_width, scrollback_lines),
nlines,
config,
}),
(mode, SessionRestoreEngine::Vterm) => Box::new(Vterm {
term: shpool_vterm::Term::new(
scrollback_lines,
shpool_vterm::Size { width: size.cols as usize, height: size.rows as usize },
),
mode,
}),
}
}