mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Move resize-specific behaviour to backend
This commit is contained in:
parent
64d0a66b5e
commit
fd75249633
@ -6,7 +6,7 @@ use std::ffi::CString;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
@ -40,30 +40,27 @@ pub struct Backend {
|
||||
struct InputParser {
|
||||
key_codes: HashMap<i32, Event>,
|
||||
last_mouse_button: Option<MouseButton>,
|
||||
event_sink: Sender<Option<Event>>,
|
||||
input_buffer: Option<Event>,
|
||||
}
|
||||
|
||||
impl InputParser {
|
||||
fn new(event_sink: Sender<Option<Event>>) -> Self {
|
||||
fn new() -> Self {
|
||||
InputParser {
|
||||
key_codes: initialize_keymap(),
|
||||
last_mouse_button: None,
|
||||
event_sink,
|
||||
input_buffer: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_next(&mut self) {
|
||||
fn parse_next(&mut self) -> Option<Event> {
|
||||
if let Some(event) = self.input_buffer.take() {
|
||||
return Some(event);
|
||||
}
|
||||
|
||||
let ch: i32 = ncurses::getch();
|
||||
|
||||
if ch == 410 {
|
||||
// Ignore resize events.
|
||||
self.parse_next();
|
||||
return;
|
||||
}
|
||||
|
||||
if ch == -1 {
|
||||
self.event_sink.send(None);
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
// Is it a UTF-8 starting point?
|
||||
@ -77,7 +74,8 @@ impl InputParser {
|
||||
} else {
|
||||
self.parse_ncurses_char(ch)
|
||||
};
|
||||
self.event_sink.send(Some(event));
|
||||
|
||||
Some(event)
|
||||
}
|
||||
|
||||
fn parse_ncurses_char(&mut self, ch: i32) -> Event {
|
||||
@ -146,7 +144,7 @@ impl InputParser {
|
||||
if event.is_none() {
|
||||
event = Some(e);
|
||||
} else {
|
||||
self.event_sink.send(Some(make_event(e)));
|
||||
self.input_buffer = Some(make_event(e));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -185,7 +183,6 @@ fn write_to_tty(bytes: &[u8]) -> io::Result<()> {
|
||||
|
||||
impl Backend {
|
||||
pub fn init() -> Box<backend::Backend> {
|
||||
|
||||
let signals = Some(Signals::new(&[libc::SIGWINCH]).unwrap());
|
||||
|
||||
// Change the locale.
|
||||
@ -312,37 +309,61 @@ impl backend::Backend for Backend {
|
||||
let resize_running = Arc::clone(&running);
|
||||
let resize_sender = event_sink.clone();
|
||||
let signals = self.signals.take().unwrap();
|
||||
let resize_requests = input_request.clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
// This thread will listen to SIGWINCH events and report them.
|
||||
while resize_running.load(Ordering::Relaxed) {
|
||||
// We know it will only contain SIGWINCH signals, so no need to check.
|
||||
for _ in signals.pending() {
|
||||
if signals.wait().count() > 0 {
|
||||
// Tell ncurses about the new terminal size.
|
||||
// Well, do the actual resizing later on, in the main thread.
|
||||
// Ncurses isn't really thread-safe so calling resize_term() can crash
|
||||
// other calls like clear() or refresh().
|
||||
needs_resize.store(true, Ordering::Relaxed);
|
||||
|
||||
resize_sender.send(Some(Event::WindowResize));
|
||||
// We've sent the message.
|
||||
// This means Cursive was listening, and will now soon be sending a new request.
|
||||
// This means the input thread accepted a request, but hasn't sent a message yet.
|
||||
// So we KNOW the input thread is not waiting for a new request.
|
||||
|
||||
// We sent an event for free, so pay for it now by consuming a request
|
||||
while let Some(backend::InputRequest::Peek) =
|
||||
resize_requests.recv()
|
||||
{
|
||||
// At this point Cursive will now listen for input.
|
||||
// There is a chance the input thread will send his event before us.
|
||||
// But without some extra atomic flag, it'd be hard to know.
|
||||
// So instead, keep sending `None`
|
||||
|
||||
// Repeat until we receive a blocking call
|
||||
resize_sender.send(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut parser = InputParser::new(event_sink);
|
||||
let mut parser = InputParser::new();
|
||||
|
||||
// This thread will take input from ncurses for each request.
|
||||
thread::spawn(move || {
|
||||
for req in input_request {
|
||||
match req {
|
||||
backend::InputRequest::Peek => {
|
||||
// When peeking, we want an answer instantly from ncurses
|
||||
ncurses::timeout(0);
|
||||
}
|
||||
backend::InputRequest::Block => {
|
||||
ncurses::timeout(-1);
|
||||
}
|
||||
}
|
||||
parser.parse_next();
|
||||
|
||||
// Do the actual polling & parsing.
|
||||
event_sink.send(parser.parse_next());
|
||||
}
|
||||
// The request channel is closed, which means Cursive has been
|
||||
// dropped, so stop the resize-detection thread as well.
|
||||
running.store(false, Ordering::Relaxed);
|
||||
});
|
||||
}
|
||||
|
@ -131,9 +131,9 @@ impl Cursive {
|
||||
let theme = theme::load_default();
|
||||
|
||||
let (cb_sink, cb_source) = crossbeam_channel::unbounded();
|
||||
let (event_sink, event_source) = crossbeam_channel::unbounded();
|
||||
let (event_sink, event_source) = crossbeam_channel::bounded(0);
|
||||
|
||||
let (input_sink, input_source) = crossbeam_channel::unbounded();
|
||||
let (input_sink, input_source) = crossbeam_channel::bounded(0);
|
||||
|
||||
let mut backend = backend_init();
|
||||
backend.start_input_thread(event_sink.clone(), input_source);
|
||||
@ -600,7 +600,8 @@ impl Cursive {
|
||||
}
|
||||
|
||||
self.input_trigger.send(backend::InputRequest::Peek);
|
||||
self.backend.prepare_input(&self.event_sink, backend::InputRequest::Peek);
|
||||
self.backend
|
||||
.prepare_input(&self.event_sink, backend::InputRequest::Peek);
|
||||
self.event_source.recv().unwrap().map(Interruption::Event)
|
||||
}
|
||||
|
||||
@ -610,7 +611,8 @@ impl Cursive {
|
||||
fn poll(&mut self) -> Option<Interruption> {
|
||||
if !self.expecting_event {
|
||||
self.input_trigger.send(backend::InputRequest::Block);
|
||||
self.backend.prepare_input(&self.event_sink, backend::InputRequest::Block);
|
||||
self.backend
|
||||
.prepare_input(&self.event_sink, backend::InputRequest::Block);
|
||||
self.expecting_event = true;
|
||||
}
|
||||
|
||||
@ -623,6 +625,9 @@ impl Cursive {
|
||||
|
||||
select! {
|
||||
recv(self.event_source, event) => {
|
||||
// Ok, we processed the event.
|
||||
self.expecting_event = false;
|
||||
|
||||
event.unwrap().map(Interruption::Event)
|
||||
},
|
||||
recv(self.cb_source, cb) => {
|
||||
@ -735,16 +740,20 @@ impl Cursive {
|
||||
self.draw();
|
||||
self.backend.refresh();
|
||||
|
||||
// First, read all events available while peeking.
|
||||
while let Some(interruption) = self.peek() {
|
||||
if let Some(interruption) = self.poll() {
|
||||
self.handle_interruption(interruption);
|
||||
if !self.running {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(interruption) = self.poll() {
|
||||
// Don't block, but try to read any other pending event.
|
||||
// This lets us batch-process chunks of events, like big copy-paste or mouse drags.
|
||||
while let Some(interruption) = self.peek() {
|
||||
self.handle_interruption(interruption);
|
||||
if !self.running {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -758,7 +767,6 @@ impl Cursive {
|
||||
|
||||
if event == Event::WindowResize {
|
||||
self.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if let Event::Mouse {
|
||||
@ -794,9 +802,6 @@ impl Cursive {
|
||||
EventResult::Consumed(Some(cb)) => cb(self),
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, we processed the event.
|
||||
self.expecting_event = false;
|
||||
}
|
||||
Interruption::Callback(cb) => {
|
||||
cb.call_box(self);
|
||||
|
Loading…
Reference in New Issue
Block a user