Add methods to initialize backends from stdio

This commit is contained in:
Alexandre Bury 2020-08-04 13:54:57 -07:00
parent b4dfaf4d9f
commit bf25cb90ae
4 changed files with 58 additions and 8 deletions

View File

@ -1,11 +1,12 @@
# Changelog # Changelog
## Next version (cursive-core 0.1.2) ## Next version (cursive-core 0.1.2, cursive 0.15.1)
### API updates ### API updates
- Add `ProgressBar::set_{min,max,range,counter,label}` for non-chained API. - Add `ProgressBar::set_{min,max,range,counter,label}` for non-chained API.
- Derive Clone, Copy, Debug, PartialEq, Hash for more types. - Derive Clone, Copy, Debug, PartialEq, Hash for more types.
- Add backend initializers using other files than /dev/tty for ncurses and termion.
### Improvements ### Improvements

View File

@ -193,6 +193,7 @@ impl Backend {
{ {
enable_raw_mode()?; enable_raw_mode()?;
// TODO: Use the stdout we define down there
execute!( execute!(
io::stdout(), io::stdout(),
EnterAlternateScreen, EnterAlternateScreen,

View File

@ -56,7 +56,24 @@ fn write_to_tty(bytes: &[u8]) -> io::Result<()> {
impl Backend { impl Backend {
/// Creates a new ncurses-based backend. /// Creates a new ncurses-based backend.
///
/// Uses `/dev/tty` for input/output.
pub fn init() -> io::Result<Box<dyn backend::Backend>> { pub fn init() -> io::Result<Box<dyn backend::Backend>> {
Self::init_with_files("/dev/tty", "/dev/tty")
}
/// Creates a new ncurses-based backend.
///
/// Uses stdin/stdout for input/output.
pub fn init_stdio() -> io::Result<Box<dyn backend::Backend>> {
Self::init_with_files("/dev/stdin", "/dev/stdout")
}
/// Creates a new ncurses-based backend using the given files for input/output.
pub fn init_with_files(
input_path: &str,
output_path: &str,
) -> 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())
@ -79,10 +96,17 @@ impl Backend {
// Don't output to standard IO, directly feed into /dev/tty // Don't output to standard IO, directly feed into /dev/tty
// This leaves stdin and stdout usable for other purposes. // This leaves stdin and stdout usable for other purposes.
let tty_path = CString::new("/dev/tty").unwrap(); let input = {
let mode = CString::new("r+").unwrap(); let mode = CString::new("r").unwrap();
let tty = unsafe { libc::fopen(tty_path.as_ptr(), mode.as_ptr()) }; let path = CString::new(input_path).unwrap();
ncurses::newterm(None, tty, tty); unsafe { libc::fopen(path.as_ptr(), mode.as_ptr()) }
};
let output = {
let mode = CString::new("w").unwrap();
let path = CString::new(output_path).unwrap();
unsafe { libc::fopen(path.as_ptr(), mode.as_ptr()) }
};
ncurses::newterm(None, output, input);
// Enable keypad (like arrows) // Enable keypad (like arrows)
ncurses::keypad(ncurses::stdscr(), true); ncurses::keypad(ncurses::stdscr(), true);

View File

@ -29,6 +29,7 @@ use std::thread;
/// Backend using termion /// Backend using termion
pub struct Backend { pub struct Backend {
// Do we want to make this generic on the writer?
terminal: terminal:
RefCell<AlternateScreen<MouseTerminal<RawTerminal<BufWriter<File>>>>>, RefCell<AlternateScreen<MouseTerminal<RawTerminal<BufWriter<File>>>>>,
current_style: Cell<theme::ColorPair>, current_style: Cell<theme::ColorPair>,
@ -42,12 +43,35 @@ pub struct Backend {
impl Backend { impl Backend {
/// Creates a new termion-based backend. /// Creates a new termion-based backend.
///
/// Uses `/dev/tty` for input and output.
pub fn init() -> std::io::Result<Box<dyn backend::Backend>> { pub fn init() -> std::io::Result<Box<dyn backend::Backend>> {
Self::init_with_files(
File::open("/dev/tty")?,
File::create("/dev/tty")?,
)
}
/// Creates a new termion-based backend.
///
/// Uses `stdin` and `stdout` for input/output.
pub fn init_stdio() -> std::io::Result<Box<dyn backend::Backend>> {
Self::init_with_files(
File::open("/dev/stdin")?,
File::create("/dev/stdout")?,
)
}
/// Creates a new termion-based backend using the given input and output files.
pub fn init_with_files(
input: File,
output: File,
) -> 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(8_000_000, File::create("/dev/tty")?) BufWriter::with_capacity(8_000_000, output_file?)
.into_raw_mode()?, .into_raw_mode()?,
))); )));
@ -67,7 +91,7 @@ impl Backend {
// We want nonblocking input, but termion is blocking by default // We want nonblocking input, but termion is blocking by default
// Read input from a separate thread // Read input from a separate thread
thread::spawn(move || { thread::spawn(move || {
let input = std::fs::File::open("/dev/tty").unwrap(); let input = input_file.unwrap();
let mut events = input.events(); let mut events = input.events();
// Take all the events we can // Take all the events we can
@ -244,7 +268,7 @@ impl backend::Backend for Backend {
fn screen_size(&self) -> Vec2 { fn screen_size(&self) -> Vec2 {
// TODO: termion::terminal_size currently requires stdout. // TODO: termion::terminal_size currently requires stdout.
// When available, we should try to use /dev/tty instead. // When available, we should try to use self.terminal or something instead.
let (x, y) = termion::terminal_size().unwrap_or((1, 1)); let (x, y) = termion::terminal_size().unwrap_or((1, 1));
(x, y).into() (x, y).into()
} }