aboutsummaryrefslogtreecommitdiff
path: root/alacritty
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty')
-rw-r--r--alacritty/Cargo.toml2
-rw-r--r--alacritty/src/config/bell.rs70
-rw-r--r--alacritty/src/config/color.rs212
-rw-r--r--alacritty/src/config/mod.rs14
-rw-r--r--alacritty/src/config/ui_config.rs14
-rw-r--r--alacritty/src/display/bell.rs122
-rw-r--r--alacritty/src/display/color.rs167
-rw-r--r--alacritty/src/display/content.rs404
-rw-r--r--alacritty/src/display/cursor.rs (renamed from alacritty/src/cursor.rs)4
-rw-r--r--alacritty/src/display/meter.rs (renamed from alacritty/src/meter.rs)0
-rw-r--r--alacritty/src/display/mod.rs (renamed from alacritty/src/display.rs)71
-rw-r--r--alacritty/src/display/wayland_theme.rs (renamed from alacritty/src/wayland_theme.rs)3
-rw-r--r--alacritty/src/display/window.rs (renamed from alacritty/src/window.rs)11
-rw-r--r--alacritty/src/event.rs249
-rw-r--r--alacritty/src/input.rs157
-rw-r--r--alacritty/src/main.rs6
-rw-r--r--alacritty/src/renderer/mod.rs9
-rw-r--r--alacritty/src/renderer/rects.rs16
-rw-r--r--alacritty/src/url.rs37
19 files changed, 1312 insertions, 256 deletions
diff --git a/alacritty/Cargo.toml b/alacritty/Cargo.toml
index 73724fbe..923659d2 100644
--- a/alacritty/Cargo.toml
+++ b/alacritty/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
[dependencies.alacritty_terminal]
path = "../alacritty_terminal"
-version = "0.12.1-dev"
+version = "0.13.0-dev"
default-features = false
[dependencies.alacritty_config_derive]
diff --git a/alacritty/src/config/bell.rs b/alacritty/src/config/bell.rs
new file mode 100644
index 00000000..2516e2b3
--- /dev/null
+++ b/alacritty/src/config/bell.rs
@@ -0,0 +1,70 @@
+use std::time::Duration;
+
+use alacritty_config_derive::ConfigDeserialize;
+
+use alacritty_terminal::config::Program;
+use alacritty_terminal::term::color::Rgb;
+
+#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
+pub struct BellConfig {
+ /// Visual bell animation function.
+ pub animation: BellAnimation,
+
+ /// Command to run on bell.
+ pub command: Option<Program>,
+
+ /// Visual bell flash color.
+ pub color: Rgb,
+
+ /// Visual bell duration in milliseconds.
+ duration: u16,
+}
+
+impl Default for BellConfig {
+ fn default() -> Self {
+ Self {
+ color: Rgb { r: 255, g: 255, b: 255 },
+ animation: Default::default(),
+ command: Default::default(),
+ duration: Default::default(),
+ }
+ }
+}
+
+impl BellConfig {
+ pub fn duration(&self) -> Duration {
+ Duration::from_millis(self.duration as u64)
+ }
+}
+
+/// `VisualBellAnimations` are modeled after a subset of CSS transitions and Robert
+/// Penner's Easing Functions.
+#[derive(ConfigDeserialize, Clone, Copy, Debug, PartialEq, Eq)]
+pub enum BellAnimation {
+ // CSS animation.
+ Ease,
+ // CSS animation.
+ EaseOut,
+ // Penner animation.
+ EaseOutSine,
+ // Penner animation.
+ EaseOutQuad,
+ // Penner animation.
+ EaseOutCubic,
+ // Penner animation.
+ EaseOutQuart,
+ // Penner animation.
+ EaseOutQuint,
+ // Penner animation.
+ EaseOutExpo,
+ // Penner animation.
+ EaseOutCirc,
+ // Penner animation.
+ Linear,
+}
+
+impl Default for BellAnimation {
+ fn default() -> Self {
+ BellAnimation::EaseOutExpo
+ }
+}
diff --git a/alacritty/src/config/color.rs b/alacritty/src/config/color.rs
new file mode 100644
index 00000000..cd5d964d
--- /dev/null
+++ b/alacritty/src/config/color.rs
@@ -0,0 +1,212 @@
+use serde::de::Error as SerdeError;
+use serde::{Deserialize, Deserializer};
+
+use alacritty_config_derive::ConfigDeserialize;
+use alacritty_terminal::term::color::{CellRgb, Rgb};
+
+#[derive(ConfigDeserialize, Clone, Debug, Default, PartialEq, Eq)]
+pub struct Colors {
+ pub primary: PrimaryColors,
+ pub cursor: InvertedCellColors,
+ pub vi_mode_cursor: InvertedCellColors,
+ pub selection: InvertedCellColors,
+ pub normal: NormalColors,
+ pub bright: BrightColors,
+ pub dim: Option<DimColors>,
+ pub indexed_colors: Vec<IndexedColor>,
+ pub search: SearchColors,
+ pub line_indicator: LineIndicatorColors,
+}
+
+impl Colors {
+ pub fn search_bar_foreground(&self) -> Rgb {
+ self.search.bar.foreground.unwrap_or(self.primary.background)
+ }
+
+ pub fn search_bar_background(&self) -> Rgb {
+ self.search.bar.background.unwrap_or(self.primary.foreground)
+ }
+}
+
+#[derive(ConfigDeserialize, Copy, Clone, Default, Debug, PartialEq, Eq)]
+pub struct LineIndicatorColors {
+ pub foreground: Option<Rgb>,
+ pub background: Option<Rgb>,
+}
+
+#[derive(Deserialize, Copy, Clone, Default, Debug, PartialEq, Eq)]
+pub struct IndexedColor {
+ pub color: Rgb,
+
+ index: ColorIndex,
+}
+
+impl IndexedColor {
+ #[inline]
+ pub fn index(&self) -> u8 {
+ self.index.0
+ }
+}
+
+#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
+struct ColorIndex(u8);
+
+impl<'de> Deserialize<'de> for ColorIndex {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ let index = u8::deserialize(deserializer)?;
+
+ if index < 16 {
+ Err(SerdeError::custom(
+ "Config error: indexed_color's index is {}, but a value bigger than 15 was \
+ expected; ignoring setting",
+ ))
+ } else {
+ Ok(Self(index))
+ }
+ }
+}
+
+#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]
+pub struct InvertedCellColors {
+ #[config(alias = "text")]
+ pub foreground: CellRgb,
+ #[config(alias = "cursor")]
+ pub background: CellRgb,
+}
+
+impl Default for InvertedCellColors {
+ fn default() -> Self {
+ Self { foreground: CellRgb::CellBackground, background: CellRgb::CellForeground }
+ }
+}
+
+#[derive(ConfigDeserialize, Debug, Copy, Clone, Default, PartialEq, Eq)]
+pub struct SearchColors {
+ pub focused_match: InvertedCellColors,
+ pub matches: MatchColors,
+ bar: BarColors,
+}
+
+#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]
+pub struct MatchColors {
+ pub foreground: CellRgb,
+ pub background: CellRgb,
+}
+
+impl Default for MatchColors {
+ fn default() -> Self {
+ Self {
+ background: CellRgb::Rgb(Rgb { r: 0xff, g: 0xff, b: 0xff }),
+ foreground: CellRgb::Rgb(Rgb { r: 0x00, g: 0x00, b: 0x00 }),
+ }
+ }
+}
+
+#[derive(ConfigDeserialize, Debug, Copy, Clone, Default, PartialEq, Eq)]
+pub struct BarColors {
+ foreground: Option<Rgb>,
+ background: Option<Rgb>,
+}
+
+#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
+pub struct PrimaryColors {
+ pub foreground: Rgb,
+ pub background: Rgb,
+ pub bright_foreground: Option<Rgb>,
+ pub dim_foreground: Option<Rgb>,
+}
+
+impl Default for PrimaryColors {
+ fn default() -> Self {
+ PrimaryColors {
+ background: Rgb { r: 0x1d, g: 0x1f, b: 0x21 },
+ foreground: Rgb { r: 0xc5, g: 0xc8, b: 0xc6 },
+ bright_foreground: Default::default(),
+ dim_foreground: Default::default(),
+ }
+ }
+}
+
+#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
+pub struct NormalColors {
+ pub black: Rgb,
+ pub red: Rgb,
+ pub green: Rgb,
+ pub yellow: Rgb,
+ pub blue: Rgb,
+ pub magenta: Rgb,
+ pub cyan: Rgb,
+ pub white: Rgb,
+}
+
+impl Default for NormalColors {
+ fn default() -> Self {
+ NormalColors {
+ black: Rgb { r: 0x1d, g: 0x1f, b: 0x21 },
+ red: Rgb { r: 0xcc, g: 0x66, b: 0x66 },
+ green: Rgb { r: 0xb5, g: 0xbd, b: 0x68 },
+ yellow: Rgb { r: 0xf0, g: 0xc6, b: 0x74 },
+ blue: Rgb { r: 0x81, g: 0xa2, b: 0xbe },
+ magenta: Rgb { r: 0xb2, g: 0x94, b: 0xbb },
+ cyan: Rgb { r: 0x8a, g: 0xbe, b: 0xb7 },
+ white: Rgb { r: 0xc5, g: 0xc8, b: 0xc6 },
+ }
+ }
+}
+
+#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
+pub struct BrightColors {
+ pub black: Rgb,
+ pub red: Rgb,
+ pub green: Rgb,
+ pub yellow: Rgb,
+ pub blue: Rgb,
+ pub magenta: Rgb,
+ pub cyan: Rgb,
+ pub white: Rgb,
+}
+
+impl Default for BrightColors {
+ fn default() -> Self {
+ BrightColors {
+ black: Rgb { r: 0x66, g: 0x66, b: 0x66 },
+ red: Rgb { r: 0xd5, g: 0x4e, b: 0x53 },
+ green: Rgb { r: 0xb9, g: 0xca, b: 0x4a },
+ yellow: Rgb { r: 0xe7, g: 0xc5, b: 0x47 },
+ blue: Rgb { r: 0x7a, g: 0xa6, b: 0xda },
+ magenta: Rgb { r: 0xc3, g: 0x97, b: 0xd8 },
+ cyan: Rgb { r: 0x70, g: 0xc0, b: 0xb1 },
+ white: Rgb { r: 0xea, g: 0xea, b: 0xea },
+ }
+ }
+}
+
+#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
+pub struct DimColors {
+ pub black: Rgb,
+ pub red: Rgb,
+ pub green: Rgb,
+ pub yellow: Rgb,
+ pub blue: Rgb,
+ pub magenta: Rgb,
+ pub cyan: Rgb,
+ pub white: Rgb,
+}
+
+impl Default for DimColors {
+ fn default() -> Self {
+ DimColors {
+ black: Rgb { r: 0x13, g: 0x14, b: 0x15 },
+ red: Rgb { r: 0x86, g: 0x43, b: 0x43 },
+ green: Rgb { r: 0x77, g: 0x7c, b: 0x44 },
+ yellow: Rgb { r: 0x9e, g: 0x82, b: 0x4c },
+ blue: Rgb { r: 0x55, g: 0x6a, b: 0x7d },
+ magenta: Rgb { r: 0x75, g: 0x61, b: 0x7b },
+ cyan: Rgb { r: 0x5b, g: 0x7d, b: 0x78 },
+ white: Rgb { r: 0x82, g: 0x84, b: 0x82 },
+ }
+ }
+}
diff --git a/alacritty/src/config/mod.rs b/alacritty/src/config/mod.rs
index 0673ffd5..a782f5fe 100644
--- a/alacritty/src/config/mod.rs
+++ b/alacritty/src/config/mod.rs
@@ -1,5 +1,5 @@
use std::fmt::{self, Display, Formatter};
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
use std::{env, fs, io};
use log::{error, info};
@@ -9,6 +9,8 @@ use serde_yaml::Value;
use alacritty_terminal::config::{Config as TermConfig, LOG_TARGET_CONFIG};
+pub mod bell;
+pub mod color;
pub mod debug;
pub mod font;
pub mod monitor;
@@ -123,10 +125,10 @@ pub fn load(options: &Options) -> Config {
}
/// Attempt to reload the configuration file.
-pub fn reload(config_path: &PathBuf, options: &Options) -> Result<Config> {
+pub fn reload(config_path: &Path, options: &Options) -> Result<Config> {
// Load config, propagating errors.
let config_options = options.config_options().clone();
- let mut config = load_from(&config_path, config_options)?;
+ let mut config = load_from(config_path, config_options)?;
// Override config with CLI options.
options.override_config(&mut config);
@@ -135,7 +137,7 @@ pub fn reload(config_path: &PathBuf, options: &Options) -> Result<Config> {
}
/// Load configuration file and log errors.
-fn load_from(path: &PathBuf, cli_config: Value) -> Result<Config> {
+fn load_from(path: &Path, cli_config: Value) -> Result<Config> {
match read_config(path, cli_config) {
Ok(config) => Ok(config),
Err(err) => {
@@ -146,7 +148,7 @@ fn load_from(path: &PathBuf, cli_config: Value) -> Result<Config> {
}
/// Deserialize configuration file from path.
-fn read_config(path: &PathBuf, cli_config: Value) -> Result<Config> {
+fn read_config(path: &Path, cli_config: Value) -> Result<Config> {
let mut config_paths = Vec::new();
let mut config_value = parse_config(&path, &mut config_paths, IMPORT_RECURSION_LIMIT)?;
@@ -162,7 +164,7 @@ fn read_config(path: &PathBuf, cli_config: Value) -> Result<Config> {
/// Deserialize all configuration files as generic Value.
fn parse_config(
- path: &PathBuf,
+ path: &Path,
config_paths: &mut Vec<PathBuf>,
recursion_limit: usize,
) -> Result<Value> {
diff --git a/alacritty/src/config/ui_config.rs b/alacritty/src/config/ui_config.rs
index 25f9fb91..b3b3021a 100644
--- a/alacritty/src/config/ui_config.rs
+++ b/alacritty/src/config/ui_config.rs
@@ -6,7 +6,9 @@ use serde::{Deserialize, Deserializer};
use alacritty_config_derive::ConfigDeserialize;
use alacritty_terminal::config::{Percentage, LOG_TARGET_CONFIG};
+use crate::config::bell::BellConfig;
use crate::config::bindings::{self, Binding, KeyBinding, MouseBinding};
+use crate::config::color::Colors;
use crate::config::debug::Debug;
use crate::config::font::Font;
use crate::config::mouse::Mouse;
@@ -31,6 +33,15 @@ pub struct UIConfig {
/// Live config reload.
pub live_config_reload: bool,
+ /// Bell configuration.
+ pub bell: BellConfig,
+
+ /// RGB values for colors.
+ pub colors: Colors,
+
+ /// Should draw bold text with brighter colors instead of bold font.
+ pub draw_bold_text_with_bright_colors: bool,
+
/// Path where config was loaded from.
#[config(skip)]
pub config_paths: Vec<PathBuf>,
@@ -58,6 +69,9 @@ impl Default for UIConfig {
key_bindings: Default::default(),
mouse_bindings: Default::default(),
background_opacity: Default::default(),
+ bell: Default::default(),
+ colors: Default::default(),
+ draw_bold_text_with_bright_colors: Default::default(),
}
}
}
diff --git a/alacritty/src/display/bell.rs b/alacritty/src/display/bell.rs
new file mode 100644
index 00000000..1aee3ba6
--- /dev/null
+++ b/alacritty/src/display/bell.rs
@@ -0,0 +1,122 @@
+use std::time::{Duration, Instant};
+
+use crate::config::bell::{BellAnimation, BellConfig};
+
+pub struct VisualBell {
+ /// Visual bell animation.
+ animation: BellAnimation,
+
+ /// Visual bell duration.
+ duration: Duration,
+
+ /// The last time the visual bell rang, if at all.
+ start_time: Option<Instant>,
+}
+
+impl VisualBell {
+ /// Ring the visual bell, and return its intensity.
+ pub fn ring(&mut self) -> f64 {
+ let now = Instant::now();
+ self.start_time = Some(now);
+ self.intensity_at_instant(now)
+ }
+
+ /// Get the currently intensity of the visual bell. The bell's intensity
+ /// ramps down from 1.0 to 0.0 at a rate determined by the bell's duration.
+ pub fn intensity(&self) -> f64 {
+ self.intensity_at_instant(Instant::now())
+ }
+
+ /// Check whether or not the visual bell has completed "ringing".
+ pub fn completed(&mut self) -> bool {
+ match self.start_time {
+ Some(earlier) => {
+ if Instant::now().duration_since(earlier) >= self.duration {
+ self.start_time = None;
+ }
+ false
+ },
+ None => true,
+ }
+ }
+
+ /// Get the intensity of the visual bell at a particular instant. The bell's
+ /// intensity ramps down from 1.0 to 0.0 at a rate determined by the bell's
+ /// duration.
+ pub fn intensity_at_instant(&self, instant: Instant) -> f64 {
+ // If `duration` is zero, then the VisualBell is disabled; therefore,
+ // its `intensity` is zero.
+ if self.duration == Duration::from_secs(0) {
+ return 0.0;
+ }
+
+ match self.start_time {
+ // Similarly, if `start_time` is `None`, then the VisualBell has not
+ // been "rung"; therefore, its `intensity` is zero.
+ None => 0.0,
+
+ Some(earlier) => {
+ // Finally, if the `instant` at which we wish to compute the
+ // VisualBell's `intensity` occurred before the VisualBell was
+ // "rung", then its `intensity` is also zero.
+ if instant < earlier {
+ return 0.0;
+ }
+
+ let elapsed = instant.duration_since(earlier);
+ let elapsed_f =
+ elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9f64;
+ let duration_f = self.duration.as_secs() as f64
+ + f64::from(self.duration.subsec_nanos()) / 1e9f64;
+
+ // Otherwise, we compute a value `time` from 0.0 to 1.0
+ // inclusive that represents the ratio of `elapsed` time to the
+ // `duration` of the VisualBell.
+ let time = (elapsed_f / duration_f).min(1.0);
+
+ // We use this to compute the inverse `intensity` of the
+ // VisualBell. When `time` is 0.0, `inverse_intensity` is 0.0,
+ // and when `time` is 1.0, `inverse_intensity` is 1.0.
+ let inverse_intensity = match self.animation {
+ BellAnimation::Ease | BellAnimation::EaseOut => {
+ cubic_bezier(0.25, 0.1, 0.25, 1.0, time)
+ },
+ BellAnimation::EaseOutSine => cubic_bezier(0.39, 0.575, 0.565, 1.0, time),
+ BellAnimation::EaseOutQuad => cubic_bezier(0.25, 0.46, 0.45, 0.94, time),
+ BellAnimation::EaseOutCubic => cubic_bezier(0.215, 0.61, 0.355, 1.0, time),
+ BellAnimation::EaseOutQuart => cubic_bezier(0.165, 0.84, 0.44, 1.0, time),
+ BellAnimation::EaseOutQuint => cubic_bezier(0.23, 1.0, 0.32, 1.0, time),
+ BellAnimation::EaseOutExpo => cubic_bezier(0.19, 1.0, 0.22, 1.0, time),
+ BellAnimation::EaseOutCirc => cubic_bezier(0.075, 0.82, 0.165, 1.0, time),
+ BellAnimation::Linear => time,
+ };
+
+ // Since we want the `intensity` of the VisualBell to decay over
+ // `time`, we subtract the `inverse_intensity` from 1.0.
+ 1.0 - inverse_intensity
+ },
+ }
+ }
+
+ pub fn update_config(&mut self, bell_config: &BellConfig) {
+ self.animation = bell_config.animation;
+ self.duration = bell_config.duration();
+ }
+}
+
+impl From<&BellConfig> for VisualBell {
+ fn from(bell_config: &BellConfig) -> VisualBell {
+ VisualBell {
+ animation: bell_config.animation,
+ duration: bell_config.duration(),
+ start_time: None,
+ }
+ }
+}
+
+fn cubic_bezier(p0: f64, p1: f64, p2: f64, p3: f64, x: f64) -> f64 {
+ (1.0 - x).powi(3) * p0
+ + 3.0 * (1.0 - x).powi(2) * x * p1
+ + 3.0 * (1.0 - x) * x.powi(2) * p2
+ + x.powi(3) * p3
+}
diff --git a/alacritty/src/display/color.rs b/alacritty/src/display/color.rs
new file mode 100644
index 00000000..6e0de048
--- /dev/null
+++ b/alacritty/src/display/color.rs
@@ -0,0 +1,167 @@
+use std::ops::{Index, IndexMut};
+
+use log::trace;
+
+use alacritty_terminal::ansi::NamedColor;
+use alacritty_terminal::term::color::{Rgb, COUNT};
+
+use crate::config::color::Colors;
+
+/// Factor for automatic computation of dim colors.
+pub const DIM_FACTOR: f32 = 0.66;
+
+#[derive(Copy, Clone)]
+pub struct List([Rgb; COUNT]);
+
+impl<'a> From<&'a Colors> for List {
+ fn from(colors: &Colors) -> List {
+ // Type inference fails without this annotation.
+ let mut list = List([Rgb::default(); COUNT]);
+
+ list.fill_named(colors);
+ list.fill_cube(colors);
+ list.fill_gray_ramp(colors);
+
+ list
+ }
+}
+
+impl List {
+ pub fn fill_named(&mut self, colors: &Colors) {
+ // Normals.
+ self[NamedColor::Black] = colors.normal.black;
+ self[NamedColor::Red] = colors.normal.red;
+ self[NamedColor::Green] = colors.normal.green;
+ self[NamedColor::Yellow] = colors.normal.yellow;
+ self[NamedColor::Blue] = colors.normal.blue;
+ self[NamedColor::Magenta] = colors.normal.magenta;
+ self[NamedColor::Cyan] = colors.normal.cyan;
+ self[NamedColor::White] = colors.normal.white;
+
+ // Brights.
+ self[NamedColor::BrightBlack] = colors.bright.black;
+ self[NamedColor::BrightRed] = colors.bright.red;
+ self[NamedColor::BrightGreen] = colors.bright.green;
+ self[NamedColor::BrightYellow] = colors.bright.yellow;
+ self[NamedColor::BrightBlue] = colors.bright.blue;
+ self[NamedColor::BrightMagenta] = colors.bright.magenta;
+ self[NamedColor::BrightCyan] = colors.bright.cyan;
+ self[NamedColor::BrightWhite] = colors.bright.white;
+ self[NamedColor::BrightForeground] =
+ colors.primary.bright_foreground.unwrap_or(colors.primary.foreground);
+
+ // Foreground and background.
+ self[NamedColor::Foreground] = colors.primary.foreground;
+ self[NamedColor::Background] = colors.primary.background;
+
+ // Dims.
+ self[NamedColor::DimForeground] =
+ colors.primary.dim_foreground.unwrap_or(colors.primary.foreground * DIM_FACTOR);
+ match colors.dim {
+ Some(ref dim) => {
+ trace!("Using config-provided dim colors");
+ self[NamedColor::DimBlack] = dim.black;
+ self[NamedColor::DimRed] = dim.red;
+ self[NamedColor::DimGreen] = dim.green;
+ self[NamedColor::DimYellow] = dim.yellow;
+ self[NamedColor::DimBlue] = dim.blue;
+ self[NamedColor::DimMagenta] = dim.magenta;
+ self[NamedColor::DimCyan] = dim.cyan;
+ self[NamedColor::DimWhite] = dim.white;
+ },
+ None => {
+ trace!("Deriving dim colors from normal colors");
+ self[NamedColor::DimBlack] = colors.normal.black * DIM_FACTOR;
+ self[NamedColor::DimRed] = colors.normal.red * DIM_FACTOR;
+ self[NamedColor::DimGreen] = colors.normal.green * DIM_FACTOR;
+ self[NamedColor::DimYellow] = colors.normal.yellow * DIM_FACTOR;
+ self[NamedColor::DimBlue] = colors.normal.blue * DIM_FACTOR;
+ self[NamedColor::DimMagenta] = colors.normal.magenta * DIM_FACTOR;
+ self[NamedColor::DimCyan] = colors.normal.cyan * DIM_FACTOR;
+ self[NamedColor::DimWhite] = colors.normal.white * DIM_FACTOR;
+ },
+ }
+ }
+
+ pub fn fill_cube(&mut self, colors: &Colors) {
+ let mut index: usize = 16;
+ // Build colors.
+ for r in 0..6 {
+ for g in 0..6 {
+ for b in 0..6 {
+ // Override colors 16..232 with the config (if present).
+ if let Some(indexed_color) =
+ colors.indexed_colors.iter().find(|ic| ic.index() == index as u8)
+ {
+ self[index] = indexed_color.color;
+ } else {
+ self[index] = Rgb {
+ r: if r == 0 { 0 } else { r * 40 + 55 },
+ b: if b == 0 { 0 } else { b * 40 + 55 },
+ g: if g == 0 { 0 } else { g * 40 + 55 },
+ };
+ }
+ index += 1;
+ }
+ }
+ }
+
+ debug_assert!(index == 232);
+ }
+
+ pub fn fill_gray_ramp(&mut self, colors: &Colors) {
+ let mut index: usize = 232;
+
+ for i in 0..24 {
+ // Index of the color is number of named colors + number of cube colors + i.
+ let color_index = 16 + 216 + i;
+
+ // Override colors 232..256 with the config (if present).
+ if let Some(indexed_color) =
+ colors.indexed_colors.iter().find(|ic| ic.index() == color_index)
+ {
+ self[index] = indexed_color.color;
+ index += 1;
+ continue;
+ }
+
+ let value = i * 10 + 8;
+ self[index] = Rgb { r: value, g: value, b: value };
+ index += 1;
+ }
+
+ debug_assert!(index == 256);
+ }
+}
+
+impl Index<usize> for List {
+ type Output = Rgb;
+
+ #[inline]
+ fn index(&self, idx: usize) -> &Self::Output {
+ &self.0[idx]
+ }
+}
+
+impl IndexMut<usize> for List {
+ #[inline]
+ fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
+ &mut self.0[idx]
+ }
+}
+
+impl Index<NamedColor> for List {
+ type Output = Rgb;
+
+ #[inline]
+ fn index(&self, idx: NamedColor) -> &Self::Output {
+ &self.0[idx as usize]
+ }
+}
+
+impl IndexMut<NamedColor> for List {
+ #[inline]
+ fn index_mut(&mut self, idx: NamedColor) -> &mut Self::Output {
+ &mut self.0[idx as usize]
+ }
+}
diff --git a/alacritty/src/display/content.rs b/alacritty/src/display/content.rs
new file mode 100644
index 00000000..81c2977f
--- /dev/null
+++ b/alacritty/src/display/content.rs
@@ -0,0 +1,404 @@
+use std::cmp::max;
+use std::mem;
+use std::ops::RangeInclusive;
+
+use alacritty_terminal::ansi::{Color, CursorShape, NamedColor};
+use alacritty_terminal::config::Config;
+use alacritty_terminal::event::EventListener;
+use alacritty_terminal::grid::{Dimensions, Indexed};
+use alacritty_terminal::index::{Column, Direction, Line, Point};
+use alacritty_terminal::term::cell::{Cell, Flags};
+use alacritty_terminal::term::color::{CellRgb, Rgb};
+use alacritty_terminal::term::search::{RegexIter, RegexSearch};
+use alacritty_terminal::term::{
+ RenderableContent as TerminalContent, RenderableCursor as TerminalCursor, Term, TermMode,
+};
+
+use crate::config::ui_config::UIConfig;
+use crate::display::color::{List, DIM_FACTOR};
+
+/// Minimum contrast between a fixed cursor color and the cell's background.
+pub const MIN_CURSOR_CONTRAST: f64 = 1.5;
+
+/// Maximum number of linewraps followed outside of the viewport during search highlighting.
+const MAX_SEARCH_LINES: usize = 100;
+
+/// Renderable terminal content.
+///
+/// This provides the terminal cursor and an iterator over all non-empty cells.
+pub struct RenderableContent<'a> {
+ terminal_content: TerminalContent<'a>,
+ terminal_cursor: TerminalCursor,
+ cursor: Option<RenderableCursor>,
+ search: RenderableSearch,
+ config: &'a Config<UIConfig>,
+ colors: &'a List,
+}
+
+impl<'a> RenderableContent<'a> {
+ pub fn new<T: EventListener>(
+ term: &'a Term<T>,
+ dfas: Option<&RegexSearch>,
+ config: &'a Config<UIConfig>,
+ colors: &'a List,
+ show_cursor: bool,
+ ) -> Self {
+ let search = dfas.map(|dfas| RenderableSearch::new(&term, dfas)).unwrap_or_default();
+ let terminal_content = term.renderable_content();
+
+ // Copy the cursor and override its shape if necessary.
+ let mut terminal_cursor = terminal_content.cursor;
+ if !show_cursor {
+ terminal_cursor.shape = CursorShape::Hidden;
+ } else if !term.is_focused && config.cursor.unfocused_hollow {
+ terminal_cursor.shape = CursorShape::HollowBlock;
+ }
+
+ Self { cursor: None, terminal_content, terminal_cursor, search, config, colors }
+ }
+
+ /// Viewport offset.
+ pub fn display_offset(&self) -> usize {
+ self.terminal_content.display_offset
+ }
+
+ /// Get the terminal cursor.
+ pub fn cursor(mut self) -> Option<RenderableCursor> {
+ // Drain the iterator to make sure the cursor is created.
+ while self.next().is_some() && self.cursor.is_none() {}
+
+ self.cursor
+ }
+
+ /// Get the RGB value for a color index.
+ pub fn color(&self, color: usize) -> Rgb {
+ self.terminal_content.colors[color].unwrap_or(self.colors[color])
+ }
+
+ /// 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.terminal_cursor.shape == CursorShape::Hidden {
+ return None;
+ }
+
+ // Expand across wide cell when inside wide char or spacer.
+ let is_wide = if cell.flags.contains(Flags::WIDE_CHAR_SPACER) {
+ self.terminal_cursor.point.column -= 1;
+ true
+ } else {
+ cell.flags.contains(Flags::WIDE_CHAR)
+ };
+
+ // Cursor colors.
+ let color = if self.terminal_content.mode.contains(TermMode::VI) {
+ self.config.ui_config.colors.vi_mode_cursor
+ } else {
+ self.config.ui_config.colors.cursor
+ };
+ let mut cursor_color =
+ self.terminal_content.colors[NamedColor::Cursor].map_or(color.background, CellRgb::Rgb);
+ let mut text_color = color.foreground;
+
+ // Invert the cursor if it has a fixed background close to the cell's background.
+ if matches!(
+ cursor_color,
+ CellRgb::Rgb(color) if color.contrast(cell.bg) < MIN_CURSOR_CONTRAST
+ ) {
+ cursor_color = CellRgb::CellForeground;
+ text_color = CellRgb::CellBackground;
+ }
+
+ // Convert from cell colors to RGB.
+ let text_color = text_color.color(cell.fg, cell.bg);
+ let cursor_color = cursor_color.color(cell.fg, cell.bg);
+
+ Some(RenderableCursor {
+ point: self.terminal_cursor.point,
+ shape: self.terminal_cursor.shape,
+ cursor_color,
+ text_color,
+ is_wide,
+ })
+ }
+}
+
+impl<'a> Iterator for RenderableContent<'a> {
+ type Item = RenderableCell;
+
+ /// Gets the next renderable cell.
+ ///
+ /// Skips empty (background) cells and applies any flags to the cell state
+ /// (eg. invert fg and bg colors).
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ loop {
+ let cell = self.terminal_content.display_iter.next()?;
+ let mut cell = RenderableCell::new(self, cell);
+
+ if self.terminal_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
+ });
+
+ return Some(cell);
+ } else if !cell.is_empty() && !cell.flags.contains(Flags::WIDE_CHAR_SPACER) {
+ // Skip empty cells and wide char spacers.
+ return Some(cell);
+ }
+ }
+ }
+}
+
+/// Cell ready for rendering.
+#[derive(Clone, Debug)]
+pub struct RenderableCell {
+ pub character: char,
+ pub zerowidth: Option<Vec<char>>,
+ pub point: Point,
+ pub fg: Rgb,
+ pub bg: Rgb,
+ pub bg_alpha: f32,
+ pub flags: Flags,
+ pub is_match: bool,
+}
+
+impl RenderableCell {
+ fn new<'a>(content: &mut RenderableContent<'a>, cell: Indexed<&Cell, Line>) -> Self {
+ // Lookup RGB values.
+ let mut fg_rgb = Self::compute_fg_rgb(content, cell.fg, cell.flags);
+ let mut bg_rgb = Self::compute_bg_rgb(content, cell.bg);
+
+ let mut bg_alpha = if cell.flags.contains(Flags::INVERSE) {
+ mem::swap(&mut fg_rgb, &mut bg_rgb);
+ 1.0
+ } else {
+ Self::compute_bg_alpha(cell.bg)
+ };
+
+ let is_selected = content
+ .terminal_content
+ .selection
+ .map_or(false, |selection| selection.contains_cell(&cell, content.terminal_cursor));
+ let mut is_match = false;
+
+ let colors = &content.config.ui_config.colors;
+ if is_selected {
+ let config_bg = colors.selection.background;
+ let selected_fg = colors.selection.foreground.color(fg_rgb, bg_rgb);
+ bg_rgb = config_bg.color(fg_rgb, bg_rgb);
+ fg_rgb = selected_fg;
+
+ if fg_rgb == bg_rgb && !cell.flags.contains(Flags::HIDDEN) {
+ // Reveal inversed text when fg/bg is the same.
+ fg_rgb = content.color(NamedColor::Background as usize);
+ bg_rgb = content.color(NamedColor::Foreground as usize);
+ bg_alpha = 1.0;
+ } else if config_bg != CellRgb::CellBackground {
+ bg_alpha = 1.0;
+ }
+ } else if content.search.advance(cell.point) {
+ // Highlight the cell if it is part of a search match.
+ let config_bg = colors.search.matches.background;
+ let matched_fg = colors.search.matches.foreground.color(fg_rgb, bg_rgb);
+ bg_rgb = config_bg.color(fg_rgb, bg_rgb);
+ fg_rgb = matched_fg;
+
+ if config_bg != CellRgb::CellBackground {
+ bg_alpha = 1.0;
+ }
+
+ is_match = true;
+ }
+
+ RenderableCell {
+ character: cell.c,
+ zerowidth: cell.zerowidth().map(|zerowidth| zerowidth.to_vec()),
+ point: cell.point,
+ fg: fg_rgb,
+ bg: bg_rgb,
+ bg_alpha,
+ flags: cell.flags,
+ is_match,
+ }
+ }
+
+ /// Check if cell contains any renderable content.
+ fn is_empty(&self) -> bool {
+ self.bg_alpha == 0.
+ && !self.flags.intersects(Flags::UNDERLINE | Flags::STRIKEOUT | Flags::DOUBLE_UNDERLINE)
+ && self.character == ' '
+ && self.zerowidth.is_none()
+ }
+
+ /// Get the RGB color from a cell's foreground color.
+ fn compute_fg_rgb(content: &mut RenderableContent<'_>, fg: Color, flags: Flags) -> Rgb {
+ let ui_config = &content.config.ui_config;
+ match fg {
+ Color::Spec(rgb) => match flags & Flags::DIM {
+ Flags::DIM => rgb * DIM_FACTOR,
+ _ => rgb,
+ },
+ Color::Named(ansi) => {
+ match (ui_config.draw_bold_text_with_bright_colors, flags & Flags::DIM_BOLD) {
+ // If no bright foreground is set, treat it like the BOLD flag doesn't exist.
+ (_, Flags::DIM_BOLD)
+ if ansi == NamedColor::Foreground
+ && ui_config.colors.primary.bright_foreground.is_none() =>
+ {
+ content.color(NamedColor::DimForeground as usize)
+ },
+ // Draw bold text in bright colors *and* contains bold flag.
+ (true, Flags::BOLD) => content.color(ansi.to_bright() as usize),
+ // Cell is marked as dim and not bold.
+ (_, Flags::DIM) | (false, Flags::DIM_BOLD) => {
+ content.color(ansi.to_dim() as usize)
+ },
+ // None of the above, keep original color..
+ _ => content.color(ansi as usize),
+ }
+ },
+ Color::Indexed(idx) => {
+ let idx = match (
+ ui_config.draw_bold_text_with_bright_colors,
+ flags & Flags::DIM_BOLD,
+ idx,
+ ) {
+ (true, Flags::BOLD, 0..=7) => idx as usize + 8,
+ (false, Flags::DIM, 8..=15) => idx as usize - 8,
+ (false, Flags::DIM, 0..=7) => NamedColor::DimBlack as usize + idx as usize,
+ _ => idx as usize,
+ };
+
+ content.color(idx)
+ },
+ }
+ }
+
+ /// Get the RGB color from a cell's background color.
+ #[inline]
+ fn compute_bg_rgb(content: &mut RenderableContent<'_>, bg: Color) -> Rgb {
+ match bg {
+ Color::Spec(rgb) => rgb,
+ Color::Named(ansi) => content.color(ansi as usize),
+ Color::Indexed(idx) => content.color(idx as usize),
+ }
+ }
+
+ /// Compute background alpha based on cell's original color.
+ ///
+ /// Since an RGB color matching the background should not be transparent, this is computed
+ /// using the named input color, rather than checking the RGB of the background after its color
+ /// is computed.
+ #[inline]
+ fn compute_bg_alpha(bg: Color) -> f32 {
+ if bg == Color::Named(NamedColor::Background) {
+ 0.
+ } else {
+ 1.
+ }
+ }
+}
+
+/// Cursor storing all information relevant for rendering.
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub struct RenderableCursor {
+ shape: CursorShape,
+ cursor_color: Rgb,
+ text_color: Rgb,
+ is_wide: bool,
+ point: Point,
+}
+
+impl RenderableCursor {
+ pub fn color(&self) -> Rgb {
+ self.cursor_color
+ }
+
+ pub fn shape(&self) -> CursorShape {
+ self.shape
+ }
+
+ pub fn is_wide(&self) -> bool {
+ self.is_wide
+ }
+
+ pub fn point(&self) -> Point {
+ self.point
+ }
+}
+
+/// Regex search highlight tracking.
+#[derive(Default)]
+pub struct RenderableSearch {
+ /// All visible search matches.
+ matches: Vec<RangeInclusive<Point>>,
+
+ /// Index of the last match checked.
+ index: usize,
+}
+
+impl RenderableSearch {
+ /// Create a new renderable search iterator.
+ pub fn new<T>(term: &Term<T>, dfas: &RegexSearch) -> Self {
+ let viewport_end = term.grid().display_offset();
+ let viewport_start = viewport_end + term.screen_lines().0 - 1;
+
+ // Compute start of the first and end of the last line.
+ let start_point = Point::new(viewport_start, Column(0));
+ let mut start = term.line_search_left(start_point);
+ let end_point = Point::new(viewport_end, term.cols() - 1);
+ let mut end = term.line_search_right(end_point);
+
+ // Set upper bound on search before/after the viewport to prevent excessive blocking.
+ if start.line > viewport_start + MAX_SEARCH_LINES {
+ if start.line == 0 {
+ // Do not highlight anything if this line is the last.
+ return Self::default();
+ } else {
+ // Start at next line if this one is too long.
+ start.line -= 1;
+ }
+ }
+ end.line = max(end.line, viewport_end.saturating_sub(MAX_SEARCH_LINES));
+
+ // Create an iterater for the current regex search for all visible matches.
+ let iter = RegexIter::new(start, end, Direction::Right, term, dfas)
+ .skip_while(move |rm| rm.end().line > viewport_start)
+ .take_while(move |rm| rm.start().line >= viewport_end)
+ .map(|rm| {
+ let viewport_start = term.grid().clamp_buffer_to_visible(*rm.start());
+ let viewport_end = term.grid().clamp_buffer_to_visible(*rm.end());
+ viewport_start..=viewport_end
+ });
+
+ Self { matches: iter.collect(), index: 0 }
+ }
+
+ /// Advance the search tracker to the next point.
+ ///
+ /// This will return `true` if the point passed is part of a search match.
+ fn advance(&mut self, point: Point) -> bool {
+ while let Some(regex_match) = self.matches.get(self.index) {
+ if regex_match.start() > &point {
+ break;
+ } else if regex_match.end() < &point {
+ self.index += 1;
+ } else {
+ return true;
+ }
+ }
+ false
+ }
+}
diff --git a/alacritty/src/cursor.rs b/alacritty/src/display/cursor.rs
index a9fba66a..0750459d 100644
--- a/alacritty/src/cursor.rs
+++ b/alacritty/src/display/cursor.rs
@@ -2,9 +2,9 @@
use alacritty_terminal::ansi::CursorShape;
use alacritty_terminal::term::color::Rgb;
-use alacritty_terminal::term::render::RenderableCursor;
use alacritty_terminal::term::SizeInfo;
+use crate::display::content::RenderableCursor;
use crate::renderer::rects::RenderRect;
/// Trait for conversion into the iterator.
@@ -16,7 +16,7 @@ pub trait IntoRects {
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 x = point.column.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();
diff --git a/alacritty/src/meter.rs b/alacritty/src/display/meter.rs
index c07d901f..c07d901f 100644
--- a/alacritty/src/meter.rs
+++ b/alacritty/src/display/meter.rs
diff --git a/alacritty/src/display.rs b/alacritty/src/display/mod.rs
index 5e885b53..2a55402e 100644
--- a/alacritty/src/display.rs
+++ b/alacritty/src/display/mod.rs
@@ -22,6 +22,7 @@ use wayland_client::{Display as WaylandDisplay, EventQueue};
use crossfont::{self, Rasterize, Rasterizer};
+use alacritty_terminal::ansi::NamedColor;
use alacritty_terminal::event::{EventListener, OnResize};
use alacritty_terminal::grid::Dimensions as _;
use alacritty_terminal::index::{Column, Direction, Line, Point};
@@ -33,14 +34,27 @@ use crate::config::window::Dimensions;
#[cfg(not(windows))]
use crate::config::window::StartupMode;
use crate::config::Config;
-use crate::cursor::IntoRects;
+use crate::display::bell::VisualBell;
+use crate::display::color::List;
+use crate::display::content::RenderableContent;
+use crate::display::cursor::IntoRects;
+use crate::display::meter::Meter;
+use crate::display::window::Window;
use crate::event::{Mouse, SearchState};
use crate::message_bar::{MessageBuffer, MessageType};
-use crate::meter::Meter;
use crate::renderer::rects::{RenderLines, RenderRect};
use crate::renderer::{self, GlyphCache, QuadRenderer};
use crate::url::{Url, Urls};
-use crate::window::{self, Window};
+
+pub mod content;
+pub mod cursor;
+pub mod window;
+
+mod bell;
+mod color;
+mod meter;
+#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
+mod wayland_theme;
const FORWARD_SEARCH_LABEL: &str = "Search: ";
const BACKWARD_SEARCH_LABEL: &str = "Backward Search: ";
@@ -162,6 +176,11 @@ pub struct Display {
/// UI cursor visibility for blinking.
pub cursor_hidden: bool,
+ pub visual_bell: VisualBell,
+
+ /// Mapped RGB values for each terminal color.
+ pub colors: List,
+
renderer: QuadRenderer,
glyph_cache: GlyphCache,
meter: Meter,
@@ -246,7 +265,7 @@ impl Display {
renderer.resize(&size_info);
// Clear screen.
- let background_color = config.colors.primary.background;
+ let background_color = config.ui_config.colors.primary.background;
renderer.with_api(&config.ui_config, &size_info, |api| {
api.clear(background_color);
});
@@ -307,6 +326,8 @@ impl Display {
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
wayland_event_queue,
cursor_hidden: false,
+ visual_bell: VisualBell::from(&config.ui_config.bell),
+ colors: List::from(&config.ui_config.colors),
})
}
@@ -435,7 +456,7 @@ impl Display {
/// A reference to Term whose state is being drawn must be provided.
///
/// This call may block if vsync is enabled.
- pub fn draw<T>(
+ pub fn draw<T: EventListener>(
&mut self,
terminal: MutexGuard<'_, Term<T>>,
message_buffer: &MessageBuffer,
@@ -452,16 +473,17 @@ impl Display {
let cursor_hidden = self.cursor_hidden || search_state.regex().is_some();
// Collect renderable content before the terminal is dropped.
- let mut content = terminal.renderable_content(config, !cursor_hidden);
+ let dfas = search_state.dfas();
+ let colors = &self.colors;
+ let mut content = RenderableContent::new(&terminal, dfas, config, colors, !cursor_hidden);
let mut grid_cells = Vec::new();
while let Some(cell) = content.next() {
grid_cells.push(cell);
}
+ let background_color = content.color(NamedColor::Background as usize);
+ let display_offset = content.display_offset();
let cursor = content.cursor();
- let visual_bell_intensity = terminal.visual_bell.intensity();
- let display_offset = terminal.grid().display_offset();
- let background_color = terminal.background_color();
let cursor_point = terminal.grid().cursor.point;
let total_lines = terminal.grid().total_lines();
let metrics = self.glyph_cache.font_metrics();
@@ -496,9 +518,9 @@ impl Display {
if cell.is_match
&& viewport_match
.as_ref()
- .map_or(false, |viewport_match| viewport_match.contains(&cell.point()))
+ .map_or(false, |viewport_match| viewport_match.contains(&cell.point))
{
- let colors = config.colors.search.focused_match;
+ let colors = config.ui_config.colors.search.focused_match;
let match_fg = colors.foreground.color(cell.fg, cell.bg);
cell.bg = colors.background.color(cell.fg, cell.bg);
cell.fg = match_fg;
@@ -560,13 +582,14 @@ impl Display {
}
// Push visual bell after url/underline/strikeout rects.
+ let visual_bell_intensity = self.visual_bell.intensity();
if visual_bell_intensity != 0. {
let visual_bell_rect = RenderRect::new(
0.,
0.,
size_info.width(),
size_info.height(),
- config.bell().color,
+ config.ui_config.bell.color,
visual_bell_intensity as f32,
);
rects.push(visual_bell_rect);
@@ -581,8 +604,8 @@ impl Display {
let y = size_info.cell_height().mul_add(start_line.0 as f32, size_info.padding_y());
let bg = match message.ty() {
- MessageType::Error => config.colors.normal.red,
- MessageType::Warning => config.colors.normal.yellow,
+ MessageType::Error => config.ui_config.colors.normal.red,
+ MessageType::Warning => config.ui_config.colors.normal.yellow,
};
let message_bar_rect =
@@ -596,7 +619,7 @@ impl Display {
// Relay messages to the user.
let glyph_cache = &mut self.glyph_cache;
- let fg = config.colors.primary.background;
+ let fg = config.ui_config.colors.primary.background;
for (i, message_text) in text.iter().enumerate() {
let point = Point::new(start_line + i, Column(0));
self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
@@ -650,6 +673,12 @@ impl Display {
}
}
+ /// Update to a new configuration.
+ pub fn update_config(&mut self, config: &Config) {
+ self.visual_bell.update_config(&config.ui_config.bell);
+ self.colors = List::from(&config.ui_config.colors);
+ }
+
/// Format search regex to account for the cursor and fullwidth characters.
fn format_search(size_info: &SizeInfo, search_regex: &str, search_label: &str) -> String {
// Add spacers for wide chars.
@@ -690,8 +719,8 @@ impl Display {
let text = format!("{:<1$}", text, num_cols);
let point = Point::new(size_info.screen_lines(), Column(0));
- let fg = config.colors.search_bar_foreground();
- let bg = config.colors.search_bar_background();
+ let fg = config.ui_config.colors.search_bar_foreground();
+ let bg = config.ui_config.colors.search_bar_background();
self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
api.render_string(glyph_cache, point, fg, bg, &text);
@@ -708,8 +737,8 @@ impl Display {
let timing = format!("{:.3} usec", self.meter.average());
let point = Point::new(size_info.screen_lines() - 2, Column(0));
- let fg = config.colors.primary.background;
- let bg = config.colors.normal.red;
+ let fg = config.ui_config.colors.primary.background;
+ let bg = config.ui_config.colors.normal.red;
self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
api.render_string(glyph_cache, point, fg, bg, &timing);
@@ -727,12 +756,12 @@ impl Display {
) {
let text = format!("[{}/{}]", line, total_lines - 1);
let column = Column(size_info.cols().0.saturating_sub(text.len()));
- let colors = &config.colors;
+ let colors = &config.ui_config.colors;
let fg = colors.line_indicator.foreground.unwrap_or(colors.primary.background);
let bg = colors.line_indicator.background.unwrap_or(colors.primary.foreground);
// Do not render anything if it would obscure the vi mode cursor.
- if vi_mode_point.map_or(true, |point| point.line.0 != 0 || point.col < column) {
+ if vi_mode_point.map_or(true, |point| point.line.0 != 0 || point.column < column) {
let glyph_cache = &mut self.glyph_cache;
self.renderer.with_api(&config.ui_config, &size_info, |mut api| {
api.render_string(glyph_cache, Point::new(Line(0), column), fg, bg, &text);
diff --git a/alacritty/src/wayland_theme.rs b/alacritty/src/display/wayland_theme.rs
index 5d3bd922..1932ae01 100644
--- a/alacritty/src/wayland_theme.rs
+++ b/alacritty/src/display/wayland_theme.rs
@@ -1,8 +1,9 @@
use glutin::platform::unix::{ARGBColor, Button, ButtonState, Element, Theme as WaylandTheme};
-use alacritty_terminal::config::Colors;
use alacritty_terminal::term::color::Rgb;
+use crate::config::color::Colors;
+
const INACTIVE_OPACITY: u8 = 127;
#[derive(Debug, Clone)]
diff --git a/alacritty/src/window.rs b/alacritty/src/display/window.rs
index 3661d406..b500e8f2 100644
--- a/alacritty/src/window.rs
+++ b/alacritty/src/display/window.rs
@@ -14,9 +14,8 @@ use {
wayland_client::{Attached, EventQueue, Proxy},
glutin::platform::unix::EventLoopWindowTargetExtUnix,
- alacritty_terminal::config::Colors,
-
- crate::wayland_theme::AlacrittyWaylandTheme,
+ crate::config::color::Colors,
+ crate::display::wayland_theme::AlacrittyWaylandTheme,
};
#[rustfmt::skip]
@@ -59,7 +58,7 @@ use crate::gl;
/// Window icon for `_NET_WM_ICON` property.
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
-static WINDOW_ICON: &[u8] = include_bytes!("../alacritty.png");
+static WINDOW_ICON: &[u8] = include_bytes!("../../alacritty.png");
/// This should match the definition of IDI_ICON from `windows.rc`.
#[cfg(windows)]
@@ -206,7 +205,7 @@ impl Window {
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
let wayland_surface = if is_wayland {
// Apply client side decorations theme.
- let theme = AlacrittyWaylandTheme::new(&config.colors);
+ let theme = AlacrittyWaylandTheme::new(&config.ui_config.colors);
windowed_context.window().set_wayland_theme(theme);
// Attach surface to Alacritty's internal wayland queue to handle frame callbacks.
@@ -422,7 +421,7 @@ impl Window {
/// Adjust the IME editor position according to the new location of the cursor.
pub fn update_ime_position(&mut self, point: Point, size: &SizeInfo) {
- let nspot_x = f64::from(size.padding_x() + point.col.0 as f32 * size.cell_width());
+ let nspot_x = f64::from(size.padding_x() + point.column.0 as f32 * size.cell_width());
let nspot_y = f64::from(size.padding_y() + (point.line.0 + 1) as f32 * size.cell_height());
self.window().set_ime_position(PhysicalPosition::new(nspot_x, nspot_y));
diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs
index d83469ee..26f781dc 100644
--- a/alacritty/src/event.rs
+++ b/alacritty/src/event.rs
@@ -12,7 +12,7 @@ use std::fs::File;
use std::io::Write;
use std::mem;
use std::ops::RangeInclusive;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
#[cfg(not(any(target_os = "macos", windows)))]
use std::sync::atomic::Ordering;
use std::sync::Arc;
@@ -35,6 +35,7 @@ use alacritty_terminal::grid::{Dimensions, Scroll};
use alacritty_terminal::index::{Boundary, Column, Direction, Line, Point, Side};
use alacritty_terminal::selection::{Selection, SelectionType};
use alacritty_terminal::sync::FairMutex;
+use alacritty_terminal::term::search::{Match, RegexSearch};
use alacritty_terminal::term::{ClipboardType, SizeInfo, Term, TermMode};
#[cfg(not(windows))]
use alacritty_terminal::tty;
@@ -44,6 +45,7 @@ use crate::clipboard::Clipboard;
use crate::config;
use crate::config::Config;
use crate::daemon::start_daemon;
+use crate::display::window::Window;
use crate::display::{Display, DisplayUpdate};
use crate::input::{self, ActionContext as _, FONT_SIZE_STEP};
#[cfg(target_os = "macos")]
@@ -51,7 +53,6 @@ use crate::macos;
use crate::message_bar::{Message, MessageBuffer};
use crate::scheduler::{Scheduler, TimerId};
use crate::url::{Url, Urls};
-use crate::window::Window;
/// Duration after the last user input until an unlimited search is performed.
pub const TYPING_SEARCH_DELAY: Duration = Duration::from_millis(500);
@@ -102,13 +103,17 @@ pub struct SearchState {
/// Search regex and history.
///
- /// When a search is currently active, the first element will be what the user can modify in
- /// the current search session. While going through history, the [`history_index`] will point
- /// to the element in history which is currently being previewed.
+ /// During an active search, the first element is the user's current input.
+ ///
+ /// While going through history, the [`SearchState::history_index`] will point to the element
+ /// in history which is currently being previewed.
history: VecDeque<String>,
/// Current position in the search history.
history_index: Option<usize>,
+
+ /// Compiled search automatons.
+ dfas: Option<RegexSearch>,
}
impl SearchState {
@@ -131,6 +136,11 @@ impl SearchState {
self.focused_match.as_ref()
}
+ /// Active search dfas.
+ pub fn dfas(&self) -> Option<&RegexSearch> {
+ self.dfas.as_ref()
+ }
+
/// Search regex text if a search is active.
fn regex_mut(&mut self) -> Option<&mut String> {
self.history_index.and_then(move |index| self.history.get_mut(index))
@@ -146,6 +156,7 @@ impl Default for SearchState {
history_index: Default::default(),
history: Default::default(),
origin: Default::default(),
+ dfas: Default::default(),
}
}
}
@@ -154,31 +165,37 @@ pub struct ActionContext<'a, N, T> {
pub notifier: &'a mut N,
pub terminal: &'a mut Term<T>,
pub clipboard: &'a mut Clipboard,
- pub size_info: &'a mut SizeInfo,
pub mouse: &'a mut Mouse,
pub received_count: &'a mut usize,
pub suppress_chars: &'a mut bool,
pub modifiers: &'a mut ModifiersState,
- pub window: &'a mut Window,
+ pub display: &'a mut Display,
pub message_buffer: &'a mut MessageBuffer,
pub display_update_pending: &'a mut DisplayUpdate,
pub config: &'a mut Config,
pub event_loop: &'a EventLoopWindowTarget<Event>,
- pub urls: &'a Urls,
pub scheduler: &'a mut Scheduler,
pub search_state: &'a mut SearchState,
- cursor_hidden: &'a mut bool,
cli_options: &'a CLIOptions,
font_size: &'a mut Size,
+ dirty: bool,
}
impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionContext<'a, N, T> {
+ #[inline]
fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&mut self, val: B) {
self.notifier.notify(val);
}
+ /// Request a redraw.
+ #[inline]
+ fn mark_dirty(&mut self) {
+ self.dirty = true;
+ }
+
+ #[inline]
fn size_info(&self) -> SizeInfo {
- *self.size_info
+ self.display.size_info
}
fn scroll(&mut self, scroll: Scroll) {
@@ -202,8 +219,10 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
{
let point = self.size_info().pixels_to_coords(self.mouse().x, self.mouse().y);
let cell_side = self.mouse().cell_side;
- self.update_selection(Point { line: point.line, col: point.col }, cell_side);
+ self.update_selection(point, cell_side);
}
+
+ self.dirty = true;
}
fn copy_selection(&mut self, ty: ClipboardType) {
@@ -220,7 +239,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
fn clear_selection(&mut self) {
self.terminal.selection = None;
- self.terminal.dirty = true;
+ self.dirty = true;
}
fn update_selection(&mut self, mut point: Point, side: Side) {
@@ -243,13 +262,13 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
}
self.terminal.selection = Some(selection);
- self.terminal.dirty = true;
+ self.dirty = true;
}
fn start_selection(&mut self, ty: SelectionType, point: Point, side: Side) {
let point = self.terminal.visible_to_buffer(point);
self.terminal.selection = Some(Selection::new(ty, point, side));
- self.terminal.dirty = true;
+ self.dirty = true;
}
fn toggle_selection(&mut self, ty: SelectionType, point: Point, side: Side) {
@@ -259,7 +278,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
},
Some(selection) if !selection.is_empty() => {
selection.ty = ty;
- self.terminal.dirty = true;
+ self.dirty = true;
},
_ => self.start_selection(ty, point, side),
}
@@ -269,8 +288,8 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
let x = self.mouse.x as usize;
let y = self.mouse.y as usize;
- if self.size_info.contains_point(x, y) {
- Some(self.size_info.pixels_to_coords(x, y))
+ if self.display.size_info.contains_point(x, y) {
+ Some(self.display.size_info.pixels_to_coords(x, y))
} else {
None
}
@@ -309,12 +328,12 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
#[inline]
fn window(&self) -> &Window {
- self.window
+ &self.display.window
}
#[inline]
fn window_mut(&mut self) -> &mut Window {
- self.window
+ &mut self.display.window
}
#[inline]
@@ -387,17 +406,21 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
}
}
+ fn highlighted_url(&self) -> Option<&Url> {
+ self.display.highlighted_url.as_ref()
+ }
+
fn change_font_size(&mut self, delta: f32) {
*self.font_size = max(*self.font_size + delta, Size::new(FONT_SIZE_STEP));
let font = self.config.ui_config.font.clone().with_size(*self.font_size);
self.display_update_pending.set_font(font);
- self.terminal.dirty = true;
+ self.dirty = true;
}
fn reset_font_size(&mut self) {
*self.font_size = self.config.ui_config.font.size();
self.display_update_pending.set_font(self.config.ui_config.font.clone());
- self.terminal.dirty = true;
+ self.dirty = true;
}
#[inline]
@@ -405,6 +428,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
if !self.message_buffer.is_empty() {
self.display_update_pending.dirty = true;
self.message_buffer.pop();
+ self.dirty = true;
}
}
@@ -437,7 +461,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
}
self.display_update_pending.dirty = true;
- self.terminal.dirty = true;
+ self.dirty = true;
}
#[inline]
@@ -458,8 +482,6 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
#[inline]
fn cancel_search(&mut self) {
- self.terminal.cancel_search();
-
if self.terminal.mode().contains(TermMode::VI) {
// Recover pre-search state in vi mode.
self.search_reset_state();
@@ -471,6 +493,8 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
self.update_selection(end, Side::Right);
}
+ self.search_state.dfas = None;
+
self.exit_search();
}
@@ -591,6 +615,29 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
self.search_state.origin = origin_relative;
}
+ /// Find the next search match.
+ fn search_next(
+ &mut self,
+ origin: Point<usize>,
+ direction: Direction,
+ side: Side,
+ ) -> Option<Match> {
+ self.search_state
+ .dfas
+ .as_ref()
+ .and_then(|dfas| self.terminal.search_next(dfas, origin, direction, side, None))
+ }
+
+ #[inline]
+ fn search_direction(&self) -> Direction {
+ self.search_state.direction
+ }
+
+ #[inline]
+ fn search_active(&self) -> bool {
+ self.search_state.history_index.is_some()
+ }
+
/// Handle keyboard typing start.
///
/// This will temporarily disable some features like terminal cursor blinking or the mouse
@@ -603,24 +650,27 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
let blink_interval = self.config.cursor.blink_interval();
if let Some(timer) = self.scheduler.get_mut(TimerId::BlinkCursor) {
timer.deadline = Instant::now() + Duration::from_millis(blink_interval);
- *self.cursor_hidden = false;
- self.terminal.dirty = true;
+ self.display.cursor_hidden = false;
+ self.dirty = true;
}
// Hide mouse cursor.
if self.config.ui_config.mouse.hide_when_typing {
- self.window.set_mouse_visible(false);
+ self.display.window.set_mouse_visible(false);
}
}
+ /// Toggle the vi mode status.
#[inline]
- fn search_direction(&self) -> Direction {
- self.search_state.direction
- }
+ fn toggle_vi_mode(&mut self) {
+ if !self.terminal.mode().contains(TermMode::VI) {
+ self.clear_selection();
+ }
- #[inline]
- fn search_active(&self) -> bool {
- self.search_state.history_index.is_some()
+ self.cancel_search();
+ self.terminal.toggle_vi_mode();
+
+ self.dirty = true;
}
fn message(&self) -> Option<&Message> {
@@ -636,7 +686,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
}
fn urls(&self) -> &Urls {
- self.urls
+ &self.display.urls
}
fn clipboard_mut(&mut self) -> &mut Clipboard {
@@ -657,22 +707,22 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> {
// Hide cursor while typing into the search bar.
if self.config.ui_config.mouse.hide_when_typing {
- self.window.set_mouse_visible(false);
+ self.display.window.set_mouse_visible(false);
}
if regex.is_empty() {
// Stop search if there's nothing to search for.
self.search_reset_state();
- self.terminal.cancel_search();
+ self.search_state.dfas = None;
} else {
- // Create terminal search from the new regex string.
- self.terminal.start_search(&regex);
+ // Create search dfas for the new regex string.
+ self.search_state.dfas = RegexSearch::new(&regex).ok();
// Update search highlighting.
self.goto_match(MAX_SEARCH_WHILE_TYPING);
}
- self.terminal.dirty = true;
+ self.dirty = true;
}
/// Reset terminal to the state before search was started.
@@ -697,22 +747,26 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> {
// Reset vi mode cursor.
let mut origin = self.search_state.origin;
origin.line = min(origin.line, self.terminal.screen_lines() - 1);
- origin.col = min(origin.col, self.terminal.cols() - 1);
+ origin.column = min(origin.column, self.terminal.cols() - 1);
self.terminal.vi_mode_cursor.point = origin;
+
+ self.dirty = true;
}
/// Jump to the first regex match from the search origin.
fn goto_match(&mut self, mut limit: Option<usize>) {
- if self.search_state.history_index.is_none() {
- return;
- }
+ let dfas = match &self.search_state.dfas {
+ Some(dfas) => dfas,
+ None => return,
+ };
// Limit search only when enough lines are available to run into the limit.
limit = limit.filter(|&limit| limit <= self.terminal.total_lines());
// Jump to the next match.
let direction = self.search_state.direction;
- match self.terminal.search_next(self.absolute_origin(), direction, Side::Left, limit) {
+ let origin = self.absolute_origin();
+ match self.terminal.search_next(dfas, origin, direction, Side::Left, limit) {
Some(regex_match) => {
let old_offset = self.terminal.grid().display_offset() as isize;
@@ -752,7 +806,7 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> {
},
}
- self.terminal.dirty = true;
+ self.dirty = true;
}
/// Cleanup the search state.
@@ -767,7 +821,7 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> {
self.display_update_pending.dirty = true;
self.search_state.history_index = None;
- self.terminal.dirty = true;
+ self.dirty = true;
// Clear focused match.
self.search_state.focused_match = None;
@@ -781,7 +835,7 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> {
fn absolute_origin(&self) -> Point<usize> {
let mut relative_origin = self.search_state.origin;
relative_origin.line = min(relative_origin.line, self.terminal.screen_lines() - 1);
- relative_origin.col = min(relative_origin.col, self.terminal.cols() - 1);
+ relative_origin.column = min(relative_origin.column, self.terminal.cols() - 1);
let mut origin = self.terminal.visible_to_buffer(relative_origin);
origin.line = (origin.line as isize + self.search_state.display_offset_delta) as usize;
origin
@@ -809,8 +863,8 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> {
TimerId::BlinkCursor,
)
} else {
- *self.cursor_hidden = false;
- self.terminal.dirty = true;
+ self.display.cursor_hidden = false;
+ self.dirty = true;
}
}
}
@@ -1012,27 +1066,26 @@ impl<N: Notify + OnResize> Processor<N> {
notifier: &mut self.notifier,
mouse: &mut self.mouse,
clipboard: &mut self.clipboard,
- size_info: &mut self.display.size_info,
received_count: &mut self.received_count,
suppress_chars: &mut self.suppress_chars,
modifiers: &mut self.modifiers,
message_buffer: &mut self.message_buffer,
display_update_pending: &mut display_update_pending,
- window: &mut self.display.window,
+ display: &mut self.display,
font_size: &mut self.font_size,
config: &mut self.config,
- urls: &self.display.urls,
scheduler: &mut scheduler,
search_state: &mut self.search_state,
cli_options: &self.cli_options,
- cursor_hidden: &mut self.display.cursor_hidden,
+ dirty: false,
event_loop,
};
- let mut processor = input::Processor::new(context, &self.display.highlighted_url);
+ let mut processor = input::Processor::new(context);
for event in self.event_queue.drain(..) {
Processor::handle_event(event, &mut processor);
}
+ let dirty = processor.ctx.dirty;
// Process DisplayUpdate events.
if display_update_pending.dirty {
@@ -1045,11 +1098,9 @@ impl<N: Notify + OnResize> Processor<N> {
return;
}
- if terminal.dirty {
- terminal.dirty = false;
-
+ if dirty {
// Request immediate re-draw if visual bell animation is not finished yet.
- if !terminal.visual_bell.completed() {
+ if !self.display.visual_bell.completed() {
let event: Event = TerminalEvent::Wakeup.into();
self.event_queue.push(event.into());
@@ -1079,7 +1130,7 @@ impl<N: Notify + OnResize> Processor<N> {
/// Doesn't take self mutably due to borrow checking.
fn handle_event<T>(
event: GlutinEvent<'_, Event>,
- processor: &mut input::Processor<'_, T, ActionContext<'_, N, T>>,
+ processor: &mut input::Processor<T, ActionContext<'_, N, T>>,
) where
T: EventListener,
{
@@ -1095,40 +1146,48 @@ impl<N: Notify + OnResize> Processor<N> {
// Resize to event's dimensions, since no resize event is emitted on Wayland.
display_update_pending.set_dimensions(PhysicalSize::new(width, height));
- processor.ctx.window.dpr = scale_factor;
- processor.ctx.terminal.dirty = true;
+ processor.ctx.window_mut().dpr = scale_factor;
+ processor.ctx.dirty = true;
},
Event::Message(message) => {
processor.ctx.message_buffer.push(message);
processor.ctx.display_update_pending.dirty = true;
- processor.ctx.terminal.dirty = true;
+ processor.ctx.dirty = true;
},
Event::SearchNext => processor.ctx.goto_match(None),
Event::ConfigReload(path) => Self::reload_config(&path, processor),
Event::Scroll(scroll) => processor.ctx.scroll(scroll),
Event::BlinkCursor => {
- *processor.ctx.cursor_hidden ^= true;
- processor.ctx.terminal.dirty = true;
+ processor.ctx.display.cursor_hidden ^= true;
+ processor.ctx.dirty = true;
},
Event::TerminalEvent(event) => match event {
TerminalEvent::Title(title) => {
let ui_config = &processor.ctx.config.ui_config;
if ui_config.window.dynamic_title {
- processor.ctx.window.set_title(&title);
+ processor.ctx.window_mut().set_title(&title);
}
},
TerminalEvent::ResetTitle => {
let ui_config = &processor.ctx.config.ui_config;
if ui_config.window.dynamic_title {
- processor.ctx.window.set_title(&ui_config.window.title);
+ processor.ctx.display.window.set_title(&ui_config.window.title);
}
},
- TerminalEvent::Wakeup => processor.ctx.terminal.dirty = true,
+ TerminalEvent::Wakeup => processor.ctx.dirty = true,
TerminalEvent::Bell => {
- let bell_command = processor.ctx.config.bell().command.as_ref();
- let _ = bell_command.map(|cmd| start_daemon(cmd.program(), cmd.args()));
+ // Set window urgency.
if processor.ctx.terminal.mode().contains(TermMode::URGENCY_HINTS) {
- processor.ctx.window.set_urgent(!processor.ctx.terminal.is_focused);
+ let focused = processor.ctx.terminal.is_focused;
+ processor.ctx.window_mut().set_urgent(!focused);
+ }
+
+ // Ring visual bell.
+ processor.ctx.display.visual_bell.ring();
+
+ // Execute bell command.
+ if let Some(bell_command) = &processor.ctx.config.ui_config.bell.command {
+ start_daemon(bell_command.program(), bell_command.args());
}
},
TerminalEvent::ClipboardStore(clipboard_type, content) => {
@@ -1138,6 +1197,10 @@ impl<N: Notify + OnResize> Processor<N> {
let text = format(processor.ctx.clipboard.load(clipboard_type).as_str());
processor.ctx.write_to_pty(text.into_bytes());
},
+ TerminalEvent::ColorRequest(index, format) => {
+ let text = format(processor.ctx.display.colors[index]);
+ processor.ctx.write_to_pty(text.into_bytes());
+ },
TerminalEvent::MouseCursorDirty => processor.reset_mouse_cursor(),
TerminalEvent::Exit => (),
TerminalEvent::CursorBlinkingChange(_) => {
@@ -1145,7 +1208,7 @@ impl<N: Notify + OnResize> Processor<N> {
},
},
},
- GlutinEvent::RedrawRequested(_) => processor.ctx.terminal.dirty = true,
+ GlutinEvent::RedrawRequested(_) => processor.ctx.dirty = true,
GlutinEvent::WindowEvent { event, window_id, .. } => {
match event {
WindowEvent::CloseRequested => processor.ctx.terminal.exit(),
@@ -1159,37 +1222,37 @@ impl<N: Notify + OnResize> Processor<N> {
}
processor.ctx.display_update_pending.set_dimensions(size);
- processor.ctx.terminal.dirty = true;
+ processor.ctx.dirty = true;
},
WindowEvent::KeyboardInput { input, is_synthetic: false, .. } => {
processor.key_input(input);
},
+ WindowEvent::ModifiersChanged(modifiers) => {
+ processor.modifiers_input(modifiers)
+ },
WindowEvent::ReceivedCharacter(c) => processor.received_char(c),
WindowEvent::MouseInput { state, button, .. } => {
- processor.ctx.window.set_mouse_visible(true);
+ processor.ctx.window_mut().set_mouse_visible(true);
processor.mouse_input(state, button);
- processor.ctx.terminal.dirty = true;
- },
- WindowEvent::ModifiersChanged(modifiers) => {
- processor.modifiers_input(modifiers)
+ processor.ctx.dirty = true;
},
WindowEvent::CursorMoved { position, .. } => {
- processor.ctx.window.set_mouse_visible(true);
+ processor.ctx.window_mut().set_mouse_visible(true);
processor.mouse_moved(position);
},
WindowEvent::MouseWheel { delta, phase, .. } => {
- processor.ctx.window.set_mouse_visible(true);
+ processor.ctx.window_mut().set_mouse_visible(true);
processor.mouse_wheel_input(delta, phase);
},
WindowEvent::Focused(is_focused) => {
- if window_id == processor.ctx.window.window_id() {
+ if window_id == processor.ctx.window().window_id() {
processor.ctx.terminal.is_focused = is_focused;
- processor.ctx.terminal.dirty = true;
+ processor.ctx.dirty = true;
if is_focused {
- processor.ctx.window.set_urgent(false);
+ processor.ctx.window_mut().set_urgent(false);
} else {
- processor.ctx.window.set_mouse_visible(true);
+ processor.ctx.window_mut().set_mouse_visible(true);
}
processor.ctx.update_cursor_blinking();
@@ -1203,8 +1266,8 @@ impl<N: Notify + OnResize> Processor<N> {
WindowEvent::CursorLeft { .. } => {
processor.ctx.mouse.inside_text_area = false;
- if processor.highlighted_url.is_some() {
- processor.ctx.terminal.dirty = true;
+ if processor.ctx.highlighted_url().is_some() {
+ processor.ctx.dirty = true;
}
},
WindowEvent::KeyboardInput { is_synthetic: true, .. }
@@ -1253,10 +1316,9 @@ impl<N: Notify + OnResize> Processor<N> {
}
}
- fn reload_config<T>(
- path: &PathBuf,
- processor: &mut input::Processor<'_, T, ActionContext<'_, N, T>>,
- ) where
+ /// Reload the configuration files from disk.
+ fn reload_config<T>(path: &Path, processor: &mut input::Processor<T, ActionContext<'_, N, T>>)
+ where
T: EventListener,
{
if !processor.ctx.message_buffer.is_empty() {
@@ -1269,6 +1331,7 @@ impl<N: Notify + OnResize> Processor<N> {
Err(_) => return,
};
+ processor.ctx.display.update_config(&config);
processor.ctx.terminal.update_config(&config);
// Reload cursor if its thickness has changed.
@@ -1300,12 +1363,12 @@ impl<N: Notify + OnResize> Processor<N> {
if !config.ui_config.window.dynamic_title
|| processor.ctx.config.ui_config.window.title != config.ui_config.window.title
{
- processor.ctx.window.set_title(&config.ui_config.window.title);
+ processor.ctx.window_mut().set_title(&config.ui_config.window.title);
}
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
if processor.ctx.event_loop.is_wayland() {
- processor.ctx.window.set_wayland_theme(&config.colors);
+ processor.ctx.window_mut().set_wayland_theme(&config.ui_config.colors);
}
// Set subpixel anti-aliasing.
@@ -1314,14 +1377,14 @@ impl<N: Notify + OnResize> Processor<N> {
// Disable shadows for transparent windows on macOS.
#[cfg(target_os = "macos")]
- processor.ctx.window.set_has_shadow(config.ui_config.background_opacity() >= 1.0);
+ processor.ctx.window_mut().set_has_shadow(config.ui_config.background_opacity() >= 1.0);
*processor.ctx.config = config;
// Update cursor blinking.
processor.ctx.update_cursor_blinking();
- processor.ctx.terminal.dirty = true;
+ processor.ctx.dirty = true;
}
/// Submit the pending changes to the `Display`.
diff --git a/alacritty/src/input.rs b/alacritty/src/input.rs
index 155fab07..778dffc7 100644
--- a/alacritty/src/input.rs
+++ b/alacritty/src/input.rs
@@ -26,17 +26,18 @@ use alacritty_terminal::event::EventListener;
use alacritty_terminal::grid::{Dimensions, Scroll};
use alacritty_terminal::index::{Boundary, Column, Direction, Line, Point, Side};
use alacritty_terminal::selection::SelectionType;
+use alacritty_terminal::term::search::Match;
use alacritty_terminal::term::{ClipboardType, SizeInfo, Term, TermMode};
use alacritty_terminal::vi_mode::ViMotion;
use crate::clipboard::Clipboard;
use crate::config::{Action, Binding, BindingMode, Config, Key, SearchAction, ViAction};
use crate::daemon::start_daemon;
+use crate::display::window::Window;
use crate::event::{ClickState, Event, Mouse, TYPING_SEARCH_DELAY};
use crate::message_bar::{self, Message};
use crate::scheduler::{Scheduler, TimerId};
use crate::url::{Url, Urls};
-use crate::window::Window;
/// Font size change interval.
pub const FONT_SIZE_STEP: f32 = 0.5;
@@ -54,20 +55,20 @@ const SELECTION_SCROLLING_STEP: f64 = 20.;
///
/// An escape sequence may be emitted in case specific keys or key combinations
/// are activated.
-pub struct Processor<'a, T: EventListener, A: ActionContext<T>> {
+pub struct Processor<T: EventListener, A: ActionContext<T>> {
pub ctx: A,
- pub highlighted_url: &'a Option<Url>,
_phantom: PhantomData<T>,
}
pub trait ActionContext<T: EventListener> {
- fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&mut self, data: B);
+ fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&mut self, _data: B) {}
+ fn mark_dirty(&mut self) {}
fn size_info(&self) -> SizeInfo;
- fn copy_selection(&mut self, ty: ClipboardType);
- fn start_selection(&mut self, ty: SelectionType, point: Point, side: Side);
- fn toggle_selection(&mut self, ty: SelectionType, point: Point, side: Side);
- fn update_selection(&mut self, point: Point, side: Side);
- fn clear_selection(&mut self);
+ fn copy_selection(&mut self, _ty: ClipboardType) {}
+ fn start_selection(&mut self, _ty: SelectionType, _point: Point, _side: Side) {}
+ fn toggle_selection(&mut self, _ty: SelectionType, _point: Point, _side: Side) {}
+ fn update_selection(&mut self, _point: Point, _side: Side) {}
+ fn clear_selection(&mut self) {}
fn selection_is_empty(&self) -> bool;
fn mouse_mut(&mut self) -> &mut Mouse;
fn mouse(&self) -> &Mouse;
@@ -75,34 +76,42 @@ pub trait ActionContext<T: EventListener> {
fn received_count(&mut self) -> &mut usize;
fn suppress_chars(&mut self) -> &mut bool;
fn modifiers(&mut self) -> &mut ModifiersState;
- fn scroll(&mut self, scroll: Scroll);
+ fn scroll(&mut self, _scroll: Scroll) {}
fn window(&self) -> &Window;
fn window_mut(&mut self) -> &mut Window;
fn terminal(&self) -> &Term<T>;
fn terminal_mut(&mut self) -> &mut Term<T>;
- fn spawn_new_instance(&mut self);
- fn change_font_size(&mut self, delta: f32);
- fn reset_font_size(&mut self);
- fn pop_message(&mut self);
+ fn spawn_new_instance(&mut self) {}
+ fn change_font_size(&mut self, _delta: f32) {}
+ fn reset_font_size(&mut self) {}
+ fn pop_message(&mut self) {}
fn message(&self) -> Option<&Message>;
fn config(&self) -> &Config;
fn event_loop(&self) -> &EventLoopWindowTarget<Event>;
fn urls(&self) -> &Urls;
- fn launch_url(&self, url: Url);
+ fn launch_url(&self, _url: Url) {}
+ fn highlighted_url(&self) -> Option<&Url>;
fn mouse_mode(&self) -> bool;
fn clipboard_mut(&mut self) -> &mut Clipboard;
fn scheduler_mut(&mut self) -> &mut Scheduler;
- fn start_search(&mut self, direction: Direction);
- fn confirm_search(&mut self);
- fn cancel_search(&mut self);
- fn search_input(&mut self, c: char);
- fn search_pop_word(&mut self);
- fn search_history_previous(&mut self);
- fn search_history_next(&mut self);
- fn advance_search_origin(&mut self, direction: Direction);
+ fn start_search(&mut self, _direction: Direction) {}
+ fn confirm_search(&mut self) {}
+ fn cancel_search(&mut self) {}
+ fn search_input(&mut self, _c: char) {}
+ fn search_pop_word(&mut self) {}
+ fn search_history_previous(&mut self) {}
+ fn search_history_next(&mut self) {}
+ fn search_next(
+ &mut self,
+ origin: Point<usize>,
+ direction: Direction,
+ side: Side,
+ ) -> Option<Match>;
+ fn advance_search_origin(&mut self, _direction: Direction) {}
fn search_direction(&self) -> Direction;
fn search_active(&self) -> bool;
- fn on_typing_start(&mut self);
+ fn on_typing_start(&mut self) {}
+ fn toggle_vi_mode(&mut self) {}
}
trait Execute<T: EventListener> {
@@ -120,8 +129,8 @@ impl<T, U: EventListener> Execute<U> for Binding<T> {
impl Action {
fn toggle_selection<T, A>(ctx: &mut A, ty: SelectionType)
where
- T: EventListener,
A: ActionContext<T>,
+ T: EventListener,
{
let cursor_point = ctx.terminal().vi_mode_cursor.point;
ctx.toggle_selection(ty, cursor_point, Side::Left);
@@ -151,10 +160,11 @@ impl<T: EventListener> Execute<T> for Action {
start_daemon(program, args);
},
- Action::ToggleViMode => ctx.terminal_mut().toggle_vi_mode(),
+ Action::ToggleViMode => ctx.toggle_vi_mode(),
Action::ViMotion(motion) => {
ctx.on_typing_start();
- ctx.terminal_mut().vi_motion(motion)
+ ctx.terminal_mut().vi_motion(motion);
+ ctx.mark_dirty();
},
Action::ViAction(ViAction::ToggleNormalSelection) => {
Self::toggle_selection(ctx, SelectionType::Simple)
@@ -183,9 +193,9 @@ impl<T: EventListener> Execute<T> for Action {
Direction::Left => vi_point.sub_absolute(terminal, Boundary::Wrap, 1),
};
- let regex_match = terminal.search_next(origin, direction, Side::Left, None);
- if let Some(regex_match) = regex_match {
+ if let Some(regex_match) = ctx.search_next(origin, direction, Side::Left) {
ctx.terminal_mut().vi_goto_point(*regex_match.start());
+ ctx.mark_dirty();
}
},
Action::ViAction(ViAction::SearchPrevious) => {
@@ -197,9 +207,9 @@ impl<T: EventListener> Execute<T> for Action {
Direction::Left => vi_point.sub_absolute(terminal, Boundary::Wrap, 1),
};
- let regex_match = terminal.search_next(origin, direction, Side::Left, None);
- if let Some(regex_match) = regex_match {
+ if let Some(regex_match) = ctx.search_next(origin, direction, Side::Left) {
ctx.terminal_mut().vi_goto_point(*regex_match.start());
+ ctx.mark_dirty();
}
},
Action::ViAction(ViAction::SearchStart) => {
@@ -208,9 +218,9 @@ impl<T: EventListener> Execute<T> for Action {
.visible_to_buffer(terminal.vi_mode_cursor.point)
.sub_absolute(terminal, Boundary::Wrap, 1);
- let regex_match = terminal.search_next(origin, Direction::Left, Side::Left, None);
- if let Some(regex_match) = regex_match {
+ if let Some(regex_match) = ctx.search_next(origin, Direction::Left, Side::Left) {
ctx.terminal_mut().vi_goto_point(*regex_match.start());
+ ctx.mark_dirty();
}
},
Action::ViAction(ViAction::SearchEnd) => {
@@ -219,9 +229,9 @@ impl<T: EventListener> Execute<T> for Action {
.visible_to_buffer(terminal.vi_mode_cursor.point)
.add_absolute(terminal, Boundary::Wrap, 1);
- let regex_match = terminal.search_next(origin, Direction::Right, Side::Right, None);
- if let Some(regex_match) = regex_match {
+ if let Some(regex_match) = ctx.search_next(origin, Direction::Right, Side::Right) {
ctx.terminal_mut().vi_goto_point(*regex_match.end());
+ ctx.mark_dirty();
}
},
Action::SearchAction(SearchAction::SearchFocusNext) => {
@@ -328,6 +338,7 @@ impl<T: EventListener> Execute<T> for Action {
// Move vi mode cursor.
ctx.terminal_mut().vi_mode_cursor.point.line = Line(0);
ctx.terminal_mut().vi_motion(ViMotion::FirstOccupied);
+ ctx.mark_dirty();
},
Action::ScrollToBottom => {
ctx.scroll(Scroll::Bottom);
@@ -339,6 +350,7 @@ impl<T: EventListener> Execute<T> for Action {
// Move to beginning twice, to always jump across linewraps.
term.vi_motion(ViMotion::FirstOccupied);
term.vi_motion(ViMotion::FirstOccupied);
+ ctx.mark_dirty();
},
Action::ClearHistory => ctx.terminal_mut().clear_screen(ClearMode::Saved),
Action::ClearLogNotice => ctx.pop_message(),
@@ -387,9 +399,9 @@ impl From<MouseState> for CursorIcon {
}
}
-impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
- pub fn new(ctx: A, highlighted_url: &'a Option<Url>) -> Self {
- Self { ctx, highlighted_url, _phantom: Default::default() }
+impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
+ pub fn new(ctx: A) -> Self {
+ Self { ctx, _phantom: Default::default() }
}
#[inline]
@@ -415,7 +427,7 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
let cell_side = self.get_mouse_side();
let cell_changed =
- point.line != self.ctx.mouse().line || point.col != self.ctx.mouse().column;
+ point.line != self.ctx.mouse().line || point.column != self.ctx.mouse().column;
// Update mouse state and check for URL change.
let mouse_state = self.mouse_state();
@@ -433,7 +445,7 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
self.ctx.mouse_mut().inside_text_area = inside_text_area;
self.ctx.mouse_mut().cell_side = cell_side;
self.ctx.mouse_mut().line = point.line;
- self.ctx.mouse_mut().column = point.col;
+ self.ctx.mouse_mut().column = point.column;
// Don't launch URLs if mouse has moved.
self.ctx.mouse_mut().block_url_launcher = true;
@@ -757,7 +769,7 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
// Try to restore vi mode cursor position, to keep it above its previous content.
let term = self.ctx.terminal_mut();
term.vi_mode_cursor.point = term.grid().clamp_buffer_to_visible(absolute);
- term.vi_mode_cursor.point.col = absolute.col;
+ term.vi_mode_cursor.point.column = absolute.column;
// Update selection.
if term.mode().contains(TermMode::VI) {
@@ -986,12 +998,13 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
/// Trigger redraw when URL highlight changed.
#[inline]
fn update_url_state(&mut self, mouse_state: &MouseState) {
+ let highlighted_url = self.ctx.highlighted_url();
if let MouseState::Url(url) = mouse_state {
- if Some(url) != self.highlighted_url.as_ref() {
- self.ctx.terminal_mut().dirty = true;
+ if Some(url) != highlighted_url {
+ self.ctx.mark_dirty();
}
- } else if self.highlighted_url.is_some() {
- self.ctx.terminal_mut().dirty = true;
+ } else if highlighted_url.is_some() {
+ self.ctx.mark_dirty();
}
}
@@ -1084,10 +1097,7 @@ mod tests {
const KEY: VirtualKeyCode = VirtualKeyCode::Key0;
struct MockEventProxy;
-
- impl EventListener for MockEventProxy {
- fn send_event(&self, _event: TerminalEvent) {}
- }
+ impl EventListener for MockEventProxy {}
struct ActionContext<'a, T> {
pub terminal: &'a mut Term<T>,
@@ -1103,39 +1113,14 @@ mod tests {
}
impl<'a, T: EventListener> super::ActionContext<T> for ActionContext<'a, T> {
- fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&mut self, _val: B) {}
-
- fn update_selection(&mut self, _point: Point, _side: Side) {}
-
- fn start_selection(&mut self, _ty: SelectionType, _point: Point, _side: Side) {}
-
- fn toggle_selection(&mut self, _ty: SelectionType, _point: Point, _side: Side) {}
-
- fn copy_selection(&mut self, _: ClipboardType) {}
-
- fn clear_selection(&mut self) {}
-
- fn spawn_new_instance(&mut self) {}
-
- fn change_font_size(&mut self, _delta: f32) {}
-
- fn reset_font_size(&mut self) {}
-
- fn start_search(&mut self, _direction: Direction) {}
-
- fn confirm_search(&mut self) {}
-
- fn cancel_search(&mut self) {}
-
- fn search_input(&mut self, _c: char) {}
-
- fn search_pop_word(&mut self) {}
-
- fn search_history_previous(&mut self) {}
-
- fn search_history_next(&mut self) {}
-
- fn advance_search_origin(&mut self, _direction: Direction) {}
+ fn search_next(
+ &mut self,
+ _origin: Point<usize>,
+ _direction: Direction,
+ _side: Side,
+ ) -> Option<Match> {
+ None
+ }
fn search_direction(&self) -> Direction {
Direction::Right
@@ -1234,17 +1219,13 @@ mod tests {
unimplemented!();
}
- fn launch_url(&self, _: Url) {
+ fn highlighted_url(&self) -> Option<&Url> {
unimplemented!();
}
fn scheduler_mut(&mut self) -> &mut Scheduler {
unimplemented!();
}
-
- fn on_typing_start(&mut self) {
- unimplemented!();
- }
}
macro_rules! test_clickstate {
@@ -1293,7 +1274,7 @@ mod tests {
config: &cfg,
};
- let mut processor = Processor::new(context, &None);
+ let mut processor = Processor::new(context);
let event: GlutinEvent::<'_, TerminalEvent> = $input;
if let GlutinEvent::WindowEvent {
diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs
index 1bcf64b9..30eff73d 100644
--- a/alacritty/src/main.rs
+++ b/alacritty/src/main.rs
@@ -32,7 +32,6 @@ use alacritty_terminal::tty;
mod cli;
mod clipboard;
mod config;
-mod cursor;
mod daemon;
mod display;
mod event;
@@ -41,16 +40,11 @@ mod logging;
#[cfg(target_os = "macos")]
mod macos;
mod message_bar;
-mod meter;
#[cfg(windows)]
mod panic;
mod renderer;
mod scheduler;
mod url;
-mod window;
-
-#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
-mod wayland_theme;
mod gl {
#![allow(clippy::all)]
diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs
index 39e53b82..1f203332 100644
--- a/alacritty/src/renderer/mod.rs
+++ b/alacritty/src/renderer/mod.rs
@@ -17,11 +17,11 @@ use unicode_width::UnicodeWidthChar;
use alacritty_terminal::index::Point;
use alacritty_terminal::term::cell::Flags;
use alacritty_terminal::term::color::Rgb;
-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::display::content::RenderableCell;
use crate::gl;
use crate::gl::types::*;
use crate::renderer::rects::{RectRenderer, RenderRect};
@@ -480,8 +480,8 @@ impl Batch {
cell_flags.set(RenderingGlyphFlags::WIDE_CHAR, cell.flags.contains(Flags::WIDE_CHAR));
self.instances.push(InstanceData {
- col: cell.column.0 as u16,
- row: cell.line.0 as u16,
+ col: cell.point.column.0 as u16,
+ row: cell.point.line.0 as u16,
top: glyph.top,
left: glyph.left,
@@ -829,8 +829,7 @@ impl<'a> RenderApi<'a> {
.chars()
.enumerate()
.map(|(i, character)| RenderableCell {
- line: point.line,
- column: point.col + i,
+ point: Point::new(point.line, point.column + i),
character,
zerowidth: None,
flags: Flags::empty(),
diff --git a/alacritty/src/renderer/rects.rs b/alacritty/src/renderer/rects.rs
index cfd17379..80886c95 100644
--- a/alacritty/src/renderer/rects.rs
+++ b/alacritty/src/renderer/rects.rs
@@ -6,9 +6,9 @@ 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::render::RenderableCell;
use alacritty_terminal::term::SizeInfo;
+use crate::display::content::RenderableCell;
use crate::gl;
use crate::gl::types::*;
use crate::renderer;
@@ -105,8 +105,8 @@ impl RenderLine {
mut thickness: f32,
color: Rgb,
) -> RenderRect {
- let start_x = start.col.0 as f32 * size.cell_width();
- let end_x = (end.col.0 + 1) as f32 * size.cell_width();
+ let start_x = start.column.0 as f32 * size.cell_width();
+ let end_x = (end.column.0 + 1) as f32 * size.cell_width();
let width = end_x - start_x;
// Make sure lines are always visible.
@@ -169,16 +169,16 @@ impl RenderLines {
}
// Include wide char spacer if the current cell is a wide char.
- let mut end: Point = cell.into();
+ let mut end = cell.point;
if cell.flags.contains(Flags::WIDE_CHAR) {
- end.col += 1;
+ end.column += 1;
}
// Check if there's an active line.
if let Some(line) = self.inner.get_mut(&flag).and_then(|lines| lines.last_mut()) {
if cell.fg == line.color
- && cell.column == line.end.col + 1
- && cell.line == line.end.line
+ && cell.point.column == line.end.column + 1
+ && cell.point.line == line.end.line
{
// Update the length of the line.
line.end = end;
@@ -187,7 +187,7 @@ impl RenderLines {
}
// Start new line if there currently is none.
- let line = RenderLine { start: cell.into(), end, color: cell.fg };
+ let line = RenderLine { start: cell.point, end, color: cell.fg };
match self.inner.get_mut(&flag) {
Some(lines) => lines.push(line),
None => {
diff --git a/alacritty/src/url.rs b/alacritty/src/url.rs
index f4bf8205..d86b514a 100644
--- a/alacritty/src/url.rs
+++ b/alacritty/src/url.rs
@@ -8,10 +8,10 @@ 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::render::RenderableCell;
use alacritty_terminal::term::SizeInfo;
use crate::config::Config;
+use crate::display::content::RenderableCell;
use crate::event::Mouse;
use crate::renderer::rects::{RenderLine, RenderRect};
@@ -73,12 +73,12 @@ impl Urls {
// Update tracked URLs.
pub fn update(&mut self, num_cols: Column, cell: &RenderableCell) {
- let point: Point = cell.into();
+ let point = cell.point;
let mut end = point;
// Include the following wide char spacer.
if cell.flags.contains(Flags::WIDE_CHAR) {
- end.col += 1;
+ end.column += 1;
}
// Reset URL when empty cells have been skipped.
@@ -119,13 +119,13 @@ impl Urls {
(UrlLocation::Url(_length, end_offset), UrlLocation::Url(..)) => {
self.extend_url(point, end, cell.fg, end_offset);
},
- (UrlLocation::Scheme, _) => self.scheme_buffer.push((cell.into(), cell.fg)),
+ (UrlLocation::Scheme, _) => self.scheme_buffer.push((cell.point, cell.fg)),
(UrlLocation::Reset, _) => self.reset(),
_ => (),
}
// Reset at un-wrapped linebreak.
- if cell.column + 1 == num_cols && !cell.flags.contains(Flags::WRAPLINE) {
+ if cell.point.column + 1 == num_cols && !cell.flags.contains(Flags::WRAPLINE) {
self.reset();
}
}
@@ -202,8 +202,7 @@ mod tests {
.map(|(i, character)| RenderableCell {
character,
zerowidth: None,
- line: Line(0),
- column: Column(i),
+ point: Point::new(Line(0), Column(i)),
fg: Default::default(),
bg: Default::default(),
bg_alpha: 0.,
@@ -227,8 +226,8 @@ mod tests {
}
let url = urls.urls.first().unwrap();
- assert_eq!(url.start().col, Column(5));
- assert_eq!(url.end().col, Column(23));
+ assert_eq!(url.start().column, Column(5));
+ assert_eq!(url.end().column, Column(23));
}
#[test]
@@ -244,14 +243,14 @@ mod tests {
assert_eq!(urls.urls.len(), 3);
- assert_eq!(urls.urls[0].start().col, Column(5));
- assert_eq!(urls.urls[0].end().col, Column(9));
+ assert_eq!(urls.urls[0].start().column, Column(5));
+ assert_eq!(urls.urls[0].end().column, Column(9));
- assert_eq!(urls.urls[1].start().col, Column(11));
- assert_eq!(urls.urls[1].end().col, Column(15));
+ assert_eq!(urls.urls[1].start().column, Column(11));
+ assert_eq!(urls.urls[1].end().column, Column(15));
- assert_eq!(urls.urls[2].start().col, Column(17));
- assert_eq!(urls.urls[2].end().col, Column(21));
+ assert_eq!(urls.urls[2].start().column, Column(17));
+ assert_eq!(urls.urls[2].end().column, Column(21));
}
#[test]
@@ -267,10 +266,10 @@ mod tests {
assert_eq!(urls.urls.len(), 2);
- assert_eq!(urls.urls[0].start().col, Column(5));
- assert_eq!(urls.urls[0].end().col, Column(17));
+ assert_eq!(urls.urls[0].start().column, Column(5));
+ assert_eq!(urls.urls[0].end().column, Column(17));
- assert_eq!(urls.urls[1].start().col, Column(20));
- assert_eq!(urls.urls[1].end().col, Column(28));
+ assert_eq!(urls.urls[1].start().column, Column(20));
+ assert_eq!(urls.urls[1].end().column, Column(28));
}
}