From 41e009f9b7b46d335d41b365e41f9ad57d571ecb Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Tue, 31 Mar 2026 20:49:05 +0300 Subject: fix alt-screen scrollback handling --- vendor/vt100/src/term.rs | 551 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 551 insertions(+) create mode 100644 vendor/vt100/src/term.rs (limited to 'vendor/vt100/src/term.rs') diff --git a/vendor/vt100/src/term.rs b/vendor/vt100/src/term.rs new file mode 100644 index 00000000..b6b898c6 --- /dev/null +++ b/vendor/vt100/src/term.rs @@ -0,0 +1,551 @@ +// TODO: read all of this from terminfo + +pub trait BufWrite { + fn write_buf(&self, buf: &mut Vec); +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct ClearScreen; + +impl BufWrite for ClearScreen { + fn write_buf(&self, buf: &mut Vec) { + buf.extend_from_slice(b"\x1b[H\x1b[J"); + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct ClearRowForward; + +impl BufWrite for ClearRowForward { + fn write_buf(&self, buf: &mut Vec) { + buf.extend_from_slice(b"\x1b[K"); + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct Crlf; + +impl BufWrite for Crlf { + fn write_buf(&self, buf: &mut Vec) { + buf.extend_from_slice(b"\r\n"); + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct Backspace; + +impl BufWrite for Backspace { + fn write_buf(&self, buf: &mut Vec) { + buf.extend_from_slice(b"\x08"); + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct SaveCursor; + +impl BufWrite for SaveCursor { + fn write_buf(&self, buf: &mut Vec) { + buf.extend_from_slice(b"\x1b7"); + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct RestoreCursor; + +impl BufWrite for RestoreCursor { + fn write_buf(&self, buf: &mut Vec) { + buf.extend_from_slice(b"\x1b8"); + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct MoveTo { + row: u16, + col: u16, +} + +impl MoveTo { + pub fn new(pos: crate::grid::Pos) -> Self { + Self { + row: pos.row, + col: pos.col, + } + } +} + +impl BufWrite for MoveTo { + fn write_buf(&self, buf: &mut Vec) { + if self.row == 0 && self.col == 0 { + buf.extend_from_slice(b"\x1b[H"); + } else { + buf.extend_from_slice(b"\x1b["); + extend_itoa(buf, self.row + 1); + buf.push(b';'); + extend_itoa(buf, self.col + 1); + buf.push(b'H'); + } + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct ClearAttrs; + +impl BufWrite for ClearAttrs { + fn write_buf(&self, buf: &mut Vec) { + buf.extend_from_slice(b"\x1b[m"); + } +} + +#[derive(Debug, Clone, Copy)] +pub enum Intensity { + Normal, + Bold, + Dim, +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct Attrs { + fgcolor: Option, + bgcolor: Option, + intensity: Option, + italic: Option, + underline: Option, + inverse: Option, +} + +impl Attrs { + pub fn fgcolor(mut self, fgcolor: crate::Color) -> Self { + self.fgcolor = Some(fgcolor); + self + } + + pub fn bgcolor(mut self, bgcolor: crate::Color) -> Self { + self.bgcolor = Some(bgcolor); + self + } + + pub fn intensity(mut self, intensity: Intensity) -> Self { + self.intensity = Some(intensity); + self + } + + pub fn italic(mut self, italic: bool) -> Self { + self.italic = Some(italic); + self + } + + pub fn underline(mut self, underline: bool) -> Self { + self.underline = Some(underline); + self + } + + pub fn inverse(mut self, inverse: bool) -> Self { + self.inverse = Some(inverse); + self + } +} + +impl BufWrite for Attrs { + #[allow(unused_assignments)] + #[allow(clippy::branches_sharing_code)] + fn write_buf(&self, buf: &mut Vec) { + if self.fgcolor.is_none() + && self.bgcolor.is_none() + && self.intensity.is_none() + && self.italic.is_none() + && self.underline.is_none() + && self.inverse.is_none() + { + return; + } + + buf.extend_from_slice(b"\x1b["); + let mut first = true; + + macro_rules! write_param { + ($i:expr) => {{ + if first { + first = false; + } else { + buf.push(b';'); + } + extend_itoa(buf, $i); + }}; + } + + if let Some(fgcolor) = self.fgcolor { + match fgcolor { + crate::Color::Default => { + write_param!(39); + } + crate::Color::Idx(i) => { + if i < 8 { + write_param!(i + 30); + } else if i < 16 { + write_param!(i + 82); + } else { + write_param!(38); + write_param!(5); + write_param!(i); + } + } + crate::Color::Rgb(r, g, b) => { + write_param!(38); + write_param!(2); + write_param!(r); + write_param!(g); + write_param!(b); + } + } + } + + if let Some(bgcolor) = self.bgcolor { + match bgcolor { + crate::Color::Default => { + write_param!(49); + } + crate::Color::Idx(i) => { + if i < 8 { + write_param!(i + 40); + } else if i < 16 { + write_param!(i + 92); + } else { + write_param!(48); + write_param!(5); + write_param!(i); + } + } + crate::Color::Rgb(r, g, b) => { + write_param!(48); + write_param!(2); + write_param!(r); + write_param!(g); + write_param!(b); + } + } + } + + if let Some(intensity) = self.intensity { + match intensity { + Intensity::Normal => write_param!(22), + Intensity::Bold => write_param!(1), + Intensity::Dim => write_param!(2), + } + } + + if let Some(italic) = self.italic { + if italic { + write_param!(3); + } else { + write_param!(23); + } + } + + if let Some(underline) = self.underline { + if underline { + write_param!(4); + } else { + write_param!(24); + } + } + + if let Some(inverse) = self.inverse { + if inverse { + write_param!(7); + } else { + write_param!(27); + } + } + + buf.push(b'm'); + } +} + +#[derive(Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct MoveRight { + count: u16, +} + +impl MoveRight { + pub fn new(count: u16) -> Self { + Self { count } + } +} + +impl Default for MoveRight { + fn default() -> Self { + Self { count: 1 } + } +} + +impl BufWrite for MoveRight { + fn write_buf(&self, buf: &mut Vec) { + match self.count { + 0 => {} + 1 => buf.extend_from_slice(b"\x1b[C"), + n => { + buf.extend_from_slice(b"\x1b["); + extend_itoa(buf, n); + buf.push(b'C'); + } + } + } +} + +#[derive(Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct EraseChar { + count: u16, +} + +impl EraseChar { + pub fn new(count: u16) -> Self { + Self { count } + } +} + +impl Default for EraseChar { + fn default() -> Self { + Self { count: 1 } + } +} + +impl BufWrite for EraseChar { + fn write_buf(&self, buf: &mut Vec) { + match self.count { + 0 => {} + 1 => buf.extend_from_slice(b"\x1b[X"), + n => { + buf.extend_from_slice(b"\x1b["); + extend_itoa(buf, n); + buf.push(b'X'); + } + } + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct HideCursor { + state: bool, +} + +impl HideCursor { + pub fn new(state: bool) -> Self { + Self { state } + } +} + +impl BufWrite for HideCursor { + fn write_buf(&self, buf: &mut Vec) { + if self.state { + buf.extend_from_slice(b"\x1b[?25l"); + } else { + buf.extend_from_slice(b"\x1b[?25h"); + } + } +} + +#[derive(Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct MoveFromTo { + from: crate::grid::Pos, + to: crate::grid::Pos, +} + +impl MoveFromTo { + pub fn new(from: crate::grid::Pos, to: crate::grid::Pos) -> Self { + Self { from, to } + } +} + +impl BufWrite for MoveFromTo { + fn write_buf(&self, buf: &mut Vec) { + if self.to.row == self.from.row + 1 && self.to.col == 0 { + crate::term::Crlf.write_buf(buf); + } else if self.from.row == self.to.row && self.from.col < self.to.col + { + crate::term::MoveRight::new(self.to.col - self.from.col) + .write_buf(buf); + } else if self.to != self.from { + crate::term::MoveTo::new(self.to).write_buf(buf); + } + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct ApplicationKeypad { + state: bool, +} + +impl ApplicationKeypad { + pub fn new(state: bool) -> Self { + Self { state } + } +} + +impl BufWrite for ApplicationKeypad { + fn write_buf(&self, buf: &mut Vec) { + if self.state { + buf.extend_from_slice(b"\x1b="); + } else { + buf.extend_from_slice(b"\x1b>"); + } + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct ApplicationCursor { + state: bool, +} + +impl ApplicationCursor { + pub fn new(state: bool) -> Self { + Self { state } + } +} + +impl BufWrite for ApplicationCursor { + fn write_buf(&self, buf: &mut Vec) { + if self.state { + buf.extend_from_slice(b"\x1b[?1h"); + } else { + buf.extend_from_slice(b"\x1b[?1l"); + } + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct BracketedPaste { + state: bool, +} + +impl BracketedPaste { + pub fn new(state: bool) -> Self { + Self { state } + } +} + +impl BufWrite for BracketedPaste { + fn write_buf(&self, buf: &mut Vec) { + if self.state { + buf.extend_from_slice(b"\x1b[?2004h"); + } else { + buf.extend_from_slice(b"\x1b[?2004l"); + } + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct MouseProtocolMode { + mode: crate::MouseProtocolMode, + prev: crate::MouseProtocolMode, +} + +impl MouseProtocolMode { + pub fn new( + mode: crate::MouseProtocolMode, + prev: crate::MouseProtocolMode, + ) -> Self { + Self { mode, prev } + } +} + +impl BufWrite for MouseProtocolMode { + fn write_buf(&self, buf: &mut Vec) { + if self.mode == self.prev { + return; + } + + match self.mode { + crate::MouseProtocolMode::None => match self.prev { + crate::MouseProtocolMode::None => {} + crate::MouseProtocolMode::Press => { + buf.extend_from_slice(b"\x1b[?9l"); + } + crate::MouseProtocolMode::PressRelease => { + buf.extend_from_slice(b"\x1b[?1000l"); + } + crate::MouseProtocolMode::ButtonMotion => { + buf.extend_from_slice(b"\x1b[?1002l"); + } + crate::MouseProtocolMode::AnyMotion => { + buf.extend_from_slice(b"\x1b[?1003l"); + } + }, + crate::MouseProtocolMode::Press => { + buf.extend_from_slice(b"\x1b[?9h"); + } + crate::MouseProtocolMode::PressRelease => { + buf.extend_from_slice(b"\x1b[?1000h"); + } + crate::MouseProtocolMode::ButtonMotion => { + buf.extend_from_slice(b"\x1b[?1002h"); + } + crate::MouseProtocolMode::AnyMotion => { + buf.extend_from_slice(b"\x1b[?1003h"); + } + } + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct MouseProtocolEncoding { + encoding: crate::MouseProtocolEncoding, + prev: crate::MouseProtocolEncoding, +} + +impl MouseProtocolEncoding { + pub fn new( + encoding: crate::MouseProtocolEncoding, + prev: crate::MouseProtocolEncoding, + ) -> Self { + Self { encoding, prev } + } +} + +impl BufWrite for MouseProtocolEncoding { + fn write_buf(&self, buf: &mut Vec) { + if self.encoding == self.prev { + return; + } + + match self.encoding { + crate::MouseProtocolEncoding::Default => match self.prev { + crate::MouseProtocolEncoding::Default => {} + crate::MouseProtocolEncoding::Utf8 => { + buf.extend_from_slice(b"\x1b[?1005l"); + } + crate::MouseProtocolEncoding::Sgr => { + buf.extend_from_slice(b"\x1b[?1006l"); + } + }, + crate::MouseProtocolEncoding::Utf8 => { + buf.extend_from_slice(b"\x1b[?1005h"); + } + crate::MouseProtocolEncoding::Sgr => { + buf.extend_from_slice(b"\x1b[?1006h"); + } + } + } +} + +fn extend_itoa(buf: &mut Vec, i: I) { + let mut itoa_buf = itoa::Buffer::new(); + buf.extend_from_slice(itoa_buf.format(i).as_bytes()); +} -- cgit v1.2.3