aboutsummaryrefslogtreecommitdiff
path: root/alacritty/src
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty/src')
-rw-r--r--alacritty/src/config/debug.rs4
-rw-r--r--alacritty/src/display/content.rs53
-rw-r--r--alacritty/src/display/damage.rs86
-rw-r--r--alacritty/src/display/meter.rs2
-rw-r--r--alacritty/src/display/mod.rs185
-rw-r--r--alacritty/src/display/window.rs10
-rw-r--r--alacritty/src/event.rs6
-rw-r--r--alacritty/src/input.rs4
-rw-r--r--alacritty/src/renderer/builtin_font.rs2
9 files changed, 310 insertions, 42 deletions
diff --git a/alacritty/src/config/debug.rs b/alacritty/src/config/debug.rs
index f52cdf90..3fa987a5 100644
--- a/alacritty/src/config/debug.rs
+++ b/alacritty/src/config/debug.rs
@@ -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(),
}
}
diff --git a/alacritty/src/display/content.rs b/alacritty/src/display/content.rs
index 72d79f7e..3b549992 100644
--- a/alacritty/src/display/content.rs
+++ b/alacritty/src/display/content.rs
@@ -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;
-
- // 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
- });
+ 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.;
+ }
return Some(cell);
} else if !cell.is_empty() && !cell.flags.contains(Flags::WIDE_CHAR_SPACER) {
@@ -372,6 +370,17 @@ pub struct RenderableCursor {
}
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
}
diff --git a/alacritty/src/display/damage.rs b/alacritty/src/display/damage.rs
new file mode 100644
index 00000000..d6a69a2d
--- /dev/null
+++ b/alacritty/src/display/damage.rs
@@ -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 }
+}
diff --git a/alacritty/src/display/meter.rs b/alacritty/src/display/meter.rs
index c07d901f..9ccfe52d 100644
--- a/alacritty/src/display/meter.rs
+++ b/alacritty/src/display/meter.rs
@@ -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,
}
diff --git a/alacritty/src/display/mod.rs b/alacritty/src/display/mod.rs
index d9ec8593..7d53e678 100644
--- a/alacritty/src/display/mod.rs
+++ b/alacritty/src/display/mod.rs
@@ -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)
diff --git a/alacritty/src/display/window.rs b/alacritty/src/display/window.rs
index 493e5ef9..712b4ac9 100644
--- a/alacritty/src/display/window.rs
+++ b/alacritty/src/display/window.rs
@@ -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);
}
diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs
index aea6010d..8bd1dec7 100644
--- a/alacritty/src/event.rs
+++ b/alacritty/src/event.rs
@@ -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();
}
diff --git a/alacritty/src/input.rs b/alacritty/src/input.rs
index 0aa2cbba..51bd3fc5 100644
--- a/alacritty/src/input.rs
+++ b/alacritty/src/input.rs
@@ -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();
diff --git a/alacritty/src/renderer/builtin_font.rs b/alacritty/src/renderer/builtin_font.rs
index f3dbe9bb..05798466 100644
--- a/alacritty/src/renderer/builtin_font.rs
+++ b/alacritty/src/renderer/builtin_font.rs
@@ -784,7 +784,7 @@ impl Canvas {
}
#[cfg(test)]
-mod test {
+mod tests {
use super::*;
use crossfont::Metrics;