added read_char, read_line, read_async, read_async until for unix. Not 100% tested.

This commit is contained in:
Timon 2018-07-21 11:11:31 +00:00
parent 07502c016d
commit 9df976ed29
9 changed files with 257 additions and 120 deletions

View File

@ -23,11 +23,24 @@ mod input;
use input::keyboard::{async_input, input as stdin};
use crossterm::raw::IntoRawMode;
fn main()
{
async_input::read_async();
let context = Context::new();
{
// let screen = ::crossterm::screen::AlternateScreen::from(context.clone());
// screen.into_raw_mode(context.clone());
async_input::async_reading_on_alternate_screen();
// async_input::test();
// stdin::t();
// stdin::read_line();
// stdin::read_char();
// stdin::read_char();
}
}

View File

@ -2,12 +2,18 @@ extern crate crossterm;
use self::crossterm::input::input;
use self::crossterm::Context;
use self::crossterm::Crossterm;
use crossterm::terminal::ClearType;
use std::{thread, time};
use std::io::Read;
use crossterm::raw::IntoRawMode;
// this will capture the input until the given key was pressed.
pub fn capture_input_until_a_certain_char_async()
use std::io::{Read, Write, stdout};
use std::time::Duration;
/// this will capture the input until the given key.
pub fn read_async_until()
{
let context = Context::new();
let input = input(&context);
@ -18,8 +24,15 @@ pub fn capture_input_until_a_certain_char_async()
{
let a = stdin.next();
println!("pressed key: {:?}", a);
if let Some(Ok(b'\r')) = a {
println!("The enter key is hit and program is not listening to input anymore.");
break;
}
if let Some(Ok(b'x')) = a {
println!("The key: x was pressed.");
println!("The key: x was pressed and program is terminated.");
break;
}
@ -27,7 +40,7 @@ pub fn capture_input_until_a_certain_char_async()
}
}
// this will capture an character input until the given key was pressed.
/// this will read pressed characters async until `x` is typed .
pub fn read_async()
{
let context = Context::new();
@ -39,13 +52,98 @@ pub fn read_async()
{
let a = stdin.next();
println!("pressed: {:?}", a);
println!("pressed key: {:?}", a);
if let Some(Ok(b'x')) = a {
println!("The key: x was pressed.");
println!("The key: `x` was pressed and program is terminated.");
break;
}
thread::sleep(time::Duration::from_millis(50));
}
}
pub fn read_async_demo()
{
let crossterm = Crossterm::new();
// init some modules we use for this demo
let input = crossterm.input();
let terminal = crossterm.terminal();
let mut cursor = crossterm.cursor();
// put stdout in raw mode so that characters wil not be outputted.
let mut stdout = stdout().into_raw_mode(crossterm.context()).unwrap();
// this will setup the async reading.
let mut stdin = input.read_async().bytes();
// clear terminal and reset the cursor.
terminal.clear(ClearType::All);
cursor.goto(1, 1);
// loop until the enter key (\r) is pressed.
loop {
terminal.clear(ClearType::All);
cursor.goto(1, 1);
// get the next pressed key
let pressed_key = stdin.next();
write!(stdout, "\r{:?} <- Character pressed", pressed_key).unwrap();
// check if pressed key is enter (\r)
if let Some(Ok(b'\r')) = pressed_key {
break;
}
// wait 200 ms and reset cursor write
thread::sleep(Duration::from_millis(200));
}
}
pub fn async_reading_on_alternate_screen()
{
use crossterm::screen::AlternateScreen;
let crossterm = Crossterm::new();
// init some modules we use for this demo
let input = crossterm.input();
let terminal = crossterm.terminal();
let mut cursor = crossterm.cursor();
// switch to alternate screen
let mut alternate_screen = AlternateScreen::from(crossterm.context());
// put alternate screen in raw mode so that characters wil not be outputted.
let mut raw_screen = alternate_screen.into_raw_mode(crossterm.context());
// this will setup the async reading.
let mut stdin = input.read_async().bytes();
// clear terminal and reset the cursor.
terminal.clear(ClearType::All);
cursor.goto(1, 1);
// panic!();
// loop until the enter key (\r) is pressed.
loop {
terminal.clear(ClearType::All);
cursor.goto(1, 1);
// get the next pressed key
let pressed_key = stdin.next();
write!(alternate_screen, "\r{:?} <- Character pressed", pressed_key).unwrap();
// check if pressed key is enter (\r)
if let Some(Ok(b'\r')) = pressed_key {
break;
}
// wait 200 ms and reset cursor write
thread::sleep(Duration::from_millis(200));
}
}

