use crate::error::{Error, Result};
use std::fmt;
use std::io::{self, Read};
use std::ops::{Deref, DerefMut};
use std::os::unix::io::AsRawFd;
use std::str;
pub struct StdinRawMode {
stdin: io::Stdin,
orig: termios::Termios,
}
impl StdinRawMode {
pub fn new() -> Result<StdinRawMode> {
use termios::*;
let stdin = io::stdin();
let fd = stdin.as_raw_fd();
let mut termios = Termios::from_fd(fd)?;
let orig = termios;
termios.c_lflag &= !(ECHO | ICANON | ISIG | IEXTEN);
termios.c_iflag &= !(IXON | ICRNL | BRKINT | INPCK | ISTRIP);
termios.c_oflag &= !OPOST;
termios.c_cflag |= CS8;
termios.c_cc[VMIN] = 0;
termios.c_cc[VTIME] = 1;
tcsetattr(fd, TCSAFLUSH, &termios)?;
Ok(StdinRawMode { stdin, orig })
}
pub fn input_keys(self) -> InputSequences {
InputSequences { stdin: self }
}
}
impl Drop for StdinRawMode {
fn drop(&mut self) {
termios::tcsetattr(self.stdin.as_raw_fd(), termios::TCSAFLUSH, &self.orig).unwrap();
}
}
impl Deref for StdinRawMode {
type Target = io::Stdin;
fn deref(&self) -> &Self::Target {
&self.stdin
}
}
impl DerefMut for StdinRawMode {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.stdin
}
}
#[derive(PartialEq, Debug, Clone)]
pub enum KeySeq {
Unidentified,
Utf8Key(char),
Key(u8),
LeftKey,
RightKey,
UpKey,
DownKey,
PageUpKey,
PageDownKey,
HomeKey,
EndKey,
DeleteKey,
Cursor(usize, usize),
}
impl fmt::Display for KeySeq {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use KeySeq::*;
match self {
Unidentified => write!(f, "UNKNOWN"),
Key(b' ') => write!(f, "SPACE"),
Key(b) if b.is_ascii_control() => write!(f, "\\x{:x}", b),
Key(b) => write!(f, "{}", *b as char),
Utf8Key(c) => write!(f, "{}", c),
LeftKey => write!(f, "LEFT"),
RightKey => write!(f, "RIGHT"),
UpKey => write!(f, "UP"),
DownKey => write!(f, "DOWN"),
PageUpKey => write!(f, "PAGEUP"),
PageDownKey => write!(f, "PAGEDOWN"),
HomeKey => write!(f, "HOME"),
EndKey => write!(f, "END"),
DeleteKey => write!(f, "DELETE"),
Cursor(r, c) => write!(f, "CURSOR({},{})", r, c),
}
}
}
#[derive(PartialEq, Debug, Clone)]
pub struct InputSeq {
pub key: KeySeq,
pub ctrl: bool,
pub alt: bool,
}
impl InputSeq {
pub fn new(key: KeySeq) -> Self {
Self {
key,
ctrl: false,
alt: false,
}
}
pub fn ctrl(key: KeySeq) -> Self {
Self {
key,
ctrl: true,
alt: false,
}
}
pub fn alt(key: KeySeq) -> Self {
Self {
key,
ctrl: false,
alt: true,
}
}
}
impl fmt::Display for InputSeq {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.ctrl {
write!(f, "C-")?;
}
if self.alt {
write!(f, "M-")?;
}
write!(f, "{}", self.key)
}
}
pub struct InputSequences {
stdin: StdinRawMode,
}
impl InputSequences {
fn read_byte(&mut self) -> Result<Option<u8>> {
let mut one_byte: [u8; 1] = [0];
Ok(if self.stdin.read(&mut one_byte)? == 0 {
None
} else {
Some(one_byte[0])
})
}
fn decode_escape_sequence(&mut self) -> Result<InputSeq> {
use KeySeq::*;
match self.read_byte()? {
Some(b'[') => { }
Some(b) if b.is_ascii_control() => return Ok(InputSeq::new(Key(0x1b))),
Some(b) => {
let mut seq = self.decode(b)?;
seq.alt = true;
return Ok(seq);
}
None => return Ok(InputSeq::new(Key(0x1b))),
};
let mut buf = vec![];
let cmd = loop {
if let Some(b) = self.read_byte()? {
match b {
b'A' | b'B' | b'C' | b'D' | b'F' | b'H' | b'K' | b'J' | b'R' | b'c' | b'f'
| b'g' | b'h' | b'l' | b'm' | b'n' | b'q' | b't' | b'y' | b'~' => break b,
_ => buf.push(b),
}
} else {
return Ok(InputSeq::new(Unidentified));
}
};
fn parse_bytes_as_usize(b: &[u8]) -> Option<usize> {
str::from_utf8(b).ok().and_then(|s| s.parse().ok())
}
let mut args = buf.split(|b| *b == b';');
match cmd {
b'R' => {
let mut i = args.filter_map(parse_bytes_as_usize);
match (i.next(), i.next()) {
(Some(r), Some(c)) => Ok(InputSeq::new(Cursor(r, c))),
_ => Ok(InputSeq::new(Unidentified)),
}
}
b'A' | b'B' | b'C' | b'D' => {
let key = match cmd {
b'A' => UpKey,
b'B' => DownKey,
b'C' => RightKey,
b'D' => LeftKey,
_ => unreachable!(),
};
let ctrl = args.next() == Some(b"1") && args.next() == Some(b"5");
let alt = false;
Ok(InputSeq { key, ctrl, alt })
}
b'~' => {
match args.next() {
Some(b"5") => Ok(InputSeq::new(PageUpKey)),
Some(b"6") => Ok(InputSeq::new(PageDownKey)),
Some(b"1") | Some(b"7") => Ok(InputSeq::new(HomeKey)),
Some(b"4") | Some(b"8") => Ok(InputSeq::new(EndKey)),
Some(b"3") => Ok(InputSeq::new(DeleteKey)),
_ => Ok(InputSeq::new(Unidentified)),
}
}
b'H' | b'F' => {
let key = match cmd {
b'H' => HomeKey,
b'F' => EndKey,
_ => unreachable!(),
};
let ctrl = args.next() == Some(b"1") && args.next() == Some(b"5");
let alt = false;
Ok(InputSeq { key, ctrl, alt })
}
_ => unreachable!(),
}
}
fn decode_utf8(&mut self, b: u8) -> Result<InputSeq> {
let mut buf = [0; 4];
buf[0] = b;
let mut len = 1;
loop {
if let Some(b) = self.read_byte()? {
buf[len] = b;
len += 1;
} else {
return Err(Error::NotUtf8Input(buf[..len].to_vec()));
}
if let Ok(s) = str::from_utf8(&buf) {
return Ok(InputSeq::new(KeySeq::Utf8Key(s.chars().next().unwrap())));
}
if len == 4 {
return Err(Error::NotUtf8Input(buf.to_vec()));
}
}
}
fn decode(&mut self, b: u8) -> Result<InputSeq> {
use KeySeq::*;
match b {
0x00..=0x1f => match b {
0x1b => self.decode_escape_sequence(),
0x00 | 0x1f => Ok(InputSeq::ctrl(Key(b | 0b0010_0000))),
0x01c | 0x01d => Ok(InputSeq::ctrl(Key(b | 0b0100_0000))),
_ => Ok(InputSeq::ctrl(Key(b | 0b0110_0000))),
},
0x20..=0x7f => Ok(InputSeq::new(Key(b))),
0x80..=0x9f => Ok(InputSeq::new(KeySeq::Unidentified)),
0xa0..=0xff => self.decode_utf8(b),
}
}
fn read_seq(&mut self) -> Result<InputSeq> {
if let Some(b) = self.read_byte()? {
self.decode(b)
} else {
Ok(InputSeq::new(KeySeq::Unidentified))
}
}
}
impl Iterator for InputSequences {
type Item = Result<InputSeq>;
fn next(&mut self) -> Option<Self::Item> {
Some(self.read_seq())
}
}