summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config.rs163
-rw-r--r--src/main.rs26
-rw-r--r--src/renderer/mod.rs18
-rw-r--r--src/term.rs119
4 files changed, 245 insertions, 81 deletions
diff --git a/src/config.rs b/src/config.rs
index 3c4d25c6..56f96e80 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -8,9 +8,10 @@ use std::fs;
use std::io::{self, Read};
use std::path::{Path, PathBuf};
+use ::Rgb;
use font::Size;
use serde_yaml;
-use serde;
+use serde::{self, Error as SerdeError};
/// Top-level config type
#[derive(Debug, Deserialize, Default)]
@@ -26,6 +27,10 @@ pub struct Config {
/// Should show render timer
#[serde(default)]
render_timer: bool,
+
+ /// The standard ANSI colors to use
+ #[serde(default)]
+ colors: Colors,
}
/// Errors occurring during config loading
@@ -44,6 +49,124 @@ pub enum Error {
Yaml(serde_yaml::Error),
}
+#[derive(Debug, Deserialize)]
+pub struct Colors {
+ primary: PrimaryColors,
+ normal: AnsiColors,
+ bright: AnsiColors,
+}
+
+#[derive(Debug, Deserialize)]
+pub struct PrimaryColors {
+ background: Rgb,
+ foreground: Rgb,
+}
+
+impl Default for Colors {
+ fn default() -> Colors {
+ Colors {
+ primary: PrimaryColors {
+ background: Rgb { r: 0, g: 0, b: 0 },
+ foreground: Rgb { r: 0xea, g: 0xea, b: 0xea },
+ },
+ normal: AnsiColors {
+ black: Rgb {r: 0x00, g: 0x00, b: 0x00},
+ red: Rgb {r: 0xd5, g: 0x4e, b: 0x53},
+ green: Rgb {r: 0xb9, g: 0xca, b: 0x4a},
+ yellow: Rgb {r: 0xe6, 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: 0xba},
+ white: Rgb {r: 0x42, g: 0x42, b: 0x42},
+ },
+ bright: AnsiColors {
+ black: Rgb {r: 0x66, g: 0x66, b: 0x66},
+ red: Rgb {r: 0xff, g: 0x33, b: 0x34},
+ green: Rgb {r: 0x9e, g: 0xc4, b: 0x00},
+ yellow: Rgb {r: 0xe7, g: 0xc5, b: 0x47},
+ blue: Rgb {r: 0x7a, g: 0xa6, b: 0xda},
+ magenta: Rgb {r: 0xb7, g: 0x7e, b: 0xe0},
+ cyan: Rgb {r: 0x54, g: 0xce, b: 0xd6},
+ white: Rgb {r: 0x2a, g: 0x2a, b: 0x2a},
+ }
+ }
+ }
+}
+
+/// The normal or bright colors section of config
+#[derive(Debug, Deserialize)]
+pub struct AnsiColors {
+ black: Rgb,
+ red: Rgb,
+ green: Rgb,
+ yellow: Rgb,
+ blue: Rgb,
+ magenta: Rgb,
+ cyan: Rgb,
+ white: Rgb,
+}
+
+impl serde::de::Deserialize for Rgb {
+ fn deserialize<D>(deserializer: &mut D) -> ::std::result::Result<Self, D::Error>
+ where D: serde::de::Deserializer
+ {
+ use std::marker::PhantomData;
+
+ struct StringVisitor<__D> {
+ _marker: PhantomData<__D>,
+ }
+
+ impl<__D> ::serde::de::Visitor for StringVisitor<__D>
+ where __D: ::serde::de::Deserializer
+ {
+ type Value = String;
+
+ fn visit_str<E>(&mut self, value: &str) -> ::std::result::Result<Self::Value, E>
+ where E: ::serde::de::Error
+ {
+ Ok(value.to_owned())
+ }
+ }
+
+ deserializer
+ .deserialize_f64(StringVisitor::<D>{ _marker: PhantomData })
+ .and_then(|v| {
+ Rgb::from_str(&v[..])
+ .map_err(|_| D::Error::custom("failed to parse rgb; expect 0xrrggbb"))
+ })
+ }
+}
+
+impl Rgb {
+ fn from_str(s: &str) -> ::std::result::Result<Rgb, ()> {
+ let mut chars = s.chars();
+ let mut rgb = Rgb::default();
+
+ macro_rules! component {
+ ($($c:ident),*) => {
+ $(
+ match chars.next().unwrap().to_digit(16) {
+ Some(val) => rgb.$c = (val as u8) << 4,
+ None => return Err(())
+ }
+
+ match chars.next().unwrap().to_digit(16) {
+ Some(val) => rgb.$c |= val as u8,
+ None => return Err(())
+ }
+ )*
+ }
+ }
+
+ if chars.next().unwrap() != '0' { return Err(()); }
+ if chars.next().unwrap() != 'x' { return Err(()); }
+
+ component!(r, g, b);
+
+ Ok(rgb)
+ }
+}
+
impl ::std::error::Error for Error {
fn cause(&self) -> Option<&::std::error::Error> {
match *self {
@@ -132,6 +255,44 @@ impl Config {
}
}
+ /// Get list of colors
+ ///
+ /// The ordering returned here is expected by the terminal. Colors are simply indexed in this
+ /// array for performance.
+ pub fn color_list(&self) -> [Rgb; 16] {
+ let colors = &self.colors;
+
+ [
+ // 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,
+
+ // 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,
+ ]
+ }
+
+ pub fn fg_color(&self) -> Rgb {
+ self.colors.primary.foreground
+ }
+
+ pub fn bg_color(&self) -> Rgb {
+ self.colors.primary.background
+ }
+
/// Get font config
#[inline]
pub fn font(&self) -> &Font {
diff --git a/src/main.rs b/src/main.rs
index afc60a66..9639fd29 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -179,7 +179,13 @@ fn main() {
println!("Cell Size: ({} x {})", cell_width, cell_height);
- let terminal = Term::new(width as f32, height as f32, cell_width as f32, cell_height as f32);
+ let terminal = Term::new(
+ &config,
+ width as f32,
+ height as f32,
+ cell_width as f32,
+ cell_height as f32
+ );
let pty_io = terminal.tty().reader();
let (tx, rx) = mpsc::channel();
@@ -208,6 +214,7 @@ fn main() {
window.clone(),
renderer,
glyph_cache,
+ config.bg_color(),
render_timer,
rx
);
@@ -241,13 +248,14 @@ fn main() {
println!("Goodbye");
}
-
-
struct Display {
window: Arc<glutin::Window>,
renderer: QuadRenderer,
glyph_cache: GlyphCache,
render_timer: bool,
+ clear_red: f32,
+ clear_blue: f32,
+ clear_green: f32,
rx: mpsc::Receiver<(u32, u32)>,
meter: Meter,
}
@@ -256,6 +264,7 @@ impl Display {
pub fn new(window: Arc<glutin::Window>,
renderer: QuadRenderer,
glyph_cache: GlyphCache,
+ clear_color: Rgb,
render_timer: bool,
rx: mpsc::Receiver<(u32, u32)>)
-> Display
@@ -265,6 +274,9 @@ impl Display {
renderer: renderer,
glyph_cache: glyph_cache,
render_timer: render_timer,
+ clear_red: clear_color.r as f32 / 255.0,
+ clear_blue: clear_color.g as f32 / 255.0,
+ clear_green: clear_color.b as f32 / 255.0,
rx: rx,
meter: Meter::new(),
}
@@ -285,7 +297,7 @@ impl Display {
// TODO should be built into renderer
unsafe {
- gl::ClearColor(0.0, 0.0, 0.00, 1.0);
+ gl::ClearColor(self.clear_red, self.clear_blue, self.clear_green, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
}
@@ -310,7 +322,8 @@ impl Display {
let size_info = terminal.size_info().clone();
self.renderer.with_api(&size_info, |mut api| {
// Draw the grid
- api.render_grid(&terminal.render_grid(), glyph_cache);
+ let bg = terminal.bg;
+ api.render_grid(&bg, &terminal.render_grid(), glyph_cache);
});
}
@@ -319,7 +332,8 @@ impl Display {
let timing = format!("{:.3} usec", self.meter.average());
let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
self.renderer.with_api(terminal.size_info(), |mut api| {
- api.render_string(&timing[..], glyph_cache, &color);
+ let bg = terminal.bg;
+ api.render_string(&bg, &timing[..], glyph_cache, &color);
});
}
}
diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs
index 23157456..7ddf1d01 100644
--- a/src/renderer/mod.rs
+++ b/src/renderer/mod.rs
@@ -610,11 +610,13 @@ impl<'a> RenderApi<'a> {
}
/// Render a string in a predefined location. Used for printing render time for profiling and
/// optimization.
- pub fn render_string(&mut self,
- s: &str,
- glyph_cache: &mut GlyphCache,
- color: &Rgb)
- {
+ pub fn render_string(
+ &mut self,
+ bg: &Rgb,
+ s: &str,
+ glyph_cache: &mut GlyphCache,
+ color: &Rgb,
+ ) {
let row = 40.0;
let mut col = 100.0;
@@ -629,7 +631,7 @@ impl<'a> RenderApi<'a> {
let cell = Cell {
c: c,
fg: *color,
- bg: term::DEFAULT_BG,
+ bg: *bg,
flags: cell::INVERSE,
};
self.add_render_item(row, col, &cell, glyph);
@@ -656,12 +658,12 @@ impl<'a> RenderApi<'a> {
}
}
- pub fn render_grid(&mut self, grid: &Grid<Cell>, glyph_cache: &mut GlyphCache) {
+ pub fn render_grid(&mut self, bg: &Rgb, grid: &Grid<Cell>, glyph_cache: &mut GlyphCache) {
for (i, line) in grid.lines().enumerate() {
for (j, cell) in line.cells().enumerate() {
// Skip empty cells
if cell.c == ' ' &&
- cell.bg == term::DEFAULT_BG &&
+ cell.bg == *bg &&
!cell.flags.contains(cell::INVERSE)
{
continue;
diff --git a/src/term.rs b/src/term.rs
index 7a80c6fb..898251d7 100644
--- a/src/term.rs
+++ b/src/term.rs
@@ -21,6 +21,7 @@ use ansi::{self, Attr, Handler};
use grid::{Grid, ClearRegion};
use index::{Cursor, Column, Line};
use tty;
+use config::Config;
use ::Rgb;
@@ -89,7 +90,7 @@ pub mod cell {
}
}
- #[derive(Clone, Debug)]
+ #[derive(Clone, Debug, Copy)]
pub struct Cell {
pub c: char,
pub fg: Rgb,
@@ -123,28 +124,6 @@ pub mod cell {
pub use self::cell::Cell;
-/// tomorrow night bright
-///
-/// because contrast
-pub static COLORS: &'static [Rgb] = &[
- Rgb {r: 0x00, g: 0x00, b: 0x00}, // Black
- Rgb {r: 0xd5, g: 0x4e, b: 0x53}, // Red
- Rgb {r: 0xb9, g: 0xca, b: 0x4a}, // Green
- Rgb {r: 0xe6, g: 0xc5, b: 0x47}, // Yellow
- Rgb {r: 0x7a, g: 0xa6, b: 0xda}, // Blue
- Rgb {r: 0xc3, g: 0x97, b: 0xd8}, // Magenta
- Rgb {r: 0x70, g: 0xc0, b: 0xba}, // Cyan
- Rgb {r: 0x42, g: 0x42, b: 0x42}, // White
- Rgb {r: 0x66, g: 0x66, b: 0x66}, // Bright black
- Rgb {r: 0xff, g: 0x33, b: 0x34}, // Bright red
- Rgb {r: 0x9e, g: 0xc4, b: 0x00}, // Bright green
- Rgb {r: 0xe7, g: 0xc5, b: 0x47}, // Bright yellow
- Rgb {r: 0x7a, g: 0xa6, b: 0xda}, // Bright blue
- Rgb {r: 0xb7, g: 0x7e, b: 0xe0}, // Bright magenta
- Rgb {r: 0x54, g: 0xce, b: 0xd6}, // Bright cyan
- Rgb {r: 0x2a, g: 0x2a, b: 0x2a}, // Bright white
-];
-
pub mod mode {
bitflags! {
pub flags TermMode: u8 {
@@ -165,8 +144,6 @@ pub mod mode {
pub use self::mode::TermMode;
-pub const DEFAULT_FG: Rgb = Rgb { r: 0xea, g: 0xea, b: 0xea};
-pub const DEFAULT_BG: Rgb = Rgb { r: 0, g: 0, b: 0};
pub const TAB_SPACES: usize = 8;
pub struct Term {
@@ -189,17 +166,14 @@ pub struct Term {
alt_cursor: Cursor,
/// Active foreground color
- fg: Rgb,
+ pub fg: Rgb,
/// Active background color
- bg: Rgb,
+ pub bg: Rgb,
/// Tabstops
tabs: Vec<bool>,
- /// Cell attributes
- attr: cell::Flags,
-
/// Mode flags
mode: TermMode,
@@ -212,6 +186,12 @@ pub struct Term {
/// Template cell
template_cell: Cell,
+ /// Empty cell
+ empty_cell: Cell,
+
+ /// Text colors
+ colors: [Rgb; 16],
+
pub dirty: bool,
}
@@ -244,7 +224,13 @@ impl SizeInfo {
}
impl Term {
- pub fn new(width: f32, height: f32, cell_width: f32, cell_height: f32) -> Term {
+ pub fn new(
+ config: &Config,
+ width: f32,
+ height: f32,
+ cell_width: f32,
+ cell_height: f32
+ ) -> Term {
let size = SizeInfo {
width: width as f32,
height: height as f32,
@@ -252,17 +238,19 @@ impl Term {
cell_height: cell_height as f32,
};
-
let mut template = Cell::new(' ');
template.flags = cell::Flags::empty();
- template.bg = DEFAULT_BG;
- template.fg = DEFAULT_FG;
+ template.bg = config.bg_color();
+ template.fg = config.fg_color();
let num_cols = size.cols();
let num_lines = size.lines();
println!("num_cols, num_lines = {}, {}", num_cols, num_lines);
+ println!("bg: {:?}, fg: {:?}", template.bg, template.fg);
+ println!("colors: {:?}", config.color_list());
+
let grid = Grid::new(num_lines, num_cols, &Cell::new(' '));
let tty = tty::new(*num_lines as u8, *num_cols as u8);
@@ -284,15 +272,16 @@ impl Term {
alt: false,
cursor: Cursor::default(),
alt_cursor: Cursor::default(),
- fg: DEFAULT_FG,
- bg: DEFAULT_BG,
+ fg: config.fg_color(),
+ bg: config.bg_color(),
tty: tty,
tabs: tabs,
- attr: cell::Flags::empty(),
mode: Default::default(),
scroll_region: scroll_region,
size_info: size,
template_cell: template,
+ empty_cell: template,
+ colors: config.color_list(),
}
}
@@ -348,7 +337,7 @@ impl Term {
self.tabs[0] = false;
// Make sure bottom of terminal is clear
- let template = self.template_cell.clone();
+ let template = self.empty_cell.clone();
self.grid.clear_region((self.cursor.line).., |c| c.reset(&template));
self.alt_grid.clear_region((self.cursor.line).., |c| c.reset(&template));
@@ -384,7 +373,7 @@ impl Term {
::std::mem::swap(&mut self.cursor, &mut self.alt_cursor);
if self.alt {
- let template = self.template_cell.clone();
+ let template = self.empty_cell.clone();
self.grid.clear(|c| c.reset(&template));
}
}
@@ -397,7 +386,7 @@ impl Term {
debug_println!("scroll_down: {}", lines);
// Copy of cell template; can't have it borrowed when calling clear/scroll
- let template = self.template_cell.clone();
+ let template = self.empty_cell.clone();
// Clear `lines` lines at bottom of area
{
@@ -422,7 +411,7 @@ impl Term {
debug_println!("scroll_up: {}", lines);
// Copy of cell template; can't have it borrowed when calling clear/scroll
- let template = self.template_cell.clone();
+ let template = self.empty_cell.clone();
// Clear `lines` lines starting from origin to origin + lines
{
@@ -473,10 +462,8 @@ impl ansi::Handler for Term {
}
let cell = &mut self.grid[&self.cursor];
+ *cell = self.template_cell;
cell.c = c;
- cell.fg = self.fg;
- cell.bg = self.bg;
- cell.flags = self.attr;
self.cursor.col += 1;
}
@@ -520,7 +507,7 @@ impl ansi::Handler for Term {
// Cells were just moved out towards the end of the line; fill in
// between source and dest with blanks.
- let template = self.template_cell.clone();
+ let template = self.empty_cell.clone();
for c in &mut line[source..destination] {
c.reset(&template);
}
@@ -666,7 +653,7 @@ impl ansi::Handler for Term {
let end = start + count;
let row = &mut self.grid[self.cursor.line];
- let template = self.template_cell.clone();
+ let template = self.empty_cell.clone();
for c in &mut row[start..end] {
c.reset(&template);
}
@@ -692,7 +679,7 @@ impl ansi::Handler for Term {
}
// Clear last `count` cells in line. If deleting 1 char, need to delete 1 cell.
- let template = self.template_cell.clone();
+ let template = self.empty_cell.clone();
let end = self.size_info.cols() - count;
for c in &mut line[end..] {
c.reset(&template);
@@ -722,7 +709,7 @@ impl ansi::Handler for Term {
#[inline]
fn clear_line(&mut self, mode: ansi::LineClearMode) {
debug_println!("clear_line: {:?}", mode);
- let template = self.template_cell.clone();
+ let template = self.empty_cell.clone();
match mode {
ansi::LineClearMode::Right => {
let row = &mut self.grid[self.cursor.line];
@@ -748,7 +735,7 @@ impl ansi::Handler for Term {
#[inline]
fn clear_screen(&mut self, mode: ansi::ClearMode) {
debug_println!("clear_screen: {:?}", mode);
- let template = self.template_cell.clone();
+ let template = self.empty_cell.clone();
match mode {
ansi::ClearMode::Below => {
for row in &mut self.grid[self.cursor.line..] {
@@ -793,36 +780,36 @@ impl ansi::Handler for Term {
debug_println!("Set Attribute: {:?}", attr);
match attr {
Attr::DefaultForeground => {
- self.fg = DEFAULT_FG;
+ self.template_cell.fg = self.fg;
},
Attr::DefaultBackground => {
- self.bg = DEFAULT_BG;
+ self.template_cell.bg = self.bg;
},
Attr::Foreground(named_color) => {
- self.fg = COLORS[named_color as usize];
+ self.template_cell.fg = self.colors[named_color as usize];
},
Attr::Background(named_color) => {
- self.bg = COLORS[named_color as usize];
+ self.template_cell.bg = self.colors[named_color as usize];
},
Attr::ForegroundSpec(rgb) => {
- self.fg = rgb;
+ self.template_cell.fg = rgb;
},
Attr::BackgroundSpec(rgb) => {
- self.bg = rgb;
+ self.template_cell.bg = rgb;
},
Attr::Reset => {
- self.fg = DEFAULT_FG;
- self.bg = DEFAULT_BG;
- self.attr = cell::Flags::empty();
+ self.template_cell.fg = self.fg;
+ self.template_cell.bg = self.bg;
+ self.template_cell.flags = cell::Flags::empty();
},
- Attr::Reverse => self.attr.insert(cell::INVERSE),
- Attr::CancelReverse => self.attr.remove(cell::INVERSE),
- Attr::Bold => self.attr.insert(cell::BOLD),
- Attr::CancelBoldDim => self.attr.remove(cell::BOLD),
- Attr::Italic => self.attr.insert(cell::ITALIC),
- Attr::CancelItalic => self.attr.remove(cell::ITALIC),
- Attr::Underscore => self.attr.insert(cell::UNDERLINE),
- Attr::CancelUnderline => self.attr.remove(cell::UNDERLINE),
+ Attr::Reverse => self.template_cell.flags.insert(cell::INVERSE),
+ Attr::CancelReverse => self.template_cell.flags.remove(cell::INVERSE),
+ Attr::Bold => self.template_cell.flags.insert(cell::BOLD),
+ Attr::CancelBoldDim => self.template_cell.flags.remove(cell::BOLD),
+ Attr::Italic => self.template_cell.flags.insert(cell::ITALIC),
+ Attr::CancelItalic => self.template_cell.flags.remove(cell::ITALIC),
+ Attr::Underscore => self.template_cell.flags.insert(cell::UNDERLINE),
+ Attr::CancelUnderline => self.template_cell.flags.remove(cell::UNDERLINE),
_ => {
debug_println!("Term got unhandled attr: {:?}", attr);
}