diff options
Diffstat (limited to 'alacritty/src')
-rw-r--r-- | alacritty/src/config/debug.rs | 4 | ||||
-rw-r--r-- | alacritty/src/display/content.rs | 53 | ||||
-rw-r--r-- | alacritty/src/display/damage.rs | 86 | ||||
-rw-r--r-- | alacritty/src/display/meter.rs | 2 | ||||
-rw-r--r-- | alacritty/src/display/mod.rs | 185 | ||||
-rw-r--r-- | alacritty/src/display/window.rs | 10 | ||||
-rw-r--r-- | alacritty/src/event.rs | 6 | ||||
-rw-r--r-- | alacritty/src/input.rs | 4 | ||||
-rw-r--r-- | alacritty/src/renderer/builtin_font.rs | 2 |
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; |