use std::fmt;
pub use portable_pty::PtySize;
use crate::error::{ClientError, IroshError, Result};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PtyOptions {
term: String,
size: PtySize,
modes: Vec<(russh::Pty, u32)>,
}
impl PtyOptions {
pub fn new(term: impl Into<String>, size: PtySize) -> Self {
Self {
term: term.into(),
size,
modes: Vec::new(),
}
}
pub fn modes(mut self, modes: impl Into<Vec<(russh::Pty, u32)>>) -> Self {
self.modes = modes.into();
self
}
pub fn term(&self) -> &str {
&self.term
}
pub fn size(&self) -> PtySize {
self.size
}
pub fn modes_slice(&self) -> &[(russh::Pty, u32)] {
&self.modes
}
}
pub fn default_pty_size() -> PtySize {
let rows = std::env::var("LINES")
.ok()
.and_then(|value| value.parse::<u16>().ok())
.filter(|value| *value > 0)
.unwrap_or(24);
let cols = std::env::var("COLUMNS")
.ok()
.and_then(|value| value.parse::<u16>().ok())
.filter(|value| *value > 0)
.unwrap_or(80);
PtySize {
rows,
cols,
pixel_width: 0,
pixel_height: 0,
}
}
pub fn pty_size(cols: u32, rows: u32, pixel_width: u32, pixel_height: u32) -> PtySize {
PtySize {
rows: rows.clamp(1, u16::MAX as u32) as u16,
cols: cols.clamp(1, u16::MAX as u32) as u16,
pixel_width: pixel_width.clamp(0, u16::MAX as u32) as u16,
pixel_height: pixel_height.clamp(0, u16::MAX as u32) as u16,
}
}
#[cfg(unix)]
pub struct RawTerminal {
fd: i32,
original: libc::termios,
}
#[cfg(unix)]
impl fmt::Debug for RawTerminal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawTerminal").field("fd", &self.fd).finish()
}
}
#[cfg(unix)]
impl RawTerminal {
pub fn new(fd: i32) -> Result<Self> {
unsafe {
let mut termios = std::mem::zeroed::<libc::termios>();
if libc::tcgetattr(fd, &mut termios) != 0 {
return Err(IroshError::Client(ClientError::TerminalIo {
source: std::io::Error::last_os_error(),
}));
}
let original = termios;
libc::cfmakeraw(&mut termios);
if libc::tcsetattr(fd, libc::TCSANOW, &termios) != 0 {
let _ = libc::tcsetattr(fd, libc::TCSANOW, &original);
return Err(IroshError::Client(ClientError::TerminalIo {
source: std::io::Error::last_os_error(),
}));
}
Ok(Self { fd, original })
}
}
}
#[cfg(unix)]
impl Drop for RawTerminal {
fn drop(&mut self) {
unsafe {
let _ = libc::tcsetattr(self.fd, libc::TCSANOW, &self.original);
}
}
}
#[cfg(unix)]
pub fn current_terminal_size() -> PtySize {
unsafe {
let mut winsize = std::mem::zeroed::<libc::winsize>();
if libc::ioctl(libc::STDOUT_FILENO, libc::TIOCGWINSZ, &mut winsize) == 0 {
return PtySize {
rows: winsize.ws_row.max(1),
cols: winsize.ws_col.max(1),
pixel_width: winsize.ws_xpixel,
pixel_height: winsize.ws_ypixel,
};
}
}
default_pty_size()
}
#[cfg(not(unix))]
pub fn current_terminal_size() -> PtySize {
default_pty_size()
}
#[cfg(unix)]
pub fn map_sig(signal: russh::Sig) -> Option<libc::c_int> {
match signal {
russh::Sig::ABRT => Some(libc::SIGABRT),
russh::Sig::ALRM => Some(libc::SIGALRM),
russh::Sig::FPE => Some(libc::SIGFPE),
russh::Sig::HUP => Some(libc::SIGHUP),
russh::Sig::ILL => Some(libc::SIGILL),
russh::Sig::INT => Some(libc::SIGINT),
russh::Sig::KILL => Some(libc::SIGKILL),
russh::Sig::PIPE => Some(libc::SIGPIPE),
russh::Sig::QUIT => Some(libc::SIGQUIT),
russh::Sig::SEGV => Some(libc::SIGSEGV),
russh::Sig::TERM => Some(libc::SIGTERM),
russh::Sig::USR1 => Some(libc::SIGUSR1),
russh::Sig::Custom(_) => None,
}
}
#[cfg(not(unix))]
pub struct RawTerminal;
#[cfg(not(unix))]
impl fmt::Debug for RawTerminal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("RawTerminal")
}
}
#[cfg(not(unix))]
impl RawTerminal {
pub fn new(_fd: i32) -> Result<Self> {
Ok(Self)
}
}
#[cfg(not(unix))]
pub fn map_sig(_signal: russh::Sig) -> Option<i32> {
None
}