1use std::io;
2use std::sync::Arc;
3use std::sync::atomic::AtomicBool;
4
5use crossterm::cursor::{Hide, Show};
6use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
7
8pub struct TerminalGuard;
11
12impl TerminalGuard {
13 pub fn enter() -> io::Result<Self> {
14 enable_raw_mode()?;
15 crossterm::execute!(io::stdout(), EnterAlternateScreen, Hide)?;
16 Ok(TerminalGuard)
17 }
18}
19
20impl Drop for TerminalGuard {
21 fn drop(&mut self) {
22 let _ = crossterm::execute!(io::stdout(), Show, LeaveAlternateScreen);
23 let _ = disable_raw_mode();
24 }
25}
26
27pub fn restore_terminal_best_effort() {
29 let _ = crossterm::execute!(io::stdout(), Show, LeaveAlternateScreen);
30 let _ = disable_raw_mode();
31}
32
33pub fn install_panic_hook() {
34 let prev = std::panic::take_hook();
35 std::panic::set_hook(Box::new(move |info| {
36 restore_terminal_best_effort();
37 prev(info);
38 }));
39}
40
41pub fn install_signal_flag() -> Arc<AtomicBool> {
43 let flag = Arc::new(AtomicBool::new(false));
44 let _ = signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&flag));
45 let _ = signal_hook::flag::register(signal_hook::consts::SIGHUP, Arc::clone(&flag));
46 flag
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52 use std::sync::atomic::Ordering;
53
54 #[test]
55 fn signal_flag_starts_false() {
56 let f = install_signal_flag();
57 assert!(!f.load(Ordering::SeqCst));
58 }
59
60 #[test]
61 fn restore_is_idempotent() {
62 restore_terminal_best_effort();
64 restore_terminal_best_effort();
65 }
66}