diff --git a/CHANGELOG.md b/CHANGELOG.md index c84ad8b..2165b62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ `find_id()` was added instead. - Breaking change: replaced `set_fps(i32)` with `set_autorefresh(bool)` - Breaking change: updated the Backend trait for a simpler input system +- Breaking change: `Cursive::{ncurses, pancurses, termion}` now return + `io::Result` instead of panicking. `Cursive::default()` still unwraps. + - Also added `Cursive::try_new` for failible backends. - Updated to Rust 2018 (now requires rustc > 1.31) - Add a logging implementation (`logger::init()`) and a `DebugConsole` (`cursive::toggle_debug_console()`) diff --git a/src/backend/blt.rs b/src/backend/blt.rs index ce1b06d..88bd573 100644 --- a/src/backend/blt.rs +++ b/src/backend/blt.rs @@ -32,6 +32,7 @@ pub struct Backend { impl Backend { /// Creates a new BearLibTerminal-based backend. pub fn init() -> Box { + // TODO: Add some error handling? terminal::open("Cursive", 80, 24); terminal::set(terminal::config::Window::empty().resizeable(true)); terminal::set(vec![ diff --git a/src/backend/curses/n.rs b/src/backend/curses/n.rs index f535bc3..525d304 100644 --- a/src/backend/curses/n.rs +++ b/src/backend/curses/n.rs @@ -58,13 +58,16 @@ fn write_to_tty(bytes: &[u8]) -> io::Result<()> { impl Backend { /// Creates a new ncurses-based backend. - pub fn init() -> Box { + pub fn init() -> io::Result> { // Check the $TERM variable. if std::env::var("TERM") .map(|var| var.is_empty()) .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. @@ -111,7 +114,7 @@ impl Backend { // This asks the terminal to provide us with mouse drag events // (Mouse move when a button is pressed). // 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 { current_style: Cell::new(ColorPair::from_256colors(0, 0)), @@ -121,7 +124,7 @@ impl Backend { input_buffer: None, }; - Box::new(c) + Ok(Box::new(c)) } /// Save a new color pair. diff --git a/src/backend/curses/pan.rs b/src/backend/curses/pan.rs index de69515..b77ee83 100644 --- a/src/backend/curses/pan.rs +++ b/src/backend/curses/pan.rs @@ -34,10 +34,12 @@ fn find_closest_pair(pair: ColorPair) -> (i16, i16) { impl Backend { /// Creates a new pancurses-based backend. - pub fn init() -> Box { + pub fn init() -> std::io::Result> { ::std::env::set_var("ESCDELAY", "25"); + // TODO: use pancurses::newterm() let window = pancurses::initscr(); + window.keypad(true); window.timeout(0); pancurses::noecho(); @@ -55,7 +57,7 @@ impl Backend { // (Mouse move when a button is pressed). // Replacing 1002 with 1003 would give us ANY mouse move. print!("\x1B[?1002h"); - stdout().flush().expect("could not flush stdout"); + stdout().flush()?; let c = Backend { current_style: Cell::new(ColorPair::from_256colors(0, 0)), @@ -66,7 +68,7 @@ impl Backend { window, }; - Box::new(c) + Ok(Box::new(c)) } /// Save a new color pair. diff --git a/src/backend/termion.rs b/src/backend/termion.rs index cfff8cf..2a890f2 100644 --- a/src/backend/termion.rs +++ b/src/backend/termion.rs @@ -43,20 +43,16 @@ pub struct Backend { impl Backend { /// Creates a new termion-based backend. - pub fn init() -> Box { + pub fn init() -> std::io::Result> { // Use a ~8MB buffer // Should be enough for a single screen most of the time. let terminal = RefCell::new(AlternateScreen::from(MouseTerminal::from( - BufWriter::with_capacity( - 8_000_000, - File::create("/dev/tty").unwrap(), - ) - .into_raw_mode() - .unwrap(), + BufWriter::with_capacity(8_000_000, File::create("/dev/tty")?) + .into_raw_mode()?, ))); - write!(terminal.borrow_mut(), "{}", termion::cursor::Hide).unwrap(); + write!(terminal.borrow_mut(), "{}", termion::cursor::Hide)?; let (input_sender, input_receiver) = crossbeam_channel::unbounded(); let (resize_sender, resize_receiver) = crossbeam_channel::bounded(0); @@ -96,7 +92,7 @@ impl Backend { resize_receiver, }; - Box::new(c) + Ok(Box::new(c)) } fn apply_colors(&self, colors: theme::ColorPair) { diff --git a/src/cursive.rs b/src/cursive.rs index 40e2780..4892cde 100644 --- a/src/cursive.rs +++ b/src/cursive.rs @@ -75,14 +75,14 @@ impl () + Send> CbFunc for F { #[cfg(feature = "termion-backend")] impl Default for Cursive { fn default() -> Self { - Self::termion() + Self::termion().unwrap() } } #[cfg(all(not(feature = "termion-backend"), feature = "pancurses-backend"))] impl Default for Cursive { fn default() -> Self { - Self::pancurses() + Self::pancurses().unwrap() } } @@ -105,11 +105,23 @@ impl Default for Cursive { ))] impl Default for Cursive { fn default() -> Self { - Self::ncurses() + Self::ncurses().unwrap() } } 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(backend_init: F) -> Self + where + F: FnOnce() -> Box, + { + Self::try_new::<_, ()>(|| Ok(backend_init())).unwrap() + } + /// Creates a new Cursive root, and initialize the back-end. /// /// * If you just want a cursive instance, use `Cursive::default()`. @@ -128,17 +140,15 @@ impl Cursive { /// # use cursive::{Cursive, backend}; /// let siv = Cursive::new(backend::dummy::Backend::init); // equivalent to Cursive::dummy() /// ``` - pub fn new(backend_init: F) -> Self + pub fn try_new(backend_init: F) -> Result where - F: FnOnce() -> Box, + F: FnOnce() -> Result, E>, { let theme = theme::load_default(); let (cb_sink, cb_source) = crossbeam_channel::unbounded(); - let backend = backend_init(); - - Cursive { + backend_init().map(|backend| Cursive { autorefresh: false, theme, screens: vec![views::StackView::new()], @@ -151,25 +161,25 @@ impl Cursive { cb_sink, backend, user_data: Box::new(()), - } + }) } /// Creates a new Cursive root using a ncurses backend. #[cfg(feature = "ncurses-backend")] - pub fn ncurses() -> Self { - Self::new(backend::curses::n::Backend::init) + pub fn ncurses() -> std::io::Result { + Self::try_new(backend::curses::n::Backend::init) } /// Creates a new Cursive root using a pancurses backend. #[cfg(feature = "pancurses-backend")] - pub fn pancurses() -> Self { - Self::new(backend::curses::pan::Backend::init) + pub fn pancurses() -> std::io::Result { + Self::try_new(backend::curses::pan::Backend::init) } /// Creates a new Cursive root using a termion backend. #[cfg(feature = "termion-backend")] - pub fn termion() -> Self { - Self::new(backend::termion::Backend::init) + pub fn termion() -> std::io::Result { + Self::try_new(backend::termion::Backend::init) } /// Creates a new Cursive root using a bear-lib-terminal backend.