diff options
Diffstat (limited to 'alacritty_terminal/src')
-rw-r--r-- | alacritty_terminal/src/ansi.rs | 45 | ||||
-rw-r--r-- | alacritty_terminal/src/config/mod.rs | 106 | ||||
-rw-r--r-- | alacritty_terminal/src/event.rs | 2 | ||||
-rw-r--r-- | alacritty_terminal/src/term/mod.rs | 73 |
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] |