diff options
-rw-r--r-- | src/event.rs | 81 | ||||
-rw-r--r-- | src/main.rs | 364 | ||||
-rw-r--r-- | src/term.rs | 5 |
3 files changed, 244 insertions, 206 deletions
diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 00000000..85487d3a --- /dev/null +++ b/src/event.rs @@ -0,0 +1,81 @@ +//! Process window events +use std::sync::{Arc, mpsc}; +use std; + +use glutin; + +use input; +use sync::PriorityMutex; +use term::Term; +use tty::process_should_exit; + +/// The event processor +pub struct Processor<'a, W: 'a> { + writer: &'a mut W, + input_processor: input::Processor, + terminal: Arc<PriorityMutex<Term>>, + resize_tx: mpsc::Sender<(u32, u32)>, +} + +impl<'a, W> Processor<'a, W> + where W: std::io::Write +{ + /// Create a new event processor + /// + /// Takes a writer which is expected to be hooked up to the write end of a + /// pty. + pub fn new(writer: &mut W, + terminal: Arc<PriorityMutex<Term>>, + resize_tx: mpsc::Sender<(u32, u32)>) + -> Processor<W> + { + Processor { + writer: writer, + terminal: terminal, + input_processor: input::Processor::new(), + resize_tx: resize_tx, + } + } + + fn handle_event(&mut self, event: glutin::Event) { + match event { + glutin::Event::Closed => panic!("window closed"), // TODO ... + glutin::Event::ReceivedCharacter(c) => { + match c { + // Ignore BACKSPACE and DEL. These are handled specially. + '\u{8}' | '\u{7f}' => (), + // OSX arrow keys send invalid characters; ignore. + '\u{f700}' | '\u{f701}' | '\u{f702}' | '\u{f703}' => (), + _ => { + let encoded = c.encode_utf8(); + self.writer.write(encoded.as_slice()).unwrap(); + } + } + }, + glutin::Event::Resized(w, h) => { + self.resize_tx.send((w, h)).expect("send new size"); + }, + glutin::Event::KeyboardInput(state, _code, key, mods) => { + // Acquire term lock + let terminal = self.terminal.lock_high(); + + self.input_processor.process(state, key, mods, + &mut input::WriteNotifier(self.writer), + *terminal.mode()); + }, + _ => (), + } + } + + /// Process at least one event and handle any additional queued events. + pub fn process_events(&mut self, window: &glutin::Window) { + for event in window.wait_events() { + self.handle_event(event); + break; + } + + for event in window.poll_events() { + self.handle_event(event); + } + } +} diff --git a/src/main.rs b/src/main.rs index 5c1bca68..b148d31f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,6 +44,7 @@ mod meter; pub mod config; mod input; mod index; +mod event; mod tty; pub mod ansi; mod term; @@ -54,7 +55,7 @@ mod sync; use std::sync::{mpsc, Arc}; use std::sync::atomic::{AtomicBool, Ordering}; -use parking_lot::{Condvar, Mutex}; +use parking_lot::{Condvar, Mutex, MutexGuard}; use config::Config; use io::{Utf8Chars, Utf8CharsError}; @@ -68,29 +69,6 @@ use util::thread; /// Channel used by resize handling on mac static mut resize_sender: Option<mpsc::Sender<(u32, u32)>> = None; -struct Waiter { - mutex: Mutex<bool>, - cond: Condvar, -} - -impl Waiter { - pub fn new() -> Waiter { - Waiter { - mutex: Mutex::new(false), - cond: Condvar::new(), - } - } - - pub fn notify(&self) { - self.cond.notify_one(); - } - - pub fn wait(&self) { - let mut guard = self.mutex.lock(); - self.cond.wait(&mut guard); - } -} - /// Resize handling for Mac fn window_resize_handler(width: u32, height: u32) { unsafe { @@ -140,6 +118,12 @@ fn main() { println!("device_pixel_ratio: {}", dpr); + for event in window.wait_events() { + if let glutin::Event::Refresh = event { + break; + } + } + let _ = unsafe { window.make_current() }; unsafe { gl::Viewport(0, 0, width as i32, height as i32); @@ -150,9 +134,6 @@ fn main() { let rasterizer = font::Rasterizer::new(dpi.x(), dpi.y(), dpr); - let waiter = Arc::new(Waiter::new()); - let dirty = Arc::new(AtomicBool::new(false)); - // Create renderer let mut renderer = QuadRenderer::new(width, height); @@ -189,212 +170,185 @@ fn main() { } let terminal = Arc::new(PriorityMutex::new(terminal)); - - let pty_reader_thread = spawn_pty_reader(terminal.clone(), - reader, - waiter.clone(), - dirty.clone(), - window.create_window_proxy()); - let window = Arc::new(window); - let _ = window.clear_current(); - let render_thread = spawn_renderer(window.clone(), - terminal.clone(), - renderer, - glyph_cache, - render_timer, - rx, - waiter.clone(), - dirty); - - handle_window_events(&mut writer, terminal, window); + let pty_reader = PtyReader::spawn(terminal.clone(), reader, window.create_window_proxy()); + + // Wraps a renderer and gives simple draw() api. + let mut display = Display::new(window.clone(), + terminal.clone(), + renderer, + glyph_cache, + render_timer, + rx); + + // Event processor + let mut processor = event::Processor::new(&mut writer, terminal.clone(), tx); + + // Main loop + loop { + // Wait for something to happen + processor.process_events(&window); + + // Maybe draw the terminal + let terminal = terminal.lock_high(); + if terminal.dirty { + println!("dirty!"); + display.draw(terminal); + } - pty_reader_thread.join().ok(); + if process_should_exit() { + break; + } + } - waiter.notify(); - render_thread.join().ok(); + // shutdown + pty_reader.join().ok(); println!("Goodbye"); } -/// Handle window events until the application should close -fn handle_window_events<W>(writer: &mut W, - terminal: Arc<PriorityMutex<Term>>, - window: Arc<glutin::Window>) - where W: std::io::Write, -{ - let mut input_processor = input::Processor::new(); - let resize_tx = unsafe { resize_sender.as_ref().cloned().unwrap() }; +struct PtyReader; + +impl PtyReader { + pub fn spawn<R>(terminal: Arc<PriorityMutex<Term>>, + mut pty: R, + proxy: ::glutin::WindowProxy) + -> std::thread::JoinHandle<()> + where R: std::io::Read + Send + 'static + { + thread::spawn_named("pty reader", move || { + let mut buf = [0u8; 4096]; + let mut start = 0; + let mut pty_parser = ansi::Parser::new(); + + loop { + if let Ok(got) = pty.read(&mut buf[start..]) { + let mut remain = 0; + + // if `start` is nonzero, then actual bytes in buffer is > `got` by `start` bytes. + let end = start + got; + { + let mut terminal = terminal.lock_low(); + terminal.dirty = true; + for c in Utf8Chars::new(&buf[..end]) { + match c { + Ok(c) => pty_parser.advance(&mut *terminal, c), + Err(err) => match err { + Utf8CharsError::IncompleteUtf8(unused) => { + remain = unused; + break; + }, + _ => panic!("{}", err), + } + } + } + } - for event in window.wait_events() { - match event { - glutin::Event::Closed => break, - glutin::Event::ReceivedCharacter(c) => { - match c { - // Ignore BACKSPACE and DEL. These are handled specially. - '\u{8}' | '\u{7f}' => (), - // OSX arrow keys send invalid characters; ignore. - '\u{f700}' | '\u{f701}' | '\u{f702}' | '\u{f703}' => (), - _ => { - let encoded = c.encode_utf8(); - writer.write(encoded.as_slice()).unwrap(); + println!("updated terminal"); + proxy.wakeup_event_loop(); + + // Move any leftover bytes to front of buffer + for i in 0..remain { + buf[i] = buf[end - (remain - i)]; } - } - }, - glutin::Event::Resized(w, h) => { - resize_tx.send((w, h)).expect("send new size"); - }, - glutin::Event::KeyboardInput(state, _code, key, mods) => { - // Acquire term lock - let terminal = terminal.lock_high(); - - input_processor.process(state, - key, - mods, - &mut input::WriteNotifier(writer), - *terminal.mode()) - }, - glutin::Event::Awakened => { - if process_should_exit() { - println!("input thread exitting"); + start = remain; + } else { break; } } - _ => (), - } + println!("pty reader stopped"); + }) } } -fn spawn_pty_reader<R>(terminal: Arc<PriorityMutex<Term>>, - mut pty: R, - waiter: Arc<Waiter>, - dirty: Arc<AtomicBool>, - proxy: ::glutin::WindowProxy) -> std::thread::JoinHandle<()> - where R: std::io::Read + Send + 'static, -{ - thread::spawn_named("pty reader", move || { - let mut buf = [0u8; 4096]; - let mut start = 0; - let mut pty_parser = ansi::Parser::new(); - - loop { - if let Ok(got) = pty.read(&mut buf[start..]) { - let mut remain = 0; - - // if `start` is nonzero, then actual bytes in buffer is > `got` by `start` bytes. - let end = start + got; - { - let mut terminal = terminal.lock_low(); - for c in Utf8Chars::new(&buf[..end]) { - match c { - Ok(c) => pty_parser.advance(&mut *terminal, c), - Err(err) => match err { - Utf8CharsError::IncompleteUtf8(unused) => { - remain = unused; - break; - }, - _ => panic!("{}", err), - } - } - } - } - - dirty.store(true, Ordering::Release); - waiter.notify(); - - // Move any leftover bytes to front of buffer - for i in 0..remain { - buf[i] = buf[end - (remain - i)]; - } - start = remain; - } else { - proxy.wakeup_event_loop(); - break; - } - } - }) +struct Display { + window: Arc<glutin::Window>, + terminal_mutex: Arc<PriorityMutex<Term>>, + renderer: QuadRenderer, + glyph_cache: GlyphCache, + render_timer: bool, + rx: mpsc::Receiver<(u32, u32)>, + meter: Meter, } -fn spawn_renderer(window: Arc<glutin::Window>, - terminal_mutex: Arc<PriorityMutex<Term>>, - mut renderer: QuadRenderer, - mut glyph_cache: GlyphCache, - render_timer: bool, - rx: mpsc::Receiver<(u32, u32)>, - waiter: Arc<Waiter>, - dirty: Arc<AtomicBool>) -> std::thread::JoinHandle<()> { - thread::spawn_named("render", move || { - unsafe { - let _ = window.make_current(); +impl Display { + pub fn new(window: Arc<glutin::Window>, + terminal_mutex: Arc<PriorityMutex<Term>>, + renderer: QuadRenderer, + glyph_cache: GlyphCache, + render_timer: bool, + rx: mpsc::Receiver<(u32, u32)>) + -> Display + { + Display { + window: window, + terminal_mutex: terminal_mutex, + renderer: renderer, + glyph_cache: glyph_cache, + render_timer: render_timer, + rx: rx, + meter: Meter::new(), } - let mut meter = Meter::new(); - - 'render_loop: loop { - // Scope ensures terminal lock isn't held when calling swap_buffers - { - // Acquire term lock - let mut terminal = terminal_mutex.lock_high(); - - if !dirty.load(Ordering::Acquire) { - drop(terminal); - waiter.wait(); - terminal = terminal_mutex.lock_high(); - } - - dirty.store(false, Ordering::Release); + } - // Resize events new_size and are handled outside the poll_events - // iterator. This has the effect of coalescing multiple resize - // events into one. - let mut new_size = None; + /// Draw the screen + /// + /// A reference to Term whose state is being drawn must be provided. + /// + /// This call may block if vsync is enabled + pub fn draw(&mut self, mut terminal: MutexGuard<Term>) { + terminal.dirty = false; - unsafe { - gl::ClearColor(0.0, 0.0, 0.00, 1.0); - gl::Clear(gl::COLOR_BUFFER_BIT); - } + // Resize events new_size and are handled outside the poll_events + // iterator. This has the effect of coalescing multiple resize + // events into one. + let mut new_size = None; - // Check for any out-of-band resize events (mac only) - while let Ok(sz) = rx.try_recv() { - new_size = Some(sz); - } + // TODO should be built into renderer + unsafe { + gl::ClearColor(0.0, 0.0, 0.00, 1.0); + gl::Clear(gl::COLOR_BUFFER_BIT); + } - // Receive any resize events; only call gl::Viewport on last - // available - if let Some((w, h)) = new_size.take() { - terminal.resize(w as f32, h as f32); - renderer.resize(w as i32, h as i32); - } + // Check for any out-of-band resize events (mac only) + while let Ok(sz) = self.rx.try_recv() { + new_size = Some(sz); + } - { - // Draw grid - { - let _sampler = meter.sampler(); + // Receive any resize events; only call gl::Viewport on last + // available + if let Some((w, h)) = new_size.take() { + terminal.resize(w as f32, h as f32); + self.renderer.resize(w as i32, h as i32); + } - let size_info = terminal.size_info().clone(); - renderer.with_api(&size_info, |mut api| { - // Draw the grid - api.render_grid(&terminal.render_grid(), &mut glyph_cache); - }); - } + { + let glyph_cache = &mut self.glyph_cache; + // Draw grid + { + let _sampler = self.meter.sampler(); - // Draw render timer - if render_timer { - let timing = format!("{:.3} usec", meter.average()); - let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 }; - renderer.with_api(terminal.size_info(), |mut api| { - api.render_string(&timing[..], &mut glyph_cache, &color); - }); - } - } + 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); + }); } - window.swap_buffers().unwrap(); - - if process_should_exit() { - break; - } else { + // Draw render timer + if self.render_timer { + 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); + }); } } - }) + + // Unlock the terminal mutex + drop(terminal); + println!("swap buffers"); + self.window.swap_buffers().unwrap(); + } } diff --git a/src/term.rs b/src/term.rs index f7906122..62a98400 100644 --- a/src/term.rs +++ b/src/term.rs @@ -209,7 +209,9 @@ pub struct Term { size_info: SizeInfo, /// Template cell - template_cell: Cell + template_cell: Cell, + + pub dirty: bool, } /// Terminal size info @@ -275,6 +277,7 @@ impl Term { let scroll_region = Line(0)..grid.num_lines(); Term { + dirty: true, grid: grid, alt_grid: alt, alt: false, |