use std::{
fs::File,
io,
os::raw::c_int,
process::{Command, Stdio},
};
pub struct RawMode;
impl RawMode {
pub fn enter() -> io::Result<Self> {
Command::new("stty").arg("-icanon").arg("-echo").spawn()?.wait()?;
print!("\x1b[?25l"); Ok(RawMode)
}
}
impl Drop for RawMode {
fn drop(&mut self) {
let _ = Command::new("stty").arg("icanon").arg("echo").spawn().and_then(|mut c| c.wait());
print!("\x1b[?25h"); }
}
unsafe extern "C" {
fn signal(sig: c_int, handler: extern "C" fn(c_int)) -> extern "C" fn(c_int);
}
const SIGINT: c_int = 2;
extern "C" fn handle_sigint(_sig: c_int) {
print!("\x1b[?25h"); let _ = Command::new("stty").arg("icanon").arg("echo").spawn().and_then(|mut c| c.wait());
std::process::exit(0);
}
pub fn install_raw_mode_signal_handler() {
unsafe {
signal(SIGINT, handle_sigint);
}
}
pub fn has_stty() -> bool {
Command::new("stty").arg("size").stdout(Stdio::null()).stderr(Stdio::null()).status().is_ok()
}
pub fn terminal_size() -> io::Result<(usize, usize)> {
let tty = File::open("/dev/tty")?;
let output = Command::new("stty").arg("size").stdin(tty).output()?;
if !output.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, "stty failed"));
}
let output_string = String::from_utf8_lossy(&output.stdout);
let mut parts = output_string.split_whitespace();
let rows = match parts.next() {
Some(rows) => match rows.parse::<usize>() {
Ok(rows) => rows,
Err(_) => {
return Err(io::Error::new(io::ErrorKind::Other, "failed to parse rows"));
},
},
None => {
return Err(io::Error::new(io::ErrorKind::Other, "failed to parse rows"));
},
};
let columns = match parts.next() {
Some(columns) => match columns.parse::<usize>() {
Ok(columns) => columns,
Err(_) => {
return Err(io::Error::new(io::ErrorKind::Other, "failed to parse columns"));
},
},
None => {
return Err(io::Error::new(io::ErrorKind::Other, "failed to parse columns"));
},
};
Ok((columns, rows))
}