diff --git a/src/backend/blt.rs b/src/backend/blt.rs index 91a7922..4969ca7 100644 --- a/src/backend/blt.rs +++ b/src/backend/blt.rs @@ -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, buttons_pressed: HashSet, mouse_position: Vec2, } -impl InputParser { - fn new(event_sink: chan::Sender) -> Self { - InputParser { - event_sink, +impl Backend { + pub fn init() -> Box { + 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 { // 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 { - 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) { - let mut parser = InputParser::new(event_sink); + fn start_input_thread(&mut self, _event_sink: chan::Sender) { + } - thread::spawn(move || { - loop { - parser.parse_next(); + fn prepare_input(&mut self, event_sink: &chan::Sender, 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); } } diff --git a/src/backend/dummy.rs b/src/backend/dummy.rs index d104d1e..ff45055 100644 --- a/src/backend/dummy.rs +++ b/src/backend/dummy.rs @@ -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) { - thread::spawn(move || event_sink.send(event::Event::Exit)); + fn start_input_thread(&mut self, _event_sink: chan::Sender) { + } + + fn prepare_input(&mut self, event_sink: &chan::Sender, _timeout: Duration) { + event_sink.send(event::Event::Exit) } fn print_at(&self, _: Vec2, _: &str) {} diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 06b290d..818b885 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -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); + /// 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, 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; - /// Main method used for printing fn print_at(&self, pos: Vec2, text: &str); diff --git a/src/cursive.rs b/src/cursive.rs index 335dcec..b1a9c5f 100644 --- a/src/cursive.rs +++ b/src/cursive.rs @@ -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>, event_source: chan::Receiver, + event_sink: chan::Sender, } /// 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);