1 use std::io;
2
3 use crate::backend::Backend;
4 use crate::buffer::Cell;
5 use crate::layout::Rect;
6 use crate::style::{Color, Modifier, Style};
7 use crate::symbols::{bar, block};
8 #[cfg(unix)]
9 use crate::symbols::{line, DOT};
10 #[cfg(unix)]
11 use pancurses::{chtype, ToChtype};
12 use unicode_segmentation::UnicodeSegmentation;
13
14 pub struct CursesBackend {
15 curses: easycurses::EasyCurses,
16 }
17
18 impl CursesBackend {
new() -> Option<CursesBackend>19 pub fn new() -> Option<CursesBackend> {
20 let curses = easycurses::EasyCurses::initialize_system()?;
21 Some(CursesBackend { curses })
22 }
23
with_curses(curses: easycurses::EasyCurses) -> CursesBackend24 pub fn with_curses(curses: easycurses::EasyCurses) -> CursesBackend {
25 CursesBackend { curses }
26 }
27
get_curses(&self) -> &easycurses::EasyCurses28 pub fn get_curses(&self) -> &easycurses::EasyCurses {
29 &self.curses
30 }
31
get_curses_mut(&mut self) -> &mut easycurses::EasyCurses32 pub fn get_curses_mut(&mut self) -> &mut easycurses::EasyCurses {
33 &mut self.curses
34 }
35 }
36
37 impl Backend for CursesBackend {
draw<'a, I>(&mut self, content: I) -> io::Result<()> where I: Iterator<Item = (u16, u16, &'a Cell)>,38 fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
39 where
40 I: Iterator<Item = (u16, u16, &'a Cell)>,
41 {
42 let mut last_col = 0;
43 let mut last_row = 0;
44 let mut style = Style {
45 fg: Color::Reset,
46 bg: Color::Reset,
47 modifier: Modifier::empty(),
48 };
49 let mut curses_style = CursesStyle {
50 fg: easycurses::Color::White,
51 bg: easycurses::Color::Black,
52 };
53 let mut update_color = false;
54 for (col, row, cell) in content {
55 // eprintln!("{:?}", cell);
56 if row != last_row || col != last_col + 1 {
57 self.curses.move_rc(i32::from(row), i32::from(col));
58 }
59 last_col = col;
60 last_row = row;
61 if cell.style.modifier != style.modifier {
62 apply_modifier_diff(&mut self.curses.win, style.modifier, cell.style.modifier);
63 style.modifier = cell.style.modifier;
64 };
65 if cell.style.fg != style.fg {
66 update_color = true;
67 if let Some(ccolor) = cell.style.fg.into() {
68 style.fg = cell.style.fg;
69 curses_style.fg = ccolor;
70 } else {
71 style.fg = Color::White;
72 curses_style.fg = easycurses::Color::White;
73 }
74 };
75 if cell.style.bg != style.bg {
76 update_color = true;
77 if let Some(ccolor) = cell.style.bg.into() {
78 style.bg = cell.style.bg;
79 curses_style.bg = ccolor;
80 } else {
81 style.bg = Color::Black;
82 curses_style.bg = easycurses::Color::Black;
83 }
84 };
85 if update_color {
86 self.curses
87 .set_color_pair(easycurses::ColorPair::new(curses_style.fg, curses_style.bg));
88 };
89 update_color = false;
90 draw(&mut self.curses, cell.symbol.as_str());
91 }
92 self.curses.win.attrset(pancurses::Attribute::Normal);
93 self.curses.set_color_pair(easycurses::ColorPair::new(
94 easycurses::Color::White,
95 easycurses::Color::Black,
96 ));
97 Ok(())
98 }
hide_cursor(&mut self) -> io::Result<()>99 fn hide_cursor(&mut self) -> io::Result<()> {
100 self.curses
101 .set_cursor_visibility(easycurses::CursorVisibility::Invisible);
102 Ok(())
103 }
show_cursor(&mut self) -> io::Result<()>104 fn show_cursor(&mut self) -> io::Result<()> {
105 self.curses
106 .set_cursor_visibility(easycurses::CursorVisibility::Visible);
107 Ok(())
108 }
get_cursor(&mut self) -> io::Result<(u16, u16)>109 fn get_cursor(&mut self) -> io::Result<(u16, u16)> {
110 let (y, x) = self.curses.get_cursor_rc();
111 Ok((x as u16, y as u16))
112 }
set_cursor(&mut self, x: u16, y: u16) -> io::Result<()>113 fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> {
114 self.curses.move_rc(i32::from(y), i32::from(x));
115 Ok(())
116 }
clear(&mut self) -> io::Result<()>117 fn clear(&mut self) -> io::Result<()> {
118 self.curses.clear();
119 // self.curses.refresh();
120 Ok(())
121 }
size(&self) -> Result<Rect, io::Error>122 fn size(&self) -> Result<Rect, io::Error> {
123 let (nrows, ncols) = self.curses.get_row_col_count();
124 Ok(Rect::new(0, 0, ncols as u16, nrows as u16))
125 }
flush(&mut self) -> io::Result<()>126 fn flush(&mut self) -> io::Result<()> {
127 self.curses.refresh();
128 Ok(())
129 }
130 }
131
132 struct CursesStyle {
133 fg: easycurses::Color,
134 bg: easycurses::Color,
135 }
136
137 #[cfg(unix)]
138 /// Deals with lack of unicode support for ncurses on unix
draw(curses: &mut easycurses::EasyCurses, symbol: &str)139 fn draw(curses: &mut easycurses::EasyCurses, symbol: &str) {
140 for grapheme in symbol.graphemes(true) {
141 let ch = match grapheme {
142 line::TOP_RIGHT => pancurses::ACS_URCORNER(),
143 line::VERTICAL => pancurses::ACS_VLINE(),
144 line::HORIZONTAL => pancurses::ACS_HLINE(),
145 line::TOP_LEFT => pancurses::ACS_ULCORNER(),
146 line::BOTTOM_RIGHT => pancurses::ACS_LRCORNER(),
147 line::BOTTOM_LEFT => pancurses::ACS_LLCORNER(),
148 line::VERTICAL_LEFT => pancurses::ACS_RTEE(),
149 line::VERTICAL_RIGHT => pancurses::ACS_LTEE(),
150 line::HORIZONTAL_DOWN => pancurses::ACS_TTEE(),
151 line::HORIZONTAL_UP => pancurses::ACS_BTEE(),
152 block::FULL => pancurses::ACS_BLOCK(),
153 block::SEVEN_EIGHTHS => pancurses::ACS_BLOCK(),
154 block::THREE_QUARTERS => pancurses::ACS_BLOCK(),
155 block::FIVE_EIGHTHS => pancurses::ACS_BLOCK(),
156 block::HALF => pancurses::ACS_BLOCK(),
157 block::THREE_EIGHTHS => ' ' as chtype,
158 block::ONE_QUARTER => ' ' as chtype,
159 block::ONE_EIGHTH => ' ' as chtype,
160 bar::SEVEN_EIGHTHS => pancurses::ACS_BLOCK(),
161 bar::THREE_QUARTERS => pancurses::ACS_BLOCK(),
162 bar::FIVE_EIGHTHS => pancurses::ACS_BLOCK(),
163 bar::HALF => pancurses::ACS_BLOCK(),
164 bar::THREE_EIGHTHS => pancurses::ACS_S9(),
165 bar::ONE_QUARTER => pancurses::ACS_S9(),
166 bar::ONE_EIGHTH => pancurses::ACS_S9(),
167 DOT => pancurses::ACS_BULLET(),
168 unicode_char => {
169 if unicode_char.is_ascii() {
170 let mut chars = unicode_char.chars();
171 if let Some(ch) = chars.next() {
172 ch.to_chtype()
173 } else {
174 pancurses::ACS_BLOCK()
175 }
176 } else {
177 pancurses::ACS_BLOCK()
178 }
179 }
180 };
181 curses.win.addch(ch);
182 }
183 }
184
185 #[cfg(windows)]
draw(curses: &mut easycurses::EasyCurses, symbol: &str)186 fn draw(curses: &mut easycurses::EasyCurses, symbol: &str) {
187 for grapheme in symbol.graphemes(true) {
188 let ch = match grapheme {
189 block::SEVEN_EIGHTHS => block::FULL,
190 block::THREE_QUARTERS => block::FULL,
191 block::FIVE_EIGHTHS => block::HALF,
192 block::THREE_EIGHTHS => block::HALF,
193 block::ONE_QUARTER => block::HALF,
194 block::ONE_EIGHTH => " ",
195 bar::SEVEN_EIGHTHS => bar::FULL,
196 bar::THREE_QUARTERS => bar::FULL,
197 bar::FIVE_EIGHTHS => bar::HALF,
198 bar::THREE_EIGHTHS => bar::HALF,
199 bar::ONE_QUARTER => bar::HALF,
200 bar::ONE_EIGHTH => " ",
201 ch => ch,
202 };
203 // curses.win.addch(ch);
204 curses.print(ch);
205 }
206 }
207
208 impl From<Color> for Option<easycurses::Color> {
from(color: Color) -> Option<easycurses::Color>209 fn from(color: Color) -> Option<easycurses::Color> {
210 match color {
211 Color::Reset => None,
212 Color::Black => Some(easycurses::Color::Black),
213 Color::Red | Color::LightRed => Some(easycurses::Color::Red),
214 Color::Green | Color::LightGreen => Some(easycurses::Color::Green),
215 Color::Yellow | Color::LightYellow => Some(easycurses::Color::Yellow),
216 Color::Magenta | Color::LightMagenta => Some(easycurses::Color::Magenta),
217 Color::Cyan | Color::LightCyan => Some(easycurses::Color::Cyan),
218 Color::White | Color::Gray | Color::DarkGray => Some(easycurses::Color::White),
219 Color::Blue | Color::LightBlue => Some(easycurses::Color::Blue),
220 Color::Indexed(_) => None,
221 Color::Rgb(_, _, _) => None,
222 }
223 }
224 }
225
apply_modifier_diff(win: &mut pancurses::Window, from: Modifier, to: Modifier)226 fn apply_modifier_diff(win: &mut pancurses::Window, from: Modifier, to: Modifier) {
227 remove_modifier(win, from - to);
228 add_modifier(win, to - from);
229 }
230
remove_modifier(win: &mut pancurses::Window, remove: Modifier)231 fn remove_modifier(win: &mut pancurses::Window, remove: Modifier) {
232 if remove.contains(Modifier::BOLD) {
233 win.attroff(pancurses::Attribute::Bold);
234 }
235 if remove.contains(Modifier::DIM) {
236 win.attroff(pancurses::Attribute::Dim);
237 }
238 if remove.contains(Modifier::ITALIC) {
239 win.attroff(pancurses::Attribute::Italic);
240 }
241 if remove.contains(Modifier::UNDERLINED) {
242 win.attroff(pancurses::Attribute::Underline);
243 }
244 if remove.contains(Modifier::SLOW_BLINK) || remove.contains(Modifier::RAPID_BLINK) {
245 win.attroff(pancurses::Attribute::Blink);
246 }
247 if remove.contains(Modifier::REVERSED) {
248 win.attroff(pancurses::Attribute::Reverse);
249 }
250 if remove.contains(Modifier::HIDDEN) {
251 win.attroff(pancurses::Attribute::Invisible);
252 }
253 if remove.contains(Modifier::CROSSED_OUT) {
254 win.attroff(pancurses::Attribute::Strikeout);
255 }
256 }
257
add_modifier(win: &mut pancurses::Window, add: Modifier)258 fn add_modifier(win: &mut pancurses::Window, add: Modifier) {
259 if add.contains(Modifier::BOLD) {
260 win.attron(pancurses::Attribute::Bold);
261 }
262 if add.contains(Modifier::DIM) {
263 win.attron(pancurses::Attribute::Dim);
264 }
265 if add.contains(Modifier::ITALIC) {
266 win.attron(pancurses::Attribute::Italic);
267 }
268 if add.contains(Modifier::UNDERLINED) {
269 win.attron(pancurses::Attribute::Underline);
270 }
271 if add.contains(Modifier::SLOW_BLINK) || add.contains(Modifier::RAPID_BLINK) {
272 win.attron(pancurses::Attribute::Blink);
273 }
274 if add.contains(Modifier::REVERSED) {
275 win.attron(pancurses::Attribute::Reverse);
276 }
277 if add.contains(Modifier::HIDDEN) {
278 win.attron(pancurses::Attribute::Invisible);
279 }
280 if add.contains(Modifier::CROSSED_OUT) {
281 win.attron(pancurses::Attribute::Strikeout);
282 }
283 }
284