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]
|
||||
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,
|
||||
/// High-intensity blue.
|
||||
HiBlue,
|
||||
/// High-intensity megenta.
|
||||
/// High-intensity magenta.
|
||||
HiMagenta,
|
||||
/// High-intensity cyan.
|
||||
HiCyan,
|
||||
|
@ -42,6 +42,13 @@ pub enum Color {
|
|||
use 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 {
|
||||
self.debug_check();
|
||||
|
||||
|
|
69
src/input.rs
69
src/input.rs
|
@ -1,8 +1,70 @@
|
|||
use std::io::{Read, Write};
|
||||
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.
|
||||
pub trait TermRead {
|
||||
/// An iterator over key inputs.
|
||||
#[cfg(feature = "nightly")]
|
||||
fn keys(self) -> Keys<Self> where Self: Sized;
|
||||
|
||||
/// Read a password.
|
||||
///
|
||||
/// 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 {
|
||||
#[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> {
|
||||
let _raw = try!(writer.into_raw_mode());
|
||||
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"))]
|
||||
extern crate libc;
|
||||
|
@ -10,7 +20,9 @@ mod control;
|
|||
pub use control::TermWrite;
|
||||
|
||||
mod input;
|
||||
pub use input::TermRead;
|
||||
pub use input::{TermRead, Key};
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use input::Keys;
|
||||
|
||||
mod error;
|
||||
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 {
|
||||
/// Switch to raw mode.
|
||||
///
|
||||
|
|
Loading…
Reference in New Issue