Crossterm Performance Update (#361)

This commit is contained in:
Timon 2019-07-30 18:48:50 +02:00 committed by Alexandre Bury
parent ede8423e92
commit bddb1a9dde
5 changed files with 72 additions and 99 deletions

View File

@ -7,15 +7,17 @@
use crate::vec::Vec2; use crate::vec::Vec2;
use crate::{backend, theme}; use crate::{backend, theme};
use crossterm::{ use crossterm::{
cursor, input, terminal, AlternateScreen, AsyncReader, Attribute, cursor, execute, input, queue, terminal, AlternateScreen, AsyncReader,
ClearType, Color, Colored, InputEvent as CInputEvent, Attribute, Clear, ClearType, Color, Colored, Command, Goto, Hide,
KeyEvent as CKeyEvent, MouseButton as CMouseButton, InputEvent as CInputEvent, KeyEvent as CKeyEvent,
MouseEvent as CMouseEvent, Terminal, TerminalCursor, MouseButton as CMouseButton, MouseEvent as CMouseEvent, Output, RawScreen,
SetAttr, SetBg, SetFg, Show, Terminal, TerminalCursor,
}; };
use crate::event::{Event, Key, MouseButton, MouseEvent}; use crate::event::{Event, Key, MouseButton, MouseEvent};
use core::borrow::BorrowMut;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::io::{self, Stdout, Write}; use std::io::{self, BufWriter, Stdout, StdoutLock, Write};
/// Backend using crossterm /// Backend using crossterm
pub struct Backend { pub struct Backend {
@ -24,7 +26,7 @@ pub struct Backend {
// reader to read user input async. // reader to read user input async.
async_reader: AsyncReader, async_reader: AsyncReader,
_alternate_screen: AlternateScreen, _alternate_screen: AlternateScreen,
stdout: RefCell<Stdout>, stdout: RefCell<BufWriter<Stdout>>,
cursor: TerminalCursor, cursor: TerminalCursor,
terminal: Terminal, terminal: Terminal,
} }
@ -48,22 +50,31 @@ impl Backend {
last_button: None, last_button: None,
async_reader, async_reader,
_alternate_screen, _alternate_screen,
stdout: RefCell::new(io::stdout()), stdout: RefCell::new(BufWriter::new(io::stdout())),
terminal: terminal(), terminal: terminal(),
cursor: cursor(), cursor: cursor(),
})) }))
} }
fn apply_colors(&self, colors: theme::ColorPair) { fn apply_colors(&self, colors: theme::ColorPair) {
with_color(colors.front, |c| self.write(Colored::Fg(*c))); with_color(colors.front, |c| {
with_color(colors.back, |c| self.write(Colored::Bg(*c))); queue!(self.stdout.borrow_mut(), SetFg(*c))
});
with_color(colors.back, |c| {
queue!(self.stdout.borrow_mut(), SetBg(*c))
});
} }
fn write<T>(&self, content: T) fn write<T>(&self, content: T)
where where
T: std::fmt::Display, T: std::fmt::Display,
{ {
write!(self.stdout.borrow_mut(), "{}", format!("{}", content)).unwrap() write!(self.stdout.borrow_mut(), "{}", format!("{}", content))
.unwrap();
}
fn set_attr(&self, attr: Attribute) {
queue!(self.stdout.borrow_mut(), SetAttr(attr));
} }
fn map_key(&mut self, event: CInputEvent) -> Event { fn map_key(&mut self, event: CInputEvent) -> Event {
@ -164,11 +175,16 @@ impl backend::Backend for Backend {
} }
fn finish(&mut self) { fn finish(&mut self) {
self.cursor.goto(1, 1).unwrap(); execute!(
self.terminal.clear(ClearType::All).unwrap(); self.stdout.borrow_mut(),
self.write(Attribute::Reset); Goto(0, 0),
Clear(ClearType::All),
SetBg(Color::Reset),
SetFg(Color::Reset),
SetAttr(Attribute::Reset),
Show
);
input().disable_mouse_mode().unwrap(); input().disable_mouse_mode().unwrap();
cursor().show().unwrap();
} }
fn refresh(&mut self) { fn refresh(&mut self) {
@ -182,11 +198,12 @@ impl backend::Backend for Backend {
fn screen_size(&self) -> Vec2 { fn screen_size(&self) -> Vec2 {
let size = self.terminal.terminal_size(); let size = self.terminal.terminal_size();
Vec2::from(size) + (1, 1) Vec2::from(size)
} }
fn print_at(&self, pos: Vec2, text: &str) { fn print_at(&self, pos: Vec2, text: &str) {
self.cursor.goto(pos.x as u16, pos.y as u16).unwrap(); queue!(self.stdout.borrow_mut(), Goto(pos.x as u16, pos.y as u16))
.unwrap();
self.write(text); self.write(text);
} }
@ -194,7 +211,7 @@ impl backend::Backend for Backend {
if repetitions > 0 { if repetitions > 0 {
let mut out = self.stdout.borrow_mut(); let mut out = self.stdout.borrow_mut();
self.cursor.goto(pos.x as u16, pos.y as u16).unwrap(); queue!(out, Goto(pos.x as u16, pos.y as u16)).unwrap();
// as I (Timon) wrote this I figured out that calling `write_str` for unix was flushing the stdout. // as I (Timon) wrote this I figured out that calling `write_str` for unix was flushing the stdout.
// Current work aground is writing bytes instead of a string to the terminal. // Current work aground is writing bytes instead of a string to the terminal.
@ -214,7 +231,7 @@ impl backend::Backend for Backend {
back: color, back: color,
}); });
self.terminal.clear(ClearType::All).unwrap(); queue!(self.stdout.borrow_mut(), Clear(ClearType::All)).unwrap();
} }
fn set_color(&self, color: theme::ColorPair) -> theme::ColorPair { fn set_color(&self, color: theme::ColorPair) -> theme::ColorPair {
@ -231,22 +248,22 @@ impl backend::Backend for Backend {
fn set_effect(&self, effect: theme::Effect) { fn set_effect(&self, effect: theme::Effect) {
match effect { match effect {
theme::Effect::Simple => (), theme::Effect::Simple => (),
theme::Effect::Reverse => self.write(Attribute::Reverse), theme::Effect::Reverse => self.set_attr(Attribute::Reverse),
theme::Effect::Bold => self.write(Attribute::Bold), theme::Effect::Bold => self.set_attr(Attribute::Bold),
theme::Effect::Italic => self.write(Attribute::Italic), theme::Effect::Italic => self.set_attr(Attribute::Italic),
theme::Effect::Strikethrough => self.write(Attribute::CrossedOut), theme::Effect::Strikethrough => self.set_attr(Attribute::CrossedOut),
theme::Effect::Underline => self.write(Attribute::Underlined), theme::Effect::Underline => self.set_attr(Attribute::Underlined),
} }
} }
fn unset_effect(&self, effect: theme::Effect) { fn unset_effect(&self, effect: theme::Effect) {
match effect { match effect {
theme::Effect::Simple => (), theme::Effect::Simple => (),
theme::Effect::Reverse => self.write(Attribute::Reverse), theme::Effect::Reverse => self.set_attr(Attribute::Reverse),
theme::Effect::Bold => self.write(Attribute::NoBold), theme::Effect::Bold => self.set_attr(Attribute::NoBold),
theme::Effect::Italic => self.write(Attribute::NoItalic), theme::Effect::Italic => self.set_attr(Attribute::NoItalic),
theme::Effect::Strikethrough => self.write(Attribute::NotCrossedOut), theme::Effect::Strikethrough => self.set_attr(Attribute::NotCrossedOut),
theme::Effect::Underline => self.write(Attribute::Underlined), theme::Effect::Underline => self.set_attr(Attribute::Underlined),
} }
} }
} }

View File

@ -97,8 +97,7 @@ impl Core {
/// Returns a sub-printer ready to draw the content. /// Returns a sub-printer ready to draw the content.
pub fn sub_printer<'a, 'b>( pub fn sub_printer<'a, 'b>(
&self, &self, printer: &Printer<'a, 'b>,
printer: &Printer<'a, 'b>,
) -> Printer<'a, 'b> { ) -> Printer<'a, 'b> {
// Draw scrollbar? // Draw scrollbar?
let scrolling = self.is_scrolling(); let scrolling = self.is_scrolling();
@ -185,9 +184,7 @@ impl Core {
/// Handle an event after processing by the content. /// Handle an event after processing by the content.
pub fn on_inner_event( pub fn on_inner_event(
&mut self, &mut self, event: Event, inner_result: EventResult,
event: Event,
inner_result: EventResult,
important_area: Rect, important_area: Rect,
) -> EventResult { ) -> EventResult {
match inner_result { match inner_result {
@ -365,9 +362,7 @@ impl Core {
/// Performs `View::call_on_any()` /// Performs `View::call_on_any()`
pub fn call_on_any<'a, F>( pub fn call_on_any<'a, F>(
&mut self, &mut self, selector: &Selector<'_>, cb: AnyCb<'a>,
selector: &Selector<'_>,
cb: AnyCb<'a>,
inner_call_on_any: F, inner_call_on_any: F,
) where ) where
F: FnOnce(&Selector, AnyCb), F: FnOnce(&Selector, AnyCb),
@ -377,9 +372,7 @@ impl Core {
/// Performs `View::focus_view()` /// Performs `View::focus_view()`
pub fn focus_view<F>( pub fn focus_view<F>(
&mut self, &mut self, selector: &Selector<'_>, inner_focus_view: F,
selector: &Selector<'_>,
inner_focus_view: F,
) -> Result<(), ()> ) -> Result<(), ()>
where where
F: FnOnce(&Selector) -> Result<(), ()>, F: FnOnce(&Selector) -> Result<(), ()>,
@ -413,8 +406,7 @@ impl Core {
/// Sets the padding between content and scrollbar. /// Sets the padding between content and scrollbar.
pub fn set_scrollbar_padding<V: Into<Vec2>>( pub fn set_scrollbar_padding<V: Into<Vec2>>(
&mut self, &mut self, scrollbar_padding: V,
scrollbar_padding: V,
) { ) {
self.scrollbar_padding = scrollbar_padding.into(); self.scrollbar_padding = scrollbar_padding.into();
} }
@ -423,8 +415,7 @@ impl Core {
/// ///
/// Chainable variant. /// Chainable variant.
pub fn scrollbar_padding<V: Into<Vec2>>( pub fn scrollbar_padding<V: Into<Vec2>>(
self, self, scrollbar_padding: V,
scrollbar_padding: V,
) -> Self { ) -> Self {
self.with(|s| s.set_scrollbar_padding(scrollbar_padding)) self.with(|s| s.set_scrollbar_padding(scrollbar_padding))
} }

View File

@ -54,9 +54,7 @@ impl Default for ScrollStrategy {
/// } /// }
/// ``` /// ```
pub fn on_event<T, OnEvent, ImportantArea>( pub fn on_event<T, OnEvent, ImportantArea>(
scroller: &mut T, scroller: &mut T, event: Event, on_event: OnEvent,
event: Event,
on_event: OnEvent,
important_area: ImportantArea, important_area: ImportantArea,
) -> EventResult ) -> EventResult
where where
@ -75,9 +73,7 @@ where
/// Performs `View::important_area` on a `scroll::Scroller`. /// Performs `View::important_area` on a `scroll::Scroller`.
pub fn important_area<T, ImportantArea>( pub fn important_area<T, ImportantArea>(
scroller: &T, scroller: &T, size: Vec2, mut important_area: ImportantArea,
size: Vec2,
mut important_area: ImportantArea,
) -> Rect ) -> Rect
where where
T: Scroller, T: Scroller,
@ -96,10 +92,7 @@ where
/// Performs `View::layout` on a `scroll::Scroller`. /// Performs `View::layout` on a `scroll::Scroller`.
pub fn layout<T, Layout, RequiredSize>( pub fn layout<T, Layout, RequiredSize>(
scroller: &mut T, scroller: &mut T, size: Vec2, needs_relayout: bool, layout: Layout,
size: Vec2,
needs_relayout: bool,
layout: Layout,
required_size: RequiredSize, required_size: RequiredSize,
) where ) where
T: Scroller, T: Scroller,
@ -118,9 +111,7 @@ pub fn layout<T, Layout, RequiredSize>(
/// Performs `View::required_size` on a `scroll::Scroller`. /// Performs `View::required_size` on a `scroll::Scroller`.
pub fn required_size<T, RequiredSize>( pub fn required_size<T, RequiredSize>(
scroller: &mut T, scroller: &mut T, size: Vec2, needs_relayout: bool,
size: Vec2,
needs_relayout: bool,
required_size: RequiredSize, required_size: RequiredSize,
) -> Vec2 ) -> Vec2
where where
@ -149,9 +140,7 @@ where
/// ///
/// This is an alternative to `scroll::draw()` when you just need to print individual lines. /// This is an alternative to `scroll::draw()` when you just need to print individual lines.
pub fn draw_lines<T, LineDrawer>( pub fn draw_lines<T, LineDrawer>(
scroller: &T, scroller: &T, printer: &Printer, mut line_drawer: LineDrawer,
printer: &Printer,
mut line_drawer: LineDrawer,
) where ) where
T: Scroller, T: Scroller,
LineDrawer: FnMut(&T, &Printer, usize), LineDrawer: FnMut(&T, &Printer, usize),
@ -170,11 +159,8 @@ pub fn draw_lines<T, LineDrawer>(
/// ///
/// `left_border` will be called for each row to draw the left border for the given line number. /// `left_border` will be called for each row to draw the left border for the given line number.
pub fn draw_frame<T, LeftBorder, TopBorder, RightBorder, BottomBorder>( pub fn draw_frame<T, LeftBorder, TopBorder, RightBorder, BottomBorder>(
scroller: &T, scroller: &T, printer: &Printer, mut left_border: LeftBorder,
printer: &Printer, mut top_border: TopBorder, mut right_border: RightBorder,
mut left_border: LeftBorder,
mut top_border: TopBorder,
mut right_border: RightBorder,
mut bottom_border: BottomBorder, mut bottom_border: BottomBorder,
) where ) where
T: Scroller, T: Scroller,
@ -224,9 +210,7 @@ pub fn draw_frame<T, LeftBorder, TopBorder, RightBorder, BottomBorder>(
/// ///
/// It will print a box with the appropriate `├`, `┤` and so on. /// It will print a box with the appropriate `├`, `┤` and so on.
pub fn draw_box_frame<T, IsHDelim, IsVDelim>( pub fn draw_box_frame<T, IsHDelim, IsVDelim>(
scroller: &T, scroller: &T, printer: &Printer, is_h_delim: IsHDelim,
printer: &Printer,
is_h_delim: IsHDelim,
is_v_delim: IsVDelim, is_v_delim: IsVDelim,
) where ) where
T: Scroller, T: Scroller,

View File

@ -11,9 +11,7 @@ use crate::Vec2;
/// Implements `View::draw` over the `model`. /// Implements `View::draw` over the `model`.
pub fn draw<Model, GetScroller, Draw>( pub fn draw<Model, GetScroller, Draw>(
printer: &Printer, printer: &Printer, model: &Model, mut get_scroller: GetScroller,
model: &Model,
mut get_scroller: GetScroller,
inner_draw: Draw, inner_draw: Draw,
) where ) where
Model: ?Sized, Model: ?Sized,
@ -32,12 +30,8 @@ pub fn draw<Model, GetScroller, Draw>(
/// ///
/// Returns (Inner size, Outer size, New scrolling) /// Returns (Inner size, Outer size, New scrolling)
fn sizes_when_scrolling<Model, GetScroller, RequiredSize>( fn sizes_when_scrolling<Model, GetScroller, RequiredSize>(
constraint: Vec2, constraint: Vec2, scrolling: XY<bool>, strict: bool, model: &mut Model,
scrolling: XY<bool>, get_scroller: &mut GetScroller, required_size: &mut RequiredSize,
strict: bool,
model: &mut Model,
get_scroller: &mut GetScroller,
required_size: &mut RequiredSize,
) -> (Vec2, Vec2, XY<bool>) ) -> (Vec2, Vec2, XY<bool>)
where where
Model: ?Sized, Model: ?Sized,
@ -83,12 +77,8 @@ where
/// ///
/// Returns (Inner size, Outer size) /// Returns (Inner size, Outer size)
fn sizes<Model, GetScroller, RequiredSize>( fn sizes<Model, GetScroller, RequiredSize>(
constraint: Vec2, constraint: Vec2, strict: bool, needs_relayout: bool, model: &mut Model,
strict: bool, get_scroller: &mut GetScroller, required_size: &mut RequiredSize,
needs_relayout: bool,
model: &mut Model,
get_scroller: &mut GetScroller,
required_size: &mut RequiredSize,
) -> (Vec2, Vec2) ) -> (Vec2, Vec2)
where where
Model: ?Sized, Model: ?Sized,
@ -151,11 +141,8 @@ where
/// Implements `View::layout` on the given model. /// Implements `View::layout` on the given model.
pub fn layout<Model, GetScroller, RequiredSize, Layout>( pub fn layout<Model, GetScroller, RequiredSize, Layout>(
size: Vec2, size: Vec2, needs_relayout: bool, model: &mut Model,
needs_relayout: bool, mut get_scroller: GetScroller, mut required_size: RequiredSize,
model: &mut Model,
mut get_scroller: GetScroller,
mut required_size: RequiredSize,
mut layout: Layout, mut layout: Layout,
) where ) where
Model: ?Sized, Model: ?Sized,
@ -185,11 +172,8 @@ pub fn layout<Model, GetScroller, RequiredSize, Layout>(
/// Implements `View::required_size` on the given model. /// Implements `View::required_size` on the given model.
pub fn required_size<Model, GetScroller, RequiredSize>( pub fn required_size<Model, GetScroller, RequiredSize>(
constraint: Vec2, constraint: Vec2, needs_relayout: bool, model: &mut Model,
needs_relayout: bool, mut get_scroller: GetScroller, mut required_size: RequiredSize,
model: &mut Model,
mut get_scroller: GetScroller,
mut required_size: RequiredSize,
) -> Vec2 ) -> Vec2
where where
Model: ?Sized, Model: ?Sized,
@ -210,11 +194,8 @@ where
/// Implements `View::on_event` on the given model. /// Implements `View::on_event` on the given model.
pub fn on_event<Model, GetScroller, OnEvent, ImportantArea>( pub fn on_event<Model, GetScroller, OnEvent, ImportantArea>(
event: Event, event: Event, model: &mut Model, mut get_scroller: GetScroller,
model: &mut Model, mut on_event: OnEvent, mut important_area: ImportantArea,
mut get_scroller: GetScroller,
mut on_event: OnEvent,
mut important_area: ImportantArea,
) -> EventResult ) -> EventResult
where where
Model: ?Sized, Model: ?Sized,