Introduce 'Key', which can decode special key input
This commit is contained in:
		
							parent
							
								
									d7a3e607ba
								
							
						
					
					
						commit
						6f1621d2d0
					
				|  | @ -5,3 +5,6 @@ authors = ["Ticki <Ticki@users.noreply.github.com>"] | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| libc = "0.2.8" | libc = "0.2.8" | ||||||
|  | 
 | ||||||
|  | [features] | ||||||
|  | nightly = [] | ||||||
|  |  | ||||||
|  | @ -0,0 +1,35 @@ | ||||||
|  | extern crate libterm; | ||||||
|  | 
 | ||||||
|  | use libterm::{TermRead, TermWrite, IntoRawMode, Color, Style, Key}; | ||||||
|  | use std::io::{Read, Write, stdout, stdin}; | ||||||
|  | 
 | ||||||
|  | fn main() { | ||||||
|  |     let stdin = stdin(); | ||||||
|  |     let mut stdout = stdout().into_raw_mode().unwrap(); | ||||||
|  | 
 | ||||||
|  |     stdout.clear(); | ||||||
|  |     stdout.goto(0, 0); | ||||||
|  |     stdout.write(b"q to exit. Type stuff, use alt, and so on."); | ||||||
|  |     stdout.hide_cursor(); | ||||||
|  |     stdout.flush(); | ||||||
|  | 
 | ||||||
|  |     for c in stdin.keys() { | ||||||
|  |         stdout.goto(5, 5); | ||||||
|  |         stdout.clear_line(); | ||||||
|  |         match c { | ||||||
|  |             Key::Char('q') => break, | ||||||
|  |             Key::Char(c) => println!("{}", c), | ||||||
|  |             Key::Alt(c) => println!("^{}", c), | ||||||
|  |             Key::Left => println!("←"), | ||||||
|  |             Key::Right => println!("→"), | ||||||
|  |             Key::Up => println!("∆"), | ||||||
|  |             Key::Down => println!("∇"), | ||||||
|  |             Key::Backspace => println!("×"), | ||||||
|  |             Key::Invalid => println!("???"), | ||||||
|  |             Key::Error => println!("ERROR"), | ||||||
|  |         } | ||||||
|  |         stdout.flush(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     stdout.show_cursor(); | ||||||
|  | } | ||||||
|  | @ -27,7 +27,7 @@ pub enum Color { | ||||||
|     HiYellow, |     HiYellow, | ||||||
|     /// High-intensity blue.
 |     /// High-intensity blue.
 | ||||||
|     HiBlue, |     HiBlue, | ||||||
|     /// High-intensity megenta.
 |     /// High-intensity magenta.
 | ||||||
|     HiMagenta, |     HiMagenta, | ||||||
|     /// High-intensity cyan.
 |     /// High-intensity cyan.
 | ||||||
|     HiCyan, |     HiCyan, | ||||||
|  | @ -42,6 +42,13 @@ pub enum Color { | ||||||
| use Color::*; | use Color::*; | ||||||
| 
 | 
 | ||||||
| impl Color { | impl Color { | ||||||
|  |     /// Get the corresponding ANSI value.
 | ||||||
|  |     ///
 | ||||||
|  |     /// Panics
 | ||||||
|  |     /// ======
 | ||||||
|  |     ///
 | ||||||
|  |     /// This method will panic in debug mode, if `self` is invalid (that is, the values are out of
 | ||||||
|  |     /// bound).
 | ||||||
|     pub fn to_ansi_val(self) -> u8 { |     pub fn to_ansi_val(self) -> u8 { | ||||||
|         self.debug_check(); |         self.debug_check(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										69
									
								
								src/input.rs
								
								
								
								
							
							
						
						
									
										69
									
								
								src/input.rs
								
								
								
								
							|  | @ -1,8 +1,70 @@ | ||||||
| use std::io::{Read, Write}; | use std::io::{Read, Write}; | ||||||
| use {IntoRawMode, TerminalError}; | use {IntoRawMode, TerminalError}; | ||||||
| 
 | 
 | ||||||
|  | #[cfg(feature = "nightly")] | ||||||
|  | use std::io::Chars; | ||||||
|  | 
 | ||||||
|  | /// A key.
 | ||||||
|  | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | ||||||
|  | pub enum Key { | ||||||
|  |     /// Backspace.
 | ||||||
|  |     Backspace, | ||||||
|  |     /// Left arrow.
 | ||||||
|  |     Left, | ||||||
|  |     /// Right arrow.
 | ||||||
|  |     Right, | ||||||
|  |     /// Up arrow.
 | ||||||
|  |     Up, | ||||||
|  |     /// Down arrow.
 | ||||||
|  |     Down, | ||||||
|  |     /// Alt modified character.
 | ||||||
|  |     Alt(char), | ||||||
|  |     /// Normal character.
 | ||||||
|  |     Char(char), | ||||||
|  |     /// Invalid character code.
 | ||||||
|  |     Invalid, | ||||||
|  |     // TODO handle errors better?
 | ||||||
|  |     /// IO error.
 | ||||||
|  |     Error, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "nightly")] | ||||||
|  | /// An iterator over input keys.
 | ||||||
|  | pub struct Keys<R> { | ||||||
|  |     chars: Chars<R>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "nightly")] | ||||||
|  | impl<R: Read> Iterator for Keys<R> { | ||||||
|  |     type Item = Key; | ||||||
|  | 
 | ||||||
|  |     fn next(&mut self) -> Option<Key> { | ||||||
|  |         match self.chars.next() { | ||||||
|  |             Some(Ok('\x1B')) => Some(match self.chars.next() { | ||||||
|  |                 Some(Ok('[')) => match self.chars.next() { | ||||||
|  |                     Some(Ok('D')) => Key::Left, | ||||||
|  |                     Some(Ok('C')) => Key::Right, | ||||||
|  |                     Some(Ok('A')) => Key::Up, | ||||||
|  |                     Some(Ok('B')) => Key::Down, | ||||||
|  |                     _ => Key::Invalid, | ||||||
|  |                 }, | ||||||
|  |                 Some(Ok(c)) => Key::Alt(c), | ||||||
|  |                 Some(Err(_)) | None => Key::Invalid, | ||||||
|  |             }), | ||||||
|  |             Some(Ok('\x7F')) => Some(Key::Backspace), | ||||||
|  |             Some(Ok(c)) => Some(Key::Char(c)), | ||||||
|  |             None => None, | ||||||
|  |             Some(Err(_)) => Some(Key::Error), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// Extension to `Read` trait.
 | /// Extension to `Read` trait.
 | ||||||
| pub trait TermRead { | pub trait TermRead { | ||||||
|  |     /// An iterator over key inputs.
 | ||||||
|  |     #[cfg(feature = "nightly")] | ||||||
|  |     fn keys(self) -> Keys<Self> where Self: Sized; | ||||||
|  | 
 | ||||||
|     /// Read a password.
 |     /// Read a password.
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// EOT and ETX will abort the prompt, returning `None`. Newline or carriage return will
 |     /// EOT and ETX will abort the prompt, returning `None`. Newline or carriage return will
 | ||||||
|  | @ -11,6 +73,13 @@ pub trait TermRead { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<R: Read> TermRead for R { | impl<R: Read> TermRead for R { | ||||||
|  |     #[cfg(feature = "nightly")] | ||||||
|  |     fn keys(self) -> Keys<R> { | ||||||
|  |         Keys { | ||||||
|  |             chars: self.chars(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     fn read_passwd<W: Write>(&mut self, writer: &mut W) -> Result<Option<String>, TerminalError> { |     fn read_passwd<W: Write>(&mut self, writer: &mut W) -> Result<Option<String>, TerminalError> { | ||||||
|         let _raw = try!(writer.into_raw_mode()); |         let _raw = try!(writer.into_raw_mode()); | ||||||
|         let mut passbuf = Vec::with_capacity(30); |         let mut passbuf = Vec::with_capacity(30); | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								src/lib.rs
								
								
								
								
							
							
						
						
									
										16
									
								
								src/lib.rs
								
								
								
								
							|  | @ -1,4 +1,14 @@ | ||||||
| #[warn(missing_docs)] | //! Libterm is a pure Rust library for reading, manipulating, and handling terminals.
 | ||||||
|  | //!
 | ||||||
|  | //! This crate is not stable, yet. However, if you do want stability, you should specify the
 | ||||||
|  | //! revision (commit hash) in your `Cargo.toml`, this way builds are complete reproducible, and won't
 | ||||||
|  | //! break.
 | ||||||
|  | 
 | ||||||
|  | #![cfg_attr(feature = "nightly",
 | ||||||
|  |             feature(io))] | ||||||
|  | 
 | ||||||
|  | #![warn(missing_docs)] | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| #[cfg(not(target_os = "redox"))] | #[cfg(not(target_os = "redox"))] | ||||||
| extern crate libc; | extern crate libc; | ||||||
|  | @ -10,7 +20,9 @@ mod control; | ||||||
| pub use control::TermWrite; | pub use control::TermWrite; | ||||||
| 
 | 
 | ||||||
| mod input; | mod input; | ||||||
| pub use input::TermRead; | pub use input::{TermRead, Key}; | ||||||
|  | #[cfg(feature = "nightly")] | ||||||
|  | pub use input::Keys; | ||||||
| 
 | 
 | ||||||
| mod error; | mod error; | ||||||
| pub use error::TerminalError; | pub use error::TerminalError; | ||||||
|  |  | ||||||
|  | @ -49,6 +49,7 @@ impl<W> DerefMut for TerminalRestorer<W> { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// Types which can be converted into "raw mode".
 | ||||||
| pub trait IntoRawMode: Sized { | pub trait IntoRawMode: Sized { | ||||||
|     /// Switch to raw mode.
 |     /// Switch to raw mode.
 | ||||||
|     ///
 |     ///
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue