mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Share resize thread logic between ncurses backends
This commit is contained in:
parent
fd75249633
commit
e0cc2ea703
@ -6,10 +6,16 @@
|
||||
extern crate term_size;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use signal_hook::iterator::Signals;
|
||||
|
||||
use backend;
|
||||
use event::{Event, Key};
|
||||
use theme::{BaseColor, Color, ColorPair};
|
||||
|
||||
use vec::Vec2;
|
||||
|
||||
#[cfg(feature = "ncurses-backend")]
|
||||
@ -135,3 +141,43 @@ fn find_closest(color: Color, max_colors: i16) -> i16 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn start_resize_thread(
|
||||
signals: Signals, resize_sender: Sender<Option<Event>>,
|
||||
resize_requests: Receiver<backend::InputRequest>,
|
||||
resize_running: Arc<AtomicBool>, needs_resize: Arc<AtomicBool>,
|
||||
) {
|
||||
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.
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use std::ffi::CString;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
@ -304,45 +304,14 @@ impl backend::Backend for Backend {
|
||||
input_request: Receiver<backend::InputRequest>,
|
||||
) {
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
let needs_resize = Arc::clone(&self.needs_resize);
|
||||
|
||||
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.
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
super::start_resize_thread(
|
||||
self.signals.take().unwrap(),
|
||||
event_sink.clone(),
|
||||
input_request.clone(),
|
||||
Arc::clone(&running),
|
||||
Arc::clone(&self.needs_resize),
|
||||
);
|
||||
|
||||
let mut parser = InputParser::new();
|
||||
|
||||
|
@ -7,20 +7,20 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
use crossbeam_channel::{Sender, Receiver};
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
|
||||
#[cfg(unix)]
|
||||
use signal_hook::iterator::Signals;
|
||||
#[cfg(unix)]
|
||||
use libc;
|
||||
#[cfg(unix)]
|
||||
use signal_hook::iterator::Signals;
|
||||
|
||||
use backend;
|
||||
use event::{Event, Key, MouseButton, MouseEvent};
|
||||
use theme::{Color, ColorPair, Effect};
|
||||
use vec::Vec2;
|
||||
|
||||
use super::split_i32;
|
||||
use self::pancurses::mmask_t;
|
||||
use super::split_i32;
|
||||
|
||||
pub struct Backend {
|
||||
// Used
|
||||
@ -66,7 +66,6 @@ impl InputParser {
|
||||
|
||||
fn parse_next(&mut self) {
|
||||
let event = if let Some(ev) = self.window.getch() {
|
||||
|
||||
Some(match ev {
|
||||
pancurses::Input::Character('\n') => Event::Key(Key::Enter),
|
||||
// TODO: wait for a very short delay. If more keys are
|
||||
@ -284,8 +283,6 @@ fn find_closest_pair(pair: ColorPair) -> (i16, i16) {
|
||||
super::find_closest_pair(pair, pancurses::COLORS() as i16)
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl Backend {
|
||||
pub fn init() -> Box<backend::Backend> {
|
||||
#[cfg(unix)]
|
||||
@ -455,33 +452,17 @@ impl backend::Backend for Backend {
|
||||
input_request: Receiver<backend::InputRequest>,
|
||||
) {
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
let needs_resize = Arc::clone(&self.needs_resize);
|
||||
|
||||
let resize_running = Arc::clone(&running);
|
||||
let resize_sender = event_sink.clone();
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let signals = self.signals.take().unwrap();
|
||||
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() {
|
||||
// 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));
|
||||
super::start_resize_thread(
|
||||
self.signals.take().unwrap(),
|
||||
event_sink.clone(),
|
||||
input_request.clone(),
|
||||
Arc::clone(&running),
|
||||
Arc::clone(&self.needs_resize),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// On windows we just forget the sender, so the receiver blocks forever.
|
||||
#[cfg(not(unix))]
|
||||
::std::mem::forget(resize_sender);
|
||||
|
||||
let mut input_parser =
|
||||
InputParser::new(event_sink, Arc::clone(&self.window));
|
||||
|
@ -81,14 +81,19 @@ impl<F: FnOnce(&mut Cursive) -> () + Send> CbFunc for F {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "termion")]
|
||||
#[cfg(feature = "termion-backend")]
|
||||
impl Default for Cursive {
|
||||
fn default() -> Self {
|
||||
Self::termion()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "termion"), feature = "pancurses"))]
|
||||
#[cfg(
|
||||
all(
|
||||
not(feature = "termion-backend"),
|
||||
feature = "pancurses-backend"
|
||||
)
|
||||
)]
|
||||
impl Default for Cursive {
|
||||
fn default() -> Self {
|
||||
Self::pancurses()
|
||||
@ -97,9 +102,9 @@ impl Default for Cursive {
|
||||
|
||||
#[cfg(
|
||||
all(
|
||||
not(feature = "termion"),
|
||||
not(feature = "pancurses"),
|
||||
feature = "bear-lib-terminal"
|
||||
not(feature = "termion-backend"),
|
||||
not(feature = "pancurses-backend"),
|
||||
feature = "blt-backend"
|
||||
)
|
||||
)]
|
||||
impl Default for Cursive {
|
||||
@ -110,10 +115,10 @@ impl Default for Cursive {
|
||||
|
||||
#[cfg(
|
||||
all(
|
||||
not(feature = "termion"),
|
||||
not(feature = "pancurses"),
|
||||
not(feature = "bear-lib-terminal"),
|
||||
feature = "ncurses"
|
||||
not(feature = "termion-backend"),
|
||||
not(feature = "pancurses-backend"),
|
||||
not(feature = "blt-backend"),
|
||||
feature = "ncurses-backend"
|
||||
)
|
||||
)]
|
||||
impl Default for Cursive {
|
||||
@ -158,25 +163,25 @@ impl Cursive {
|
||||
}
|
||||
|
||||
/// Creates a new Cursive root using a ncurses backend.
|
||||
#[cfg(feature = "ncurses")]
|
||||
#[cfg(feature = "ncurses-backend")]
|
||||
pub fn ncurses() -> Self {
|
||||
Self::new(backend::curses::n::Backend::init)
|
||||
}
|
||||
|
||||
/// Creates a new Cursive root using a pancurses backend.
|
||||
#[cfg(feature = "pancurses")]
|
||||
#[cfg(feature = "pancurses-backend")]
|
||||
pub fn pancurses() -> Self {
|
||||
Self::new(backend::curses::pan::Backend::init)
|
||||
}
|
||||
|
||||
/// Creates a new Cursive root using a termion backend.
|
||||
#[cfg(feature = "termion")]
|
||||
#[cfg(feature = "termion-backend")]
|
||||
pub fn termion() -> Self {
|
||||
Self::new(backend::termion::Backend::init)
|
||||
}
|
||||
|
||||
/// Creates a new Cursive root using a bear-lib-terminal backend.
|
||||
#[cfg(feature = "bear-lib-terminal")]
|
||||
#[cfg(feature = "blt-backend")]
|
||||
pub fn blt() -> Self {
|
||||
Self::new(backend::blt::Backend::init)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user