1
0
Fork 0
mirror of https://github.com/alacritty/alacritty.git synced 2024-11-25 14:05:41 -05:00

Switch to VTE's built-in ansi feature

Co-authored-by: Christian Duerr <contact@christianduerr.com>
This commit is contained in:
Anhad Singh 2023-05-24 06:35:58 +10:00 committed by GitHub
parent f0379f2da7
commit cb7ad5b7e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 211 additions and 2041 deletions

14
Cargo.lock generated
View file

@ -1487,18 +1487,18 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.57" version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4ec6d5fe0b140acb27c9a0444118cf55bfbb4e0b259739429abb4521dd67c16" checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.27" version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -1945,10 +1945,12 @@ dependencies = [
[[package]] [[package]]
name = "vte" name = "vte"
version = "0.10.1" version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197"
dependencies = [ dependencies = [
"log",
"serde",
"utf8parse", "utf8parse",
"vte_generate_state_changes", "vte_generate_state_changes",
] ]

View file

@ -23,7 +23,7 @@ pub struct BellConfig {
impl Default for BellConfig { impl Default for BellConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
color: Rgb { r: 255, g: 255, b: 255 }, color: Rgb::new(255, 255, 255),
animation: Default::default(), animation: Default::default(),
command: Default::default(), command: Default::default(),
duration: Default::default(), duration: Default::default(),
@ -39,7 +39,7 @@ impl BellConfig {
/// `VisualBellAnimations` are modeled after a subset of CSS transitions and Robert /// `VisualBellAnimations` are modeled after a subset of CSS transitions and Robert
/// Penner's Easing Functions. /// Penner's Easing Functions.
#[derive(ConfigDeserialize, Clone, Copy, Debug, PartialEq, Eq)] #[derive(ConfigDeserialize, Default, Clone, Copy, Debug, PartialEq, Eq)]
pub enum BellAnimation { pub enum BellAnimation {
// CSS animation. // CSS animation.
Ease, Ease,
@ -56,15 +56,10 @@ pub enum BellAnimation {
// Penner animation. // Penner animation.
EaseOutQuint, EaseOutQuint,
// Penner animation. // Penner animation.
#[default]
EaseOutExpo, EaseOutExpo,
// Penner animation. // Penner animation.
EaseOutCirc, EaseOutCirc,
// Penner animation. // Penner animation.
Linear, Linear,
} }
impl Default for BellAnimation {
fn default() -> Self {
BellAnimation::EaseOutExpo
}
}

View file

@ -52,8 +52,8 @@ pub struct HintStartColors {
impl Default for HintStartColors { impl Default for HintStartColors {
fn default() -> Self { fn default() -> Self {
Self { Self {
foreground: CellRgb::Rgb(Rgb { r: 0x1d, g: 0x1f, b: 0x21 }), foreground: CellRgb::Rgb(Rgb::new(0x1d, 0x1f, 0x21)),
background: CellRgb::Rgb(Rgb { r: 0xe9, g: 0xff, b: 0x5e }), background: CellRgb::Rgb(Rgb::new(0xe9, 0xff, 0x5e)),
} }
} }
} }
@ -67,8 +67,8 @@ pub struct HintEndColors {
impl Default for HintEndColors { impl Default for HintEndColors {
fn default() -> Self { fn default() -> Self {
Self { Self {
foreground: CellRgb::Rgb(Rgb { r: 0xe9, g: 0xff, b: 0x5e }), foreground: CellRgb::Rgb(Rgb::new(0xe9, 0xff, 0x5e)),
background: CellRgb::Rgb(Rgb { r: 0x1d, g: 0x1f, b: 0x21 }), background: CellRgb::Rgb(Rgb::new(0x1d, 0x1f, 0x21)),
} }
} }
} }
@ -139,8 +139,8 @@ pub struct FocusedMatchColors {
impl Default for FocusedMatchColors { impl Default for FocusedMatchColors {
fn default() -> Self { fn default() -> Self {
Self { Self {
background: CellRgb::Rgb(Rgb { r: 0x00, g: 0x00, b: 0x00 }), background: CellRgb::Rgb(Rgb::new(0x00, 0x00, 0x00)),
foreground: CellRgb::Rgb(Rgb { r: 0xff, g: 0xff, b: 0xff }), foreground: CellRgb::Rgb(Rgb::new(0xff, 0xff, 0xff)),
} }
} }
} }
@ -154,8 +154,8 @@ pub struct MatchColors {
impl Default for MatchColors { impl Default for MatchColors {
fn default() -> Self { fn default() -> Self {
Self { Self {
background: CellRgb::Rgb(Rgb { r: 0xff, g: 0xff, b: 0xff }), background: CellRgb::Rgb(Rgb::new(0xff, 0xff, 0xff)),
foreground: CellRgb::Rgb(Rgb { r: 0x00, g: 0x00, b: 0x00 }), foreground: CellRgb::Rgb(Rgb::new(0x00, 0x00, 0x00)),
} }
} }
} }
@ -177,8 +177,8 @@ pub struct PrimaryColors {
impl Default for PrimaryColors { impl Default for PrimaryColors {
fn default() -> Self { fn default() -> Self {
PrimaryColors { PrimaryColors {
background: Rgb { r: 0x1d, g: 0x1f, b: 0x21 }, background: Rgb::new(0x1d, 0x1f, 0x21),
foreground: Rgb { r: 0xc5, g: 0xc8, b: 0xc6 }, foreground: Rgb::new(0xc5, 0xc8, 0xc6),
bright_foreground: Default::default(), bright_foreground: Default::default(),
dim_foreground: Default::default(), dim_foreground: Default::default(),
} }
@ -200,14 +200,14 @@ pub struct NormalColors {
impl Default for NormalColors { impl Default for NormalColors {
fn default() -> Self { fn default() -> Self {
NormalColors { NormalColors {
black: Rgb { r: 0x1d, g: 0x1f, b: 0x21 }, black: Rgb::new(0x1d, 0x1f, 0x21),
red: Rgb { r: 0xcc, g: 0x66, b: 0x66 }, red: Rgb::new(0xcc, 0x66, 0x66),
green: Rgb { r: 0xb5, g: 0xbd, b: 0x68 }, green: Rgb::new(0xb5, 0xbd, 0x68),
yellow: Rgb { r: 0xf0, g: 0xc6, b: 0x74 }, yellow: Rgb::new(0xf0, 0xc6, 0x74),
blue: Rgb { r: 0x81, g: 0xa2, b: 0xbe }, blue: Rgb::new(0x81, 0xa2, 0xbe),
magenta: Rgb { r: 0xb2, g: 0x94, b: 0xbb }, magenta: Rgb::new(0xb2, 0x94, 0xbb),
cyan: Rgb { r: 0x8a, g: 0xbe, b: 0xb7 }, cyan: Rgb::new(0x8a, 0xbe, 0xb7),
white: Rgb { r: 0xc5, g: 0xc8, b: 0xc6 }, white: Rgb::new(0xc5, 0xc8, 0xc6),
} }
} }
} }
@ -227,14 +227,14 @@ pub struct BrightColors {
impl Default for BrightColors { impl Default for BrightColors {
fn default() -> Self { fn default() -> Self {
BrightColors { BrightColors {
black: Rgb { r: 0x66, g: 0x66, b: 0x66 }, black: Rgb::new(0x66, 0x66, 0x66),
red: Rgb { r: 0xd5, g: 0x4e, b: 0x53 }, red: Rgb::new(0xd5, 0x4e, 0x53),
green: Rgb { r: 0xb9, g: 0xca, b: 0x4a }, green: Rgb::new(0xb9, 0xca, 0x4a),
yellow: Rgb { r: 0xe7, g: 0xc5, b: 0x47 }, yellow: Rgb::new(0xe7, 0xc5, 0x47),
blue: Rgb { r: 0x7a, g: 0xa6, b: 0xda }, blue: Rgb::new(0x7a, 0xa6, 0xda),
magenta: Rgb { r: 0xc3, g: 0x97, b: 0xd8 }, magenta: Rgb::new(0xc3, 0x97, 0xd8),
cyan: Rgb { r: 0x70, g: 0xc0, b: 0xb1 }, cyan: Rgb::new(0x70, 0xc0, 0xb1),
white: Rgb { r: 0xea, g: 0xea, b: 0xea }, white: Rgb::new(0xea, 0xea, 0xea),
} }
} }
} }
@ -254,14 +254,14 @@ pub struct DimColors {
impl Default for DimColors { impl Default for DimColors {
fn default() -> Self { fn default() -> Self {
DimColors { DimColors {
black: Rgb { r: 0x13, g: 0x14, b: 0x15 }, black: Rgb::new(0x13, 0x14, 0x15),
red: Rgb { r: 0x86, g: 0x43, b: 0x43 }, red: Rgb::new(0x86, 0x43, 0x43),
green: Rgb { r: 0x77, g: 0x7c, b: 0x44 }, green: Rgb::new(0x77, 0x7c, 0x44),
yellow: Rgb { r: 0x9e, g: 0x82, b: 0x4c }, yellow: Rgb::new(0x9e, 0x82, 0x4c),
blue: Rgb { r: 0x55, g: 0x6a, b: 0x7d }, blue: Rgb::new(0x55, 0x6a, 0x7d),
magenta: Rgb { r: 0x75, g: 0x61, b: 0x7b }, magenta: Rgb::new(0x75, 0x61, 0x7b),
cyan: Rgb { r: 0x5b, g: 0x7d, b: 0x78 }, cyan: Rgb::new(0x5b, 0x7d, 0x78),
white: Rgb { r: 0x82, g: 0x84, b: 0x82 }, white: Rgb::new(0x82, 0x84, 0x82),
} }
} }
} }

View file

@ -154,8 +154,9 @@ impl Default for Identity {
} }
} }
#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)] #[derive(ConfigDeserialize, Default, Debug, Copy, Clone, PartialEq, Eq)]
pub enum StartupMode { pub enum StartupMode {
#[default]
Windowed, Windowed,
Maximized, Maximized,
Fullscreen, Fullscreen,
@ -163,14 +164,9 @@ pub enum StartupMode {
SimpleFullscreen, SimpleFullscreen,
} }
impl Default for StartupMode { #[derive(ConfigDeserialize, Default, Debug, Copy, Clone, PartialEq, Eq)]
fn default() -> StartupMode {
StartupMode::Windowed
}
}
#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]
pub enum Decorations { pub enum Decorations {
#[default]
Full, Full,
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
Transparent, Transparent,
@ -179,12 +175,6 @@ pub enum Decorations {
None, None,
} }
impl Default for Decorations {
fn default() -> Decorations {
Decorations::Full
}
}
/// Window Dimensions. /// Window Dimensions.
/// ///
/// Newtype to avoid passing values incorrectly. /// Newtype to avoid passing values incorrectly.

View file

@ -95,11 +95,11 @@ impl List {
{ {
self[index] = indexed_color.color; self[index] = indexed_color.color;
} else { } else {
self[index] = Rgb { self[index] = Rgb::new(
r: if r == 0 { 0 } else { r * 40 + 55 }, if r == 0 { 0 } else { r * 40 + 55 },
b: if b == 0 { 0 } else { b * 40 + 55 }, if b == 0 { 0 } else { b * 40 + 55 },
g: if g == 0 { 0 } else { g * 40 + 55 }, if g == 0 { 0 } else { g * 40 + 55 },
}; );
} }
index += 1; index += 1;
} }
@ -126,7 +126,7 @@ impl List {
} }
let value = i * 10 + 8; let value = i * 10 + 8;
self[index] = Rgb { r: value, g: value, b: value }; self[index] = Rgb::new(value, value, value);
index += 1; index += 1;
} }

View file

@ -121,7 +121,7 @@ impl<'a> RenderableContent<'a> {
let insufficient_contrast = (!matches!(cursor_color, CellRgb::Rgb(_)) let insufficient_contrast = (!matches!(cursor_color, CellRgb::Rgb(_))
|| !matches!(text_color, CellRgb::Rgb(_))) || !matches!(text_color, CellRgb::Rgb(_)))
&& cell.fg.contrast(cell.bg) < MIN_CURSOR_CONTRAST; && cell.fg.contrast(*cell.bg) < MIN_CURSOR_CONTRAST;
// Convert from cell colors to RGB. // Convert from cell colors to RGB.
let mut text_color = text_color.color(cell.fg, cell.bg); let mut text_color = text_color.color(cell.fg, cell.bg);
@ -307,8 +307,11 @@ impl RenderableCell {
let config = &content.config; let config = &content.config;
match fg { match fg {
Color::Spec(rgb) => match flags & Flags::DIM { Color::Spec(rgb) => match flags & Flags::DIM {
Flags::DIM => rgb * DIM_FACTOR, Flags::DIM => {
_ => rgb, let rgb: Rgb = rgb.into();
rgb * DIM_FACTOR
},
_ => rgb.into(),
}, },
Color::Named(ansi) => { Color::Named(ansi) => {
match (config.draw_bold_text_with_bright_colors, flags & Flags::DIM_BOLD) { match (config.draw_bold_text_with_bright_colors, flags & Flags::DIM_BOLD) {
@ -350,7 +353,7 @@ impl RenderableCell {
#[inline] #[inline]
fn compute_bg_rgb(content: &mut RenderableContent<'_>, bg: Color) -> Rgb { fn compute_bg_rgb(content: &mut RenderableContent<'_>, bg: Color) -> Rgb {
match bg { match bg {
Color::Spec(rgb) => rgb, Color::Spec(rgb) => rgb.into(),
Color::Named(ansi) => content.color(ansi as usize), Color::Named(ansi) => content.color(ansi as usize),
Color::Indexed(idx) => content.color(idx as usize), Color::Indexed(idx) => content.color(idx as usize),
} }

View file

@ -647,25 +647,25 @@ mod tests {
#[test] #[test]
fn collect_unique_hyperlinks() { fn collect_unique_hyperlinks() {
let mut term = mock_term("000\r\n111"); let mut term = mock_term("000\r\n111");
term.goto(Line(0), Column(0)); term.goto(0, 0);
let hyperlink_foo = Hyperlink::new(Some("1"), String::from("foo")); let hyperlink_foo = Hyperlink::new(Some("1"), String::from("foo"));
let hyperlink_bar = Hyperlink::new(Some("2"), String::from("bar")); let hyperlink_bar = Hyperlink::new(Some("2"), String::from("bar"));
// Create 2 hyperlinks on the first line. // Create 2 hyperlinks on the first line.
term.set_hyperlink(Some(hyperlink_foo.clone())); term.set_hyperlink(Some(hyperlink_foo.clone().into()));
term.input('b'); term.input('b');
term.input('a'); term.input('a');
term.set_hyperlink(Some(hyperlink_bar.clone())); term.set_hyperlink(Some(hyperlink_bar.clone().into()));
term.input('r'); term.input('r');
term.set_hyperlink(Some(hyperlink_foo.clone())); term.set_hyperlink(Some(hyperlink_foo.clone().into()));
term.goto(Line(1), Column(0)); term.goto(1, 0);
// Ditto for the second line. // Ditto for the second line.
term.set_hyperlink(Some(hyperlink_foo)); term.set_hyperlink(Some(hyperlink_foo.into()));
term.input('b'); term.input('b');
term.input('a'); term.input('a');
term.set_hyperlink(Some(hyperlink_bar)); term.set_hyperlink(Some(hyperlink_bar.into()));
term.input('r'); term.input('r');
term.set_hyperlink(None); term.set_hyperlink(None);

View file

@ -73,7 +73,7 @@ const BACKWARD_SEARCH_LABEL: &str = "Backward Search: ";
const SHORTENER: char = '…'; const SHORTENER: char = '…';
/// Color which is used to highlight damaged rects when debugging. /// Color which is used to highlight damaged rects when debugging.
const DAMAGE_RECT_COLOR: Rgb = Rgb { r: 255, g: 0, b: 255 }; const DAMAGE_RECT_COLOR: Rgb = Rgb::new(255, 0, 255);
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {

View file

@ -366,7 +366,7 @@ impl RectRenderer {
let y = -rect.y / half_height + 1.0; let y = -rect.y / half_height + 1.0;
let width = rect.width / half_width; let width = rect.width / half_width;
let height = rect.height / half_height; let height = rect.height / half_height;
let Rgb { r, g, b } = rect.color; let (r, g, b) = rect.color.as_tuple();
let a = (rect.alpha * 255.) as u8; let a = (rect.alpha * 255.) as u8;
// Make quad vertices. // Make quad vertices.

View file

@ -30,7 +30,7 @@ regex-automata = "0.1.9"
serde = { version = "1", features = ["derive", "rc"] } serde = { version = "1", features = ["derive", "rc"] }
serde_yaml = "0.8" serde_yaml = "0.8"
unicode-width = "0.1" unicode-width = "0.1"
vte = { version = "0.10.0", default-features = false } vte = { version = "0.11.1", default-features = false, features = ["ansi", "serde"] }
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
nix = { version = "0.26.2", default-features = false, features = ["term"] } nix = { version = "0.26.2", default-features = false, features = ["term"] }

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,7 @@ use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};
mod scrolling; mod scrolling;
use crate::ansi::{CursorShape, CursorStyle}; use crate::ansi::{CursorShapeShim, CursorStyle};
pub use crate::config::scrolling::{Scrolling, MAX_SCROLLBACK_LINES}; pub use crate::config::scrolling::{Scrolling, MAX_SCROLLBACK_LINES};
@ -129,10 +129,10 @@ impl Cursor {
#[derive(SerdeReplace, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] #[derive(SerdeReplace, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
#[serde(untagged)] #[serde(untagged)]
pub enum ConfigCursorStyle { pub enum ConfigCursorStyle {
Shape(CursorShape), Shape(CursorShapeShim),
WithBlinking { WithBlinking {
#[serde(default)] #[serde(default)]
shape: CursorShape, shape: CursorShapeShim,
#[serde(default)] #[serde(default)]
blinking: CursorBlinking, blinking: CursorBlinking,
}, },
@ -140,7 +140,7 @@ pub enum ConfigCursorStyle {
impl Default for ConfigCursorStyle { impl Default for ConfigCursorStyle {
fn default() -> Self { fn default() -> Self {
Self::Shape(CursorShape::default()) Self::Shape(CursorShapeShim::default())
} }
} }
@ -157,28 +157,23 @@ impl ConfigCursorStyle {
impl From<ConfigCursorStyle> for CursorStyle { impl From<ConfigCursorStyle> for CursorStyle {
fn from(config_style: ConfigCursorStyle) -> Self { fn from(config_style: ConfigCursorStyle) -> Self {
match config_style { match config_style {
ConfigCursorStyle::Shape(shape) => Self { shape, blinking: false }, ConfigCursorStyle::Shape(shape) => Self { shape: shape.into(), blinking: false },
ConfigCursorStyle::WithBlinking { shape, blinking } => { ConfigCursorStyle::WithBlinking { shape, blinking } => {
Self { shape, blinking: blinking.into() } Self { shape: shape.into(), blinking: blinking.into() }
}, },
} }
} }
} }
#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)] #[derive(ConfigDeserialize, Default, Debug, Copy, Clone, PartialEq, Eq)]
pub enum CursorBlinking { pub enum CursorBlinking {
Never, Never,
#[default]
Off, Off,
On, On,
Always, Always,
} }
impl Default for CursorBlinking {
fn default() -> Self {
CursorBlinking::Off
}
}
impl CursorBlinking { impl CursorBlinking {
fn blinking_override(&self) -> Option<bool> { fn blinking_override(&self) -> Option<bool> {
match self { match self {

View file

@ -333,8 +333,9 @@ where
'event_loop: loop { 'event_loop: loop {
// Wakeup the event loop when a synchronized update timeout was reached. // Wakeup the event loop when a synchronized update timeout was reached.
let sync_timeout = state.parser.sync_timeout(); let handler = state.parser.sync_timeout();
let timeout = sync_timeout.map(|st| st.saturating_duration_since(Instant::now())); let timeout =
handler.sync_timeout().map(|st| st.saturating_duration_since(Instant::now()));
if let Err(err) = self.poll.poll(&mut events, timeout) { if let Err(err) = self.poll.poll(&mut events, timeout) {
match err.kind() { match err.kind() {

View file

@ -3,6 +3,7 @@ use std::sync::Arc;
use bitflags::bitflags; use bitflags::bitflags;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use vte::ansi::Hyperlink as VteHyperlink;
use crate::ansi::{Color, NamedColor}; use crate::ansi::{Color, NamedColor};
use crate::grid::{self, GridCell}; use crate::grid::{self, GridCell};
@ -57,6 +58,18 @@ impl Hyperlink {
} }
} }
impl From<VteHyperlink> for Hyperlink {
fn from(value: VteHyperlink) -> Self {
Self::new(value.id, value.uri)
}
}
impl From<Hyperlink> for VteHyperlink {
fn from(val: Hyperlink) -> Self {
VteHyperlink { id: Some(val.id().to_owned()), uri: val.uri().to_owned() }
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
struct HyperlinkInner { struct HyperlinkInner {
/// Identifier for the given hyperlink. /// Identifier for the given hyperlink.

View file

@ -1,89 +1,62 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::ops::{Add, Index, IndexMut, Mul}; use std::ops::{Add, Deref, Index, IndexMut, Mul};
use std::str::FromStr; use std::str::FromStr;
use log::trace;
use serde::de::{Error as _, Visitor}; use serde::de::{Error as _, Visitor};
use serde::{Deserialize, Deserializer, Serialize}; use serde::{Deserialize, Deserializer, Serialize};
use serde_yaml::Value; use serde_yaml::Value;
use alacritty_config_derive::SerdeReplace; use alacritty_config_derive::SerdeReplace;
use vte::ansi::Rgb as VteRgb;
use crate::ansi::NamedColor; use crate::ansi::NamedColor;
/// Number of terminal colors. /// Number of terminal colors.
pub const COUNT: usize = 269; pub const COUNT: usize = 269;
#[derive(SerdeReplace, Debug, Eq, PartialEq, Copy, Clone, Default, Serialize)] #[derive(SerdeReplace, Debug, Eq, PartialEq, Copy, Clone, Default, Serialize)]
pub struct Rgb { pub struct Rgb(VteRgb);
pub r: u8,
pub g: u8,
pub b: u8,
}
impl Rgb { impl Rgb {
/// Implementation of W3C's luminance #[inline]
/// [algorithm](https://www.w3.org/TR/WCAG20/#relativeluminancedef) pub const fn new(r: u8, g: u8, b: u8) -> Self {
fn luminance(self) -> f64 { Self(VteRgb { r, g, b })
let channel_luminance = |channel| {
let channel = channel as f64 / 255.;
if channel <= 0.03928 {
channel / 12.92
} else {
f64::powf((channel + 0.055) / 1.055, 2.4)
}
};
let r_luminance = channel_luminance(self.r);
let g_luminance = channel_luminance(self.g);
let b_luminance = channel_luminance(self.b);
0.2126 * r_luminance + 0.7152 * g_luminance + 0.0722 * b_luminance
} }
/// Implementation of [W3C's contrast algorithm]. #[inline]
/// pub fn as_tuple(self) -> (u8, u8, u8) {
/// [W3C's contrast algorithm]: https://www.w3.org/TR/WCAG20/#contrast-ratiodef (self.0.r, self.0.g, self.0.b)
pub fn contrast(self, other: Rgb) -> f64 { }
let self_luminance = self.luminance(); }
let other_luminance = other.luminance();
impl From<VteRgb> for Rgb {
let (darker, lighter) = if self_luminance > other_luminance { fn from(value: VteRgb) -> Self {
(other_luminance, self_luminance) Self(value)
} else { }
(self_luminance, other_luminance) }
};
impl Deref for Rgb {
(lighter + 0.05) / (darker + 0.05) type Target = VteRgb;
fn deref(&self) -> &Self::Target {
&self.0
} }
} }
// A multiply function for Rgb, as the default dim is just *2/3.
impl Mul<f32> for Rgb { impl Mul<f32> for Rgb {
type Output = Rgb; type Output = Rgb;
fn mul(self, rhs: f32) -> Rgb { fn mul(self, rhs: f32) -> Self::Output {
let result = Rgb { Rgb(self.0 * rhs)
r: (f32::from(self.r) * rhs).clamp(0.0, 255.0) as u8,
g: (f32::from(self.g) * rhs).clamp(0.0, 255.0) as u8,
b: (f32::from(self.b) * rhs).clamp(0.0, 255.0) as u8,
};
trace!("Scaling RGB by {} from {:?} to {:?}", rhs, self, result);
result
} }
} }
impl Add<Rgb> for Rgb { impl Add<Rgb> for Rgb {
type Output = Rgb; type Output = Rgb;
fn add(self, rhs: Rgb) -> Rgb { fn add(self, rhs: Rgb) -> Self::Output {
Rgb { Rgb(self.0 + rhs.0)
r: self.r.saturating_add(rhs.r),
g: self.g.saturating_add(rhs.g),
b: self.b.saturating_add(rhs.b),
}
} }
} }
@ -130,7 +103,7 @@ impl<'de> Deserialize<'de> for Rgb {
// Attempt to deserialize from struct form. // Attempt to deserialize from struct form.
if let Ok(RgbDerivedDeser { r, g, b }) = RgbDerivedDeser::deserialize(value.clone()) { if let Ok(RgbDerivedDeser { r, g, b }) = RgbDerivedDeser::deserialize(value.clone()) {
return Ok(Rgb { r, g, b }); return Ok(Rgb::new(r, g, b));
} }
// Deserialize from hex notation (either 0xff00ff or #ff00ff). // Deserialize from hex notation (either 0xff00ff or #ff00ff).
@ -163,7 +136,7 @@ impl FromStr for Rgb {
let g = (color & 0xff) as u8; let g = (color & 0xff) as u8;
color >>= 8; color >>= 8;
let r = color as u8; let r = color as u8;
Ok(Rgb { r, g, b }) Ok(Rgb::new(r, g, b))
}, },
Err(_) => Err(()), Err(_) => Err(()),
} }
@ -283,26 +256,3 @@ impl IndexMut<NamedColor> for Colors {
&mut self.0[index as usize] &mut self.0[index as usize]
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn contrast() {
let rgb1 = Rgb { r: 0xff, g: 0xff, b: 0xff };
let rgb2 = Rgb { r: 0x00, g: 0x00, b: 0x00 };
assert!((rgb1.contrast(rgb2) - 21.).abs() < f64::EPSILON);
let rgb1 = Rgb { r: 0xff, g: 0xff, b: 0xff };
assert!((rgb1.contrast(rgb1) - 1.).abs() < f64::EPSILON);
let rgb1 = Rgb { r: 0xff, g: 0x00, b: 0xff };
let rgb2 = Rgb { r: 0x00, g: 0xff, b: 0x00 };
assert!((rgb1.contrast(rgb2) - 2.285_543_608_124_253_3).abs() < f64::EPSILON);
let rgb1 = Rgb { r: 0x12, g: 0x34, b: 0x56 };
let rgb2 = Rgb { r: 0xfe, g: 0xdc, b: 0xba };
assert!((rgb1.contrast(rgb2) - 9.786_558_997_257_74).abs() < f64::EPSILON);
}
}

View file

@ -7,6 +7,7 @@ use std::{cmp, mem, ptr, slice, str};
use bitflags::bitflags; use bitflags::bitflags;
use log::{debug, trace}; use log::{debug, trace};
use unicode_width::UnicodeWidthChar; use unicode_width::UnicodeWidthChar;
use vte::ansi::{Hyperlink as VteHyperlink, Rgb as VteRgb};
use crate::ansi::{ use crate::ansi::{
self, Attr, CharsetIndex, Color, CursorShape, CursorStyle, Handler, NamedColor, StandardCharset, self, Attr, CharsetIndex, Color, CursorShape, CursorStyle, Handler, NamedColor, StandardCharset,
@ -16,8 +17,8 @@ use crate::event::{Event, EventListener};
use crate::grid::{Dimensions, Grid, GridIterator, Scroll}; use crate::grid::{Dimensions, Grid, GridIterator, Scroll};
use crate::index::{self, Boundary, Column, Direction, Line, Point, Side}; use crate::index::{self, Boundary, Column, Direction, Line, Point, Side};
use crate::selection::{Selection, SelectionRange, SelectionType}; use crate::selection::{Selection, SelectionRange, SelectionType};
use crate::term::cell::{Cell, Flags, Hyperlink, LineLength}; use crate::term::cell::{Cell, Flags, LineLength};
use crate::term::color::{Colors, Rgb}; use crate::term::color::Colors;
use crate::vi_mode::{ViModeCursor, ViMotion}; use crate::vi_mode::{ViModeCursor, ViMotion};
pub mod cell; pub mod cell;
@ -1069,7 +1070,10 @@ impl<T: EventListener> Handler for Term<T> {
} }
#[inline] #[inline]
fn goto(&mut self, line: Line, col: Column) { fn goto(&mut self, line: i32, col: usize) {
let line = Line(line);
let col = Column(col);
trace!("Going to: line={}, col={}", line, col); trace!("Going to: line={}, col={}", line, col);
let (y_offset, max_y) = if self.mode.contains(TermMode::ORIGIN) { let (y_offset, max_y) = if self.mode.contains(TermMode::ORIGIN) {
(self.scroll_region.start, self.scroll_region.end - 1) (self.scroll_region.start, self.scroll_region.end - 1)
@ -1085,15 +1089,15 @@ impl<T: EventListener> Handler for Term<T> {
} }
#[inline] #[inline]
fn goto_line(&mut self, line: Line) { fn goto_line(&mut self, line: i32) {
trace!("Going to line: {}", line); trace!("Going to line: {}", line);
self.goto(line, self.grid.cursor.point.column) self.goto(line, self.grid.cursor.point.column.0)
} }
#[inline] #[inline]
fn goto_col(&mut self, col: Column) { fn goto_col(&mut self, col: usize) {
trace!("Going to column: {}", col); trace!("Going to column: {}", col);
self.goto(self.grid.cursor.point.line, col) self.goto(self.grid.cursor.point.line.0, col)
} }
#[inline] #[inline]
@ -1127,17 +1131,23 @@ impl<T: EventListener> Handler for Term<T> {
#[inline] #[inline]
fn move_up(&mut self, lines: usize) { fn move_up(&mut self, lines: usize) {
trace!("Moving up: {}", lines); trace!("Moving up: {}", lines);
self.goto(self.grid.cursor.point.line - lines, self.grid.cursor.point.column)
let line = self.grid.cursor.point.line - lines;
let column = self.grid.cursor.point.column;
self.goto(line.0, column.0)
} }
#[inline] #[inline]
fn move_down(&mut self, lines: usize) { fn move_down(&mut self, lines: usize) {
trace!("Moving down: {}", lines); trace!("Moving down: {}", lines);
self.goto(self.grid.cursor.point.line + lines, self.grid.cursor.point.column)
let line = self.grid.cursor.point.line + lines;
let column = self.grid.cursor.point.column;
self.goto(line.0, column.0)
} }
#[inline] #[inline]
fn move_forward(&mut self, cols: Column) { fn move_forward(&mut self, cols: usize) {
trace!("Moving forward: {}", cols); trace!("Moving forward: {}", cols);
let last_column = cmp::min(self.grid.cursor.point.column + cols, self.last_column()); let last_column = cmp::min(self.grid.cursor.point.column + cols, self.last_column());
@ -1149,9 +1159,9 @@ impl<T: EventListener> Handler for Term<T> {
} }
#[inline] #[inline]
fn move_backward(&mut self, cols: Column) { fn move_backward(&mut self, cols: usize) {
trace!("Moving backward: {}", cols); trace!("Moving backward: {}", cols);
let column = self.grid.cursor.point.column.saturating_sub(cols.0); let column = self.grid.cursor.point.column.saturating_sub(cols);
let cursor_line = self.grid.cursor.point.line.0 as usize; let cursor_line = self.grid.cursor.point.line.0 as usize;
self.damage.damage_line(cursor_line, column, self.grid.cursor.point.column.0); self.damage.damage_line(cursor_line, column, self.grid.cursor.point.column.0);
@ -1198,13 +1208,17 @@ impl<T: EventListener> Handler for Term<T> {
#[inline] #[inline]
fn move_down_and_cr(&mut self, lines: usize) { fn move_down_and_cr(&mut self, lines: usize) {
trace!("Moving down and cr: {}", lines); trace!("Moving down and cr: {}", lines);
self.goto(self.grid.cursor.point.line + lines, Column(0))
let line = self.grid.cursor.point.line + lines;
self.goto(line.0, 0)
} }
#[inline] #[inline]
fn move_up_and_cr(&mut self, lines: usize) { fn move_up_and_cr(&mut self, lines: usize) {
trace!("Moving up and cr: {}", lines); trace!("Moving up and cr: {}", lines);
self.goto(self.grid.cursor.point.line - lines, Column(0))
let line = self.grid.cursor.point.line - lines;
self.goto(line.0, 0)
} }
/// Insert tab at cursor position. /// Insert tab at cursor position.
@ -1362,7 +1376,7 @@ impl<T: EventListener> Handler for Term<T> {
} }
#[inline] #[inline]
fn erase_chars(&mut self, count: Column) { fn erase_chars(&mut self, count: usize) {
let cursor = &self.grid.cursor; let cursor = &self.grid.cursor;
trace!("Erasing chars: count={}, col={}", count, cursor.point.column); trace!("Erasing chars: count={}, col={}", count, cursor.point.column);
@ -1479,9 +1493,11 @@ impl<T: EventListener> Handler for Term<T> {
/// Set the indexed color value. /// Set the indexed color value.
#[inline] #[inline]
fn set_color(&mut self, index: usize, color: Rgb) { fn set_color(&mut self, index: usize, color: VteRgb) {
trace!("Setting color[{}] = {:?}", index, color); trace!("Setting color[{}] = {:?}", index, color);
let color = color.into();
// Damage terminal if the color changed and it's not the cursor. // Damage terminal if the color changed and it's not the cursor.
if index != NamedColor::Cursor as usize && self.colors[index] != Some(color) { if index != NamedColor::Cursor as usize && self.colors[index] != Some(color) {
self.mark_fully_damaged(); self.mark_fully_damaged();
@ -1679,9 +1695,9 @@ impl<T: EventListener> Handler for Term<T> {
} }
#[inline] #[inline]
fn set_hyperlink(&mut self, hyperlink: Option<Hyperlink>) { fn set_hyperlink(&mut self, hyperlink: Option<VteHyperlink>) {
trace!("Setting hyperlink: {:?}", hyperlink); trace!("Setting hyperlink: {:?}", hyperlink);
self.grid.cursor.template.set_hyperlink(hyperlink); self.grid.cursor.template.set_hyperlink(hyperlink.map(|e| e.into()));
} }
/// Set a terminal attribute. /// Set a terminal attribute.
@ -1858,7 +1874,7 @@ impl<T: EventListener> Handler for Term<T> {
let screen_lines = Line(self.screen_lines() as i32); let screen_lines = Line(self.screen_lines() as i32);
self.scroll_region.start = cmp::min(start, screen_lines); self.scroll_region.start = cmp::min(start, screen_lines);
self.scroll_region.end = cmp::min(end, screen_lines); self.scroll_region.end = cmp::min(end, screen_lines);
self.goto(Line(0), Column(0)); self.goto(0, 0);
} }
#[inline] #[inline]
@ -2756,7 +2772,7 @@ mod tests {
// Reset terminal for partial damage tests since it's initialized as fully damaged. // Reset terminal for partial damage tests since it's initialized as fully damaged.
term.reset_damage(); term.reset_damage();
term.goto(Line(1), Column(1)); term.goto(1, 1);
// NOTE While we can use `[Term::damage]` to access terminal damage information, in the // NOTE While we can use `[Term::damage]` to access terminal damage information, in the
// following tests we will be accessing `term.damage.lines` directly to avoid adding extra // following tests we will be accessing `term.damage.lines` directly to avoid adding extra
@ -2766,13 +2782,13 @@ mod tests {
assert_eq!(term.damage.lines[1], LineDamageBounds { line: 1, left: 1, right: 1 }); assert_eq!(term.damage.lines[1], LineDamageBounds { line: 1, left: 1, right: 1 });
term.damage.reset(num_cols); term.damage.reset(num_cols);
term.move_forward(Column(3)); term.move_forward(3);
assert_eq!(term.damage.lines[1], LineDamageBounds { line: 1, left: 1, right: 4 }); assert_eq!(term.damage.lines[1], LineDamageBounds { line: 1, left: 1, right: 4 });
term.damage.reset(num_cols); term.damage.reset(num_cols);
term.move_backward(Column(8)); term.move_backward(8);
assert_eq!(term.damage.lines[1], LineDamageBounds { line: 1, left: 0, right: 4 }); assert_eq!(term.damage.lines[1], LineDamageBounds { line: 1, left: 0, right: 4 });
term.goto(Line(5), Column(5)); term.goto(5, 5);
term.damage.reset(num_cols); term.damage.reset(num_cols);
term.backspace(); term.backspace();
@ -2795,7 +2811,7 @@ mod tests {
term.wrapline(); term.wrapline();
assert_eq!(term.damage.lines[6], LineDamageBounds { line: 6, left: 3, right: 3 }); assert_eq!(term.damage.lines[6], LineDamageBounds { line: 6, left: 3, right: 3 });
assert_eq!(term.damage.lines[7], LineDamageBounds { line: 7, left: 0, right: 0 }); assert_eq!(term.damage.lines[7], LineDamageBounds { line: 7, left: 0, right: 0 });
term.move_forward(Column(3)); term.move_forward(3);
term.move_up(1); term.move_up(1);
term.damage.reset(num_cols); term.damage.reset(num_cols);
@ -2808,20 +2824,20 @@ mod tests {
assert_eq!(term.damage.lines[7], LineDamageBounds { line: 7, left: 0, right: 3 }); assert_eq!(term.damage.lines[7], LineDamageBounds { line: 7, left: 0, right: 3 });
term.damage.reset(num_cols); term.damage.reset(num_cols);
term.erase_chars(Column(5)); term.erase_chars(5);
assert_eq!(term.damage.lines[7], LineDamageBounds { line: 7, left: 0, right: 5 }); assert_eq!(term.damage.lines[7], LineDamageBounds { line: 7, left: 0, right: 5 });
term.damage.reset(num_cols); term.damage.reset(num_cols);
term.delete_chars(3); term.delete_chars(3);
let right = term.columns() - 1; let right = term.columns() - 1;
assert_eq!(term.damage.lines[7], LineDamageBounds { line: 7, left: 0, right }); assert_eq!(term.damage.lines[7], LineDamageBounds { line: 7, left: 0, right });
term.move_forward(Column(term.columns())); term.move_forward(term.columns());
term.damage.reset(num_cols); term.damage.reset(num_cols);
term.move_backward_tabs(1); term.move_backward_tabs(1);
assert_eq!(term.damage.lines[7], LineDamageBounds { line: 7, left: 8, right }); assert_eq!(term.damage.lines[7], LineDamageBounds { line: 7, left: 8, right });
term.save_cursor_position(); term.save_cursor_position();
term.goto(Line(1), Column(1)); term.goto(1, 1);
term.damage.reset(num_cols); term.damage.reset(num_cols);
term.restore_cursor_position(); term.restore_cursor_position();
@ -2896,12 +2912,12 @@ mod tests {
term.reset_damage(); term.reset_damage();
let color_index = 257; let color_index = 257;
term.set_color(color_index, Rgb::default()); term.set_color(color_index, VteRgb::default());
assert!(term.damage.is_fully_damaged); assert!(term.damage.is_fully_damaged);
term.reset_damage(); term.reset_damage();
// Setting the same color once again shouldn't trigger full damage. // Setting the same color once again shouldn't trigger full damage.
term.set_color(color_index, Rgb::default()); term.set_color(color_index, VteRgb::default());
assert!(!term.damage.is_fully_damaged); assert!(!term.damage.is_fully_damaged);
term.reset_color(color_index); term.reset_color(color_index);
@ -2909,7 +2925,7 @@ mod tests {
term.reset_damage(); term.reset_damage();
// We shouldn't trigger fully damage when cursor gets update. // We shouldn't trigger fully damage when cursor gets update.
term.set_color(NamedColor::Cursor as usize, Rgb::default()); term.set_color(NamedColor::Cursor as usize, VteRgb::default());
assert!(!term.damage.is_fully_damaged); assert!(!term.damage.is_fully_damaged);
// However requesting terminal damage should mark terminal as fully damaged in `Insert` // However requesting terminal damage should mark terminal as fully damaged in `Insert`

View file

@ -109,7 +109,7 @@ fn ref_test(dir: &Path) {
config.scrolling.set_history(ref_config.history_size); config.scrolling.set_history(ref_config.history_size);
let mut terminal = Term::new(&config, &size, Mock); let mut terminal = Term::new(&config, &size, Mock);
let mut parser = ansi::Processor::new(); let mut parser: ansi::Processor = ansi::Processor::new();
for byte in recording { for byte in recording {
parser.advance(&mut terminal, byte); parser.advance(&mut terminal, byte);