aboutsummaryrefslogtreecommitdiff
path: root/alacritty/src/display/damage.rs
diff options
context:
space:
mode:
authorKirill Chibisov <contact@kchibisov.com>2023-11-23 16:48:09 +0400
committerGitHub <noreply@github.com>2023-11-23 16:48:09 +0400
commit40160c5da1fafeab3ccedc52efe2c135f1eab843 (patch)
treeb763e853844e6292535300534aaf68597d708e3d /alacritty/src/display/damage.rs
parent0589b7189445e5ee236a7ab17b4f3a2047543481 (diff)
downloadr-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.rs184
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 }
}