Cursive::ncurses & cie now return io::Result<Self>

This commit is contained in:
Alexandre Bury 2019-03-03 19:24:39 -08:00
parent dcee6da16a
commit f765e9ac07
6 changed files with 46 additions and 31 deletions

View File

@ -8,6 +8,9 @@
`find_id()` was added instead. `find_id()` was added instead.
- Breaking change: replaced `set_fps(i32)` with `set_autorefresh(bool)` - Breaking change: replaced `set_fps(i32)` with `set_autorefresh(bool)`
- Breaking change: updated the Backend trait for a simpler input system - Breaking change: updated the Backend trait for a simpler input system
- Breaking change: `Cursive::{ncurses, pancurses, termion}` now return
`io::Result<Self>` instead of panicking. `Cursive::default()` still unwraps.
- Also added `Cursive::try_new` for failible backends.
- Updated to Rust 2018 (now requires rustc > 1.31) - Updated to Rust 2018 (now requires rustc > 1.31)
- Add a logging implementation (`logger::init()`) and a `DebugConsole` - Add a logging implementation (`logger::init()`) and a `DebugConsole`
(`cursive::toggle_debug_console()`) (`cursive::toggle_debug_console()`)

View File

@ -32,6 +32,7 @@ pub struct Backend {
impl Backend { impl Backend {
/// Creates a new BearLibTerminal-based backend. /// Creates a new BearLibTerminal-based backend.
pub fn init() -> Box<dyn backend::Backend> { pub fn init() -> Box<dyn backend::Backend> {
// TODO: Add some error handling?
terminal::open("Cursive", 80, 24); terminal::open("Cursive", 80, 24);
terminal::set(terminal::config::Window::empty().resizeable(true)); terminal::set(terminal::config::Window::empty().resizeable(true));
terminal::set(vec![ terminal::set(vec![

View File

@ -58,13 +58,16 @@ fn write_to_tty(bytes: &[u8]) -> io::Result<()> {
impl Backend { impl Backend {
/// Creates a new ncurses-based backend. /// Creates a new ncurses-based backend.
pub fn init() -> Box<dyn backend::Backend> { pub fn init() -> io::Result<Box<dyn backend::Backend>> {
// Check the $TERM variable. // Check the $TERM variable.
if std::env::var("TERM") if std::env::var("TERM")
.map(|var| var.is_empty()) .map(|var| var.is_empty())
.unwrap_or(true) .unwrap_or(true)
{ {
panic!("$TERM is unset. Cannot initialize ncurses interface."); return Err(io::Error::new(
io::ErrorKind::Other,
"$TERM is unset. Cannot initialize ncurses interface.",
));
} }
// Change the locale. // Change the locale.
@ -111,7 +114,7 @@ impl Backend {
// This asks the terminal to provide us with mouse drag events // This asks the terminal to provide us with mouse drag events
// (Mouse move when a button is pressed). // (Mouse move when a button is pressed).
// Replacing 1002 with 1003 would give us ANY mouse move. // Replacing 1002 with 1003 would give us ANY mouse move.
write_to_tty(b"\x1B[?1002h").unwrap(); write_to_tty(b"\x1B[?1002h")?;
let c = Backend { let c = Backend {
current_style: Cell::new(ColorPair::from_256colors(0, 0)), current_style: Cell::new(ColorPair::from_256colors(0, 0)),
@ -121,7 +124,7 @@ impl Backend {
input_buffer: None, input_buffer: None,
}; };
Box::new(c) Ok(Box::new(c))
} }
/// Save a new color pair. /// Save a new color pair.

View File

@ -34,10 +34,12 @@ fn find_closest_pair(pair: ColorPair) -> (i16, i16) {
impl Backend { impl Backend {
/// Creates a new pancurses-based backend. /// Creates a new pancurses-based backend.
pub fn init() -> Box<dyn backend::Backend> { pub fn init() -> std::io::Result<Box<dyn backend::Backend>> {
::std::env::set_var("ESCDELAY", "25"); ::std::env::set_var("ESCDELAY", "25");
// TODO: use pancurses::newterm()
let window = pancurses::initscr(); let window = pancurses::initscr();
window.keypad(true); window.keypad(true);
window.timeout(0); window.timeout(0);
pancurses::noecho(); pancurses::noecho();
@ -55,7 +57,7 @@ impl Backend {
// (Mouse move when a button is pressed). // (Mouse move when a button is pressed).
// Replacing 1002 with 1003 would give us ANY mouse move. // Replacing 1002 with 1003 would give us ANY mouse move.
print!("\x1B[?1002h"); print!("\x1B[?1002h");
stdout().flush().expect("could not flush stdout"); stdout().flush()?;
let c = Backend { let c = Backend {
current_style: Cell::new(ColorPair::from_256colors(0, 0)), current_style: Cell::new(ColorPair::from_256colors(0, 0)),
@ -66,7 +68,7 @@ impl Backend {
window, window,
}; };
Box::new(c) Ok(Box::new(c))
} }
/// Save a new color pair. /// Save a new color pair.

View File

@ -43,20 +43,16 @@ pub struct Backend {
impl Backend { impl Backend {
/// Creates a new termion-based backend. /// Creates a new termion-based backend.
pub fn init() -> Box<dyn backend::Backend> { pub fn init() -> std::io::Result<Box<dyn backend::Backend>> {
// Use a ~8MB buffer // Use a ~8MB buffer
// Should be enough for a single screen most of the time. // Should be enough for a single screen most of the time.
let terminal = let terminal =
RefCell::new(AlternateScreen::from(MouseTerminal::from( RefCell::new(AlternateScreen::from(MouseTerminal::from(
BufWriter::with_capacity( BufWriter::with_capacity(8_000_000, File::create("/dev/tty")?)
8_000_000, .into_raw_mode()?,
File::create("/dev/tty").unwrap(),
)
.into_raw_mode()
.unwrap(),
))); )));
write!(terminal.borrow_mut(), "{}", termion::cursor::Hide).unwrap(); write!(terminal.borrow_mut(), "{}", termion::cursor::Hide)?;
let (input_sender, input_receiver) = crossbeam_channel::unbounded(); let (input_sender, input_receiver) = crossbeam_channel::unbounded();
let (resize_sender, resize_receiver) = crossbeam_channel::bounded(0); let (resize_sender, resize_receiver) = crossbeam_channel::bounded(0);
@ -96,7 +92,7 @@ impl Backend {
resize_receiver, resize_receiver,
}; };
Box::new(c) Ok(Box::new(c))
} }
fn apply_colors(&self, colors: theme::ColorPair) { fn apply_colors(&self, colors: theme::ColorPair) {

View File

@ -75,14 +75,14 @@ impl<F: FnOnce(&mut Cursive) -> () + Send> CbFunc for F {
#[cfg(feature = "termion-backend")] #[cfg(feature = "termion-backend")]
impl Default for Cursive { impl Default for Cursive {
fn default() -> Self { fn default() -> Self {
Self::termion() Self::termion().unwrap()
} }
} }
#[cfg(all(not(feature = "termion-backend"), feature = "pancurses-backend"))] #[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().unwrap()
} }
} }
@ -105,11 +105,23 @@ impl Default for Cursive {
))] ))]
impl Default for Cursive { impl Default for Cursive {
fn default() -> Self { fn default() -> Self {
Self::ncurses() Self::ncurses().unwrap()
} }
} }
impl Cursive { impl Cursive {
/// Shortcut for `Cursive::try_new` with non-failible init function.
///
/// You probably don't want to use this function directly. Instead,
/// `Cursive::default()` or `Cursive::ncurses()` may be what you're
/// looking for.
pub fn new<F>(backend_init: F) -> Self
where
F: FnOnce() -> Box<dyn backend::Backend>,
{
Self::try_new::<_, ()>(|| Ok(backend_init())).unwrap()
}
/// Creates a new Cursive root, and initialize the back-end. /// Creates a new Cursive root, and initialize the back-end.
/// ///
/// * If you just want a cursive instance, use `Cursive::default()`. /// * If you just want a cursive instance, use `Cursive::default()`.
@ -128,17 +140,15 @@ impl Cursive {
/// # use cursive::{Cursive, backend}; /// # use cursive::{Cursive, backend};
/// let siv = Cursive::new(backend::dummy::Backend::init); // equivalent to Cursive::dummy() /// let siv = Cursive::new(backend::dummy::Backend::init); // equivalent to Cursive::dummy()
/// ``` /// ```
pub fn new<F>(backend_init: F) -> Self pub fn try_new<F, E>(backend_init: F) -> Result<Self, E>
where where
F: FnOnce() -> Box<dyn backend::Backend>, F: FnOnce() -> Result<Box<dyn backend::Backend>, E>,
{ {
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 backend = backend_init(); backend_init().map(|backend| Cursive {
Cursive {
autorefresh: false, autorefresh: false,
theme, theme,
screens: vec![views::StackView::new()], screens: vec![views::StackView::new()],
@ -151,25 +161,25 @@ impl Cursive {
cb_sink, cb_sink,
backend, backend,
user_data: Box::new(()), user_data: Box::new(()),
} })
} }
/// Creates a new Cursive root using a ncurses backend. /// Creates a new Cursive root using a ncurses backend.
#[cfg(feature = "ncurses-backend")] #[cfg(feature = "ncurses-backend")]
pub fn ncurses() -> Self { pub fn ncurses() -> std::io::Result<Self> {
Self::new(backend::curses::n::Backend::init) Self::try_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-backend")] #[cfg(feature = "pancurses-backend")]
pub fn pancurses() -> Self { pub fn pancurses() -> std::io::Result<Self> {
Self::new(backend::curses::pan::Backend::init) Self::try_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-backend")] #[cfg(feature = "termion-backend")]
pub fn termion() -> Self { pub fn termion() -> std::io::Result<Self> {
Self::new(backend::termion::Backend::init) Self::try_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.