View File

@ -44,7 +44,6 @@ impl TerminalInput
pub fn read_async(&self) -> AsyncReader
{
self.terminal_input.read_async()
// todo: async reading
}
pub fn read_until_async(&self, delimiter: u8) -> AsyncReader

View File

@ -7,8 +7,9 @@ use self::windows_input::WindowsInput;
#[cfg(target_os = "windows")]
mod windows_input;
#[cfg(target_os = "windows")]
#[cfg(not(target_os = "windows"))]
use self::unix_input::UnixInput;
#[cfg(not(target_os = "windows"))]
mod unix_input;

View File

@ -3,124 +3,76 @@ use std::io::Write;
use std::char;
use std::sync::mpsc;
use std::thread;
use std::io::Read;
use super::super::terminal::terminal;
//use super::super::kernel::unix_kernel::terminal::get_tty;
use super::super::kernel::unix_kernel::terminal::{get_tty, read_char};
use super::{ Key, ITerminalInput, AsyncReader };
pub struct UnixInput;
impl UnixInput
{
pub fn new() -> UnixInput
{
UnixInput {}
}
}
// fn read_line(&self) -> io::Result<String>
// {
// let mut rv = String::new();
// io::stdin().read_line(&mut rv)?;
// let len = rv.trim_right_matches(&['\r', '\n'][..]).len();
// rv.truncate(len);
// Ok(rv)
// }
//
// fn read_char(&self) -> io::Result<char>
// {
// let mut buf = [0u8; 20];
// let mut termios = termios::Termios::from_fd(fd)?;
// let original = termios.clone();
// termios::cfmakeraw(&mut termios);
// termios::tcsetattr(fd, termios::TCSADRAIN, &termios)?;
// let rv = unsafe {
// let read = libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, 20);
// if read < 0 {
// Err(io::Error::last_os_error())
// } else if buf[0] == b'\x03' {
// Err(io::Error::new(io::ErrorKind::Interrupted, "read interrupted"))
// } else {
// Ok(key_from_escape_codes(&buf[..read as usize]))
// }
// };
// termios::tcsetattr(fd, termios::TCSADRAIN, &original)?;
//
// // if the user hit ^C we want to signal SIGINT to outselves.
// if let Err(ref err) = rv {
// if err.kind() == io::ErrorKind::Interrupted {
// unsafe { libc::raise(libc::SIGINT); }
// }
// }
//
// rv
// }
//
// fn read_pressed_key(&self) -> io::Result<Key>
// {
// use Context;
// let context = Context::new();
//
// let buf: [u8; 1024] = unsafe { ::std::mem::zeroed() };
//// reading::read(&mut buf, &context.screen_manager);
//
// Ok(Key::Unknown)
//// let pressed_char = unsafe { _getwch() };
////
//// // if 0 or 0xe0 we need to listen again because the next key will be an special key
//// if pressed_char == 0 || pressed_char == 0xe0 {
//// let special_key: i32 = unsafe { _getwch() };
//// println!("spkey {}",special_key);
//// return Ok(key_from_key_code(0x26));
//// } else {
//// match char::from_u32(pressed_char as u32)
//// {
//// Some(c) => return Ok(Key::Char(c)),
//// None => { panic!("Some error needs to be returned") }
//// }
//// }
// }
//
// fn read_async(&self) -> AsyncReader
// {
// let (send, recv) = mpsc::channel();
//
// thread::spawn(move || for i in get_tty().unwrap().bytes() {
//
// match i {
// Ok(byte) => {
// let end_of_stream = &byte == &delimiter;
// let send_error = send.send(Ok(byte)).is_err();
//
// if end_of_stream || send_error { return; }
// },
// Err(_) => { return; }
// }
// });
//
// AsyncReader { recv: recv }
// }
//
// fn read_until_async(&self, delimiter: u8) -> AsyncReader
// {
// let (tx, rx) = mpsc::channel();
//
// thread::spawn(move || {
// loop
// {
// let pressed_char: u8 = (unsafe { _getwch() }) as u8;
//
// let end_of_stream = (pressed_char == delimiter);
//
// // we could return error but maybe option to keep listening until valid character is inputted.
// if pressed_char == 0 || pressed_char == 0xe0 || end_of_stream {
// return;
// }
//
// tx.send(Ok(pressed_char as u8));
// }
// });
//
// AsyncReader { recv: rx }
// }
//}
}
impl ITerminalInput for UnixInput
{
fn read_line(&self) -> io::Result<String>
{
let mut rv = String::new();
io::stdin().read_line(&mut rv)?;
let len = rv.trim_right_matches(&['\r', '\n'][..]).len();
rv.truncate(len);
Ok(rv)
}
fn read_char(&self) -> io::Result<char>
{
read_char()
}
fn read_pressed_key(&self) -> io::Result<Key>
{
Ok(Key::Unknown)
}
fn read_async(&self) -> AsyncReader
{
let (send, recv) = mpsc::channel();
thread::spawn(move || for i in get_tty().unwrap().bytes() {
if send.send(i).is_err() {
return;
}
});
AsyncReader { recv: recv }
}
fn read_until_async(&self, delimiter: u8) -> AsyncReader
{
let (send, recv) = mpsc::channel();
thread::spawn(move || for i in get_tty().unwrap().bytes() {
match i {
Ok(byte) => {
let end_of_stream = &byte == &delimiter;
let send_error = send.send(Ok(byte)).is_err();
if end_of_stream || send_error { return; }
},
Err(_) => { return; }
}
});
AsyncReader { recv: recv }
}
}

