From b6aaf829b86d8b8ab443510c8ff815677dd419eb Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Sat, 21 Jul 2018 14:57:24 -0700 Subject: [PATCH] add ClearToEndOfScreen operation --- src/render/terminfo.rs | 24 ++++++++++++++++++++++++ src/render/windows.rs | 30 ++++++++++++++++++++++++++++++ src/screen.rs | 37 ++++++++++++++++++++++++++++++++++++- 3 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/render/terminfo.rs b/src/render/terminfo.rs index 8250f81d1..6760164a5 100644 --- a/src/render/terminfo.rs +++ b/src/render/terminfo.rs @@ -331,6 +331,30 @@ impl TerminfoRenderer { )?; } } + Change::ClearToEndOfScreen(color) => { + // ClearScreen implicitly resets all to default + let defaults = CellAttributes::default() + .set_background(color.clone()) + .clone(); + if self.current_attr != defaults { + self.pending_attr = Some(defaults); + self.flush_pending_attr(out)?; + } + self.pending_attr = None; + + // FIXME: this doesn't behave correctly for terminals without bce. + // If we knew the current cursor position, we would be able to + // emit the correctly colored background for that case. + if let Some(clr) = self.get_capability::() { + clr.expand().to(out.by_ref())?; + } else { + write!( + out, + "{}", + CSI::Edit(Edit::EraseInDisplay(EraseInDisplay::EraseToEndOfDisplay)) + )?; + } + } Change::Attribute(AttributeChange::Intensity(value)) => { record!(set_intensity, value); } diff --git a/src/render/windows.rs b/src/render/windows.rs index 9ff511074..063e84bd6 100644 --- a/src/render/windows.rs +++ b/src/render/windows.rs @@ -170,6 +170,36 @@ impl WindowsConsoleRenderer { width, )?; } + Change::ClearToEndOfScreen(color) => { + out.flush()?; + self.current_attr = CellAttributes::default() + .set_background(color.clone()) + .clone(); + + let info = out.get_buffer_info()?; + let width = + (info.dwSize.X as u32).saturating_sub(info.dwCursorPosition.X as u32); + out.fill_char(' ', info.dwCursorPosition.X, info.dwCursorPosition.Y, width)?; + out.fill_attr( + to_attr_word(&self.current_attr), + info.dwCursorPosition.X, + info.dwCursorPosition.Y, + width, + )?; + // Clear the full width of the buffer (not the viewport size) + let visible_width = info.dwSize.X as u32; + // And clear all of the visible lines below the cursor + let visible_height = + (info.dwSize.Y as u32).saturating_sub((info.dwCursorPosition.Y as u32) + 1); + let num_spaces = visible_width * visible_height; + out.fill_char(' ', 0, info.dwCursorPosition.Y + 1, num_spaces as u32)?; + out.fill_attr( + to_attr_word(&self.current_attr), + 0, + info.dwCursorPosition.Y + 1, + num_spaces as u32, + )?; + } Change::Text(text) => { out.flush()?; out.set_attr(to_attr_word(&self.current_attr))?; diff --git a/src/screen.rs b/src/screen.rs index 398cad086..c7bab6c6b 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -44,7 +44,12 @@ pub enum Change { /// edge of the screen. The background color is set to the /// provided color. The cursor position remains unchanged. ClearToEndOfLine(ColorAttribute), - // ClearToEndOfScreen, + /// Clear from the current cursor X position to the rightmost + /// edge of the screen on the current line. Clear all of the + /// lines below the current cursor Y position. The background + /// color is set ot the provided color. The cursor position + /// remains unchanged. + ClearToEndOfScreen(ColorAttribute), /// Move the cursor to the specified `Position`. CursorPosition { x: Position, y: Position }, /* CursorVisibility(bool), @@ -250,6 +255,7 @@ impl Screen { Change::CursorPosition { x, y } => self.set_cursor_pos(x, y), Change::ClearScreen(color) => self.clear_screen(color), Change::ClearToEndOfLine(color) => self.clear_eol(color), + Change::ClearToEndOfScreen(color) => self.clear_eos(color), } } @@ -265,6 +271,21 @@ impl Screen { } } + fn clear_eos(&mut self, color: &ColorAttribute) { + self.attributes = CellAttributes::default() + .set_background(color.clone()) + .clone(); + let cleared = Cell::new(' ', self.attributes.clone()); + for cell in self.lines[self.ypos].cells.iter_mut().skip(self.xpos) { + *cell = cleared.clone(); + } + for line in &mut self.lines.iter_mut().skip(self.ypos + 1) { + for cell in &mut line.cells { + *cell = cleared.clone(); + } + } + } + fn clear_eol(&mut self, color: &ColorAttribute) { self.attributes = CellAttributes::default() .set_background(color.clone()) @@ -758,6 +779,20 @@ mod test { assert_eq!(s.screen_chars_to_string(), " \nw \nfoo\n"); } + #[test] + fn clear_eos() { + let mut s = Screen::new(3, 3); + s.add_change("helwowfoo"); + s.add_change(Change::ClearToEndOfScreen(Default::default())); + assert_eq!(s.screen_chars_to_string(), "hel\nwow\nfoo\n"); + s.add_change(Change::CursorPosition { + x: Position::Absolute(1), + y: Position::Absolute(1), + }); + s.add_change(Change::ClearToEndOfScreen(Default::default())); + assert_eq!(s.screen_chars_to_string(), "hel\nw \n \n"); + } + #[test] fn cursor_movement() { let mut s = Screen::new(4, 3);