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 {
15 mouse: bool,
16 alt_screen: bool,
17}
18
19impl TerminalGuard {
20 pub fn enter(mouse: bool, with_alt_screen: bool) -> io::Result<Self> {
21 enable_raw_mode()?;
22 if with_alt_screen {
23 crossterm::execute!(io::stdout(), EnterAlternateScreen, Hide)?;
24 } else {
25 crossterm::execute!(io::stdout(), Hide)?;
26 }
27 if mouse {
28 crossterm::execute!(io::stdout(), crossterm::event::EnableMouseCapture)?;
29 }
30 Ok(TerminalGuard { mouse, alt_screen: with_alt_screen })
31 }
32}
33
34impl Drop for TerminalGuard {
35 fn drop(&mut self) {
36 if self.mouse {
37 let _ = crossterm::execute!(io::stdout(), crossterm::event::DisableMouseCapture);
38 }
39 if self.alt_screen {
40 let _ = crossterm::execute!(io::stdout(), Show, LeaveAlternateScreen);
41 } else {
42 let _ = crossterm::execute!(io::stdout(), Show);
43 }
44 let _ = disable_raw_mode();
45 }
46}
47
48pub fn restore_terminal_best_effort() {
50 let _ = crossterm::execute!(io::stdout(), Show, LeaveAlternateScreen);
51 let _ = disable_raw_mode();
52}
53
54pub fn install_panic_hook() {
55 let prev = std::panic::take_hook();
56 std::panic::set_hook(Box::new(move |info| {
57 restore_terminal_best_effort();
58 prev(info);
59 }));
60}
61
62pub fn install_signal_flag() -> Arc<AtomicBool> {
64 let flag = Arc::new(AtomicBool::new(false));
65 let _ = signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&flag));
66 let _ = signal_hook::flag::register(signal_hook::consts::SIGHUP, Arc::clone(&flag));
67 flag
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use std::sync::atomic::Ordering;
74
75 #[test]
76 fn signal_flag_starts_false() {
77 let f = install_signal_flag();
78 assert!(!f.load(Ordering::SeqCst));
79 }
80
81 #[test]
82 fn restore_is_idempotent() {
83 restore_terminal_best_effort();
85 restore_terminal_best_effort();
86 }
87}