use std::fs::File;
use std::io::prelude::*;
use std::os::unix::io::AsRawFd;
use std::os::unix::io::FromRawFd;
use std::sync::Arc;
use std::time::{Duration, Instant};
use nix::fcntl::{fcntl, FcntlArg, OFlag};
use crate::error::TuikitError;
use crate::key::Key::*;
use crate::key::{Key, MouseButton};
use crate::raw::get_tty;
use crate::spinlock::SpinLock;
use crate::sys::file::wait_until_ready;
use crate::Result;
pub trait ReadAndAsRawFd: Read + AsRawFd + Send {}
const KEY_WAIT: Duration = Duration::from_millis(10);
const DOUBLE_CLICK_DURATION: u128 = 300;
impl<T> ReadAndAsRawFd for T where T: Read + AsRawFd + Send {}
pub struct KeyBoard {
file: Box<dyn ReadAndAsRawFd>,
sig_tx: Arc<SpinLock<File>>,
sig_rx: File,
byte_buf: Vec<u8>,
raw_mouse: bool,
next_key: Option<Result<Key>>,
last_click: Key,
last_click_time: SpinLock<Instant>,
}
impl KeyBoard {
pub fn new(file: Box<dyn ReadAndAsRawFd>) -> Self {
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_tx: Arc::new(SpinLock::new(unsafe { File::from_raw_fd(tx) })),
sig_rx: unsafe { File::from_raw_fd(rx) },
byte_buf: Vec::new(),
raw_mouse: false,
next_key: None,
last_click: Key::Null,
last_click_time: SpinLock::new(Instant::now()),
}
}
pub fn new_with_tty() -> Self {
Self::new(Box::new(
get_tty().expect("KeyBoard::new_with_tty: failed to get tty"),
))
}
pub fn raw_mouse(mut self, raw_mouse: bool) -> Self {
self.raw_mouse = raw_mouse;
self
}
pub fn get_interrupt_handler(&self) -> KeyboardHandler {
KeyboardHandler {
handler: self.sig_tx.clone(),
}
}
fn fetch_bytes(&mut self, timeout: Duration) -> Result<()> {
let mut reader_buf = [0; 1];
while let Ok(_) = self.sig_rx.read(&mut reader_buf) {}
wait_until_ready(
self.file.as_raw_fd(),
Some(self.sig_rx.as_raw_fd()),
timeout,
)?;
self.read_unread_bytes();
Ok(())
}
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]);
}
}
#[allow(dead_code)]
fn next_byte(&mut self) -> Result<u8> {
self.next_byte_timeout(Duration::new(0, 0))
}
fn next_byte_timeout(&mut self, timeout: Duration) -> Result<u8> {
trace!("next_byte_timeout: timeout: {:?}", timeout);
if self.byte_buf.is_empty() {
self.fetch_bytes(timeout)?;
}
trace!("next_byte_timeout: after fetch, buf = {:?}", self.byte_buf);
Ok(self.byte_buf.remove(0))
}
#[allow(dead_code)]
fn next_char(&mut self) -> Result<char> {
self.next_char_timeout(Duration::new(0, 0))
}
fn next_char_timeout(&mut self, timeout: Duration) -> Result<char> {
trace!("next_char_timeout: timeout: {:?}", timeout);
if self.byte_buf.is_empty() {
self.fetch_bytes(timeout)?;
}
trace!("get_chars: buf: {:?}", self.byte_buf);
let bytes = std::mem::replace(&mut self.byte_buf, Vec::new());
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 merge_wheel(&mut self, current_key: Result<Key>) -> (Result<Key>, Option<Result<Key>>) {
match current_key {
Ok(Key::MousePress(key @ MouseButton::WheelUp, row, col))
| Ok(Key::MousePress(key @ MouseButton::WheelDown, row, col)) => {
let mut count = 1;
let mut o_next_key;
loop {
o_next_key = self.try_next_raw_key();
match o_next_key {
Some(Ok(Key::MousePress(k, r, c))) if key == k && row == r && col == c => {
count += 1
}
_ => break,
}
}
match key {
MouseButton::WheelUp => (Ok(Key::WheelUp(row, col, count)), o_next_key),
MouseButton::WheelDown => (Ok(Key::WheelDown(row, col, count)), o_next_key),
_ => unreachable!(),
}
}
_ => (current_key, None),
}
}
pub fn next_key(&mut self) -> Result<Key> {
self.next_key_timeout(Duration::new(0, 0))
}
pub fn next_key_timeout(&mut self, timeout: Duration) -> Result<Key> {
if self.raw_mouse {
return self.next_raw_key_timeout(timeout);
}
let next_key = if self.next_key.is_some() {
self.next_key.take().unwrap()
} else {
let next_key = self.next_raw_key_timeout(timeout);
let (next_key, next_next_key) = self.merge_wheel(next_key);
self.next_key = next_next_key;
next_key
};
match next_key {
Ok(key @ MousePress(..)) => {
if let MousePress(button, row, col) = key {
let ret = if key == self.last_click
&& self.last_click_time.lock().elapsed().as_millis() < DOUBLE_CLICK_DURATION
{
DoubleClick(button, row, col)
} else {
self.last_click = key;
SingleClick(button, row, col)
};
*self.last_click_time.lock() = Instant::now();
Ok(ret)
} else {
unreachable!();
}
}
_ => return next_key,
}
}
#[allow(dead_code)]
fn next_raw_key(&mut self) -> Result<Key> {
self.next_raw_key_timeout(Duration::new(0, 0))
}
fn try_next_raw_key(&mut self) -> Option<Result<Key>> {
match self.next_raw_key_timeout(KEY_WAIT) {
Ok(key) => Some(Ok(key)),
Err(TuikitError::Timeout(_)) => None,
Err(error) => Some(Err(error)),
}
}
fn next_raw_key_timeout(&mut self, timeout: Duration) -> Result<Key> {
trace!("next_raw_key_timeout: {:?}", timeout);
let ch = self.next_char_timeout(timeout)?;
match ch {
'\u{00}' => Ok(Ctrl(' ')),
'\u{01}' => Ok(Ctrl('a')),
'\u{02}' => Ok(Ctrl('b')),
'\u{03}' => Ok(Ctrl('c')),
'\u{04}' => Ok(Ctrl('d')),
'\u{05}' => Ok(Ctrl('e')),
'\u{06}' => Ok(Ctrl('f')),
'\u{07}' => Ok(Ctrl('g')),
'\u{08}' => Ok(Ctrl('h')),
'\u{09}' => Ok(Tab),
'\u{0A}' => Ok(Ctrl('j')),
'\u{0B}' => Ok(Ctrl('k')),
'\u{0C}' => Ok(Ctrl('l')),
'\u{0D}' => Ok(Enter),
'\u{0E}' => Ok(Ctrl('n')),
'\u{0F}' => Ok(Ctrl('o')),
'\u{10}' => Ok(Ctrl('p')),
'\u{11}' => Ok(Ctrl('q')),
'\u{12}' => Ok(Ctrl('r')),
'\u{13}' => Ok(Ctrl('s')),
'\u{14}' => Ok(Ctrl('t')),
'\u{15}' => Ok(Ctrl('u')),
'\u{16}' => Ok(Ctrl('v')),
'\u{17}' => Ok(Ctrl('w')),
'\u{18}' => Ok(Ctrl('x')),
'\u{19}' => Ok(Ctrl('y')),
'\u{1A}' => Ok(Ctrl('z')),
'\u{1B}' => self.escape_sequence(),
'\u{7F}' => Ok(Backspace),
ch => Ok(Char(ch)),
}
}
fn escape_sequence(&mut self) -> Result<Key> {
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 parse_alt(&mut self, ch: char) -> Result<Key> {
match ch {
'\u{1B}' => {
match self.next_byte_timeout(KEY_WAIT) {
Ok(b'[') => {}
Ok(c) => {
return Err(TuikitError::UnknownSequence(format!("ESC ESC {}", c)));
}
Err(_) => return Ok(ESC),
}
match self.escape_csi() {
Ok(Up) => Ok(AltUp),
Ok(Down) => Ok(AltDown),
Ok(Left) => Ok(AltLeft),
Ok(Right) => Ok(AltRight),
Ok(PageUp) => Ok(AltPageUp),
Ok(PageDown) => Ok(AltPageDown),
_ => Err(TuikitError::UnknownSequence(format!("ESC ESC [ ..."))),
}
}
'\u{00}' => Ok(CtrlAlt(' ')),
'\u{01}' => Ok(CtrlAlt('a')),
'\u{02}' => Ok(CtrlAlt('b')),
'\u{03}' => Ok(CtrlAlt('c')),
'\u{04}' => Ok(CtrlAlt('d')),
'\u{05}' => Ok(CtrlAlt('e')),
'\u{06}' => Ok(CtrlAlt('f')),
'\u{07}' => Ok(CtrlAlt('g')),
'\u{08}' => Ok(CtrlAlt('h')),
'\u{09}' => Ok(AltTab),
'\u{0A}' => Ok(CtrlAlt('j')),
'\u{0B}' => Ok(CtrlAlt('k')),
'\u{0C}' => Ok(CtrlAlt('l')),
'\u{0D}' => Ok(AltEnter),
'\u{0E}' => Ok(CtrlAlt('n')),
'\u{0F}' => Ok(CtrlAlt('o')),
'\u{10}' => Ok(CtrlAlt('p')),
'\u{11}' => Ok(CtrlAlt('q')),
'\u{12}' => Ok(CtrlAlt('r')),
'\u{13}' => Ok(CtrlAlt('s')),
'\u{14}' => Ok(CtrlAlt('t')),
'\u{15}' => Ok(CtrlAlt('u')),
'\u{16}' => Ok(CtrlAlt('v')),
'\u{17}' => Ok(CtrlAlt('w')),
'\u{18}' => Ok(CtrlAlt('x')),
'\u{19}' => Ok(AltBackTab),
'\u{1A}' => Ok(CtrlAlt('z')),
'\u{7F}' => Ok(AltBackspace),
ch => Ok(Alt(ch)),
}
}
fn escape_csi(&mut self) -> Result<Key> {
let cursor_pos = self.parse_cursor_report();
if cursor_pos.is_ok() {
return cursor_pos;
}
let seq2 = self.next_byte_timeout(KEY_WAIT)?;
match seq2 {
b'0' | b'9' => Err(TuikitError::UnknownSequence(format!("ESC [ {:x?}", seq2))),
b'1'..=b'8' => self.extended_escape(seq2),
b'[' => {
let seq3 = self.next_byte_timeout(KEY_WAIT)?;
match seq3 {
b'A' => Ok(F(1)),
b'B' => Ok(F(2)),
b'C' => Ok(F(3)),
b'D' => Ok(F(4)),
b'E' => Ok(F(5)),
_ => Err(TuikitError::UnknownSequence(format!("ESC [ [ {:x?}", seq3))),
}
}
b'A' => Ok(Up),
b'B' => Ok(Down),
b'C' => Ok(Right),
b'D' => Ok(Left),
b'H' => Ok(Home),
b'F' => Ok(End),
b'Z' => Ok(BackTab),
b'M' => {
let cb = self.next_byte_timeout(KEY_WAIT)?;
let cx = self.next_byte_timeout(KEY_WAIT)?.saturating_sub(32) as u16 - 1;
let cy = self.next_byte_timeout(KEY_WAIT)?.saturating_sub(32) as u16 - 1;
match cb & 0b11 {
0 => {
if cb & 0x40 != 0 {
Ok(MousePress(MouseButton::WheelUp, cy, cx))
} else {
Ok(MousePress(MouseButton::Left, cy, cx))
}
}
1 => {
if cb & 0x40 != 0 {
Ok(MousePress(MouseButton::WheelDown, cy, cx))
} else {
Ok(MousePress(MouseButton::Middle, cy, cx))
}
}
2 => Ok(MousePress(MouseButton::Right, cy, cx)),
3 => Ok(MouseRelease(cy, cx)),
_ => Err(TuikitError::UnknownSequence(format!(
"ESC M {:?}{:?}{:?}",
cb, cx, cy
))),
}
}
b'<' => {
self.read_unread_bytes();
if !self.byte_buf.contains(&b'm') && !self.byte_buf.contains(&b'M') {
return Err(TuikitError::UnknownSequence(format!(
"ESC [ < (not ending with m/M)"
)));
}
let mut str_buf = String::new();
let mut c = self.next_char_timeout(KEY_WAIT)?;
while c != 'm' && c != 'M' {
str_buf.push(c);
c = self.next_char_timeout(KEY_WAIT)?;
}
let nums = &mut str_buf.split(';');
let cb = nums.next().unwrap().parse::<u16>().unwrap();
let cx = nums.next().unwrap().parse::<u16>().unwrap() - 1;
let cy = nums.next().unwrap().parse::<u16>().unwrap() - 1;
match cb {
0..=2 | 64..=65 => {
let button = match cb {
0 => MouseButton::Left,
1 => MouseButton::Middle,
2 => MouseButton::Right,
64 => MouseButton::WheelUp,
65 => MouseButton::WheelDown,
_ => {
return Err(TuikitError::UnknownSequence(format!(
"ESC [ < {} {}",
str_buf, c
)));
}
};
match c {
'M' => Ok(MousePress(button, cy, cx)),
'm' => Ok(MouseRelease(cy, cx)),
_ => Err(TuikitError::UnknownSequence(format!(
"ESC [ < {} {}",
str_buf, c
))),
}
}
32 => Ok(MouseHold(cy, cx)),
_ => Err(TuikitError::UnknownSequence(format!(
"ESC [ < {} {}",
str_buf, c
))),
}
}
_ => Err(TuikitError::UnknownSequence(format!("ESC [ {:?}", seq2))),
}
}
fn parse_cursor_report(&mut self) -> Result<Key> {
self.read_unread_bytes();
let pos_semi = self.byte_buf.iter().position(|&b| b == b';');
let pos_r = self.byte_buf.iter().position(|&b| b == b'R');
if pos_semi.is_some() && pos_r.is_some() {
let pos_semi = pos_semi.unwrap();
let pos_r = pos_r.unwrap();
let remain = self.byte_buf.split_off(pos_r + 1);
let mut col_str = self.byte_buf.split_off(pos_semi + 1);
let mut row_str = std::mem::replace(&mut self.byte_buf, remain);
row_str.pop();
col_str.pop();
let row = String::from_utf8(row_str)?;
let col = String::from_utf8(col_str)?;
let row_num = row.parse::<u16>()?;
let col_num = col.parse::<u16>()?;
Ok(CursorPos(row_num - 1, col_num - 1))
} else {
Err(TuikitError::NoCursorReportResponse)
}
}
fn extended_escape(&mut self, seq2: u8) -> Result<Key> {
let seq3 = self.next_byte_timeout(KEY_WAIT)?;
if seq3 == b'~' {
match seq2 {
b'1' | b'7' => Ok(Home),
b'2' => Ok(Insert),
b'3' => Ok(Delete),
b'4' | b'8' => Ok(End),
b'5' => Ok(PageUp),
b'6' => Ok(PageDown),
_ => Err(TuikitError::UnknownSequence(format!("ESC [ {} ~", seq2))),
}
} else if seq3 >= b'0' && seq3 <= b'9' {
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' => {
let mut nums = str_buf.split(';');
let cb = nums.next().unwrap().parse::<u16>().unwrap();
let cx = nums.next().unwrap().parse::<u16>().unwrap() - 1;
let cy = nums.next().unwrap().parse::<u16>().unwrap() - 1;
match cb {
32 => Ok(MousePress(MouseButton::Left, cy, cx)),
33 => Ok(MousePress(MouseButton::Middle, cy, cx)),
34 => Ok(MousePress(MouseButton::Right, cy, cx)),
35 => Ok(MouseRelease(cy, cx)),
64 => Ok(MouseHold(cy, cx)),
96 | 97 => Ok(MousePress(MouseButton::WheelUp, cy, cx)),
_ => Err(TuikitError::UnknownSequence(format!("ESC [ {} M", str_buf))),
}
}
b'~' => {
let num: u8 = str_buf.parse().unwrap();
match num {
v @ 11..=15 => Ok(F(v - 10)),
v @ 17..=21 => Ok(F(v - 11)),
v @ 23..=24 => Ok(F(v - 12)),
200 => Ok(BracketedPasteStart),
201 => Ok(BracketedPasteEnd),
_ => Err(TuikitError::UnknownSequence(format!("ESC [ {} ~", str_buf))),
}
}
_ => unreachable!(),
}
} else if seq3 == b';' {
let seq4 = self.next_byte_timeout(KEY_WAIT)?;
if seq4 >= b'0' && seq4 <= b'9' {
let seq5 = self.next_byte_timeout(KEY_WAIT)?;
if seq2 == b'1' {
match (seq4, seq5) {
(b'5', b'A') => Ok(CtrlUp),
(b'5', b'B') => Ok(CtrlDown),
(b'5', b'C') => Ok(CtrlRight),
(b'5', b'D') => Ok(CtrlLeft),
(b'4', b'A') => Ok(AltShiftUp),
(b'4', b'B') => Ok(AltShiftDown),
(b'4', b'C') => Ok(AltShiftRight),
(b'4', b'D') => Ok(AltShiftLeft),
(b'3', b'H') => Ok(AltHome),
(b'3', b'F') => Ok(AltEnd),
(b'2', b'A') => Ok(ShiftUp),
(b'2', b'B') => Ok(ShiftDown),
(b'2', b'C') => Ok(ShiftRight),
(b'2', b'D') => Ok(ShiftLeft),
_ => Err(TuikitError::UnknownSequence(format!(
"ESC [ 1 ; {:x?} {:x?}",
seq4, seq5
))),
}
} else {
Err(TuikitError::UnknownSequence(format!(
"ESC [ {:x?} ; {:x?} {:x?}",
seq2, seq4, seq5
)))
}
} else {
Err(TuikitError::UnknownSequence(format!(
"ESC [ {:x?} ; {:x?}",
seq2, seq4
)))
}
} else {
match (seq2, seq3) {
(b'5', b'A') => Ok(CtrlUp),
(b'5', b'B') => Ok(CtrlDown),
(b'5', b'C') => Ok(CtrlRight),
(b'5', b'D') => Ok(CtrlLeft),
_ => Err(TuikitError::UnknownSequence(format!(
"ESC [ {:x?} {:x?}",
seq2, seq3
))),
}
}
}
fn escape_o(&mut self) -> Result<Key> {
let seq2 = self.next_byte_timeout(KEY_WAIT)?;
match seq2 {
b'A' => Ok(Up),
b'B' => Ok(Down),
b'C' => Ok(Right),
b'D' => Ok(Left),
b'F' => Ok(End),
b'H' => Ok(Home),
b'P' => Ok(F(1)),
b'Q' => Ok(F(2)),
b'R' => Ok(F(3)),
b'S' => Ok(F(4)),
b'a' => Ok(CtrlUp),
b'b' => Ok(CtrlDown),
b'c' => Ok(CtrlRight),
b'd' => Ok(CtrlLeft),
_ => Err(TuikitError::UnknownSequence(format!("ESC O {:x?}", seq2))),
}
}
}
pub struct KeyboardHandler {
handler: Arc<SpinLock<File>>,
}
impl KeyboardHandler {
pub fn interrupt(&self) {
let mut handler = self.handler.lock();
let _ = handler.write_all(b"x");
let _ = handler.flush();
}
}