summaryrefslogtreecommitdiff
path: root/alacritty_terminal/src
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty_terminal/src')
-rw-r--r--alacritty_terminal/src/ansi.rs45
-rw-r--r--alacritty_terminal/src/config/mod.rs106
-rw-r--r--alacritty_terminal/src/event.rs2
-rw-r--r--alacritty_terminal/src/term/mod.rs73
4 files changed, 189 insertions, 37 deletions
diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs
index 877fd65f..7567eba2 100644
--- a/alacritty_terminal/src/ansi.rs
+++ b/alacritty_terminal/src/ansi.rs
@@ -141,6 +141,9 @@ pub trait Handler {
/// Set the cursor style.
fn set_cursor_style(&mut self, _: Option<CursorStyle>) {}
+ /// Set the cursor shape.
+ fn set_cursor_shape(&mut self, _shape: CursorShape) {}
+
/// A character to be displayed.
fn input(&mut self, _c: char) {}
@@ -324,9 +327,16 @@ pub trait Handler {
fn text_area_size_chars<W: io::Write>(&mut self, _: &mut W) {}
}
-/// Describes shape of cursor.
-#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash, Deserialize)]
-pub enum CursorStyle {
+/// Terminal cursor configuration.
+#[derive(Deserialize, Default, Debug, Eq, PartialEq, Copy, Clone, Hash)]
+pub struct CursorStyle {
+ pub shape: CursorShape,
+ pub blinking: bool,
+}
+
+/// Terminal cursor shape.
+#[derive(Deserialize, Debug, Eq, PartialEq, Copy, Clone, Hash)]
+pub enum CursorShape {
/// Cursor is a block like `▒`.
Block,
@@ -345,9 +355,9 @@ pub enum CursorStyle {
Hidden,
}
-impl Default for CursorStyle {
- fn default() -> CursorStyle {
- CursorStyle::Block
+impl Default for CursorShape {
+ fn default() -> CursorShape {
+ CursorShape::Block
}
}
@@ -874,13 +884,13 @@ where
&& params[1].len() >= 13
&& params[1][0..12] == *b"CursorShape="
{
- let style = match params[1][12] as char {
- '0' => CursorStyle::Block,
- '1' => CursorStyle::Beam,
- '2' => CursorStyle::Underline,
+ let shape = match params[1][12] as char {
+ '0' => CursorShape::Block,
+ '1' => CursorShape::Beam,
+ '2' => CursorShape::Underline,
_ => return unhandled(params),
};
- self.handler.set_cursor_style(Some(style));
+ self.handler.set_cursor_shape(shape);
return;
}
unhandled(params);
@@ -1065,18 +1075,21 @@ where
('P', None) => handler.delete_chars(Column(next_param_or(1) as usize)),
('q', Some(b' ')) => {
// DECSCUSR (CSI Ps SP q) -- Set Cursor Style.
- let style = match next_param_or(0) {
+ let cursor_style_id = next_param_or(0);
+ let shape = match cursor_style_id {
0 => None,
- 1 | 2 => Some(CursorStyle::Block),
- 3 | 4 => Some(CursorStyle::Underline),
- 5 | 6 => Some(CursorStyle::Beam),
+ 1 | 2 => Some(CursorShape::Block),
+ 3 | 4 => Some(CursorShape::Underline),
+ 5 | 6 => Some(CursorShape::Beam),
_ => {
unhandled!();
return;
},
};
+ let cursor_style =
+ shape.map(|shape| CursorStyle { shape, blinking: cursor_style_id % 2 == 1 });
- handler.set_cursor_style(style);
+ handler.set_cursor_style(cursor_style);
},
('r', None) => {
let top = next_param_or(1) as usize;
diff --git a/alacritty_terminal/src/config/mod.rs b/alacritty_terminal/src/config/mod.rs
index 98849d90..f3221920 100644
--- a/alacritty_terminal/src/config/mod.rs
+++ b/alacritty_terminal/src/config/mod.rs
@@ -1,3 +1,4 @@
+use std::cmp::max;
use std::collections::HashMap;
use std::fmt::Display;
use std::path::PathBuf;
@@ -10,15 +11,16 @@ mod bell;
mod colors;
mod scrolling;
-use crate::ansi::CursorStyle;
+use crate::ansi::{CursorShape, CursorStyle};
pub use crate::config::bell::{BellAnimation, BellConfig};
pub use crate::config::colors::Colors;
pub use crate::config::scrolling::Scrolling;
pub const LOG_TARGET_CONFIG: &str = "alacritty_config";
-const MAX_SCROLLBACK_LINES: u32 = 100_000;
const DEFAULT_CURSOR_THICKNESS: f32 = 0.15;
+const MAX_SCROLLBACK_LINES: u32 = 100_000;
+const MIN_BLINK_INTERVAL: u64 = 10;
pub type MockConfig = Config<HashMap<String, serde_yaml::Value>>;
@@ -121,9 +123,11 @@ impl Default for EscapeChars {
#[derive(Deserialize, Copy, Clone, Debug, PartialEq)]
pub struct Cursor {
#[serde(deserialize_with = "failure_default")]
- pub style: CursorStyle,
+ pub style: ConfigCursorStyle,
#[serde(deserialize_with = "option_explicit_none")]
- pub vi_mode_style: Option<CursorStyle>,
+ pub vi_mode_style: Option<ConfigCursorStyle>,
+ #[serde(deserialize_with = "failure_default")]
+ blink_interval: BlinkInterval,
#[serde(deserialize_with = "deserialize_cursor_thickness")]
thickness: Percentage,
#[serde(deserialize_with = "failure_default")]
@@ -140,6 +144,21 @@ impl Cursor {
pub fn thickness(self) -> f64 {
self.thickness.0 as f64
}
+
+ #[inline]
+ pub fn style(self) -> CursorStyle {
+ self.style.into()
+ }
+
+ #[inline]
+ pub fn vi_mode_style(self) -> Option<CursorStyle> {
+ self.vi_mode_style.map(From::from)
+ }
+
+ #[inline]
+ pub fn blink_interval(self) -> u64 {
+ max(self.blink_interval.0, MIN_BLINK_INTERVAL)
+ }
}
impl Default for Cursor {
@@ -149,10 +168,20 @@ impl Default for Cursor {
vi_mode_style: Default::default(),
thickness: Percentage::new(DEFAULT_CURSOR_THICKNESS),
unfocused_hollow: Default::default(),
+ blink_interval: Default::default(),
}
}
}
+#[derive(Deserialize, Copy, Clone, Debug, PartialEq)]
+struct BlinkInterval(u64);
+
+impl Default for BlinkInterval {
+ fn default() -> Self {
+ BlinkInterval(750)
+ }
+}
+
fn deserialize_cursor_thickness<'a, D>(deserializer: D) -> Result<Percentage, D::Error>
where
D: Deserializer<'a>,
@@ -174,6 +203,75 @@ where
}
#[serde(untagged)]
+#[derive(Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
+pub enum ConfigCursorStyle {
+ Shape(CursorShape),
+ WithBlinking {
+ #[serde(default, deserialize_with = "failure_default")]
+ shape: CursorShape,
+ #[serde(default, deserialize_with = "failure_default")]
+ blinking: CursorBlinking,
+ },
+}
+
+impl Default for ConfigCursorStyle {
+ fn default() -> Self {
+ Self::WithBlinking { shape: CursorShape::default(), blinking: CursorBlinking::default() }
+ }
+}
+
+impl ConfigCursorStyle {
+ /// Check if blinking is force enabled/disabled.
+ pub fn blinking_override(&self) -> Option<bool> {
+ match self {
+ Self::Shape(_) => None,
+ Self::WithBlinking { blinking, .. } => blinking.blinking_override(),
+ }
+ }
+}
+
+impl From<ConfigCursorStyle> for CursorStyle {
+ fn from(config_style: ConfigCursorStyle) -> Self {
+ match config_style {
+ ConfigCursorStyle::Shape(shape) => Self { shape, blinking: false },
+ ConfigCursorStyle::WithBlinking { shape, blinking } => {
+ Self { shape, blinking: blinking.into() }
+ },
+ }
+ }
+}
+
+#[derive(Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
+pub enum CursorBlinking {
+ Never,
+ Off,
+ On,
+ Always,
+}
+
+impl Default for CursorBlinking {
+ fn default() -> Self {
+ CursorBlinking::Off
+ }
+}
+
+impl CursorBlinking {
+ fn blinking_override(&self) -> Option<bool> {
+ match self {
+ Self::Never => Some(false),
+ Self::Off | Self::On => None,
+ Self::Always => Some(true),
+ }
+ }
+}
+
+impl Into<bool> for CursorBlinking {
+ fn into(self) -> bool {
+ self == Self::On || self == Self::Always
+ }
+}
+
+#[serde(untagged)]
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum Program {
Just(String),
diff --git a/alacritty_terminal/src/event.rs b/alacritty_terminal/src/event.rs
index c7129b24..351b7bc2 100644
--- a/alacritty_terminal/src/event.rs
+++ b/alacritty_terminal/src/event.rs
@@ -11,6 +11,7 @@ pub enum Event {
ResetTitle,
ClipboardStore(ClipboardType, String),
ClipboardLoad(ClipboardType, Arc<dyn Fn(&str) -> String + Sync + Send + 'static>),
+ CursorBlinkingChange(bool),
Wakeup,
Bell,
Exit,
@@ -27,6 +28,7 @@ impl Debug for Event {
Event::Wakeup => write!(f, "Wakeup"),
Event::Bell => write!(f, "Bell"),
Event::Exit => write!(f, "Exit"),
+ Event::CursorBlinkingChange(blinking) => write!(f, "CursorBlinking({})", blinking),
}
}
}
diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs
index 926b89d7..accb4dc1 100644
--- a/alacritty_terminal/src/term/mod.rs
+++ b/alacritty_terminal/src/term/mod.rs
@@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
use unicode_width::UnicodeWidthChar;
use crate::ansi::{
- self, Attr, CharsetIndex, Color, CursorStyle, Handler, NamedColor, StandardCharset,
+ self, Attr, CharsetIndex, Color, CursorShape, CursorStyle, Handler, NamedColor, StandardCharset,
};
use crate::config::{BellAnimation, BellConfig, Config};
use crate::event::{Event, EventListener};
@@ -61,7 +61,7 @@ struct RenderableCursor {
/// A key for caching cursor glyphs.
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash, Deserialize)]
pub struct CursorKey {
- pub style: CursorStyle,
+ pub shape: CursorShape,
pub is_wide: bool,
}
@@ -202,7 +202,7 @@ impl<'a, C> RenderableCellsIter<'a, C> {
let cell = self.inner.next()?;
let mut cell = RenderableCell::new(self, cell);
- if self.cursor.key.style == CursorStyle::Block {
+ if self.cursor.key.shape == CursorShape::Block {
cell.fg = match self.cursor.cursor_color {
// Apply cursor color, or invert the cursor if it has a fixed background
// close to the cell's background.
@@ -249,7 +249,7 @@ impl<'a, C> RenderableCellsIter<'a, C> {
};
// Do not invert block cursor at selection boundaries.
- if self.cursor.key.style == CursorStyle::Block
+ if self.cursor.key.shape == CursorShape::Block
&& self.cursor.point == point
&& (selection.start == point
|| selection.end == point
@@ -855,8 +855,8 @@ impl<T> Term<T> {
original_colors: colors,
semantic_escape_chars: config.selection.semantic_escape_chars().to_owned(),
cursor_style: None,
- default_cursor_style: config.cursor.style,
- vi_mode_cursor_style: config.cursor.vi_mode_style,
+ default_cursor_style: config.cursor.style(),
+ vi_mode_cursor_style: config.cursor.vi_mode_style(),
event_proxy,
is_focused: true,
title: None,
@@ -885,8 +885,8 @@ impl<T> Term<T> {
if let Some(0) = config.scrolling.faux_multiplier() {
self.mode.remove(TermMode::ALTERNATE_SCROLL);
}
- self.default_cursor_style = config.cursor.style;
- self.vi_mode_cursor_style = config.cursor.vi_mode_style;
+ self.default_cursor_style = config.cursor.style();
+ self.vi_mode_cursor_style = config.cursor.vi_mode_style();
let title_event = match &self.title {
Some(title) => Event::Title(title.clone()),
@@ -1207,7 +1207,10 @@ impl<T> Term<T> {
/// Toggle the vi mode.
#[inline]
- pub fn toggle_vi_mode(&mut self) {
+ pub fn toggle_vi_mode(&mut self)
+ where
+ T: EventListener,
+ {
self.mode ^= TermMode::VI;
let vi_mode = self.mode.contains(TermMode::VI);
@@ -1226,6 +1229,9 @@ impl<T> Term<T> {
self.cancel_search();
}
+ // Update UI about cursor blinking state changes.
+ self.event_proxy.send_event(Event::CursorBlinkingChange(self.cursor_style().blinking));
+
self.dirty = true;
}
@@ -1332,6 +1338,20 @@ impl<T> Term<T> {
&self.semantic_escape_chars
}
+ /// Active terminal cursor style.
+ ///
+ /// While vi mode is active, this will automatically return the vi mode cursor style.
+ #[inline]
+ pub fn cursor_style(&self) -> CursorStyle {
+ let cursor_style = self.cursor_style.unwrap_or(self.default_cursor_style);
+
+ if self.mode.contains(TermMode::VI) {
+ self.vi_mode_cursor_style.unwrap_or(cursor_style)
+ } else {
+ cursor_style
+ }
+ }
+
/// Insert a linebreak at the current cursor position.
#[inline]
fn wrapline(&mut self)
@@ -1395,18 +1415,18 @@ impl<T> Term<T> {
// Cursor shape.
let hidden =
!self.mode.contains(TermMode::SHOW_CURSOR) || point.line >= self.screen_lines();
- let cursor_style = if hidden && !vi_mode {
+ let cursor_shape = if hidden && !vi_mode {
point.line = Line(0);
- CursorStyle::Hidden
+ CursorShape::Hidden
} else if !self.is_focused && config.cursor.unfocused_hollow() {
- CursorStyle::HollowBlock
+ CursorShape::HollowBlock
} else {
let cursor_style = self.cursor_style.unwrap_or(self.default_cursor_style);
if vi_mode {
- self.vi_mode_cursor_style.unwrap_or(cursor_style)
+ self.vi_mode_cursor_style.unwrap_or(cursor_style).shape
} else {
- cursor_style
+ cursor_style.shape
}
};
@@ -1432,7 +1452,7 @@ impl<T> Term<T> {
RenderableCursor {
text_color,
cursor_color,
- key: CursorKey { style: cursor_style, is_wide },
+ key: CursorKey { shape: cursor_shape, is_wide },
point,
rendered: false,
}
@@ -2098,6 +2118,9 @@ impl<T: EventListener> Handler for Term<T> {
// Preserve vi mode across resets.
self.mode &= TermMode::VI;
self.mode.insert(TermMode::default());
+
+ let blinking = self.cursor_style().blinking;
+ self.event_proxy.send_event(Event::CursorBlinkingChange(blinking));
}
#[inline]
@@ -2199,7 +2222,9 @@ impl<T: EventListener> Handler for Term<T> {
ansi::Mode::DECCOLM => self.deccolm(),
ansi::Mode::Insert => self.mode.insert(TermMode::INSERT),
ansi::Mode::BlinkingCursor => {
- trace!("... unimplemented mode");
+ let style = self.cursor_style.get_or_insert(self.default_cursor_style);
+ style.blinking = true;
+ self.event_proxy.send_event(Event::CursorBlinkingChange(true));
},
}
}
@@ -2239,7 +2264,9 @@ impl<T: EventListener> Handler for Term<T> {
ansi::Mode::DECCOLM => self.deccolm(),
ansi::Mode::Insert => self.mode.remove(TermMode::INSERT),
ansi::Mode::BlinkingCursor => {
- trace!("... unimplemented mode");
+ let style = self.cursor_style.get_or_insert(self.default_cursor_style);
+ style.blinking = false;
+ self.event_proxy.send_event(Event::CursorBlinkingChange(false));
},
}
}
@@ -2296,6 +2323,18 @@ impl<T: EventListener> Handler for Term<T> {
fn set_cursor_style(&mut self, style: Option<CursorStyle>) {
trace!("Setting cursor style {:?}", style);
self.cursor_style = style;
+
+ // Notify UI about blinking changes.
+ let blinking = style.unwrap_or(self.default_cursor_style).blinking;
+ self.event_proxy.send_event(Event::CursorBlinkingChange(blinking));
+ }
+
+ #[inline]
+ fn set_cursor_shape(&mut self, shape: CursorShape) {
+ trace!("Setting cursor shape {:?}", shape);
+
+ let style = self.cursor_style.get_or_insert(self.default_cursor_style);
+ style.shape = shape;
}
#[inline]