#![forbid(unsafe_code)]
use std::io::{self, Write};
use crate::terminal_capabilities::TerminalCapabilities;
const ESC: u8 = 0x1b;
const ST: &[u8] = b"\x1b\\";
pub fn tmux_wrap<W: Write>(w: &mut W, sequence: &[u8]) -> io::Result<()> {
w.write_all(b"\x1bPtmux;")?;
for &byte in sequence {
if byte == ESC {
w.write_all(&[ESC, ESC])?;
} else {
w.write_all(&[byte])?;
}
}
w.write_all(ST)
}
const SCREEN_DCS_MAX: usize = 255;
pub fn screen_wrap<W: Write>(w: &mut W, sequence: &[u8]) -> io::Result<()> {
let mut rest = sequence;
loop {
let mut len = rest.len().min(SCREEN_DCS_MAX);
if let Some(esc) = rest[..len].windows(2).position(|pair| pair == ST) {
len = esc + 1;
}
w.write_all(b"\x1bP")?;
w.write_all(&rest[..len])?;
w.write_all(ST)?;
rest = &rest[len..];
if rest.is_empty() {
return Ok(());
}
}
}
pub fn mux_wrap<W: Write>(
w: &mut W,
caps: &TerminalCapabilities,
sequence: &[u8],
) -> io::Result<()> {
if caps.in_tmux {
tmux_wrap(w, sequence)
} else if caps.in_screen {
screen_wrap(w, sequence)
} else {
w.write_all(sequence)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn to_bytes<F: FnOnce(&mut Vec<u8>) -> io::Result<()>>(f: F) -> Vec<u8> {
let mut buf = Vec::new();
f(&mut buf).unwrap();
buf
}
#[test]
fn tmux_wrap_doubles_escapes() {
let osc8 = b"\x1b]8;;https://example.com\x07";
let wrapped = to_bytes(|w| tmux_wrap(w, osc8));
assert!(wrapped.starts_with(b"\x1bPtmux;"));
assert!(wrapped.ends_with(b"\x1b\\"));
let inner = &wrapped[7..wrapped.len() - 2]; let esc_count = inner.windows(2).filter(|w| w == &[ESC, ESC]).count();
assert_eq!(
esc_count, 1,
"Only the ESC from OSC start should be doubled"
);
}
#[test]
fn tmux_wrap_no_escape_passthrough() {
let plain = b"hello world";
let wrapped = to_bytes(|w| tmux_wrap(w, plain));
assert_eq!(wrapped, b"\x1bPtmux;hello world\x1b\\");
}
#[test]
fn tmux_wrap_empty_sequence() {
let wrapped = to_bytes(|w| tmux_wrap(w, b""));
assert_eq!(wrapped, b"\x1bPtmux;\x1b\\");
}
#[test]
fn screen_wrap_basic() {
let seq = b"\x1b]8;;https://example.com\x07";
let wrapped = to_bytes(|w| screen_wrap(w, seq));
assert!(wrapped.starts_with(b"\x1bP"));
assert!(wrapped.ends_with(b"\x1b\\"));
let inner = &wrapped[2..wrapped.len() - 2];
assert_eq!(inner, seq);
}
#[test]
fn screen_wrap_does_not_double_escapes() {
let seq = b"\x1b[?2026h"; let wrapped = to_bytes(|w| screen_wrap(w, seq));
assert_eq!(wrapped, b"\x1bP\x1b[?2026h\x1b\\");
}
#[test]
fn mux_wrap_selects_tmux() {
let mut caps = TerminalCapabilities::basic();
caps.in_tmux = true;
let seq = b"\x1b[?2026h";
let result = to_bytes(|w| mux_wrap(w, &caps, seq));
assert!(result.starts_with(b"\x1bPtmux;"));
}
#[test]
fn mux_wrap_selects_screen() {
let mut caps = TerminalCapabilities::basic();
caps.in_screen = true;
let seq = b"\x1b[?2026h";
let result = to_bytes(|w| mux_wrap(w, &caps, seq));
assert!(result.starts_with(b"\x1bP"));
assert!(!result.starts_with(b"\x1bPtmux;")); }
#[test]
fn mux_wrap_passthrough_for_zellij() {
let mut caps = TerminalCapabilities::basic();
caps.in_zellij = true;
let seq = b"\x1b[?2026h";
let result = to_bytes(|w| mux_wrap(w, &caps, seq));
assert_eq!(result, seq);
}
#[test]
fn mux_wrap_passthrough_for_bare_terminal() {
let caps = TerminalCapabilities::basic();
let seq = b"\x1b[?2026h";
let result = to_bytes(|w| mux_wrap(w, &caps, seq));
assert_eq!(result, seq);
}
#[test]
fn tmux_priority_over_screen() {
let mut caps = TerminalCapabilities::basic();
caps.in_tmux = true;
caps.in_screen = true;
let seq = b"test";
let result = to_bytes(|w| mux_wrap(w, &caps, seq));
assert!(result.starts_with(b"\x1bPtmux;"));
}
#[test]
fn tmux_wrap_multiple_escapes() {
let seq = &[ESC, b'[', b'm', ESC, b']', b'0', ESC, b'\\'];
let wrapped = to_bytes(|w| tmux_wrap(w, seq));
let inner = &wrapped[7..wrapped.len() - 2];
let esc_count = inner.iter().filter(|&&b| b == ESC).count();
assert_eq!(esc_count, 6, "3 ESC bytes should become 6 (doubled)");
}
#[test]
fn tmux_wrap_all_escape_bytes() {
let seq = &[ESC, ESC, ESC];
let wrapped = to_bytes(|w| tmux_wrap(w, seq));
let inner = &wrapped[7..wrapped.len() - 2];
assert_eq!(inner.len(), 6);
assert!(inner.iter().all(|&b| b == ESC));
}
#[test]
fn tmux_wrap_preserves_non_escape_bytes() {
let seq = b"ABCDEF";
let wrapped = to_bytes(|w| tmux_wrap(w, seq));
assert_eq!(wrapped, b"\x1bPtmux;ABCDEF\x1b\\");
}
#[test]
fn tmux_wrap_binary_data() {
let seq: Vec<u8> = (0u8..=255).filter(|&b| b != ESC).collect();
let wrapped = to_bytes(|w| tmux_wrap(w, &seq));
let inner = &wrapped[7..wrapped.len() - 2];
assert_eq!(inner.len(), seq.len());
}
#[test]
fn screen_wrap_empty_sequence() {
let wrapped = to_bytes(|w| screen_wrap(w, b""));
assert_eq!(wrapped, b"\x1bP\x1b\\");
}
#[test]
fn screen_wrap_preserves_all_bytes() {
let seq = &[ESC, ESC, 0x00, 0xFF];
let wrapped = to_bytes(|w| screen_wrap(w, seq));
let inner = &wrapped[2..wrapped.len() - 2];
assert_eq!(inner, seq);
}
#[test]
fn mux_wrap_tmux_priority_over_zellij() {
let mut caps = TerminalCapabilities::basic();
caps.in_tmux = true;
caps.in_zellij = true;
let result = to_bytes(|w| mux_wrap(w, &caps, b"x"));
assert!(result.starts_with(b"\x1bPtmux;"));
}
#[test]
fn mux_wrap_screen_priority_over_zellij() {
let mut caps = TerminalCapabilities::basic();
caps.in_screen = true;
caps.in_zellij = true;
let result = to_bytes(|w| mux_wrap(w, &caps, b"x"));
assert!(result.starts_with(b"\x1bP"));
assert!(!result.starts_with(b"\x1bPtmux;"));
}
#[test]
fn esc_constant_value() {
assert_eq!(ESC, 0x1b);
}
#[test]
fn st_constant_value() {
assert_eq!(ST, b"\x1b\\");
}
#[test]
fn tmux_wrap_large_sequence() {
let seq = vec![b'A'; 10_000];
let wrapped = to_bytes(|w| tmux_wrap(w, &seq));
assert_eq!(wrapped.len(), 10_009);
}
fn screen_strings(wrapped: &[u8]) -> Vec<&[u8]> {
let mut strings = Vec::new();
let mut rest = wrapped;
while !rest.is_empty() {
let body = rest.strip_prefix(b"\x1bP").expect("opens with ESC P");
let end = body
.windows(2)
.position(|pair| pair == ST)
.expect("closes with ST");
strings.push(&body[..end]);
rest = &body[end + ST.len()..];
}
strings
}
#[test]
fn screen_wrap_splits_a_largest_clipboard_write_into_strings_screen_forwards() {
let seq = crate::osc52::encode_set(
crate::osc52::ClipboardSelection::Clipboard,
&vec![0; 56_244],
)
.unwrap();
let wrapped = to_bytes(|w| screen_wrap(w, &seq));
let strings = screen_strings(&wrapped);
assert!(strings.iter().all(|s| s.len() <= SCREEN_DCS_MAX));
assert_eq!(strings.concat(), seq);
}
#[test]
fn screen_wrap_cuts_an_st_inside_the_sequence_between_its_bytes() {
let seq = b"\x1b]8;;https://example.com\x1b\\";
let wrapped = to_bytes(|w| screen_wrap(w, seq));
assert_eq!(
screen_strings(&wrapped),
[&b"\x1b]8;;https://example.com\x1b"[..], b"\\"]
);
}
}