use std::io::{self, Stdout, Write};
use crossterm::cursor::{Hide, Show};
use crossterm::event::{DisableBracketedPaste, EnableBracketedPaste};
use crossterm::execute;
use crossterm::terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
};
use ratatui::backend::CrosstermBackend;
use ratatui::{Frame, Terminal};
type CrosstermTerminal = Terminal<CrosstermBackend<Stdout>>;
pub(crate) struct TerminalSession {
terminal: CrosstermTerminal,
active: bool,
}
impl TerminalSession {
pub(crate) fn enter() -> io::Result<Self> {
enable_raw_mode()?;
let mut stdout = io::stdout();
if let Err(error) = execute!(stdout, EnterAlternateScreen, EnableBracketedPaste, Hide) {
let _ = disable_raw_mode();
return Err(error);
}
let terminal = match Terminal::new(CrosstermBackend::new(stdout)) {
Ok(terminal) => terminal,
Err(error) => {
restore_process_terminal();
return Err(error);
}
};
Ok(Self {
terminal,
active: true,
})
}
pub(crate) fn draw(&mut self, render: impl FnOnce(&mut Frame<'_>)) -> io::Result<()> {
self.terminal.draw(render).map(|_| ())
}
pub(crate) fn resize(&mut self) -> io::Result<()> {
self.terminal.autoresize()
}
pub(crate) fn restore(&mut self) -> io::Result<()> {
if !self.active {
return Ok(());
}
self.active = false;
let mut first_error = self.terminal.show_cursor().err();
if let Err(error) = execute!(
self.terminal.backend_mut(),
DisableBracketedPaste,
LeaveAlternateScreen,
Show
) {
first_error.get_or_insert(error);
}
if let Err(error) = self.terminal.backend_mut().flush() {
first_error.get_or_insert(error);
}
if let Err(error) = disable_raw_mode() {
first_error.get_or_insert(error);
}
match first_error {
Some(error) => Err(error),
None => Ok(()),
}
}
}
impl Drop for TerminalSession {
fn drop(&mut self) {
let _ = self.restore();
}
}
fn restore_process_terminal() {
let mut stdout = io::stdout();
let _ = execute!(stdout, DisableBracketedPaste, LeaveAlternateScreen, Show);
let _ = stdout.flush();
let _ = disable_raw_mode();
}
#[cfg(all(test, windows))]
#[path = "terminal_windows_tests.rs"]
mod tests;