diff options
author | Kirill Chibisov <contact@kchibisov.com> | 2023-11-23 16:48:09 +0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-23 16:48:09 +0400 |
commit | 40160c5da1fafeab3ccedc52efe2c135f1eab843 (patch) | |
tree | b763e853844e6292535300534aaf68597d708e3d /alacritty/src/display/damage.rs | |
parent | 0589b7189445e5ee236a7ab17b4f3a2047543481 (diff) | |
download | r-alacritty-40160c5da1fafeab3ccedc52efe2c135f1eab843.tar.gz r-alacritty-40160c5da1fafeab3ccedc52efe2c135f1eab843.tar.bz2 r-alacritty-40160c5da1fafeab3ccedc52efe2c135f1eab843.zip |
Damage only terminal inside `alacritty_terminal`
The damage tracking was including selection and vi_cursor which were
rendering viewport related, however all the damage tracking inside
the `alacritty_terminal` was _terminal viewport_ related, meaning that
it should be affected by `display_offset`.
Refactor the damage tracking so `alacritty_terminal` is only tracking
actual terminal updates and properly applying display offset to them,
while `alacritty` pulls this damage into its own UI damage state.
Fixes #7111.
Diffstat (limited to 'alacritty/src/display/damage.rs')
-rw-r--r-- | alacritty/src/display/damage.rs | 184 |
1 files changed, 180 insertions, 4 deletions
diff --git a/alacritty/src/display/damage.rs b/alacritty/src/display/damage.rs index 82230dff..24033fa5 100644 --- a/alacritty/src/display/damage.rs +++ b/alacritty/src/display/damage.rs @@ -1,20 +1,196 @@ -use std::cmp; use std::iter::Peekable; +use std::{cmp, mem}; use glutin::surface::Rect; +use alacritty_terminal::index::Point; +use alacritty_terminal::selection::SelectionRange; use alacritty_terminal::term::{LineDamageBounds, TermDamageIterator}; use crate::display::SizeInfo; +/// State of the damage tracking for the [`Display`]. +/// +/// [`Display`]: crate::display::Display +#[derive(Debug)] +pub struct DamageTracker { + /// Position of the previously drawn Vi cursor. + pub old_vi_cursor: Option<Point<usize>>, + /// The location of the old selection. + pub old_selection: Option<SelectionRange>, + /// Highlight damage submitted for the compositor. + pub debug: bool, + + /// The damage for the frames. + frames: [FrameDamage; 2], + screen_lines: usize, + columns: usize, +} + +impl DamageTracker { + pub fn new(screen_lines: usize, columns: usize) -> Self { + let mut tracker = Self { + columns, + screen_lines, + debug: false, + old_vi_cursor: None, + old_selection: None, + frames: Default::default(), + }; + tracker.resize(screen_lines, columns); + tracker + } + + #[inline] + #[must_use] + pub fn frame(&mut self) -> &mut FrameDamage { + &mut self.frames[0] + } + + #[inline] + #[must_use] + pub fn next_frame(&mut self) -> &mut FrameDamage { + &mut self.frames[1] + } + + /// Advance to the next frame resetting the state for the active frame. + #[inline] + pub fn swap_damage(&mut self) { + let screen_lines = self.screen_lines; + let columns = self.columns; + self.frame().reset(screen_lines, columns); + self.frames.swap(0, 1); + } + + /// Resize the damage information in the tracker. + pub fn resize(&mut self, screen_lines: usize, columns: usize) { + self.screen_lines = screen_lines; + self.columns = columns; + for frame in &mut self.frames { + frame.reset(screen_lines, columns); + } + self.frame().full = true; + } + + /// Damage vi cursor inside the viewport. + pub fn damage_vi_cursor(&mut self, mut vi_cursor: Option<Point<usize>>) { + mem::swap(&mut self.old_vi_cursor, &mut vi_cursor); + + if self.frame().full { + return; + } + + if let Some(vi_cursor) = self.old_vi_cursor { + self.frame().damage_point(vi_cursor); + } + + if let Some(vi_cursor) = vi_cursor { + self.frame().damage_point(vi_cursor); + } + } + + /// Get shaped frame damage for the active frame. + pub fn shape_frame_damage(&self, size_info: SizeInfo<u32>) -> Vec<Rect> { + if self.frames[0].full { + vec![Rect::new(0, 0, size_info.width() as i32, size_info.height() as i32)] + } else { + let lines_damage = RenderDamageIterator::new( + TermDamageIterator::new(&self.frames[0].lines, 0), + &size_info, + ); + lines_damage.chain(self.frames[0].rects.iter().copied()).collect() + } + } + + /// Add the current frame's selection damage. + pub fn damage_selection( + &mut self, + mut selection: Option<SelectionRange>, + display_offset: usize, + ) { + mem::swap(&mut self.old_selection, &mut selection); + + if self.frame().full || selection == self.old_selection { + return; + } + + for selection in self.old_selection.into_iter().chain(selection) { + let display_offset = display_offset as i32; + let last_visible_line = self.screen_lines as i32 - 1; + let columns = self.columns; + + // Ignore invisible selection. + if selection.end.line.0 + display_offset < 0 + || selection.start.line.0.abs() < display_offset - last_visible_line + { + continue; + }; + + let start = cmp::max(selection.start.line.0 + display_offset, 0) as usize; + let end = (selection.end.line.0 + display_offset).clamp(0, last_visible_line) as usize; + for line in start..=end { + self.frame().lines[line].expand(0, columns - 1); + } + } + } +} + +/// Damage state for the rendering frame. +#[derive(Debug, Default)] +pub struct FrameDamage { + /// The entire frame needs to be redrawn. + full: bool, + /// Terminal lines damaged in the given frame. + lines: Vec<LineDamageBounds>, + /// Rectangular regions damage in the given frame. + rects: Vec<Rect>, +} + +impl FrameDamage { + /// Damage line for the given frame. + #[inline] + pub fn damage_line(&mut self, damage: LineDamageBounds) { + self.lines[damage.line].expand(damage.left, damage.right); + } + + #[inline] + pub fn damage_point(&mut self, point: Point<usize>) { + self.lines[point.line].expand(point.column.0, point.column.0); + } + + /// Mark the frame as fully damaged. + #[inline] + pub fn mark_fully_damaged(&mut self) { + self.full = true; + } + + /// Add a damage rectangle. + /// + /// This allows covering elements outside of the terminal viewport, like message bar. + #[inline] + pub fn add_rect(&mut self, x: i32, y: i32, width: i32, height: i32) { + self.rects.push(Rect { x, y, width, height }); + } + + fn reset(&mut self, num_lines: usize, num_cols: usize) { + self.full = false; + self.rects.clear(); + self.lines.clear(); + self.lines.reserve(num_lines); + for line in 0..num_lines { + self.lines.push(LineDamageBounds::undamaged(line, num_cols)); + } + } +} + /// Iterator which converts `alacritty_terminal` damage information into renderer damaged rects. -pub struct RenderDamageIterator<'a> { +struct RenderDamageIterator<'a> { damaged_lines: Peekable<TermDamageIterator<'a>>, - size_info: SizeInfo<u32>, + size_info: &'a SizeInfo<u32>, } impl<'a> RenderDamageIterator<'a> { - pub fn new(damaged_lines: TermDamageIterator<'a>, size_info: SizeInfo<u32>) -> Self { + pub fn new(damaged_lines: TermDamageIterator<'a>, size_info: &'a SizeInfo<u32>) -> Self { Self { damaged_lines: damaged_lines.peekable(), size_info } } |