use nix::sys::termios;
use std::io;
use std::os::fd::BorrowedFd;
use std::os::unix::io::AsRawFd;
use std::sync::{Mutex as StdMutex, OnceLock};
static ORIGINAL_TERMIOS: OnceLock<StdMutex<Option<(i32, termios::Termios)>>> = OnceLock::new();
fn get_or_init_global() -> &'static StdMutex<Option<(i32, termios::Termios)>> {
ORIGINAL_TERMIOS.get_or_init(|| StdMutex::new(None))
}
pub fn emergency_restore() {
let lock = get_or_init_global();
if let Ok(guard) = lock.lock() {
if let Some((fd, ref original)) = *guard {
let borrowed = unsafe { BorrowedFd::borrow_raw(fd) };
let _ = termios::tcsetattr(borrowed, termios::SetArg::TCSANOW, original);
}
}
}
pub struct RawMode {
original: termios::Termios,
fd: i32,
}
impl RawMode {
pub fn enter() -> anyhow::Result<Self> {
let fd = io::stdin().as_raw_fd();
let borrowed = unsafe { BorrowedFd::borrow_raw(fd) };
let original = termios::tcgetattr(borrowed)?;
if let Ok(mut guard) = get_or_init_global().lock() {
*guard = Some((fd, original.clone()));
}
let mut raw = original.clone();
termios::cfmakeraw(&mut raw);
termios::tcsetattr(borrowed, termios::SetArg::TCSANOW, &raw)?;
Ok(Self { original, fd })
}
}
impl Drop for RawMode {
fn drop(&mut self) {
let borrowed = unsafe { BorrowedFd::borrow_raw(self.fd) };
if let Err(e) = termios::tcsetattr(borrowed, termios::SetArg::TCSANOW, &self.original) {
tracing::warn!(error = %e, "failed to restore terminal mode");
}
if let Ok(mut guard) = get_or_init_global().lock() {
*guard = None;
}
}
}