use crate::program::MouseMode;
use crossterm::{
cursor, execute,
terminal::{
self, disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
},
};
use std::io::{self, Stdout, Write};
#[derive(Debug, Clone)]
pub struct TerminalConfig {
pub alt_screen: bool,
pub mouse_mode: MouseMode,
pub bracketed_paste: bool,
pub focus_reporting: bool,
pub headless: bool,
}
impl Default for TerminalConfig {
fn default() -> Self {
Self {
alt_screen: true,
mouse_mode: MouseMode::None,
bracketed_paste: false,
focus_reporting: false,
headless: false,
}
}
}
pub struct TerminalManager {
stdout: Option<Stdout>,
config: TerminalConfig,
is_released: bool,
}
impl TerminalManager {
pub fn new(config: TerminalConfig) -> io::Result<Self> {
let headless = config.headless;
let mut manager = Self {
stdout: if !headless { Some(io::stdout()) } else { None },
config,
is_released: false,
};
if !headless {
manager.setup()?;
}
Ok(manager)
}
fn setup(&mut self) -> io::Result<()> {
if let Some(ref mut stdout) = self.stdout {
enable_raw_mode()?;
if self.config.alt_screen {
execute!(stdout, EnterAlternateScreen)?;
}
execute!(stdout, terminal::Clear(terminal::ClearType::All))?;
execute!(stdout, cursor::MoveTo(0, 0))?;
execute!(stdout, cursor::Hide)?;
match self.config.mouse_mode {
MouseMode::CellMotion => {
execute!(stdout, crossterm::event::EnableMouseCapture)?;
}
MouseMode::AllMotion => {
execute!(stdout, crossterm::event::EnableMouseCapture,)?;
}
MouseMode::None => {}
}
if self.config.bracketed_paste {
execute!(stdout, crossterm::event::EnableBracketedPaste)?;
}
if self.config.focus_reporting {
execute!(stdout, crossterm::event::EnableFocusChange)?;
}
stdout.flush()?;
}
Ok(())
}
pub fn cleanup(&mut self) -> io::Result<()> {
if self.is_released || self.config.headless {
return Ok(());
}
if let Some(ref mut stdout) = self.stdout {
execute!(stdout, cursor::Show)?;
if self.config.focus_reporting {
execute!(stdout, crossterm::event::DisableFocusChange)?;
}
if self.config.bracketed_paste {
execute!(stdout, crossterm::event::DisableBracketedPaste)?;
}
if self.config.mouse_mode != MouseMode::None {
execute!(stdout, crossterm::event::DisableMouseCapture)?;
}
if self.config.alt_screen {
execute!(stdout, LeaveAlternateScreen)?;
}
disable_raw_mode()?;
stdout.flush()?;
}
self.is_released = true;
Ok(())
}
pub fn stdout_mut(&mut self) -> Option<&mut Stdout> {
self.stdout.as_mut()
}
pub fn is_setup(&self) -> bool {
!self.is_released && self.stdout.is_some()
}
pub fn size(&self) -> io::Result<(u16, u16)> {
terminal::size()
}
pub fn release(&mut self) -> io::Result<()> {
self.cleanup()
}
pub fn restore(&mut self) -> io::Result<()> {
self.cleanup()
}
}
impl Drop for TerminalManager {
fn drop(&mut self) {
let _ = self.cleanup();
}
}