mirror of
https://github.com/alacritty/alacritty.git
synced 2025-04-14 17:53:03 -04:00
Add damage tracking and reporting to compatible compositors
This allows compositors to only process damaged (that is, updated) regions of our window buffer, which for larger window sizes (think 4k) should significantly reduce compositing workload under compositors that support/honor it, which is good for performance, battery life and lower latency over remote connections like VNC. On Wayland, clients are expected to always report correct damage, so this makes us a good citizen there. It can also aid remote desktop (waypipe, rdp, vnc, ...) and other types of screencopy by having damage bubble up correctly. Fixes #3186.
This commit is contained in:
parent
d58dff18ef
commit
8f1abe13e6
19 changed files with 998 additions and 142 deletions
|
@ -13,7 +13,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
|
||||
### Added
|
||||
|
||||
- Option `font.builtin_box_drawing` to disable the built-in font for drawing box characters
|
||||
- Option `font.builtin_box_drawing` to disable the built-in font for drawing box characters
|
||||
- Track and report surface damage information to Wayland compositors
|
||||
|
||||
### Changed
|
||||
|
||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -56,7 +56,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "alacritty_terminal"
|
||||
version = "0.16.1-dev"
|
||||
version = "0.17.0-dev"
|
||||
dependencies = [
|
||||
"alacritty_config_derive",
|
||||
"base64",
|
||||
|
|
|
@ -876,3 +876,6 @@
|
|||
|
||||
# Print all received window events.
|
||||
#print_events: false
|
||||
|
||||
# Highlight window damage information.
|
||||
#highlight_damage: false
|
||||
|
|
|
@ -11,7 +11,7 @@ rust-version = "1.56.0"
|
|||
|
||||
[dependencies.alacritty_terminal]
|
||||
path = "../alacritty_terminal"
|
||||
version = "0.16.1-dev"
|
||||
version = "0.17.0-dev"
|
||||
default-features = false
|
||||
|
||||
[dependencies.alacritty_config_derive]
|
||||
|
|
|
@ -15,6 +15,9 @@ pub struct Debug {
|
|||
/// Should show render timer.
|
||||
pub render_timer: bool,
|
||||
|
||||
/// Highlight damage information produced by alacritty.
|
||||
pub highlight_damage: bool,
|
||||
|
||||
/// Record ref test.
|
||||
#[config(skip)]
|
||||
pub ref_test: bool,
|
||||
|
@ -27,6 +30,7 @@ impl Default for Debug {
|
|||
print_events: Default::default(),
|
||||
persistent_logging: Default::default(),
|
||||
render_timer: Default::default(),
|
||||
highlight_damage: Default::default(),
|
||||
ref_test: Default::default(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use alacritty_terminal::ansi::{Color, CursorShape, NamedColor};
|
|||
use alacritty_terminal::event::EventListener;
|
||||
use alacritty_terminal::grid::{Dimensions, Indexed};
|
||||
use alacritty_terminal::index::{Column, Direction, Line, Point};
|
||||
use alacritty_terminal::selection::SelectionRange;
|
||||
use alacritty_terminal::term::cell::{Cell, Flags};
|
||||
use alacritty_terminal::term::color::{CellRgb, Rgb};
|
||||
use alacritty_terminal::term::search::{Match, RegexIter, RegexSearch};
|
||||
|
@ -26,7 +27,7 @@ pub const MIN_CURSOR_CONTRAST: f64 = 1.5;
|
|||
/// This provides the terminal cursor and an iterator over all non-empty cells.
|
||||
pub struct RenderableContent<'a> {
|
||||
terminal_content: TerminalContent<'a>,
|
||||
cursor: Option<RenderableCursor>,
|
||||
cursor: RenderableCursor,
|
||||
cursor_shape: CursorShape,
|
||||
cursor_point: Point<usize>,
|
||||
search: Option<Regex<'a>>,
|
||||
|
@ -73,7 +74,7 @@ impl<'a> RenderableContent<'a> {
|
|||
|
||||
Self {
|
||||
colors: &display.colors,
|
||||
cursor: None,
|
||||
cursor: RenderableCursor::new_hidden(),
|
||||
terminal_content,
|
||||
focused_match,
|
||||
cursor_shape,
|
||||
|
@ -90,7 +91,7 @@ impl<'a> RenderableContent<'a> {
|
|||
}
|
||||
|
||||
/// Get the terminal cursor.
|
||||
pub fn cursor(mut self) -> Option<RenderableCursor> {
|
||||
pub fn cursor(mut self) -> RenderableCursor {
|
||||
// Assure this function is only called after the iterator has been drained.
|
||||
debug_assert!(self.next().is_none());
|
||||
|
||||
|
@ -102,14 +103,14 @@ impl<'a> RenderableContent<'a> {
|
|||
self.terminal_content.colors[color].unwrap_or(self.colors[color])
|
||||
}
|
||||
|
||||
pub fn selection_range(&self) -> Option<SelectionRange> {
|
||||
self.terminal_content.selection
|
||||
}
|
||||
|
||||
/// Assemble the information required to render the terminal cursor.
|
||||
///
|
||||
/// This will return `None` when there is no cursor visible.
|
||||
fn renderable_cursor(&mut self, cell: &RenderableCell) -> Option<RenderableCursor> {
|
||||
if self.cursor_shape == CursorShape::Hidden {
|
||||
return None;
|
||||
}
|
||||
|
||||
fn renderable_cursor(&mut self, cell: &RenderableCell) -> RenderableCursor {
|
||||
// Cursor colors.
|
||||
let color = if self.terminal_content.mode.contains(TermMode::VI) {
|
||||
self.config.colors.vi_mode_cursor
|
||||
|
@ -134,13 +135,13 @@ impl<'a> RenderableContent<'a> {
|
|||
text_color = self.config.colors.primary.background;
|
||||
}
|
||||
|
||||
Some(RenderableCursor {
|
||||
RenderableCursor {
|
||||
is_wide: cell.flags.contains(Flags::WIDE_CHAR),
|
||||
shape: self.cursor_shape,
|
||||
point: self.cursor_point,
|
||||
cursor_color,
|
||||
text_color,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,18 +160,15 @@ impl<'a> Iterator for RenderableContent<'a> {
|
|||
|
||||
if self.cursor_point == cell.point {
|
||||
// Store the cursor which should be rendered.
|
||||
self.cursor = self.renderable_cursor(&cell).map(|cursor| {
|
||||
if cursor.shape == CursorShape::Block {
|
||||
cell.fg = cursor.text_color;
|
||||
cell.bg = cursor.cursor_color;
|
||||
self.cursor = self.renderable_cursor(&cell);
|
||||
if self.cursor.shape == CursorShape::Block {
|
||||
cell.fg = self.cursor.text_color;
|
||||
cell.bg = self.cursor.cursor_color;
|
||||
|
||||
// Since we draw Block cursor by drawing cell below it with a proper color,
|
||||
// we must adjust alpha to make it visible.
|
||||
cell.bg_alpha = 1.;
|
||||
}
|
||||
|
||||
cursor
|
||||
});
|
||||
// Since we draw Block cursor by drawing cell below it with a proper color,
|
||||
// we must adjust alpha to make it visible.
|
||||
cell.bg_alpha = 1.;
|
||||
}
|
||||
|
||||
return Some(cell);
|
||||
} else if !cell.is_empty() && !cell.flags.contains(Flags::WIDE_CHAR_SPACER) {
|
||||
|
@ -371,6 +369,17 @@ pub struct RenderableCursor {
|
|||
point: Point<usize>,
|
||||
}
|
||||
|
||||
impl RenderableCursor {
|
||||
fn new_hidden() -> Self {
|
||||
let shape = CursorShape::Hidden;
|
||||
let cursor_color = Rgb::default();
|
||||
let text_color = Rgb::default();
|
||||
let is_wide = false;
|
||||
let point = Point::default();
|
||||
Self { shape, cursor_color, text_color, is_wide, point }
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderableCursor {
|
||||
pub fn color(&self) -> Rgb {
|
||||
self.cursor_color
|
||||
|
|
86
alacritty/src/display/damage.rs
Normal file
86
alacritty/src/display/damage.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
use std::cmp;
|
||||
use std::iter::Peekable;
|
||||
|
||||
use glutin::Rect;
|
||||
|
||||
use alacritty_terminal::term::{LineDamageBounds, SizeInfo, TermDamageIterator};
|
||||
|
||||
/// Iterator which converts `alacritty_terminal` damage information into renderer damaged rects.
|
||||
pub struct RenderDamageIterator<'a> {
|
||||
damaged_lines: Peekable<TermDamageIterator<'a>>,
|
||||
size_info: SizeInfo<u32>,
|
||||
}
|
||||
|
||||
impl<'a> RenderDamageIterator<'a> {
|
||||
pub fn new(damaged_lines: TermDamageIterator<'a>, size_info: SizeInfo<u32>) -> Self {
|
||||
Self { damaged_lines: damaged_lines.peekable(), size_info }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rect_for_line(&self, line_damage: LineDamageBounds) -> Rect {
|
||||
let size_info = &self.size_info;
|
||||
let y_top = size_info.height() - size_info.padding_y();
|
||||
let x = size_info.padding_x() + line_damage.left as u32 * size_info.cell_width();
|
||||
let y = y_top - (line_damage.line + 1) as u32 * size_info.cell_height();
|
||||
let width = (line_damage.right - line_damage.left + 1) as u32 * size_info.cell_width();
|
||||
Rect { x, y, height: size_info.cell_height(), width }
|
||||
}
|
||||
|
||||
// Make sure to damage near cells to include wide chars.
|
||||
#[inline]
|
||||
fn overdamage(&self, mut rect: Rect) -> Rect {
|
||||
let size_info = &self.size_info;
|
||||
rect.x = rect.x.saturating_sub(size_info.cell_width());
|
||||
rect.width = cmp::min(size_info.width() - rect.x, rect.width + 2 * size_info.cell_width());
|
||||
rect.y = rect.y.saturating_sub(size_info.cell_height() / 2);
|
||||
rect.height = cmp::min(size_info.height() - rect.y, rect.height + size_info.cell_height());
|
||||
|
||||
rect
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for RenderDamageIterator<'a> {
|
||||
type Item = Rect;
|
||||
|
||||
fn next(&mut self) -> Option<Rect> {
|
||||
let line = self.damaged_lines.next()?;
|
||||
let mut total_damage_rect = self.overdamage(self.rect_for_line(line));
|
||||
|
||||
// Merge rectangles which overlap with each other.
|
||||
while let Some(line) = self.damaged_lines.peek().copied() {
|
||||
let next_rect = self.overdamage(self.rect_for_line(line));
|
||||
if !rects_overlap(total_damage_rect, next_rect) {
|
||||
break;
|
||||
}
|
||||
|
||||
total_damage_rect = merge_rects(total_damage_rect, next_rect);
|
||||
let _ = self.damaged_lines.next();
|
||||
}
|
||||
|
||||
Some(total_damage_rect)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if two given [`glutin::Rect`] overlap.
|
||||
fn rects_overlap(lhs: Rect, rhs: Rect) -> bool {
|
||||
!(
|
||||
// `lhs` is left of `rhs`.
|
||||
lhs.x + lhs.width < rhs.x
|
||||
// `lhs` is right of `rhs`.
|
||||
|| rhs.x + rhs.width < lhs.x
|
||||
// `lhs` is below `rhs`.
|
||||
|| lhs.y + lhs.height < rhs.y
|
||||
// `lhs` is above `rhs`.
|
||||
|| rhs.y + rhs.height < lhs.y
|
||||
)
|
||||
}
|
||||
|
||||
/// Merge two [`glutin::Rect`] by producing the smallest rectangle that contains both.
|
||||
#[inline]
|
||||
fn merge_rects(lhs: Rect, rhs: Rect) -> Rect {
|
||||
let left_x = cmp::min(lhs.x, rhs.x);
|
||||
let right_x = cmp::max(lhs.x + lhs.width, rhs.x + rhs.width);
|
||||
let y_top = cmp::max(lhs.y + lhs.height, rhs.y + rhs.height);
|
||||
let y_bottom = cmp::min(lhs.y, rhs.y);
|
||||
Rect { x: left_x, y: y_bottom, width: right_x - left_x, height: y_top - y_bottom }
|
||||
}
|
|
@ -31,7 +31,7 @@ pub struct Meter {
|
|||
/// Average sample time in microseconds.
|
||||
avg: f64,
|
||||
|
||||
/// Index of next time to update..
|
||||
/// Index of next time to update.
|
||||
index: usize,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
//! The display subsystem including window management, font rasterization, and
|
||||
//! GPU drawing.
|
||||
|
||||
use std::cmp::min;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{self, Formatter};
|
||||
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::time::Instant;
|
||||
use std::{f64, mem};
|
||||
use std::{cmp, mem};
|
||||
|
||||
use glutin::dpi::PhysicalSize;
|
||||
use glutin::event::ModifiersState;
|
||||
|
@ -15,6 +14,7 @@ use glutin::event_loop::EventLoopWindowTarget;
|
|||
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
|
||||
use glutin::platform::unix::EventLoopWindowTargetExtUnix;
|
||||
use glutin::window::CursorIcon;
|
||||
use glutin::Rect as DamageRect;
|
||||
use log::{debug, info};
|
||||
use parking_lot::MutexGuard;
|
||||
use unicode_width::UnicodeWidthChar;
|
||||
|
@ -24,12 +24,16 @@ use wayland_client::EventQueue;
|
|||
use crossfont::{self, Rasterize, Rasterizer};
|
||||
|
||||
use alacritty_terminal::ansi::NamedColor;
|
||||
use alacritty_terminal::config::MAX_SCROLLBACK_LINES;
|
||||
use alacritty_terminal::event::{EventListener, OnResize};
|
||||
use alacritty_terminal::grid::Dimensions as _;
|
||||
use alacritty_terminal::index::{Column, Direction, Line, Point};
|
||||
use alacritty_terminal::selection::Selection;
|
||||
use alacritty_terminal::selection::{Selection, SelectionRange};
|
||||
use alacritty_terminal::term::cell::Flags;
|
||||
use alacritty_terminal::term::{SizeInfo, Term, TermMode, MIN_COLUMNS, MIN_SCREEN_LINES};
|
||||
use alacritty_terminal::term::color::Rgb;
|
||||
use alacritty_terminal::term::{
|
||||
SizeInfo, Term, TermDamage, TermMode, MIN_COLUMNS, MIN_SCREEN_LINES,
|
||||
};
|
||||
|
||||
use crate::config::font::Font;
|
||||
#[cfg(not(windows))]
|
||||
|
@ -40,6 +44,7 @@ use crate::display::bell::VisualBell;
|
|||
use crate::display::color::List;
|
||||
use crate::display::content::RenderableContent;
|
||||
use crate::display::cursor::IntoRects;
|
||||
use crate::display::damage::RenderDamageIterator;
|
||||
use crate::display::hint::{HintMatch, HintState};
|
||||
use crate::display::meter::Meter;
|
||||
use crate::display::window::Window;
|
||||
|
@ -55,6 +60,7 @@ pub mod window;
|
|||
|
||||
mod bell;
|
||||
mod color;
|
||||
mod damage;
|
||||
mod meter;
|
||||
|
||||
/// Maximum number of linewraps followed outside of the viewport during search highlighting.
|
||||
|
@ -66,6 +72,9 @@ const FORWARD_SEARCH_LABEL: &str = "Search: ";
|
|||
/// Label for the backward terminal search bar.
|
||||
const BACKWARD_SEARCH_LABEL: &str = "Backward Search: ";
|
||||
|
||||
/// Color which is used to highlight damaged rects when debugging.
|
||||
const DAMAGE_RECT_COLOR: Rgb = Rgb { r: 255, g: 0, b: 255 };
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Error with window management.
|
||||
|
@ -193,6 +202,9 @@ pub struct Display {
|
|||
/// Unprocessed display updates.
|
||||
pub pending_update: DisplayUpdate,
|
||||
|
||||
is_damage_supported: bool,
|
||||
debug_damage: bool,
|
||||
damage_rects: Vec<DamageRect>,
|
||||
renderer: QuadRenderer,
|
||||
glyph_cache: GlyphCache,
|
||||
meter: Meter,
|
||||
|
@ -319,6 +331,13 @@ impl Display {
|
|||
}
|
||||
|
||||
let hint_state = HintState::new(config.hints.alphabet());
|
||||
let is_damage_supported = window.swap_buffers_with_damage_supported();
|
||||
let debug_damage = config.debug.highlight_damage;
|
||||
let damage_rects = if is_damage_supported || debug_damage {
|
||||
Vec::with_capacity(size_info.screen_lines())
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
window,
|
||||
|
@ -335,6 +354,9 @@ impl Display {
|
|||
visual_bell: VisualBell::from(&config.bell),
|
||||
colors: List::from(&config.colors),
|
||||
pending_update: Default::default(),
|
||||
is_damage_supported,
|
||||
debug_damage,
|
||||
damage_rects,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -457,10 +479,58 @@ impl Display {
|
|||
self.window.resize(physical);
|
||||
self.renderer.resize(&self.size_info);
|
||||
|
||||
if self.collect_damage() {
|
||||
let lines = self.size_info.screen_lines();
|
||||
if lines > self.damage_rects.len() {
|
||||
self.damage_rects.reserve(lines);
|
||||
} else {
|
||||
self.damage_rects.shrink_to(lines);
|
||||
}
|
||||
}
|
||||
|
||||
info!("Padding: {} x {}", self.size_info.padding_x(), self.size_info.padding_y());
|
||||
info!("Width: {}, Height: {}", self.size_info.width(), self.size_info.height());
|
||||
}
|
||||
|
||||
fn update_damage<T: EventListener>(
|
||||
&mut self,
|
||||
terminal: &mut MutexGuard<'_, Term<T>>,
|
||||
selection_range: Option<SelectionRange>,
|
||||
search_state: &SearchState,
|
||||
) {
|
||||
let requires_full_damage = self.visual_bell.intensity() != 0.
|
||||
|| self.hint_state.active()
|
||||
|| search_state.regex().is_some();
|
||||
if requires_full_damage {
|
||||
terminal.mark_fully_damaged();
|
||||
}
|
||||
|
||||
self.damage_highlighted_hints(terminal);
|
||||
let size_info: SizeInfo<u32> = self.size_info.into();
|
||||
match terminal.damage(selection_range) {
|
||||
TermDamage::Full => {
|
||||
let screen_rect =
|
||||
DamageRect { x: 0, y: 0, width: size_info.width(), height: size_info.height() };
|
||||
self.damage_rects.push(screen_rect);
|
||||
},
|
||||
TermDamage::Partial(damaged_lines) => {
|
||||
let damaged_rects = RenderDamageIterator::new(damaged_lines, size_info);
|
||||
for damaged_rect in damaged_rects {
|
||||
self.damage_rects.push(damaged_rect);
|
||||
}
|
||||
},
|
||||
}
|
||||
terminal.reset_damage();
|
||||
|
||||
// Ensure that the content requiring full damage is cleaned up again on the next frame.
|
||||
if requires_full_damage {
|
||||
terminal.mark_fully_damaged();
|
||||
}
|
||||
|
||||
// Damage highlighted hints for the next frame as well, so we'll clear them.
|
||||
self.damage_highlighted_hints(terminal);
|
||||
}
|
||||
|
||||
/// Draw the screen.
|
||||
///
|
||||
/// A reference to Term whose state is being drawn must be provided.
|
||||
|
@ -468,7 +538,7 @@ impl Display {
|
|||
/// This call may block if vsync is enabled.
|
||||
pub fn draw<T: EventListener>(
|
||||
&mut self,
|
||||
terminal: MutexGuard<'_, Term<T>>,
|
||||
mut terminal: MutexGuard<'_, Term<T>>,
|
||||
message_buffer: &MessageBuffer,
|
||||
config: &UiConfig,
|
||||
search_state: &SearchState,
|
||||
|
@ -479,6 +549,7 @@ impl Display {
|
|||
for cell in &mut content {
|
||||
grid_cells.push(cell);
|
||||
}
|
||||
let selection_range = content.selection_range();
|
||||
let background_color = content.color(NamedColor::Background as usize);
|
||||
let display_offset = content.display_offset();
|
||||
let cursor = content.cursor();
|
||||
|
@ -491,6 +562,11 @@ impl Display {
|
|||
let vi_mode = terminal.mode().contains(TermMode::VI);
|
||||
let vi_mode_cursor = if vi_mode { Some(terminal.vi_mode_cursor) } else { None };
|
||||
|
||||
if self.collect_damage() {
|
||||
self.damage_rects.clear();
|
||||
self.update_damage(&mut terminal, selection_range, search_state);
|
||||
}
|
||||
|
||||
// Drop terminal as early as possible to free lock.
|
||||
drop(terminal);
|
||||
|
||||
|
@ -549,11 +625,9 @@ impl Display {
|
|||
self.draw_line_indicator(config, &size_info, total_lines, None, display_offset);
|
||||
}
|
||||
|
||||
// Push the cursor rects for rendering.
|
||||
if let Some(cursor) = cursor {
|
||||
for rect in cursor.rects(&size_info, config.terminal_config.cursor.thickness()) {
|
||||
rects.push(rect);
|
||||
}
|
||||
// Draw cursor.
|
||||
for rect in cursor.rects(&size_info, config.terminal_config.cursor.thickness()) {
|
||||
rects.push(rect);
|
||||
}
|
||||
|
||||
// Push visual bell after url/underline/strikeout rects.
|
||||
|
@ -570,6 +644,10 @@ impl Display {
|
|||
rects.push(visual_bell_rect);
|
||||
}
|
||||
|
||||
if self.debug_damage {
|
||||
self.highlight_damage(&mut rects);
|
||||
}
|
||||
|
||||
if let Some(message) = message_buffer.message() {
|
||||
let search_offset = if search_state.regex().is_some() { 1 } else { 0 };
|
||||
let text = message.text(&size_info);
|
||||
|
@ -636,7 +714,12 @@ impl Display {
|
|||
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
||||
self.request_frame(&self.window);
|
||||
|
||||
self.window.swap_buffers();
|
||||
// Clearing debug highlights from the previous frame requires full redraw.
|
||||
if self.is_damage_supported && !self.debug_damage {
|
||||
self.window.swap_buffers_with_damage(&self.damage_rects);
|
||||
} else {
|
||||
self.window.swap_buffers();
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
|
||||
if self.is_x11 {
|
||||
|
@ -651,6 +734,7 @@ impl Display {
|
|||
|
||||
/// Update to a new configuration.
|
||||
pub fn update_config(&mut self, config: &UiConfig) {
|
||||
self.debug_damage = config.debug.highlight_damage;
|
||||
self.visual_bell.update_config(&config.bell);
|
||||
self.colors = List::from(&config.colors);
|
||||
}
|
||||
|
@ -722,7 +806,7 @@ impl Display {
|
|||
let num_cols = size_info.columns();
|
||||
let label_len = search_label.chars().count();
|
||||
let regex_len = formatted_regex.chars().count();
|
||||
let truncate_len = min((regex_len + label_len).saturating_sub(num_cols), regex_len);
|
||||
let truncate_len = cmp::min((regex_len + label_len).saturating_sub(num_cols), regex_len);
|
||||
let index = formatted_regex.char_indices().nth(truncate_len).map(|(i, _c)| i).unwrap_or(0);
|
||||
let truncated_regex = &formatted_regex[index..];
|
||||
|
||||
|
@ -758,13 +842,15 @@ impl Display {
|
|||
return;
|
||||
}
|
||||
|
||||
let glyph_cache = &mut self.glyph_cache;
|
||||
|
||||
let timing = format!("{:.3} usec", self.meter.average());
|
||||
let point = Point::new(size_info.screen_lines().saturating_sub(2), Column(0));
|
||||
let fg = config.colors.primary.background;
|
||||
let bg = config.colors.normal.red;
|
||||
|
||||
// Damage the entire line.
|
||||
self.damage_from_point(point, self.size_info.columns() as u32);
|
||||
|
||||
let glyph_cache = &mut self.glyph_cache;
|
||||
self.renderer.with_api(config, size_info, |mut api| {
|
||||
api.draw_string(glyph_cache, point, fg, bg, &timing);
|
||||
});
|
||||
|
@ -779,8 +865,26 @@ impl Display {
|
|||
obstructed_column: Option<Column>,
|
||||
line: usize,
|
||||
) {
|
||||
const fn num_digits(mut number: u32) -> usize {
|
||||
let mut res = 0;
|
||||
loop {
|
||||
number /= 10;
|
||||
res += 1;
|
||||
if number == 0 {
|
||||
break res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let text = format!("[{}/{}]", line, total_lines - 1);
|
||||
let column = Column(size_info.columns().saturating_sub(text.len()));
|
||||
let point = Point::new(0, column);
|
||||
|
||||
// Damage the maximum possible length of the format text, which could be achieved when
|
||||
// using `MAX_SCROLLBACK_LINES` as current and total lines adding a `3` for formatting.
|
||||
const MAX_LEN: usize = num_digits(MAX_SCROLLBACK_LINES) + 3;
|
||||
self.damage_from_point(Point::new(0, point.column - MAX_LEN), MAX_LEN as u32 * 2);
|
||||
|
||||
let colors = &config.colors;
|
||||
let fg = colors.line_indicator.foreground.unwrap_or(colors.primary.background);
|
||||
let bg = colors.line_indicator.background.unwrap_or(colors.primary.foreground);
|
||||
|
@ -789,11 +893,60 @@ impl Display {
|
|||
if obstructed_column.map_or(true, |obstructed_column| obstructed_column < column) {
|
||||
let glyph_cache = &mut self.glyph_cache;
|
||||
self.renderer.with_api(config, size_info, |mut api| {
|
||||
api.draw_string(glyph_cache, Point::new(0, column), fg, bg, &text);
|
||||
api.draw_string(glyph_cache, point, fg, bg, &text);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Damage `len` starting from a `point`.
|
||||
#[inline]
|
||||
fn damage_from_point(&mut self, point: Point<usize>, len: u32) {
|
||||
if !self.collect_damage() {
|
||||
return;
|
||||
}
|
||||
|
||||
let size_info: SizeInfo<u32> = self.size_info.into();
|
||||
let x = size_info.padding_x() + point.column.0 as u32 * size_info.cell_width();
|
||||
let y_top = size_info.height() - size_info.padding_y();
|
||||
let y = y_top - (point.line as u32 + 1) * size_info.cell_height();
|
||||
let width = len as u32 * size_info.cell_width();
|
||||
self.damage_rects.push(DamageRect { x, y, width, height: size_info.cell_height() })
|
||||
}
|
||||
|
||||
/// Damage currently highlighted `Display` hints.
|
||||
#[inline]
|
||||
fn damage_highlighted_hints<T: EventListener>(&self, terminal: &mut Term<T>) {
|
||||
let display_offset = terminal.grid().display_offset();
|
||||
for hint in self.highlighted_hint.iter().chain(&self.vi_highlighted_hint) {
|
||||
for point in (hint.bounds.start().line.0..=hint.bounds.end().line.0).flat_map(|line| {
|
||||
point_to_viewport(display_offset, Point::new(Line(line), Column(0)))
|
||||
}) {
|
||||
terminal.damage_line(point.line, 0, terminal.columns() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if damage information should be collected, `false` otherwise.
|
||||
#[inline]
|
||||
fn collect_damage(&self) -> bool {
|
||||
self.is_damage_supported || self.debug_damage
|
||||
}
|
||||
|
||||
/// Highlight damaged rects.
|
||||
///
|
||||
/// This function is for debug purposes only.
|
||||
fn highlight_damage(&self, render_rects: &mut Vec<RenderRect>) {
|
||||
for damage_rect in &self.damage_rects {
|
||||
let x = damage_rect.x as f32;
|
||||
let height = damage_rect.height as f32;
|
||||
let width = damage_rect.width as f32;
|
||||
let y = self.size_info.height() - damage_rect.y as f32 - height;
|
||||
let render_rect = RenderRect::new(x, y, width, height, DAMAGE_RECT_COLOR, 0.5);
|
||||
|
||||
render_rects.push(render_rect);
|
||||
}
|
||||
}
|
||||
|
||||
/// Requst a new frame for a window on Wayland.
|
||||
#[inline]
|
||||
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
|
||||
|
@ -824,12 +977,14 @@ impl Drop for Display {
|
|||
}
|
||||
|
||||
/// Convert a terminal point to a viewport relative point.
|
||||
#[inline]
|
||||
pub fn point_to_viewport(display_offset: usize, point: Point) -> Option<Point<usize>> {
|
||||
let viewport_line = point.line.0 + display_offset as i32;
|
||||
usize::try_from(viewport_line).ok().map(|line| Point::new(line, point.column))
|
||||
}
|
||||
|
||||
/// Convert a viewport relative point to a terminal point.
|
||||
#[inline]
|
||||
pub fn viewport_to_point(display_offset: usize, point: Point<usize>) -> Point {
|
||||
let line = Line(point.line as i32) - display_offset;
|
||||
Point::new(line, point.column)
|
||||
|
|
|
@ -39,7 +39,7 @@ use glutin::platform::windows::IconExtWindows;
|
|||
use glutin::window::{
|
||||
CursorIcon, Fullscreen, UserAttentionType, Window as GlutinWindow, WindowBuilder, WindowId,
|
||||
};
|
||||
use glutin::{self, ContextBuilder, PossiblyCurrent, WindowedContext};
|
||||
use glutin::{self, ContextBuilder, PossiblyCurrent, Rect, WindowedContext};
|
||||
#[cfg(target_os = "macos")]
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
#[cfg(target_os = "macos")]
|
||||
|
@ -428,6 +428,14 @@ impl Window {
|
|||
self.windowed_context.swap_buffers().expect("swap buffers");
|
||||
}
|
||||
|
||||
pub fn swap_buffers_with_damage(&self, damage: &[Rect]) {
|
||||
self.windowed_context.swap_buffers_with_damage(damage).expect("swap buffes with damage");
|
||||
}
|
||||
|
||||
pub fn swap_buffers_with_damage_supported(&self) -> bool {
|
||||
self.windowed_context.swap_buffers_with_damage_supported()
|
||||
}
|
||||
|
||||
pub fn resize(&self, size: PhysicalSize<u32>) {
|
||||
self.windowed_context.resize(size);
|
||||
}
|
||||
|
|
|
@ -768,7 +768,11 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
|||
/// Toggle the vi mode status.
|
||||
#[inline]
|
||||
fn toggle_vi_mode(&mut self) {
|
||||
if !self.terminal.mode().contains(TermMode::VI) {
|
||||
if self.terminal.mode().contains(TermMode::VI) {
|
||||
// Damage line indicator and Vi cursor if we're leaving Vi mode.
|
||||
self.terminal.damage_vi_cursor();
|
||||
self.terminal.damage_line(0, 0, self.terminal.columns() - 1);
|
||||
} else {
|
||||
self.clear_selection();
|
||||
}
|
||||
|
||||
|
|
|
@ -801,7 +801,9 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
|
|||
|
||||
self.ctx.on_typing_start();
|
||||
|
||||
self.ctx.scroll(Scroll::Bottom);
|
||||
if self.ctx.terminal().grid().display_offset() != 0 {
|
||||
self.ctx.scroll(Scroll::Bottom);
|
||||
}
|
||||
self.ctx.clear_selection();
|
||||
|
||||
let utf8_len = c.len_utf8();
|
||||
|
|
|
@ -784,7 +784,7 @@ impl Canvas {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crossfont::Metrics;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "alacritty_terminal"
|
||||
version = "0.16.1-dev"
|
||||
version = "0.17.0-dev"
|
||||
authors = ["Christian Duerr <contact@christianduerr.com>", "Joe Wilm <joe@jwilm.com>"]
|
||||
license = "Apache-2.0"
|
||||
description = "Library for writing terminal emulators"
|
||||
|
|
|
@ -10,7 +10,7 @@ mod scrolling;
|
|||
|
||||
use crate::ansi::{CursorShape, CursorStyle};
|
||||
|
||||
pub use crate::config::scrolling::Scrolling;
|
||||
pub use crate::config::scrolling::{Scrolling, MAX_SCROLLBACK_LINES};
|
||||
|
||||
pub const LOG_TARGET_CONFIG: &str = "alacritty_config_derive";
|
||||
const MIN_BLINK_INTERVAL: u64 = 10;
|
||||
|
|
|
@ -4,7 +4,7 @@ use serde::{Deserialize, Deserializer};
|
|||
use alacritty_config_derive::ConfigDeserialize;
|
||||
|
||||
/// Maximum scrollback amount configurable.
|
||||
const MAX_SCROLLBACK_LINES: u32 = 100_000;
|
||||
pub const MAX_SCROLLBACK_LINES: u32 = 100_000;
|
||||
|
||||
/// Struct for scrolling related settings.
|
||||
#[derive(ConfigDeserialize, Copy, Clone, Debug, PartialEq, Eq)]
|
||||
|
|
|
@ -41,6 +41,7 @@ pub struct SelectionRange {
|
|||
|
||||
impl SelectionRange {
|
||||
pub fn new(start: Point, end: Point, is_block: bool) -> Self {
|
||||
assert!(start <= end);
|
||||
Self { start, end, is_block }
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -52,7 +52,7 @@ pub enum ViMotion {
|
|||
}
|
||||
|
||||
/// Cursor tracking vi mode position.
|
||||
#[derive(Default, Copy, Clone)]
|
||||
#[derive(Default, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct ViModeCursor {
|
||||
pub point: Point,
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue