use std::io::{Stdout, stdout};
use std::ops::{Deref, DerefMut};
use color_eyre::Result;
use ratatui::Terminal;
use ratatui::backend::CrosstermBackend;
use ratatui::crossterm::{
event::{DisableMouseCapture, EnableMouseCapture},
execute,
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
};
pub(crate) type Backend = CrosstermBackend<Stdout>;
pub(crate) struct Tui {
terminal: Terminal<Backend>,
}
impl Tui {
pub(crate) fn new() -> Result<Self> {
let terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
Ok(Self { terminal })
}
pub(crate) fn enter(&mut self) -> Result<()> {
enable_raw_mode()?;
execute!(stdout(), EnterAlternateScreen, EnableMouseCapture)?;
Self::install_panic_hook();
self.terminal.clear()?;
Ok(())
}
pub(crate) fn exit() {
Self::restore();
}
fn restore() {
let _ = execute!(stdout(), LeaveAlternateScreen, DisableMouseCapture);
let _ = disable_raw_mode();
}
fn install_panic_hook() {
let hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
Self::restore();
hook(info);
}));
}
pub(crate) fn suspend(&mut self) -> Result<()> {
Self::restore();
#[cfg(unix)]
signal_hook::low_level::raise(signal_hook::consts::SIGTSTP)?;
enable_raw_mode()?;
execute!(stdout(), EnterAlternateScreen, EnableMouseCapture)?;
self.terminal.clear()?;
Ok(())
}
}
impl Deref for Tui {
type Target = Terminal<Backend>;
fn deref(&self) -> &Self::Target {
&self.terminal
}
}
impl DerefMut for Tui {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.terminal
}
}
impl Drop for Tui {
fn drop(&mut self) {
Self::restore();
}
}