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 theme::{BaseColor, Color, ColorPair, Effect};
use vec::Vec2; use vec::Vec2;
use chan; use chan;
use std::thread; use std::time::{Duration, Instant};
enum ColorRole { enum ColorRole {
Foreground, Foreground,
@ -23,28 +23,38 @@ enum ColorRole {
} }
pub struct Backend { pub struct Backend {
}
struct InputParser {
event_sink: chan::Sender<Event>,
buttons_pressed: HashSet<MouseButton>, buttons_pressed: HashSet<MouseButton>,
mouse_position: Vec2, mouse_position: Vec2,
} }
impl InputParser { impl Backend {
fn new(event_sink: chan::Sender<Event>) -> Self { pub fn init() -> Box<backend::Backend> {
InputParser { terminal::open("Cursive", 80, 24);
event_sink, 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(), buttons_pressed: HashSet::new(),
mouse_position: Vec2::zero(), 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. // TODO: we could add backend-specific controls here.
// Ex: ctrl+mouse wheel cause window cellsize to change // 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 { match ev {
BltEvent::Close => Event::Exit, BltEvent::Close => Event::Exit,
BltEvent::Resize { .. } => Event::WindowResize, BltEvent::Resize { .. } => Event::WindowResize,
@ -94,10 +104,7 @@ impl InputParser {
Event::Refresh Event::Refresh
} }
} }
} else { })
Event::Refresh
};
self.event_sink.send(event);
} }
fn blt_keycode_to_ev( 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 { impl backend::Backend for Backend {
fn finish(&mut self) { fn finish(&mut self) {
terminal::close(); terminal::close();
@ -320,14 +305,19 @@ impl backend::Backend for Backend {
terminal::print_xy(pos.x as i32, pos.y as i32, text); terminal::print_xy(pos.x as i32, pos.y as i32, text);
} }
fn start_input_thread(&mut self, event_sink: chan::Sender<Event>) { fn start_input_thread(&mut self, _event_sink: chan::Sender<Event>) {
let mut parser = InputParser::new(event_sink); }
thread::spawn(move || { fn prepare_input(&mut self, event_sink: &chan::Sender<Event>, timeout: Duration) {
loop { // Wait for up to `timeout_ms`.
parser.parse_next(); 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 event;
use vec::Vec2; use vec::Vec2;
use std::thread; use std::time::Duration;
use chan; use chan;
@ -32,8 +32,11 @@ impl backend::Backend for Backend {
(1, 1).into() (1, 1).into()
} }
fn start_input_thread(&mut self, event_sink: chan::Sender<event::Event>) { fn start_input_thread(&mut self, _event_sink: chan::Sender<event::Event>) {
thread::spawn(move || event_sink.send(event::Event::Exit)); }
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) {} fn print_at(&self, _: Vec2, _: &str) {}

View File

@ -14,6 +14,8 @@ use chan::Sender;
use vec::Vec2; use vec::Vec2;
use std::time::Duration;
pub mod dummy; pub mod dummy;
pub mod termion; pub mod termion;
@ -29,8 +31,20 @@ pub trait Backend {
/// This should clear any state in the terminal. /// This should clear any state in the terminal.
fn finish(&mut self); 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>); 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. /// Refresh the screen.
fn refresh(&mut self); fn refresh(&mut self);
@ -40,9 +54,6 @@ pub trait Backend {
/// Returns the screen size. /// Returns the screen size.
fn screen_size(&self) -> Vec2; fn screen_size(&self) -> Vec2;
// /// Gets a receiver for input events.
// fn event_receiver(&mut self) -> Receiver<event::Event>;
/// Main method used for printing /// Main method used for printing
fn print_at(&self, pos: Vec2, text: &str); fn print_at(&self, pos: Vec2, text: &str);

View File

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