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;
|
extern crate term_size;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
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 event::{Event, Key};
|
||||||
use theme::{BaseColor, Color, ColorPair};
|
use theme::{BaseColor, Color, ColorPair};
|
||||||
|
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
|
|
||||||
#[cfg(feature = "ncurses-backend")]
|
#[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::fs::File;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
@ -304,45 +304,14 @@ impl backend::Backend for Backend {
|
|||||||
input_request: Receiver<backend::InputRequest>,
|
input_request: Receiver<backend::InputRequest>,
|
||||||
) {
|
) {
|
||||||
let running = Arc::new(AtomicBool::new(true));
|
let running = Arc::new(AtomicBool::new(true));
|
||||||
let needs_resize = Arc::clone(&self.needs_resize);
|
|
||||||
|
|
||||||
let resize_running = Arc::clone(&running);
|
super::start_resize_thread(
|
||||||
let resize_sender = event_sink.clone();
|
self.signals.take().unwrap(),
|
||||||
let signals = self.signals.take().unwrap();
|
event_sink.clone(),
|
||||||
let resize_requests = input_request.clone();
|
input_request.clone(),
|
||||||
|
Arc::clone(&running),
|
||||||
thread::spawn(move || {
|
Arc::clone(&self.needs_resize),
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut parser = InputParser::new();
|
let mut parser = InputParser::new();
|
||||||
|
|
||||||
|
@ -7,20 +7,20 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use crossbeam_channel::{Sender, Receiver};
|
use crossbeam_channel::{Receiver, Sender};
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
use signal_hook::iterator::Signals;
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use libc;
|
use libc;
|
||||||
|
#[cfg(unix)]
|
||||||
|
use signal_hook::iterator::Signals;
|
||||||
|
|
||||||
use backend;
|
use backend;
|
||||||
use event::{Event, Key, MouseButton, MouseEvent};
|
use event::{Event, Key, MouseButton, MouseEvent};
|
||||||
use theme::{Color, ColorPair, Effect};
|
use theme::{Color, ColorPair, Effect};
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
|
|
||||||
use super::split_i32;
|
|
||||||
use self::pancurses::mmask_t;
|
use self::pancurses::mmask_t;
|
||||||
|
use super::split_i32;
|
||||||
|
|
||||||
pub struct Backend {
|
pub struct Backend {
|
||||||
// Used
|
// Used
|
||||||
@ -66,7 +66,6 @@ impl InputParser {
|
|||||||
|
|
||||||
fn parse_next(&mut self) {
|
fn parse_next(&mut self) {
|
||||||
let event = if let Some(ev) = self.window.getch() {
|
let event = if let Some(ev) = self.window.getch() {
|
||||||
|
|
||||||
Some(match ev {
|
Some(match ev {
|
||||||
pancurses::Input::Character('\n') => Event::Key(Key::Enter),
|
pancurses::Input::Character('\n') => Event::Key(Key::Enter),
|
||||||
// TODO: wait for a very short delay. If more keys are
|
// 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)
|
super::find_closest_pair(pair, pancurses::COLORS() as i16)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl Backend {
|
impl Backend {
|
||||||
pub fn init() -> Box<backend::Backend> {
|
pub fn init() -> Box<backend::Backend> {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@ -455,34 +452,18 @@ impl backend::Backend for Backend {
|
|||||||
input_request: Receiver<backend::InputRequest>,
|
input_request: Receiver<backend::InputRequest>,
|
||||||
) {
|
) {
|
||||||
let running = Arc::new(AtomicBool::new(true));
|
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)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
let signals = self.signals.take().unwrap();
|
super::start_resize_thread(
|
||||||
thread::spawn(move || {
|
self.signals.take().unwrap(),
|
||||||
// This thread will listen to SIGWINCH events and report them.
|
event_sink.clone(),
|
||||||
while resize_running.load(Ordering::Relaxed) {
|
input_request.clone(),
|
||||||
// We know it will only contain SIGWINCH signals, so no need to check.
|
Arc::clone(&running),
|
||||||
for _ in signals.pending() {
|
Arc::clone(&self.needs_resize),
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// On windows we just forget the sender, so the receiver blocks forever.
|
|
||||||
#[cfg(not(unix))]
|
|
||||||
::std::mem::forget(resize_sender);
|
|
||||||
|
|
||||||
let mut input_parser =
|
let mut input_parser =
|
||||||
InputParser::new(event_sink, Arc::clone(&self.window));
|
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 {
|
impl Default for Cursive {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::termion()
|
Self::termion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(not(feature = "termion"), feature = "pancurses"))]
|
#[cfg(
|
||||||
|
all(
|
||||||
|
not(feature = "termion-backend"),
|
||||||
|
feature = "pancurses-backend"
|
||||||
|
)
|
||||||
|
)]
|
||||||
impl Default for Cursive {
|
impl Default for Cursive {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::pancurses()
|
Self::pancurses()
|
||||||
@ -97,9 +102,9 @@ impl Default for Cursive {
|
|||||||
|
|
||||||
#[cfg(
|
#[cfg(
|
||||||
all(
|
all(
|
||||||
not(feature = "termion"),
|
not(feature = "termion-backend"),
|
||||||
not(feature = "pancurses"),
|
not(feature = "pancurses-backend"),
|
||||||
feature = "bear-lib-terminal"
|
feature = "blt-backend"
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
impl Default for Cursive {
|
impl Default for Cursive {
|
||||||
@ -110,10 +115,10 @@ impl Default for Cursive {
|
|||||||
|
|
||||||
#[cfg(
|
#[cfg(
|
||||||
all(
|
all(
|
||||||
not(feature = "termion"),
|
not(feature = "termion-backend"),
|
||||||
not(feature = "pancurses"),
|
not(feature = "pancurses-backend"),
|
||||||
not(feature = "bear-lib-terminal"),
|
not(feature = "blt-backend"),
|
||||||
feature = "ncurses"
|
feature = "ncurses-backend"
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
impl Default for Cursive {
|
impl Default for Cursive {
|
||||||
@ -158,25 +163,25 @@ impl Cursive {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Cursive root using a ncurses backend.
|
/// Creates a new Cursive root using a ncurses backend.
|
||||||
#[cfg(feature = "ncurses")]
|
#[cfg(feature = "ncurses-backend")]
|
||||||
pub fn ncurses() -> Self {
|
pub fn ncurses() -> Self {
|
||||||
Self::new(backend::curses::n::Backend::init)
|
Self::new(backend::curses::n::Backend::init)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Cursive root using a pancurses backend.
|
/// Creates a new Cursive root using a pancurses backend.
|
||||||
#[cfg(feature = "pancurses")]
|
#[cfg(feature = "pancurses-backend")]
|
||||||
pub fn pancurses() -> Self {
|
pub fn pancurses() -> Self {
|
||||||
Self::new(backend::curses::pan::Backend::init)
|
Self::new(backend::curses::pan::Backend::init)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Cursive root using a termion backend.
|
/// Creates a new Cursive root using a termion backend.
|
||||||
#[cfg(feature = "termion")]
|
#[cfg(feature = "termion-backend")]
|
||||||
pub fn termion() -> Self {
|
pub fn termion() -> Self {
|
||||||
Self::new(backend::termion::Backend::init)
|
Self::new(backend::termion::Backend::init)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Cursive root using a bear-lib-terminal backend.
|
/// Creates a new Cursive root using a bear-lib-terminal backend.
|
||||||
#[cfg(feature = "bear-lib-terminal")]
|
#[cfg(feature = "blt-backend")]
|
||||||
pub fn blt() -> Self {
|
pub fn blt() -> Self {
|
||||||
Self::new(backend::blt::Backend::init)
|
Self::new(backend::blt::Backend::init)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user