#[derive(Debug, Clone, Copy)]
pub enum Key {
Backspace,
Left,
Right,
Up,
Down,
Home,
End,
PageUp,
PageDown,
BackTab,
Delete,
Insert,
F(u8),
Char(char),
Alt(char),
Ctrl(char),
Null,
Esc,
}
mod convert {
#[cfg(any(feature = "crossterm", feature = "termion"))]
use super::Key;
#[cfg(feature = "crossterm")]
impl std::convert::TryFrom<crossterm::event::KeyEvent> for Key {
type Error = crossterm::event::KeyEvent;
fn try_from(value: crossterm::event::KeyEvent) -> Result<Self, Self::Error> {
use crossterm::event::{KeyCode::*, KeyModifiers};
Ok(match value.code {
Backspace => Key::Backspace,
Enter => Key::Char('\n'),
Left => Key::Left,
Right => Key::Right,
Up => Key::Up,
Down => Key::Down,
Home => Key::Home,
End => Key::End,
PageUp => Key::PageUp,
PageDown => Key::PageDown,
Tab => Key::Char('\t'),
BackTab => Key::BackTab,
Delete => Key::Delete,
Insert => Key::Insert,
F(k) => Key::F(k),
Null => Key::Null,
Esc => Key::Esc,
Char(c) => match value.modifiers {
KeyModifiers::NONE | KeyModifiers::SHIFT => Key::Char(c),
KeyModifiers::CONTROL => Key::Ctrl(c),
KeyModifiers::ALT => Key::Alt(c),
_ => return Err(value),
},
})
}
}
#[cfg(feature = "termion")]
impl std::convert::TryFrom<termion::event::Key> for Key {
type Error = termion::event::Key;
fn try_from(value: termion::event::Key) -> Result<Self, Self::Error> {
use termion::event::Key::*;
Ok(match value {
Backspace => Key::Backspace,
Left => Key::Left,
Right => Key::Right,
Up => Key::Up,
Down => Key::Down,
Home => Key::Home,
End => Key::End,
PageUp => Key::PageUp,
PageDown => Key::PageDown,
BackTab => Key::BackTab,
Delete => Key::Delete,
Insert => Key::Insert,
F(c) => Key::F(c),
Char(c) => Key::Char(c),
Alt(c) => Key::Alt(c),
Ctrl(c) => Key::Ctrl(c),
Null => Key::Null,
Esc => Key::Esc,
_ => return Err(value),
})
}
}
}
#[cfg(feature = "crossterm")]
mod _impl {
pub fn key_input_channel() -> std::sync::mpsc::Receiver<super::Key> {
use std::convert::TryInto;
let (key_send, key_receive) = std::sync::mpsc::sync_channel(0);
std::thread::spawn(move || -> Result<(), std::io::Error> {
loop {
let event = crossterm::event::read().map_err(crate::crossterm_utils::into_io_error)?;
match event {
crossterm::event::Event::Key(key) => {
let key: Result<super::Key, _> = key.try_into();
if let Ok(key) = key {
if key_send.send(key).is_err() {
break;
}
};
}
_ => continue,
};
}
Ok(())
});
key_receive
}
#[cfg(feature = "input-async-crossterm")]
pub fn key_input_stream() -> impl futures_core::stream::Stream<Item = super::Key> {
use futures_lite::StreamExt;
use std::convert::TryFrom;
crossterm::event::EventStream::new()
.filter_map(|r| r.ok())
.filter_map(|e| match e {
crossterm::event::Event::Key(key) => super::Key::try_from(key).ok(),
_ => None,
})
}
}
#[cfg(all(feature = "termion", not(feature = "crossterm")))]
mod _impl {
#[cfg(feature = "input-async")]
pub fn key_input_stream() -> impl futures_core::stream::Stream<Item = super::Key> {
use std::{convert::TryInto, io};
use termion::input::TermRead;
let (key_send, key_receive) = async_channel::bounded::<super::Key>(1);
std::thread::spawn(move || -> Result<(), io::Error> {
for key in io::stdin().keys() {
let key: Result<super::Key, _> = key?.try_into();
if let Ok(key) = key {
if futures_lite::future::block_on(key_send.send(key)).is_err() {
break;
}
}
}
Ok(())
});
key_receive
}
pub fn key_input_channel() -> std::sync::mpsc::Receiver<super::Key> {
use std::{convert::TryInto, io};
use termion::input::TermRead;
let (key_send, key_receive) = std::sync::mpsc::sync_channel(0);
std::thread::spawn(move || -> Result<(), io::Error> {
for key in io::stdin().keys() {
let key: Result<super::Key, _> = key?.try_into();
if let Ok(key) = key {
if key_send.send(key).is_err() {
break;
}
}
}
Ok(())
});
key_receive
}
}
#[cfg(any(feature = "termion", feature = "crossterm"))]
pub use _impl::*;