From e0cc2ea703cca171276729a5e44c2944d5b46bbf Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Wed, 18 Jul 2018 19:49:10 -0700 Subject: [PATCH] Share resize thread logic between ncurses backends --- src/backend/curses/mod.rs | 48 ++++++++++++++++++++++++++++++++++++++- src/backend/curses/n.rs | 47 +++++++------------------------------- src/backend/curses/pan.rs | 41 +++++++++------------------------ src/cursive.rs | 31 ++++++++++++++----------- 4 files changed, 84 insertions(+), 83 deletions(-) diff --git a/src/backend/curses/mod.rs b/src/backend/curses/mod.rs index f5b75ae..8fead4e 100644 --- a/src/backend/curses/mod.rs +++ b/src/backend/curses/mod.rs @@ -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>, + resize_requests: Receiver, + resize_running: Arc, needs_resize: Arc, +) { + 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); + } + } + } + }); +} diff --git a/src/backend/curses/n.rs b/src/backend/curses/n.rs index 6e86ec6..18805db 100644 --- a/src/backend/curses/n.rs +++ b/src/backend/curses/n.rs @@ -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, ) { 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(); diff --git a/src/backend/curses/pan.rs b/src/backend/curses/pan.rs index 0da0e76..a55b383 100644 --- a/src/backend/curses/pan.rs +++ b/src/backend/curses/pan.rs @@ -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 { #[cfg(unix)] @@ -455,34 +452,18 @@ impl backend::Backend for Backend { input_request: Receiver, ) { 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)); diff --git a/src/cursive.rs b/src/cursive.rs index 9f658a1..b7f0230 100644 --- a/src/cursive.rs +++ b/src/cursive.rs @@ -81,14 +81,19 @@ impl () + 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) }