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> {
55 buffer: W,
56}
57
58impl<W> CrosstermBackend<W>
59where
60 W: Write,
61{
62 pub fn new(buffer: W) -> CrosstermBackend<W> {
77 CrosstermBackend { buffer }
78 }
79}
80
81impl<W> Write for CrosstermBackend<W>
86where
87 W: Write,
88{
89 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
90 self.buffer.write(buf)
91 }
92
93 fn flush(&mut self) -> io::Result<()> {
94 self.buffer.flush()
95 }
96}
97
98impl<W> Backend for CrosstermBackend<W>
99where
100 W: Write,
101{
102 fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
120 where
121 I: Iterator<Item = (u16, u16, &'a Cell)>,
122 {
123 let mut fg = Color::Reset;
124 let mut bg = Color::Reset;
125 let mut modifier = Modifier::empty();
126 let mut last_pos: Option<(u16, u16)> = None;
127 for (x, y, cell) in content {
128 if !matches!(last_pos, Some(p) if x == p.0 + 1 && y == p.1) {
130 queue!(self.buffer, MoveTo(x, y))?;
131 }
132 last_pos = Some((x, y));
133 if cell.modifier != modifier {
134 let diff = ModifierDiff {
135 from: modifier,
136 to: cell.modifier,
137 };
138 diff.queue(&mut self.buffer)?;
139 modifier = cell.modifier;
140 }
141 if cell.fg != fg {
142 let color = CColor::from(cell.fg);
143 queue!(self.buffer, SetForegroundColor(color))?;
144 fg = cell.fg;
145 }
146 if cell.bg != bg {
147 let color = CColor::from(cell.bg);
148 queue!(self.buffer, SetBackgroundColor(color))?;
149 bg = cell.bg;
150 }
151
152 queue!(self.buffer, Print(&cell.symbol))?;
153 }
154
155 queue!(
156 self.buffer,
157 SetForegroundColor(CColor::Reset),
158 SetBackgroundColor(CColor::Reset),
159 SetAttribute(CAttribute::Reset)
160 )
161 }
162
163 fn hide_cursor(&mut self) -> io::Result<()> {
165 execute!(self.buffer, Hide)
166 }
167
168 fn show_cursor(&mut self) -> io::Result<()> {
170 execute!(self.buffer, Show)
171 }
172
173 fn get_cursor(&mut self) -> io::Result<(u16, u16)> {
177 crossterm::cursor::position()
178 .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))
179 }
180
181 fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> {
183 execute!(self.buffer, MoveTo(x, y))
184 }
185
186 fn clear(&mut self) -> io::Result<()> {
188 execute!(self.buffer, Clear(ClearType::All))
189 }
190
191 fn size(&self) -> io::Result<Rect> {
195 let (width, height) =
196 terminal::size().map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
197
198 Ok(Rect::new(0, 0, width, height))
199 }
200
201 fn flush(&mut self) -> io::Result<()> {
203 self.buffer.flush()
204 }
205}
206
207impl From<Color> for CColor {
208 fn from(color: Color) -> Self {
209 match color {
210 Color::Reset => CColor::Reset,
211 Color::Black => CColor::Black,
212 Color::Red => CColor::DarkRed,
213 Color::Green => CColor::DarkGreen,
214 Color::Yellow => CColor::DarkYellow,
215 Color::Blue => CColor::DarkBlue,
216 Color::Magenta => CColor::DarkMagenta,
217 Color::Cyan => CColor::DarkCyan,
218 Color::Gray => CColor::Grey,
219 Color::DarkGray => CColor::DarkGrey,
220 Color::LightRed => CColor::Red,
221 Color::LightGreen => CColor::Green,
222 Color::LightBlue => CColor::Blue,
223 Color::LightYellow => CColor::Yellow,
224 Color::LightMagenta => CColor::Magenta,
225 Color::LightCyan => CColor::Cyan,
226 Color::White => CColor::White,
227 Color::Indexed(i) => CColor::AnsiValue(i),
228 Color::Rgb(r, g, b) => CColor::Rgb { r, g, b },
229 }
230 }
231}
232
233#[derive(Debug)]
234struct ModifierDiff {
235 pub from: Modifier,
236 pub to: Modifier,
237}
238
239impl ModifierDiff {
240 fn queue<W>(&self, mut w: W) -> io::Result<()>
241 where
242 W: io::Write,
243 {
244 let removed = self.from - self.to;
246 if removed.contains(Modifier::REVERSED) {
247 queue!(w, SetAttribute(CAttribute::NoReverse))?;
248 }
249 if removed.contains(Modifier::BOLD) {
250 queue!(w, SetAttribute(CAttribute::NormalIntensity))?;
251 if self.to.contains(Modifier::DIM) {
252 queue!(w, SetAttribute(CAttribute::Dim))?;
253 }
254 }
255 if removed.contains(Modifier::ITALIC) {
256 queue!(w, SetAttribute(CAttribute::NoItalic))?;
257 }
258 if removed.contains(Modifier::UNDERLINED) {
259 queue!(w, SetAttribute(CAttribute::NoUnderline))?;
260 }
261 if removed.contains(Modifier::DIM) {
262 queue!(w, SetAttribute(CAttribute::NormalIntensity))?;
263 }
264 if removed.contains(Modifier::CROSSED_OUT) {
265 queue!(w, SetAttribute(CAttribute::NotCrossedOut))?;
266 }
267 if removed.contains(Modifier::SLOW_BLINK) || removed.contains(Modifier::RAPID_BLINK) {
268 queue!(w, SetAttribute(CAttribute::NoBlink))?;
269 }
270
271 let added = self.to - self.from;
272 if added.contains(Modifier::REVERSED) {
273 queue!(w, SetAttribute(CAttribute::Reverse))?;
274 }
275 if added.contains(Modifier::BOLD) {
276 queue!(w, SetAttribute(CAttribute::Bold))?;
277 }
278 if added.contains(Modifier::ITALIC) {
279 queue!(w, SetAttribute(CAttribute::Italic))?;
280 }
281 if added.contains(Modifier::UNDERLINED) {
282 queue!(w, SetAttribute(CAttribute::Underlined))?;
283 }
284 if added.contains(Modifier::DIM) {
285 queue!(w, SetAttribute(CAttribute::Dim))?;
286 }
287 if added.contains(Modifier::CROSSED_OUT) {
288 queue!(w, SetAttribute(CAttribute::CrossedOut))?;
289 }
290 if added.contains(Modifier::SLOW_BLINK) {
291 queue!(w, SetAttribute(CAttribute::SlowBlink))?;
292 }
293 if added.contains(Modifier::RAPID_BLINK) {
294 queue!(w, SetAttribute(CAttribute::RapidBlink))?;
295 }
296
297 Ok(())
298 }
299}