mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-10 03:10:41 +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::fs::File;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
@ -40,30 +40,27 @@ pub struct Backend {
|
|||||||
struct InputParser {
|
struct InputParser {
|
||||||
key_codes: HashMap<i32, Event>,
|
key_codes: HashMap<i32, Event>,
|
||||||
last_mouse_button: Option<MouseButton>,
|
last_mouse_button: Option<MouseButton>,
|
||||||
event_sink: Sender<Option<Event>>,
|
input_buffer: Option<Event>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputParser {
|
impl InputParser {
|
||||||
fn new(event_sink: Sender<Option<Event>>) -> Self {
|
fn new() -> Self {
|
||||||
InputParser {
|
InputParser {
|
||||||
key_codes: initialize_keymap(),
|
key_codes: initialize_keymap(),
|
||||||
last_mouse_button: None,
|
last_mouse_button: None,
|
||||||
event_sink,
|
input_buffer: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_next(&mut self) {
|
fn parse_next(&mut self) -> Option<Event> {
|
||||||
let ch: i32 = ncurses::getch();
|
if let Some(event) = self.input_buffer.take() {
|
||||||
|
return Some(event);
|
||||||
if ch == 410 {
|
|
||||||
// Ignore resize events.
|
|
||||||
self.parse_next();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ch: i32 = ncurses::getch();
|
||||||
|
|
||||||
if ch == -1 {
|
if ch == -1 {
|
||||||
self.event_sink.send(None);
|
return None;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is it a UTF-8 starting point?
|
// Is it a UTF-8 starting point?
|
||||||
@ -77,7 +74,8 @@ impl InputParser {
|
|||||||
} else {
|
} else {
|
||||||
self.parse_ncurses_char(ch)
|
self.parse_ncurses_char(ch)
|
||||||
};
|
};
|
||||||
self.event_sink.send(Some(event));
|
|
||||||
|
Some(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_ncurses_char(&mut self, ch: i32) -> Event {
|
fn parse_ncurses_char(&mut self, ch: i32) -> Event {
|
||||||
@ -146,7 +144,7 @@ impl InputParser {
|
|||||||
if event.is_none() {
|
if event.is_none() {
|
||||||
event = Some(e);
|
event = Some(e);
|
||||||
} else {
|
} 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 {
|
impl Backend {
|
||||||
pub fn init() -> Box<backend::Backend> {
|
pub fn init() -> Box<backend::Backend> {
|
||||||
|
|
||||||
let signals = Some(Signals::new(&[libc::SIGWINCH]).unwrap());
|
let signals = Some(Signals::new(&[libc::SIGWINCH]).unwrap());
|
||||||
|
|
||||||
// Change the locale.
|
// Change the locale.
|
||||||
@ -312,37 +309,61 @@ impl backend::Backend for Backend {
|
|||||||
let resize_running = Arc::clone(&running);
|
let resize_running = Arc::clone(&running);
|
||||||
let resize_sender = event_sink.clone();
|
let resize_sender = event_sink.clone();
|
||||||
let signals = self.signals.take().unwrap();
|
let signals = self.signals.take().unwrap();
|
||||||
|
let resize_requests = input_request.clone();
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
// This thread will listen to SIGWINCH events and report them.
|
// This thread will listen to SIGWINCH events and report them.
|
||||||
while resize_running.load(Ordering::Relaxed) {
|
while resize_running.load(Ordering::Relaxed) {
|
||||||
// We know it will only contain SIGWINCH signals, so no need to check.
|
// 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.
|
// Tell ncurses about the new terminal size.
|
||||||
// Well, do the actual resizing later on, in the main thread.
|
// Well, do the actual resizing later on, in the main thread.
|
||||||
// Ncurses isn't really thread-safe so calling resize_term() can crash
|
// Ncurses isn't really thread-safe so calling resize_term() can crash
|
||||||
// other calls like clear() or refresh().
|
// other calls like clear() or refresh().
|
||||||
needs_resize.store(true, Ordering::Relaxed);
|
needs_resize.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
resize_sender.send(Some(Event::WindowResize));
|
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.
|
// This thread will take input from ncurses for each request.
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
for req in input_request {
|
for req in input_request {
|
||||||
match req {
|
match req {
|
||||||
backend::InputRequest::Peek => {
|
backend::InputRequest::Peek => {
|
||||||
|
// When peeking, we want an answer instantly from ncurses
|
||||||
ncurses::timeout(0);
|
ncurses::timeout(0);
|
||||||
}
|
}
|
||||||
backend::InputRequest::Block => {
|
backend::InputRequest::Block => {
|
||||||
ncurses::timeout(-1);
|
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);
|
running.store(false, Ordering::Relaxed);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -131,9 +131,9 @@ impl Cursive {
|
|||||||
let theme = theme::load_default();
|
let theme = theme::load_default();
|
||||||
|
|
||||||
let (cb_sink, cb_source) = crossbeam_channel::unbounded();
|
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();
|
let mut backend = backend_init();
|
||||||
backend.start_input_thread(event_sink.clone(), input_source);
|
backend.start_input_thread(event_sink.clone(), input_source);
|
||||||
@ -600,7 +600,8 @@ impl Cursive {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.input_trigger.send(backend::InputRequest::Peek);
|
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)
|
self.event_source.recv().unwrap().map(Interruption::Event)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -610,7 +611,8 @@ impl Cursive {
|
|||||||
fn poll(&mut self) -> Option<Interruption> {
|
fn poll(&mut self) -> Option<Interruption> {
|
||||||
if !self.expecting_event {
|
if !self.expecting_event {
|
||||||
self.input_trigger.send(backend::InputRequest::Block);
|
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;
|
self.expecting_event = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -623,6 +625,9 @@ impl Cursive {
|
|||||||
|
|
||||||
select! {
|
select! {
|
||||||
recv(self.event_source, event) => {
|
recv(self.event_source, event) => {
|
||||||
|
// Ok, we processed the event.
|
||||||
|
self.expecting_event = false;
|
||||||
|
|
||||||
event.unwrap().map(Interruption::Event)
|
event.unwrap().map(Interruption::Event)
|
||||||
},
|
},
|
||||||
recv(self.cb_source, cb) => {
|
recv(self.cb_source, cb) => {
|
||||||
@ -735,16 +740,20 @@ impl Cursive {
|
|||||||
self.draw();
|
self.draw();
|
||||||
self.backend.refresh();
|
self.backend.refresh();
|
||||||
|
|
||||||
// First, read all events available while peeking.
|
if let Some(interruption) = self.poll() {
|
||||||
while let Some(interruption) = self.peek() {
|
|
||||||
self.handle_interruption(interruption);
|
self.handle_interruption(interruption);
|
||||||
if !self.running {
|
if !self.running {
|
||||||
return;
|
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);
|
self.handle_interruption(interruption);
|
||||||
|
if !self.running {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -758,7 +767,6 @@ impl Cursive {
|
|||||||
|
|
||||||
if event == Event::WindowResize {
|
if event == Event::WindowResize {
|
||||||
self.clear();
|
self.clear();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Event::Mouse {
|
if let Event::Mouse {
|
||||||
@ -794,9 +802,6 @@ impl Cursive {
|
|||||||
EventResult::Consumed(Some(cb)) => cb(self),
|
EventResult::Consumed(Some(cb)) => cb(self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ok, we processed the event.
|
|
||||||
self.expecting_event = false;
|
|
||||||
}
|
}
|
||||||
Interruption::Callback(cb) => {
|
Interruption::Callback(cb) => {
|
||||||
cb.call_box(self);
|
cb.call_box(self);
|
||||||
|
Loading…
Reference in New Issue
Block a user