use crossterm::terminal::size as term_size;
use std::sync::{Mutex, OnceLock};
static SAVED_TERMIOS: OnceLock<Mutex<Option<libc::termios>>> = OnceLock::new();
pub fn enable_raw_mode() -> std::io::Result<()> {
use std::os::fd::AsRawFd;
let fd = std::io::stdin().as_raw_fd();
let mut t: libc::termios = unsafe { std::mem::zeroed() };
if unsafe { libc::tcgetattr(fd, &mut t) } != 0 {
return Err(std::io::Error::last_os_error());
}
*SAVED_TERMIOS
.get_or_init(|| Mutex::new(None))
.lock()
.unwrap() = Some(t);
t.c_iflag &= !(libc::IGNBRK
| libc::BRKINT
| libc::PARMRK
| libc::ISTRIP
| libc::INLCR
| libc::IGNCR
| libc::ICRNL
| libc::IXON);
t.c_oflag &= !libc::OPOST;
t.c_lflag &= !(libc::ECHO | libc::ECHONL | libc::ICANON | libc::ISIG | libc::IEXTEN);
t.c_cflag &= !(libc::CSIZE | libc::PARENB);
t.c_cflag |= libc::CS8;
t.c_cc[libc::VMIN] = 1;
t.c_cc[libc::VTIME] = 0;
if unsafe { libc::tcsetattr(fd, libc::TCSANOW, &t) } != 0 {
return Err(std::io::Error::last_os_error());
}
Ok(())
}
pub fn disable_raw_mode() -> std::io::Result<()> {
use std::os::fd::AsRawFd;
let saved = SAVED_TERMIOS
.get_or_init(|| Mutex::new(None))
.lock()
.unwrap()
.take();
if let Some(t) = saved {
let fd = std::io::stdin().as_raw_fd();
if unsafe { libc::tcsetattr(fd, libc::TCSANOW, &t) } != 0 {
return Err(std::io::Error::last_os_error());
}
}
Ok(())
}
pub fn init_terminal() -> Result<(), Box<dyn std::error::Error>> {
enable_raw_mode()?;
Ok(())
}
pub fn restore_terminal() -> Result<(), Box<dyn std::error::Error>> {
disable_raw_mode()?;
Ok(())
}
pub fn get_window_size() -> Result<(u16, u16), Box<dyn std::error::Error>> {
let (w, h) = term_size()?;
Ok((w, h))
}