diff --git a/src/grid.rs b/src/grid.rs index 42dcf210..3ce0a036 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -1,6 +1,7 @@ //! Functions for computing properties of the terminal grid use std::ops::{Index, IndexMut, Deref, DerefMut, Range, RangeTo, RangeFrom}; +use std::cmp::Ordering; use std::slice::{Iter, IterMut}; use util::Rotate; @@ -104,12 +105,55 @@ impl Grid { self.clear_region(region); } - pub fn clear_region(&mut self, region: Range) { - for row in self.raw[region].iter_mut() { - for cell in row.iter_mut() { - cell.reset(); - } + pub fn resize(&mut self, rows: usize, cols: usize) { + // Check that there's actually work to do and return early if not + if rows == self.rows && cols == self.cols { + return; } + + match self.rows.cmp(&rows) { + Ordering::Less => self.grow_rows(rows), + Ordering::Greater => self.shrink_rows(rows), + Ordering::Equal => (), + } + + match self.cols.cmp(&cols) { + Ordering::Less => self.grow_cols(cols), + Ordering::Greater => self.shrink_cols(cols), + Ordering::Equal => (), + } + } + + fn grow_rows(&mut self, rows: usize) { + for _ in self.num_rows()..rows { + self.raw.push(Row::new(self.cols)); + } + + self.rows = rows; + } + + fn shrink_rows(&mut self, rows: usize) { + while self.raw.len() != rows { + self.raw.pop(); + } + + self.rows = rows; + } + + fn grow_cols(&mut self, cols: usize) { + for row in self.rows_mut() { + row.grow(cols); + } + + self.cols = cols; + } + + fn shrink_cols(&mut self, cols: usize) { + for row in self.rows_mut() { + row.shrink(cols); + } + + self.cols = cols; } } @@ -154,6 +198,18 @@ impl Row { Row(vec![Cell::new(' '); columns]) } + pub fn grow(&mut self, cols: usize) { + while self.len() != cols { + self.push(Cell::new(' ')); + } + } + + pub fn shrink(&mut self, cols: usize) { + while self.len() != cols { + self.pop(); + } + } + pub fn cells(&self) -> Iter { self.0.iter() } @@ -221,3 +277,25 @@ impl IndexMut> for Row { &mut self.0[index] } } + +pub trait ClearRegion { + fn clear_region(&mut self, region: T); +} + +macro_rules! clear_region_impl { + ($range:ty) => { + impl ClearRegion<$range> for Grid { + fn clear_region(&mut self, region: $range) { + for row in self.raw[region].iter_mut() { + for cell in row.iter_mut() { + cell.reset(); + } + } + } + } + } +} + +clear_region_impl!(Range); +clear_region_impl!(RangeTo); +clear_region_impl!(RangeFrom); diff --git a/src/main.rs b/src/main.rs index 49f2655e..de0ea05e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,6 +65,7 @@ fn handle_event(event: Event, writer: &mut W, terminal: &mut Term, pty_parser: &mut ansi::Parser, + render_tx: &mpsc::Sender<(u32, u32)>, input_processor: &mut input::Processor) -> ShouldExit where W: Write { @@ -78,6 +79,10 @@ fn handle_event(event: Event, let encoded = c.encode_utf8(); writer.write(encoded.as_slice()).unwrap(); }, + glutin::Event::Resized(w, h) => { + terminal.resize(w as f32, h as f32); + render_tx.send((w, h)).expect("render thread active"); + }, glutin::Event::KeyboardInput(state, _code, key) => { input_processor.process(state, key, &mut WriteNotifier(writer), *terminal.mode()) }, @@ -165,6 +170,8 @@ fn main() { let window = Arc::new(window); let window_ref = window.clone(); + let (render_tx, render_rx) = mpsc::channel::<(u32, u32)>(); + let update_thread = thread::spawn_named("Update", move || { 'main_loop: loop { let mut writer = BufWriter::new(&writer); @@ -192,6 +199,7 @@ fn main() { &mut writer, &mut *terminal, &mut pty_parser, + &render_tx, &mut input_processor); if res == ShouldExit::Yes { break; @@ -205,6 +213,7 @@ fn main() { &mut writer, &mut *terminal, &mut pty_parser, + &render_tx, &mut input_processor); if res == ShouldExit::Yes { @@ -249,6 +258,15 @@ fn main() { gl::Clear(gl::COLOR_BUFFER_BIT); } + // Receive any resize events; only call gl::Viewport on last available + let mut new_size = None; + while let Ok(val) = render_rx.try_recv() { + new_size = Some(val); + } + if let Some((w, h)) = new_size.take() { + renderer.resize(w as i32, h as i32); + } + // Need scope so lock is released when swap_buffers is called { // Flag that it's time for render diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 9df4052d..8e548dd3 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -478,6 +478,18 @@ impl QuadRenderer { self.active_tex = 0; self.program = program; } + + pub fn resize(&mut self, width: i32, height: i32) { + // viewport + unsafe { + gl::Viewport(0, 0, width, height); + } + + // update projection + self.program.activate(); + self.program.update_projection(width as f32, height as f32); + self.program.deactivate(); + } } impl<'a> RenderApi<'a> { @@ -665,20 +677,25 @@ impl ShaderProgram { u_background: background, }; + shader.update_projection(width as f32, height as f32); + + shader.deactivate(); + + Ok(shader) + } + + fn update_projection(&self, width: f32, height: f32) { // set projection uniform - let ortho = cgmath::ortho(0., width as f32, 0., height as f32, -1., 1.); + let ortho = cgmath::ortho(0., width, 0., height, -1., 1.); let projection: [[f32; 4]; 4] = ortho.into(); println!("width: {}, height: {}", width, height); unsafe { - gl::UniformMatrix4fv(shader.u_projection, + gl::UniformMatrix4fv(self.u_projection, 1, gl::FALSE, projection.as_ptr() as *const _); } - shader.deactivate(); - - Ok(shader) } fn set_term_uniforms(&self, props: &term::SizeInfo) { diff --git a/src/term.rs b/src/term.rs index c11a0d08..d4168ead 100644 --- a/src/term.rs +++ b/src/term.rs @@ -2,10 +2,21 @@ use std::ops::Range; use ansi::{self, Attr}; -use grid::{self, Grid, CellFlags}; +use grid::{self, Grid, CellFlags, ClearRegion}; use tty; use ::Rgb; +/// coerce val to be between min and max +fn limit(val: T, min: T, max: T) -> T { + if val < min { + min + } else if val > max { + max + } else { + val + } +} + /// tomorrow night bright /// /// because contrast @@ -190,6 +201,67 @@ impl Term { } } + /// Resize terminal to new dimensions + pub fn resize(&mut self, width: f32, height: f32) { + let size = SizeInfo { + width: width, + height: height, + cell_width: self.size_info.cell_width, + cell_height: self.size_info.cell_height, + }; + + let old_cols = self.size_info.cols(); + let old_rows = self.size_info.rows(); + let num_cols = size.cols(); + let num_rows = size.rows(); + + self.size_info = size; + + if old_cols == num_cols && old_rows == num_rows { + return; + } + + // Scroll up to keep cursor and as much context as possible in grid. This only runs when the + // rows decreases. + self.scroll_region = 0..self.grid.num_rows(); + // XXX why is +1 required? + let row_diff = (self.cursor_y() as isize - num_rows as isize) + 1; + if row_diff > 0 { + self.scroll(row_diff); + self.cursor.advance(-row_diff as i64, 0); + } + + println!("num_cols, num_rows = {}, {}", num_cols, num_rows); + + // Resize grids to new size + self.grid.resize(num_rows, num_cols); + self.alt_grid.resize(num_rows, num_cols); + + // Ensure cursor is in-bounds + self.cursor.y = limit(self.cursor.y, 0, num_rows as u16); + self.cursor.x = limit(self.cursor.x, 0, num_cols as u16); + + // Recreate tabs list + self.tabs = (0..self.grid.num_cols()).map(|i| i % TAB_SPACES == 0) + .collect::>(); + + // Make sure bottom of terminal is clear + if row_diff > 0 { + self.grid.clear_region((self.cursor.y as usize)..); + self.alt_grid.clear_region((self.cursor.y as usize)..); + } + + // Reset scrolling region to new size + self.scroll_region = 0..self.grid.num_rows(); + + // Inform tty of new dimensions + self.tty.resize(num_rows, + num_cols, + self.size_info.width as usize, + self.size_info.height as usize); + + } + #[inline] pub fn tty(&self) -> &tty::Tty { &self.tty