#[cfg(all(not(feature = "crossterm"), feature = "termion"))]
use std::os::fd::AsFd;
use std::{fmt::Display, io};
#[cfg(feature = "crossterm")]
#[cfg_attr(docsrs, doc(cfg(feature = "crossterm")))]
pub fn get_backend<W: io::Write>(buf: W) -> impl Backend {
CrosstermBackend::new(buf)
}
#[cfg(all(not(feature = "crossterm"), feature = "termion"))]
#[cfg_attr(docsrs, doc(cfg(feature = "termion")))]
pub fn get_backend<W>(buf: W) -> impl Backend
where
W: io::Write + AsFd,
{
TermionBackend::new(buf)
}
mod test_backend;
pub use test_backend::TestBackend;
#[cfg(feature = "termion")]
mod termion;
#[cfg(feature = "termion")]
pub use self::termion::{TermionBackend, TermionDisplayBackend};
#[cfg(feature = "crossterm")]
mod crossterm;
#[cfg(feature = "crossterm")]
pub use self::crossterm::CrosstermBackend;
use crate::style::{Attributes, Color, Styled};
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Default)]
#[allow(missing_docs)]
pub struct Size {
pub width: u16,
pub height: u16,
}
impl Size {
pub fn area(self) -> u16 {
self.width * self.height
}
}
impl From<(u16, u16)> for Size {
fn from((width, height): (u16, u16)) -> Self {
Size { width, height }
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum ClearType {
All,
FromCursorDown,
FromCursorUp,
CurrentLine,
UntilNewLine,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum MoveDirection {
Up(u16),
Down(u16),
Left(u16),
Right(u16),
NextLine(u16),
PrevLine(u16),
Column(u16),
}
pub trait DisplayBackend: io::Write {
fn set_attributes(&mut self, attributes: Attributes) -> io::Result<()>;
fn set_fg(&mut self, color: Color) -> io::Result<()>;
fn set_bg(&mut self, color: Color) -> io::Result<()>;
fn write_styled(&mut self, styled: &Styled<dyn Display + '_>) -> io::Result<()> {
styled.write(self)
}
}
pub trait Backend: DisplayBackend {
fn enable_raw_mode(&mut self) -> io::Result<()>;
fn disable_raw_mode(&mut self) -> io::Result<()>;
fn hide_cursor(&mut self) -> io::Result<()>;
fn show_cursor(&mut self) -> io::Result<()>;
fn get_cursor_pos(&mut self) -> io::Result<(u16, u16)>;
fn move_cursor_to(&mut self, x: u16, y: u16) -> io::Result<()>;
fn move_cursor(&mut self, direction: MoveDirection) -> io::Result<()> {
default_move_cursor(self, direction)
}
fn scroll(&mut self, dist: i16) -> io::Result<()>;
fn clear(&mut self, clear_type: ClearType) -> io::Result<()>;
fn size(&self) -> io::Result<Size>;
}
fn default_move_cursor<B: Backend + ?Sized>(
backend: &mut B,
direction: MoveDirection,
) -> io::Result<()> {
let (mut x, mut y) = backend.get_cursor_pos()?;
match direction {
MoveDirection::Up(dy) => y = y.saturating_sub(dy),
MoveDirection::Down(dy) => y = y.saturating_add(dy),
MoveDirection::Left(dx) => x = x.saturating_sub(dx),
MoveDirection::Right(dx) => x = x.saturating_add(dx),
MoveDirection::NextLine(dy) => {
x = 0;
y = y.saturating_add(dy);
}
MoveDirection::Column(new_x) => x = new_x,
MoveDirection::PrevLine(dy) => {
x = 0;
y = y.saturating_sub(dy);
}
}
backend.move_cursor_to(x, y)
}
impl<B: DisplayBackend> DisplayBackend for &mut B {
fn set_attributes(&mut self, attributes: Attributes) -> io::Result<()> {
(**self).set_attributes(attributes)
}
fn set_fg(&mut self, color: Color) -> io::Result<()> {
(**self).set_fg(color)
}
fn set_bg(&mut self, color: Color) -> io::Result<()> {
(**self).set_bg(color)
}
fn write_styled(&mut self, styled: &Styled<dyn Display + '_>) -> io::Result<()> {
(**self).write_styled(styled)
}
}
impl<B: Backend> Backend for &mut B {
fn enable_raw_mode(&mut self) -> io::Result<()> {
(**self).enable_raw_mode()
}
fn disable_raw_mode(&mut self) -> io::Result<()> {
(**self).disable_raw_mode()
}
fn hide_cursor(&mut self) -> io::Result<()> {
(**self).hide_cursor()
}
fn show_cursor(&mut self) -> io::Result<()> {
(**self).show_cursor()
}
fn get_cursor_pos(&mut self) -> io::Result<(u16, u16)> {
(**self).get_cursor_pos()
}
fn move_cursor_to(&mut self, x: u16, y: u16) -> io::Result<()> {
(**self).move_cursor_to(x, y)
}
fn move_cursor(&mut self, direction: MoveDirection) -> io::Result<()> {
(**self).move_cursor(direction)
}
fn scroll(&mut self, dist: i16) -> io::Result<()> {
(**self).scroll(dist)
}
fn clear(&mut self, clear_type: ClearType) -> io::Result<()> {
(**self).clear(clear_type)
}
fn size(&self) -> io::Result<Size> {
(**self).size()
}
}