parent
ab2db49af5
commit
33abfe34a8
|
@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Default Command+N keybinding for SpawnNewInstance on macOS
|
- Default Command+N keybinding for SpawnNewInstance on macOS
|
||||||
- Vi mode for copying text and opening links
|
- Vi mode for copying text and opening links
|
||||||
- `CopySelection` action which copies into selection buffer on Linux/BSD
|
- `CopySelection` action which copies into selection buffer on Linux/BSD
|
||||||
|
- Option `cursor.thickness` to set terminal cursor thickness
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
|
@ -318,6 +318,10 @@
|
||||||
# window is not focused.
|
# window is not focused.
|
||||||
#unfocused_hollow: true
|
#unfocused_hollow: true
|
||||||
|
|
||||||
|
# Thickness of the cursor relative to the cell width as floating point number
|
||||||
|
# from `0.0` to `1.0`.
|
||||||
|
#thickness: 0.15
|
||||||
|
|
||||||
# Live config reload (changes require restart)
|
# Live config reload (changes require restart)
|
||||||
#live_config_reload: true
|
#live_config_reload: true
|
||||||
|
|
||||||
|
|
|
@ -20,20 +20,19 @@ use alacritty_terminal::ansi::CursorStyle;
|
||||||
|
|
||||||
use font::{BitmapBuffer, Metrics, RasterizedGlyph};
|
use font::{BitmapBuffer, Metrics, RasterizedGlyph};
|
||||||
|
|
||||||
/// Width/Height of the cursor relative to the font width
|
|
||||||
pub const CURSOR_WIDTH_PERCENTAGE: f64 = 0.15;
|
|
||||||
|
|
||||||
pub fn get_cursor_glyph(
|
pub fn get_cursor_glyph(
|
||||||
cursor: CursorStyle,
|
cursor: CursorStyle,
|
||||||
metrics: Metrics,
|
metrics: Metrics,
|
||||||
offset_x: i8,
|
offset_x: i8,
|
||||||
offset_y: i8,
|
offset_y: i8,
|
||||||
is_wide: bool,
|
is_wide: bool,
|
||||||
|
cursor_thickness: f64,
|
||||||
) -> RasterizedGlyph {
|
) -> RasterizedGlyph {
|
||||||
// Calculate the cell metrics
|
// Calculate the cell metrics
|
||||||
let height = metrics.line_height as i32 + i32::from(offset_y);
|
let height = metrics.line_height as i32 + i32::from(offset_y);
|
||||||
let mut width = metrics.average_advance as i32 + i32::from(offset_x);
|
let mut width = metrics.average_advance as i32 + i32::from(offset_x);
|
||||||
let line_width = cmp::max((f64::from(width) * CURSOR_WIDTH_PERCENTAGE).round() as i32, 1);
|
|
||||||
|
let line_width = cmp::max((cursor_thickness * f64::from(width)).round() as i32, 1);
|
||||||
|
|
||||||
// Double the cursor width if it's above a double-width glyph
|
// Double the cursor width if it's above a double-width glyph
|
||||||
if is_wide {
|
if is_wide {
|
||||||
|
|
|
@ -286,7 +286,15 @@ impl Display {
|
||||||
size_info.cell_height = cell_height;
|
size_info.cell_height = cell_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process update events
|
/// Clear glyph cache.
|
||||||
|
fn clear_glyph_cache(&mut self) {
|
||||||
|
let cache = &mut self.glyph_cache;
|
||||||
|
self.renderer.with_loader(|mut api| {
|
||||||
|
cache.clear_glyph_cache(&mut api);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process update events.
|
||||||
pub fn handle_update<T>(
|
pub fn handle_update<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
terminal: &mut Term<T>,
|
terminal: &mut Term<T>,
|
||||||
|
@ -298,6 +306,8 @@ impl Display {
|
||||||
// Update font size and cell dimensions
|
// Update font size and cell dimensions
|
||||||
if let Some(font) = update_pending.font {
|
if let Some(font) = update_pending.font {
|
||||||
self.update_glyph_cache(config, font);
|
self.update_glyph_cache(config, font);
|
||||||
|
} else if update_pending.cursor {
|
||||||
|
self.clear_glyph_cache();
|
||||||
}
|
}
|
||||||
|
|
||||||
let cell_width = self.size_info.cell_width;
|
let cell_width = self.size_info.cell_width;
|
||||||
|
|
|
@ -49,13 +49,14 @@ use crate::window::Window;
|
||||||
#[derive(Default, Clone, Debug, PartialEq)]
|
#[derive(Default, Clone, Debug, PartialEq)]
|
||||||
pub struct DisplayUpdate {
|
pub struct DisplayUpdate {
|
||||||
pub dimensions: Option<PhysicalSize<u32>>,
|
pub dimensions: Option<PhysicalSize<u32>>,
|
||||||
pub message_buffer: Option<()>,
|
pub message_buffer: bool,
|
||||||
pub font: Option<Font>,
|
pub font: Option<Font>,
|
||||||
|
pub cursor: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DisplayUpdate {
|
impl DisplayUpdate {
|
||||||
fn is_empty(&self) -> bool {
|
fn is_empty(&self) -> bool {
|
||||||
self.dimensions.is_none() && self.font.is_none() && self.message_buffer.is_none()
|
self.dimensions.is_none() && self.font.is_none() && !self.message_buffer && !self.cursor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,7 +256,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_message(&mut self) {
|
fn pop_message(&mut self) {
|
||||||
self.display_update_pending.message_buffer = Some(());
|
self.display_update_pending.message_buffer = true;
|
||||||
self.message_buffer.pop();
|
self.message_buffer.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,7 +527,7 @@ impl<N: Notify + OnResize> Processor<N> {
|
||||||
Event::ConfigReload(path) => Self::reload_config(&path, processor),
|
Event::ConfigReload(path) => Self::reload_config(&path, processor),
|
||||||
Event::Message(message) => {
|
Event::Message(message) => {
|
||||||
processor.ctx.message_buffer.push(message);
|
processor.ctx.message_buffer.push(message);
|
||||||
processor.ctx.display_update_pending.message_buffer = Some(());
|
processor.ctx.display_update_pending.message_buffer = true;
|
||||||
processor.ctx.terminal.dirty = true;
|
processor.ctx.terminal.dirty = true;
|
||||||
},
|
},
|
||||||
Event::MouseCursorDirty => processor.reset_mouse_cursor(),
|
Event::MouseCursorDirty => processor.reset_mouse_cursor(),
|
||||||
|
@ -659,7 +660,7 @@ impl<N: Notify + OnResize> Processor<N> {
|
||||||
T: EventListener,
|
T: EventListener,
|
||||||
{
|
{
|
||||||
processor.ctx.message_buffer.remove_target(LOG_TARGET_CONFIG);
|
processor.ctx.message_buffer.remove_target(LOG_TARGET_CONFIG);
|
||||||
processor.ctx.display_update_pending.message_buffer = Some(());
|
processor.ctx.display_update_pending.message_buffer = true;
|
||||||
|
|
||||||
let config = match config::reload_from(&path) {
|
let config = match config::reload_from(&path) {
|
||||||
Ok(config) => config,
|
Ok(config) => config,
|
||||||
|
@ -671,6 +672,13 @@ impl<N: Notify + OnResize> Processor<N> {
|
||||||
|
|
||||||
processor.ctx.terminal.update_config(&config);
|
processor.ctx.terminal.update_config(&config);
|
||||||
|
|
||||||
|
// Reload cursor if we've changed its thickness
|
||||||
|
if (processor.ctx.config.cursor.thickness() - config.cursor.thickness()).abs()
|
||||||
|
> std::f64::EPSILON
|
||||||
|
{
|
||||||
|
processor.ctx.display_update_pending.cursor = true;
|
||||||
|
}
|
||||||
|
|
||||||
if processor.ctx.config.font != config.font {
|
if processor.ctx.config.font != config.font {
|
||||||
// Do not update font size if it has been changed at runtime
|
// Do not update font size if it has been changed at runtime
|
||||||
if *processor.ctx.font_size == processor.ctx.config.font.size {
|
if *processor.ctx.font_size == processor.ctx.config.font.size {
|
||||||
|
|
|
@ -213,10 +213,7 @@ impl GlyphCache {
|
||||||
metrics,
|
metrics,
|
||||||
};
|
};
|
||||||
|
|
||||||
cache.load_glyphs_for_font(regular, loader);
|
cache.load_common_glyphs(loader);
|
||||||
cache.load_glyphs_for_font(bold, loader);
|
|
||||||
cache.load_glyphs_for_font(italic, loader);
|
|
||||||
cache.load_glyphs_for_font(bold_italic, loader);
|
|
||||||
|
|
||||||
Ok(cache)
|
Ok(cache)
|
||||||
}
|
}
|
||||||
|
@ -302,17 +299,21 @@ impl GlyphCache {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clear currently cached data in both GL and the registry.
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_font_size<L: LoadGlyph>(
|
pub fn update_font_size<L: LoadGlyph>(
|
||||||
&mut self,
|
&mut self,
|
||||||
font: config::Font,
|
font: config::Font,
|
||||||
dpr: f64,
|
dpr: f64,
|
||||||
loader: &mut L,
|
loader: &mut L,
|
||||||
) -> Result<(), font::Error> {
|
) -> Result<(), font::Error> {
|
||||||
// Clear currently cached data in both GL and the registry
|
|
||||||
loader.clear();
|
|
||||||
self.cache = HashMap::default();
|
|
||||||
self.cursor_cache = HashMap::default();
|
|
||||||
|
|
||||||
// Update dpi scaling
|
// Update dpi scaling
|
||||||
self.rasterizer.update_dpr(dpr as f32);
|
self.rasterizer.update_dpr(dpr as f32);
|
||||||
|
|
||||||
|
@ -332,10 +333,7 @@ impl GlyphCache {
|
||||||
self.bold_italic_key = bold_italic;
|
self.bold_italic_key = bold_italic;
|
||||||
self.metrics = metrics;
|
self.metrics = metrics;
|
||||||
|
|
||||||
self.load_glyphs_for_font(regular, loader);
|
self.clear_glyph_cache(loader);
|
||||||
self.load_glyphs_for_font(bold, loader);
|
|
||||||
self.load_glyphs_for_font(italic, loader);
|
|
||||||
self.load_glyphs_for_font(bold_italic, loader);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -344,6 +342,14 @@ impl GlyphCache {
|
||||||
self.metrics
|
self.metrics
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prefetch glyphs that are almost guaranteed to be loaded anyways.
|
||||||
|
fn load_common_glyphs<L: LoadGlyph>(&mut self, loader: &mut L) {
|
||||||
|
self.load_glyphs_for_font(self.font_key, loader);
|
||||||
|
self.load_glyphs_for_font(self.bold_italic_key, loader);
|
||||||
|
self.load_glyphs_for_font(self.italic_key, loader);
|
||||||
|
self.load_glyphs_for_font(self.bold_italic_key, loader);
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate font metrics without access to a glyph cache
|
// Calculate font metrics without access to a glyph cache
|
||||||
pub fn static_metrics(font: Font, dpr: f64) -> Result<font::Metrics, font::Error> {
|
pub fn static_metrics(font: Font, dpr: f64) -> Result<font::Metrics, font::Error> {
|
||||||
let mut rasterizer = font::Rasterizer::new(dpr as f32, font.use_thin_strokes())?;
|
let mut rasterizer = font::Rasterizer::new(dpr as f32, font.use_thin_strokes())?;
|
||||||
|
@ -1019,6 +1025,7 @@ impl<'a, C> RenderApi<'a, C> {
|
||||||
self.config.font.offset.x,
|
self.config.font.offset.x,
|
||||||
self.config.font.offset.y,
|
self.config.font.offset.y,
|
||||||
cursor_key.is_wide,
|
cursor_key.is_wide,
|
||||||
|
self.config.cursor.thickness(),
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
self.add_render_item(cell, glyph);
|
self.add_render_item(cell, glyph);
|
||||||
|
|
|
@ -40,6 +40,7 @@ use crate::term::color::Rgb;
|
||||||
|
|
||||||
pub const LOG_TARGET_CONFIG: &str = "alacritty_config";
|
pub const LOG_TARGET_CONFIG: &str = "alacritty_config";
|
||||||
const MAX_SCROLLBACK_LINES: u32 = 100_000;
|
const MAX_SCROLLBACK_LINES: u32 = 100_000;
|
||||||
|
const DEFAULT_CURSOR_THICKNESS: f32 = 0.15;
|
||||||
|
|
||||||
pub type MockConfig = Config<HashMap<String, serde_yaml::Value>>;
|
pub type MockConfig = Config<HashMap<String, serde_yaml::Value>>;
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ pub struct Config<T> {
|
||||||
|
|
||||||
/// Background opacity from 0.0 to 1.0
|
/// Background opacity from 0.0 to 1.0
|
||||||
#[serde(default, deserialize_with = "failure_default")]
|
#[serde(default, deserialize_with = "failure_default")]
|
||||||
background_opacity: Alpha,
|
background_opacity: Percentage,
|
||||||
|
|
||||||
/// Window configuration
|
/// Window configuration
|
||||||
#[serde(default, deserialize_with = "failure_default")]
|
#[serde(default, deserialize_with = "failure_default")]
|
||||||
|
@ -213,7 +214,7 @@ impl<T> Config<T> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn background_opacity(&self) -> f32 {
|
pub fn background_opacity(&self) -> f32 {
|
||||||
self.background_opacity.0
|
self.background_opacity.0 as f32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,20 +243,47 @@ impl Default for EscapeChars {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[derive(Deserialize, Copy, Clone, Debug, Default, PartialEq, Eq)]
|
#[derive(Deserialize, Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct Cursor {
|
pub struct Cursor {
|
||||||
#[serde(deserialize_with = "failure_default")]
|
#[serde(deserialize_with = "failure_default")]
|
||||||
pub style: CursorStyle,
|
pub style: CursorStyle,
|
||||||
#[serde(deserialize_with = "option_explicit_none")]
|
#[serde(deserialize_with = "option_explicit_none")]
|
||||||
pub vi_mode_style: Option<CursorStyle>,
|
pub vi_mode_style: Option<CursorStyle>,
|
||||||
|
#[serde(deserialize_with = "deserialize_cursor_thickness")]
|
||||||
|
thickness: Percentage,
|
||||||
#[serde(deserialize_with = "failure_default")]
|
#[serde(deserialize_with = "failure_default")]
|
||||||
unfocused_hollow: DefaultTrueBool,
|
unfocused_hollow: DefaultTrueBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cursor {
|
impl Cursor {
|
||||||
|
#[inline]
|
||||||
pub fn unfocused_hollow(self) -> bool {
|
pub fn unfocused_hollow(self) -> bool {
|
||||||
self.unfocused_hollow.0
|
self.unfocused_hollow.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn thickness(self) -> f64 {
|
||||||
|
self.thickness.0 as f64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Cursor {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
style: Default::default(),
|
||||||
|
vi_mode_style: Default::default(),
|
||||||
|
thickness: Percentage::new(DEFAULT_CURSOR_THICKNESS),
|
||||||
|
unfocused_hollow: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_cursor_thickness<'a, D>(deserializer: D) -> Result<Percentage, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'a>,
|
||||||
|
{
|
||||||
|
Ok(Percentage::deserialize(Value::deserialize(deserializer)?)
|
||||||
|
.unwrap_or_else(|_| Percentage::new(DEFAULT_CURSOR_THICKNESS)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||||
|
@ -300,13 +328,13 @@ pub struct Delta<T: Default + PartialEq + Eq> {
|
||||||
pub y: T,
|
pub y: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper around f32 that represents an alpha value between 0.0 and 1.0
|
/// Wrapper around f32 that represents a percentage value between 0.0 and 1.0.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct Alpha(f32);
|
pub struct Percentage(f32);
|
||||||
|
|
||||||
impl Alpha {
|
impl Percentage {
|
||||||
pub fn new(value: f32) -> Self {
|
pub fn new(value: f32) -> Self {
|
||||||
Alpha(if value < 0.0 {
|
Percentage(if value < 0.0 {
|
||||||
0.0
|
0.0
|
||||||
} else if value > 1.0 {
|
} else if value > 1.0 {
|
||||||
1.0
|
1.0
|
||||||
|
@ -316,18 +344,18 @@ impl Alpha {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Alpha {
|
impl Default for Percentage {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Alpha(1.0)
|
Percentage(1.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Deserialize<'a> for Alpha {
|
impl<'a> Deserialize<'a> for Percentage {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'a>,
|
D: Deserializer<'a>,
|
||||||
{
|
{
|
||||||
Ok(Alpha::new(f32::deserialize(deserializer)?))
|
Ok(Percentage::new(f32::deserialize(deserializer)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ struct ScrollingMultiplier(u8);
|
||||||
|
|
||||||
impl Default for ScrollingMultiplier {
|
impl Default for ScrollingMultiplier {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
ScrollingMultiplier(3)
|
Self(3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ struct ScrollingHistory(u32);
|
||||||
|
|
||||||
impl Default for ScrollingHistory {
|
impl Default for ScrollingHistory {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
ScrollingHistory(10_000)
|
Self(10_000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue