Add event polling to Blt backend

Basically simulates a constant set_fps(30)
This commit is contained in:
Alexandre Bury 2018-05-20 10:42:52 -07:00
parent 05e1212a50
commit 0b6e5b6ad4
4 changed files with 60 additions and 51 deletions

View File

@ -15,7 +15,7 @@ use std::collections::HashSet;
use theme::{BaseColor, Color, ColorPair, Effect};
use vec::Vec2;
use chan;
use std::thread;
use std::time::{Duration, Instant};
enum ColorRole {
Foreground,
@ -23,28 +23,38 @@ enum ColorRole {
}
pub struct Backend {
}
struct InputParser {
event_sink: chan::Sender<Event>,
buttons_pressed: HashSet<MouseButton>,
mouse_position: Vec2,
}
impl InputParser {
fn new(event_sink: chan::Sender<Event>) -> Self {
InputParser {
event_sink,
impl Backend {
pub fn init() -> Box<backend::Backend> {
terminal::open("Cursive", 80, 24);
terminal::set(terminal::config::Window::empty().resizeable(true));
terminal::set(vec![
terminal::config::InputFilter::Group {
group: terminal::config::InputFilterGroup::Keyboard,
both: false,
},
terminal::config::InputFilter::Group {
group: terminal::config::InputFilterGroup::Mouse,
both: true,
},
]);
let c = Backend {
buttons_pressed: HashSet::new(),
mouse_position: Vec2::zero(),
}
};
Box::new(c)
}
fn parse_next(&mut self) {
fn parse_next(&mut self) -> Option<Event> {
// TODO: we could add backend-specific controls here.
// Ex: ctrl+mouse wheel cause window cellsize to change
let event = if let Some(ev) = terminal::wait_event() {
terminal::read_event().map(|ev| {
match ev {
BltEvent::Close => Event::Exit,
BltEvent::Resize { .. } => Event::WindowResize,
@ -94,10 +104,7 @@ impl InputParser {
Event::Refresh
}
}
} else {
Event::Refresh
};
self.event_sink.send(event);
})
}
fn blt_keycode_to_ev(
@ -223,28 +230,6 @@ impl InputParser {
}
}
impl Backend {
pub fn init() -> Box<backend::Backend> {
terminal::open("Cursive", 80, 24);
terminal::set(terminal::config::Window::empty().resizeable(true));
terminal::set(vec![
terminal::config::InputFilter::Group {
group: terminal::config::InputFilterGroup::Keyboard,
both: false,
},
terminal::config::InputFilter::Group {
group: terminal::config::InputFilterGroup::Mouse,
both: true,
},
]);
let c = Backend {};
Box::new(c)
}
}
impl backend::Backend for Backend {
fn finish(&mut self) {
terminal::close();
@ -320,14 +305,19 @@ impl backend::Backend for Backend {
terminal::print_xy(pos.x as i32, pos.y as i32, text);
}
fn start_input_thread(&mut self, event_sink: chan::Sender<Event>) {
let mut parser = InputParser::new(event_sink);
fn start_input_thread(&mut self, _event_sink: chan::Sender<Event>) {
}
thread::spawn(move || {
loop {
parser.parse_next();
fn prepare_input(&mut self, event_sink: &chan::Sender<Event>, timeout: Duration) {
// Wait for up to `timeout_ms`.
let start = Instant::now();
while start.elapsed() < timeout {
if let Some(event) = self.parse_next() {
event_sink.send(event);
return;
}
});
}
event_sink.send(Event::Refresh);
}
}

View File

@ -4,7 +4,7 @@ use theme;
use event;
use vec::Vec2;
use std::thread;
use std::time::Duration;
use chan;
@ -32,8 +32,11 @@ impl backend::Backend for Backend {
(1, 1).into()
}
fn start_input_thread(&mut self, event_sink: chan::Sender<event::Event>) {
thread::spawn(move || event_sink.send(event::Event::Exit));
fn start_input_thread(&mut self, _event_sink: chan::Sender<event::Event>) {
}
fn prepare_input(&mut self, event_sink: &chan::Sender<event::Event>, _timeout: Duration) {
event_sink.send(event::Event::Exit)
}
fn print_at(&self, _: Vec2, _: &str) {}

View File

@ -14,6 +14,8 @@ use chan::Sender;
use vec::Vec2;
use std::time::Duration;
pub mod dummy;
pub mod termion;
@ -29,8 +31,20 @@ pub trait Backend {
/// This should clear any state in the terminal.
fn finish(&mut self);
/// Starts a thread to collect input and send it to the given channel.
fn start_input_thread(&mut self, event_sink: Sender<event::Event>);
/// Prepares the backend to collect input.
///
/// This is only required for non-thread-safe backends like BearLibTerminal
/// where we cannot collect input in a separate thread.
fn prepare_input(&mut self, event_sink: &Sender<event::Event>, timeout: Duration) {
// Dummy implementation for most backends.
// Little trick to avoid unused variables.
let _ = event_sink;
let _ = timeout;
}
/// Refresh the screen.
fn refresh(&mut self);
@ -40,9 +54,6 @@ pub trait Backend {
/// Returns the screen size.
fn screen_size(&self) -> Vec2;
// /// Gets a receiver for input events.
// fn event_receiver(&mut self) -> Receiver<event::Event>;
/// Main method used for printing
fn print_at(&self, pos: Vec2, text: &str);

View File

@ -5,6 +5,7 @@ use event::{Callback, Event, EventResult};
use printer::Printer;
use std::any::Any;
use std::collections::HashMap;
use std::time::Duration;
use std::path::Path;
use theme;
use vec::Vec2;
@ -40,6 +41,7 @@ pub struct Cursive {
cb_sink: chan::Sender<Box<CbFunc>>,
event_source: chan::Receiver<Event>,
event_sink: chan::Sender<Event>,
}
/// Describes one of the possible interruptions we should handle.
@ -122,7 +124,7 @@ impl Cursive {
let (cb_sink, cb_source) = chan::async();
let (event_sink, event_source) = chan::async();
backend.start_input_thread(event_sink);
backend.start_input_thread(event_sink.clone());
Cursive {
fps: 0,
@ -136,6 +138,7 @@ impl Cursive {
cb_source,
cb_sink,
event_source,
event_sink,
backend: backend,
}
}
@ -573,6 +576,8 @@ impl Cursive {
let input_channel = &self.event_source;
let cb_channel = &self.cb_source;
self.backend.prepare_input(&self.event_sink, Duration::from_millis(30));
if self.fps > 0 {
let timeout = 1000 / self.fps;
let timeout = chan::after_ms(timeout);