Add support for indexed colors

ANSI escape sequences like `\x1b[48;5;10m` were not supported until now.
Specifically, the second attribute, 5, says that the following attribute
is a color index.

The ref tests were updated since `enum Color` variants changed.
This commit is contained in:
Joe Wilm 2016-12-04 11:14:27 -08:00
parent 3151ef8625
commit 23e36f1925
15 changed files with 184 additions and 122 deletions

View File

@ -310,7 +310,7 @@ pub enum TabulationClearMode {
/// The order here matters since the enum should be castable to a `usize` for
/// indexing a color list.
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Color {
pub enum NamedColor {
/// Black
Black = 0,
/// Red
@ -344,11 +344,18 @@ pub enum Color {
/// Bright white
BrightWhite,
/// The foreground color
Foreground,
Foreground = 256,
/// The background color
Background,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Color {
Named(NamedColor),
Spec(Rgb),
Indexed(u8),
}
/// Terminal character attributes
#[derive(Debug, Eq, PartialEq)]
pub enum Attr {
@ -388,12 +395,8 @@ pub enum Attr {
CancelStrike,
/// Set indexed foreground color
Foreground(Color),
/// Set specific foreground color
ForegroundSpec(Rgb),
/// Set indexed background color
Background(Color),
/// Set specific background color
BackgroundSpec(Rgb),
}
impl<'a, H: Handler + TermInfo + 'a> vte::Perform for Performer<'a, H> {
@ -575,54 +578,54 @@ impl<'a, H: Handler + TermInfo + 'a> vte::Perform for Performer<'a, H> {
27 => Attr::CancelReverse,
28 => Attr::CancelHidden,
29 => Attr::CancelStrike,
30 => Attr::Foreground(Color::Black),
31 => Attr::Foreground(Color::Red),
32 => Attr::Foreground(Color::Green),
33 => Attr::Foreground(Color::Yellow),
34 => Attr::Foreground(Color::Blue),
35 => Attr::Foreground(Color::Magenta),
36 => Attr::Foreground(Color::Cyan),
37 => Attr::Foreground(Color::White),
30 => Attr::Foreground(Color::Named(NamedColor::Black)),
31 => Attr::Foreground(Color::Named(NamedColor::Red)),
32 => Attr::Foreground(Color::Named(NamedColor::Green)),
33 => Attr::Foreground(Color::Named(NamedColor::Yellow)),
34 => Attr::Foreground(Color::Named(NamedColor::Blue)),
35 => Attr::Foreground(Color::Named(NamedColor::Magenta)),
36 => Attr::Foreground(Color::Named(NamedColor::Cyan)),
37 => Attr::Foreground(Color::Named(NamedColor::White)),
38 => {
if let Some(spec) = parse_color(&args[i..], &mut i) {
Attr::ForegroundSpec(spec)
if let Some(color) = parse_color(&args[i..], &mut i) {
Attr::Foreground(color)
} else {
break;
}
},
39 => Attr::Foreground(Color::Foreground),
40 => Attr::Background(Color::Black),
41 => Attr::Background(Color::Red),
42 => Attr::Background(Color::Green),
43 => Attr::Background(Color::Yellow),
44 => Attr::Background(Color::Blue),
45 => Attr::Background(Color::Magenta),
46 => Attr::Background(Color::Cyan),
47 => Attr::Background(Color::White),
39 => Attr::Foreground(Color::Named(NamedColor::Foreground)),
40 => Attr::Background(Color::Named(NamedColor::Black)),
41 => Attr::Background(Color::Named(NamedColor::Red)),
42 => Attr::Background(Color::Named(NamedColor::Green)),
43 => Attr::Background(Color::Named(NamedColor::Yellow)),
44 => Attr::Background(Color::Named(NamedColor::Blue)),
45 => Attr::Background(Color::Named(NamedColor::Magenta)),
46 => Attr::Background(Color::Named(NamedColor::Cyan)),
47 => Attr::Background(Color::Named(NamedColor::White)),
48 => {
if let Some(spec) = parse_color(&args[i..], &mut i) {
Attr::BackgroundSpec(spec)
if let Some(color) = parse_color(&args[i..], &mut i) {
Attr::Background(color)
} else {
break;
}
},
49 => Attr::Background(Color::Background),
90 => Attr::Foreground(Color::BrightBlack),
91 => Attr::Foreground(Color::BrightRed),
92 => Attr::Foreground(Color::BrightGreen),
93 => Attr::Foreground(Color::BrightYellow),
94 => Attr::Foreground(Color::BrightBlue),
95 => Attr::Foreground(Color::BrightMagenta),
96 => Attr::Foreground(Color::BrightCyan),
97 => Attr::Foreground(Color::BrightWhite),
100 => Attr::Foreground(Color::BrightBlack),
101 => Attr::Foreground(Color::BrightRed),
102 => Attr::Foreground(Color::BrightGreen),
103 => Attr::Foreground(Color::BrightYellow),
104 => Attr::Foreground(Color::BrightBlue),
105 => Attr::Foreground(Color::BrightMagenta),
106 => Attr::Foreground(Color::BrightCyan),
107 => Attr::Foreground(Color::BrightWhite),
49 => Attr::Background(Color::Named(NamedColor::Background)),
90 => Attr::Foreground(Color::Named(NamedColor::BrightBlack)),
91 => Attr::Foreground(Color::Named(NamedColor::BrightRed)),
92 => Attr::Foreground(Color::Named(NamedColor::BrightGreen)),
93 => Attr::Foreground(Color::Named(NamedColor::BrightYellow)),
94 => Attr::Foreground(Color::Named(NamedColor::BrightBlue)),
95 => Attr::Foreground(Color::Named(NamedColor::BrightMagenta)),
96 => Attr::Foreground(Color::Named(NamedColor::BrightCyan)),
97 => Attr::Foreground(Color::Named(NamedColor::BrightWhite)),
100 => Attr::Foreground(Color::Named(NamedColor::BrightBlack)),
101 => Attr::Foreground(Color::Named(NamedColor::BrightRed)),
102 => Attr::Foreground(Color::Named(NamedColor::BrightGreen)),
103 => Attr::Foreground(Color::Named(NamedColor::BrightYellow)),
104 => Attr::Foreground(Color::Named(NamedColor::BrightBlue)),
105 => Attr::Foreground(Color::Named(NamedColor::BrightMagenta)),
106 => Attr::Foreground(Color::Named(NamedColor::BrightCyan)),
107 => Attr::Foreground(Color::Named(NamedColor::BrightWhite)),
_ => unhandled!(),
};
@ -674,7 +677,7 @@ impl<'a, H: Handler + TermInfo + 'a> vte::Perform for Performer<'a, H> {
/// Parse a color specifier from list of attributes
fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Rgb> {
fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
if attrs.len() < 2 {
return None;
}
@ -699,11 +702,29 @@ fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Rgb> {
return None;
}
Some(Rgb {
Some(Color::Spec(Rgb {
r: r as u8,
g: g as u8,
b: b as u8
})
}))
},
5 => {
if attrs.len() < 3 {
err_println!("Expected color index; got {:?}", attrs);
None
} else {
*i = *i + 2;
let idx = attrs[*i];
match idx {
0 ... 255 => {
Some(Color::Indexed(idx as u8))
},
_ => {
err_println!("Invalid color index: {}", idx);
None
}
}
}
},
_ => {
err_println!("Unexpected color attr: {}", attrs[*i+1]);
@ -863,7 +884,7 @@ pub mod C1 {
#[cfg(test)]
mod tests {
use index::{Line, Column};
use super::{Processor, Handler, Attr, TermInfo};
use super::{Processor, Handler, Attr, TermInfo, Color};
use ::Rgb;
#[derive(Default)]
@ -923,7 +944,7 @@ mod tests {
b: 255
};
assert_eq!(handler.attr, Some(Attr::ForegroundSpec(spec)));
assert_eq!(handler.attr, Some(Attr::Foreground(Color::Spec(spec))));
}
/// No exactly a test; useful for debugging

View File

@ -665,34 +665,59 @@ impl Config {
///
/// The ordering returned here is expected by the terminal. Colors are simply indexed in this
/// array for performance.
pub fn color_list(&self) -> [Rgb; 18] {
let colors = &self.colors;
pub fn color_list(&self) -> Vec<Rgb> {
let mut colors = Vec::with_capacity(258);
[
// Normals
colors.normal.black,
colors.normal.red,
colors.normal.green,
colors.normal.yellow,
colors.normal.blue,
colors.normal.magenta,
colors.normal.cyan,
colors.normal.white,
// Normals
colors.push(self.colors.normal.black);
colors.push(self.colors.normal.red);
colors.push(self.colors.normal.green);
colors.push(self.colors.normal.yellow);
colors.push(self.colors.normal.blue);
colors.push(self.colors.normal.magenta);
colors.push(self.colors.normal.cyan);
colors.push(self.colors.normal.white);
// Brights
colors.bright.black,
colors.bright.red,
colors.bright.green,
colors.bright.yellow,
colors.bright.blue,
colors.bright.magenta,
colors.bright.cyan,
colors.bright.white,
// Brights
colors.push(self.colors.bright.black);
colors.push(self.colors.bright.red);
colors.push(self.colors.bright.green);
colors.push(self.colors.bright.yellow);
colors.push(self.colors.bright.blue);
colors.push(self.colors.bright.magenta);
colors.push(self.colors.bright.cyan);
colors.push(self.colors.bright.white);
// Foreground and background
colors.primary.foreground,
colors.primary.background,
]
// Build colors
for r in 0..6 {
for g in 0..6 {
for b in 0..6 {
colors.push(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 },
});
}
}
}
// Build grays
for i in 0..24 {
let value = i * 10 + 8;
colors.push(Rgb {
r: value,
g: value,
b: value
});
}
debug_assert!(colors.len() == 256);
// Foreground and background
colors.push(self.colors.primary.foreground);
colors.push(self.colors.primary.background);
colors
}
pub fn key_bindings(&self) -> &[KeyBinding] {

View File

@ -371,7 +371,7 @@ impl Display {
// Draw render timer
if self.render_timer {
let timing = format!("{:.3} usec", self.meter.average());
let color = alacritty::term::cell::Color::Rgb(Rgb { r: 0xd5, g: 0x4e, b: 0x53 });
let color = alacritty::ansi::Color::Spec(Rgb { r: 0xd5, g: 0x4e, b: 0x53 });
self.renderer.with_api(terminal.size_info(), |mut api| {
api.render_string(&timing[..], glyph_cache, &color);
});

View File

@ -26,6 +26,8 @@ use gl;
use notify::{Watcher as WatcherApi, RecommendedWatcher as Watcher, op};
use index::{Line, Column};
use ansi::{Color, NamedColor};
use config::Config;
use term::{self, cell, IndexedCell, Cell};
@ -241,7 +243,7 @@ pub struct QuadRenderer {
atlas: Vec<Atlas>,
active_tex: GLuint,
batch: Batch,
colors: [Rgb; 18],
colors: Vec<Rgb>,
draw_bold_text_with_bright_colors: bool,
rx: mpsc::Receiver<Msg>,
}
@ -252,7 +254,7 @@ pub struct RenderApi<'a> {
batch: &'a mut Batch,
atlas: &'a mut Vec<Atlas>,
program: &'a mut ShaderProgram,
colors: &'a [Rgb; 18],
colors: &'a [Rgb],
}
#[derive(Debug)]
@ -271,7 +273,7 @@ pub struct PackedVertex {
pub struct Batch {
tex: GLuint,
instances: Vec<InstanceData>,
colors: [Rgb; 18],
colors: Vec<Rgb>,
draw_bold_text_with_bright_colors: bool,
}
@ -292,22 +294,35 @@ impl Batch {
}
let fg = match cell.fg {
::term::cell::Color::Rgb(rgb) => rgb,
::term::cell::Color::Ansi(ansi) => {
Color::Spec(rgb) => rgb,
Color::Named(ansi) => {
if self.draw_bold_text_with_bright_colors
&& cell.bold()
&& ansi < ::ansi::Color::BrightBlack
&& ansi < NamedColor::BrightBlack
{
self.colors[ansi as usize + 8]
} else {
self.colors[ansi as usize]
}
},
Color::Indexed(idx) => {
let idx = if self.draw_bold_text_with_bright_colors
&& cell.bold()
&& idx < 8
{
idx + 8
} else {
idx
};
self.colors[idx as usize]
}
};
let bg = match cell.bg {
::term::cell::Color::Rgb(rgb) => rgb,
::term::cell::Color::Ansi(ansi) => self.colors[ansi as usize],
Color::Spec(rgb) => rgb,
Color::Named(ansi) => self.colors[ansi as usize],
Color::Indexed(idx) => self.colors[idx as usize],
};
self.instances.push(InstanceData {
@ -620,7 +635,7 @@ impl QuadRenderer {
impl<'a> RenderApi<'a> {
pub fn clear(&self) {
let color = self.colors[::ansi::Color::Background as usize];
let color = self.colors[NamedColor::Background as usize];
unsafe {
gl::ClearColor(
color.r as f32 / 255.0,
@ -666,7 +681,7 @@ impl<'a> RenderApi<'a> {
&mut self,
string: &str,
glyph_cache: &mut GlyphCache,
color: &::term::cell::Color,
color: &Color,
) {
let line = Line(23);
let col = Column(0);
@ -679,7 +694,7 @@ impl<'a> RenderApi<'a> {
inner: Cell {
c: c,
bg: *color,
fg: cell::Color::Rgb(Rgb { r: 0, g: 0, b: 0}),
fg: Color::Spec(Rgb { r: 0, g: 0, b: 0}),
flags: cell::Flags::empty(),
}
})

View File

@ -15,8 +15,7 @@
use std::mem;
use ansi;
use Rgb;
use ansi::{NamedColor, Color};
bitflags! {
#[derive(Serialize, Deserialize)]
@ -28,12 +27,6 @@ bitflags! {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Color {
Rgb(Rgb),
Ansi(ansi::Color),
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct Cell {
pub c: char,
@ -59,7 +52,7 @@ impl Cell {
#[inline]
pub fn is_empty(&self) -> bool {
self.c == ' ' &&
self.bg == Color::Ansi(ansi::Color::Background) &&
self.bg == Color::Named(NamedColor::Background) &&
!self.flags.contains(INVERSE)
}

View File

@ -20,7 +20,7 @@ use std::cmp;
use ansi::{self, Attr, Handler};
use grid::{Grid, ClearRegion};
use index::{Cursor, Column, Line};
use ansi::Color;
use ansi::{Color, NamedColor};
pub mod cell;
pub use self::cell::Cell;
@ -250,8 +250,8 @@ impl Term {
pub fn new(size: SizeInfo) -> Term {
let template = Cell::new(
' ',
cell::Color::Ansi(Color::Foreground),
cell::Color::Ansi(Color::Background)
Color::Named(NamedColor::Foreground),
Color::Named(NamedColor::Background)
);
let num_cols = size.cols();
@ -800,21 +800,11 @@ impl ansi::Handler for Term {
fn terminal_attribute(&mut self, attr: Attr) {
debug_println!("Set Attribute: {:?}", attr);
match attr {
Attr::Foreground(named_color) => {
self.template_cell.fg = cell::Color::Ansi(named_color);
},
Attr::Background(named_color) => {
self.template_cell.bg = cell::Color::Ansi(named_color);
},
Attr::ForegroundSpec(rgb) => {
self.template_cell.fg = cell::Color::Rgb(rgb);
},
Attr::BackgroundSpec(rgb) => {
self.template_cell.bg = cell::Color::Rgb(rgb);
},
Attr::Foreground(color) => self.template_cell.fg = color,
Attr::Background(color) => self.template_cell.bg = color,
Attr::Reset => {
self.template_cell.fg = cell::Color::Ansi(Color::Foreground);
self.template_cell.bg = cell::Color::Ansi(Color::Background);
self.template_cell.fg = Color::Named(NamedColor::Foreground);
self.template_cell.bg = Color::Named(NamedColor::Background);
self.template_cell.flags = cell::Flags::empty();
},
Attr::Reverse => self.template_cell.flags.insert(cell::INVERSE),
@ -888,10 +878,10 @@ mod tests {
use super::limit;
use ansi::{Color};
use ansi::{Color, NamedColor};
use grid::Grid;
use index::{Line, Column};
use term::{cell, Cell};
use term::{Cell};
/// Check that the grid can be serialized back and forth losslessly
///
@ -901,8 +891,8 @@ mod tests {
fn grid_serde() {
let template = Cell::new(
' ',
cell::Color::Ansi(Color::Foreground),
cell::Color::Ansi(Color::Background)
Color::Named(NamedColor::Foreground),
Color::Named(NamedColor::Background)
);
let grid = Grid::new(Line(24), Column(80), &template);

View File

@ -79,6 +79,7 @@ mod reference {
vim_simple_edit,
tmux_htop,
tmux_git_log,
vim_large_window_scroll
vim_large_window_scroll,
indexed_256_colors
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"width":1124.0,"height":628.0,"cell_width":14.0,"cell_height":26.0}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long