1use std::io::{self, Stdout};
9
10use crossterm::execute;
11use crossterm::terminal::{
12 EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode,
13};
14use ratatui::Terminal;
15use ratatui::backend::CrosstermBackend;
16
17pub type Tty = Terminal<CrosstermBackend<Stdout>>;
18
19pub struct TerminalGuard {
23 pub tty: Tty,
24}
25
26impl std::fmt::Debug for TerminalGuard {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 f.debug_struct("TerminalGuard").finish_non_exhaustive()
29 }
30}
31
32impl TerminalGuard {
33 pub fn init() -> io::Result<Self> {
39 install_panic_hook();
40 enable_raw_mode()?;
41 let mut stdout = io::stdout();
42 execute!(stdout, EnterAlternateScreen)?;
43 let backend = CrosstermBackend::new(stdout);
44 let tty = Terminal::new(backend)?;
45 Ok(Self { tty })
46 }
47}
48
49impl Drop for TerminalGuard {
50 fn drop(&mut self) {
51 let _ = restore();
52 }
53}
54
55fn restore() -> io::Result<()> {
57 let mut stdout = io::stdout();
58 execute!(stdout, LeaveAlternateScreen)?;
59 disable_raw_mode()?;
60 Ok(())
61}
62
63fn install_panic_hook() {
66 use std::sync::Once;
67 static ONCE: Once = Once::new();
68 ONCE.call_once(|| {
69 let original = std::panic::take_hook();
70 std::panic::set_hook(Box::new(move |info| {
71 let _ = restore();
72 original(info);
73 }));
74 });
75}