aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorJoe Wilm <joe@jwilm.com>2016-06-09 08:06:47 -0700
committerJoe Wilm <joe@jwilm.com>2016-06-09 08:06:47 -0700
commitaff56a65a4e49d07ca00636fc33cb2141409f6b3 (patch)
treeb0bd87d5e100b494e8cababed28cedf9a9528492 /src/main.rs
parent8b1e82f31a7ecaade440b0694c0bbe5a843b48ac (diff)
downloadr-alacritty-aff56a65a4e49d07ca00636fc33cb2141409f6b3.tar.gz
r-alacritty-aff56a65a4e49d07ca00636fc33cb2141409f6b3.tar.bz2
r-alacritty-aff56a65a4e49d07ca00636fc33cb2141409f6b3.zip
Make state updates and rendering event driven
The main thing preventing this system being event driven in the past was input from the keyboard had to be polled separately from pty activity. This commit adds a thread for the window event iterator and sends them on the same channel as pty characters. With that in place, the render loop looks like - Block on 1 available input - Get all remaining available input that won't cause blocking - Render Which means that rendering is only performed on state changes. This obsoleted the need for a `dirty` flag in the Term struct.
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs179
1 files changed, 115 insertions, 64 deletions
diff --git a/src/main.rs b/src/main.rs
index 94cc1bc1..76216685 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -30,7 +30,7 @@ mod ansi;
mod term;
mod util;
-use std::sync::mpsc::TryRecvError;
+use std::sync::mpsc;
use std::collections::HashMap;
use std::io::{BufReader, Read, BufRead, Write, BufWriter};
use std::sync::Arc;
@@ -46,6 +46,64 @@ use meter::Meter;
use util::thread;
use tty::process_should_exit;
+/// Things that the render/update thread needs to respond to
+#[derive(Debug)]
+enum Event {
+ PtyChar(char),
+ Glutin(glutin::Event),
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+enum ShouldExit {
+ Yes,
+ No
+}
+
+fn handle_event<W>(event: Event,
+ writer: &mut W,
+ terminal: &mut Term,
+ pty_parser: &mut ansi::Parser) -> ShouldExit
+ where W: Write
+{
+ match event {
+ // Handle char from pty
+ Event::PtyChar(c) => pty_parser.advance(terminal, c),
+ // Handle keyboard/mouse input and other window events
+ Event::Glutin(gevent) => match gevent {
+ glutin::Event::Closed => return ShouldExit::Yes,
+ glutin::Event::ReceivedCharacter(c) => {
+ let encoded = c.encode_utf8();
+ writer.write(encoded.as_slice()).unwrap();
+ },
+ glutin::Event::KeyboardInput(state, _code, key) => {
+ match state {
+ glutin::ElementState::Pressed => {
+ match key {
+ Some(glutin::VirtualKeyCode::Up) => {
+ writer.write("\x1b[A".as_bytes()).unwrap();
+ },
+ Some(glutin::VirtualKeyCode::Down) => {
+ writer.write("\x1b[B".as_bytes()).unwrap();
+ },
+ Some(glutin::VirtualKeyCode::Left) => {
+ writer.write("\x1b[D".as_bytes()).unwrap();
+ },
+ Some(glutin::VirtualKeyCode::Right) => {
+ writer.write("\x1b[C".as_bytes()).unwrap();
+ },
+ _ => (),
+ }
+ },
+ _ => (),
+ }
+ },
+ _ => ()
+ }
+ }
+
+ ShouldExit::No
+}
+
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
pub struct Rgb {
r: u8,
@@ -130,11 +188,12 @@ fn main() {
gl::Enable(gl::MULTISAMPLE);
}
- let (chars_tx, chars_rx) = ::std::sync::mpsc::channel();
+ let (tx, rx) = mpsc::channel();
+ let reader_tx = tx.clone();
let reader_thread = thread::spawn_named("TTY Reader", move || {
for c in reader.chars() {
let c = c.unwrap();
- chars_tx.send(c).unwrap();
+ reader_tx.send(Event::PtyChar(c)).unwrap();
}
});
@@ -143,50 +202,45 @@ fn main() {
let mut pty_parser = ansi::Parser::new();
+ let window = Arc::new(window);
+ let window_ref = window.clone();
+ let input_thread = thread::spawn_named("Input Thread", move || {
+ for event in window_ref.wait_events() {
+ tx.send(Event::Glutin(event));
+ if process_should_exit() {
+ break;
+ }
+ }
+
+ });
+
'main_loop: loop {
- // Handle keyboard/mouse input and other window events
- {
- let mut writer = BufWriter::new(&writer);
- for event in window.poll_events() {
- match event {
- glutin::Event::Closed => break 'main_loop,
- glutin::Event::ReceivedCharacter(c) => {
- let encoded = c.encode_utf8();
- writer.write(encoded.as_slice()).unwrap();
- },
- glutin::Event::KeyboardInput(state, _code, key) => {
- match state {
- glutin::ElementState::Pressed => {
- match key {
- Some(glutin::VirtualKeyCode::Up) => {
- writer.write("\x1b[A".as_bytes()).unwrap();
- },
- Some(glutin::VirtualKeyCode::Down) => {
- writer.write("\x1b[B".as_bytes()).unwrap();
- },
- Some(glutin::VirtualKeyCode::Left) => {
- writer.write("\x1b[D".as_bytes()).unwrap();
- },
- Some(glutin::VirtualKeyCode::Right) => {
- writer.write("\x1b[C".as_bytes()).unwrap();
- },
- _ => (),
- }
- },
- _ => (),
- }
- },
- _ => ()
+ // Block waiting for next event
+ match rx.recv() {
+ Ok(e) => {
+ let res = handle_event(e, &mut writer, &mut terminal, &mut pty_parser);
+ if res == ShouldExit::Yes {
+ break;
}
- }
+ },
+ Err(mpsc::RecvError) => break,
}
+ // Handle Any events that have been queued
loop {
- match chars_rx.try_recv() {
- Ok(c) => pty_parser.advance(&mut terminal, c),
- Err(TryRecvError::Disconnected) => break 'main_loop,
- Err(TryRecvError::Empty) => break,
+ match rx.try_recv() {
+ Ok(e) => {
+ let res = handle_event(e, &mut writer, &mut terminal, &mut pty_parser);
+
+ if res == ShouldExit::Yes {
+ break;
+ }
+ },
+ Err(mpsc::TryRecvError::Disconnected) => break 'main_loop,
+ Err(mpsc::TryRecvError::Empty) => break,
}
+
+ // TODO make sure this doesn't block renders
}
unsafe {
@@ -194,39 +248,36 @@ fn main() {
gl::Clear(gl::COLOR_BUFFER_BIT);
}
- if terminal.dirty() {
- {
- let _sampler = meter.sampler();
-
- renderer.with_api(&props, |mut api| {
- // Draw the grid
- api.render_grid(terminal.grid(), &mut glyph_cache);
-
- // Also draw the cursor
- if !terminal.mode().contains(term::mode::TEXT_CURSOR) {
- api.render_cursor(terminal.cursor(), &mut glyph_cache);
- }
- })
- }
+ {
+ let _sampler = meter.sampler();
- // Draw render timer
- let timing = format!("{:.3} usec", meter.average());
- let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
renderer.with_api(&props, |mut api| {
- api.render_string(&timing[..], &mut glyph_cache, &color);
- });
-
- terminal.clear_dirty();
+ // Draw the grid
+ api.render_grid(terminal.grid(), &mut glyph_cache);
- window.swap_buffers().unwrap();
+ // Also draw the cursor
+ if !terminal.mode().contains(term::mode::TEXT_CURSOR) {
+ api.render_cursor(terminal.cursor(), &mut glyph_cache);
+ }
+ })
}
+ // Draw render timer
+ let timing = format!("{:.3} usec", meter.average());
+ let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
+ renderer.with_api(&props, |mut api| {
+ api.render_string(&timing[..], &mut glyph_cache, &color);
+ });
+
+ window.swap_buffers().unwrap();
+
if process_should_exit() {
break;
}
}
- reader_thread.join();
+ reader_thread.join().ok();
+ input_thread.join().ok();
println!("Goodbye");
}