diff options
author | Joe Wilm <joe@jwilm.com> | 2016-10-23 15:37:06 -0700 |
---|---|---|
committer | Joe Wilm <joe@jwilm.com> | 2016-10-23 15:37:06 -0700 |
commit | 5876b4bf7a88a59482aefcc85e04a9ef6ecfed74 (patch) | |
tree | 8e55fbf061383569fca610418e02368b05a29fc2 /src | |
parent | ea07f03ac901c366ed31da540711b84ca9e75602 (diff) | |
download | alacritty-5876b4bf7a88a59482aefcc85e04a9ef6ecfed74.tar.gz alacritty-5876b4bf7a88a59482aefcc85e04a9ef6ecfed74.zip |
Proof of concept live reloading for colors
The architecture here is really poor. Need to move file watching into a
dedicated location and probably have an spmc broadcast queue. other
modules besides rendering will care about config reloading in the
future.
Diffstat (limited to 'src')
-rw-r--r-- | src/ansi.rs | 12 | ||||
-rw-r--r-- | src/config.rs | 6 | ||||
-rw-r--r-- | src/main.rs | 17 | ||||
-rw-r--r-- | src/renderer/mod.rs | 139 | ||||
-rw-r--r-- | src/term.rs | 84 |
5 files changed, 157 insertions, 101 deletions
diff --git a/src/ansi.rs b/src/ansi.rs index 45cc66f9..c419d7e5 100644 --- a/src/ansi.rs +++ b/src/ansi.rs @@ -337,6 +337,10 @@ pub enum Color { BrightCyan, /// Bright white BrightWhite, + /// The foreground color + Foreground, + /// The background color + Background, } /// Terminal character attributes @@ -384,10 +388,6 @@ pub enum Attr { Background(Color), /// Set specific background color BackgroundSpec(Rgb), - /// Set default foreground - DefaultForeground, - /// Set default background - DefaultBackground, } impl<'a, H: Handler + TermInfo + 'a> vte::Perform for Performer<'a, H> { @@ -584,7 +584,7 @@ impl<'a, H: Handler + TermInfo + 'a> vte::Perform for Performer<'a, H> { break; } }, - 39 => Attr::DefaultForeground, + 39 => Attr::Foreground(Color::Foreground), 40 => Attr::Background(Color::Black), 41 => Attr::Background(Color::Red), 42 => Attr::Background(Color::Green), @@ -600,7 +600,7 @@ impl<'a, H: Handler + TermInfo + 'a> vte::Perform for Performer<'a, H> { break; } }, - 49 => Attr::DefaultBackground, + 49 => Attr::Background(Color::Background), 90 => Attr::Foreground(Color::BrightBlack), 91 => Attr::Foreground(Color::BrightRed), 92 => Attr::Foreground(Color::BrightGreen), diff --git a/src/config.rs b/src/config.rs index 56f96e80..27fcf3ca 100644 --- a/src/config.rs +++ b/src/config.rs @@ -259,7 +259,7 @@ 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; 16] { + pub fn color_list(&self) -> [Rgb; 18] { let colors = &self.colors; [ @@ -282,6 +282,10 @@ impl Config { colors.bright.magenta, colors.bright.cyan, colors.bright.white, + + // Foreground and background + colors.primary.foreground, + colors.primary.background, ] } diff --git a/src/main.rs b/src/main.rs index 9639fd29..9042494d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -112,6 +112,7 @@ pub struct Rgb { } mod gl { + #![allow(non_upper_case_globals)] include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs")); } @@ -155,7 +156,7 @@ fn main() { let rasterizer = font::Rasterizer::new(dpi.x(), dpi.y(), dpr); // Create renderer - let mut renderer = QuadRenderer::new(width, height); + let mut renderer = QuadRenderer::new(&config, width, height); // Initialize glyph cache let glyph_cache = { @@ -180,7 +181,6 @@ fn main() { println!("Cell Size: ({} x {})", cell_width, cell_height); let terminal = Term::new( - &config, width as f32, height as f32, cell_width as f32, @@ -295,11 +295,6 @@ impl Display { // events into one. let mut new_size = None; - // TODO should be built into renderer - unsafe { - gl::ClearColor(self.clear_red, self.clear_blue, self.clear_green, 1.0); - gl::Clear(gl::COLOR_BUFFER_BIT); - } // Check for any out-of-band resize events (mac only) while let Ok(sz) = self.rx.try_recv() { @@ -322,18 +317,16 @@ impl Display { let size_info = terminal.size_info().clone(); self.renderer.with_api(&size_info, |mut api| { // Draw the grid - let bg = terminal.bg; - api.render_grid(&bg, &terminal.render_grid(), glyph_cache); + api.render_grid(&terminal.render_grid(), glyph_cache); }); } // Draw render timer if self.render_timer { let timing = format!("{:.3} usec", self.meter.average()); - let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 }; + let color = ::term::cell::Color::Rgb(Rgb { r: 0xd5, g: 0x4e, b: 0x53 }); self.renderer.with_api(terminal.size_info(), |mut api| { - let bg = terminal.bg; - api.render_string(&bg, &timing[..], glyph_cache, &color); + api.render_string(&timing[..], glyph_cache, &color); }); } } diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 7ddf1d01..959b3d4f 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -20,6 +20,7 @@ use std::path::{PathBuf}; use std::ptr; use std::sync::Arc; use std::sync::atomic::{Ordering, AtomicBool}; +use std::sync::mpsc; use cgmath; use font::{self, Rasterizer, RasterizedGlyph, FontDesc, GlyphKey, FontKey}; @@ -42,6 +43,38 @@ pub trait LoadGlyph { fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph; } +enum Msg { + ConfigReload(Config), + ShaderReload, +} + +/// Colors! +/// +/// FIXME this is obviously bad; need static for reload logic for now. Hacking something in with +/// minimal effort and will improve later. +/// +/// Only renderer is allowed to access this to prevent race conditions +static mut COLORS: [Rgb; 18] = [ + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, + Rgb { r: 0, g: 0, b: 0 }, +]; + /// Text drawing program /// /// Uniforms are prefixed with "u", and vertex attributes are prefixed with "a". @@ -222,7 +255,6 @@ struct InstanceData { #[derive(Debug)] pub struct QuadRenderer { program: ShaderProgram, - should_reload: Arc<AtomicBool>, vao: GLuint, vbo: GLuint, ebo: GLuint, @@ -230,6 +262,7 @@ pub struct QuadRenderer { atlas: Vec<Atlas>, active_tex: GLuint, batch: Batch, + rx: mpsc::Receiver<Msg>, } #[derive(Debug)] @@ -272,6 +305,18 @@ impl Batch { self.tex = glyph.tex_id; } + // TODO move colors list to uniform buffer and do this indexing in vertex shader + let fg = match cell.fg { + ::term::cell::Color::Rgb(rgb) => rgb, + ::term::cell::Color::Ansi(ansi) => unsafe { COLORS[ansi as usize] }, + }; + + // TODO move colors list to uniform buffer and do this indexing in vertex shader + let bg = match cell.bg { + ::term::cell::Color::Rgb(rgb) => rgb, + ::term::cell::Color::Ansi(ansi) => unsafe { COLORS[ansi as usize] }, + }; + let mut instance = InstanceData { col: col, row: row, @@ -286,23 +331,23 @@ impl Batch { uv_width: glyph.uv_width, uv_height: glyph.uv_height, - r: cell.fg.r as f32, - g: cell.fg.g as f32, - b: cell.fg.b as f32, + r: fg.r as f32, + g: fg.g as f32, + b: fg.b as f32, - bg_r: cell.bg.r as f32, - bg_g: cell.bg.g as f32, - bg_b: cell.bg.b as f32, + bg_r: bg.r as f32, + bg_g: bg.g as f32, + bg_b: bg.b as f32, }; if cell.flags.contains(cell::INVERSE) { - instance.r = cell.bg.r as f32; - instance.g = cell.bg.g as f32; - instance.b = cell.bg.b as f32; + instance.r = bg.r as f32; + instance.g = bg.g as f32; + instance.b = bg.b as f32; - instance.bg_r = cell.fg.r as f32; - instance.bg_g = cell.fg.g as f32; - instance.bg_b = cell.fg.b as f32; + instance.bg_r = fg.r as f32; + instance.bg_g = fg.g as f32; + instance.bg_b = fg.b as f32; } self.instances.push(instance); @@ -345,7 +390,7 @@ const ATLAS_SIZE: i32 = 1024; impl QuadRenderer { // TODO should probably hand this a transform instead of width/height - pub fn new(width: u32, height: u32) -> QuadRenderer { + pub fn new(config: &Config, width: u32, height: u32) -> QuadRenderer { let program = ShaderProgram::new(width, height).unwrap(); let mut vao: GLuint = 0; @@ -448,11 +493,14 @@ impl QuadRenderer { let should_reload = Arc::new(AtomicBool::new(false)); let should_reload2 = should_reload.clone(); + let (msg_tx, msg_rx) = mpsc::channel(); + ::std::thread::spawn(move || { - let (tx, rx) = ::std::sync::mpsc::channel(); + let (tx, rx) = mpsc::channel(); let mut watcher = Watcher::new(tx).unwrap(); watcher.watch(TEXT_SHADER_F_PATH).expect("watch fragment shader"); watcher.watch(TEXT_SHADER_V_PATH).expect("watch vertex shader"); + watcher.watch("/home/jwilm/.alacritty.yml").expect("watch alacritty yml"); loop { let event = rx.recv().expect("watcher event"); @@ -468,10 +516,17 @@ impl QuadRenderer { if let Err(err) = watcher.watch(path) { println!("failed to establish watch on {:?}: {:?}", path, err); } - } - // This is last event we see after saving in vim - should_reload2.store(true, Ordering::Relaxed); + if path == ::std::path::Path::new("/home/jwilm/.alacritty.yml") { + if let Ok(config) = Config::load() { + msg_tx.send(Msg::ConfigReload(config)) + .expect("msg send ok"); + }; + } else { + msg_tx.send(Msg::ShaderReload) + .expect("msg send ok"); + } + } } } } @@ -479,7 +534,6 @@ impl QuadRenderer { let mut renderer = QuadRenderer { program: program, - should_reload: should_reload, vao: vao, vbo: vbo, ebo: ebo, @@ -487,8 +541,13 @@ impl QuadRenderer { atlas: Vec::new(), active_tex: 0, batch: Batch::new(), + rx: msg_rx, }; + unsafe { + COLORS = config.color_list(); + } + let atlas = Atlas::new(ATLAS_SIZE); renderer.atlas.push(atlas); @@ -498,8 +557,17 @@ impl QuadRenderer { pub fn with_api<F, T>(&mut self, props: &term::SizeInfo, func: F) -> T where F: FnOnce(RenderApi) -> T { - if self.should_reload.load(Ordering::Relaxed) { - self.reload_shaders(props.width as u32, props.height as u32); + while let Ok(msg) = self.rx.try_recv() { + match msg { + Msg::ConfigReload(config) => { + unsafe { + COLORS = config.color_list(); + } + }, + Msg::ShaderReload => { + self.reload_shaders(props.width as u32, props.height as u32); + } + } } unsafe { @@ -544,7 +612,6 @@ impl QuadRenderer { } pub fn reload_shaders(&mut self, width: u32, height: u32) { - self.should_reload.store(false, Ordering::Relaxed); let program = match ShaderProgram::new(width, height) { Ok(program) => program, Err(err) => { @@ -612,10 +679,9 @@ impl<'a> RenderApi<'a> { /// optimization. pub fn render_string( &mut self, - bg: &Rgb, s: &str, glyph_cache: &mut GlyphCache, - color: &Rgb, + color: &::term::cell::Color, ) { let row = 40.0; let mut col = 100.0; @@ -630,8 +696,8 @@ impl<'a> RenderApi<'a> { if let Some(glyph) = glyph_cache.get(&glyph_key, self) { let cell = Cell { c: c, - fg: *color, - bg: *bg, + fg: color.clone(), + bg: cell::Color::Rgb(Rgb { r: 0, g: 0, b: 0}), flags: cell::INVERSE, }; self.add_render_item(row, col, &cell, glyph); @@ -658,12 +724,27 @@ impl<'a> RenderApi<'a> { } } - pub fn render_grid(&mut self, bg: &Rgb, grid: &Grid<Cell>, glyph_cache: &mut GlyphCache) { + pub fn render_grid( + &mut self, + grid: &Grid<Cell>, + glyph_cache: &mut GlyphCache + ) { + // TODO should be built into renderer + let color = unsafe { COLORS[::ansi::Color::Background as usize] }; + unsafe { + gl::ClearColor( + color.r as f32 / 255.0, + color.g as f32 / 255.0, + color.b as f32 / 255.0, + 1.0 + ); + gl::Clear(gl::COLOR_BUFFER_BIT); + } + for (i, line) in grid.lines().enumerate() { for (j, cell) in line.cells().enumerate() { // Skip empty cells - if cell.c == ' ' && - cell.bg == *bg && + if cell.c == ' ' && cell.bg == cell::Color::Ansi(::ansi::Color::Background) && !cell.flags.contains(cell::INVERSE) { continue; diff --git a/src/term.rs b/src/term.rs index 898251d7..a2b6aa3c 100644 --- a/src/term.rs +++ b/src/term.rs @@ -21,9 +21,7 @@ use ansi::{self, Attr, Handler}; use grid::{Grid, ClearRegion}; use index::{Cursor, Column, Line}; use tty; -use config::Config; - -use ::Rgb; +use ansi::Color; /// RAII type which manages grid state for render /// @@ -90,20 +88,26 @@ pub mod cell { } } - #[derive(Clone, Debug, Copy)] + #[derive(Debug, Clone, PartialEq, Eq)] + pub enum Color { + Rgb(Rgb), + Ansi(::ansi::Color), + } + + #[derive(Clone, Debug)] pub struct Cell { pub c: char, - pub fg: Rgb, - pub bg: Rgb, + pub fg: Color, + pub bg: Color, pub flags: Flags, } impl Cell { - pub fn new(c: char) -> Cell { + pub fn new(c: char, fg: Color, bg: Color) -> Cell { Cell { c: c.into(), - bg: Default::default(), - fg: Default::default(), + bg: bg, + fg: fg, flags: Flags::empty(), } } @@ -111,13 +115,7 @@ pub mod cell { #[inline] pub fn reset(&mut self, template: &Cell) { // memcpy template to self - unsafe { - ::std::ptr::copy_nonoverlapping( - template as *const Cell, - self as *mut Cell, - 1 - ); - } + *self = template.clone(); } } } @@ -165,12 +163,6 @@ pub struct Term { /// Alt cursor alt_cursor: Cursor, - /// Active foreground color - pub fg: Rgb, - - /// Active background color - pub bg: Rgb, - /// Tabstops tabs: Vec<bool>, @@ -189,9 +181,6 @@ pub struct Term { /// Empty cell empty_cell: Cell, - /// Text colors - colors: [Rgb; 16], - pub dirty: bool, } @@ -225,7 +214,6 @@ impl SizeInfo { impl Term { pub fn new( - config: &Config, width: f32, height: f32, cell_width: f32, @@ -238,20 +226,18 @@ impl Term { cell_height: cell_height as f32, }; - let mut template = Cell::new(' '); - template.flags = cell::Flags::empty(); - template.bg = config.bg_color(); - template.fg = config.fg_color(); + let template = Cell::new( + ' ', + cell::Color::Ansi(Color::Foreground), + cell::Color::Ansi(Color::Background) + ); 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 grid = Grid::new(num_lines, num_cols, &template); let tty = tty::new(*num_lines as u8, *num_cols as u8); tty.resize(*num_lines as usize, *num_cols as usize, size.width as usize, size.height as usize); @@ -272,16 +258,13 @@ impl Term { alt: false, cursor: Cursor::default(), alt_cursor: Cursor::default(), - fg: config.fg_color(), - bg: config.bg_color(), tty: tty, tabs: tabs, mode: Default::default(), scroll_region: scroll_region, size_info: size, - template_cell: template, + template_cell: template.clone(), empty_cell: template, - colors: config.color_list(), } } @@ -323,8 +306,9 @@ impl Term { println!("num_cols, num_lines = {}, {}", num_cols, num_lines); // Resize grids to new size - self.grid.resize(num_lines, num_cols, &Cell::new(' ')); - self.alt_grid.resize(num_lines, num_cols, &Cell::new(' ')); + let template = self.template_cell.clone(); + self.grid.resize(num_lines, num_cols, &template); + self.alt_grid.resize(num_lines, num_cols, &template); // Ensure cursor is in-bounds self.cursor.line = limit(self.cursor.line, Line(0), num_lines); @@ -462,7 +446,7 @@ impl ansi::Handler for Term { } let cell = &mut self.grid[&self.cursor]; - *cell = self.template_cell; + *cell = self.template_cell.clone(); cell.c = c; self.cursor.col += 1; } @@ -779,27 +763,21 @@ impl ansi::Handler for Term { fn terminal_attribute(&mut self, attr: Attr) { debug_println!("Set Attribute: {:?}", attr); match attr { - Attr::DefaultForeground => { - self.template_cell.fg = self.fg; - }, - Attr::DefaultBackground => { - self.template_cell.bg = self.bg; - }, Attr::Foreground(named_color) => { - self.template_cell.fg = self.colors[named_color as usize]; + self.template_cell.fg = cell::Color::Ansi(named_color); }, Attr::Background(named_color) => { - self.template_cell.bg = self.colors[named_color as usize]; + self.template_cell.bg = cell::Color::Ansi(named_color); }, Attr::ForegroundSpec(rgb) => { - self.template_cell.fg = rgb; + self.template_cell.fg = cell::Color::Rgb(rgb); }, Attr::BackgroundSpec(rgb) => { - self.template_cell.bg = rgb; + self.template_cell.bg = cell::Color::Rgb(rgb); }, Attr::Reset => { - self.template_cell.fg = self.fg; - self.template_cell.bg = self.bg; + self.template_cell.fg = cell::Color::Ansi(Color::Foreground); + self.template_cell.bg = cell::Color::Ansi(Color::Background); self.template_cell.flags = cell::Flags::empty(); }, Attr::Reverse => self.template_cell.flags.insert(cell::INVERSE), |