//! Terminal setup and teardown. use std::io::{self, stdout}; use ratatui::DefaultTerminal; use ratatui::crossterm::event::{ KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags, }; use ratatui::crossterm::{execute, terminal}; /// What this terminal can tell us about the keyboard. #[derive(Clone, Copy, Debug)] pub struct KeyboardCaps { /// The terminal reports key releases (the kitty keyboard protocol), so a /// scene can know a key is being held. Without it, holding a key looks /// like one press, a ~0.5 s pause, then auto-repeats, and real-time /// controls need a fallback such as a throttle you step up and down. pub key_release: bool, } /// Raw mode, alternate screen, and the kitty keyboard protocol where available. pub fn init() -> io::Result<(DefaultTerminal, KeyboardCaps)> { let terminal = ratatui::try_init()?; let key_release = terminal::supports_keyboard_enhancement().unwrap_or(false); if key_release { execute!( stdout(), PushKeyboardEnhancementFlags( KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES | KeyboardEnhancementFlags::REPORT_EVENT_TYPES ) )?; // ratatui's panic hook restores raw mode and the alternate screen but // not these flags, which would leave the shell receiving kitty-encoded // keys. Pop them first, then hand over to ratatui's hook. let previous = std::panic::take_hook(); std::panic::set_hook(Box::new(move |info| { let _ = execute!(stdout(), PopKeyboardEnhancementFlags); previous(info); })); } Ok((terminal, KeyboardCaps { key_release })) } pub fn restore(caps: KeyboardCaps) { if caps.key_release { let _ = execute!(stdout(), PopKeyboardEnhancementFlags); } ratatui::restore(); }