View File

@ -3,12 +3,13 @@
pub use self::libc::termios;
use self::libc::{c_int, c_ushort, ioctl, STDOUT_FILENO, TIOCGWINSZ};
use state::commands::{IStateCommand, NoncanonicalModeCommand};
use termios::Termios;
use {libc, CommandManager, Context, StateManager};
use termios::{ Termios,cfmakeraw,tcsetattr,TCSADRAIN };
use std::io::Error;
use std::rc::Rc;
use std::{io, mem, fs};
use std::os::unix::io::AsRawFd;
/// A representation of the size of the current terminal.
#[repr(C)]
@ -140,6 +141,70 @@ pub fn get_tty() -> io::Result<fs::File> {
fs::OpenOptions::new().read(true).write(true).open("/dev/tty")
}
pub fn read_char() -> io::Result<char>
{
let mut buf = [0u8; 20];
// get tty raw handle.
let tty_f;
let fd = unsafe
{
if libc::isatty(libc::STDIN_FILENO) == 1
{
libc::STDIN_FILENO
} else {
tty_f = fs::File::open("/dev/tty")?;
tty_f.as_raw_fd()
}
};
let mut termios = Termios::from_fd(fd)?;
let original = termios.clone();
make_raw(&mut termios);
tcsetattr(fd, TCSADRAIN, &termios)?;
// read input and convert it to char
let rv = unsafe {
let read = libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, 20);
if read < 0 {
Err(io::Error::last_os_error())
} else if buf[0] == b'\x03' {
Err(io::Error::new(io::ErrorKind::Interrupted, "read interrupted"))
} else {
let mut pressed_char = Ok(' ');
if let Ok(s) = ::std::str::from_utf8(&buf[..read as usize])
{
if let Some(c) = s.chars().next()
{
pressed_char = Ok(c);
}
}else {
pressed_char = Err(io::Error::new(io::ErrorKind::Interrupted, "Could not parse char to utf8 char"));
}
pressed_char
}
};
tcsetattr(fd, TCSADRAIN, &original)?;
// if the user hit ^C we want to signal SIGINT to outselves.
if let Err(ref err) = rv {
if err.kind() == io::ErrorKind::Interrupted {
unsafe { libc::raise(libc::SIGINT); }
}
}
rv
}
pub fn exit() {
::std::process::exit(0);
}

View File

@ -27,6 +27,8 @@
use super::super::cursor;
use super::super::style;
use super::super::terminal::terminal;
use super::super::input::input;
use Context;
use std::fmt::Display;
@ -126,6 +128,11 @@ impl Crossterm {
return style::TerminalColor::new(self.context.clone());
}
pub fn input(&self) -> input::TerminalInput
{
return input::TerminalInput::new(self.context.clone());
}
/// Wraps an displayable object so it can be formatted with colors and attributes.
///
/// Check `/examples/color` in the library for more specific examples.

View File

@ -107,6 +107,7 @@ impl AlternateScreen {
context: context.clone(),
command_id: command_id,
};
screen.to_alternate();
return screen;
}
@ -134,6 +135,7 @@ impl AlternateScreen {
let mut mx = &state_manager.get(self.command_id);
{
let mut command = mx.lock().unwrap();
command.execute();
}
}