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