use std::io::{self, Stdout};
use crossterm::execute;
use crossterm::terminal::{
EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode,
};
use ratatui::Terminal;
use ratatui::backend::CrosstermBackend;
pub type Tty = Terminal<CrosstermBackend<Stdout>>;
pub struct TerminalGuard {
pub tty: Tty,
}
impl std::fmt::Debug for TerminalGuard {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TerminalGuard").finish_non_exhaustive()
}
}
impl TerminalGuard {
pub fn init() -> io::Result<Self> {
install_panic_hook();
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout);
let tty = Terminal::new(backend)?;
Ok(Self { tty })
}
}
impl Drop for TerminalGuard {
fn drop(&mut self) {
let _ = restore();
}
}
fn restore() -> io::Result<()> {
let mut stdout = io::stdout();
execute!(stdout, LeaveAlternateScreen)?;
disable_raw_mode()?;
Ok(())
}
fn install_panic_hook() {
use std::sync::Once;
static ONCE: Once = Once::new();
ONCE.call_once(|| {
let original = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let _ = restore();
original(info);
}));
});
}