use nix::fcntl::fcntl;
use nix::fcntl::FcntlArg;
use nix::fcntl::OFlag;
use nix::sys::select;
use nix::sys::time::TimeVal;
use nix::sys::time::TimeValLike;
use std::os::unix::prelude::FromRawFd;
use std::os::unix::prelude::RawFd;
use std::time::Duration;
use std::{fs::File, io::Read, os::unix::prelude::AsRawFd};
use crate::error::TermixError;
use crate::key::Key;
use crate::raw::get_tty;
const KEY_WAIT: Duration = Duration::from_millis(10);
fn duration_to_timeval(duration: Duration) -> TimeVal {
let sec = duration.as_secs() * 1000 + (duration.subsec_millis() as u64);
TimeVal::milliseconds(sec as i64)
}
pub fn wait_until_ready(
fd: RawFd,
signal_fd: Option<RawFd>,
timeout: Duration,
) -> Result<(), TermixError> {
let mut timeout_spec = if timeout == Duration::new(0, 0) {
None
} else {
Some(duration_to_timeval(timeout))
};
let mut fdset = select::FdSet::new();
fdset.insert(fd);
if let Some(fd) = signal_fd {
fdset.insert(fd)
}
let n = select::select(None, &mut fdset, None, None, &mut timeout_spec)
.map_err(|_| TermixError::KeyListener)?;
if n < 1 {
Err(TermixError::KeyListener) } else if fdset.contains(fd) {
Ok(())
} else {
Err(TermixError::KeyListener) }
}
pub struct KeyBoard {
file: Box<File>,
pub byte_buf: Vec<u8>,
sig_rx: File,
next_key: Option<Key>,
}
impl Default for KeyBoard {
fn default() -> Self {
Self::new()
}
}
impl KeyBoard {
pub fn new() -> KeyBoard {
let file = get_tty();
let (rx, _tx) = nix::unistd::pipe().expect("failed to set pipe");
let flag = fcntl(rx, FcntlArg::F_GETFL).expect("Get fcntl failed");
let mut flag = OFlag::from_bits_truncate(flag);
flag.insert(OFlag::O_NONBLOCK);
let _ = fcntl(rx, FcntlArg::F_SETFL(flag));
let flag = fcntl(file.as_raw_fd(), FcntlArg::F_GETFL).expect("Get fcntl failed");
let mut flag = OFlag::from_bits_truncate(flag);
flag.insert(OFlag::O_NONBLOCK);
let _ = fcntl(file.as_raw_fd(), FcntlArg::F_SETFL(flag));
KeyBoard {
file,
sig_rx: unsafe { File::from_raw_fd(rx) },
byte_buf: vec![],
next_key: None,
}
}
#[allow(dead_code)]
pub fn next_key(&mut self) -> Result<Key, TermixError> {
self.next_key_timeout(Duration::new(0, 0))
}
#[allow(dead_code)]
fn next_raw_key(&mut self) -> Result<Key, TermixError> {
self.next_raw_key_timeout(Duration::new(0, 0))
}
pub fn next_key_timeout(&mut self, timeout: Duration) -> Result<Key, TermixError> {
let next_key = if self.next_key.is_some() {
self.next_key.take().unwrap()
} else {
self.next_raw_key_timeout(timeout)?
};
Ok(next_key)
}
fn next_raw_key_timeout(&mut self, timeout: Duration) -> Result<Key, TermixError> {
let ch = self.next_char_timeout(timeout)?;
match ch {
'\u{00}' => Ok(Key::Ctrl(' ')),
'\u{01}' => Ok(Key::Ctrl('a')),
'\u{02}' => Ok(Key::Ctrl('b')),
'\u{03}' => Ok(Key::Ctrl('c')),
'\u{04}' => Ok(Key::Ctrl('d')),
'\u{05}' => Ok(Key::Ctrl('e')),
'\u{06}' => Ok(Key::Ctrl('f')),
'\u{07}' => Ok(Key::Ctrl('g')),
'\u{08}' => Ok(Key::Ctrl('h')),
'\t' => Ok(Key::Tab),
'\u{0a}' => Ok(Key::Ctrl('j')),
'\u{0b}' => Ok(Key::Ctrl('k')),
'\u{0c}' => Ok(Key::Ctrl('l')),
'\u{0d}' => Ok(Key::Enter),
'\u{0e}' => Ok(Key::Ctrl('n')),
'\u{0f}' => Ok(Key::Ctrl('o')),
'\u{10}' => Ok(Key::Ctrl('p')),
'\u{11}' => Ok(Key::Ctrl('q')),
'\u{12}' => Ok(Key::Ctrl('r')),
'\u{13}' => Ok(Key::Ctrl('s')),
'\u{14}' => Ok(Key::Ctrl('t')),
'\u{15}' => Ok(Key::Ctrl('u')),
'\u{16}' => Ok(Key::Ctrl('v')),
'\u{17}' => Ok(Key::Ctrl('w')),
'\u{18}' => Ok(Key::Ctrl('x')),
'\u{19}' => Ok(Key::Ctrl('y')),
'\u{1a}' => Ok(Key::Ctrl('z')),
'\u{1b}' => self.escape_sequence(),
'\u{7F}' => Ok(Key::Backspace),
_ => Ok(Key::Char(ch)),
}
}
fn escape_sequence(&mut self) -> Result<Key, TermixError> {
let seq1 = self.next_char_timeout(KEY_WAIT).unwrap_or('\u{1B}');
match seq1 {
'[' => self.escape_csi(),
'O' => self.escape_o(),
_ => self.parse_alt(seq1),
}
}
fn escape_o(&mut self) -> Result<Key, TermixError> {
let seq2 = self.next_byte_timeout(KEY_WAIT)?;
match seq2 {
b'A' => Ok(Key::Up), b'B' => Ok(Key::Down), b'C' => Ok(Key::Right), b'D' => Ok(Key::Left), b'F' => Ok(Key::End), b'H' => Ok(Key::Home), b'P' => Ok(Key::F(1)), b'Q' => Ok(Key::F(2)), b'R' => Ok(Key::F(3)), b'S' => Ok(Key::F(4)), b'a' => Ok(Key::CtrlUp),
b'b' => Ok(Key::CtrlDown),
b'c' => Ok(Key::CtrlRight), b'd' => Ok(Key::CtrlLeft), _ => Err(TermixError::KeyRead(seq2)),
}
}
fn escape_csi(&mut self) -> Result<Key, TermixError> {
let seq2 = self.next_byte_timeout(KEY_WAIT)?;
match seq2 {
b'0' | b'9' => Err(TermixError::KeyRead(seq2)),
b'1'..=b'8' => self.extended_escape(seq2),
b'[' => {
let seq3 = self.next_byte_timeout(KEY_WAIT)?;
match seq3 {
b'A' => Ok(Key::F(1)),
b'B' => Ok(Key::F(2)),
b'C' => Ok(Key::F(3)),
b'D' => Ok(Key::F(4)),
b'E' => Ok(Key::F(5)),
_ => Err(TermixError::KeyRead(seq2)),
}
}
b'A' => Ok(Key::Up), b'B' => Ok(Key::Down), b'C' => Ok(Key::Right), b'D' => Ok(Key::Left), b'H' => Ok(Key::Home), b'F' => Ok(Key::End),
b'Z' => Ok(Key::BackTab),
b'M' => {
unimplemented!();
}
b'<' => {
unimplemented!();
}
_ => Err(TermixError::KeyRead(seq2)),
}
}
fn extended_escape(&mut self, seq2: u8) -> Result<Key, TermixError> {
let seq3 = self.next_byte_timeout(KEY_WAIT)?;
if seq3 == b'~' {
match seq2 {
b'1' | b'7' => Ok(Key::Home), b'2' => Ok(Key::Insert),
b'3' => Ok(Key::Delete), b'4' | b'8' => Ok(Key::End), b'5' => Ok(Key::PageUp), b'6' => Ok(Key::PageDown), _ => Err(anyhow::anyhow!("todo! error handle").into()),
}
} else if (b'0'..=b'9').contains(&seq3) {
let mut str_buf = String::new();
str_buf.push(seq2 as char);
str_buf.push(seq3 as char);
let mut seq_last = self.next_byte_timeout(KEY_WAIT)?;
while seq_last != b'M' && seq_last != b'~' {
str_buf.push(seq_last as char);
seq_last = self.next_byte_timeout(KEY_WAIT)?;
}
match seq_last {
b'M' => {
unimplemented!()
}
b'~' => {
unimplemented!()
}
_ => unreachable!(),
}
} else if seq3 == b';' {
let seq4 = self.next_byte_timeout(KEY_WAIT)?;
if (b'0'..=b'9').contains(&seq4) {
let seq5 = self.next_byte_timeout(KEY_WAIT)?;
if seq2 == b'1' {
match (seq4, seq5) {
(b'5', b'A') => Ok(Key::CtrlUp),
(b'5', b'B') => Ok(Key::CtrlDown),
(b'5', b'C') => Ok(Key::CtrlRight),
(b'5', b'D') => Ok(Key::CtrlLeft),
(b'4', b'A') => Ok(Key::AltShiftUp),
(b'4', b'B') => Ok(Key::AltShiftDown),
(b'4', b'C') => Ok(Key::AltShiftRight),
(b'4', b'D') => Ok(Key::AltShiftLeft),
(b'3', b'H') => Ok(Key::AltHome),
(b'3', b'F') => Ok(Key::AltEnd),
(b'2', b'A') => Ok(Key::ShiftUp),
(b'2', b'B') => Ok(Key::ShiftDown),
(b'2', b'C') => Ok(Key::ShiftRight),
(b'2', b'D') => Ok(Key::ShiftLeft),
_ => Err(TermixError::KeyRead(seq2)),
}
} else {
Err(TermixError::KeyRead(seq2))
}
} else {
Err(TermixError::KeyRead(seq2))
}
} else {
match (seq2, seq3) {
(b'5', b'A') => Ok(Key::CtrlUp),
(b'5', b'B') => Ok(Key::CtrlDown),
(b'5', b'C') => Ok(Key::CtrlRight),
(b'5', b'D') => Ok(Key::CtrlLeft),
_ => Err(TermixError::KeyRead(seq2)),
}
}
}
fn parse_alt(&mut self, ch: char) -> Result<Key, TermixError> {
match ch {
'\u{1B}' => {
match self.next_byte_timeout(KEY_WAIT) {
Ok(b'[') => {}
Ok(c) => {
return Err(TermixError::KeyRead(c));
}
Err(_) => return Ok(Key::ESC),
}
match self.escape_csi() {
Ok(Key::Up) => Ok(Key::AltUp),
Ok(Key::Down) => Ok(Key::AltDown),
Ok(Key::Left) => Ok(Key::AltLeft),
Ok(Key::Right) => Ok(Key::AltRight),
Ok(Key::PageUp) => Ok(Key::AltPageUp),
Ok(Key::PageDown) => Ok(Key::AltPageDown),
_ => Err(anyhow::anyhow!("todo: erorr handle").into()),
}
}
'\u{00}' => Ok(Key::CtrlAlt(' ')),
'\u{01}' => Ok(Key::CtrlAlt('a')),
'\u{02}' => Ok(Key::CtrlAlt('b')),
'\u{03}' => Ok(Key::CtrlAlt('c')),
'\u{04}' => Ok(Key::CtrlAlt('d')),
'\u{05}' => Ok(Key::CtrlAlt('e')),
'\u{06}' => Ok(Key::CtrlAlt('f')),
'\u{07}' => Ok(Key::CtrlAlt('g')),
'\u{08}' => Ok(Key::CtrlAlt('h')),
'\u{09}' => Ok(Key::AltTab),
'\u{0A}' => Ok(Key::CtrlAlt('j')),
'\u{0B}' => Ok(Key::CtrlAlt('k')),
'\u{0C}' => Ok(Key::CtrlAlt('l')),
'\u{0D}' => Ok(Key::AltEnter),
'\u{0E}' => Ok(Key::CtrlAlt('n')),
'\u{0F}' => Ok(Key::CtrlAlt('o')),
'\u{10}' => Ok(Key::CtrlAlt('p')),
'\u{11}' => Ok(Key::CtrlAlt('q')),
'\u{12}' => Ok(Key::CtrlAlt('r')),
'\u{13}' => Ok(Key::CtrlAlt('s')),
'\u{14}' => Ok(Key::CtrlAlt('t')),
'\u{15}' => Ok(Key::CtrlAlt('u')),
'\u{16}' => Ok(Key::CtrlAlt('v')),
'\u{17}' => Ok(Key::CtrlAlt('w')),
'\u{18}' => Ok(Key::CtrlAlt('x')),
'\u{19}' => Ok(Key::AltBackTab),
'\u{1A}' => Ok(Key::CtrlAlt('z')),
'\u{7F}' => Ok(Key::AltBackspace),
ch => Ok(Key::Alt(ch)),
}
}
#[allow(dead_code)]
pub fn next_char(&mut self) -> Result<char, TermixError> {
self.next_char_timeout(Duration::new(0, 0))
}
fn next_byte_timeout(&mut self, timeout: Duration) -> Result<u8, TermixError> {
if self.byte_buf.is_empty() {
self.fetch_bytes(timeout)?;
}
Ok(self.byte_buf.remove(0))
}
#[allow(dead_code)]
fn next_byte(&mut self) -> Result<u8, TermixError> {
self.next_byte_timeout(Duration::new(0, 0))
}
fn next_char_timeout(&mut self, timeout: Duration) -> Result<char, TermixError> {
if self.byte_buf.is_empty() {
self.fetch_bytes(timeout)?;
}
let bytes = std::mem::take(&mut self.byte_buf);
match String::from_utf8(bytes) {
Ok(string) => {
let ret = string
.chars()
.next()
.expect("failed to get next char from input");
self.byte_buf
.extend_from_slice(&string.as_bytes()[ret.len_utf8()..]);
Ok(ret)
}
Err(error) => {
let valid_up_to = error.utf8_error().valid_up_to();
let bytes = error.into_bytes();
let string = String::from_utf8_lossy(&bytes[..valid_up_to]);
let ret = string
.chars()
.next()
.expect("failed to get next char from input");
self.byte_buf.extend_from_slice(&bytes[ret.len_utf8()..]);
Ok(ret)
}
}
}
fn fetch_bytes(&mut self, timeout: Duration) -> Result<(), TermixError> {
let mut reader_buf = [0; 1];
while self.sig_rx.read(&mut reader_buf).is_ok() {}
wait_until_ready(
self.file.as_raw_fd(),
Some(self.sig_rx.as_raw_fd()),
timeout,
)?;
self.read_unread_bytes();
Ok(())
}
pub fn read_unread_bytes(&mut self) {
let mut reader_buf = [0; 1];
while let Ok(..) = self.file.read(&mut reader_buf) {
self.byte_buf.push(reader_buf[0]);
}
}
}