1#![forbid(unsafe_code)]
20#![warn(missing_docs)]
21
22use std::io::{self, Write};
23use termion::{clear, cursor, style};
24use tuxtui_core::backend::Backend;
25use tuxtui_core::buffer::Cell;
26use tuxtui_core::geometry::{Position, Rect};
27use tuxtui_core::style::{Color as TuxColor, Modifier, Style};
28
29pub struct TermionBackend<W: Write> {
31 writer: W,
32}
33
34impl<W: Write> TermionBackend<W> {
35 pub fn new(writer: W) -> Self {
37 Self { writer }
38 }
39
40 fn convert_fg_color(&mut self, color: TuxColor) -> io::Result<()> {
41 use termion::color::*;
42 match color {
43 TuxColor::Reset => write!(self.writer, "{}", Fg(Reset)),
44 TuxColor::Black => write!(self.writer, "{}", Fg(Black)),
45 TuxColor::Red => write!(self.writer, "{}", Fg(Red)),
46 TuxColor::Green => write!(self.writer, "{}", Fg(Green)),
47 TuxColor::Yellow => write!(self.writer, "{}", Fg(Yellow)),
48 TuxColor::Blue => write!(self.writer, "{}", Fg(Blue)),
49 TuxColor::Magenta => write!(self.writer, "{}", Fg(Magenta)),
50 TuxColor::Cyan => write!(self.writer, "{}", Fg(Cyan)),
51 TuxColor::White | TuxColor::Gray => write!(self.writer, "{}", Fg(White)),
52 TuxColor::LightRed => write!(self.writer, "{}", Fg(LightRed)),
53 TuxColor::LightGreen => write!(self.writer, "{}", Fg(LightGreen)),
54 TuxColor::LightYellow => write!(self.writer, "{}", Fg(LightYellow)),
55 TuxColor::LightBlue => write!(self.writer, "{}", Fg(LightBlue)),
56 TuxColor::LightMagenta => write!(self.writer, "{}", Fg(LightMagenta)),
57 TuxColor::LightCyan => write!(self.writer, "{}", Fg(LightCyan)),
58 TuxColor::LightGray => write!(self.writer, "{}", Fg(LightWhite)),
59 TuxColor::Indexed(i) => write!(self.writer, "{}", Fg(AnsiValue(i))),
60 TuxColor::Rgb(r, g, b) => write!(self.writer, "{}", Fg(Rgb(r, g, b))),
61 }
62 }
63
64 fn convert_bg_color(&mut self, color: TuxColor) -> io::Result<()> {
65 use termion::color::*;
66 match color {
67 TuxColor::Reset => write!(self.writer, "{}", Bg(Reset)),
68 TuxColor::Black => write!(self.writer, "{}", Bg(Black)),
69 TuxColor::Red => write!(self.writer, "{}", Bg(Red)),
70 TuxColor::Green => write!(self.writer, "{}", Bg(Green)),
71 TuxColor::Yellow => write!(self.writer, "{}", Bg(Yellow)),
72 TuxColor::Blue => write!(self.writer, "{}", Bg(Blue)),
73 TuxColor::Magenta => write!(self.writer, "{}", Bg(Magenta)),
74 TuxColor::Cyan => write!(self.writer, "{}", Bg(Cyan)),
75 TuxColor::White | TuxColor::Gray => write!(self.writer, "{}", Bg(White)),
76 TuxColor::LightRed => write!(self.writer, "{}", Bg(LightRed)),
77 TuxColor::LightGreen => write!(self.writer, "{}", Bg(LightGreen)),
78 TuxColor::LightYellow => write!(self.writer, "{}", Bg(LightYellow)),
79 TuxColor::LightBlue => write!(self.writer, "{}", Bg(LightBlue)),
80 TuxColor::LightMagenta => write!(self.writer, "{}", Bg(LightMagenta)),
81 TuxColor::LightCyan => write!(self.writer, "{}", Bg(LightCyan)),
82 TuxColor::LightGray => write!(self.writer, "{}", Bg(LightWhite)),
83 TuxColor::Indexed(i) => write!(self.writer, "{}", Bg(AnsiValue(i))),
84 TuxColor::Rgb(r, g, b) => write!(self.writer, "{}", Bg(Rgb(r, g, b))),
85 }
86 }
87
88 fn apply_modifiers(&mut self, modifiers: Modifier) -> io::Result<()> {
89 if modifiers.contains(Modifier::BOLD) {
90 write!(self.writer, "{}", style::Bold)?;
91 }
92 if modifiers.contains(Modifier::DIM) {
93 write!(self.writer, "{}", style::Faint)?;
94 }
95 if modifiers.contains(Modifier::ITALIC) {
96 write!(self.writer, "{}", style::Italic)?;
97 }
98 if modifiers.contains(Modifier::UNDERLINED) {
99 write!(self.writer, "{}", style::Underline)?;
100 }
101 if modifiers.contains(Modifier::SLOW_BLINK) {
102 write!(self.writer, "{}", style::Blink)?;
103 }
104 if modifiers.contains(Modifier::REVERSED) {
105 write!(self.writer, "{}", style::Invert)?;
106 }
107 if modifiers.contains(Modifier::CROSSED_OUT) {
108 write!(self.writer, "{}", style::CrossedOut)?;
109 }
110 Ok(())
111 }
112}
113
114impl<W: Write> Backend for TermionBackend<W> {
115 type Error = io::Error;
116
117 fn size(&self) -> Result<Rect, Self::Error> {
118 let (width, height) = termion::terminal_size()?;
119 Ok(Rect::new(0, 0, width, height))
120 }
121
122 fn clear(&mut self) -> Result<(), Self::Error> {
123 write!(self.writer, "{}", clear::All)
124 }
125
126 fn hide_cursor(&mut self) -> Result<(), Self::Error> {
127 write!(self.writer, "{}", cursor::Hide)
128 }
129
130 fn show_cursor(&mut self) -> Result<(), Self::Error> {
131 write!(self.writer, "{}", cursor::Show)
132 }
133
134 fn get_cursor(&mut self) -> Result<Position, Self::Error> {
135 Ok(Position::new(0, 0)) }
137
138 fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), Self::Error> {
139 write!(self.writer, "{}", cursor::Goto(x + 1, y + 1))
140 }
141
142 fn draw_cell(&mut self, x: u16, y: u16, cell: &Cell) -> Result<(), Self::Error> {
143 if cell.skip {
144 return Ok(());
145 }
146
147 write!(self.writer, "{}", cursor::Goto(x + 1, y + 1))?;
148
149 if let Some(fg) = cell.style.fg {
150 self.convert_fg_color(fg)?;
151 }
152 if let Some(bg) = cell.style.bg {
153 self.convert_bg_color(bg)?;
154 }
155
156 self.apply_modifiers(cell.style.add_modifier)?;
157
158 write!(self.writer, "{}", cell.symbol)?;
159 write!(self.writer, "{}", style::Reset)?;
160
161 Ok(())
162 }
163
164 fn set_style(&mut self, style: Style) -> Result<(), Self::Error> {
165 if let Some(fg) = style.fg {
166 self.convert_fg_color(fg)?;
167 }
168 if let Some(bg) = style.bg {
169 self.convert_bg_color(bg)?;
170 }
171 self.apply_modifiers(style.add_modifier)?;
172 Ok(())
173 }
174
175 fn reset_style(&mut self) -> Result<(), Self::Error> {
176 write!(self.writer, "{}", style::Reset)
177 }
178
179 fn flush(&mut self) -> Result<(), Self::Error> {
180 self.writer.flush()
181 }
182
183 fn enable_raw_mode(&mut self) -> Result<(), Self::Error> {
184 Ok(())
186 }
187
188 fn disable_raw_mode(&mut self) -> Result<(), Self::Error> {
189 Ok(())
191 }
192
193 fn enter_alternate_screen(&mut self) -> Result<(), Self::Error> {
194 write!(self.writer, "{}", termion::screen::ToAlternateScreen)
195 }
196
197 fn leave_alternate_screen(&mut self) -> Result<(), Self::Error> {
198 write!(self.writer, "{}", termion::screen::ToMainScreen)
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn test_backend_creation() {
208 let buffer = Vec::new();
209 let _backend = TermionBackend::new(buffer);
210 }
211}