use super::draw::Drawer;
pub(crate) fn restore_terminal() {
use std::io::Write;
let_go(&mut std::io::stdout());
if let Err(e) = ratatui::try_restore() {
let _ = writeln!(std::io::stderr(), "Failed to restore terminal: {e}");
}
}
fn let_go(out: &mut impl std::io::Write) {
let _ = crossterm::execute!(out, crossterm::event::DisableMouseCapture);
let _ = crossterm::execute!(out, crossterm::event::DisableFocusChange);
let _ = crossterm::execute!(out, crossterm::event::PopKeyboardEnhancementFlags);
}
pub(crate) fn push_keyboard_flags() {
let _ = crossterm::execute!(
std::io::stdout(),
crossterm::event::PushKeyboardEnhancementFlags(
crossterm::event::KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
)
);
}
pub(crate) fn follow_focus(out: &mut impl std::io::Write) {
let _ = crossterm::execute!(out, crossterm::event::EnableFocusChange);
}
pub(crate) struct QuietTerminal(pub(crate) Option<ratatui::DefaultTerminal>, Drawer);
impl QuietTerminal {
pub(crate) fn new(terminal: ratatui::DefaultTerminal) -> Self {
Self(Some(terminal), Drawer::new(false))
}
pub(crate) fn draw(&mut self, render: impl FnOnce(&mut ratatui::Frame)) -> std::io::Result<()> {
let terminal = self.0.as_mut().expect("held until dropped");
self.1.draw(terminal, render)
}
pub(crate) fn clear(&mut self) -> std::io::Result<()> {
let terminal = self.0.as_mut().expect("held until dropped");
self.1.clear(terminal)
}
pub(crate) fn repaint(&mut self) {
self.1.repaint();
}
pub(crate) fn scroll_with_region(&mut self, on: bool) {
self.1.set_scroll(on);
}
}
impl Drop for QuietTerminal {
fn drop(&mut self) {
if let Some(mut terminal) = self.0.take()
&& terminal.show_cursor().is_err()
{
std::mem::forget(terminal);
}
}
}
pub(crate) struct TakenTerminal {
pub(crate) restored: bool,
}
impl TakenTerminal {
pub(crate) fn restore(&mut self) {
if !std::mem::replace(&mut self.restored, true) {
restore_terminal();
}
}
}
impl Drop for TakenTerminal {
fn drop(&mut self) {
if !std::thread::panicking() {
self.restore();
}
}
}
#[cfg(all(test, unix))]
mod tests {
use super::*;
#[test]
fn letting_go_turns_off_focus_reports() {
let mut out = Vec::new();
let_go(&mut out);
let out = String::from_utf8(out).unwrap();
assert!(out.contains("\x1b[?1004l"), "{out:?}");
assert!(out.contains("\x1b[<1u"), "{out:?}");
let mut on = Vec::new();
follow_focus(&mut on);
assert_eq!(on, b"\x1b[?1004h");
}
}