aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/event.rs81
-rw-r--r--src/main.rs364
-rw-r--r--src/term.rs5
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,