aboutsummaryrefslogtreecommitdiff
path: root/alacritty
diff options
context:
space:
mode:
authorKirill Chibisov <contact@kchibisov.com>2020-12-28 12:45:39 +0300
committerGitHub <noreply@github.com>2020-12-28 09:45:39 +0000
commit12fbd0051cd743bcea79f45777325f76485fd865 (patch)
tree8e09bf529451b21bfffaa27ed42116338837216c /alacritty
parentfdc10d270e423e6bec756cab61b502e28260129e (diff)
downloadr-alacritty-12fbd0051cd743bcea79f45777325f76485fd865.tar.gz
r-alacritty-12fbd0051cd743bcea79f45777325f76485fd865.tar.bz2
r-alacritty-12fbd0051cd743bcea79f45777325f76485fd865.zip
Draw cursor with rect renderer
This commit makes cursors being drawn via rects, thus it's always above underlines/strikeouts. Also, since the cursor isn't a glyph anymore, it can't be obscured due to atlas switching while glyphs are rendered. Fixes #4404. Fixes #3471.
Diffstat (limited to 'alacritty')
-rw-r--r--alacritty/src/cursor.rs168
-rw-r--r--alacritty/src/display.rs33
-rw-r--r--alacritty/src/input.rs3
-rw-r--r--alacritty/src/renderer/mod.rs58
-rw-r--r--alacritty/src/renderer/rects.rs3
-rw-r--r--alacritty/src/url.rs16
6 files changed, 120 insertions, 161 deletions
diff --git a/alacritty/src/cursor.rs b/alacritty/src/cursor.rs
index 806f6ff8..a9fba66a 100644
--- a/alacritty/src/cursor.rs
+++ b/alacritty/src/cursor.rs
@@ -1,112 +1,92 @@
-//! Helpers for creating different cursor glyphs from font metrics.
-
-use crossfont::{BitmapBuffer, Metrics, RasterizedGlyph};
+//! Convert a cursor into an iterator of rects.
use alacritty_terminal::ansi::CursorShape;
+use alacritty_terminal::term::color::Rgb;
+use alacritty_terminal::term::render::RenderableCursor;
+use alacritty_terminal::term::SizeInfo;
-pub fn get_cursor_glyph(
- cursor: CursorShape,
- metrics: Metrics,
- offset_x: i8,
- offset_y: i8,
- is_wide: bool,
- cursor_thickness: f32,
-) -> RasterizedGlyph {
- // Calculate the cell metrics.
- //
- // NOTE: With Rust 1.47+ `f64 as usize` is defined to clamp automatically:
- // https://github.com/rust-lang/rust/commit/14d608f1d8a0b84da5f3bccecb3efb3d35f980dc
- let height = (metrics.line_height + f64::from(offset_y)).max(1.) as usize;
- let mut width = (metrics.average_advance + f64::from(offset_x)).max(1.) as usize;
- let line_width = (cursor_thickness * width as f32).round().max(1.) as usize;
-
- // Double the cursor width if it's above a double-width glyph.
- if is_wide {
- width *= 2;
- }
+use crate::renderer::rects::RenderRect;
- match cursor {
- CursorShape::HollowBlock => get_box_cursor_glyph(height, width, line_width),
- CursorShape::Underline => get_underline_cursor_glyph(width, line_width),
- CursorShape::Beam => get_beam_cursor_glyph(height, line_width),
- CursorShape::Block => get_block_cursor_glyph(height, width),
- CursorShape::Hidden => RasterizedGlyph::default(),
- }
+/// Trait for conversion into the iterator.
+pub trait IntoRects {
+ /// Consume the cursor for an iterator of rects.
+ fn rects(self, size_info: &SizeInfo, thickness: f32) -> CursorRects;
}
-/// Return a custom underline cursor character.
-pub fn get_underline_cursor_glyph(width: usize, line_width: usize) -> RasterizedGlyph {
- // Create a new rectangle, the height is relative to the font width.
- let buffer = BitmapBuffer::RGB(vec![255u8; width * line_width * 3]);
-
- // Create a custom glyph with the rectangle data attached to it.
- RasterizedGlyph {
- character: ' ',
- top: line_width as i32,
- left: 0,
- height: line_width as i32,
- width: width as i32,
- buffer,
+impl IntoRects for RenderableCursor {
+ fn rects(self, size_info: &SizeInfo, thickness: f32) -> CursorRects {
+ let point = self.point();
+ let x = point.col.0 as f32 * size_info.cell_width() + size_info.padding_x();
+ let y = point.line.0 as f32 * size_info.cell_height() + size_info.padding_y();
+
+ let mut width = size_info.cell_width();
+ let height = size_info.cell_height();
+
+ if self.is_wide() {
+ width *= 2.;
+ }
+
+ let thickness = (thickness * width as f32).round().max(1.);
+
+ match self.shape() {
+ CursorShape::Beam => beam(x, y, height, thickness, self.color()),
+ CursorShape::Underline => underline(x, y, width, height, thickness, self.color()),
+ CursorShape::HollowBlock => hollow(x, y, width, height, thickness, self.color()),
+ _ => CursorRects::default(),
+ }
}
}
-/// Return a custom beam cursor character.
-pub fn get_beam_cursor_glyph(height: usize, line_width: usize) -> RasterizedGlyph {
- // Create a new rectangle that is at least one pixel wide
- let buffer = BitmapBuffer::RGB(vec![255u8; line_width * height * 3]);
-
- // Create a custom glyph with the rectangle data attached to it
- RasterizedGlyph {
- character: ' ',
- top: height as i32,
- left: 0,
- height: height as i32,
- width: line_width as i32,
- buffer,
- }
+/// Cursor rect iterator.
+#[derive(Default)]
+pub struct CursorRects {
+ rects: [Option<RenderRect>; 4],
+ index: usize,
}
-/// Returns a custom box cursor character.
-pub fn get_box_cursor_glyph(height: usize, width: usize, line_width: usize) -> RasterizedGlyph {
- // Create a new box outline rectangle.
- let mut buffer = Vec::with_capacity(width * height * 3);
- for y in 0..height {
- for x in 0..width {
- if y < line_width
- || y >= height - line_width
- || x < line_width
- || x >= width - line_width
- {
- buffer.append(&mut vec![255u8; 3]);
- } else {
- buffer.append(&mut vec![0u8; 3]);
- }
- }
+impl From<RenderRect> for CursorRects {
+ fn from(rect: RenderRect) -> Self {
+ Self { rects: [Some(rect), None, None, None], index: 0 }
}
+}
+
+impl Iterator for CursorRects {
+ type Item = RenderRect;
- // Create a custom glyph with the rectangle data attached to it.
- RasterizedGlyph {
- character: ' ',
- top: height as i32,
- left: 0,
- height: height as i32,
- width: width as i32,
- buffer: BitmapBuffer::RGB(buffer),
+ fn next(&mut self) -> Option<Self::Item> {
+ let rect = self.rects.get_mut(self.index)?;
+ self.index += 1;
+ rect.take()
}
}
-/// Return a custom block cursor character.
-pub fn get_block_cursor_glyph(height: usize, width: usize) -> RasterizedGlyph {
- // Create a completely filled glyph.
- let buffer = BitmapBuffer::RGB(vec![255u8; width * height * 3]);
-
- // Create a custom glyph with the rectangle data attached to it.
- RasterizedGlyph {
- character: ' ',
- top: height as i32,
- left: 0,
- height: height as i32,
- width: width as i32,
- buffer,
+/// Create an iterator yielding a single beam rect.
+fn beam(x: f32, y: f32, height: f32, thickness: f32, color: Rgb) -> CursorRects {
+ RenderRect::new(x, y, thickness, height, color, 1.).into()
+}
+
+/// Create an iterator yielding a single underline rect.
+fn underline(x: f32, y: f32, width: f32, height: f32, thickness: f32, color: Rgb) -> CursorRects {
+ let y = y + height - thickness;
+ RenderRect::new(x, y, width, thickness, color, 1.).into()
+}
+
+/// Create an iterator yielding a rect for each side of the hollow block cursor.
+fn hollow(x: f32, y: f32, width: f32, height: f32, thickness: f32, color: Rgb) -> CursorRects {
+ let top_line = RenderRect::new(x, y, width, thickness, color, 1.);
+
+ let vertical_y = y + thickness;
+ let vertical_height = height - 2. * thickness;
+ let left_line = RenderRect::new(x, vertical_y, thickness, vertical_height, color, 1.);
+
+ let bottom_y = y + height - thickness;
+ let bottom_line = RenderRect::new(x, bottom_y, width, thickness, color, 1.);
+
+ let right_x = x + width - thickness;
+ let right_line = RenderRect::new(right_x, vertical_y, thickness, vertical_height, color, 1.);
+
+ CursorRects {
+ rects: [Some(top_line), Some(bottom_line), Some(left_line), Some(right_line)],
+ index: 0,
}
}
diff --git a/alacritty/src/display.rs b/alacritty/src/display.rs
index 7fbf0d54..4084d639 100644
--- a/alacritty/src/display.rs
+++ b/alacritty/src/display.rs
@@ -33,6 +33,7 @@ use crate::config::window::Dimensions;
#[cfg(not(windows))]
use crate::config::window::StartupMode;
use crate::config::Config;
+use crate::cursor::IntoRects;
use crate::event::{Mouse, SearchState};
use crate::message_bar::{MessageBuffer, MessageType};
use crate::meter::Meter;
@@ -246,7 +247,7 @@ impl Display {
// Clear screen.
let background_color = config.colors.primary.background;
- renderer.with_api(&config.ui_config, config.cursor, &size_info, |api| {
+ renderer.with_api(&config.ui_config, &size_info, |api| {
api.clear(background_color);
});
@@ -268,7 +269,7 @@ impl Display {
#[cfg(not(any(target_os = "macos", windows)))]
if is_x11 {
window.swap_buffers();
- renderer.with_api(&config.ui_config, config.cursor, &size_info, |api| {
+ renderer.with_api(&config.ui_config, &size_info, |api| {
api.finish();
});
}
@@ -450,7 +451,14 @@ impl Display {
.and_then(|focused_match| terminal.grid().clamp_buffer_range_to_visible(focused_match));
let cursor_hidden = self.cursor_hidden || search_state.regex().is_some();
- let grid_cells = terminal.renderable_cells(config, !cursor_hidden).collect::<Vec<_>>();
+ // Collect renderable content before the terminal is dropped.
+ let mut content = terminal.renderable_content(config, !cursor_hidden);
+ let mut grid_cells = Vec::new();
+ while let Some(cell) = content.next() {
+ grid_cells.push(cell);
+ }
+ let cursor = content.cursor();
+
let visual_bell_intensity = terminal.visual_bell.intensity();
let background_color = terminal.background_color();
let cursor_point = terminal.grid().cursor.point;
@@ -471,7 +479,7 @@ impl Display {
// Drop terminal as early as possible to free lock.
drop(terminal);
- self.renderer.with_api(&config.ui_config, config.cursor, &size_info, |api| {
+ self.renderer.with_api(&config.ui_config, &size_info, |api| {
api.clear(background_color);
});
@@ -482,7 +490,7 @@ impl Display {
{
let _sampler = self.meter.sampler();
- self.renderer.with_api(&config.ui_config, config.cursor, &size_info, |mut api| {
+ self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
// Iterate over all non-empty cells in the grid.
for mut cell in grid_cells {
// Invert the active match in vi-less search.
@@ -538,6 +546,13 @@ impl Display {
}
}
+ // Push the cursor rects for rendering.
+ if let Some(cursor) = cursor {
+ for rect in cursor.rects(&size_info, config.cursor.thickness()) {
+ rects.push(rect);
+ }
+ }
+
// Push visual bell after url/underline/strikeout rects.
if visual_bell_intensity != 0. {
let visual_bell_rect = RenderRect::new(
@@ -576,7 +591,7 @@ impl Display {
// Relay messages to the user.
let fg = config.colors.primary.background;
for (i, message_text) in text.iter().enumerate() {
- self.renderer.with_api(&config.ui_config, config.cursor, &size_info, |mut api| {
+ self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
api.render_string(glyph_cache, start_line + i, &message_text, fg, None);
});
}
@@ -621,7 +636,7 @@ impl Display {
// On X11 `swap_buffers` does not block for vsync. However the next OpenGl command
// will block to synchronize (this is `glClear` in Alacritty), which causes a
// permanent one frame delay.
- self.renderer.with_api(&config.ui_config, config.cursor, &size_info, |api| {
+ self.renderer.with_api(&config.ui_config, &size_info, |api| {
api.finish();
});
}
@@ -668,7 +683,7 @@ impl Display {
let fg = config.colors.search_bar_foreground();
let bg = config.colors.search_bar_background();
- self.renderer.with_api(&config.ui_config, config.cursor, &size_info, |mut api| {
+ self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
api.render_string(glyph_cache, size_info.screen_lines(), &text, fg, Some(bg));
});
}
@@ -684,7 +699,7 @@ impl Display {
let fg = config.colors.primary.background;
let bg = config.colors.normal.red;
- self.renderer.with_api(&config.ui_config, config.cursor, &size_info, |mut api| {
+ self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
api.render_string(glyph_cache, size_info.screen_lines() - 2, &timing[..], fg, Some(bg));
});
}
diff --git a/alacritty/src/input.rs b/alacritty/src/input.rs
index 4f66721c..55799dc5 100644
--- a/alacritty/src/input.rs
+++ b/alacritty/src/input.rs
@@ -26,8 +26,7 @@ use alacritty_terminal::event::EventListener;
use alacritty_terminal::grid::{Dimensions, Scroll};
use alacritty_terminal::index::{Column, Direction, Line, Point, Side};
use alacritty_terminal::selection::SelectionType;
-use alacritty_terminal::term::mode::TermMode;
-use alacritty_terminal::term::{ClipboardType, SizeInfo, Term};
+use alacritty_terminal::term::{ClipboardType, SizeInfo, Term, TermMode};
use alacritty_terminal::vi_mode::ViMotion;
use crate::clipboard::Clipboard;
diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs
index ca3553dc..70ac993b 100644
--- a/alacritty/src/renderer/mod.rs
+++ b/alacritty/src/renderer/mod.rs
@@ -15,15 +15,14 @@ use fnv::FnvHasher;
use log::{debug, error, info};
use unicode_width::UnicodeWidthChar;
-use alacritty_terminal::config::Cursor;
use alacritty_terminal::index::{Column, Line};
use alacritty_terminal::term::cell::Flags;
use alacritty_terminal::term::color::Rgb;
-use alacritty_terminal::term::{CursorKey, RenderableCell, RenderableCellContent, SizeInfo};
+use alacritty_terminal::term::render::RenderableCell;
+use alacritty_terminal::term::SizeInfo;
use crate::config::font::{Font, FontDescription};
use crate::config::ui_config::{Delta, UIConfig};
-use crate::cursor;
use crate::gl;
use crate::gl::types::*;
use crate::renderer::rects::{RectRenderer, RenderRect};
@@ -116,9 +115,6 @@ pub struct GlyphCache {
/// Cache of buffered glyphs.
cache: HashMap<GlyphKey, Glyph, BuildHasherDefault<FnvHasher>>,
- /// Cache of buffered cursor glyphs.
- cursor_cache: HashMap<CursorKey, Glyph, BuildHasherDefault<FnvHasher>>,
-
/// Rasterizer for loading new glyphs.
rasterizer: Rasterizer,
@@ -164,7 +160,6 @@ impl GlyphCache {
let mut cache = Self {
cache: HashMap::default(),
- cursor_cache: HashMap::default(),
rasterizer,
font_size: font.size(),
font_key: regular,
@@ -328,7 +323,6 @@ impl GlyphCache {
pub fn clear_glyph_cache<L: LoadGlyph>(&mut self, loader: &mut L) {
loader.clear();
self.cache = HashMap::default();
- self.cursor_cache = HashMap::default();
self.load_common_glyphs(loader);
}
@@ -459,7 +453,6 @@ pub struct RenderApi<'a> {
current_atlas: &'a mut usize,
program: &'a mut TextShaderProgram,
config: &'a UIConfig,
- cursor_config: Cursor,
}
#[derive(Debug)]
@@ -693,13 +686,7 @@ impl QuadRenderer {
}
}
- pub fn with_api<F, T>(
- &mut self,
- config: &UIConfig,
- cursor_config: Cursor,
- props: &SizeInfo,
- func: F,
- ) -> T
+ pub fn with_api<F, T>(&mut self, config: &UIConfig, props: &SizeInfo, func: F) -> T
where
F: FnOnce(RenderApi<'_>) -> T,
{
@@ -720,7 +707,6 @@ impl QuadRenderer {
current_atlas: &mut self.current_atlas,
program: &mut self.program,
config,
- cursor_config,
});
unsafe {
@@ -848,10 +834,11 @@ impl<'a> RenderApi<'a> {
let cells = string
.chars()
.enumerate()
- .map(|(i, c)| RenderableCell {
+ .map(|(i, character)| RenderableCell {
line,
column: Column(i),
- inner: RenderableCellContent::Chars((c, None)),
+ character,
+ zerowidth: None,
flags: Flags::empty(),
bg_alpha,
fg,
@@ -881,26 +868,6 @@ impl<'a> RenderApi<'a> {
}
pub fn render_cell(&mut self, mut cell: RenderableCell, glyph_cache: &mut GlyphCache) {
- let (mut character, zerowidth) = match cell.inner {
- RenderableCellContent::Cursor(cursor_key) => {
- // Raw cell pixel buffers like cursors don't need to go through font lookup.
- let metrics = glyph_cache.metrics;
- let glyph = glyph_cache.cursor_cache.entry(cursor_key).or_insert_with(|| {
- self.load_glyph(&cursor::get_cursor_glyph(
- cursor_key.shape,
- metrics,
- self.config.font.offset.x,
- self.config.font.offset.y,
- cursor_key.is_wide,
- self.cursor_config.thickness(),
- ))
- });
- self.add_render_item(&cell, glyph);
- return;
- },
- RenderableCellContent::Chars((c, ref mut zerowidth)) => (c, zerowidth.take()),
- };
-
// Get font key for cell.
let font_key = match cell.flags & Flags::BOLD_ITALIC {
Flags::BOLD_ITALIC => glyph_cache.bold_italic_key,
@@ -911,11 +878,12 @@ impl<'a> RenderApi<'a> {
// Ignore hidden cells and render tabs as spaces to prevent font issues.
let hidden = cell.flags.contains(Flags::HIDDEN);
- if character == '\t' || hidden {
- character = ' ';
+ if cell.character == '\t' || hidden {
+ cell.character = ' ';
}
- let mut glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, character };
+ let mut glyph_key =
+ GlyphKey { font_key, size: glyph_cache.font_size, character: cell.character };
// Add cell to batch.
match glyph_cache.get(glyph_key, self) {
@@ -930,9 +898,9 @@ impl<'a> RenderApi<'a> {
}
// Render visible zero-width characters.
- if let Some(zerowidth) = zerowidth.filter(|_| !hidden) {
- for character in zerowidth.iter() {
- glyph_key.character = *character;
+ if let Some(zerowidth) = cell.zerowidth.take().filter(|_| !hidden) {
+ for character in zerowidth {
+ glyph_key.character = character;
if let Ok(glyph) = glyph_cache.get(glyph_key, self) {
self.add_render_item(&cell, &glyph);
}
diff --git a/alacritty/src/renderer/rects.rs b/alacritty/src/renderer/rects.rs
index 1f50da87..cfd17379 100644
--- a/alacritty/src/renderer/rects.rs
+++ b/alacritty/src/renderer/rects.rs
@@ -6,7 +6,8 @@ use crossfont::Metrics;
use alacritty_terminal::index::{Column, Point};
use alacritty_terminal::term::cell::Flags;
use alacritty_terminal::term::color::Rgb;
-use alacritty_terminal::term::{RenderableCell, SizeInfo};
+use alacritty_terminal::term::render::RenderableCell;
+use alacritty_terminal::term::SizeInfo;
use crate::gl;
use crate::gl::types::*;
diff --git a/alacritty/src/url.rs b/alacritty/src/url.rs
index b33b532e..f4bf8205 100644
--- a/alacritty/src/url.rs
+++ b/alacritty/src/url.rs
@@ -8,7 +8,8 @@ use urlocator::{UrlLocation, UrlLocator};
use alacritty_terminal::index::{Column, Point};
use alacritty_terminal::term::cell::Flags;
use alacritty_terminal::term::color::Rgb;
-use alacritty_terminal::term::{RenderableCell, RenderableCellContent, SizeInfo};
+use alacritty_terminal::term::render::RenderableCell;
+use alacritty_terminal::term::SizeInfo;
use crate::config::Config;
use crate::event::Mouse;
@@ -72,12 +73,6 @@ impl Urls {
// Update tracked URLs.
pub fn update(&mut self, num_cols: Column, cell: &RenderableCell) {
- // Convert cell to character.
- let c = match &cell.inner {
- RenderableCellContent::Chars((c, _zerowidth)) => *c,
- RenderableCellContent::Cursor(_) => return,
- };
-
let point: Point = cell.into();
let mut end = point;
@@ -107,7 +102,7 @@ impl Urls {
}
// Advance parser.
- let last_state = mem::replace(&mut self.state, self.locator.advance(c));
+ let last_state = mem::replace(&mut self.state, self.locator.advance(cell.character));
match (self.state, last_state) {
(UrlLocation::Url(_length, end_offset), UrlLocation::Scheme) => {
// Create empty URL.
@@ -204,8 +199,9 @@ mod tests {
fn text_to_cells(text: &str) -> Vec<RenderableCell> {
text.chars()
.enumerate()
- .map(|(i, c)| RenderableCell {
- inner: RenderableCellContent::Chars((c, None)),
+ .map(|(i, character)| RenderableCell {
+ character,
+ zerowidth: None,
line: Line(0),
column: Column(i),
fg: Default::default(),