1use crate::{
2 backend::Backend,
3 buffer::Cell,
4 layout::Rect,
5 style::{Color, Modifier},
6};
7use crossterm::{
8 cursor::{Hide, MoveTo, Show},
9 execute, queue,
10 style::{
11 Attribute as CAttribute, Color as CColor, Print, SetAttribute, SetBackgroundColor,
12 SetForegroundColor,
13 },
14 terminal::{self, Clear, ClearType},
15};
16use std::io::{self, Write};
17
18pub struct CrosstermBackend<W: Write> {
19 buffer: W,
20}
21
22impl<W> CrosstermBackend<W>
23where
24 W: Write,
25{
26 pub fn new(buffer: W) -> CrosstermBackend<W> {
27 CrosstermBackend { buffer }
28 }
29}
30
31impl<W> Write for CrosstermBackend<W>
32where
33 W: Write,
34{
35 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
36 self.buffer.write(buf)
37 }
38
39 fn flush(&mut self) -> io::Result<()> {
40 self.buffer.flush()
41 }
42}
43
44impl<W> Backend for CrosstermBackend<W>
45where
46 W: Write,
47{
48 fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
49 where
50 I: Iterator<Item = (u16, u16, &'a Cell)>,
51 {
52 let mut fg = Color::Reset;
53 let mut bg = Color::Reset;
54 let mut modifier = Modifier::empty();
55 let mut last_pos: Option<(u16, u16)> = None;
56 for (x, y, cell) in content {
57 if !matches!(last_pos, Some(p) if x == p.0 + 1 && y == p.1) {
59 map_error(queue!(self.buffer, MoveTo(x, y)))?;
60 }
61 last_pos = Some((x, y));
62 if cell.modifier != modifier {
63 let diff = ModifierDiff {
64 from: modifier,
65 to: cell.modifier,
66 };
67 diff.queue(&mut self.buffer)?;
68 modifier = cell.modifier;
69 }
70 if cell.fg != fg {
71 let color = CColor::from(cell.fg);
72 map_error(queue!(self.buffer, SetForegroundColor(color)))?;
73 fg = cell.fg;
74 }
75 if cell.bg != bg {
76 let color = CColor::from(cell.bg);
77 map_error(queue!(self.buffer, SetBackgroundColor(color)))?;
78 bg = cell.bg;
79 }
80
81 map_error(queue!(self.buffer, Print(&cell.symbol)))?;
82 }
83
84 map_error(queue!(
85 self.buffer,
86 SetForegroundColor(CColor::Reset),
87 SetBackgroundColor(CColor::Reset),
88 SetAttribute(CAttribute::Reset)
89 ))
90 }
91
92 fn hide_cursor(&mut self) -> io::Result<()> {
93 map_error(execute!(self.buffer, Hide))
94 }
95
96 fn show_cursor(&mut self) -> io::Result<()> {
97 map_error(execute!(self.buffer, Show))
98 }
99
100 fn get_cursor(&mut self) -> io::Result<(u16, u16)> {
101 crossterm::cursor::position()
102 .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))
103 }
104
105 fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> {
106 map_error(execute!(self.buffer, MoveTo(x, y)))
107 }
108
109 fn clear(&mut self) -> io::Result<()> {
110 map_error(execute!(self.buffer, Clear(ClearType::All)))
111 }
112
113 fn size(&self) -> io::Result<Rect> {
114 let (width, height) =
115 terminal::size().map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
116
117 Ok(Rect::new(0, 0, width, height))
118 }
119
120 fn flush(&mut self) -> io::Result<()> {
121 self.buffer.flush()
122 }
123}
124
125fn map_error(error: crossterm::Result<()>) -> io::Result<()> {
126 error.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))
127}
128
129impl From<Color> for CColor {
130 fn from(color: Color) -> Self {
131 match color {
132 Color::Reset => CColor::Reset,
133 Color::Black => CColor::Black,
134 Color::Red => CColor::DarkRed,
135 Color::Green => CColor::DarkGreen,
136 Color::Yellow => CColor::DarkYellow,
137 Color::Blue => CColor::DarkBlue,
138 Color::Magenta => CColor::DarkMagenta,
139 Color::Cyan => CColor::DarkCyan,
140 Color::Gray => CColor::Grey,
141 Color::DarkGray => CColor::DarkGrey,
142 Color::LightRed => CColor::Red,
143 Color::LightGreen => CColor::Green,
144 Color::LightBlue => CColor::Blue,
145 Color::LightYellow => CColor::Yellow,
146 Color::LightMagenta => CColor::Magenta,
147 Color::LightCyan => CColor::Cyan,
148 Color::White => CColor::White,
149 Color::Indexed(i) => CColor::AnsiValue(i),
150 Color::Rgb(r, g, b) => CColor::Rgb { r, g, b },
151 }
152 }
153}
154
155#[derive(Debug)]
156struct ModifierDiff {
157 pub from: Modifier,
158 pub to: Modifier,
159}
160
161impl ModifierDiff {
162 fn queue<W>(&self, mut w: W) -> io::Result<()>
163 where
164 W: io::Write,
165 {
166 let removed = self.from - self.to;
168 if removed.contains(Modifier::REVERSED) {
169 map_error(queue!(w, SetAttribute(CAttribute::NoReverse)))?;
170 }
171 if removed.contains(Modifier::BOLD) {
172 map_error(queue!(w, SetAttribute(CAttribute::NormalIntensity)))?;
173 if self.to.contains(Modifier::DIM) {
174 map_error(queue!(w, SetAttribute(CAttribute::Dim)))?;
175 }
176 }
177 if removed.contains(Modifier::ITALIC) {
178 map_error(queue!(w, SetAttribute(CAttribute::NoItalic)))?;
179 }
180 if removed.contains(Modifier::UNDERLINED) {
181 map_error(queue!(w, SetAttribute(CAttribute::NoUnderline)))?;
182 }
183 if removed.contains(Modifier::DIM) {
184 map_error(queue!(w, SetAttribute(CAttribute::NormalIntensity)))?;
185 }
186 if removed.contains(Modifier::CROSSED_OUT) {
187 map_error(queue!(w, SetAttribute(CAttribute::NotCrossedOut)))?;
188 }
189 if removed.contains(Modifier::SLOW_BLINK) || removed.contains(Modifier::RAPID_BLINK) {
190 map_error(queue!(w, SetAttribute(CAttribute::NoBlink)))?;
191 }
192
193 let added = self.to - self.from;
194 if added.contains(Modifier::REVERSED) {
195 map_error(queue!(w, SetAttribute(CAttribute::Reverse)))?;
196 }
197 if added.contains(Modifier::BOLD) {
198 map_error(queue!(w, SetAttribute(CAttribute::Bold)))?;
199 }
200 if added.contains(Modifier::ITALIC) {
201 map_error(queue!(w, SetAttribute(CAttribute::Italic)))?;
202 }
203 if added.contains(Modifier::UNDERLINED) {
204 map_error(queue!(w, SetAttribute(CAttribute::Underlined)))?;
205 }
206 if added.contains(Modifier::DIM) {
207 map_error(queue!(w, SetAttribute(CAttribute::Dim)))?;
208 }
209 if added.contains(Modifier::CROSSED_OUT) {
210 map_error(queue!(w, SetAttribute(CAttribute::CrossedOut)))?;
211 }
212 if added.contains(Modifier::SLOW_BLINK) {
213 map_error(queue!(w, SetAttribute(CAttribute::SlowBlink)))?;
214 }
215 if added.contains(Modifier::RAPID_BLINK) {
216 map_error(queue!(w, SetAttribute(CAttribute::RapidBlink)))?;
217 }
218
219 Ok(())
220 }
221}