use std::io::{stdout, Write};
use std::panic;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Once;
use crossterm::cursor::{Hide, Show};
use crossterm::event::{
DisableMouseCapture, EnableMouseCapture, KeyboardEnhancementFlags,
PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
};
use crossterm::terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
};
use crossterm::ExecutableCommand;
static PANIC_HOOK: Once = Once::new();
static KEYBOARD_ENHANCED: AtomicBool = AtomicBool::new(false);
fn install_panic_hook() {
PANIC_HOOK.call_once(|| {
let default = panic::take_hook();
panic::set_hook(Box::new(move |info| {
let _ = restore_terminal();
default(info);
}));
});
}
pub fn enter() -> anyhow::Result<bool> {
install_panic_hook();
enable_raw_mode()?;
let mut out = stdout();
let enhanced = matches!(
crossterm::terminal::supports_keyboard_enhancement(),
Ok(true)
);
KEYBOARD_ENHANCED.store(enhanced, Ordering::SeqCst);
if enhanced {
let _ = out.execute(PushKeyboardEnhancementFlags(
KeyboardEnhancementFlags::REPORT_EVENT_TYPES
| KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES,
));
}
out.execute(EnterAlternateScreen)?;
let _ = out.execute(EnableMouseCapture);
out.execute(Hide)?;
let _ = out.flush();
Ok(enhanced)
}
pub fn restore_terminal() -> anyhow::Result<()> {
let mut out = stdout();
let _ = out.execute(PopKeyboardEnhancementFlags);
let _ = write!(out, "\x1b[=0u");
KEYBOARD_ENHANCED.store(false, Ordering::SeqCst);
let _ = out.execute(DisableMouseCapture);
let _ = out.execute(Show);
let _ = out.execute(LeaveAlternateScreen);
let _ = disable_raw_mode();
let _ = out.flush();
Ok(())
}
pub struct Guard;
impl Drop for Guard {
fn drop(&mut self) {
let _ = restore_terminal();
}
}