diff options
Diffstat (limited to 'src/term.rs')
| -rw-r--r-- | src/term.rs | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/src/term.rs b/src/term.rs new file mode 100644 index 0000000..b11bbdc --- /dev/null +++ b/src/term.rs @@ -0,0 +1,50 @@ +//! 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(); +} |