From e4873e4da9a213df7a7dc718e1f400b807870724 Mon Sep 17 00:00:00 2001 From: Timon Date: Fri, 29 Nov 2019 09:06:59 +0100 Subject: [PATCH] feat(backend): bump crossterm to 0.13 * removed flush calls because execute already calls flush under the hood. * moved some static functions into From traits * removed useless clone in demo * upgrade to crossterm 0.13 * map all errors --- Cargo.toml | 2 +- examples/crossterm_demo.rs | 29 ++--- src/backend/crossterm.rs | 233 +++++++++++++++++-------------------- src/backend/rustbox.rs | 2 +- 4 files changed, 126 insertions(+), 140 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dfe00f3..fae93b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ unicode-segmentation = "1.2" unicode-width = "0.1" termion = { version = "1.5" , optional = true } rustbox = { version = "0.11", optional = true } -crossterm = { version = "^0.10", optional = true } +crossterm = { version = "0.13", optional = true } easycurses = { version = "0.12.2", optional = true } pancurses = { version = "0.16.1", optional = true, features = ["win32a"] } diff --git a/examples/crossterm_demo.rs b/examples/crossterm_demo.rs index 2bf2b50..72870ea 100644 --- a/examples/crossterm_demo.rs +++ b/examples/crossterm_demo.rs @@ -7,7 +7,10 @@ use std::sync::mpsc; use std::thread; use std::time::Duration; -use crossterm::{input, AlternateScreen, InputEvent, KeyEvent}; +use crossterm::{ + input::{input, InputEvent, KeyEvent}, + screen::AlternateScreen, +}; use structopt::StructOpt; use tui::backend::CrosstermBackend; use tui::Terminal; @@ -43,10 +46,10 @@ fn main() -> Result<(), failure::Error> { let tx = tx.clone(); thread::spawn(move || { let input = input(); - let reader = input.read_sync(); - for event in reader { - match event { - InputEvent::Keyboard(key) => { + let mut reader = input.read_sync(); + loop { + match reader.next() { + Some(InputEvent::Keyboard(key)) => { if let Err(_) = tx.send(Event::Input(key.clone())) { return; } @@ -59,16 +62,14 @@ fn main() -> Result<(), failure::Error> { } }); } - { + + thread::spawn(move || { let tx = tx.clone(); - thread::spawn(move || { - let tx = tx.clone(); - loop { - tx.send(Event::Tick).unwrap(); - thread::sleep(Duration::from_millis(cli.tick_rate)); - } - }); - } + loop { + tx.send(Event::Tick).unwrap(); + thread::sleep(Duration::from_millis(cli.tick_rate)); + } + }); let mut app = App::new("Crossterm Demo"); diff --git a/src/backend/crossterm.rs b/src/backend/crossterm.rs index fa73ef2..117b591 100644 --- a/src/backend/crossterm.rs +++ b/src/backend/crossterm.rs @@ -4,19 +4,23 @@ use std::{ }; use crossterm::{ - execute, queue, terminal, Clear, ClearType, Crossterm, ErrorKind, Goto, Hide, SetAttr, SetBg, - SetFg, Show, + cursor::{Hide, MoveTo, Show}, + execute, queue, + screen::AlternateScreen, + style::{ + Attribute as CAttribute, Color as CColor, SetAttribute, SetBackgroundColor, + SetForegroundColor, + }, + terminal::{self, Clear, ClearType}, + Output, }; -use crate::{ - backend::Backend, - buffer::Cell, - layout::Rect, - style::{Color, Modifier, Style}, -}; +use crate::backend::Backend; +use crate::style::{Color, Modifier}; +use crate::{buffer::Cell, layout::Rect, style}; pub struct CrosstermBackend { - alternate_screen: Option, + alternate_screen: Option, stdout: W, } @@ -33,7 +37,7 @@ where pub fn with_alternate_screen( stdout: W, - alternate_screen: crossterm::AlternateScreen, + alternate_screen: AlternateScreen, ) -> Result, io::Error> { Ok(CrosstermBackend { alternate_screen: Some(alternate_screen), @@ -41,30 +45,12 @@ where }) } - pub fn alternate_screen(&self) -> Option<&crossterm::AlternateScreen> { + pub fn alternate_screen(&self) -> Option<&AlternateScreen> { match &self.alternate_screen { Some(alt_screen) => Some(&alt_screen), None => None, } } - - pub fn crossterm(&self) -> crossterm::Crossterm { - Crossterm::new() - } -} - -fn convert_error(error: ErrorKind) -> io::Error { - match error { - ErrorKind::IoError(err) => err, - ErrorKind::FmtError(err) => { - io::Error::new(io::ErrorKind::Other, format!("Invalid formatting: {}", err)) - } - ErrorKind::ResizingTerminalFailure(err) => io::Error::new( - io::ErrorKind::Other, - format!("Failed to resize terminal: {}", err), - ), - _ => io::Error::new(io::ErrorKind::Other, "Unknown crossterm error"), - } } impl Write for CrosstermBackend @@ -84,53 +70,21 @@ impl Backend for CrosstermBackend where W: Write, { - fn clear(&mut self) -> io::Result<()> { - execute!(self.stdout, Clear(ClearType::All)).map_err(convert_error) - } - - fn hide_cursor(&mut self) -> io::Result<()> { - execute!(self.stdout, Hide).map_err(convert_error) - } - - fn show_cursor(&mut self) -> io::Result<()> { - execute!(self.stdout, Show).map_err(convert_error) - } - - fn get_cursor(&mut self) -> io::Result<(u16, u16)> { - let cursor = crossterm::cursor(); - Ok(cursor.pos()) - } - - fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> { - execute!(self.stdout, Goto(x, y)).map_err(convert_error) - } - - fn size(&self) -> io::Result { - let terminal = terminal(); - let (width, height) = terminal.terminal_size(); - // crossterm reports max 0-based col/row index instead of count - Ok(Rect::new(0, 0, width, height)) - } - - fn flush(&mut self) -> io::Result<()> { - self.stdout.flush() - } - fn draw<'a, I>(&mut self, content: I) -> io::Result<()> where I: Iterator, { - use std::fmt::Write; + use fmt::Write; let mut string = String::with_capacity(content.size_hint().0 * 3); - let mut style = Style::default(); + let mut style = style::Style::default(); let mut last_y = 0; let mut last_x = 0; let mut inst = 0; for (x, y, cell) in content { if y != last_y || x != last_x + 1 || inst == 0 { - queue!(string, Goto(x, y))?; + map_error(queue!(string, MoveTo(x, y)))?; } last_x = x; last_y = y; @@ -144,14 +98,14 @@ where style.modifier = cell.style.modifier; } if cell.style.fg != style.fg { - let color = to_crossterm_color(cell.style.fg); - queue!(string, SetFg(color))?; + let color = CColor::from(cell.style.fg); + map_error(queue!(string, SetForegroundColor(color)))?; style.fg = cell.style.fg; inst += 1; } if cell.style.bg != style.bg { - let color = to_crossterm_color(cell.style.bg); - queue!(string, SetBg(color))?; + let color = CColor::from(cell.style.bg); + map_error(queue!(string, SetBackgroundColor(color)))?; style.bg = cell.style.bg; inst += 1; } @@ -160,18 +114,75 @@ where inst += 1; } - write!( + map_error(queue!( self.stdout, - "{}{}{}{}", - string, - SetFg(crossterm::Color::Reset), - SetBg(crossterm::Color::Reset), - SetAttr(crossterm::Attribute::Reset) - )?; + Output(string), + SetForegroundColor(CColor::Reset), + SetBackgroundColor(CColor::Reset), + SetAttribute(CAttribute::Reset) + )) + } - Crossterm::new().color().reset()?; + fn hide_cursor(&mut self) -> io::Result<()> { + map_error(execute!(self.stdout, Hide)) + } - Ok(()) + fn show_cursor(&mut self) -> io::Result<()> { + map_error(execute!(self.stdout, Show)) + } + + fn get_cursor(&mut self) -> io::Result<(u16, u16)> { + crossterm::cursor::position() + .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string())) + } + + fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> { + map_error(execute!(self.stdout, MoveTo(x, y))) + } + + fn clear(&mut self) -> io::Result<()> { + map_error(execute!(self.stdout, Clear(ClearType::All))) + } + + fn size(&self) -> io::Result { + let (width, height) = + terminal::size().map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?; + + Ok(Rect::new(0, 0, width, height)) + } + + fn flush(&mut self) -> io::Result<()> { + as Write>::flush(self) + } +} + +fn map_error(error: crossterm::Result<()>) -> io::Result<()> { + error.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string())) +} + +impl From for CColor { + fn from(color: Color) -> Self { + match color { + Color::Reset => CColor::Reset, + Color::Black => CColor::Black, + Color::Red => CColor::DarkRed, + Color::Green => CColor::DarkGreen, + Color::Yellow => CColor::DarkYellow, + Color::Blue => CColor::DarkBlue, + Color::Magenta => CColor::DarkMagenta, + Color::Cyan => CColor::DarkCyan, + Color::Gray => CColor::Grey, + Color::DarkGray => CColor::DarkGrey, + Color::LightRed => CColor::Red, + Color::LightGreen => CColor::Green, + Color::LightBlue => CColor::Blue, + Color::LightYellow => CColor::Yellow, + Color::LightMagenta => CColor::Magenta, + Color::LightCyan => CColor::Cyan, + Color::White => CColor::White, + Color::Indexed(i) => CColor::AnsiValue(i), + Color::Rgb(r, g, b) => CColor::Rgb { r, g, b }, + } } } @@ -187,57 +198,57 @@ impl ModifierDiff { where W: fmt::Write, { - use crossterm::Attribute; + //use crossterm::Attribute; let removed = self.from - self.to; if removed.contains(Modifier::REVERSED) { - queue!(w, SetAttr(Attribute::NoInverse))?; + map_error(queue!(w, SetAttribute(CAttribute::NoReverse)))?; } if removed.contains(Modifier::BOLD) { - queue!(w, SetAttr(Attribute::NormalIntensity))?; + map_error(queue!(w, SetAttribute(CAttribute::NormalIntensity)))?; if self.to.contains(Modifier::DIM) { - queue!(w, SetAttr(Attribute::Dim))?; + map_error(queue!(w, SetAttribute(CAttribute::Dim)))?; } } if removed.contains(Modifier::ITALIC) { - queue!(w, SetAttr(Attribute::NoItalic))?; + map_error(queue!(w, SetAttribute(CAttribute::NoItalic)))?; } if removed.contains(Modifier::UNDERLINED) { - queue!(w, SetAttr(Attribute::NoUnderline))?; + map_error(queue!(w, SetAttribute(CAttribute::NoUnderline)))?; } if removed.contains(Modifier::DIM) { - queue!(w, SetAttr(Attribute::NormalIntensity))?; + map_error(queue!(w, SetAttribute(CAttribute::NormalIntensity)))?; } if removed.contains(Modifier::CROSSED_OUT) { - queue!(w, SetAttr(Attribute::NotCrossedOut))?; + map_error(queue!(w, SetAttribute(CAttribute::NotCrossedOut)))?; } if removed.contains(Modifier::SLOW_BLINK) || removed.contains(Modifier::RAPID_BLINK) { - queue!(w, SetAttr(Attribute::NoBlink))?; + map_error(queue!(w, SetAttribute(CAttribute::NoBlink)))?; } let added = self.to - self.from; if added.contains(Modifier::REVERSED) { - queue!(w, SetAttr(Attribute::Reverse))?; + map_error(queue!(w, SetAttribute(CAttribute::Reverse)))?; } if added.contains(Modifier::BOLD) { - queue!(w, SetAttr(Attribute::Bold))?; + map_error(queue!(w, SetAttribute(CAttribute::Bold)))?; } if added.contains(Modifier::ITALIC) { - queue!(w, SetAttr(Attribute::Italic))?; + map_error(queue!(w, SetAttribute(CAttribute::Italic)))?; } if added.contains(Modifier::UNDERLINED) { - queue!(w, SetAttr(Attribute::Underlined))?; + map_error(queue!(w, SetAttribute(CAttribute::Underlined)))?; } if added.contains(Modifier::DIM) { - queue!(w, SetAttr(Attribute::Dim))?; + map_error(queue!(w, SetAttribute(CAttribute::Dim)))?; } if added.contains(Modifier::CROSSED_OUT) { - queue!(w, SetAttr(Attribute::CrossedOut))?; + map_error(queue!(w, SetAttribute(CAttribute::CrossedOut)))?; } if added.contains(Modifier::SLOW_BLINK) { - queue!(w, SetAttr(Attribute::SlowBlink))?; + map_error(queue!(w, SetAttribute(CAttribute::SlowBlink)))?; } if added.contains(Modifier::RAPID_BLINK) { - queue!(w, SetAttr(Attribute::RapidBlink))?; + map_error(queue!(w, SetAttribute(CAttribute::RapidBlink)))?; } Ok(()) @@ -250,47 +261,21 @@ impl ModifierDiff { where W: fmt::Write, { - use crossterm::Attribute; - let removed = self.from - self.to; if removed.contains(Modifier::BOLD) { - queue!(w, SetAttr(Attribute::NormalIntensity))?; + map_error(queue!(w, SetAttribute(CAttribute::NormalIntensity)))?; } if removed.contains(Modifier::UNDERLINED) { - queue!(w, SetAttr(Attribute::NoUnderline))?; + map_error(queue!(w, SetAttribute(CAttribute::NoUnderline)))?; } let added = self.to - self.from; if added.contains(Modifier::BOLD) { - queue!(w, SetAttr(Attribute::Bold))?; + map_error(queue!(w, SetAttribute(CAttribute::Bold)))?; } if added.contains(Modifier::UNDERLINED) { - queue!(w, SetAttr(Attribute::Underlined))?; + map_error(queue!(w, SetAttribute(CAttribute::Underlined)))?; } Ok(()) } } - -fn to_crossterm_color(color: Color) -> crossterm::Color { - match color { - Color::Reset => crossterm::Color::Reset, - Color::Black => crossterm::Color::Black, - Color::Red => crossterm::Color::DarkRed, - Color::Green => crossterm::Color::DarkGreen, - Color::Yellow => crossterm::Color::DarkYellow, - Color::Blue => crossterm::Color::DarkBlue, - Color::Magenta => crossterm::Color::DarkMagenta, - Color::Cyan => crossterm::Color::DarkCyan, - Color::Gray => crossterm::Color::Grey, - Color::DarkGray => crossterm::Color::DarkGrey, - Color::LightRed => crossterm::Color::Red, - Color::LightGreen => crossterm::Color::Green, - Color::LightBlue => crossterm::Color::Blue, - Color::LightYellow => crossterm::Color::Yellow, - Color::LightMagenta => crossterm::Color::Magenta, - Color::LightCyan => crossterm::Color::Cyan, - Color::White => crossterm::Color::White, - Color::Indexed(i) => crossterm::Color::AnsiValue(i), - Color::Rgb(r, g, b) => crossterm::Color::Rgb { r, g, b }, - } -} diff --git a/src/backend/rustbox.rs b/src/backend/rustbox.rs index f50cc1e..6adecbf 100644 --- a/src/backend/rustbox.rs +++ b/src/backend/rustbox.rs @@ -12,7 +12,7 @@ pub struct RustboxBackend { impl RustboxBackend { pub fn new() -> Result { - let rustbox = r#try!(rustbox::RustBox::init(Default::default())); + let rustbox = rustbox::RustBox::init(Default::default())?; Ok(RustboxBackend { rustbox }) }