aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-04-05 16:24:20 +0300
committerCan Celik <ogulcancelik@gmail.com>2026-04-05 16:38:50 +0300
commitb2e6929aa8199574565fd4adf1e631c68d479e6c (patch)
tree99964965fe84f9ea31a0dec7ede2393631dc7f50
parent822c3b9e2d1edb7481fa558497dd33c7f5dca9ee (diff)
refactor: remove vt100 backend and make ghostty default
-rw-r--r--Cargo.lock38
-rw-r--r--Cargo.toml9
-rw-r--r--build.rs4
-rw-r--r--justfile2
-rw-r--r--src/app/mod.rs3
-rw-r--r--src/detect.rs26
-rw-r--r--src/ghostty/mod.rs1
-rw-r--r--src/input/encode.rs4
-rw-r--r--src/input/mod.rs1
-rw-r--r--src/main.rs2
-rw-r--r--src/pane.rs539
-rw-r--r--src/pty_callbacks.rs450
-rw-r--r--src/selection.rs7
-rw-r--r--src/terminal_theme.rs1
-rw-r--r--vendor/vt100/.cargo-ok1
-rw-r--r--vendor/vt100/.cargo_vcs_info.json6
-rw-r--r--vendor/vt100/CHANGELOG.md396
-rw-r--r--vendor/vt100/Cargo.lock540
-rw-r--r--vendor/vt100/Cargo.toml75
-rw-r--r--vendor/vt100/Cargo.toml.orig28
-rw-r--r--vendor/vt100/LICENSE21
-rw-r--r--vendor/vt100/README.md36
-rw-r--r--vendor/vt100/src/attrs.rs144
-rw-r--r--vendor/vt100/src/callbacks.rs69
-rw-r--r--vendor/vt100/src/cell.rs179
-rw-r--r--vendor/vt100/src/grid.rs740
-rw-r--r--vendor/vt100/src/lib.rs64
-rw-r--r--vendor/vt100/src/parser.rs96
-rw-r--r--vendor/vt100/src/perform.rs277
-rw-r--r--vendor/vt100/src/row.rs474
-rw-r--r--vendor/vt100/src/screen.rs1354
-rw-r--r--vendor/vt100/src/term.rs551
32 files changed, 49 insertions, 6089 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 8b66e213..baf7aabe 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -24,12 +24,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
-name = "arrayvec"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
-
-[[package]]
name = "atomic"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -466,9 +460,7 @@ dependencies = [
"toml",
"tracing",
"tracing-subscriber",
- "tui-term",
"unicode-width",
- "vt100",
]
[[package]]
@@ -1615,17 +1607,6 @@ dependencies = [
]
[[package]]
-name = "tui-term"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c40c57cf174390b66d2386cb1288907c65ec389bbf6e99c919c375dc7e08e4d3"
-dependencies = [
- "ratatui-core",
- "ratatui-widgets",
- "vt100",
-]
-
-[[package]]
name = "typenum"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1703,25 +1684,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
-name = "vt100"
-version = "0.16.2"
-dependencies = [
- "itoa",
- "unicode-width",
- "vte",
-]
-
-[[package]]
-name = "vte"
-version = "0.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5924018406ce0063cd67f8e008104968b74b563ee1b85dde3ed1f7cb87d3dbd"
-dependencies = [
- "arrayvec",
- "memchr",
-]
-
-[[package]]
name = "vtparse"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index f0849154..b310dd5a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,10 +11,6 @@ keywords = ["terminal", "tui", "ai", "agents", "multiplexer"]
categories = ["command-line-utilities"]
include = ["src/**/*", "assets/sounds/*", "README.md", "LICENSE", "Cargo.toml"]
-[features]
-default = []
-ghostty-vt = []
-
[dependencies]
base64 = "0.22.1"
bytes = "1"
@@ -29,9 +25,4 @@ tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time"
toml = "0.8"
tracing = "0.1.44"
tracing-subscriber = { version = "0.3.23", features = ["env-filter"] }
-tui-term = "0.3"
unicode-width = "0.2"
-vt100 = "0.16"
-
-[patch.crates-io]
-vt100 = { path = "vendor/vt100" }
diff --git a/build.rs b/build.rs
index faab7f06..f4cbd170 100644
--- a/build.rs
+++ b/build.rs
@@ -10,10 +10,6 @@ fn main() {
println!("cargo:rerun-if-changed=vendor/libghostty-vt/VERSION");
println!("cargo:rerun-if-env-changed=LIBGHOSTTY_VT_OPTIMIZE");
- if env::var_os("CARGO_FEATURE_GHOSTTY_VT").is_none() {
- return;
- }
-
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));
let vendored_dir = manifest_dir.join("vendor/libghostty-vt");
let optimize = env::var("LIBGHOSTTY_VT_OPTIMIZE").unwrap_or_else(|_| "ReleaseFast".into());
diff --git a/justfile b/justfile
index 1105aa9e..505217ff 100644
--- a/justfile
+++ b/justfile
@@ -3,14 +3,12 @@
# Run unit tests
test:
cargo test
- cargo test --features ghostty-vt ghostty::tests
python3 -m unittest scripts.test_changelog scripts.test_vendor_libghostty_vt
# Check formatting + run unit tests
check:
cargo fmt --check
cargo test
- cargo test --features ghostty-vt ghostty::tests
python3 -m unittest scripts.test_changelog scripts.test_vendor_libghostty_vt
# Run the full local test suite
diff --git a/src/app/mod.rs b/src/app/mod.rs
index 064b153a..65a1cc4a 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -774,12 +774,10 @@ impl App {
return;
}
- #[cfg(feature = "ghostty-vt")]
if let Some((ws_idx, pane_id)) = self.last_focus {
self.send_pane_focus_event(ws_idx, pane_id, crate::ghostty::FocusEvent::Lost);
}
if let Some((ws_idx, pane_id)) = current_focus {
- #[cfg(feature = "ghostty-vt")]
self.send_pane_focus_event(ws_idx, pane_id, crate::ghostty::FocusEvent::Gained);
self.emit_event(crate::api::schema::EventEnvelope {
event: crate::api::schema::EventKind::WorkspaceFocused,
@@ -812,7 +810,6 @@ impl App {
self.last_focus = current_focus;
}
- #[cfg(feature = "ghostty-vt")]
fn send_pane_focus_event(
&self,
ws_idx: usize,
diff --git a/src/detect.rs b/src/detect.rs
index bc59fe25..b7ffa441 100644
--- a/src/detect.rs
+++ b/src/detect.rs
@@ -1,6 +1,6 @@
//! Agent state detection via screen content pattern matching.
//!
-//! Each pane's `vt100::Screen` content is read periodically and matched
+//! Each pane's visible terminal content is read periodically and matched
//! against known agent output patterns to determine state.
/// The detected state of a terminal pane.
@@ -1177,31 +1177,19 @@ mod tests {
let _ = tpgid;
}
- // ---- VT100 integration ----
-
#[test]
- fn vt100_screen_content_works_with_detection() {
- let mut parser = vt100::Parser::new(24, 80, 0);
- parser.process(b"Working...");
- let content = parser.screen().contents();
- assert_eq!(detect_pi(&content), AgentState::Working);
+ fn visible_screen_content_works_with_detection() {
+ assert_eq!(detect_pi("Working..."), AgentState::Working);
}
#[test]
- fn vt100_screen_with_ansi_colors() {
- let mut parser = vt100::Parser::new(24, 80, 0);
- // Red "Working..." with ANSI codes — contents() strips formatting
- parser.process(b"\x1b[31mWorking...\x1b[0m");
- let content = parser.screen().contents();
- assert_eq!(detect_pi(&content), AgentState::Working);
+ fn ansi_colored_content_still_detects_working() {
+ assert_eq!(detect_pi("\x1b[31mWorking...\x1b[0m"), AgentState::Working);
}
#[test]
- fn vt100_screen_claude_prompt_box() {
- let mut parser = vt100::Parser::new(24, 80, 0);
+ fn visible_claude_prompt_box_is_idle() {
let screen = "Task complete.\n─────────────\n❯ \n─────────────";
- parser.process(screen.as_bytes());
- let content = parser.screen().contents();
- assert_eq!(detect_claude(&content), AgentState::Idle);
+ assert_eq!(detect_claude(screen), AgentState::Idle);
}
}
diff --git a/src/ghostty/mod.rs b/src/ghostty/mod.rs
index c5214472..e2bd409c 100644
--- a/src/ghostty/mod.rs
+++ b/src/ghostty/mod.rs
@@ -1,4 +1,3 @@
-#![cfg(feature = "ghostty-vt")]
#![allow(dead_code)]
#[allow(
diff --git a/src/input/encode.rs b/src/input/encode.rs
index 610cc694..4aa68e17 100644
--- a/src/input/encode.rs
+++ b/src/input/encode.rs
@@ -39,6 +39,7 @@ pub fn encode_cursor_key(code: KeyCode, application_cursor: bool) -> Vec<u8> {
}
}
+#[allow(dead_code)] // exercised in input unit tests; pane runtime uses backend helpers
pub fn encode_mouse_scroll(
kind: MouseEventKind,
column: u16,
@@ -56,6 +57,7 @@ pub fn encode_mouse_scroll(
encode_mouse_cb(button, false, column, row, modifiers, encoding)
}
+#[allow(dead_code)] // exercised in input unit tests; pane runtime uses backend helpers
pub fn encode_mouse_button(
kind: MouseEventKind,
column: u16,
@@ -78,6 +80,7 @@ pub fn encode_mouse_button(
encode_mouse_cb(button, release, column, row, modifiers, encoding)
}
+#[allow(dead_code)] // only reached through mouse encoding helpers above
fn encode_mouse_cb(
base_button: u16,
release: bool,
@@ -129,6 +132,7 @@ fn encode_mouse_cb(
}
}
+#[allow(dead_code)] // only reached through mouse encoding helpers above
fn push_mouse_codepoint(bytes: &mut Vec<u8>, value: u32) -> Option<()> {
let ch = char::from_u32(value)?;
let mut buf = [0u8; 4];
diff --git a/src/input/mod.rs b/src/input/mod.rs
index 4e34674c..109fd312 100644
--- a/src/input/mod.rs
+++ b/src/input/mod.rs
@@ -4,6 +4,7 @@ mod parse;
#[cfg(test)]
pub use encode::encode_key;
+#[allow(unused_imports)]
pub use encode::{
encode_cursor_key, encode_mouse_button, encode_mouse_scroll, encode_terminal_key,
};
diff --git a/src/main.rs b/src/main.rs
index b0f986bd..4fdfc9de 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -24,7 +24,6 @@ mod cli;
mod config;
mod detect;
mod events;
-#[cfg(feature = "ghostty-vt")]
mod ghostty;
mod input;
mod integration;
@@ -32,7 +31,6 @@ mod layout;
mod pane;
mod persist;
mod platform;
-mod pty_callbacks;
mod raw_input;
mod release_notes;
mod selection;
diff --git a/src/pane.rs b/src/pane.rs
index f71822bd..02bf5033 100644
--- a/src/pane.rs
+++ b/src/pane.rs
@@ -2,24 +2,20 @@ use std::cell::Cell;
use std::io::{BufWriter, Read, Write};
use std::sync::{
atomic::{AtomicBool, AtomicU16, AtomicU32, Ordering},
- Arc, Mutex, RwLock,
+ Arc, Mutex,
};
use bytes::Bytes;
use portable_pty::{native_pty_system, CommandBuilder, PtySize};
-#[cfg(feature = "ghostty-vt")]
use ratatui::style::{Color, Modifier, Style};
use ratatui::{layout::Rect, Frame};
use tokio::sync::{mpsc, Notify};
use tracing::{debug, error, info, warn};
-use tui_term::widget::PseudoTerminal;
-#[cfg(feature = "ghostty-vt")]
use unicode_width::UnicodeWidthStr;
use crate::detect::{Agent, AgentState};
use crate::events::AppEvent;
use crate::layout::PaneId;
-use crate::pty_callbacks::PtyResponses;
const CLAUDE_WORKING_HOLD: std::time::Duration = std::time::Duration::from_millis(1200);
const RELEASE_REACQUIRE_SUPPRESSION: std::time::Duration = std::time::Duration::from_secs(1);
@@ -289,29 +285,17 @@ struct ProcessBytesResult {
request_render: bool,
}
-#[cfg_attr(feature = "ghostty-vt", allow(dead_code))]
-struct VtPaneTerminal {
- parser: Arc<RwLock<vt100::Parser<PtyResponses>>>,
- screen_content: Arc<RwLock<String>>,
- mouse_alternate_scroll: Arc<AtomicBool>,
-}
-
-#[cfg(feature = "ghostty-vt")]
struct GhosttyPaneTerminal {
core: Mutex<GhosttyPaneCore>,
}
-#[cfg(feature = "ghostty-vt")]
struct GhosttyPaneCore {
terminal: crate::ghostty::Terminal,
render_state: crate::ghostty::RenderState,
}
-enum PaneTerminal {
- #[cfg_attr(feature = "ghostty-vt", allow(dead_code))]
- Vt(VtPaneTerminal),
- #[cfg(feature = "ghostty-vt")]
- Ghostty(GhosttyPaneTerminal),
+struct PaneTerminal {
+ ghostty: GhosttyPaneTerminal,
}
impl PaneTerminal {
@@ -321,119 +305,63 @@ impl PaneTerminal {
bytes: &[u8],
response_writer: &mpsc::Sender<Bytes>,
) -> ProcessBytesResult {
- match self {
- Self::Vt(vt) => vt.process_pty_bytes(pane_id, bytes, response_writer),
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.process_pty_bytes(pane_id, bytes, response_writer),
- }
+ self.ghostty
+ .process_pty_bytes(pane_id, bytes, response_writer)
}
fn resize(&self, rows: u16, cols: u16) {
- match self {
- Self::Vt(vt) => vt.resize(rows, cols),
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.resize(rows, cols),
- }
+ self.ghostty.resize(rows, cols);
}
fn scroll_up(&self, lines: usize) {
- match self {
- Self::Vt(vt) => vt.scroll_up(lines),
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.scroll_up(lines),
- }
+ self.ghostty.scroll_up(lines);
}
fn scroll_down(&self, lines: usize) {
- match self {
- Self::Vt(vt) => vt.scroll_down(lines),
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.scroll_down(lines),
- }
+ self.ghostty.scroll_down(lines);
}
fn scroll_reset(&self) {
- match self {
- Self::Vt(vt) => vt.scroll_reset(),
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.scroll_reset(),
- }
+ self.ghostty.scroll_reset();
}
fn set_scroll_offset_from_bottom(&self, lines: usize) {
- match self {
- Self::Vt(vt) => vt.set_scroll_offset_from_bottom(lines),
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.set_scroll_offset_from_bottom(lines),
- }
+ self.ghostty.set_scroll_offset_from_bottom(lines);
}
fn scroll_metrics(&self) -> Option<ScrollMetrics> {
- match self {
- Self::Vt(vt) => vt.scroll_metrics(),
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.scroll_metrics(),
- }
+ self.ghostty.scroll_metrics()
}
fn input_state(&self) -> Option<InputState> {
- match self {
- Self::Vt(vt) => vt.input_state(),
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.input_state(),
- }
+ self.ghostty.input_state()
}
fn visible_text(&self) -> String {
- match self {
- Self::Vt(vt) => vt.visible_text(),
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.visible_text(),
- }
+ self.ghostty.visible_text()
}
fn recent_text(&self, lines: usize) -> String {
- match self {
- Self::Vt(vt) => vt.recent_text(lines),
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.recent_text(lines),
- }
+ self.ghostty.recent_text(lines)
}
fn extract_selection(&self, selection: &crate::selection::Selection) -> Option<String> {
- match self {
- Self::Vt(vt) => vt.extract_selection(selection),
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.extract_selection(selection),
- }
+ self.ghostty.extract_selection(selection)
}
fn render(&self, frame: &mut Frame, area: Rect) {
- match self {
- Self::Vt(vt) => vt.render(frame, area),
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.render(frame, area),
- }
+ self.ghostty.render(frame, area);
}
fn apply_host_terminal_theme(&self, theme: crate::terminal_theme::TerminalTheme) {
- let _ = theme;
- match self {
- Self::Vt(_) => {}
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.apply_host_terminal_theme(theme),
- }
+ self.ghostty.apply_host_terminal_theme(theme);
}
fn keyboard_protocol(
&self,
fallback: crate::input::KeyboardProtocol,
) -> crate::input::KeyboardProtocol {
- match self {
- Self::Vt(_) => fallback,
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.keyboard_protocol().unwrap_or(fallback),
- }
+ self.ghostty.keyboard_protocol().unwrap_or(fallback)
}
fn encode_terminal_key(
@@ -441,11 +369,7 @@ impl PaneTerminal {
key: crate::input::TerminalKey,
protocol: crate::input::KeyboardProtocol,
) -> Vec<u8> {
- match self {
- Self::Vt(vt) => vt.encode_terminal_key(key, protocol),
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.encode_terminal_key(key, protocol),
- }
+ self.ghostty.encode_terminal_key(key, protocol)
}
fn encode_mouse_button(
@@ -455,19 +379,8 @@ impl PaneTerminal {
row: u16,
modifiers: crossterm::event::KeyModifiers,
) -> Option<Vec<u8>> {
- match self {
- Self::Vt(vt) => vt.input_state().and_then(|input_state| {
- crate::input::encode_mouse_button(
- kind,
- column,
- row,
- modifiers,
- input_state.mouse_protocol_encoding,
- )
- }),
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.encode_mouse_button(kind, column, row, modifiers),
- }
+ self.ghostty
+ .encode_mouse_button(kind, column, row, modifiers)
}
fn encode_mouse_wheel(
@@ -477,219 +390,11 @@ impl PaneTerminal {
row: u16,
modifiers: crossterm::event::KeyModifiers,
) -> Option<Vec<u8>> {
- match self {
- Self::Vt(vt) => vt.input_state().and_then(|input_state| {
- crate::input::encode_mouse_scroll(
- kind,
- column,
- row,
- modifiers,
- input_state.mouse_protocol_encoding,
- )
- }),
- #[cfg(feature = "ghostty-vt")]
- Self::Ghostty(ghostty) => ghostty.encode_mouse_wheel(kind, column, row, modifiers),
- }
- }
-}
-
-#[cfg_attr(feature = "ghostty-vt", allow(dead_code))]
-impl VtPaneTerminal {
- fn new(
- parser: Arc<RwLock<vt100::Parser<PtyResponses>>>,
- screen_content: Arc<RwLock<String>>,
- mouse_alternate_scroll: Arc<AtomicBool>,
- ) -> Self {
- Self {
- parser,
- screen_content,
- mouse_alternate_scroll,
- }
- }
-
- fn process_pty_bytes(
- &self,
- pane_id: PaneId,
- bytes: &[u8],
- response_writer: &mpsc::Sender<Bytes>,
- ) -> ProcessBytesResult {
- let resp = if let Ok(mut parser) = self.parser.write() {
- parser.process(bytes);
-
- let scrollback = parser.screen().scrollback();
- if scrollback > 0 {
- parser.screen_mut().set_scrollback(0);
- }
- let content = parser.screen().contents();
- if scrollback > 0 {
- parser.screen_mut().set_scrollback(scrollback);
- }
- if let Ok(mut screen_content) = self.screen_content.write() {
- *screen_content = content;
- }
- parser.callbacks_mut().take()
- } else {
- error!(pane = pane_id.raw(), "parser lock poisoned in reader");
- return ProcessBytesResult {
- request_render: false,
- };
- };
-
- if !resp.is_empty() {
- if let Err(e) = response_writer.try_send(Bytes::from(resp)) {
- warn!(pane = pane_id.raw(), err = %e, "dropped terminal query response");
- }
- }
-
- ProcessBytesResult {
- request_render: true,
- }
- }
-
- fn resize(&self, rows: u16, cols: u16) {
- if let Ok(mut parser) = self.parser.write() {
- parser.screen_mut().set_size(rows, cols);
- }
- }
-
- fn scroll_up(&self, lines: usize) {
- if let Ok(mut parser) = self.parser.write() {
- let current = parser.screen().scrollback();
- parser.screen_mut().set_scrollback(current + lines);
- }
- }
-
- fn scroll_down(&self, lines: usize) {
- if let Ok(mut parser) = self.parser.write() {
- let current = parser.screen().scrollback();
- parser
- .screen_mut()
- .set_scrollback(current.saturating_sub(lines));
- }
- }
-
- fn scroll_reset(&self) {
- if let Ok(mut parser) = self.parser.write() {
- parser.screen_mut().set_scrollback(0);
- }
- }
-
- fn set_scroll_offset_from_bottom(&self, lines: usize) {
- if let Ok(mut parser) = self.parser.write() {
- parser.screen_mut().set_scrollback(lines);
- }
- }
-
- fn scroll_metrics(&self) -> Option<ScrollMetrics> {
- let Ok(mut parser) = self.parser.write() else {
- return None;
- };
- let max_offset_from_bottom = max_scrollback(&mut parser);
- let screen = parser.screen();
- let (viewport_rows, _) = screen.size();
- Some(ScrollMetrics {
- offset_from_bottom: screen.scrollback(),
- max_offset_from_bottom,
- viewport_rows: viewport_rows as usize,
- })
- }
-
- fn input_state(&self) -> Option<InputState> {
- let Ok(parser) = self.parser.read() else {
- return None;
- };
- let screen = parser.screen();
- Some(InputState {
- alternate_screen: screen.alternate_screen(),
- application_cursor: screen.application_cursor(),
- bracketed_paste: screen.bracketed_paste(),
- focus_reporting: false,
- mouse_protocol_mode: match screen.mouse_protocol_mode() {
- vt100::MouseProtocolMode::None => crate::input::MouseProtocolMode::None,
- vt100::MouseProtocolMode::Press => crate::input::MouseProtocolMode::Press,
- vt100::MouseProtocolMode::PressRelease => {
- crate::input::MouseProtocolMode::PressRelease
- }
- vt100::MouseProtocolMode::ButtonMotion => {
- crate::input::MouseProtocolMode::ButtonMotion
- }
- vt100::MouseProtocolMode::AnyMotion => crate::input::MouseProtocolMode::AnyMotion,
- },
- mouse_protocol_encoding: match screen.mouse_protocol_encoding() {
- vt100::MouseProtocolEncoding::Default => {
- crate::input::MouseProtocolEncoding::Default
- }
- vt100::MouseProtocolEncoding::Utf8 => crate::input::MouseProtocolEncoding::Utf8,
- vt100::MouseProtocolEncoding::Sgr => crate::input::MouseProtocolEncoding::Sgr,
- },
- mouse_alternate_scroll: self.mouse_alternate_scroll.load(Ordering::Relaxed),
- })
- }
-
- fn visible_text(&self) -> String {
- let Ok(content) = self.screen_content.read() else {
- return String::new();
- };
- let mut rows: Vec<String> = content
- .lines()
- .map(|line| line.trim_end().to_string())
- .collect();
- trim_trailing_blank_rows(&mut rows);
- let text = rows.join("\n");
- if text.is_empty() {
- text
- } else {
- format!("{text}\n")
- }
- }
-
- fn recent_text(&self, lines: usize) -> String {
- self.parser
- .write()
- .map(|mut parser| recent_text_from_parser(&mut parser, lines))
- .unwrap_or_default()
- }
-
- fn extract_selection(&self, selection: &crate::selection::Selection) -> Option<String> {
- self.parser
- .read()
- .map(|parser| crate::selection::extract_text(parser.screen(), selection))
- .ok()
- }
-
- fn render(&self, frame: &mut Frame, area: Rect) {
- if let Ok(parser) = self.parser.read() {
- let show_cursor = parser.screen().scrollback() == 0;
- let pt = PseudoTerminal::new(parser.screen())
- .cursor(tui_term::widget::Cursor::default().visibility(show_cursor));
- frame.render_widget(pt, area);
- }
- }
-
- fn encode_terminal_key(
- &self,
- key: crate::input::TerminalKey,
- protocol: crate::input::KeyboardProtocol,
- ) -> Vec<u8> {
- let application_cursor = self
- .input_state()
- .map(|state| state.application_cursor)
- .unwrap_or(false);
- if matches!(
- key.code,
- crossterm::event::KeyCode::Up
- | crossterm::event::KeyCode::Down
- | crossterm::event::KeyCode::Left
- | crossterm::event::KeyCode::Right
- ) && key.modifiers.is_empty()
- {
- return crate::input::encode_cursor_key(key.code, application_cursor);
- }
- crate::input::encode_terminal_key(key, protocol)
+ self.ghostty
+ .encode_mouse_wheel(kind, column, row, modifiers)
}
}
-#[cfg(feature = "ghostty-vt")]
impl GhosttyPaneTerminal {
fn new(
mut terminal: crate::ghostty::Terminal,
@@ -1019,8 +724,6 @@ impl GhosttyPaneTerminal {
}
}
-#[cfg(feature = "ghostty-vt")]
-#[cfg(feature = "ghostty-vt")]
fn ghostty_key_event_from_terminal_key(
key: crate::input::TerminalKey,
) -> Option<crate::ghostty::KeyEvent> {
@@ -1055,12 +758,10 @@ fn ghostty_key_event_from_terminal_key(
Some(event)
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_prefers_herdr_text_encoding(key: crate::input::TerminalKey) -> bool {
matches!(key.code, crossterm::event::KeyCode::Char(_))
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_mods_from_key_modifiers(modifiers: crossterm::event::KeyModifiers) -> u16 {
let mut ghostty_mods = 0u16;
if modifiers.contains(crossterm::event::KeyModifiers::SHIFT) {
@@ -1078,7 +779,6 @@ fn ghostty_mods_from_key_modifiers(modifiers: crossterm::event::KeyModifiers) ->
ghostty_mods
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_mouse_encoder_for_terminal(
terminal: &crate::ghostty::Terminal,
) -> Option<crate::ghostty::MouseEncoder> {
@@ -1090,7 +790,6 @@ fn ghostty_mouse_encoder_for_terminal(
Some(encoder)
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_mouse_event_from_button_kind(
kind: crossterm::event::MouseEventKind,
column: u16,
@@ -1148,7 +847,6 @@ fn ghostty_mouse_event_from_button_kind(
Some(event)
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_mouse_event_from_wheel_kind(
kind: crossterm::event::MouseEventKind,
column: u16,
@@ -1170,7 +868,6 @@ fn ghostty_mouse_event_from_wheel_kind(
Some(event)
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_key_text(key: crate::input::TerminalKey) -> Option<String> {
match key.code {
crossterm::event::KeyCode::Char(c) => Some(
@@ -1183,7 +880,6 @@ fn ghostty_key_text(key: crate::input::TerminalKey) -> Option<String> {
}
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_unshifted_codepoint(key: crate::input::TerminalKey) -> Option<u32> {
match key.code {
crossterm::event::KeyCode::Char(c) => Some(c as u32),
@@ -1191,7 +887,6 @@ fn ghostty_unshifted_codepoint(key: crate::input::TerminalKey) -> Option<u32> {
}
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_key_from_crossterm_key_code(
code: crossterm::event::KeyCode,
shifted_codepoint: Option<u32>,
@@ -1234,7 +929,6 @@ fn ghostty_key_from_crossterm_key_code(
}
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_key_from_char(c: char, shifted_codepoint: Option<u32>) -> Option<u32> {
use crate::ghostty::ffi;
@@ -1297,7 +991,6 @@ fn ghostty_key_from_char(c: char, shifted_codepoint: Option<u32>) -> Option<u32>
}
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_unshifted_ascii_pair(c: char) -> Option<char> {
Some(match c {
'!' => '1',
@@ -1325,7 +1018,6 @@ fn ghostty_unshifted_ascii_pair(c: char) -> Option<char> {
})
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_visible_text(core: &mut GhosttyPaneCore) -> Result<String, crate::ghostty::Error> {
let GhosttyPaneCore {
terminal,
@@ -1344,7 +1036,6 @@ fn ghostty_visible_text(core: &mut GhosttyPaneCore) -> Result<String, crate::gho
Ok(lines_to_text(lines))
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_recent_text(
core: &GhosttyPaneCore,
lines: usize,
@@ -1360,7 +1051,6 @@ fn ghostty_recent_text(
Ok(recent_text_from_rows(&rows, lines))
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_extract_selection(
core: &mut GhosttyPaneCore,
selection: &crate::selection::Selection,
@@ -1395,7 +1085,6 @@ fn ghostty_extract_selection(
Ok(lines.join("\n"))
}
-#[cfg(feature = "ghostty-vt")]
fn selection_cols_for_row(selection: &crate::selection::Selection, row: u16) -> (u16, u16) {
let ((start_row, start_col), (end_row, end_col)) = selection.ordered_cells();
if start_row == end_row {
@@ -1409,7 +1098,6 @@ fn selection_cols_for_row(selection: &crate::selection::Selection, row: u16) ->
}
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_screen_row(
core: &GhosttyPaneCore,
cols: u16,
@@ -1431,7 +1119,6 @@ fn ghostty_screen_row(
Ok(line.trim_end().to_string())
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_line_from_cells(
cells: &mut crate::ghostty::RowCellIter<'_>,
) -> Result<String, crate::ghostty::Error> {
@@ -1442,7 +1129,6 @@ fn ghostty_line_from_cells(
Ok(line.trim_end().to_string())
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_cell_symbol(
cells: &crate::ghostty::RowCellIter<'_>,
) -> Result<String, crate::ghostty::Error> {
@@ -1462,7 +1148,6 @@ fn ghostty_cell_symbol(
Ok(text)
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_blank_symbol_for_width(wide: crate::ghostty::CellWide) -> &'static str {
match wide {
crate::ghostty::CellWide::Wide => " ",
@@ -1471,7 +1156,6 @@ fn ghostty_blank_symbol_for_width(wide: crate::ghostty::CellWide) -> &'static st
}
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_normalize_buffer_symbol(symbol: &str, wide: crate::ghostty::CellWide) -> String {
let expected_width = match wide {
crate::ghostty::CellWide::Wide => 2,
@@ -1485,7 +1169,6 @@ fn ghostty_normalize_buffer_symbol(symbol: &str, wide: crate::ghostty::CellWide)
ghostty_blank_symbol_for_width(wide).to_string()
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_buffer_symbol(
cells: &crate::ghostty::RowCellIter<'_>,
wide: crate::ghostty::CellWide,
@@ -1500,7 +1183,6 @@ fn ghostty_buffer_symbol(
Ok(ghostty_normalize_buffer_symbol(&symbol, wide))
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_reset_cell(
cell: &mut ratatui::buffer::Cell,
default_fg: Option<Color>,
@@ -1516,7 +1198,6 @@ fn ghostty_reset_cell(
}
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_cell_style(
cells: &crate::ghostty::RowCellIter<'_>,
default_fg: Option<Color>,
@@ -1572,7 +1253,6 @@ fn ghostty_cell_style(
style.add_modifier(modifiers)
}
-#[cfg(feature = "ghostty-vt")]
fn ghostty_color(color: crate::ghostty::RgbColor) -> Color {
Color::Rgb(color.r, color.g, color.b)
}
@@ -1608,44 +1288,6 @@ fn trim_trailing_blank_rows(rows: &mut Vec<String>) {
}
}
-fn max_scrollback(parser: &mut vt100::Parser<PtyResponses>) -> usize {
- let screen = parser.screen_mut();
- let original_scrollback = screen.scrollback();
- screen.set_scrollback(usize::MAX);
- let max_scrollback = screen.scrollback();
- screen.set_scrollback(original_scrollback);
- max_scrollback
-}
-
-fn parser_rows(parser: &mut vt100::Parser<PtyResponses>, lines: usize) -> Vec<String> {
- let max_scrollback = max_scrollback(parser);
- let screen = parser.screen_mut();
- let original_scrollback = screen.scrollback();
-
- let (_, cols) = screen.size();
- screen.set_scrollback(0);
- let visible_rows: Vec<String> = screen
- .rows(0, cols)
- .map(|row| row.trim_end().to_string())
- .collect();
- let extra_rows = lines.saturating_sub(visible_rows.len()).min(max_scrollback);
-
- let mut rows = Vec::with_capacity(extra_rows + visible_rows.len());
- if extra_rows > 0 {
- for offset in (1..=extra_rows).rev() {
- screen.set_scrollback(offset);
- if let Some(row) = screen.rows(0, cols).next() {
- rows.push(row.trim_end().to_string());
- }
- }
- }
-
- screen.set_scrollback(original_scrollback);
- rows.extend(visible_rows);
- trim_trailing_blank_rows(&mut rows);
- rows
-}
-
fn recent_text_from_rows(rows: &[String], lines: usize) -> String {
let start = rows.len().saturating_sub(lines);
let text = rows[start..].join("\n");
@@ -1656,11 +1298,6 @@ fn recent_text_from_rows(rows: &[String], lines: usize) -> String {
}
}
-fn recent_text_from_parser(parser: &mut vt100::Parser<PtyResponses>, lines: usize) -> String {
- let rows = parser_rows(parser, lines);
- recent_text_from_rows(&rows, lines)
-}
-
fn wait_for_processes_to_exit(pids: &[u32], timeout: std::time::Duration) -> bool {
let deadline = std::time::Instant::now() + timeout;
loop {
@@ -1756,41 +1393,14 @@ impl PaneRuntime {
// --- Writer channel ---
let (input_tx, mut input_rx) = mpsc::channel::<Bytes>(32);
- // Live screen snapshot for detection (decoupled from parser scrollback)
- #[cfg_attr(feature = "ghostty-vt", allow(unused_variables))]
- let screen_content = Arc::new(RwLock::new(String::new()));
-
- #[cfg(not(feature = "ghostty-vt"))]
- let terminal = {
- let responses = PtyResponses::new();
- let kitty_keyboard_flags = responses.kitty_keyboard_flags.clone();
- let mouse_alternate_scroll = responses.mouse_alternate_scroll.clone();
- let parser = Arc::new(RwLock::new(vt100::Parser::new_with_callbacks(
- rows, cols, 10000, responses,
- )));
- (
- Arc::new(PaneTerminal::Vt(VtPaneTerminal::new(
- parser,
- screen_content.clone(),
- mouse_alternate_scroll,
- ))),
- kitty_keyboard_flags,
- )
- };
-
- #[cfg(feature = "ghostty-vt")]
- let terminal = {
- let terminal = crate::ghostty::Terminal::new(cols, rows, 10000)
- .map_err(|e| std::io::Error::other(e.to_string()))?;
- let pane_terminal = GhosttyPaneTerminal::new(terminal, input_tx.clone())?;
- pane_terminal.apply_host_terminal_theme(_host_terminal_theme);
- (
- Arc::new(PaneTerminal::Ghostty(pane_terminal)),
- Arc::new(AtomicU16::new(0)),
- )
- };
-
- let (terminal, kitty_keyboard_flags) = terminal;
+ let terminal = crate::ghostty::Terminal::new(cols, rows, 10000)
+ .map_err(|e| std::io::Error::other(e.to_string()))?;
+ let pane_terminal = GhosttyPaneTerminal::new(terminal, input_tx.clone())?;
+ pane_terminal.apply_host_terminal_theme(_host_terminal_theme);
+ let terminal = Arc::new(PaneTerminal {
+ ghostty: pane_terminal,
+ });
+ let kitty_keyboard_flags = Arc::new(AtomicU16::new(0));
let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".into());
let mut cmd = CommandBuilder::new(&shell);
@@ -2141,7 +1751,6 @@ impl PaneRuntime {
self.send_bytes(Bytes::from(payload)).await
}
- #[cfg(feature = "ghostty-vt")]
pub fn try_send_focus_event(&self, event: crate::ghostty::FocusEvent) -> bool {
if !self
.input_state()
@@ -2229,7 +1838,6 @@ impl PaneRuntime {
mod tests {
use super::*;
- #[cfg(feature = "ghostty-vt")]
#[test]
fn ghostty_keyboard_protocol_tracks_live_terminal_flags() {
let (tx, _rx) = mpsc::channel(4);
@@ -2243,7 +1851,6 @@ mod tests {
);
}
- #[cfg(feature = "ghostty-vt")]
#[test]
fn ghostty_plain_text_chars_still_encode_as_text() {
let (tx, _rx) = mpsc::channel(4);
@@ -2261,7 +1868,6 @@ mod tests {
assert_eq!(encoded, b"a");
}
- #[cfg(feature = "ghostty-vt")]
#[test]
fn ghostty_char_keys_still_use_herdr_encoding() {
let (tx, _rx) = mpsc::channel(4);
@@ -2280,7 +1886,6 @@ mod tests {
assert_eq!(encoded, vec![1]);
}
- #[cfg(feature = "ghostty-vt")]
#[test]
fn ghostty_key_encoding_honors_application_cursor_mode() {
let (tx, _rx) = mpsc::channel(4);
@@ -2301,7 +1906,6 @@ mod tests {
assert_eq!(encoded, b"\x1bOA");
}
- #[cfg(feature = "ghostty-vt")]
#[test]
fn ghostty_mouse_button_encoding_uses_live_terminal_state() {
let (tx, _rx) = mpsc::channel(4);
@@ -2319,7 +1923,6 @@ mod tests {
assert_eq!(encoded.as_deref(), Some(&b"\x1b[<0;12;10m"[..]));
}
- #[cfg(feature = "ghostty-vt")]
#[test]
fn ghostty_mouse_drag_encoding_uses_motion_reporting_state() {
let (tx, _rx) = mpsc::channel(4);
@@ -2337,7 +1940,6 @@ mod tests {
assert_eq!(encoded.as_deref(), Some(&b"\x1b[<36;5;7M"[..]));
}
- #[cfg(feature = "ghostty-vt")]
#[test]
fn ghostty_normalize_buffer_symbol_uses_ratatui_width_contract() {
assert_eq!(
@@ -2362,7 +1964,6 @@ mod tests {
);
}
- #[cfg(feature = "ghostty-vt")]
#[tokio::test]
async fn focus_events_are_forwarded_when_enabled() {
let (tx, mut rx) = mpsc::channel(4);
@@ -2372,9 +1973,9 @@ mod tests {
.mode_set(crate::ghostty::MODE_FOCUS_EVENT, true)
.unwrap();
let runtime = PaneRuntime {
- terminal: Arc::new(PaneTerminal::Ghostty(
- GhosttyPaneTerminal::new(terminal, tx.clone()).unwrap(),
- )),
+ terminal: Arc::new(PaneTerminal {
+ ghostty: GhosttyPaneTerminal::new(terminal, tx.clone()).unwrap(),
+ }),
sender: tx,
resize_tx,
current_size: Cell::new((80, 24)),
@@ -2389,16 +1990,15 @@ mod tests {
assert_eq!(rx.recv().await.unwrap(), Bytes::from_static(b"\x1b[I"));
}
- #[cfg(feature = "ghostty-vt")]
#[tokio::test]
async fn focus_events_are_suppressed_when_disabled() {
let (tx, mut rx) = mpsc::channel(4);
let (resize_tx, _resize_rx) = mpsc::channel(1);
let terminal = crate::ghostty::Terminal::new(80, 24, 0).unwrap();
let runtime = PaneRuntime {
- terminal: Arc::new(PaneTerminal::Ghostty(
- GhosttyPaneTerminal::new(terminal, tx.clone()).unwrap(),
- )),
+ terminal: Arc::new(PaneTerminal {
+ ghostty: GhosttyPaneTerminal::new(terminal, tx.clone()).unwrap(),
+ }),
sender: tx,
resize_tx,
current_size: Cell::new((80, 24)),
@@ -2418,29 +2018,6 @@ mod tests {
}
#[test]
- fn recent_text_reconstructs_scrollback_tail() {
- let responses = PtyResponses::new();
- let mut parser = vt100::Parser::new_with_callbacks(3, 10, 100, responses);
- parser.process(b"a\r\nb\r\nc\r\nd\r\ne");
-
- let recent = recent_text_from_parser(&mut parser, 4);
- assert_eq!(recent, "b\nc\nd\ne\n");
- }
-
- #[test]
- fn max_scrollback_reports_clamped_history_without_changing_position() {
- let responses = PtyResponses::new();
- let mut parser = vt100::Parser::new_with_callbacks(3, 10, 100, responses);
- parser.process(b"a\r\nb\r\nc\r\nd\r\ne");
- parser.screen_mut().set_scrollback(1);
-
- let max = max_scrollback(&mut parser);
-
- assert_eq!(max, 2);
- assert_eq!(parser.screen().scrollback(), 1);
- }
-
- #[test]
fn trim_trailing_blank_rows_drops_empty_viewport_tail() {
let mut rows = vec!["hello".to_string(), "".to_string(), " ".to_string()];
trim_trailing_blank_rows(&mut rows);
@@ -2448,48 +2025,6 @@ mod tests {
}
#[test]
- fn alternate_screen_does_not_accumulate_host_scrollback() {
- let responses = PtyResponses::new();
- let mut parser = vt100::Parser::new_with_callbacks(3, 10, 100, responses);
- parser.process(b"\x1b[?1049h1\r\n2\r\n3\r\n4");
-
- assert!(parser.screen().alternate_screen());
- assert_eq!(max_scrollback(&mut parser), 0);
- assert_eq!(recent_text_from_parser(&mut parser, 4), "2\n3\n4\n");
- }
-
- #[test]
- fn normal_screen_top_anchored_scroll_regions_feed_scrollback() {
- let responses = PtyResponses::new();
- let mut parser = vt100::Parser::new_with_callbacks(5, 10, 100, responses);
- parser.process(b"1\r\n2\r\n3\r\n4\r\n5");
- parser.process(b"\x1b[1;3r\x1b[3;1H\r\nX");
-
- assert_eq!(max_scrollback(&mut parser), 1);
- }
-
- #[test]
- fn normal_screen_non_top_anchored_scroll_regions_do_not_feed_scrollback() {
- let responses = PtyResponses::new();
- let mut parser = vt100::Parser::new_with_callbacks(5, 10, 100, responses);
- parser.process(b"1\r\n2\r\n3\r\n4\r\n5");
- parser.process(b"\x1b[2;4r\x1b[4;1H\r\nX");
-
- assert_eq!(max_scrollback(&mut parser), 0);
- }
-
- #[test]
- fn alternate_screen_scroll_regions_do_not_create_host_scrollback() {
- let responses = PtyResponses::new();
- let mut parser = vt100::Parser::new_with_callbacks(5, 10, 100, responses);
- parser.process(b"\x1b[?1049h1\r\n2\r\n3\r\n4\r\n5");
- parser.process(b"\x1b[1;3r\x1b[3;1H\r\nX");
-
- assert!(parser.screen().alternate_screen());
- assert_eq!(max_scrollback(&mut parser), 0);
- }
-
- #[test]
fn claude_working_is_sticky_for_short_gap() {
let now = std::time::Instant::now();
let mut last_working = None;
diff --git a/src/pty_callbacks.rs b/src/pty_callbacks.rs
deleted file mode 100644
index 53483e51..00000000
--- a/src/pty_callbacks.rs
+++ /dev/null
@@ -1,450 +0,0 @@
-//! Terminal query responses.
-//!
-//! Programs like fzf, htop, and vim send escape sequences asking "what terminal am I?"
-//! and "where's the cursor?". A real terminal responds immediately. We need to do the same.
-//!
-//! There are ~15 query types that real programs use. We handle the common ones.
-//! The vt100 parser handles all OUTPUT sequences (formatting, cursor, etc.) — those
-//! don't need responses. Only QUERY sequences need us to reply.
-
-use std::sync::{
- atomic::{AtomicBool, AtomicU16, Ordering},
- Arc, Mutex,
-};
-
-/// Collects response bytes that need to be written back to the PTY.
-/// Also tracks whether the child has requested the Kitty keyboard protocol.
-#[derive(Clone)]
-pub struct PtyResponses {
- pending: Arc<Mutex<Vec<u8>>>,
- /// Stack of kitty keyboard enhancement flags pushed by child programs.
- /// Each push adds a flags value; pop removes the top. This correctly handles
- /// nested programs (e.g. fish pushes mode 1, neovim pushes mode 3, neovim
- /// pops → fish's mode 1 is restored).
- kitty_stack: Arc<Mutex<Vec<u16>>>,
- /// Derived from kitty_stack: true when stack is non-empty.
- /// Kept for quick feature checks.
- pub kitty_keyboard: Arc<AtomicBool>,
- /// Exact active kitty keyboard flags from the top of the stack.
- pub kitty_keyboard_flags: Arc<AtomicU16>,
- /// Tracks DECSET 1007 (alternate scroll mode).
- /// Default on, matching Ghostty/xterm-style behavior for fullscreen apps.
- pub mouse_alternate_scroll: Arc<AtomicBool>,
-}
-
-impl Default for PtyResponses {
- fn default() -> Self {
- Self {
- pending: Arc::default(),
- kitty_stack: Arc::default(),
- kitty_keyboard: Arc::new(AtomicBool::new(false)),
- kitty_keyboard_flags: Arc::new(AtomicU16::new(0)),
- mouse_alternate_scroll: Arc::new(AtomicBool::new(true)),
- }
- }
-}
-
-#[cfg_attr(feature = "ghostty-vt", allow(dead_code))]
-impl PtyResponses {
- pub fn new() -> Self {
- Self::default()
- }
-
- /// Take any pending response bytes (clears the buffer).
- pub fn take(&self) -> Vec<u8> {
- let mut pending = self.pending.lock().unwrap();
- std::mem::take(&mut *pending)
- }
-
- fn push(&self, bytes: &[u8]) {
- self.pending.lock().unwrap().extend_from_slice(bytes);
- }
-
- fn has_private_mode(params: &[&[u16]], mode: u16) -> bool {
- params
- .iter()
- .any(|param| param.len() == 1 && param[0] == mode)
- }
-}
-
-impl vt100::Callbacks for PtyResponses {
- fn unhandled_csi(
- &mut self,
- screen: &mut vt100::Screen,
- i1: Option<u8>,
- _i2: Option<u8>,
- params: &[&[u16]],
- c: char,
- ) {
- let param0 = params.first().and_then(|p| p.first()).copied().unwrap_or(0);
-
- match (i1, c) {
- // === Device Attributes ===
-
- // DA1: \e[c or \e[0c → "what terminal are you?"
- // Respond as VT220 with ANSI color
- (None, 'c') if param0 == 0 => {
- self.push(b"\x1b[?62;22c");
- }
-
- // DA2: \e[>c or \e[>0c → "secondary device attributes"
- (Some(b'>'), 'c') => {
- // Type 0 (VT100), firmware version 0, ROM version 0
- self.push(b"\x1b[>0;0;0c");
- }
-
- // === Cursor / Status Reports ===
-
- // DSR: \e[Nn where N selects the report type
- (None, 'n') => match param0 {
- // CPR: \e[6n → cursor position report
- 5 => {
- // Device status: "OK"
- self.push(b"\x1b[0n");
- }
- 6 => {
- let (row, col) = screen.cursor_position();
- let response = format!("\x1b[{};{}R", row + 1, col + 1);
- self.push(response.as_bytes());
- }
- _ => {}
- },
-
- // DECXCPR: \e[?6n → extended cursor position report (with page)
- (Some(b'?'), 'n') if param0 == 6 => {
- let (row, col) = screen.cursor_position();
- let response = format!("\x1b[?{};{}R", row + 1, col + 1);
- self.push(response.as_bytes());
- }
-
- // === Mode Queries (DECRQM) ===
-
- // DECRQM: \e[?Np → "is DEC private mode N set?"
- // Response: \e[?N;Ps$y where Ps = 1 (set), 2 (reset), 0 (unknown)
- (Some(b'?'), 'p') => {
- let state = match param0 {
- 1007 => {
- if self.mouse_alternate_scroll.load(Ordering::Relaxed) {
- 1
- } else {
- 2
- }
- }
- _ => 2,
- };
- let response = format!("\x1b[?{param0};{state}$y");
- self.push(response.as_bytes());
- }
-
- // ANSI DECRQM: \e[Np → "is ANSI mode N set?"
- (None, 'p') => {
- let response = format!("\x1b[{param0};2$y");
- self.push(response.as_bytes());
- }
-
- // === Keyboard Protocol ===
-
- // DECSET/DECRST 1007: alternate scroll mode.
- (Some(b'?'), 'h') if Self::has_private_mode(params, 1007) => {
- self.mouse_alternate_scroll.store(true, Ordering::Relaxed);
- }
- (Some(b'?'), 'l') if Self::has_private_mode(params, 1007) => {
- self.mouse_alternate_scroll.store(false, Ordering::Relaxed);
- }
-
- // Kitty keyboard query: \e[?u → "what keyboard flags are active?"
- (Some(b'?'), 'u') => {
- let stack = self.kitty_stack.lock().unwrap();
- let flags = stack.last().copied().unwrap_or(0);
- self.push(format!("\x1b[?{flags}u").as_bytes());
- }
-
- // Kitty keyboard push: \e[>Nu → child wants Kitty key encoding
- (Some(b'>'), 'u') => {
- let mut stack = self.kitty_stack.lock().unwrap();
- stack.push(param0);
- self.kitty_keyboard.store(true, Ordering::Relaxed);
- self.kitty_keyboard_flags.store(param0, Ordering::Relaxed);
- }
-
- // Kitty keyboard pop: \e[<Nu → child reverts to previous mode
- // N = number of entries to pop (default 1)
- (Some(b'<'), 'u') => {
- let mut stack = self.kitty_stack.lock().unwrap();
- let count = (param0 as usize).max(1);
- for _ in 0..count {
- if stack.pop().is_none() {
- break;
- }
- }
- let flags = stack.last().copied().unwrap_or(0);
- self.kitty_keyboard
- .store(!stack.is_empty(), Ordering::Relaxed);
- self.kitty_keyboard_flags.store(flags, Ordering::Relaxed);
- }
-
- // === Terminal Identification ===
-
- // XTVERSION: \e[>q → "what terminal version?"
- (Some(b'>'), 'q') => {
- // Respond in DCS format: \eP>|herdr 0.1\e\\
- self.push(b"\x1bP>|herdr 0.1\x1b\\");
- }
-
- _ => {}
- }
- }
-
- fn unhandled_osc(&mut self, _screen: &mut vt100::Screen, params: &[&[u8]]) {
- let Some(cmd) = params.first() else { return };
-
- match *cmd {
- // OSC 10 ; ? ST → query foreground color
- b"10" => {
- if params.get(1) == Some(&&b"?"[..]) {
- // Respond with a default light foreground
- self.push(b"\x1b]10;rgb:cccc/cccc/cccc\x1b\\");
- }
- }
- // OSC 11 ; ? ST → query background color
- b"11" => {
- if params.get(1) == Some(&&b"?"[..]) {
- // Respond with a default dark background
- self.push(b"\x1b]11;rgb:1e1e/1e1e/2e2e\x1b\\");
- }
- }
- // OSC 12 ; ? ST → query cursor color
- b"12" => {
- if params.get(1) == Some(&&b"?"[..]) {
- self.push(b"\x1b]12;rgb:cccc/cccc/cccc\x1b\\");
- }
- }
- _ => {}
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- fn make_parser(responses: PtyResponses) -> vt100::Parser<PtyResponses> {
- vt100::Parser::new_with_callbacks(24, 80, 0, responses)
- }
-
- #[test]
- fn responds_to_da1() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
- p.process(b"\x1b[c");
- assert_eq!(r.take(), b"\x1b[?62;22c");
- }
-
- #[test]
- fn responds_to_da1_explicit_zero() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
- p.process(b"\x1b[0c");
- assert_eq!(r.take(), b"\x1b[?62;22c");
- }
-
- #[test]
- fn responds_to_da2() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
- p.process(b"\x1b[>c");
- assert_eq!(r.take(), b"\x1b[>0;0;0c");
- }
-
- #[test]
- fn responds_to_cpr() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
- p.process(b"\x1b[5;10H"); // move cursor to row 5, col 10
- p.process(b"\x1b[6n");
- assert_eq!(r.take(), b"\x1b[5;10R");
- }
-
- #[test]
- fn responds_to_dsr_status() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
- p.process(b"\x1b[5n"); // device status report
- assert_eq!(r.take(), b"\x1b[0n"); // "OK"
- }
-
- #[test]
- fn responds_to_extended_cpr() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
- p.process(b"\x1b[3;7H"); // move cursor
- p.process(b"\x1b[?6n"); // extended CPR
- assert_eq!(r.take(), b"\x1b[?3;7R");
- }
-
- #[test]
- fn responds_to_decrqm_private() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
- p.process(b"\x1b[?25p"); // query: is cursor visible (mode 25)?
- assert_eq!(r.take(), b"\x1b[?25;2$y"); // "reset" (2)
- }
-
- #[test]
- fn alternate_scroll_defaults_on_and_reports_set() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
-
- assert!(r.mouse_alternate_scroll.load(Ordering::Relaxed));
- p.process(b"\x1b[?1007p");
- assert_eq!(r.take(), b"\x1b[?1007;1$y");
- }
-
- #[test]
- fn decset_decrst_1007_updates_alternate_scroll_mode() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
-
- p.process(b"\x1b[?1007l");
- assert!(!r.mouse_alternate_scroll.load(Ordering::Relaxed));
- p.process(b"\x1b[?1007p");
- assert_eq!(r.take(), b"\x1b[?1007;2$y");
-
- p.process(b"\x1b[?1007h");
- assert!(r.mouse_alternate_scroll.load(Ordering::Relaxed));
- p.process(b"\x1b[?1007p");
- assert_eq!(r.take(), b"\x1b[?1007;1$y");
- }
-
- #[test]
- fn responds_to_kitty_keyboard_query() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
- p.process(b"\x1b[?u");
- assert_eq!(r.take(), b"\x1b[?0u");
- }
-
- #[test]
- fn kitty_push_pop_stack() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
-
- // Initially off
- assert!(!r.kitty_keyboard.load(Ordering::Relaxed));
- assert_eq!(r.kitty_keyboard_flags.load(Ordering::Relaxed), 0);
-
- // Fish pushes mode 1
- p.process(b"\x1b[>1u");
- assert!(r.kitty_keyboard.load(Ordering::Relaxed));
- assert_eq!(r.kitty_keyboard_flags.load(Ordering::Relaxed), 1);
- p.process(b"\x1b[?u");
- assert_eq!(r.take(), b"\x1b[?1u");
-
- // Neovim pushes mode 3
- p.process(b"\x1b[>3u");
- assert!(r.kitty_keyboard.load(Ordering::Relaxed));
- assert_eq!(r.kitty_keyboard_flags.load(Ordering::Relaxed), 3);
- p.process(b"\x1b[?u");
- assert_eq!(r.take(), b"\x1b[?3u");
-
- // Neovim pops → fish's mode 1 is restored
- p.process(b"\x1b[<u");
- assert!(r.kitty_keyboard.load(Ordering::Relaxed));
- assert_eq!(r.kitty_keyboard_flags.load(Ordering::Relaxed), 1);
- p.process(b"\x1b[?u");
- assert_eq!(r.take(), b"\x1b[?1u");
-
- // Fish pops → back to legacy
- p.process(b"\x1b[<u");
- assert!(!r.kitty_keyboard.load(Ordering::Relaxed));
- assert_eq!(r.kitty_keyboard_flags.load(Ordering::Relaxed), 0);
- p.process(b"\x1b[?u");
- assert_eq!(r.take(), b"\x1b[?0u");
- }
-
- #[test]
- fn kitty_pop_on_empty_is_harmless() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
-
- // Pop with nothing on stack — should not crash
- p.process(b"\x1b[<u");
- assert!(!r.kitty_keyboard.load(Ordering::Relaxed));
- assert_eq!(r.kitty_keyboard_flags.load(Ordering::Relaxed), 0);
- }
-
- #[test]
- fn kitty_pop_count() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
-
- // Push three times
- p.process(b"\x1b[>1u");
- p.process(b"\x1b[>3u");
- p.process(b"\x1b[>5u");
- assert!(r.kitty_keyboard.load(Ordering::Relaxed));
- assert_eq!(r.kitty_keyboard_flags.load(Ordering::Relaxed), 5);
-
- // Pop 2 at once
- p.process(b"\x1b[<2u");
- assert!(r.kitty_keyboard.load(Ordering::Relaxed));
- assert_eq!(r.kitty_keyboard_flags.load(Ordering::Relaxed), 1);
- p.process(b"\x1b[?u");
- assert_eq!(r.take(), b"\x1b[?1u"); // only first push remains
-
- // Pop last one
- p.process(b"\x1b[<u");
- assert!(!r.kitty_keyboard.load(Ordering::Relaxed));
- assert_eq!(r.kitty_keyboard_flags.load(Ordering::Relaxed), 0);
- }
-
- #[test]
- fn responds_to_xtversion() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
- p.process(b"\x1b[>q");
- assert_eq!(r.take(), b"\x1bP>|herdr 0.1\x1b\\");
- }
-
- #[test]
- fn responds_to_osc_fg_color_query() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
- p.process(b"\x1b]10;?\x1b\\");
- assert_eq!(r.take(), b"\x1b]10;rgb:cccc/cccc/cccc\x1b\\");
- }
-
- #[test]
- fn responds_to_osc_bg_color_query() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
- p.process(b"\x1b]11;?\x1b\\");
- assert_eq!(r.take(), b"\x1b]11;rgb:1e1e/1e1e/2e2e\x1b\\");
- }
-
- #[test]
- fn responds_to_osc_cursor_color_query() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
- p.process(b"\x1b]12;?\x1b\\");
- assert_eq!(r.take(), b"\x1b]12;rgb:cccc/cccc/cccc\x1b\\");
- }
-
- #[test]
- fn no_response_for_regular_output() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
- p.process(b"hello world\x1b[31mred\x1b[0m");
- assert!(r.take().is_empty());
- }
-
- #[test]
- fn multiple_queries_accumulate() {
- let r = PtyResponses::new();
- let mut p = make_parser(r.clone());
- p.process(b"\x1b[c\x1b[6n");
- let bytes = r.take();
- assert!(bytes.starts_with(b"\x1b[?62;22c"));
- assert!(bytes.ends_with(b"\x1b[1;1R"));
- }
-}
diff --git a/src/selection.rs b/src/selection.rs
index 78c9a2f3..5531a189 100644
--- a/src/selection.rs
+++ b/src/selection.rs
@@ -142,13 +142,6 @@ impl Selection {
}
}
-/// Extract selected text from a vt100 screen.
-pub fn extract_text(screen: &vt100::Screen, selection: &Selection) -> String {
- let ((sr, sc), (er, ec)) = selection.ordered();
- // contents_between end_col is exclusive in vt100
- screen.contents_between(sr, sc, er, ec + 1)
-}
-
/// Write text to the system clipboard via OSC 52.
///
/// OSC 52 format: `ESC ] 52 ; c ; <base64> BEL`
diff --git a/src/terminal_theme.rs b/src/terminal_theme.rs
index e0b4b9d5..523e3e38 100644
--- a/src/terminal_theme.rs
+++ b/src/terminal_theme.rs
@@ -47,7 +47,6 @@ pub fn parse_default_color_response(sequence: &str) -> Option<(DefaultColorKind,
Some((kind, parse_rgb_color(value)?))
}
-#[cfg_attr(not(feature = "ghostty-vt"), allow(dead_code))]
pub fn osc_set_default_color_sequence(kind: DefaultColorKind, color: RgbColor) -> String {
let command = match kind {
DefaultColorKind::Foreground => 10,
diff --git a/vendor/vt100/.cargo-ok b/vendor/vt100/.cargo-ok
deleted file mode 100644
index 5f8b7958..00000000
--- a/vendor/vt100/.cargo-ok
+++ /dev/null
@@ -1 +0,0 @@
-{"v":1} \ No newline at end of file
diff --git a/vendor/vt100/.cargo_vcs_info.json b/vendor/vt100/.cargo_vcs_info.json
deleted file mode 100644
index 99725106..00000000
--- a/vendor/vt100/.cargo_vcs_info.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "git": {
- "sha1": "eb66ffaf7d771c13303ef73b29f6f2a56fdacecf"
- },
- "path_in_vcs": ""
-} \ No newline at end of file
diff --git a/vendor/vt100/CHANGELOG.md b/vendor/vt100/CHANGELOG.md
deleted file mode 100644
index d589ed7a..00000000
--- a/vendor/vt100/CHANGELOG.md
+++ /dev/null
@@ -1,396 +0,0 @@
-# Changelog
-
-## [0.16.2] - 2025-07-11
-
-### Fixed
-
-* Fixed potential cursor out of bounds when using decrc after resizing. (#13)
-
-## [0.16.1] - 2025-07-10
-
-### Changed
-
-* Reverted to the 2021 edition for now.
-
-## [0.16.0] - 2025-07-08
-
-### Added
-
-* `Parser::process_cb`, which works the same as `Parser::process` except that
- it calls callbacks during parsing when it finds a terminal escape which is
- potentially useful but not something that affects the screen itself.
-* Support for xterm window resize request escape codes, via the new callback
- mechanism.
-* Support for dim formatting. (Daniel Faust, #9)
-* Support for CNL/CPL escape codes. (Danny Weinberg, #10)
-* Support for OSC 52 (clipboard manipulation).
-
-### Removed
-
-* These methods on `Screen` have been removed in favor of the new callback
- API described above:
- * `title_formatted`
- * `title_diff`
- * `title`
- * `icon_name`
- * `bells_diff`
- * `audible_bell_count`
- * `visual_bell_count`
- * `errors`
-* Additionally, unhandled escape sequences no longer log to STDERR; they
- instead call various callback methods which can be defined to log if
- desired.
-* `Cell` no longer implements `Default`.
-* `Screen` no longer implements `vte::Perform`.
-
-### Changed
-
-* `Parser::set_size` and `Parser::set_scrollback` have been moved to methods
- on `Screen`, and `Parser::screen_mut` was added to get a mutable reference
- to the screen.
-* `Cell::contents` now returns `&str` instead of `String`, eliminating an
- allocation in many cases. (Chris Olszewski, #14)
-
-### Fixed
-
-* Fixed some issues with calculating scrollback offsets correctly in
- `Grid::visible_rows`. (rezigned, #11)
-
-## [0.15.2] - 2023-02-05
-
-### Changed
-
-* Bumped dependencies
-
-## [0.15.1] - 2021-12-21
-
-### Changed
-
-* Removed a lot of unnecessary test data from the packaged crate, making
- downloads faster
-
-## [0.15.0] - 2021-12-15
-
-### Added
-
-* `Screen::errors` to track the number of parsing errors seen so far
-
-### Fixed
-
-* No longer generate spurious diffs in some cases where the cursor is past the
- end of a row
-* Fix restoring the cursor position when scrolled back
-
-### Changed
-
-* Various internal refactorings
-
-## [0.14.0] - 2021-12-06
-
-### Changed
-
-* Unknown UTF-8 characters default to a width of 1, rather than 0 (except for
- control characters, as mentioned below)
-
-### Fixed
-
-* Ignore C1 control characters rather than adding them to the cell data, since
- they are non-printable
-
-## [0.13.2] - 2021-12-05
-
-### Changed
-
-* Delay allocation of the alternate screen until it is used (saves a bit of
- memory in basic cases)
-
-## [0.13.1] - 2021-12-04
-
-### Fixed
-
-* Fixed various line wrapping state issues
-* Fixed cursor positioning after writing zero width characters at the end of
- the line
-* Fixed `Screen::cursor_state_formatted` to draw the last character in a line
- with the appropriate drawing attributes if it needs to redraw it
-
-## [0.13.0] - 2021-11-17
-
-### Added
-
-* `Screen::alternate_screen` to determine if the alternate screen is in use
-* `Screen::row_wrapped` to determine whether the row at the given index should
- wrap its text
-* `Screen::cursor_state_formatted` to set the cursor position and hidden state
- (including internal state like the one-past-the-end state which isn't visible
- in the return value of `cursor_position`)
-
-### Fixed
-
-* `Screen::rows_formatted` now outputs correct escape codes in some edge cases
- at the beginning of a row when the previous row was wrapped
-* VPA escape sequence can no longer position the cursor off the screen
-
-## [0.12.0] - 2021-03-09
-
-### Added
-
-* `Screen::state_formatted` and `Screen::state_diff` convenience wrappers
-
-### Fixed
-
-* `Screen::attributes_formatted` now correctly resets previously set attributes
- where necessary
-
-### Removed
-
-* Removed `Screen::attributes_diff`, since I can't actually think of any
- situation where it does a thing that makes sense.
-
-## [0.11.1] - 2021-03-07
-
-### Changed
-
-* Drop dependency on `enumset`
-
-## [0.11.0] - 2021-03-07
-
-### Added
-
-* `Screen::attributes_formatted` and `Screen::attributes_diff` to retrieve the
- current state of the drawing attributes as escape sequences
-* `Screen::fgcolor`, `Screen::bgcolor`, `Screen::bold`, `Screen::italic`,
- `Screen::underline`, and `Screen::inverse` to retrieve the current state of
- the drawing attributes directly
-
-## [0.10.0] - 2021-03-06
-
-### Added
-
-* Implementation of `std::io::Write` for `Parser`
-
-## [0.9.0] - 2021-03-05
-
-### Added
-
-* `Screen::contents_between`, for returning the contents logically between two
- given cells (for things like clipboard selection)
-* Support SGR subparameters (so `\e[38:2:255:0:0m` behaves the same way as
- `\e[38;2;255;0;0m`)
-
-### Fixed
-
-* Bump `enumset` to fix a dependency which fails to build
-
-## [0.8.1] - 2020-02-09
-
-### Changed
-
-* Bumped `vte` dep to 0.6.
-
-## [0.8.0] - 2019-12-07
-
-### Removed
-
-* Removed the unicode-normalization feature altogether - it turns out that it
- still has a couple edge cases where it causes incorrect behavior, and fixing
- those would be a lot more effort.
-
-### Fixed
-
-* Fix a couple more end-of-line/wrapping bugs, especially around cursor
- positioning.
-* Fix applying combining characters to wide characters.
-* Ensure cells can't have contents with width zero (to avoid ambiguity). If an
- empty cell gets a combining character applied to it, default that cell to a
- (normal-width) space first.
-
-## [0.7.0] - 2019-11-23
-
-### Added
-
-* New (default-on) cargo feature `unicode-normalization` which can be disabled
- to disable normalizing cell contents to NFC - it's a pretty small edge case,
- and the data tables required to support it are quite large, which affects
- size-sensitive targets like wasm
-
-## [0.6.3] - 2019-11-20
-
-### Fixed
-
-* Fix output of `contents_formatted` and `contents_diff` when the cursor
- position ends at one past the end of a row.
-* If the cursor position is one past the end of a row, any char, even a
- combining char, needs to cause the cursor position to wrap.
-
-## [0.6.2] - 2019-11-13
-
-### Fixed
-
-* Fix zero-width characters when the cursor is at the end of a row.
-
-## [0.6.1] - 2019-11-13
-
-### Added
-
-* Add more debug logging for unhandled escape sequences.
-
-### Changed
-
-* Unhandled escape sequence warnings are now at the `debug` log level.
-
-## [0.6.0] - 2019-11-13
-
-### Added
-
-* `Screen::input_mode_formatted` and `Screen::input_mode_diff` give escape
- codes to set the current terminal input modes.
-* `Screen::title_formatted` and `Screen::title_diff` give escape codes to set
- the terminal window title.
-* `Screen::bells_diff` gives escape codes to trigger any audible or visual
- bells which have been seen since the previous state.
-
-### Changed
-
-* `Screen::contents_diff` no longer includes audible or visual bells (see
- `Screen::bells_diff` instead).
-
-## [0.5.1] - 2019-11-12
-
-### Fixed
-
-* `Screen::set_size` now actually resizes when requested (previously the
- underlying storage was not being resized, leading to panics when writing
- outside of the original screen).
-
-## [0.5.0] - 2019-11-12
-
-### Added
-
-* Scrollback support.
-* `Default` impl for `Parser` which creates an 80x24 terminal with no
- scrollback.
-
-### Removed
-
-* `Parser::screen_mut` (and the `pub` `&mut self` methods on `Screen`). The few
- things you can do to change the screen state directly are now exposed as
- methods on `Parser` itself.
-
-### Changed
-
-* `Cell::contents` now returns a `String` instead of a `&str`.
-* `Screen::check_audible_bell` and `Screen::check_visual_bell` have been
- replaced with `Screen::audible_bell_count` and `Screen::visual_bell_count`.
- You should keep track of the "since the last method call" state yourself
- instead of having the screen track it for you.
-
-### Fixed
-
-* Lots of performance and output optimizations.
-* Clearing a cell now sets all of that cell's attributes to the current
- attribute set, since different terminals render different things for an empty
- cell based on the attributes.
-* `Screen::contents_diff` now includes audible and visual bells when
- appropriate.
-
-## [0.4.0] - 2019-11-08
-
-### Removed
-
-* `Screen::fgcolor`, `Screen::bgcolor`, `Screen::bold`, `Screen::italic`,
- `Screen::underline`, `Screen::inverse`, and `Screen::alternate_screen`:
- these are just implementation details that people shouldn't need to care
- about.
-
-### Fixed
-
-* Fixed cursor movement when the cursor position is already outside of an
- active scroll region.
-
-## [0.3.2] - 2019-11-08
-
-### Fixed
-
-* Clearing cells now correctly sets the cell background color.
-* Fixed a couple bugs in wide character handling in `contents_formatted` and
- `contents_diff`.
-* Fixed RI when the cursor is at the top of the screen (fixes scrolling up in
- `less`, for instance).
-* Fixed VPA incorrectly being clamped to the scroll region.
-* Stop treating soft hyphen specially (as far as i can tell, no other terminals
- do this, and i'm not sure why i thought it was necessary to begin with).
-* `contents_formatted` now also resets attributes at the start, like
- `contents_diff` does.
-
-## [0.3.1] - 2019-11-06
-
-### Fixed
-
-* Make `contents_formatted` explicitly show the cursor when necessary, in case
- the cursor was previously hidden.
-
-## [0.3.0] - 2019-11-06
-
-### Added
-
-* `Screen::rows` which is like `Screen::contents` except that it returns the
- data by row instead of all at once, and also allows you to restrict the
- region returned to a subset of columns.
-* `Screen::rows_formatted` which is like `Screen::rows`, but returns escape
- sequences sufficient to draw the requested subset of each row.
-* `Screen::contents_diff` and `Screen::rows_diff` which return escape sequences
- sufficient to turn the visible state of one screen (or a subset of the screen
- in the case of `rows_diff`) into another.
-
-### Changed
-
-* The screen is now exposed separately from the parser, and is cloneable.
-* `contents_formatted` now returns `Vec<u8>` instead of `String`.
-* `contents` and `contents_formatted` now only allow getting the contents of
- the entire screen rather than a subset (but see the entry for `rows` and
- `rows_formatted` above).
-
-### Removed
-
-* `Cell::new`, since there's not really any reason that this is useful for
- someone to do from outside of the crate.
-
-### Fixed
-
-* `contents_formatted` now preserves the state of empty cells instead of
- filling them with spaces.
-* We now clear the row wrapping state when the number of columns in the
- terminal is changed.
-* `contents_formatted` now ensures that the cursor has the correct hidden state
- and location.
-* `contents_formatted` now clears the screen before starting to draw.
-
-## [0.2.0] - 2019-11-04
-
-### Changed
-
-* Reimplemented in pure safe rust, with a much more accurate parser
-* A bunch of minor API tweaks, some backwards-incompatible
-
-## [0.1.2] - 2016-06-04
-
-### Fixed
-
-* Fix returning uninit memory in get_string_formatted/get_string_plaintext
-* Handle emoji and zero width unicode characters properly
-* Fix cursor positioning with regards to scroll regions and wrapping
-* Fix parsing of (ignored) character set escapes
-* Explicitly suppress status report escapes
-
-## [0.1.1] - 2016-04-28
-
-### Fixed
-
-* Fix builds
-
-## [0.1.0] - 2016-04-28
-
-### Added
-
-* Initial release
diff --git a/vendor/vt100/Cargo.lock b/vendor/vt100/Cargo.lock
deleted file mode 100644
index b984ee78..00000000
--- a/vendor/vt100/Cargo.lock
+++ /dev/null
@@ -1,540 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 4
-
-[[package]]
-name = "aho-corasick"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "arrayvec"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
-
-[[package]]
-name = "bitflags"
-version = "2.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
-
-[[package]]
-name = "cfg-if"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
-
-[[package]]
-name = "cfg_aliases"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
-
-[[package]]
-name = "env_logger"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
-dependencies = [
- "log",
- "regex",
-]
-
-[[package]]
-name = "errno"
-version = "0.3.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
-dependencies = [
- "libc",
- "windows-sys 0.60.2",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.2.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi 0.11.1+wasi-snapshot-preview1",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
-dependencies = [
- "cfg-if",
- "libc",
- "r-efi",
- "wasi 0.14.2+wasi-0.2.4",
-]
-
-[[package]]
-name = "itoa"
-version = "1.0.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
-
-[[package]]
-name = "libc"
-version = "0.2.174"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
-
-[[package]]
-name = "linux-raw-sys"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
-
-[[package]]
-name = "log"
-version = "0.4.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
-
-[[package]]
-name = "memchr"
-version = "2.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
-
-[[package]]
-name = "nix"
-version = "0.30.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
-dependencies = [
- "bitflags",
- "cfg-if",
- "cfg_aliases",
- "libc",
-]
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
-dependencies = [
- "zerocopy",
-]
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.95"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "quickcheck"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
-dependencies = [
- "env_logger",
- "log",
- "rand 0.8.5",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.40"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "r-efi"
-version = "5.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
-
-[[package]]
-name = "rand"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
-dependencies = [
- "rand_core 0.6.4",
-]
-
-[[package]]
-name = "rand"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
-dependencies = [
- "rand_chacha",
- "rand_core 0.9.3",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
-dependencies = [
- "ppv-lite86",
- "rand_core 0.9.3",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom 0.2.16",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
-dependencies = [
- "getrandom 0.3.3",
-]
-
-[[package]]
-name = "regex"
-version = "1.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
-
-[[package]]
-name = "rustix"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
-dependencies = [
- "bitflags",
- "errno",
- "libc",
- "linux-raw-sys",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "ryu"
-version = "1.0.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
-
-[[package]]
-name = "serde"
-version = "1.0.219"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.219"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "serde_json"
-version = "1.0.140"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
-dependencies = [
- "itoa",
- "memchr",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.104"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "terminal_size"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed"
-dependencies = [
- "rustix",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
-
-[[package]]
-name = "unicode-width"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
-
-[[package]]
-name = "vt100"
-version = "0.16.2"
-dependencies = [
- "itoa",
- "nix",
- "quickcheck",
- "rand 0.9.1",
- "serde",
- "serde_json",
- "terminal_size",
- "unicode-width",
- "vte",
-]
-
-[[package]]
-name = "vte"
-version = "0.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5924018406ce0063cd67f8e008104968b74b563ee1b85dde3ed1f7cb87d3dbd"
-dependencies = [
- "arrayvec",
- "memchr",
-]
-
-[[package]]
-name = "wasi"
-version = "0.11.1+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
-
-[[package]]
-name = "wasi"
-version = "0.14.2+wasi-0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
-dependencies = [
- "wit-bindgen-rt",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.59.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.60.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
-dependencies = [
- "windows-targets 0.53.2",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
-dependencies = [
- "windows_aarch64_gnullvm 0.52.6",
- "windows_aarch64_msvc 0.52.6",
- "windows_i686_gnu 0.52.6",
- "windows_i686_gnullvm 0.52.6",
- "windows_i686_msvc 0.52.6",
- "windows_x86_64_gnu 0.52.6",
- "windows_x86_64_gnullvm 0.52.6",
- "windows_x86_64_msvc 0.52.6",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.53.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
-dependencies = [
- "windows_aarch64_gnullvm 0.53.0",
- "windows_aarch64_msvc 0.53.0",
- "windows_i686_gnu 0.53.0",
- "windows_i686_gnullvm 0.53.0",
- "windows_i686_msvc 0.53.0",
- "windows_x86_64_gnu 0.53.0",
- "windows_x86_64_gnullvm 0.53.0",
- "windows_x86_64_msvc 0.53.0",
-]
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
-
-[[package]]
-name = "windows_i686_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
-
-[[package]]
-name = "windows_i686_gnullvm"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
-
-[[package]]
-name = "wit-bindgen-rt"
-version = "0.39.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "zerocopy"
-version = "0.8.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
-dependencies = [
- "zerocopy-derive",
-]
-
-[[package]]
-name = "zerocopy-derive"
-version = "0.8.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
diff --git a/vendor/vt100/Cargo.toml b/vendor/vt100/Cargo.toml
deleted file mode 100644
index 098eea05..00000000
--- a/vendor/vt100/Cargo.toml
+++ /dev/null
@@ -1,75 +0,0 @@
-# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
-#
-# When uploading crates to the registry Cargo will automatically
-# "normalize" Cargo.toml files for maximal compatibility
-# with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies.
-#
-# If you are reading this file be aware that the original Cargo.toml
-# will likely look very different (and much more reasonable).
-# See Cargo.toml.orig for the original contents.
-
-[package]
-edition = "2021"
-rust-version = "1.70"
-name = "vt100"
-version = "0.16.2"
-authors = ["Jesse Luehrs <doy@tozt.net>"]
-build = false
-include = [
- "src/**/*",
- "LICENSE",
- "README.md",
- "CHANGELOG.md",
-]
-autolib = false
-autobins = false
-autoexamples = false
-autotests = false
-autobenches = false
-description = "Library for parsing terminal data"
-homepage = "https://github.com/doy/vt100-rust"
-readme = "README.md"
-keywords = [
- "terminal",
- "vt100",
-]
-categories = [
- "command-line-interface",
- "encoding",
-]
-license = "MIT"
-repository = "https://github.com/doy/vt100-rust"
-
-[lib]
-name = "vt100"
-path = "src/lib.rs"
-
-[dependencies.itoa]
-version = "1.0.15"
-
-[dependencies.unicode-width]
-version = "0.2.1"
-
-[dependencies.vte]
-version = "0.15.0"
-
-[dev-dependencies.nix]
-version = "0.30.1"
-features = ["term"]
-
-[dev-dependencies.quickcheck]
-version = "1.0"
-
-[dev-dependencies.rand]
-version = "0.9"
-
-[dev-dependencies.serde]
-version = "1.0.219"
-features = ["derive"]
-
-[dev-dependencies.serde_json]
-version = "1.0.140"
-
-[dev-dependencies.terminal_size]
-version = "0.4.2"
diff --git a/vendor/vt100/Cargo.toml.orig b/vendor/vt100/Cargo.toml.orig
deleted file mode 100644
index db35e207..00000000
--- a/vendor/vt100/Cargo.toml.orig
+++ /dev/null
@@ -1,28 +0,0 @@
-[package]
-name = "vt100"
-version = "0.16.2"
-authors = ["Jesse Luehrs <doy@tozt.net>"]
-edition = "2021"
-rust-version = "1.70"
-
-description = "Library for parsing terminal data"
-homepage = "https://github.com/doy/vt100-rust"
-repository = "https://github.com/doy/vt100-rust"
-readme = "README.md"
-keywords = ["terminal", "vt100"]
-categories = ["command-line-interface", "encoding"]
-license = "MIT"
-include = ["src/**/*", "LICENSE", "README.md", "CHANGELOG.md"]
-
-[dependencies]
-itoa = "1.0.15"
-unicode-width = "0.2.1"
-vte = "0.15.0"
-
-[dev-dependencies]
-nix = { version = "0.30.1", features = ["term"] }
-quickcheck = "1.0"
-rand = "0.9"
-serde = { version = "1.0.219", features = ["derive"] }
-serde_json = "1.0.140"
-terminal_size = "0.4.2"
diff --git a/vendor/vt100/LICENSE b/vendor/vt100/LICENSE
deleted file mode 100644
index a1dd5b0e..00000000
--- a/vendor/vt100/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2016 Jesse Luehrs
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/vendor/vt100/README.md b/vendor/vt100/README.md
deleted file mode 100644
index a8e1b8f6..00000000
--- a/vendor/vt100/README.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# vt100
-
-This crate parses a terminal byte stream and provides an in-memory
-representation of the rendered contents.
-
-## Overview
-
-This is essentially the terminal parser component of a graphical terminal
-emulator pulled out into a separate crate. Although you can use this crate
-to build a graphical terminal emulator, it also contains functionality
-necessary for implementing terminal applications that want to run other
-terminal applications - programs like `screen` or `tmux` for example.
-
-## Synopsis
-
-```rust
-let mut parser = vt100::Parser::new(24, 80, 0);
-
-let screen = parser.screen().clone();
-parser.process(b"this text is \x1b[31mRED\x1b[m");
-assert_eq!(
- parser.screen().cell(0, 13).unwrap().fgcolor(),
- vt100::Color::Idx(1),
-);
-
-let screen = parser.screen().clone();
-parser.process(b"\x1b[3D\x1b[32mGREEN");
-assert_eq!(
- parser.screen().contents_formatted(),
- &b"\x1b[?25h\x1b[m\x1b[H\x1b[Jthis text is \x1b[32mGREEN"[..],
-);
-assert_eq!(
- parser.screen().contents_diff(&screen),
- &b"\x1b[1;14H\x1b[32mGREEN"[..],
-);
-```
diff --git a/vendor/vt100/src/attrs.rs b/vendor/vt100/src/attrs.rs
deleted file mode 100644
index e5044f2e..00000000
--- a/vendor/vt100/src/attrs.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use crate::term::BufWrite as _;
-
-/// Represents a foreground or background color for cells.
-#[derive(Eq, PartialEq, Debug, Copy, Clone, Default)]
-pub enum Color {
- /// The default terminal color.
- #[default]
- Default,
-
- /// An indexed terminal color.
- Idx(u8),
-
- /// An RGB terminal color. The parameters are (red, green, blue).
- Rgb(u8, u8, u8),
-}
-
-const TEXT_MODE_INTENSITY: u8 = 0b0000_0011;
-const TEXT_MODE_BOLD: u8 = 0b0000_0001;
-const TEXT_MODE_DIM: u8 = 0b0000_0010;
-const TEXT_MODE_ITALIC: u8 = 0b0000_0100;
-const TEXT_MODE_UNDERLINE: u8 = 0b0000_1000;
-const TEXT_MODE_INVERSE: u8 = 0b0001_0000;
-
-#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
-pub struct Attrs {
- pub fgcolor: Color,
- pub bgcolor: Color,
- pub mode: u8,
-}
-
-impl Attrs {
- pub fn bold(&self) -> bool {
- self.mode & TEXT_MODE_BOLD != 0
- }
-
- pub fn dim(&self) -> bool {
- self.mode & TEXT_MODE_DIM != 0
- }
-
- fn intensity(&self) -> u8 {
- self.mode & TEXT_MODE_INTENSITY
- }
-
- pub fn set_bold(&mut self) {
- self.mode &= !TEXT_MODE_INTENSITY;
- self.mode |= TEXT_MODE_BOLD;
- }
-
- pub fn set_dim(&mut self) {
- self.mode &= !TEXT_MODE_INTENSITY;
- self.mode |= TEXT_MODE_DIM;
- }
-
- pub fn set_normal_intensity(&mut self) {
- self.mode &= !TEXT_MODE_INTENSITY;
- }
-
- pub fn italic(&self) -> bool {
- self.mode & TEXT_MODE_ITALIC != 0
- }
-
- pub fn set_italic(&mut self, italic: bool) {
- if italic {
- self.mode |= TEXT_MODE_ITALIC;
- } else {
- self.mode &= !TEXT_MODE_ITALIC;
- }
- }
-
- pub fn underline(&self) -> bool {
- self.mode & TEXT_MODE_UNDERLINE != 0
- }
-
- pub fn set_underline(&mut self, underline: bool) {
- if underline {
- self.mode |= TEXT_MODE_UNDERLINE;
- } else {
- self.mode &= !TEXT_MODE_UNDERLINE;
- }
- }
-
- pub fn inverse(&self) -> bool {
- self.mode & TEXT_MODE_INVERSE != 0
- }
-
- pub fn set_inverse(&mut self, inverse: bool) {
- if inverse {
- self.mode |= TEXT_MODE_INVERSE;
- } else {
- self.mode &= !TEXT_MODE_INVERSE;
- }
- }
-
- pub fn write_escape_code_diff(
- &self,
- contents: &mut Vec<u8>,
- other: &Self,
- ) {
- if self != other && self == &Self::default() {
- crate::term::ClearAttrs.write_buf(contents);
- return;
- }
-
- let attrs = crate::term::Attrs::default();
-
- let attrs = if self.fgcolor == other.fgcolor {
- attrs
- } else {
- attrs.fgcolor(self.fgcolor)
- };
- let attrs = if self.bgcolor == other.bgcolor {
- attrs
- } else {
- attrs.bgcolor(self.bgcolor)
- };
- let attrs = if self.intensity() == other.intensity() {
- attrs
- } else {
- attrs.intensity(match self.intensity() {
- 0 => crate::term::Intensity::Normal,
- TEXT_MODE_BOLD => crate::term::Intensity::Bold,
- TEXT_MODE_DIM => crate::term::Intensity::Dim,
- _ => unreachable!(),
- })
- };
- let attrs = if self.italic() == other.italic() {
- attrs
- } else {
- attrs.italic(self.italic())
- };
- let attrs = if self.underline() == other.underline() {
- attrs
- } else {
- attrs.underline(self.underline())
- };
- let attrs = if self.inverse() == other.inverse() {
- attrs
- } else {
- attrs.inverse(self.inverse())
- };
-
- attrs.write_buf(contents);
- }
-}
diff --git a/vendor/vt100/src/callbacks.rs b/vendor/vt100/src/callbacks.rs
deleted file mode 100644
index cce6f6c0..00000000
--- a/vendor/vt100/src/callbacks.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-/// This trait is used by the parser to handle extra escape sequences that
-/// don't have an impact on the terminal screen directly.
-pub trait Callbacks {
- /// This callback is called when the terminal requests an audible bell
- /// (typically with `^G`).
- fn audible_bell(&mut self, _: &mut crate::Screen) {}
- /// This callback is called when the terminal requests a visual bell
- /// (typically with `\eg`).
- fn visual_bell(&mut self, _: &mut crate::Screen) {}
- /// This callback is called when the terminal requests a resize
- /// (typically with `\e[8;<rows>;<cols>t`).
- fn resize(&mut self, _: &mut crate::Screen, _request: (u16, u16)) {}
- /// This callback is called when the terminal requests the window title
- /// to be set (typically with `\e]1;<icon_name>\a`)
- fn set_window_icon_name(
- &mut self,
- _: &mut crate::Screen,
- _icon_name: &[u8],
- ) {
- }
- /// This callback is called when the terminal requests the window title
- /// to be set (typically with `\e]2;<title>\a`)
- fn set_window_title(&mut self, _: &mut crate::Screen, _title: &[u8]) {}
- /// This callback is called when the terminal requests data to be copied
- /// to the system clipboard (typically with `\e]52;<ty>;<data>\a`). Note
- /// that `data` will be encoded as base64.
- fn copy_to_clipboard(
- &mut self,
- _: &mut crate::Screen,
- _ty: &[u8],
- _data: &[u8],
- ) {
- }
- /// This callback is called when the terminal requests data to be pasted
- /// from the system clipboard (typically with `\e]52;<ty>;?\a`).
- fn paste_from_clipboard(&mut self, _: &mut crate::Screen, _ty: &[u8]) {}
- /// This callback is called when the terminal receives an escape sequence
- /// which is otherwise not implemented.
- fn unhandled_char(&mut self, _: &mut crate::Screen, _c: char) {}
- /// This callback is called when the terminal receives a control
- /// character which is otherwise not implemented.
- fn unhandled_control(&mut self, _: &mut crate::Screen, _b: u8) {}
- /// This callback is called when the terminal receives an escape sequence
- /// which is otherwise not implemented.
- fn unhandled_escape(
- &mut self,
- _: &mut crate::Screen,
- _i1: Option<u8>,
- _i2: Option<u8>,
- _b: u8,
- ) {
- }
- /// This callback is called when the terminal receives a CSI sequence
- /// (`\e[`) which is otherwise not implemented.
- fn unhandled_csi(
- &mut self,
- _: &mut crate::Screen,
- _i1: Option<u8>,
- _i2: Option<u8>,
- _params: &[&[u16]],
- _c: char,
- ) {
- }
- /// This callback is called when the terminal receives a OSC sequence
- /// (`\e]`) which is otherwise not implemented.
- fn unhandled_osc(&mut self, _: &mut crate::Screen, _params: &[&[u8]]) {}
-}
-
-impl Callbacks for () {}
diff --git a/vendor/vt100/src/cell.rs b/vendor/vt100/src/cell.rs
deleted file mode 100644
index c000cf30..00000000
--- a/vendor/vt100/src/cell.rs
+++ /dev/null
@@ -1,179 +0,0 @@
-use unicode_width::UnicodeWidthChar as _;
-
-// chosen to make the size of the cell struct 32 bytes
-const CONTENT_BYTES: usize = 22;
-
-const IS_WIDE: u8 = 0b1000_0000;
-const IS_WIDE_CONTINUATION: u8 = 0b0100_0000;
-const LEN_BITS: u8 = 0b0001_1111;
-
-/// Represents a single terminal cell.
-#[derive(Clone, Debug, Eq)]
-pub struct Cell {
- contents: [u8; CONTENT_BYTES],
- len: u8,
- attrs: crate::attrs::Attrs,
-}
-const _: () = assert!(std::mem::size_of::<Cell>() == 32);
-
-impl PartialEq<Self> for Cell {
- fn eq(&self, other: &Self) -> bool {
- if self.len != other.len {
- return false;
- }
- if self.attrs != other.attrs {
- return false;
- }
- let len = self.len();
- self.contents[..len] == other.contents[..len]
- }
-}
-
-impl Cell {
- pub(crate) fn new() -> Self {
- Self {
- contents: Default::default(),
- len: 0,
- attrs: crate::attrs::Attrs::default(),
- }
- }
-
- fn len(&self) -> usize {
- usize::from(self.len & LEN_BITS)
- }
-
- pub(crate) fn set(&mut self, c: char, a: crate::attrs::Attrs) {
- self.len = 0;
- self.append_char(0, c);
- // strings in this context should always be an arbitrary character
- // followed by zero or more zero-width characters, so we should only
- // have to look at the first character
- self.set_wide(c.width().unwrap_or(1) > 1);
- self.attrs = a;
- }
-
- pub(crate) fn append(&mut self, c: char) {
- let len = self.len();
- if len >= CONTENT_BYTES - 4 {
- return;
- }
- if len == 0 {
- self.contents[0] = b' ';
- self.len += 1;
- }
-
- // we already checked that we have space for another codepoint
- self.append_char(self.len(), c);
- }
-
- // Writes bytes representing c at start
- // Requires caller to verify start <= CODEPOINTS_IN_CELL * 4
- fn append_char(&mut self, start: usize, c: char) {
- c.encode_utf8(&mut self.contents[start..]);
- self.len += u8::try_from(c.len_utf8()).unwrap();
- }
-
- pub(crate) fn clear(&mut self, attrs: crate::attrs::Attrs) {
- self.len = 0;
- self.attrs = attrs;
- }
-
- /// Returns the text contents of the cell.
- ///
- /// Can include multiple unicode characters if combining characters are
- /// used, but will contain at most one character with a non-zero character
- /// width.
- // Since contents has been constructed by appending chars encoded as UTF-8 it will be valid UTF-8
- #[allow(clippy::missing_panics_doc)]
- #[must_use]
- pub fn contents(&self) -> &str {
- std::str::from_utf8(&self.contents[..self.len()]).unwrap()
- }
-
- /// Returns whether the cell contains any text data.
- #[must_use]
- pub fn has_contents(&self) -> bool {
- self.len() > 0
- }
-
- /// Returns whether the text data in the cell represents a wide character.
- #[must_use]
- pub fn is_wide(&self) -> bool {
- self.len & IS_WIDE != 0
- }
-
- /// Returns whether the cell contains the second half of a wide character
- /// (in other words, whether the previous cell in the row contains a wide
- /// character)
- #[must_use]
- pub fn is_wide_continuation(&self) -> bool {
- self.len & IS_WIDE_CONTINUATION != 0
- }
-
- fn set_wide(&mut self, wide: bool) {
- if wide {
- self.len |= IS_WIDE;
- } else {
- self.len &= !IS_WIDE;
- }
- }
-
- pub(crate) fn set_wide_continuation(&mut self, wide: bool) {
- if wide {
- self.len |= IS_WIDE_CONTINUATION;
- } else {
- self.len &= !IS_WIDE_CONTINUATION;
- }
- }
-
- pub(crate) fn attrs(&self) -> &crate::attrs::Attrs {
- &self.attrs
- }
-
- /// Returns the foreground color of the cell.
- #[must_use]
- pub fn fgcolor(&self) -> crate::Color {
- self.attrs.fgcolor
- }
-
- /// Returns the background color of the cell.
- #[must_use]
- pub fn bgcolor(&self) -> crate::Color {
- self.attrs.bgcolor
- }
-
- /// Returns whether the cell should be rendered with the bold text
- /// attribute.
- #[must_use]
- pub fn bold(&self) -> bool {
- self.attrs.bold()
- }
-
- /// Returns whether the cell should be rendered with the dim text
- /// attribute.
- #[must_use]
- pub fn dim(&self) -> bool {
- self.attrs.dim()
- }
-
- /// Returns whether the cell should be rendered with the italic text
- /// attribute.
- #[must_use]
- pub fn italic(&self) -> bool {
- self.attrs.italic()
- }
-
- /// Returns whether the cell should be rendered with the underlined text
- /// attribute.
- #[must_use]
- pub fn underline(&self) -> bool {
- self.attrs.underline()
- }
-
- /// Returns whether the cell should be rendered with the inverse text
- /// attribute.
- #[must_use]
- pub fn inverse(&self) -> bool {
- self.attrs.inverse()
- }
-}
diff --git a/vendor/vt100/src/grid.rs b/vendor/vt100/src/grid.rs
deleted file mode 100644
index f8958579..00000000
--- a/vendor/vt100/src/grid.rs
+++ /dev/null
@@ -1,740 +0,0 @@
-use crate::term::BufWrite as _;
-
-#[derive(Clone, Debug)]
-pub struct Grid {
- size: Size,
- pos: Pos,
- saved_pos: Pos,
- rows: Vec<crate::row::Row>,
- scroll_top: u16,
- scroll_bottom: u16,
- origin_mode: bool,
- saved_origin_mode: bool,
- scrollback: std::collections::VecDeque<crate::row::Row>,
- scrollback_len: usize,
- scrollback_offset: usize,
-}
-
-impl Grid {
- pub fn new(size: Size, scrollback_len: usize) -> Self {
- Self {
- size,
- pos: Pos::default(),
- saved_pos: Pos::default(),
- rows: vec![],
- scroll_top: 0,
- scroll_bottom: size.rows - 1,
- origin_mode: false,
- saved_origin_mode: false,
- scrollback: std::collections::VecDeque::new(),
- scrollback_len,
- scrollback_offset: 0,
- }
- }
-
- pub fn allocate_rows(&mut self) {
- if self.rows.is_empty() {
- self.rows.extend(
- std::iter::repeat_with(|| {
- crate::row::Row::new(self.size.cols)
- })
- .take(usize::from(self.size.rows)),
- );
- }
- }
-
- fn new_row(&self) -> crate::row::Row {
- crate::row::Row::new(self.size.cols)
- }
-
- pub fn clear(&mut self) {
- self.pos = Pos::default();
- self.saved_pos = Pos::default();
- for row in self.drawing_rows_mut() {
- row.clear(crate::attrs::Attrs::default());
- }
- self.scroll_top = 0;
- self.scroll_bottom = self.size.rows - 1;
- self.origin_mode = false;
- self.saved_origin_mode = false;
- }
-
- pub fn size(&self) -> Size {
- self.size
- }
-
- pub fn set_size(&mut self, size: Size) {
- if size.cols != self.size.cols {
- for row in &mut self.rows {
- row.wrap(false);
- }
- }
-
- if self.scroll_bottom == self.size.rows - 1 {
- self.scroll_bottom = size.rows - 1;
- }
-
- self.size = size;
- for row in &mut self.rows {
- row.resize(size.cols, crate::Cell::new());
- }
- self.rows.resize(usize::from(size.rows), self.new_row());
-
- if self.scroll_bottom >= size.rows {
- self.scroll_bottom = size.rows - 1;
- }
- if self.scroll_bottom < self.scroll_top {
- self.scroll_top = 0;
- }
-
- self.row_clamp_top(false);
- self.row_clamp_bottom(false);
- self.col_clamp();
-
- if self.saved_pos.row > self.size.rows - 1 {
- self.saved_pos.row = self.size.rows - 1;
- }
- if self.saved_pos.col > self.size.cols - 1 {
- self.saved_pos.col = self.size.cols - 1;
- }
- }
-
- pub fn pos(&self) -> Pos {
- self.pos
- }
-
- pub fn set_pos(&mut self, mut pos: Pos) {
- if self.origin_mode {
- pos.row = pos.row.saturating_add(self.scroll_top);
- }
- self.pos = pos;
- self.row_clamp_top(self.origin_mode);
- self.row_clamp_bottom(self.origin_mode);
- self.col_clamp();
- }
-
- pub fn save_cursor(&mut self) {
- self.saved_pos = self.pos;
- self.saved_origin_mode = self.origin_mode;
- }
-
- pub fn restore_cursor(&mut self) {
- self.pos = self.saved_pos;
- self.origin_mode = self.saved_origin_mode;
- }
-
- pub fn visible_rows(&self) -> impl Iterator<Item = &crate::row::Row> {
- let scrollback_len = self.scrollback.len();
- let rows_len = self.rows.len();
- self.scrollback
- .iter()
- .skip(scrollback_len - self.scrollback_offset)
- // when scrollback_offset > rows_len (e.g. rows = 3,
- // scrollback_len = 10, offset = 9) the skip(10 - 9)
- // will take 9 rows instead of 3. we need to set
- // the upper bound to rows_len (e.g. 3)
- .take(rows_len)
- // same for rows_len - scrollback_offset (e.g. 3 - 9).
- // it'll panic with overflow. we have to saturate the subtraction.
- .chain(
- self.rows
- .iter()
- .take(rows_len.saturating_sub(self.scrollback_offset)),
- )
- }
-
- pub fn drawing_rows(&self) -> impl Iterator<Item = &crate::row::Row> {
- self.rows.iter()
- }
-
- pub fn drawing_rows_mut(
- &mut self,
- ) -> impl Iterator<Item = &mut crate::row::Row> {
- self.rows.iter_mut()
- }
-
- pub fn visible_row(&self, row: u16) -> Option<&crate::row::Row> {
- self.visible_rows().nth(usize::from(row))
- }
-
- pub fn drawing_row(&self, row: u16) -> Option<&crate::row::Row> {
- self.drawing_rows().nth(usize::from(row))
- }
-
- pub fn drawing_row_mut(
- &mut self,
- row: u16,
- ) -> Option<&mut crate::row::Row> {
- self.drawing_rows_mut().nth(usize::from(row))
- }
-
- pub fn current_row_mut(&mut self) -> &mut crate::row::Row {
- self.drawing_row_mut(self.pos.row)
- // we assume self.pos.row is always valid
- .unwrap()
- }
-
- pub fn visible_cell(&self, pos: Pos) -> Option<&crate::Cell> {
- self.visible_row(pos.row).and_then(|r| r.get(pos.col))
- }
-
- pub fn drawing_cell(&self, pos: Pos) -> Option<&crate::Cell> {
- self.drawing_row(pos.row).and_then(|r| r.get(pos.col))
- }
-
- pub fn drawing_cell_mut(&mut self, pos: Pos) -> Option<&mut crate::Cell> {
- self.drawing_row_mut(pos.row)
- .and_then(|r| r.get_mut(pos.col))
- }
-
- pub fn scrollback_len(&self) -> usize {
- self.scrollback_len
- }
-
- pub fn scrollback(&self) -> usize {
- self.scrollback_offset
- }
-
- pub fn set_scrollback(&mut self, rows: usize) {
- self.scrollback_offset = rows.min(self.scrollback.len());
- }
-
- pub fn write_contents(&self, contents: &mut String) {
- let mut wrapping = false;
- for row in self.visible_rows() {
- row.write_contents(contents, 0, self.size.cols, wrapping);
- if !row.wrapped() {
- contents.push('\n');
- }
- wrapping = row.wrapped();
- }
-
- while contents.ends_with('\n') {
- contents.truncate(contents.len() - 1);
- }
- }
-
- pub fn write_contents_formatted(
- &self,
- contents: &mut Vec<u8>,
- ) -> crate::attrs::Attrs {
- crate::term::ClearAttrs.write_buf(contents);
- crate::term::ClearScreen.write_buf(contents);
-
- let mut prev_attrs = crate::attrs::Attrs::default();
- let mut prev_pos = Pos::default();
- let mut wrapping = false;
- for (i, row) in self.visible_rows().enumerate() {
- // we limit the number of cols to a u16 (see Size), so
- // visible_rows() can never return more rows than will fit
- let i = i.try_into().unwrap();
- let (new_pos, new_attrs) = row.write_contents_formatted(
- contents,
- 0,
- self.size.cols,
- i,
- wrapping,
- Some(prev_pos),
- Some(prev_attrs),
- );
- prev_pos = new_pos;
- prev_attrs = new_attrs;
- wrapping = row.wrapped();
- }
-
- self.write_cursor_position_formatted(
- contents,
- Some(prev_pos),
- Some(prev_attrs),
- );
-
- prev_attrs
- }
-
- pub fn write_contents_diff(
- &self,
- contents: &mut Vec<u8>,
- prev: &Self,
- mut prev_attrs: crate::attrs::Attrs,
- ) -> crate::attrs::Attrs {
- let mut prev_pos = prev.pos;
- let mut wrapping = false;
- let mut prev_wrapping = false;
- for (i, (row, prev_row)) in
- self.visible_rows().zip(prev.visible_rows()).enumerate()
- {
- // we limit the number of cols to a u16 (see Size), so
- // visible_rows() can never return more rows than will fit
- let i = i.try_into().unwrap();
- let (new_pos, new_attrs) = row.write_contents_diff(
- contents,
- prev_row,
- 0,
- self.size.cols,
- i,
- wrapping,
- prev_wrapping,
- prev_pos,
- prev_attrs,
- );
- prev_pos = new_pos;
- prev_attrs = new_attrs;
- wrapping = row.wrapped();
- prev_wrapping = prev_row.wrapped();
- }
-
- self.write_cursor_position_formatted(
- contents,
- Some(prev_pos),
- Some(prev_attrs),
- );
-
- prev_attrs
- }
-
- pub fn write_cursor_position_formatted(
- &self,
- contents: &mut Vec<u8>,
- prev_pos: Option<Pos>,
- prev_attrs: Option<crate::attrs::Attrs>,
- ) {
- let prev_attrs = prev_attrs.unwrap_or_default();
- // writing a character to the last column of a row doesn't wrap the
- // cursor immediately - it waits until the next character is actually
- // drawn. it is only possible for the cursor to have this kind of
- // position after drawing a character though, so if we end in this
- // position, we need to redraw the character at the end of the row.
- if prev_pos != Some(self.pos) && self.pos.col >= self.size.cols {
- let mut pos = Pos {
- row: self.pos.row,
- col: self.size.cols - 1,
- };
- if self
- .drawing_cell(pos)
- // we assume self.pos.row is always valid, and self.size.cols
- // - 1 is always a valid column
- .unwrap()
- .is_wide_continuation()
- {
- pos.col = self.size.cols - 2;
- }
- let cell =
- // we assume self.pos.row is always valid, and self.size.cols
- // - 2 must be a valid column because self.size.cols - 1 is
- // always valid and we just checked that the cell at
- // self.size.cols - 1 is a wide continuation character, which
- // means that the first half of the wide character must be
- // before it
- self.drawing_cell(pos).unwrap();
- if cell.has_contents() {
- if let Some(prev_pos) = prev_pos {
- crate::term::MoveFromTo::new(prev_pos, pos)
- .write_buf(contents);
- } else {
- crate::term::MoveTo::new(pos).write_buf(contents);
- }
- cell.attrs().write_escape_code_diff(contents, &prev_attrs);
- contents.extend(cell.contents().as_bytes());
- prev_attrs.write_escape_code_diff(contents, cell.attrs());
- } else {
- // if the cell doesn't have contents, we can't have gotten
- // here by drawing a character in the last column. this means
- // that as far as i'm aware, we have to have reached here from
- // a newline when we were already after the end of an earlier
- // row. in the case where we are already after the end of an
- // earlier row, we can just write a few newlines, otherwise we
- // also need to do the same as above to get ourselves to after
- // the end of a row.
- let mut found = false;
- for i in (0..self.pos.row).rev() {
- pos.row = i;
- pos.col = self.size.cols - 1;
- if self
- .drawing_cell(pos)
- // i is always less than self.pos.row, which we assume
- // to be always valid, so it must also be valid.
- // self.size.cols - 1 is always a valid col.
- .unwrap()
- .is_wide_continuation()
- {
- pos.col = self.size.cols - 2;
- }
- let cell = self
- .drawing_cell(pos)
- // i is always less than self.pos.row, which we assume
- // to be always valid, so it must also be valid.
- // self.size.cols - 2 is valid because self.size.cols
- // - 1 is always valid, and col gets set to
- // self.size.cols - 2 when the cell at self.size.cols
- // - 1 is a wide continuation character, meaning that
- // the first half of the wide character must be before
- // it
- .unwrap();
- if cell.has_contents() {
- if let Some(prev_pos) = prev_pos {
- if prev_pos.row != i
- || prev_pos.col < self.size.cols
- {
- crate::term::MoveFromTo::new(prev_pos, pos)
- .write_buf(contents);
- cell.attrs().write_escape_code_diff(
- contents,
- &prev_attrs,
- );
- contents.extend(cell.contents().as_bytes());
- prev_attrs.write_escape_code_diff(
- contents,
- cell.attrs(),
- );
- }
- } else {
- crate::term::MoveTo::new(pos).write_buf(contents);
- cell.attrs().write_escape_code_diff(
- contents,
- &prev_attrs,
- );
- contents.extend(cell.contents().as_bytes());
- prev_attrs.write_escape_code_diff(
- contents,
- cell.attrs(),
- );
- }
- contents.extend(
- "\n".repeat(usize::from(self.pos.row - i))
- .as_bytes(),
- );
- found = true;
- break;
- }
- }
-
- // this can happen if you get the cursor off the end of a row,
- // and then do something to clear the end of the current row
- // without moving the cursor (IL, DL, ED, EL, etc). we know
- // there can't be something in the last column because we
- // would have caught that above, so it should be safe to
- // overwrite it.
- if !found {
- pos = Pos {
- row: self.pos.row,
- col: self.size.cols - 1,
- };
- if let Some(prev_pos) = prev_pos {
- crate::term::MoveFromTo::new(prev_pos, pos)
- .write_buf(contents);
- } else {
- crate::term::MoveTo::new(pos).write_buf(contents);
- }
- contents.push(b' ');
- // we know that the cell has no contents, but it still may
- // have drawing attributes (background color, etc)
- let end_cell = self
- .drawing_cell(pos)
- // we assume self.pos.row is always valid, and
- // self.size.cols - 1 is always a valid column
- .unwrap();
- end_cell
- .attrs()
- .write_escape_code_diff(contents, &prev_attrs);
- crate::term::SaveCursor.write_buf(contents);
- crate::term::Backspace.write_buf(contents);
- crate::term::EraseChar::new(1).write_buf(contents);
- crate::term::RestoreCursor.write_buf(contents);
- prev_attrs
- .write_escape_code_diff(contents, end_cell.attrs());
- }
- }
- } else if let Some(prev_pos) = prev_pos {
- crate::term::MoveFromTo::new(prev_pos, self.pos)
- .write_buf(contents);
- } else {
- crate::term::MoveTo::new(self.pos).write_buf(contents);
- }
- }
-
- pub fn erase_all(&mut self, attrs: crate::attrs::Attrs) {
- for row in self.drawing_rows_mut() {
- row.clear(attrs);
- }
- }
-
- pub fn erase_all_forward(&mut self, attrs: crate::attrs::Attrs) {
- let pos = self.pos;
- for row in self.drawing_rows_mut().skip(usize::from(pos.row) + 1) {
- row.clear(attrs);
- }
-
- self.erase_row_forward(attrs);
- }
-
- pub fn erase_all_backward(&mut self, attrs: crate::attrs::Attrs) {
- let pos = self.pos;
- for row in self.drawing_rows_mut().take(usize::from(pos.row)) {
- row.clear(attrs);
- }
-
- self.erase_row_backward(attrs);
- }
-
- pub fn erase_row(&mut self, attrs: crate::attrs::Attrs) {
- self.current_row_mut().clear(attrs);
- }
-
- pub fn erase_row_forward(&mut self, attrs: crate::attrs::Attrs) {
- let size = self.size;
- let pos = self.pos;
- let row = self.current_row_mut();
- for col in pos.col..size.cols {
- row.erase(col, attrs);
- }
- }
-
- pub fn erase_row_backward(&mut self, attrs: crate::attrs::Attrs) {
- let size = self.size;
- let pos = self.pos;
- let row = self.current_row_mut();
- for col in 0..=pos.col.min(size.cols - 1) {
- row.erase(col, attrs);
- }
- }
-
- pub fn insert_cells(&mut self, count: u16) {
- let size = self.size;
- let pos = self.pos;
- let wide = pos.col < size.cols
- && self
- .drawing_cell(pos)
- // we assume self.pos.row is always valid, and we know we are
- // not off the end of a row because we just checked pos.col <
- // size.cols
- .unwrap()
- .is_wide_continuation();
- let row = self.current_row_mut();
- for _ in 0..count {
- if wide {
- row.get_mut(pos.col).unwrap().set_wide_continuation(false);
- }
- row.insert(pos.col, crate::Cell::new());
- if wide {
- row.get_mut(pos.col).unwrap().set_wide_continuation(true);
- }
- }
- row.truncate(size.cols);
- }
-
- pub fn delete_cells(&mut self, count: u16) {
- let size = self.size;
- let pos = self.pos;
- let row = self.current_row_mut();
- for _ in 0..(count.min(size.cols - pos.col)) {
- row.remove(pos.col);
- }
- row.resize(size.cols, crate::Cell::new());
- }
-
- pub fn erase_cells(&mut self, count: u16, attrs: crate::attrs::Attrs) {
- let size = self.size;
- let pos = self.pos;
- let row = self.current_row_mut();
- for col in pos.col..((pos.col.saturating_add(count)).min(size.cols)) {
- row.erase(col, attrs);
- }
- }
-
- pub fn insert_lines(&mut self, count: u16) {
- for _ in 0..count {
- self.rows.remove(usize::from(self.scroll_bottom));
- self.rows.insert(usize::from(self.pos.row), self.new_row());
- // self.scroll_bottom is maintained to always be a valid row
- self.rows[usize::from(self.scroll_bottom)].wrap(false);
- }
- }
-
- pub fn delete_lines(&mut self, count: u16) {
- for _ in 0..(count.min(self.size.rows - self.pos.row)) {
- self.rows
- .insert(usize::from(self.scroll_bottom) + 1, self.new_row());
- self.rows.remove(usize::from(self.pos.row));
- }
- }
-
- pub fn scroll_up(&mut self, count: u16) {
- for _ in 0..(count.min(self.size.rows - self.scroll_top)) {
- self.rows
- .insert(usize::from(self.scroll_bottom) + 1, self.new_row());
- let removed = self.rows.remove(usize::from(self.scroll_top));
- // Top-anchored scroll regions still represent terminal output moving
- // off the visible transcript and should feed host scrollback.
- if self.scrollback_len > 0 && self.scroll_top == 0 {
- self.scrollback.push_back(removed);
- while self.scrollback.len() > self.scrollback_len {
- self.scrollback.pop_front();
- }
- if self.scrollback_offset > 0 {
- self.scrollback_offset =
- self.scrollback.len().min(self.scrollback_offset + 1);
- }
- }
- }
- }
-
- pub fn scroll_down(&mut self, count: u16) {
- for _ in 0..count {
- self.rows.remove(usize::from(self.scroll_bottom));
- self.rows
- .insert(usize::from(self.scroll_top), self.new_row());
- // self.scroll_bottom is maintained to always be a valid row
- self.rows[usize::from(self.scroll_bottom)].wrap(false);
- }
- }
-
- pub fn set_scroll_region(&mut self, top: u16, bottom: u16) {
- let bottom = bottom.min(self.size().rows - 1);
- if top < bottom {
- self.scroll_top = top;
- self.scroll_bottom = bottom;
- } else {
- self.scroll_top = 0;
- self.scroll_bottom = self.size().rows - 1;
- }
- self.pos.row = self.scroll_top;
- self.pos.col = 0;
- }
-
- fn in_scroll_region(&self) -> bool {
- self.pos.row >= self.scroll_top && self.pos.row <= self.scroll_bottom
- }
-
- pub fn set_origin_mode(&mut self, mode: bool) {
- self.origin_mode = mode;
- self.set_pos(Pos { row: 0, col: 0 });
- }
-
- pub fn row_inc_clamp(&mut self, count: u16) {
- let in_scroll_region = self.in_scroll_region();
- self.pos.row = self.pos.row.saturating_add(count);
- self.row_clamp_bottom(in_scroll_region);
- }
-
- pub fn row_inc_scroll(&mut self, count: u16) -> u16 {
- let in_scroll_region = self.in_scroll_region();
- self.pos.row = self.pos.row.saturating_add(count);
- let lines = self.row_clamp_bottom(in_scroll_region);
- if in_scroll_region {
- self.scroll_up(lines);
- lines
- } else {
- 0
- }
- }
-
- pub fn row_dec_clamp(&mut self, count: u16) {
- let in_scroll_region = self.in_scroll_region();
- self.pos.row = self.pos.row.saturating_sub(count);
- self.row_clamp_top(in_scroll_region);
- }
-
- pub fn row_dec_scroll(&mut self, count: u16) {
- let in_scroll_region = self.in_scroll_region();
- // need to account for clamping by both row_clamp_top and by
- // saturating_sub
- let extra_lines = count.saturating_sub(self.pos.row);
- self.pos.row = self.pos.row.saturating_sub(count);
- let lines = self.row_clamp_top(in_scroll_region);
- self.scroll_down(lines + extra_lines);
- }
-
- pub fn row_set(&mut self, i: u16) {
- self.pos.row = i;
- self.row_clamp();
- }
-
- pub fn col_inc(&mut self, count: u16) {
- self.pos.col = self.pos.col.saturating_add(count);
- }
-
- pub fn col_inc_clamp(&mut self, count: u16) {
- self.pos.col = self.pos.col.saturating_add(count);
- self.col_clamp();
- }
-
- pub fn col_dec(&mut self, count: u16) {
- self.pos.col = self.pos.col.saturating_sub(count);
- }
-
- pub fn col_tab(&mut self) {
- self.pos.col -= self.pos.col % 8;
- self.pos.col += 8;
- self.col_clamp();
- }
-
- pub fn col_set(&mut self, i: u16) {
- self.pos.col = i;
- self.col_clamp();
- }
-
- pub fn col_wrap(&mut self, width: u16, wrap: bool) {
- if self.pos.col > self.size.cols - width {
- let mut prev_pos = self.pos;
- self.pos.col = 0;
- let scrolled = self.row_inc_scroll(1);
- prev_pos.row -= scrolled;
- let new_pos = self.pos;
- self.drawing_row_mut(prev_pos.row)
- // we assume self.pos.row is always valid, and so prev_pos.row
- // must be valid because it is always less than or equal to
- // self.pos.row
- .unwrap()
- .wrap(wrap && prev_pos.row + 1 == new_pos.row);
- }
- }
-
- fn row_clamp_top(&mut self, limit_to_scroll_region: bool) -> u16 {
- if limit_to_scroll_region && self.pos.row < self.scroll_top {
- let rows = self.scroll_top - self.pos.row;
- self.pos.row = self.scroll_top;
- rows
- } else {
- 0
- }
- }
-
- fn row_clamp_bottom(&mut self, limit_to_scroll_region: bool) -> u16 {
- let bottom = if limit_to_scroll_region {
- self.scroll_bottom
- } else {
- self.size.rows - 1
- };
- if self.pos.row > bottom {
- let rows = self.pos.row - bottom;
- self.pos.row = bottom;
- rows
- } else {
- 0
- }
- }
-
- fn row_clamp(&mut self) {
- if self.pos.row > self.size.rows - 1 {
- self.pos.row = self.size.rows - 1;
- }
- }
-
- fn col_clamp(&mut self) {
- if self.pos.col > self.size.cols - 1 {
- self.pos.col = self.size.cols - 1;
- }
- }
-}
-
-#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
-pub struct Size {
- pub rows: u16,
- pub cols: u16,
-}
-
-#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
-pub struct Pos {
- pub row: u16,
- pub col: u16,
-}
diff --git a/vendor/vt100/src/lib.rs b/vendor/vt100/src/lib.rs
deleted file mode 100644
index 19746a94..00000000
--- a/vendor/vt100/src/lib.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-//! This crate parses a terminal byte stream and provides an in-memory
-//! representation of the rendered contents.
-//!
-//! # Overview
-//!
-//! This is essentially the terminal parser component of a graphical terminal
-//! emulator pulled out into a separate crate. Although you can use this crate
-//! to build a graphical terminal emulator, it also contains functionality
-//! necessary for implementing terminal applications that want to run other
-//! terminal applications - programs like `screen` or `tmux` for example.
-//!
-//! # Synopsis
-//!
-//! ```
-//! let mut parser = vt100::Parser::new(24, 80, 0);
-//!
-//! let screen = parser.screen().clone();
-//! parser.process(b"this text is \x1b[31mRED\x1b[m");
-//! assert_eq!(
-//! parser.screen().cell(0, 13).unwrap().fgcolor(),
-//! vt100::Color::Idx(1),
-//! );
-//!
-//! let screen = parser.screen().clone();
-//! parser.process(b"\x1b[3D\x1b[32mGREEN");
-//! assert_eq!(
-//! parser.screen().contents_formatted(),
-//! &b"\x1b[?25h\x1b[m\x1b[H\x1b[Jthis text is \x1b[32mGREEN"[..],
-//! );
-//! assert_eq!(
-//! parser.screen().contents_diff(&screen),
-//! &b"\x1b[1;14H\x1b[32mGREEN"[..],
-//! );
-//! ```
-
-#![warn(missing_docs)]
-#![warn(clippy::cargo)]
-#![warn(clippy::pedantic)]
-#![warn(clippy::nursery)]
-#![warn(clippy::as_conversions)]
-#![warn(clippy::get_unwrap)]
-#![allow(clippy::cognitive_complexity)]
-#![allow(clippy::missing_const_for_fn)]
-#![allow(clippy::similar_names)]
-#![allow(clippy::struct_excessive_bools)]
-#![allow(clippy::too_many_arguments)]
-#![allow(clippy::too_many_lines)]
-#![allow(clippy::type_complexity)]
-
-mod attrs;
-mod callbacks;
-mod cell;
-mod grid;
-mod parser;
-mod perform;
-mod row;
-mod screen;
-mod term;
-
-pub use attrs::Color;
-pub use callbacks::Callbacks;
-pub use cell::Cell;
-pub use parser::Parser;
-pub use screen::{MouseProtocolEncoding, MouseProtocolMode, Screen};
diff --git a/vendor/vt100/src/parser.rs b/vendor/vt100/src/parser.rs
deleted file mode 100644
index c81495b8..00000000
--- a/vendor/vt100/src/parser.rs
+++ /dev/null
@@ -1,96 +0,0 @@
-/// A parser for terminal output which produces an in-memory representation of
-/// the terminal contents.
-pub struct Parser<CB: crate::callbacks::Callbacks = ()> {
- parser: vte::Parser,
- screen: crate::perform::WrappedScreen<CB>,
-}
-
-impl Parser {
- /// Creates a new terminal parser of the given size and with the given
- /// amount of scrollback.
- #[must_use]
- pub fn new(rows: u16, cols: u16, scrollback_len: usize) -> Self {
- Self {
- parser: vte::Parser::new(),
- screen: crate::perform::WrappedScreen::new(
- rows,
- cols,
- scrollback_len,
- ),
- }
- }
-}
-
-impl<CB: crate::callbacks::Callbacks> Parser<CB> {
- /// Creates a new terminal parser of the given size and with the given
- /// amount of scrollback. Terminal events will be reported via method
- /// calls on the provided [`Callbacks`](crate::callbacks::Callbacks)
- /// implementation.
- pub fn new_with_callbacks(
- rows: u16,
- cols: u16,
- scrollback_len: usize,
- callbacks: CB,
- ) -> Self {
- Self {
- parser: vte::Parser::new(),
- screen: crate::perform::WrappedScreen::new_with_callbacks(
- rows,
- cols,
- scrollback_len,
- callbacks,
- ),
- }
- }
-
- /// Processes the contents of the given byte string, and updates the
- /// in-memory terminal state.
- pub fn process(&mut self, bytes: &[u8]) {
- self.parser.advance(&mut self.screen, bytes);
- }
-
- /// Returns a reference to a [`Screen`](crate::Screen) object containing
- /// the terminal state.
- #[must_use]
- pub fn screen(&self) -> &crate::Screen {
- &self.screen.screen
- }
-
- /// Returns a mutable reference to a [`Screen`](crate::Screen) object
- /// containing the terminal state.
- #[must_use]
- pub fn screen_mut(&mut self) -> &mut crate::Screen {
- &mut self.screen.screen
- }
-
- /// Returns a reference to the [`Callbacks`](crate::callbacks::Callbacks)
- /// state object passed into the constructor.
- pub fn callbacks(&self) -> &CB {
- &self.screen.callbacks
- }
-
- /// Returns a mutable reference to the
- /// [`Callbacks`](crate::callbacks::Callbacks) state object passed into
- /// the constructor.
- pub fn callbacks_mut(&mut self) -> &mut CB {
- &mut self.screen.callbacks
- }
-}
-
-impl Default for Parser {
- /// Returns a parser with dimensions 80x24 and no scrollback.
- fn default() -> Self {
- Self::new(24, 80, 0)
- }
-}
-
-impl std::io::Write for Parser {
- fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
- self.process(buf);
- Ok(buf.len())
- }
-
- fn flush(&mut self) -> std::io::Result<()> {
- Ok(())
- }
-}
diff --git a/vendor/vt100/src/perform.rs b/vendor/vt100/src/perform.rs
deleted file mode 100644
index e36cfd83..00000000
--- a/vendor/vt100/src/perform.rs
+++ /dev/null
@@ -1,277 +0,0 @@
-const BASE64: &[u8] =
- b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
-const CLIPBOARD_SELECTOR: &[u8] = b"cpqs01234567";
-
-pub struct WrappedScreen<CB: crate::callbacks::Callbacks = ()> {
- pub screen: crate::screen::Screen,
- pub callbacks: CB,
-}
-
-impl WrappedScreen<()> {
- pub fn new(rows: u16, cols: u16, scrollback_len: usize) -> Self {
- Self::new_with_callbacks(rows, cols, scrollback_len, ())
- }
-}
-
-impl<CB: crate::callbacks::Callbacks> WrappedScreen<CB> {
- pub fn new_with_callbacks(
- rows: u16,
- cols: u16,
- scrollback_len: usize,
- callbacks: CB,
- ) -> Self {
- Self {
- screen: crate::screen::Screen::new(
- crate::grid::Size { rows, cols },
- scrollback_len,
- ),
- callbacks,
- }
- }
-}
-
-impl<CB: crate::callbacks::Callbacks> vte::Perform for WrappedScreen<CB> {
- fn print(&mut self, c: char) {
- if c == '\u{fffd}' || ('\u{80}'..'\u{a0}').contains(&c) {
- self.callbacks.unhandled_char(&mut self.screen, c);
- } else {
- self.screen.text(c);
- }
- }
-
- fn execute(&mut self, b: u8) {
- match b {
- 7 => self.callbacks.audible_bell(&mut self.screen),
- 8 => self.screen.bs(),
- 9 => self.screen.tab(),
- 10 => self.screen.lf(),
- 11 => self.screen.vt(),
- 12 => self.screen.ff(),
- 13 => self.screen.cr(),
- // we don't implement shift in/out alternate character sets, but
- // it shouldn't count as an "error"
- 14 | 15 => {}
- _ => self.callbacks.unhandled_control(&mut self.screen, b),
- }
- }
-
- fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, b: u8) {
- if let Some(i) = intermediates.first() {
- self.callbacks.unhandled_escape(
- &mut self.screen,
- Some(*i),
- intermediates.get(1).copied(),
- b,
- );
- } else {
- match b {
- b'7' => self.screen.decsc(),
- b'8' => self.screen.decrc(),
- b'=' => self.screen.deckpam(),
- b'>' => self.screen.deckpnm(),
- b'M' => self.screen.ri(),
- b'c' => self.screen.ris(),
- b'g' => self.callbacks.visual_bell(&mut self.screen),
- _ => {
- self.callbacks.unhandled_escape(
- &mut self.screen,
- None,
- None,
- b,
- );
- }
- }
- }
- }
-
- fn csi_dispatch(
- &mut self,
- params: &vte::Params,
- intermediates: &[u8],
- _ignore: bool,
- c: char,
- ) {
- let unhandled = |screen: &mut crate::screen::Screen| {
- self.callbacks.unhandled_csi(
- screen,
- intermediates.first().copied(),
- intermediates.get(1).copied(),
- &params.iter().collect::<Vec<_>>(),
- c,
- );
- };
- match intermediates.first() {
- None => match c {
- '@' => self.screen.ich(canonicalize_params_1(params, 1)),
- 'A' => self.screen.cuu(canonicalize_params_1(params, 1)),
- 'B' => self.screen.cud(canonicalize_params_1(params, 1)),
- 'C' => self.screen.cuf(canonicalize_params_1(params, 1)),
- 'D' => self.screen.cub(canonicalize_params_1(params, 1)),
- 'E' => self.screen.cnl(canonicalize_params_1(params, 1)),
- 'F' => self.screen.cpl(canonicalize_params_1(params, 1)),
- 'G' => self.screen.cha(canonicalize_params_1(params, 1)),
- 'H' => self.screen.cup(canonicalize_params_2(params, 1, 1)),
- 'J' => self
- .screen
- .ed(canonicalize_params_1(params, 0), unhandled),
- 'K' => self
- .screen
- .el(canonicalize_params_1(params, 0), unhandled),
- 'L' => self.screen.il(canonicalize_params_1(params, 1)),
- 'M' => self.screen.dl(canonicalize_params_1(params, 1)),
- 'P' => self.screen.dch(canonicalize_params_1(params, 1)),
- 'S' => self.screen.su(canonicalize_params_1(params, 1)),
- 'T' => self.screen.sd(canonicalize_params_1(params, 1)),
- 'X' => self.screen.ech(canonicalize_params_1(params, 1)),
- 'd' => self.screen.vpa(canonicalize_params_1(params, 1)),
- 'm' => self.screen.sgr(params, unhandled),
- 'r' => self.screen.decstbm(canonicalize_params_decstbm(
- params,
- self.screen.grid().size(),
- )),
- 't' => {
- let mut params_iter = params.iter();
- let op =
- params_iter.next().and_then(|x| x.first().copied());
- if op == Some(8) {
- let (screen_rows, screen_cols) = self.screen.size();
- let rows =
- params_iter.next().map_or(screen_rows, |x| {
- *x.first().unwrap_or(&screen_rows)
- });
- let cols =
- params_iter.next().map_or(screen_cols, |x| {
- *x.first().unwrap_or(&screen_cols)
- });
- self.callbacks.resize(&mut self.screen, (rows, cols));
- } else {
- self.callbacks.unhandled_csi(
- &mut self.screen,
- None,
- None,
- &params.iter().collect::<Vec<_>>(),
- c,
- );
- }
- }
- _ => {
- self.callbacks.unhandled_csi(
- &mut self.screen,
- None,
- None,
- &params.iter().collect::<Vec<_>>(),
- c,
- );
- }
- },
- Some(b'?') => match c {
- 'J' => self
- .screen
- .decsed(canonicalize_params_1(params, 0), unhandled),
- 'K' => self
- .screen
- .decsel(canonicalize_params_1(params, 0), unhandled),
- 'h' => self.screen.decset(params, unhandled),
- 'l' => self.screen.decrst(params, unhandled),
- _ => {
- self.callbacks.unhandled_csi(
- &mut self.screen,
- Some(b'?'),
- intermediates.get(1).copied(),
- &params.iter().collect::<Vec<_>>(),
- c,
- );
- }
- },
- Some(i) => {
- self.callbacks.unhandled_csi(
- &mut self.screen,
- Some(*i),
- intermediates.get(1).copied(),
- &params.iter().collect::<Vec<_>>(),
- c,
- );
- }
- }
- }
-
- fn osc_dispatch(&mut self, params: &[&[u8]], _bel_terminated: bool) {
- match params {
- [b"0", s] => {
- self.callbacks.set_window_icon_name(&mut self.screen, s);
- self.callbacks.set_window_title(&mut self.screen, s);
- }
- [b"1", s] => {
- self.callbacks.set_window_icon_name(&mut self.screen, s);
- }
- [b"2", s] => {
- self.callbacks.set_window_title(&mut self.screen, s);
- }
- [b"52", ty, data] => {
- match (
- ty.iter().all(|c| CLIPBOARD_SELECTOR.contains(c)),
- *data,
- ) {
- (true, b"?") => {
- self.callbacks
- .paste_from_clipboard(&mut self.screen, ty);
- }
- (true, data)
- if data.iter().all(|c| BASE64.contains(c)) =>
- {
- self.callbacks.copy_to_clipboard(
- &mut self.screen,
- ty,
- data,
- );
- }
- _ => {
- self.callbacks
- .unhandled_osc(&mut self.screen, params);
- }
- }
- }
- _ => {
- self.callbacks.unhandled_osc(&mut self.screen, params);
- }
- }
- }
-}
-
-fn canonicalize_params_1(params: &vte::Params, default: u16) -> u16 {
- let first = params.iter().next().map_or(0, |x| *x.first().unwrap_or(&0));
- if first == 0 {
- default
- } else {
- first
- }
-}
-
-fn canonicalize_params_2(
- params: &vte::Params,
- default1: u16,
- default2: u16,
-) -> (u16, u16) {
- let mut iter = params.iter();
- let first = iter.next().map_or(0, |x| *x.first().unwrap_or(&0));
- let first = if first == 0 { default1 } else { first };
-
- let second = iter.next().map_or(0, |x| *x.first().unwrap_or(&0));
- let second = if second == 0 { default2 } else { second };
-
- (first, second)
-}
-
-fn canonicalize_params_decstbm(
- params: &vte::Params,
- size: crate::grid::Size,
-) -> (u16, u16) {
- let mut iter = params.iter();
- let top = iter.next().map_or(0, |x| *x.first().unwrap_or(&0));
- let top = if top == 0 { 1 } else { top };
-
- let bottom = iter.next().map_or(0, |x| *x.first().unwrap_or(&0));
- let bottom = if bottom == 0 { size.rows } else { bottom };
-
- (top, bottom)
-}
diff --git a/vendor/vt100/src/row.rs b/vendor/vt100/src/row.rs
deleted file mode 100644
index 4ae8d868..00000000
--- a/vendor/vt100/src/row.rs
+++ /dev/null
@@ -1,474 +0,0 @@
-use crate::term::BufWrite as _;
-
-#[derive(Clone, Debug)]
-pub struct Row {
- cells: Vec<crate::Cell>,
- wrapped: bool,
-}
-
-impl Row {
- pub fn new(cols: u16) -> Self {
- Self {
- cells: vec![crate::Cell::new(); usize::from(cols)],
- wrapped: false,
- }
- }
-
- fn cols(&self) -> u16 {
- self.cells
- .len()
- .try_into()
- // we limit the number of cols to a u16 (see Size)
- .unwrap()
- }
-
- pub fn clear(&mut self, attrs: crate::attrs::Attrs) {
- for cell in &mut self.cells {
- cell.clear(attrs);
- }
- self.wrapped = false;
- }
-
- fn cells(&self) -> impl Iterator<Item = &crate::Cell> {
- self.cells.iter()
- }
-
- pub fn get(&self, col: u16) -> Option<&crate::Cell> {
- self.cells.get(usize::from(col))
- }
-
- pub fn get_mut(&mut self, col: u16) -> Option<&mut crate::Cell> {
- self.cells.get_mut(usize::from(col))
- }
-
- pub fn insert(&mut self, i: u16, cell: crate::Cell) {
- self.cells.insert(usize::from(i), cell);
- self.wrapped = false;
- }
-
- pub fn remove(&mut self, i: u16) {
- self.clear_wide(i);
- self.cells.remove(usize::from(i));
- self.wrapped = false;
- }
-
- pub fn erase(&mut self, i: u16, attrs: crate::attrs::Attrs) {
- let wide = self.cells[usize::from(i)].is_wide();
- self.clear_wide(i);
- self.cells[usize::from(i)].clear(attrs);
- if i == self.cols() - if wide { 2 } else { 1 } {
- self.wrapped = false;
- }
- }
-
- pub fn truncate(&mut self, len: u16) {
- self.cells.truncate(usize::from(len));
- self.wrapped = false;
- let last_cell = &mut self.cells[usize::from(len) - 1];
- if last_cell.is_wide() {
- last_cell.clear(*last_cell.attrs());
- }
- }
-
- pub fn resize(&mut self, len: u16, cell: crate::Cell) {
- self.cells.resize(usize::from(len), cell);
- self.wrapped = false;
- }
-
- pub fn wrap(&mut self, wrap: bool) {
- self.wrapped = wrap;
- }
-
- pub fn wrapped(&self) -> bool {
- self.wrapped
- }
-
- pub fn clear_wide(&mut self, col: u16) {
- let cell = &self.cells[usize::from(col)];
- let other = if cell.is_wide() {
- &mut self.cells[usize::from(col + 1)]
- } else if cell.is_wide_continuation() {
- &mut self.cells[usize::from(col - 1)]
- } else {
- return;
- };
- other.clear(*other.attrs());
- }
-
- pub fn write_contents(
- &self,
- contents: &mut String,
- start: u16,
- width: u16,
- wrapping: bool,
- ) {
- let mut prev_was_wide = false;
-
- let mut prev_col = start;
- for (col, cell) in self
- .cells()
- .enumerate()
- .skip(usize::from(start))
- .take(usize::from(width))
- {
- if prev_was_wide {
- prev_was_wide = false;
- continue;
- }
- prev_was_wide = cell.is_wide();
-
- // we limit the number of cols to a u16 (see Size)
- let col: u16 = col.try_into().unwrap();
- if cell.has_contents() {
- for _ in 0..(col - prev_col) {
- contents.push(' ');
- }
- prev_col += col - prev_col;
-
- contents.push_str(cell.contents());
- prev_col += if cell.is_wide() { 2 } else { 1 };
- }
- }
- if prev_col == start && wrapping {
- contents.push('\n');
- }
- }
-
- pub fn write_contents_formatted(
- &self,
- contents: &mut Vec<u8>,
- start: u16,
- width: u16,
- row: u16,
- wrapping: bool,
- prev_pos: Option<crate::grid::Pos>,
- prev_attrs: Option<crate::attrs::Attrs>,
- ) -> (crate::grid::Pos, crate::attrs::Attrs) {
- let mut prev_was_wide = false;
- let default_cell = crate::Cell::new();
-
- let mut prev_pos = prev_pos.unwrap_or_else(|| {
- if wrapping {
- crate::grid::Pos {
- row: row - 1,
- col: self.cols(),
- }
- } else {
- crate::grid::Pos { row, col: start }
- }
- });
- let mut prev_attrs = prev_attrs.unwrap_or_default();
-
- let first_cell = &self.cells[usize::from(start)];
- if wrapping && first_cell == &default_cell {
- let default_attrs = default_cell.attrs();
- if &prev_attrs != default_attrs {
- default_attrs.write_escape_code_diff(contents, &prev_attrs);
- prev_attrs = *default_attrs;
- }
- contents.push(b' ');
- crate::term::Backspace.write_buf(contents);
- crate::term::EraseChar::new(1).write_buf(contents);
- prev_pos = crate::grid::Pos { row, col: 0 };
- }
-
- let mut erase: Option<(u16, &crate::attrs::Attrs)> = None;
- for (col, cell) in self
- .cells()
- .enumerate()
- .skip(usize::from(start))
- .take(usize::from(width))
- {
- if prev_was_wide {
- prev_was_wide = false;
- continue;
- }
- prev_was_wide = cell.is_wide();
-
- // we limit the number of cols to a u16 (see Size)
- let col: u16 = col.try_into().unwrap();
- let pos = crate::grid::Pos { row, col };
-
- if let Some((prev_col, attrs)) = erase {
- if cell.has_contents() || cell.attrs() != attrs {
- let new_pos = crate::grid::Pos { row, col: prev_col };
- if wrapping
- && prev_pos.row + 1 == new_pos.row
- && prev_pos.col >= self.cols()
- {
- if new_pos.col > 0 {
- contents.extend(
- " ".repeat(usize::from(new_pos.col))
- .as_bytes(),
- );
- } else {
- contents.extend(b" ");
- crate::term::Backspace.write_buf(contents);
- }
- } else {
- crate::term::MoveFromTo::new(prev_pos, new_pos)
- .write_buf(contents);
- }
- prev_pos = new_pos;
- if &prev_attrs != attrs {
- attrs.write_escape_code_diff(contents, &prev_attrs);
- prev_attrs = *attrs;
- }
- crate::term::EraseChar::new(pos.col - prev_col)
- .write_buf(contents);
- erase = None;
- }
- }
-
- if cell != &default_cell {
- let attrs = cell.attrs();
- if cell.has_contents() {
- if pos != prev_pos {
- if !wrapping
- || prev_pos.row + 1 != pos.row
- || prev_pos.col
- < self.cols() - u16::from(cell.is_wide())
- || pos.col != 0
- {
- crate::term::MoveFromTo::new(prev_pos, pos)
- .write_buf(contents);
- }
- prev_pos = pos;
- }
-
- if &prev_attrs != attrs {
- attrs.write_escape_code_diff(contents, &prev_attrs);
- prev_attrs = *attrs;
- }
-
- prev_pos.col += if cell.is_wide() { 2 } else { 1 };
- let cell_contents = cell.contents();
- contents.extend(cell_contents.as_bytes());
- } else if erase.is_none() {
- erase = Some((pos.col, attrs));
- }
- }
- }
- if let Some((prev_col, attrs)) = erase {
- let new_pos = crate::grid::Pos { row, col: prev_col };
- if wrapping
- && prev_pos.row + 1 == new_pos.row
- && prev_pos.col >= self.cols()
- {
- if new_pos.col > 0 {
- contents.extend(
- " ".repeat(usize::from(new_pos.col)).as_bytes(),
- );
- } else {
- contents.extend(b" ");
- crate::term::Backspace.write_buf(contents);
- }
- } else {
- crate::term::MoveFromTo::new(prev_pos, new_pos)
- .write_buf(contents);
- }
- prev_pos = new_pos;
- if &prev_attrs != attrs {
- attrs.write_escape_code_diff(contents, &prev_attrs);
- prev_attrs = *attrs;
- }
- crate::term::ClearRowForward.write_buf(contents);
- }
-
- (prev_pos, prev_attrs)
- }
-
- // while it's true that most of the logic in this is identical to
- // write_contents_formatted, i can't figure out how to break out the
- // common parts without making things noticeably slower.
- pub fn write_contents_diff(
- &self,
- contents: &mut Vec<u8>,
- prev: &Self,
- start: u16,
- width: u16,
- row: u16,
- wrapping: bool,
- prev_wrapping: bool,
- mut prev_pos: crate::grid::Pos,
- mut prev_attrs: crate::attrs::Attrs,
- ) -> (crate::grid::Pos, crate::attrs::Attrs) {
- let mut prev_was_wide = false;
-
- let first_cell = &self.cells[usize::from(start)];
- let prev_first_cell = &prev.cells[usize::from(start)];
- if wrapping
- && !prev_wrapping
- && first_cell == prev_first_cell
- && prev_pos.row + 1 == row
- && prev_pos.col
- >= self.cols() - u16::from(prev_first_cell.is_wide())
- {
- let first_cell_attrs = first_cell.attrs();
- if &prev_attrs != first_cell_attrs {
- first_cell_attrs
- .write_escape_code_diff(contents, &prev_attrs);
- prev_attrs = *first_cell_attrs;
- }
- let mut cell_contents = prev_first_cell.contents();
- let need_erase = if cell_contents.is_empty() {
- cell_contents = " ";
- true
- } else {
- false
- };
- contents.extend(cell_contents.as_bytes());
- crate::term::Backspace.write_buf(contents);
- if prev_first_cell.is_wide() {
- crate::term::Backspace.write_buf(contents);
- }
- if need_erase {
- crate::term::EraseChar::new(1).write_buf(contents);
- }
- prev_pos = crate::grid::Pos { row, col: 0 };
- }
-
- let mut erase: Option<(u16, &crate::attrs::Attrs)> = None;
- for (col, (cell, prev_cell)) in self
- .cells()
- .zip(prev.cells())
- .enumerate()
- .skip(usize::from(start))
- .take(usize::from(width))
- {
- if prev_was_wide {
- prev_was_wide = false;
- continue;
- }
- prev_was_wide = cell.is_wide();
-
- // we limit the number of cols to a u16 (see Size)
- let col: u16 = col.try_into().unwrap();
- let pos = crate::grid::Pos { row, col };
-
- if let Some((prev_col, attrs)) = erase {
- if cell.has_contents() || cell.attrs() != attrs {
- let new_pos = crate::grid::Pos { row, col: prev_col };
- if wrapping
- && prev_pos.row + 1 == new_pos.row
- && prev_pos.col >= self.cols()
- {
- if new_pos.col > 0 {
- contents.extend(
- " ".repeat(usize::from(new_pos.col))
- .as_bytes(),
- );
- } else {
- contents.extend(b" ");
- crate::term::Backspace.write_buf(contents);
- }
- } else {
- crate::term::MoveFromTo::new(prev_pos, new_pos)
- .write_buf(contents);
- }
- prev_pos = new_pos;
- if &prev_attrs != attrs {
- attrs.write_escape_code_diff(contents, &prev_attrs);
- prev_attrs = *attrs;
- }
- crate::term::EraseChar::new(pos.col - prev_col)
- .write_buf(contents);
- erase = None;
- }
- }
-
- if cell != prev_cell {
- let attrs = cell.attrs();
- if cell.has_contents() {
- if pos != prev_pos {
- if !wrapping
- || prev_pos.row + 1 != pos.row
- || prev_pos.col
- < self.cols() - u16::from(cell.is_wide())
- || pos.col != 0
- {
- crate::term::MoveFromTo::new(prev_pos, pos)
- .write_buf(contents);
- }
- prev_pos = pos;
- }
-
- if &prev_attrs != attrs {
- attrs.write_escape_code_diff(contents, &prev_attrs);
- prev_attrs = *attrs;
- }
-
- prev_pos.col += if cell.is_wide() { 2 } else { 1 };
- contents.extend(cell.contents().as_bytes());
- } else if erase.is_none() {
- erase = Some((pos.col, attrs));
- }
- }
- }
- if let Some((prev_col, attrs)) = erase {
- let new_pos = crate::grid::Pos { row, col: prev_col };
- if wrapping
- && prev_pos.row + 1 == new_pos.row
- && prev_pos.col >= self.cols()
- {
- if new_pos.col > 0 {
- contents.extend(
- " ".repeat(usize::from(new_pos.col)).as_bytes(),
- );
- } else {
- contents.extend(b" ");
- crate::term::Backspace.write_buf(contents);
- }
- } else {
- crate::term::MoveFromTo::new(prev_pos, new_pos)
- .write_buf(contents);
- }
- prev_pos = new_pos;
- if &prev_attrs != attrs {
- attrs.write_escape_code_diff(contents, &prev_attrs);
- prev_attrs = *attrs;
- }
- crate::term::ClearRowForward.write_buf(contents);
- }
-
- // if this row is going from wrapped to not wrapped, we need to erase
- // and redraw the last character to break wrapping. if this row is
- // wrapped, we need to redraw the last character without erasing it to
- // position the cursor after the end of the line correctly so that
- // drawing the next line can just start writing and be wrapped.
- if (!self.wrapped && prev.wrapped) || (!prev.wrapped && self.wrapped)
- {
- let end_pos = if self.cells[usize::from(self.cols() - 1)]
- .is_wide_continuation()
- {
- crate::grid::Pos {
- row,
- col: self.cols() - 2,
- }
- } else {
- crate::grid::Pos {
- row,
- col: self.cols() - 1,
- }
- };
- crate::term::MoveFromTo::new(prev_pos, end_pos)
- .write_buf(contents);
- prev_pos = end_pos;
- if !self.wrapped {
- crate::term::EraseChar::new(1).write_buf(contents);
- }
- let end_cell = &self.cells[usize::from(end_pos.col)];
- if end_cell.has_contents() {
- let attrs = end_cell.attrs();
- if &prev_attrs != attrs {
- attrs.write_escape_code_diff(contents, &prev_attrs);
- prev_attrs = *attrs;
- }
- contents.extend(end_cell.contents().as_bytes());
- prev_pos.col += if end_cell.is_wide() { 2 } else { 1 };
- }
- }
-
- (prev_pos, prev_attrs)
- }
-}
diff --git a/vendor/vt100/src/screen.rs b/vendor/vt100/src/screen.rs
deleted file mode 100644
index 7dfec97d..00000000
--- a/vendor/vt100/src/screen.rs
+++ /dev/null
@@ -1,1354 +0,0 @@
-use crate::term::BufWrite as _;
-use unicode_width::UnicodeWidthChar as _;
-
-const MODE_APPLICATION_KEYPAD: u8 = 0b0000_0001;
-const MODE_APPLICATION_CURSOR: u8 = 0b0000_0010;
-const MODE_HIDE_CURSOR: u8 = 0b0000_0100;
-const MODE_ALTERNATE_SCREEN: u8 = 0b0000_1000;
-const MODE_BRACKETED_PASTE: u8 = 0b0001_0000;
-
-/// The xterm mouse handling mode currently in use.
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
-pub enum MouseProtocolMode {
- /// Mouse handling is disabled.
- #[default]
- None,
-
- /// Mouse button events should be reported on button press. Also known as
- /// X10 mouse mode.
- Press,
-
- /// Mouse button events should be reported on button press and release.
- /// Also known as VT200 mouse mode.
- PressRelease,
-
- // Highlight,
- /// Mouse button events should be reported on button press and release, as
- /// well as when the mouse moves between cells while a button is held
- /// down.
- ButtonMotion,
-
- /// Mouse button events should be reported on button press and release,
- /// and mouse motion events should be reported when the mouse moves
- /// between cells regardless of whether a button is held down or not.
- AnyMotion,
- // DecLocator,
-}
-
-/// The encoding to use for the enabled [`MouseProtocolMode`].
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
-pub enum MouseProtocolEncoding {
- /// Default single-printable-byte encoding.
- #[default]
- Default,
-
- /// UTF-8-based encoding.
- Utf8,
-
- /// SGR-like encoding.
- Sgr,
- // Urxvt,
-}
-
-/// Represents the overall terminal state.
-#[derive(Clone, Debug)]
-pub struct Screen {
- grid: crate::grid::Grid,
- alternate_grid: crate::grid::Grid,
-
- attrs: crate::attrs::Attrs,
- saved_attrs: crate::attrs::Attrs,
-
- modes: u8,
- mouse_protocol_mode: MouseProtocolMode,
- mouse_protocol_encoding: MouseProtocolEncoding,
-}
-
-impl Screen {
- pub(crate) fn new(
- size: crate::grid::Size,
- scrollback_len: usize,
- ) -> Self {
- let mut grid = crate::grid::Grid::new(size, scrollback_len);
- grid.allocate_rows();
- Self {
- grid,
- alternate_grid: crate::grid::Grid::new(size, 0),
-
- attrs: crate::attrs::Attrs::default(),
- saved_attrs: crate::attrs::Attrs::default(),
-
- modes: 0,
- mouse_protocol_mode: MouseProtocolMode::default(),
- mouse_protocol_encoding: MouseProtocolEncoding::default(),
- }
- }
-
- /// Resizes the terminal.
- pub fn set_size(&mut self, rows: u16, cols: u16) {
- self.grid.set_size(crate::grid::Size { rows, cols });
- self.alternate_grid
- .set_size(crate::grid::Size { rows, cols });
- }
-
- /// Returns the current size of the terminal.
- ///
- /// The return value will be (rows, cols).
- #[must_use]
- pub fn size(&self) -> (u16, u16) {
- let size = self.grid().size();
- (size.rows, size.cols)
- }
-
- /// Scrolls to the given position in the scrollback.
- ///
- /// This position indicates the offset from the top of the screen, and
- /// should be `0` to put the normal screen in view.
- ///
- /// This affects the return values of methods called on the screen: for
- /// instance, `screen.cell(0, 0)` will return the top left corner of the
- /// screen after taking the scrollback offset into account.
- ///
- /// The value given will be clamped to the actual size of the scrollback.
- pub fn set_scrollback(&mut self, rows: usize) {
- self.grid_mut().set_scrollback(rows);
- }
-
- /// Returns the current position in the scrollback.
- ///
- /// This position indicates the offset from the top of the screen, and is
- /// `0` when the normal screen is in view.
- #[must_use]
- pub fn scrollback(&self) -> usize {
- self.grid().scrollback()
- }
-
- /// Returns the text contents of the terminal.
- ///
- /// This will not include any formatting information, and will be in plain
- /// text format.
- #[must_use]
- pub fn contents(&self) -> String {
- let mut contents = String::new();
- self.write_contents(&mut contents);
- contents
- }
-
- fn write_contents(&self, contents: &mut String) {
- self.grid().write_contents(contents);
- }
-
- /// Returns the text contents of the terminal by row, restricted to the
- /// given subset of columns.
- ///
- /// This will not include any formatting information, and will be in plain
- /// text format.
- ///
- /// Newlines will not be included.
- pub fn rows(
- &self,
- start: u16,
- width: u16,
- ) -> impl Iterator<Item = String> + '_ {
- self.grid().visible_rows().map(move |row| {
- let mut contents = String::new();
- row.write_contents(&mut contents, start, width, false);
- contents
- })
- }
-
- /// Returns the text contents of the terminal logically between two cells.
- /// This will include the remainder of the starting row after `start_col`,
- /// followed by the entire contents of the rows between `start_row` and
- /// `end_row`, followed by the beginning of the `end_row` up until
- /// `end_col`. This is useful for things like determining the contents of
- /// a clipboard selection.
- #[must_use]
- pub fn contents_between(
- &self,
- start_row: u16,
- start_col: u16,
- end_row: u16,
- end_col: u16,
- ) -> String {
- match start_row.cmp(&end_row) {
- std::cmp::Ordering::Less => {
- let (_, cols) = self.size();
- let mut contents = String::new();
- for (i, row) in self
- .grid()
- .visible_rows()
- .enumerate()
- .skip(usize::from(start_row))
- .take(usize::from(end_row) - usize::from(start_row) + 1)
- {
- if i == usize::from(start_row) {
- row.write_contents(
- &mut contents,
- start_col,
- cols - start_col,
- false,
- );
- if !row.wrapped() {
- contents.push('\n');
- }
- } else if i == usize::from(end_row) {
- row.write_contents(&mut contents, 0, end_col, false);
- } else {
- row.write_contents(&mut contents, 0, cols, false);
- if !row.wrapped() {
- contents.push('\n');
- }
- }
- }
- contents
- }
- std::cmp::Ordering::Equal => {
- if start_col < end_col {
- self.rows(start_col, end_col - start_col)
- .nth(usize::from(start_row))
- .unwrap_or_default()
- } else {
- String::new()
- }
- }
- std::cmp::Ordering::Greater => String::new(),
- }
- }
-
- /// Return escape codes sufficient to reproduce the entire contents of the
- /// current terminal state. This is a convenience wrapper around
- /// [`contents_formatted`](Self::contents_formatted) and
- /// [`input_mode_formatted`](Self::input_mode_formatted).
- #[must_use]
- pub fn state_formatted(&self) -> Vec<u8> {
- let mut contents = vec![];
- self.write_contents_formatted(&mut contents);
- self.write_input_mode_formatted(&mut contents);
- contents
- }
-
- /// Return escape codes sufficient to turn the terminal state of the
- /// screen `prev` into the current terminal state. This is a convenience
- /// wrapper around [`contents_diff`](Self::contents_diff) and
- /// [`input_mode_diff`](Self::input_mode_diff).
- #[must_use]
- pub fn state_diff(&self, prev: &Self) -> Vec<u8> {
- let mut contents = vec![];
- self.write_contents_diff(&mut contents, prev);
- self.write_input_mode_diff(&mut contents, prev);
- contents
- }
-
- /// Returns the formatted visible contents of the terminal.
- ///
- /// Formatting information will be included inline as terminal escape
- /// codes. The result will be suitable for feeding directly to a raw
- /// terminal parser, and will result in the same visual output.
- #[must_use]
- pub fn contents_formatted(&self) -> Vec<u8> {
- let mut contents = vec![];
- self.write_contents_formatted(&mut contents);
- contents
- }
-
- fn write_contents_formatted(&self, contents: &mut Vec<u8>) {
- crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
- let prev_attrs = self.grid().write_contents_formatted(contents);
- self.attrs.write_escape_code_diff(contents, &prev_attrs);
- }
-
- /// Returns the formatted visible contents of the terminal by row,
- /// restricted to the given subset of columns.
- ///
- /// Formatting information will be included inline as terminal escape
- /// codes. The result will be suitable for feeding directly to a raw
- /// terminal parser, and will result in the same visual output.
- ///
- /// You are responsible for positioning the cursor before printing each
- /// row, and the final cursor position after displaying each row is
- /// unspecified.
- // the unwraps in this method shouldn't be reachable
- #[allow(clippy::missing_panics_doc)]
- pub fn rows_formatted(
- &self,
- start: u16,
- width: u16,
- ) -> impl Iterator<Item = Vec<u8>> + '_ {
- let mut wrapping = false;
- self.grid().visible_rows().enumerate().map(move |(i, row)| {
- // number of rows in a grid is stored in a u16 (see Size), so
- // visible_rows can never return enough rows to overflow here
- let i = i.try_into().unwrap();
- let mut contents = vec![];
- row.write_contents_formatted(
- &mut contents,
- start,
- width,
- i,
- wrapping,
- None,
- None,
- );
- if start == 0 && width == self.grid.size().cols {
- wrapping = row.wrapped();
- }
- contents
- })
- }
-
- /// Returns a terminal byte stream sufficient to turn the visible contents
- /// of the screen described by `prev` into the visible contents of the
- /// screen described by `self`.
- ///
- /// The result of rendering `prev.contents_formatted()` followed by
- /// `self.contents_diff(prev)` should be equivalent to the result of
- /// rendering `self.contents_formatted()`. This is primarily useful when
- /// you already have a terminal parser whose state is described by `prev`,
- /// since the diff will likely require less memory and cause less
- /// flickering than redrawing the entire screen contents.
- #[must_use]
- pub fn contents_diff(&self, prev: &Self) -> Vec<u8> {
- let mut contents = vec![];
- self.write_contents_diff(&mut contents, prev);
- contents
- }
-
- fn write_contents_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
- if self.hide_cursor() != prev.hide_cursor() {
- crate::term::HideCursor::new(self.hide_cursor())
- .write_buf(contents);
- }
- let prev_attrs = self.grid().write_contents_diff(
- contents,
- prev.grid(),
- prev.attrs,
- );
- self.attrs.write_escape_code_diff(contents, &prev_attrs);
- }
-
- /// Returns a sequence of terminal byte streams sufficient to turn the
- /// visible contents of the subset of each row from `prev` (as described
- /// by `start` and `width`) into the visible contents of the corresponding
- /// row subset in `self`.
- ///
- /// You are responsible for positioning the cursor before printing each
- /// row, and the final cursor position after displaying each row is
- /// unspecified.
- // the unwraps in this method shouldn't be reachable
- #[allow(clippy::missing_panics_doc)]
- pub fn rows_diff<'a>(
- &'a self,
- prev: &'a Self,
- start: u16,
- width: u16,
- ) -> impl Iterator<Item = Vec<u8>> + 'a {
- self.grid()
- .visible_rows()
- .zip(prev.grid().visible_rows())
- .enumerate()
- .map(move |(i, (row, prev_row))| {
- // number of rows in a grid is stored in a u16 (see Size), so
- // visible_rows can never return enough rows to overflow here
- let i = i.try_into().unwrap();
- let mut contents = vec![];
- row.write_contents_diff(
- &mut contents,
- prev_row,
- start,
- width,
- i,
- false,
- false,
- crate::grid::Pos { row: i, col: start },
- crate::attrs::Attrs::default(),
- );
- contents
- })
- }
-
- /// Returns terminal escape sequences sufficient to set the current
- /// terminal's input modes.
- ///
- /// Supported modes are:
- /// * application keypad
- /// * application cursor
- /// * bracketed paste
- /// * xterm mouse support
- #[must_use]
- pub fn input_mode_formatted(&self) -> Vec<u8> {
- let mut contents = vec![];
- self.write_input_mode_formatted(&mut contents);
- contents
- }
-
- fn write_input_mode_formatted(&self, contents: &mut Vec<u8>) {
- crate::term::ApplicationKeypad::new(
- self.mode(MODE_APPLICATION_KEYPAD),
- )
- .write_buf(contents);
- crate::term::ApplicationCursor::new(
- self.mode(MODE_APPLICATION_CURSOR),
- )
- .write_buf(contents);
- crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE))
- .write_buf(contents);
- crate::term::MouseProtocolMode::new(
- self.mouse_protocol_mode,
- MouseProtocolMode::None,
- )
- .write_buf(contents);
- crate::term::MouseProtocolEncoding::new(
- self.mouse_protocol_encoding,
- MouseProtocolEncoding::Default,
- )
- .write_buf(contents);
- }
-
- /// Returns terminal escape sequences sufficient to change the previous
- /// terminal's input modes to the input modes enabled in the current
- /// terminal.
- #[must_use]
- pub fn input_mode_diff(&self, prev: &Self) -> Vec<u8> {
- let mut contents = vec![];
- self.write_input_mode_diff(&mut contents, prev);
- contents
- }
-
- fn write_input_mode_diff(&self, contents: &mut Vec<u8>, prev: &Self) {
- if self.mode(MODE_APPLICATION_KEYPAD)
- != prev.mode(MODE_APPLICATION_KEYPAD)
- {
- crate::term::ApplicationKeypad::new(
- self.mode(MODE_APPLICATION_KEYPAD),
- )
- .write_buf(contents);
- }
- if self.mode(MODE_APPLICATION_CURSOR)
- != prev.mode(MODE_APPLICATION_CURSOR)
- {
- crate::term::ApplicationCursor::new(
- self.mode(MODE_APPLICATION_CURSOR),
- )
- .write_buf(contents);
- }
- if self.mode(MODE_BRACKETED_PASTE) != prev.mode(MODE_BRACKETED_PASTE)
- {
- crate::term::BracketedPaste::new(self.mode(MODE_BRACKETED_PASTE))
- .write_buf(contents);
- }
- crate::term::MouseProtocolMode::new(
- self.mouse_protocol_mode,
- prev.mouse_protocol_mode,
- )
- .write_buf(contents);
- crate::term::MouseProtocolEncoding::new(
- self.mouse_protocol_encoding,
- prev.mouse_protocol_encoding,
- )
- .write_buf(contents);
- }
-
- /// Returns terminal escape sequences sufficient to set the current
- /// terminal's drawing attributes.
- ///
- /// Supported drawing attributes are:
- /// * fgcolor
- /// * bgcolor
- /// * bold
- /// * dim
- /// * italic
- /// * underline
- /// * inverse
- ///
- /// This is not typically necessary, since
- /// [`contents_formatted`](Self::contents_formatted) will leave
- /// the current active drawing attributes in the correct state, but this
- /// can be useful in the case of drawing additional things on top of a
- /// terminal output, since you will need to restore the terminal state
- /// without the terminal contents necessarily being the same.
- #[must_use]
- pub fn attributes_formatted(&self) -> Vec<u8> {
- let mut contents = vec![];
- self.write_attributes_formatted(&mut contents);
- contents
- }
-
- fn write_attributes_formatted(&self, contents: &mut Vec<u8>) {
- crate::term::ClearAttrs.write_buf(contents);
- self.attrs.write_escape_code_diff(
- contents,
- &crate::attrs::Attrs::default(),
- );
- }
-
- /// Returns the current cursor position of the terminal.
- ///
- /// The return value will be (row, col).
- #[must_use]
- pub fn cursor_position(&self) -> (u16, u16) {
- let pos = self.grid().pos();
- (pos.row, pos.col)
- }
-
- /// Returns terminal escape sequences sufficient to set the current
- /// cursor state of the terminal.
- ///
- /// This is not typically necessary, since
- /// [`contents_formatted`](Self::contents_formatted) will leave
- /// the cursor in the correct state, but this can be useful in the case of
- /// drawing additional things on top of a terminal output, since you will
- /// need to restore the terminal state without the terminal contents
- /// necessarily being the same.
- ///
- /// Note that the bytes returned by this function may alter the active
- /// drawing attributes, because it may require redrawing existing cells in
- /// order to position the cursor correctly (for instance, in the case
- /// where the cursor is past the end of a row). Therefore, you should
- /// ensure to reset the active drawing attributes if necessary after
- /// processing this data, for instance by using
- /// [`attributes_formatted`](Self::attributes_formatted).
- #[must_use]
- pub fn cursor_state_formatted(&self) -> Vec<u8> {
- let mut contents = vec![];
- self.write_cursor_state_formatted(&mut contents);
- contents
- }
-
- fn write_cursor_state_formatted(&self, contents: &mut Vec<u8>) {
- crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents);
- self.grid()
- .write_cursor_position_formatted(contents, None, None);
-
- // we don't just call write_attributes_formatted here, because that
- // would still be confusing - consider the case where the user sets
- // their own unrelated drawing attributes (on a different parser
- // instance) and then calls cursor_state_formatted. just documenting
- // it and letting the user handle it on their own is more
- // straightforward.
- }
-
- /// Returns the [`Cell`](crate::Cell) object at the given location in the
- /// terminal, if it exists.
- #[must_use]
- pub fn cell(&self, row: u16, col: u16) -> Option<&crate::Cell> {
- self.grid().visible_cell(crate::grid::Pos { row, col })
- }
-
- /// Returns whether the text in row `row` should wrap to the next line.
- #[must_use]
- pub fn row_wrapped(&self, row: u16) -> bool {
- self.grid()
- .visible_row(row)
- .is_some_and(crate::row::Row::wrapped)
- }
-
- /// Returns whether the alternate screen is currently in use.
- #[must_use]
- pub fn alternate_screen(&self) -> bool {
- self.mode(MODE_ALTERNATE_SCREEN)
- }
-
- /// Returns whether the terminal should be in application keypad mode.
- #[must_use]
- pub fn application_keypad(&self) -> bool {
- self.mode(MODE_APPLICATION_KEYPAD)
- }
-
- /// Returns whether the terminal should be in application cursor mode.
- #[must_use]
- pub fn application_cursor(&self) -> bool {
- self.mode(MODE_APPLICATION_CURSOR)
- }
-
- /// Returns whether the terminal should be in hide cursor mode.
- #[must_use]
- pub fn hide_cursor(&self) -> bool {
- self.mode(MODE_HIDE_CURSOR)
- }
-
- /// Returns whether the terminal should be in bracketed paste mode.
- #[must_use]
- pub fn bracketed_paste(&self) -> bool {
- self.mode(MODE_BRACKETED_PASTE)
- }
-
- /// Returns the currently active [`MouseProtocolMode`].
- #[must_use]
- pub fn mouse_protocol_mode(&self) -> MouseProtocolMode {
- self.mouse_protocol_mode
- }
-
- /// Returns the currently active [`MouseProtocolEncoding`].
- #[must_use]
- pub fn mouse_protocol_encoding(&self) -> MouseProtocolEncoding {
- self.mouse_protocol_encoding
- }
-
- /// Returns the currently active foreground color.
- #[must_use]
- pub fn fgcolor(&self) -> crate::Color {
- self.attrs.fgcolor
- }
-
- /// Returns the currently active background color.
- #[must_use]
- pub fn bgcolor(&self) -> crate::Color {
- self.attrs.bgcolor
- }
-
- /// Returns whether newly drawn text should be rendered with the bold text
- /// attribute.
- #[must_use]
- pub fn bold(&self) -> bool {
- self.attrs.bold()
- }
-
- /// Returns whether newly drawn text should be rendered with the dim text
- /// attribute.
- #[must_use]
- pub fn dim(&self) -> bool {
- self.attrs.dim()
- }
-
- /// Returns whether newly drawn text should be rendered with the italic
- /// text attribute.
- #[must_use]
- pub fn italic(&self) -> bool {
- self.attrs.italic()
- }
-
- /// Returns whether newly drawn text should be rendered with the
- /// underlined text attribute.
- #[must_use]
- pub fn underline(&self) -> bool {
- self.attrs.underline()
- }
-
- /// Returns whether newly drawn text should be rendered with the inverse
- /// text attribute.
- #[must_use]
- pub fn inverse(&self) -> bool {
- self.attrs.inverse()
- }
-
- pub(crate) fn grid(&self) -> &crate::grid::Grid {
- if self.mode(MODE_ALTERNATE_SCREEN) {
- &self.alternate_grid
- } else {
- &self.grid
- }
- }
-
- fn grid_mut(&mut self) -> &mut crate::grid::Grid {
- if self.mode(MODE_ALTERNATE_SCREEN) {
- &mut self.alternate_grid
- } else {
- &mut self.grid
- }
- }
-
- fn enter_alternate_grid(&mut self) {
- self.grid_mut().set_scrollback(0);
- self.set_mode(MODE_ALTERNATE_SCREEN);
- self.alternate_grid.allocate_rows();
- }
-
- fn exit_alternate_grid(&mut self) {
- self.clear_mode(MODE_ALTERNATE_SCREEN);
- }
-
- fn save_cursor(&mut self) {
- self.grid_mut().save_cursor();
- self.saved_attrs = self.attrs;
- }
-
- fn restore_cursor(&mut self) {
- self.grid_mut().restore_cursor();
- self.attrs = self.saved_attrs;
- }
-
- fn set_mode(&mut self, mode: u8) {
- self.modes |= mode;
- }
-
- fn clear_mode(&mut self, mode: u8) {
- self.modes &= !mode;
- }
-
- fn mode(&self, mode: u8) -> bool {
- self.modes & mode != 0
- }
-
- fn set_mouse_mode(&mut self, mode: MouseProtocolMode) {
- self.mouse_protocol_mode = mode;
- }
-
- fn clear_mouse_mode(&mut self, mode: MouseProtocolMode) {
- if self.mouse_protocol_mode == mode {
- self.mouse_protocol_mode = MouseProtocolMode::default();
- }
- }
-
- fn set_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
- self.mouse_protocol_encoding = encoding;
- }
-
- fn clear_mouse_encoding(&mut self, encoding: MouseProtocolEncoding) {
- if self.mouse_protocol_encoding == encoding {
- self.mouse_protocol_encoding = MouseProtocolEncoding::default();
- }
- }
-}
-
-impl Screen {
- pub(crate) fn text(&mut self, c: char) {
- let pos = self.grid().pos();
- let size = self.grid().size();
- let attrs = self.attrs;
-
- let width = c.width();
- if width.is_none() && (u32::from(c)) < 256 {
- // don't even try to draw control characters
- return;
- }
- let width = width
- .unwrap_or(1)
- .try_into()
- // width() can only return 0, 1, or 2
- .unwrap();
-
- // it doesn't make any sense to wrap if the last column in a row
- // didn't already have contents. don't try to handle the case where a
- // character wraps because there was only one column left in the
- // previous row - literally everything handles this case differently,
- // and this is tmux behavior (and also the simplest). i'm open to
- // reconsidering this behavior, but only with a really good reason
- // (xterm handles this by introducing the concept of triple width
- // cells, which i really don't want to do).
- let mut wrap = false;
- if pos.col > size.cols - width {
- let last_cell = self
- .grid()
- .drawing_cell(crate::grid::Pos {
- row: pos.row,
- col: size.cols - 1,
- })
- // pos.row is valid, since it comes directly from
- // self.grid().pos() which we assume to always have a valid
- // row value. size.cols - 1 is also always a valid column.
- .unwrap();
- if last_cell.has_contents() || last_cell.is_wide_continuation() {
- wrap = true;
- }
- }
- self.grid_mut().col_wrap(width, wrap);
- let pos = self.grid().pos();
-
- if width == 0 {
- if pos.col > 0 {
- let mut prev_cell = self
- .grid_mut()
- .drawing_cell_mut(crate::grid::Pos {
- row: pos.row,
- col: pos.col - 1,
- })
- // pos.row is valid, since it comes directly from
- // self.grid().pos() which we assume to always have a
- // valid row value. pos.col - 1 is valid because we just
- // checked for pos.col > 0.
- .unwrap();
- if prev_cell.is_wide_continuation() {
- prev_cell = self
- .grid_mut()
- .drawing_cell_mut(crate::grid::Pos {
- row: pos.row,
- col: pos.col - 2,
- })
- // pos.row is valid, since it comes directly from
- // self.grid().pos() which we assume to always have a
- // valid row value. we know pos.col - 2 is valid
- // because the cell at pos.col - 1 is a wide
- // continuation character, which means there must be
- // the first half of the wide character before it.
- .unwrap();
- }
- prev_cell.append(c);
- } else if pos.row > 0 {
- let prev_row = self
- .grid()
- .drawing_row(pos.row - 1)
- // pos.row is valid, since it comes directly from
- // self.grid().pos() which we assume to always have a
- // valid row value. pos.row - 1 is valid because we just
- // checked for pos.row > 0.
- .unwrap();
- if prev_row.wrapped() {
- let mut prev_cell = self
- .grid_mut()
- .drawing_cell_mut(crate::grid::Pos {
- row: pos.row - 1,
- col: size.cols - 1,
- })
- // pos.row is valid, since it comes directly from
- // self.grid().pos() which we assume to always have a
- // valid row value. pos.row - 1 is valid because we
- // just checked for pos.row > 0. col of size.cols - 1
- // is always valid.
- .unwrap();
- if prev_cell.is_wide_continuation() {
- prev_cell = self
- .grid_mut()
- .drawing_cell_mut(crate::grid::Pos {
- row: pos.row - 1,
- col: size.cols - 2,
- })
- // pos.row is valid, since it comes directly from
- // self.grid().pos() which we assume to always
- // have a valid row value. pos.row - 1 is valid
- // because we just checked for pos.row > 0. col of
- // size.cols - 2 is valid because the cell at
- // size.cols - 1 is a wide continuation character,
- // so it must have the first half of the wide
- // character before it.
- .unwrap();
- }
- prev_cell.append(c);
- }
- }
- } else {
- if self
- .grid()
- .drawing_cell(pos)
- // pos.row is valid because we assume self.grid().pos() to
- // always have a valid row value. pos.col is valid because we
- // called col_wrap() immediately before this, which ensures
- // that self.grid().pos().col has a valid value.
- .unwrap()
- .is_wide_continuation()
- {
- let prev_cell = self
- .grid_mut()
- .drawing_cell_mut(crate::grid::Pos {
- row: pos.row,
- col: pos.col - 1,
- })
- // pos.row is valid because we assume self.grid().pos() to
- // always have a valid row value. pos.col is valid because
- // we called col_wrap() immediately before this, which
- // ensures that self.grid().pos().col has a valid value.
- // pos.col - 1 is valid because the cell at pos.col is a
- // wide continuation character, so it must have the first
- // half of the wide character before it.
- .unwrap();
- prev_cell.clear(attrs);
- }
-
- if self
- .grid()
- .drawing_cell(pos)
- // pos.row is valid because we assume self.grid().pos() to
- // always have a valid row value. pos.col is valid because we
- // called col_wrap() immediately before this, which ensures
- // that self.grid().pos().col has a valid value.
- .unwrap()
- .is_wide()
- {
- let next_cell = self
- .grid_mut()
- .drawing_cell_mut(crate::grid::Pos {
- row: pos.row,
- col: pos.col + 1,
- })
- // pos.row is valid because we assume self.grid().pos() to
- // always have a valid row value. pos.col is valid because
- // we called col_wrap() immediately before this, which
- // ensures that self.grid().pos().col has a valid value.
- // pos.col + 1 is valid because the cell at pos.col is a
- // wide character, so it must have the second half of the
- // wide character after it.
- .unwrap();
- next_cell.set(' ', attrs);
- }
-
- let cell = self
- .grid_mut()
- .drawing_cell_mut(pos)
- // pos.row is valid because we assume self.grid().pos() to
- // always have a valid row value. pos.col is valid because we
- // called col_wrap() immediately before this, which ensures
- // that self.grid().pos().col has a valid value.
- .unwrap();
- cell.set(c, attrs);
- self.grid_mut().col_inc(1);
- if width > 1 {
- let pos = self.grid().pos();
- if self
- .grid()
- .drawing_cell(pos)
- // pos.row is valid because we assume self.grid().pos() to
- // always have a valid row value. pos.col is valid because
- // we called col_wrap() earlier, which ensures that
- // self.grid().pos().col has a valid value. this is true
- // even though we just called col_inc, because this branch
- // only happens if width > 1, and col_wrap takes width
- // into account.
- .unwrap()
- .is_wide()
- {
- let next_next_pos = crate::grid::Pos {
- row: pos.row,
- col: pos.col + 1,
- };
- let next_next_cell = self
- .grid_mut()
- .drawing_cell_mut(next_next_pos)
- // pos.row is valid because we assume
- // self.grid().pos() to always have a valid row value.
- // pos.col is valid because we called col_wrap()
- // earlier, which ensures that self.grid().pos().col
- // has a valid value. this is true even though we just
- // called col_inc, because this branch only happens if
- // width > 1, and col_wrap takes width into account.
- // pos.col + 1 is valid because the cell at pos.col is
- // wide, and so it must have the second half of the
- // wide character after it.
- .unwrap();
- next_next_cell.clear(attrs);
- if next_next_pos.col == size.cols - 1 {
- self.grid_mut()
- .drawing_row_mut(pos.row)
- // we assume self.grid().pos().row is always valid
- .unwrap()
- .wrap(false);
- }
- }
- let next_cell = self
- .grid_mut()
- .drawing_cell_mut(pos)
- // pos.row is valid because we assume self.grid().pos() to
- // always have a valid row value. pos.col is valid because
- // we called col_wrap() earlier, which ensures that
- // self.grid().pos().col has a valid value. this is true
- // even though we just called col_inc, because this branch
- // only happens if width > 1, and col_wrap takes width
- // into account.
- .unwrap();
- next_cell.clear(crate::attrs::Attrs::default());
- next_cell.set_wide_continuation(true);
- self.grid_mut().col_inc(1);
- }
- }
- }
-
- // control codes
-
- pub(crate) fn bs(&mut self) {
- self.grid_mut().col_dec(1);
- }
-
- pub(crate) fn tab(&mut self) {
- self.grid_mut().col_tab();
- }
-
- pub(crate) fn lf(&mut self) {
- self.grid_mut().row_inc_scroll(1);
- }
-
- pub(crate) fn vt(&mut self) {
- self.lf();
- }
-
- pub(crate) fn ff(&mut self) {
- self.lf();
- }
-
- pub(crate) fn cr(&mut self) {
- self.grid_mut().col_set(0);
- }
-
- // escape codes
-
- // ESC 7
- pub(crate) fn decsc(&mut self) {
- self.save_cursor();
- }
-
- // ESC 8
- pub(crate) fn decrc(&mut self) {
- self.restore_cursor();
- }
-
- // ESC =
- pub(crate) fn deckpam(&mut self) {
- self.set_mode(MODE_APPLICATION_KEYPAD);
- }
-
- // ESC >
- pub(crate) fn deckpnm(&mut self) {
- self.clear_mode(MODE_APPLICATION_KEYPAD);
- }
-
- // ESC M
- pub(crate) fn ri(&mut self) {
- self.grid_mut().row_dec_scroll(1);
- }
-
- // ESC c
- pub(crate) fn ris(&mut self) {
- *self = Self::new(self.grid.size(), self.grid.scrollback_len());
- }
-
- // csi codes
-
- // CSI @
- pub(crate) fn ich(&mut self, count: u16) {
- self.grid_mut().insert_cells(count);
- }
-
- // CSI A
- pub(crate) fn cuu(&mut self, offset: u16) {
- self.grid_mut().row_dec_clamp(offset);
- }
-
- // CSI B
- pub(crate) fn cud(&mut self, offset: u16) {
- self.grid_mut().row_inc_clamp(offset);
- }
-
- // CSI C
- pub(crate) fn cuf(&mut self, offset: u16) {
- self.grid_mut().col_inc_clamp(offset);
- }
-
- // CSI D
- pub(crate) fn cub(&mut self, offset: u16) {
- self.grid_mut().col_dec(offset);
- }
-
- // CSI E
- pub(crate) fn cnl(&mut self, offset: u16) {
- self.grid_mut().col_set(0);
- self.grid_mut().row_inc_clamp(offset);
- }
-
- // CSI F
- pub(crate) fn cpl(&mut self, offset: u16) {
- self.grid_mut().col_set(0);
- self.grid_mut().row_dec_clamp(offset);
- }
-
- // CSI G
- pub(crate) fn cha(&mut self, col: u16) {
- self.grid_mut().col_set(col - 1);
- }
-
- // CSI H
- pub(crate) fn cup(&mut self, (row, col): (u16, u16)) {
- self.grid_mut().set_pos(crate::grid::Pos {
- row: row - 1,
- col: col - 1,
- });
- }
-
- // CSI J
- pub(crate) fn ed(
- &mut self,
- mode: u16,
- mut unhandled: impl FnMut(&mut Self),
- ) {
- let attrs = self.attrs;
- match mode {
- 0 => self.grid_mut().erase_all_forward(attrs),
- 1 => self.grid_mut().erase_all_backward(attrs),
- 2 => self.grid_mut().erase_all(attrs),
- _ => unhandled(self),
- }
- }
-
- // CSI ? J
- pub(crate) fn decsed(
- &mut self,
- mode: u16,
- unhandled: impl FnMut(&mut Self),
- ) {
- self.ed(mode, unhandled);
- }
-
- // CSI K
- pub(crate) fn el(
- &mut self,
- mode: u16,
- mut unhandled: impl FnMut(&mut Self),
- ) {
- let attrs = self.attrs;
- match mode {
- 0 => self.grid_mut().erase_row_forward(attrs),
- 1 => self.grid_mut().erase_row_backward(attrs),
- 2 => self.grid_mut().erase_row(attrs),
- _ => unhandled(self),
- }
- }
-
- // CSI ? K
- pub(crate) fn decsel(
- &mut self,
- mode: u16,
- unhandled: impl FnMut(&mut Self),
- ) {
- self.el(mode, unhandled);
- }
-
- // CSI L
- pub(crate) fn il(&mut self, count: u16) {
- self.grid_mut().insert_lines(count);
- }
-
- // CSI M
- pub(crate) fn dl(&mut self, count: u16) {
- self.grid_mut().delete_lines(count);
- }
-
- // CSI P
- pub(crate) fn dch(&mut self, count: u16) {
- self.grid_mut().delete_cells(count);
- }
-
- // CSI S
- pub(crate) fn su(&mut self, count: u16) {
- self.grid_mut().scroll_up(count);
- }
-
- // CSI T
- pub(crate) fn sd(&mut self, count: u16) {
- self.grid_mut().scroll_down(count);
- }
-
- // CSI X
- pub(crate) fn ech(&mut self, count: u16) {
- let attrs = self.attrs;
- self.grid_mut().erase_cells(count, attrs);
- }
-
- // CSI d
- pub(crate) fn vpa(&mut self, row: u16) {
- self.grid_mut().row_set(row - 1);
- }
-
- // CSI ? h
- pub(crate) fn decset(
- &mut self,
- params: &vte::Params,
- mut unhandled: impl FnMut(&mut Self),
- ) {
- for param in params {
- match param {
- [1] => self.set_mode(MODE_APPLICATION_CURSOR),
- [6] => self.grid_mut().set_origin_mode(true),
- [9] => self.set_mouse_mode(MouseProtocolMode::Press),
- [25] => self.clear_mode(MODE_HIDE_CURSOR),
- [47] => self.enter_alternate_grid(),
- [1000] => {
- self.set_mouse_mode(MouseProtocolMode::PressRelease);
- }
- [1002] => {
- self.set_mouse_mode(MouseProtocolMode::ButtonMotion);
- }
- [1003] => self.set_mouse_mode(MouseProtocolMode::AnyMotion),
- [1005] => {
- self.set_mouse_encoding(MouseProtocolEncoding::Utf8);
- }
- [1006] => {
- self.set_mouse_encoding(MouseProtocolEncoding::Sgr);
- }
- [1049] => {
- self.decsc();
- self.alternate_grid.clear();
- self.enter_alternate_grid();
- }
- [2004] => self.set_mode(MODE_BRACKETED_PASTE),
- _ => unhandled(self),
- }
- }
- }
-
- // CSI ? l
- pub(crate) fn decrst(
- &mut self,
- params: &vte::Params,
- mut unhandled: impl FnMut(&mut Self),
- ) {
- for param in params {
- match param {
- [1] => self.clear_mode(MODE_APPLICATION_CURSOR),
- [6] => self.grid_mut().set_origin_mode(false),
- [9] => self.clear_mouse_mode(MouseProtocolMode::Press),
- [25] => self.set_mode(MODE_HIDE_CURSOR),
- [47] => {
- self.exit_alternate_grid();
- }
- [1000] => {
- self.clear_mouse_mode(MouseProtocolMode::PressRelease);
- }
- [1002] => {
- self.clear_mouse_mode(MouseProtocolMode::ButtonMotion);
- }
- [1003] => {
- self.clear_mouse_mode(MouseProtocolMode::AnyMotion);
- }
- [1005] => {
- self.clear_mouse_encoding(MouseProtocolEncoding::Utf8);
- }
- [1006] => {
- self.clear_mouse_encoding(MouseProtocolEncoding::Sgr);
- }
- [1049] => {
- self.exit_alternate_grid();
- self.decrc();
- }
- [2004] => self.clear_mode(MODE_BRACKETED_PASTE),
- _ => unhandled(self),
- }
- }
- }
-
- // CSI m
- pub(crate) fn sgr(
- &mut self,
- params: &vte::Params,
- mut unhandled: impl FnMut(&mut Self),
- ) {
- // XXX really i want to just be able to pass in a default Params
- // instance with a 0 in it, but vte doesn't allow creating new Params
- // instances
- if params.is_empty() {
- self.attrs = crate::attrs::Attrs::default();
- return;
- }
-
- let mut iter = params.iter();
-
- macro_rules! next_param {
- () => {
- match iter.next() {
- Some(n) => n,
- _ => return,
- }
- };
- }
-
- macro_rules! to_u8 {
- ($n:expr) => {
- if let Some(n) = u16_to_u8($n) {
- n
- } else {
- return;
- }
- };
- }
-
- macro_rules! next_param_u8 {
- () => {
- if let &[n] = next_param!() {
- to_u8!(n)
- } else {
- return;
- }
- };
- }
-
- loop {
- match next_param!() {
- [0] => self.attrs = crate::attrs::Attrs::default(),
- [1] => self.attrs.set_bold(),
- [2] => self.attrs.set_dim(),
- [3] => self.attrs.set_italic(true),
- [4] => self.attrs.set_underline(true),
- [7] => self.attrs.set_inverse(true),
- [22] => self.attrs.set_normal_intensity(),
- [23] => self.attrs.set_italic(false),
- [24] => self.attrs.set_underline(false),
- [27] => self.attrs.set_inverse(false),
- [n] if (30..=37).contains(n) => {
- self.attrs.fgcolor = crate::Color::Idx(to_u8!(*n) - 30);
- }
- [38, 2, r, g, b] => {
- self.attrs.fgcolor =
- crate::Color::Rgb(to_u8!(*r), to_u8!(*g), to_u8!(*b));
- }
- [38, 5, i] => {
- self.attrs.fgcolor = crate::Color::Idx(to_u8!(*i));
- }
- [38] => match next_param!() {
- [2] => {
- let r = next_param_u8!();
- let g = next_param_u8!();
- let b = next_param_u8!();
- self.attrs.fgcolor = crate::Color::Rgb(r, g, b);
- }
- [5] => {
- self.attrs.fgcolor =
- crate::Color::Idx(next_param_u8!());
- }
- _ => {
- unhandled(self);
- return;
- }
- },
- [39] => {
- self.attrs.fgcolor = crate::Color::Default;
- }
- [n] if (40..=47).contains(n) => {
- self.attrs.bgcolor = crate::Color::Idx(to_u8!(*n) - 40);
- }
- [48, 2, r, g, b] => {
- self.attrs.bgcolor =
- crate::Color::Rgb(to_u8!(*r), to_u8!(*g), to_u8!(*b));
- }
- [48, 5, i] => {
- self.attrs.bgcolor = crate::Color::Idx(to_u8!(*i));
- }
- [48] => match next_param!() {
- [2] => {
- let r = next_param_u8!();
- let g = next_param_u8!();
- let b = next_param_u8!();
- self.attrs.bgcolor = crate::Color::Rgb(r, g, b);
- }
- [5] => {
- self.attrs.bgcolor =
- crate::Color::Idx(next_param_u8!());
- }
- _ => {
- unhandled(self);
- return;
- }
- },
- [49] => {
- self.attrs.bgcolor = crate::Color::Default;
- }
- [n] if (90..=97).contains(n) => {
- self.attrs.fgcolor = crate::Color::Idx(to_u8!(*n) - 82);
- }
- [n] if (100..=107).contains(n) => {
- self.attrs.bgcolor = crate::Color::Idx(to_u8!(*n) - 92);
- }
- _ => unhandled(self),
- }
- }
- }
-
- // CSI r
- pub(crate) fn decstbm(&mut self, (top, bottom): (u16, u16)) {
- self.grid_mut().set_scroll_region(top - 1, bottom - 1);
- }
-}
-
-fn u16_to_u8(i: u16) -> Option<u8> {
- if i > u16::from(u8::MAX) {
- None
- } else {
- // safe because we just ensured that the value fits in a u8
- Some(i.try_into().unwrap())
- }
-}
diff --git a/vendor/vt100/src/term.rs b/vendor/vt100/src/term.rs
deleted file mode 100644
index b6b898c6..00000000
--- a/vendor/vt100/src/term.rs
+++ /dev/null
@@ -1,551 +0,0 @@
-// TODO: read all of this from terminfo
-
-pub trait BufWrite {
- fn write_buf(&self, buf: &mut Vec<u8>);
-}
-
-#[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<u8>) {
- 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<u8>) {
- 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<u8>) {
- 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<u8>) {
- 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<u8>) {
- 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<u8>) {
- 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<u8>) {
- 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<u8>) {
- 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<crate::Color>,
- bgcolor: Option<crate::Color>,
- intensity: Option<Intensity>,
- italic: Option<bool>,
- underline: Option<bool>,
- inverse: Option<bool>,
-}
-
-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<u8>) {
- 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<u8>) {
- 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<u8>) {
- 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<u8>) {
- 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<u8>) {
- 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<u8>) {
- 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<u8>) {
- 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<u8>) {
- 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<u8>) {
- 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<u8>) {
- 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<I: itoa::Integer>(buf: &mut Vec<u8>, i: I) {
- let mut itoa_buf = itoa::Buffer::new();
- buf.extend_from_slice(itoa_buf.format(i).as_bytes());
-}