use std::fs::File;
use std::io;
use std::os::fd::{AsFd, OwnedFd};
use std::process::Stdio;
use nix::pty::openpty;
use nix::sys::termios::{self, OutputFlags, SetArg};
use tracing::debug;
const ALT_SCREEN_ENABLE: &[u8] = b"\x1b[?1049h";
pub(super) fn contains_alt_screen_seq(data: &[u8]) -> bool {
data.windows(ALT_SCREEN_ENABLE.len())
.any(|w| w == ALT_SCREEN_ENABLE)
}
pub(super) fn get_terminal_winsize() -> libc::winsize {
let mut ws: libc::winsize = unsafe { std::mem::zeroed() };
let ret = unsafe { libc::ioctl(libc::STDOUT_FILENO, libc::TIOCGWINSZ, &mut ws) };
if ret == 0 && ws.ws_col > 0 && ws.ws_row > 0 {
ws
} else {
libc::winsize {
ws_row: 24,
ws_col: 80,
ws_xpixel: 0,
ws_ypixel: 0,
}
}
}
fn disable_opost(slave_fd: &OwnedFd) {
let fd = slave_fd.as_fd();
if let Ok(mut attrs) = termios::tcgetattr(fd) {
attrs.output_flags.remove(OutputFlags::OPOST);
let _ = termios::tcsetattr(fd, SetArg::TCSANOW, &attrs);
}
}
pub(super) fn create_session_pty() -> io::Result<(File, OwnedFd)> {
let ws = get_terminal_winsize();
let pty = openpty(Some(&ws), None).map_err(|e| io::Error::other(e.to_string()))?;
let master_file = File::from(pty.master);
Ok((master_file, pty.slave))
}
fn create_legacy_pty() -> io::Result<(File, OwnedFd)> {
let ws = get_terminal_winsize();
let pty = openpty(Some(&ws), None).map_err(|e| io::Error::other(e.to_string()))?;
disable_opost(&pty.slave);
let master_file = File::from(pty.master);
Ok((master_file, pty.slave))
}
pub(super) fn create_capture_pair() -> io::Result<(Box<dyn std::io::Read + Send>, Stdio)> {
match create_legacy_pty() {
Ok((master, slave)) => Ok((Box::new(master), slave.into())),
Err(e) => {
debug!("PTY creation failed, falling back to pipe: {e}");
let (read, write) = os_pipe::pipe()?;
Ok((Box::new(read), write.into()))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alt_screen_detection() {
assert!(contains_alt_screen_seq(b"before\x1b[?1049hafter"));
assert!(contains_alt_screen_seq(b"\x1b[?1049h"));
assert!(!contains_alt_screen_seq(b"no alt screen here"));
assert!(!contains_alt_screen_seq(b"\x1b[?1049")); }
}