use std::io::Stdout;
use anyhow::Result;
use crossterm::{
event::{
DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste,
EnableFocusChange, EnableMouseCapture, KeyboardEnhancementFlags,
PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use ratatui::{backend::CrosstermBackend, Terminal};
pub struct TerminalSetup {
pub terminal: Terminal<CrosstermBackend<Stdout>>,
pub keyboard_enhancement: bool,
}
pub fn setup() -> Result<TerminalSetup> {
enable_raw_mode()?;
let mut stdout = std::io::stdout();
execute!(stdout, EnterAlternateScreen)?;
let _ = execute!(stdout, EnableBracketedPaste);
let _ = execute!(stdout, EnableFocusChange);
let keyboard_enhancement = crossterm::terminal::supports_keyboard_enhancement()
.inspect_err(|e| tracing::warn!(error = %e, "keyboard enhancement probe failed"))
.unwrap_or(false);
if keyboard_enhancement {
let _ = execute!(
stdout,
PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES)
);
}
let backend = CrosstermBackend::new(stdout);
let terminal = Terminal::new(backend)?;
Ok(TerminalSetup {
terminal,
keyboard_enhancement,
})
}
pub fn enable_mouse() -> Result<()> {
execute!(std::io::stdout(), EnableMouseCapture)?;
Ok(())
}
pub fn disable_mouse() {
let _ = execute!(std::io::stdout(), DisableMouseCapture);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PointerShape {
Text,
Hand,
Default,
}
pub fn set_pointer_shape(shape: PointerShape) {
use std::io::Write;
let (x11, css) = match shape {
PointerShape::Text => ("xterm", "text"),
PointerShape::Hand => ("hand2", "pointer"),
PointerShape::Default => ("left_ptr", "default"),
};
let mut stdout = std::io::stdout();
let _ = write!(stdout, "\x1b]22;{x11}\x07\x1b]22;{css}\x07");
let _ = stdout.flush();
}
pub fn restore() -> Result<()> {
set_pointer_shape(PointerShape::Default);
disable_mouse();
let _ = execute!(std::io::stdout(), DisableBracketedPaste);
let _ = execute!(std::io::stdout(), DisableFocusChange);
let _ = execute!(std::io::stdout(), PopKeyboardEnhancementFlags);
disable_raw_mode()?;
execute!(std::io::stdout(), LeaveAlternateScreen)?;
Ok(())
}
pub fn re_enter(mouse: bool, keyboard_enhancement: bool) -> Result<()> {
enable_raw_mode()?;
let mut stdout = std::io::stdout();
execute!(stdout, EnterAlternateScreen)?;
let _ = execute!(stdout, EnableBracketedPaste);
let _ = execute!(stdout, EnableFocusChange);
if keyboard_enhancement {
let _ = execute!(
stdout,
PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES)
);
}
if mouse {
let _ = execute!(stdout, EnableMouseCapture);
}
Ok(())
}