use std::io::{Stdout, Write};
use termion::input::MouseTerminal;
use termion::raw::{IntoRawMode as _, RawTerminal};
use termion::screen::{AlternateScreen, IntoAlternateScreen as _};
use super::{TerminalAdapter, TerminalResult};
use crate::ratatui::{Terminal, TerminalOptions, backend};
use crate::terminal::TerminalError;
pub type TermionBackend = Terminal<backend::TermionBackend<TermionWrapper>>;
pub enum TermionWrapper {
Nothing(Stdout),
Raw(RawTerminal<Stdout>),
AlternateRaw(AlternateScreen<RawTerminal<Stdout>>),
MouseAlternateRaw(MouseTerminal<AlternateScreen<RawTerminal<Stdout>>>),
MouseRaw(MouseTerminal<RawTerminal<Stdout>>),
}
impl Write for TermionWrapper {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
match self {
TermionWrapper::Nothing(stdout) => stdout.write(buf),
TermionWrapper::Raw(raw_terminal) => raw_terminal.write(buf),
TermionWrapper::AlternateRaw(alternate_screen) => alternate_screen.write(buf),
TermionWrapper::MouseAlternateRaw(mouse_terminal) => mouse_terminal.write(buf),
TermionWrapper::MouseRaw(mouse_terminal) => mouse_terminal.write(buf),
}
}
fn flush(&mut self) -> std::io::Result<()> {
match self {
TermionWrapper::Nothing(stdout) => stdout.flush(),
TermionWrapper::Raw(raw_terminal) => raw_terminal.flush(),
TermionWrapper::AlternateRaw(alternate_screen) => alternate_screen.flush(),
TermionWrapper::MouseAlternateRaw(mouse_terminal) => mouse_terminal.flush(),
TermionWrapper::MouseRaw(mouse_terminal) => mouse_terminal.flush(),
}
}
}
pub struct TermionTerminalAdapter {
terminal: TermionBackend,
}
impl TermionTerminalAdapter {
pub fn new_nothing() -> TerminalResult<Self> {
Self::new_nothing_with_options(TerminalOptions::default())
}
pub fn new_nothing_with_options(options: TerminalOptions) -> TerminalResult<Self> {
let stdout = std::io::stdout();
let stdout = TermionWrapper::Nothing(stdout);
let terminal = Terminal::with_options(backend::TermionBackend::new(stdout), options)
.map_err(|_| TerminalError::CannotConnectStdout)?;
Ok(Self { terminal })
}
pub fn new_raw() -> TerminalResult<Self> {
Self::new_raw_with_options(TerminalOptions::default())
}
pub fn new_raw_with_options(options: TerminalOptions) -> TerminalResult<Self> {
let stdout = std::io::stdout()
.into_raw_mode()
.map_err(|_| TerminalError::CannotConnectStdout)?;
let stdout = TermionWrapper::Raw(stdout);
let terminal = Terminal::with_options(backend::TermionBackend::new(stdout), options)
.map_err(|_| TerminalError::CannotConnectStdout)?;
Ok(Self { terminal })
}
pub fn new_alternate_raw() -> TerminalResult<Self> {
Self::new_alternate_raw_with_options(TerminalOptions::default())
}
pub fn new_alternate_raw_with_options(options: TerminalOptions) -> TerminalResult<Self> {
let stdout = std::io::stdout()
.into_raw_mode()
.map_err(|_| TerminalError::CannotConnectStdout)?
.into_alternate_screen()
.map_err(|_| TerminalError::CannotConnectStdout)?;
let stdout = TermionWrapper::AlternateRaw(stdout);
let terminal = Terminal::with_options(backend::TermionBackend::new(stdout), options)
.map_err(|_| TerminalError::CannotConnectStdout)?;
Ok(Self { terminal })
}
pub fn new_mouse_alternate_raw() -> TerminalResult<Self> {
Self::new_mouse_alternate_raw_with_options(TerminalOptions::default())
}
pub fn new_mouse_alternate_raw_with_options(options: TerminalOptions) -> TerminalResult<Self> {
let stdout = std::io::stdout()
.into_raw_mode()
.map_err(|_| TerminalError::CannotConnectStdout)?
.into_alternate_screen()
.map_err(|_| TerminalError::CannotConnectStdout)?;
let stdout = TermionWrapper::MouseAlternateRaw(MouseTerminal::from(stdout));
let terminal = Terminal::with_options(backend::TermionBackend::new(stdout), options)
.map_err(|_| TerminalError::CannotConnectStdout)?;
Ok(Self { terminal })
}
pub fn new_mouse_raw() -> TerminalResult<Self> {
Self::new_mouse_raw_with_options(TerminalOptions::default())
}
pub fn new_mouse_raw_with_options(options: TerminalOptions) -> TerminalResult<Self> {
let stdout = std::io::stdout()
.into_raw_mode()
.map_err(|_| TerminalError::CannotConnectStdout)?;
let stdout = TermionWrapper::MouseRaw(MouseTerminal::from(stdout));
let terminal = Terminal::with_options(backend::TermionBackend::new(stdout), options)
.map_err(|_| TerminalError::CannotConnectStdout)?;
Ok(Self { terminal })
}
}
impl TerminalAdapter for TermionTerminalAdapter {
type Backend = backend::TermionBackend<TermionWrapper>;
fn enable_raw_mode(&mut self) -> TerminalResult<()> {
Err(TerminalError::Unsupported)
}
fn disable_raw_mode(&mut self) -> TerminalResult<()> {
Err(TerminalError::Unsupported)
}
fn enter_alternate_screen(&mut self) -> TerminalResult<()> {
Err(TerminalError::Unsupported)
}
fn leave_alternate_screen(&mut self) -> TerminalResult<()> {
Err(TerminalError::Unsupported)
}
fn enable_mouse_capture(&mut self) -> TerminalResult<()> {
Err(TerminalError::Unsupported)
}
fn disable_mouse_capture(&mut self) -> TerminalResult<()> {
Err(TerminalError::Unsupported)
}
fn raw(&self) -> &TermionBackend {
&self.terminal
}
fn raw_mut(&mut self) -> &mut TermionBackend {
&mut self.terminal
}
}