mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-27 11:16:03 +00:00
Share resize thread logic between all unix backends
This commit is contained in:
parent
e0cc2ea703
commit
acd3bbfcca
@ -6,14 +6,7 @@
|
|||||||
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;
|
||||||
@ -141,43 +134,3 @@ 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
@ -305,12 +305,12 @@ impl backend::Backend for Backend {
|
|||||||
) {
|
) {
|
||||||
let running = Arc::new(AtomicBool::new(true));
|
let running = Arc::new(AtomicBool::new(true));
|
||||||
|
|
||||||
super::start_resize_thread(
|
backend::start_resize_thread(
|
||||||
self.signals.take().unwrap(),
|
self.signals.take().unwrap(),
|
||||||
event_sink.clone(),
|
event_sink.clone(),
|
||||||
input_request.clone(),
|
input_request.clone(),
|
||||||
Arc::clone(&running),
|
Arc::clone(&running),
|
||||||
Arc::clone(&self.needs_resize),
|
Some(Arc::clone(&self.needs_resize)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut parser = InputParser::new();
|
let mut parser = InputParser::new();
|
||||||
|
@ -285,6 +285,8 @@ fn find_closest_pair(pair: ColorPair) -> (i16, i16) {
|
|||||||
|
|
||||||
impl Backend {
|
impl Backend {
|
||||||
pub fn init() -> Box<backend::Backend> {
|
pub fn init() -> Box<backend::Backend> {
|
||||||
|
// We need to create this now, before ncurses initialization
|
||||||
|
// Otherwise ncurses starts its own signal handling and it's a mess.
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let signals = Some(Signals::new(&[libc::SIGWINCH]).unwrap());
|
let signals = Some(Signals::new(&[libc::SIGWINCH]).unwrap());
|
||||||
|
|
||||||
@ -455,12 +457,12 @@ impl backend::Backend for Backend {
|
|||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
super::start_resize_thread(
|
backend::start_resize_thread(
|
||||||
self.signals.take().unwrap(),
|
self.signals.take().unwrap(),
|
||||||
event_sink.clone(),
|
event_sink.clone(),
|
||||||
input_request.clone(),
|
input_request.clone(),
|
||||||
Arc::clone(&running),
|
Arc::clone(&running),
|
||||||
Arc::clone(&self.needs_resize),
|
Some(Arc::clone(&self.needs_resize)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,11 +7,15 @@
|
|||||||
//! using some common libraries. Each of those included backends needs a
|
//! using some common libraries. Each of those included backends needs a
|
||||||
//! corresonding feature to be enabled.
|
//! corresonding feature to be enabled.
|
||||||
|
|
||||||
use event;
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use theme;
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
use crossbeam_channel::{Receiver, Sender};
|
use crossbeam_channel::{Receiver, Sender};
|
||||||
|
use signal_hook::iterator::Signals;
|
||||||
|
|
||||||
|
use event::Event;
|
||||||
|
use theme;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
|
|
||||||
pub mod dummy;
|
pub mod dummy;
|
||||||
@ -41,7 +45,7 @@ pub trait Backend {
|
|||||||
///
|
///
|
||||||
/// `event_trigger` will receive a value before any event is needed.
|
/// `event_trigger` will receive a value before any event is needed.
|
||||||
fn start_input_thread(
|
fn start_input_thread(
|
||||||
&mut self, event_sink: Sender<Option<event::Event>>,
|
&mut self, event_sink: Sender<Option<Event>>,
|
||||||
input_request: Receiver<InputRequest>,
|
input_request: Receiver<InputRequest>,
|
||||||
) {
|
) {
|
||||||
// Dummy implementation for some backends.
|
// Dummy implementation for some backends.
|
||||||
@ -54,7 +58,7 @@ pub trait Backend {
|
|||||||
/// This is only required for non-thread-safe backends like BearLibTerminal
|
/// This is only required for non-thread-safe backends like BearLibTerminal
|
||||||
/// where we cannot collect input in a separate thread.
|
/// where we cannot collect input in a separate thread.
|
||||||
fn prepare_input(
|
fn prepare_input(
|
||||||
&mut self, event_sink: &Sender<Option<event::Event>>,
|
&mut self, event_sink: &Sender<Option<Event>>,
|
||||||
input_request: InputRequest,
|
input_request: InputRequest,
|
||||||
) {
|
) {
|
||||||
// Dummy implementation for most backends.
|
// Dummy implementation for most backends.
|
||||||
@ -89,3 +93,49 @@ pub trait Backend {
|
|||||||
/// Disables the given effect.
|
/// Disables the given effect.
|
||||||
fn unset_effect(&self, effect: theme::Effect);
|
fn unset_effect(&self, effect: theme::Effect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This starts a new thread to listen for SIGWINCH signals
|
||||||
|
///
|
||||||
|
/// As long as `resize_running` is true, it will listen for SIGWINCH, and,
|
||||||
|
/// when detected, it wil set `needs_resize` to true and send an event to
|
||||||
|
/// `resize_sender`. It will also consume an event from `resize_requests`
|
||||||
|
/// afterward, to keep the balance in the force.
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn start_resize_thread(
|
||||||
|
signals: Signals, resize_sender: Sender<Option<Event>>,
|
||||||
|
resize_requests: Receiver<InputRequest>, resize_running: Arc<AtomicBool>,
|
||||||
|
needs_resize: Option<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().
|
||||||
|
if let Some(ref needs_resize) = needs_resize {
|
||||||
|
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(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
extern crate termion;
|
extern crate termion;
|
||||||
|
|
||||||
use crossbeam_channel::{self, Sender, Receiver};
|
|
||||||
use self::termion::color as tcolor;
|
use self::termion::color as tcolor;
|
||||||
use self::termion::event::Event as TEvent;
|
use self::termion::event::Event as TEvent;
|
||||||
use self::termion::event::Key as TKey;
|
use self::termion::event::Key as TKey;
|
||||||
@ -15,8 +14,9 @@ use self::termion::input::{MouseTerminal, TermRead};
|
|||||||
use self::termion::raw::{IntoRawMode, RawTerminal};
|
use self::termion::raw::{IntoRawMode, RawTerminal};
|
||||||
use self::termion::screen::AlternateScreen;
|
use self::termion::screen::AlternateScreen;
|
||||||
use self::termion::style as tstyle;
|
use self::termion::style as tstyle;
|
||||||
use signal_hook::iterator::Signals;
|
use crossbeam_channel::{self, Receiver, Sender};
|
||||||
use libc;
|
use libc;
|
||||||
|
use signal_hook::iterator::Signals;
|
||||||
|
|
||||||
use backend;
|
use backend;
|
||||||
use event::{Event, Key, MouseButton, MouseEvent};
|
use event::{Event, Key, MouseButton, MouseEvent};
|
||||||
@ -25,9 +25,9 @@ use vec::Vec2;
|
|||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::io::{Stdout, Write};
|
use std::io::{Stdout, Write};
|
||||||
use std::thread;
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicBool,Ordering};
|
use std::thread;
|
||||||
|
|
||||||
pub struct Backend {
|
pub struct Backend {
|
||||||
terminal: AlternateScreen<MouseTerminal<RawTerminal<Stdout>>>,
|
terminal: AlternateScreen<MouseTerminal<RawTerminal<Stdout>>>,
|
||||||
@ -56,7 +56,8 @@ impl InputParser {
|
|||||||
let mut events = stdin.events();
|
let mut events = stdin.events();
|
||||||
|
|
||||||
for _ in request_receiver {
|
for _ in request_receiver {
|
||||||
let event: Result<TEvent, ::std::io::Error> = events.next().unwrap();
|
let event: Result<TEvent, ::std::io::Error> =
|
||||||
|
events.next().unwrap();
|
||||||
input_sender.send(event.unwrap());
|
input_sender.send(event.unwrap());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -294,22 +295,17 @@ 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 resize_sender = event_sink.clone();
|
|
||||||
let signals = Signals::new(&[libc::SIGWINCH]).unwrap();
|
|
||||||
|
|
||||||
let resize_running = Arc::clone(&running);
|
#[cfg(unix)]
|
||||||
thread::spawn(move || {
|
{
|
||||||
while resize_running.load(Ordering::Relaxed) {
|
backend::start_resize_thread(
|
||||||
// We know it will only contain SIGWINCH signals, so no need to check.
|
Signals::new(&[libc::SIGWINCH]).unwrap(),
|
||||||
for _ in signals.pending() {
|
event_sink.clone(),
|
||||||
// Tell ncurses about the new terminal size.
|
input_request.clone(),
|
||||||
// Well, do the actual resizing later on, in the main thread.
|
Arc::clone(&running),
|
||||||
// Ncurses isn't really thread-safe so calling resize_term() can crash
|
None,
|
||||||
// other calls like clear() or refresh().
|
);
|
||||||
resize_sender.send(Some(Event::WindowResize));
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut parser = InputParser::new();
|
let mut parser = InputParser::new();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
|
Loading…
Reference in New Issue
Block a user