use std::io::Write;
use std::ffi::CStr;
use const_cstr::ConstCStr;
use crate::Event::*;
use crate::{Event, Modifiers, KeyInput};
mod ext;
fn write_now(data: &[u8]) -> Result<(), std::io::Error> {
let stdout = std::io::stdout();
let mut lock = stdout.lock();
lock.write_all(data)?;
lock.flush()?;
Ok(())
}
struct BracketedPaste {
_priv: ()
}
impl Drop for BracketedPaste {
fn drop(&mut self) {
let _ = write_now(b"\x1b[?2004l");
}
}
impl BracketedPaste {
fn start() -> Option<BracketedPaste> {
write_now(b"\x1b[?2004h").ok()?;
Some(BracketedPaste { _priv: () })
}
}
struct XTermModifyOtherKeys {
_priv: ()
}
impl Drop for XTermModifyOtherKeys {
fn drop(&mut self) {
let _ = write_now(b"\x1b[>4n");
}
}
impl XTermModifyOtherKeys {
fn start() -> Option<XTermModifyOtherKeys> {
write_now(b"\x1b[>4;2m").ok()?;
Some(XTermModifyOtherKeys { _priv: () })
}
}
enum XTermModifyKeyState {
Off,
ParsingMode(u32),
ParsingChar(u32, u32)
}
struct KittyFullMode {
_priv: ()
}
impl Drop for KittyFullMode {
fn drop(&mut self) {
let _ = write_now(b"\x1b[?2017l");
}
}
impl KittyFullMode {
fn start() -> Option<KittyFullMode> {
write_now(b"\x1b[?2017h").ok()?;
Some(KittyFullMode { _priv: () })
}
}
#[derive(Copy, Clone, Debug)]
enum KeyType {
Press,
Release,
Repeat
}
enum KittyFullModeState {
Off,
ParsingType,
ParsingModifiers(KeyType),
ParsingKey(KeyType, u8, u32)
}
pub struct InputStream {
_bracketed_paste: Option<BracketedPaste>,
_xterm_modify_keys: Option<XTermModifyOtherKeys>,
_kitty_full_mode: Option<KittyFullMode>,
extra_bound_keys: Vec<(i32, Event)>,
in_progress_codepoint: u32,
utf8_bytes_left: usize,
xterm_modify_key_state: XTermModifyKeyState,
kitty_full_mode_state: KittyFullModeState
}
unsafe fn define_if_necessary(def: &std::ffi::CStr, code: std::os::raw::c_int) -> Result<(), ()> {
if ext::key_code_for(def) == Err(ext::KeyError::NotDefined) {
ext::define_key_code(def, code)
} else {
Ok(())
}
}
const KNOWN_EXTRA_TERM_CAPABILITIES: &'static [(ConstCStr, Event)] = &[
(const_cstr!("kDC3"), KeyPress { modifiers: Modifiers(3 - 1), key: KeyInput::Special(ncurses::KEY_DC), is_repeat: false }),
(const_cstr!("kDC4"), KeyPress { modifiers: Modifiers(4 - 1), key: KeyInput::Special(ncurses::KEY_DC), is_repeat: false }),
(const_cstr!("kDC5"), KeyPress { modifiers: Modifiers(5 - 1), key: KeyInput::Special(ncurses::KEY_DC), is_repeat: false }),
(const_cstr!("kDC6"), KeyPress { modifiers: Modifiers(6 - 1), key: KeyInput::Special(ncurses::KEY_DC), is_repeat: false }),
(const_cstr!("kDC7"), KeyPress { modifiers: Modifiers(7 - 1), key: KeyInput::Special(ncurses::KEY_DC), is_repeat: false }),
(const_cstr!("kDC8"), KeyPress { modifiers: Modifiers(8 - 1), key: KeyInput::Special(ncurses::KEY_DC), is_repeat: false }),
(const_cstr!("kLFT3"), KeyPress { modifiers: Modifiers(3 - 1), key: KeyInput::Special(ncurses::KEY_LEFT), is_repeat: false }),
(const_cstr!("kLFT4"), KeyPress { modifiers: Modifiers(4 - 1), key: KeyInput::Special(ncurses::KEY_LEFT), is_repeat: false }),
(const_cstr!("kLFT5"), KeyPress { modifiers: Modifiers(5 - 1), key: KeyInput::Special(ncurses::KEY_LEFT), is_repeat: false }),
(const_cstr!("kLFT6"), KeyPress { modifiers: Modifiers(6 - 1), key: KeyInput::Special(ncurses::KEY_LEFT), is_repeat: false }),
(const_cstr!("kLFT7"), KeyPress { modifiers: Modifiers(7 - 1), key: KeyInput::Special(ncurses::KEY_LEFT), is_repeat: false }),
(const_cstr!("kLFT8"), KeyPress { modifiers: Modifiers(8 - 1), key: KeyInput::Special(ncurses::KEY_LEFT), is_repeat: false }),
(const_cstr!("kRIT3"), KeyPress { modifiers: Modifiers(3 - 1), key: KeyInput::Special(ncurses::KEY_RIGHT), is_repeat: false }),
(const_cstr!("kRIT4"), KeyPress { modifiers: Modifiers(4 - 1), key: KeyInput::Special(ncurses::KEY_RIGHT), is_repeat: false }),
(const_cstr!("kRIT5"), KeyPress { modifiers: Modifiers(5 - 1), key: KeyInput::Special(ncurses::KEY_RIGHT), is_repeat: false }),
(const_cstr!("kRIT6"), KeyPress { modifiers: Modifiers(6 - 1), key: KeyInput::Special(ncurses::KEY_RIGHT), is_repeat: false }),
(const_cstr!("kRIT7"), KeyPress { modifiers: Modifiers(7 - 1), key: KeyInput::Special(ncurses::KEY_RIGHT), is_repeat: false }),
(const_cstr!("kRIT8"), KeyPress { modifiers: Modifiers(8 - 1), key: KeyInput::Special(ncurses::KEY_RIGHT), is_repeat: false }),
(const_cstr!("kUP3"), KeyPress { modifiers: Modifiers(3 - 1), key: KeyInput::Special(ncurses::KEY_UP), is_repeat: false }),
(const_cstr!("kUP4"), KeyPress { modifiers: Modifiers(4 - 1), key: KeyInput::Special(ncurses::KEY_UP), is_repeat: false }),
(const_cstr!("kUP5"), KeyPress { modifiers: Modifiers(5 - 1), key: KeyInput::Special(ncurses::KEY_UP), is_repeat: false }),
(const_cstr!("kUP6"), KeyPress { modifiers: Modifiers(6 - 1), key: KeyInput::Special(ncurses::KEY_UP), is_repeat: false }),
(const_cstr!("kUP7"), KeyPress { modifiers: Modifiers(7 - 1), key: KeyInput::Special(ncurses::KEY_UP), is_repeat: false }),
(const_cstr!("kUP8"), KeyPress { modifiers: Modifiers(8 - 1), key: KeyInput::Special(ncurses::KEY_UP), is_repeat: false }),
(const_cstr!("kDN3"), KeyPress { modifiers: Modifiers(3 - 1), key: KeyInput::Special(ncurses::KEY_DOWN), is_repeat: false }),
(const_cstr!("kDN4"), KeyPress { modifiers: Modifiers(4 - 1), key: KeyInput::Special(ncurses::KEY_DOWN), is_repeat: false }),
(const_cstr!("kDN5"), KeyPress { modifiers: Modifiers(5 - 1), key: KeyInput::Special(ncurses::KEY_DOWN), is_repeat: false }),
(const_cstr!("kDN6"), KeyPress { modifiers: Modifiers(6 - 1), key: KeyInput::Special(ncurses::KEY_DOWN), is_repeat: false }),
(const_cstr!("kDN7"), KeyPress { modifiers: Modifiers(7 - 1), key: KeyInput::Special(ncurses::KEY_DOWN), is_repeat: false }),
(const_cstr!("kDN8"), KeyPress { modifiers: Modifiers(8 - 1), key: KeyInput::Special(ncurses::KEY_DOWN), is_repeat: false }),
(const_cstr!("kHOM3"), KeyPress { modifiers: Modifiers(3 - 1), key: KeyInput::Special(ncurses::KEY_HOME), is_repeat: false }),
(const_cstr!("kHOM4"), KeyPress { modifiers: Modifiers(4 - 1), key: KeyInput::Special(ncurses::KEY_HOME), is_repeat: false }),
(const_cstr!("kHOM5"), KeyPress { modifiers: Modifiers(5 - 1), key: KeyInput::Special(ncurses::KEY_HOME), is_repeat: false }),
(const_cstr!("kHOM6"), KeyPress { modifiers: Modifiers(6 - 1), key: KeyInput::Special(ncurses::KEY_HOME), is_repeat: false }),
(const_cstr!("kHOM7"), KeyPress { modifiers: Modifiers(7 - 1), key: KeyInput::Special(ncurses::KEY_HOME), is_repeat: false }),
(const_cstr!("kHOM8"), KeyPress { modifiers: Modifiers(8 - 1), key: KeyInput::Special(ncurses::KEY_HOME), is_repeat: false }),
(const_cstr!("kEND3"), KeyPress { modifiers: Modifiers(3 - 1), key: KeyInput::Special(ncurses::KEY_END), is_repeat: false }),
(const_cstr!("kEND4"), KeyPress { modifiers: Modifiers(4 - 1), key: KeyInput::Special(ncurses::KEY_END), is_repeat: false }),
(const_cstr!("kEND5"), KeyPress { modifiers: Modifiers(5 - 1), key: KeyInput::Special(ncurses::KEY_END), is_repeat: false }),
(const_cstr!("kEND6"), KeyPress { modifiers: Modifiers(6 - 1), key: KeyInput::Special(ncurses::KEY_END), is_repeat: false }),
(const_cstr!("kEND7"), KeyPress { modifiers: Modifiers(7 - 1), key: KeyInput::Special(ncurses::KEY_END), is_repeat: false }),
(const_cstr!("kEND8"), KeyPress { modifiers: Modifiers(8 - 1), key: KeyInput::Special(ncurses::KEY_END), is_repeat: false }),
(const_cstr!("kPRV3"), KeyPress { modifiers: Modifiers(3 - 1), key: KeyInput::Special(ncurses::KEY_PPAGE), is_repeat: false }),
(const_cstr!("kPRV4"), KeyPress { modifiers: Modifiers(4 - 1), key: KeyInput::Special(ncurses::KEY_PPAGE), is_repeat: false }),
(const_cstr!("kPRV5"), KeyPress { modifiers: Modifiers(5 - 1), key: KeyInput::Special(ncurses::KEY_PPAGE), is_repeat: false }),
(const_cstr!("kPRV6"), KeyPress { modifiers: Modifiers(6 - 1), key: KeyInput::Special(ncurses::KEY_PPAGE), is_repeat: false }),
(const_cstr!("kPRV7"), KeyPress { modifiers: Modifiers(7 - 1), key: KeyInput::Special(ncurses::KEY_PPAGE), is_repeat: false }),
(const_cstr!("kPRV8"), KeyPress { modifiers: Modifiers(8 - 1), key: KeyInput::Special(ncurses::KEY_PPAGE), is_repeat: false }),
(const_cstr!("kNXT3"), KeyPress { modifiers: Modifiers(3 - 1), key: KeyInput::Special(ncurses::KEY_NPAGE), is_repeat: false }),
(const_cstr!("kNXT4"), KeyPress { modifiers: Modifiers(4 - 1), key: KeyInput::Special(ncurses::KEY_NPAGE), is_repeat: false }),
(const_cstr!("kNXT5"), KeyPress { modifiers: Modifiers(5 - 1), key: KeyInput::Special(ncurses::KEY_NPAGE), is_repeat: false }),
(const_cstr!("kNXT6"), KeyPress { modifiers: Modifiers(6 - 1), key: KeyInput::Special(ncurses::KEY_NPAGE), is_repeat: false }),
(const_cstr!("kNXT7"), KeyPress { modifiers: Modifiers(7 - 1), key: KeyInput::Special(ncurses::KEY_NPAGE), is_repeat: false }),
(const_cstr!("kNXT8"), KeyPress { modifiers: Modifiers(8 - 1), key: KeyInput::Special(ncurses::KEY_NPAGE), is_repeat: false }),
];
impl InputStream {
pub unsafe fn init(window: ncurses::WINDOW) -> Self {
ncurses::ll::keypad(window, true as ncurses::ll::c_bool);
ncurses::ll::raw();
ncurses::ll::noecho();
ncurses::ll::mousemask(ncurses::ALL_MOUSE_EVENTS as ncurses::mmask_t, core::ptr::null_mut());
ncurses::ll::mouseinterval(0);
let bracketed_paste_guard = if ext::define_key_code(const_cstr!("\x1b[200~").as_cstr(), 2000).is_ok() &&
ext::define_key_code(const_cstr!("\x1b[201~").as_cstr(), 2001).is_ok() {
BracketedPaste::start()
} else {
None
};
let xterm_modify_other_keys_guard = if ext::define_key_code(const_cstr!("\x1b[27;").as_cstr(), 2100).is_ok() {
XTermModifyOtherKeys::start()
} else {
None
};
let kitty_full_mode_guard = if ext::define_key_code(const_cstr!("\x1b_K").as_cstr(), 2200).is_ok() &&
ext::define_key_code(const_cstr!("\x1b\\").as_cstr(), 2201).is_ok() {
KittyFullMode::start()
} else {
None
};
if std::env::var_os("ESCDELAY").is_none() {
ncurses::ll::set_escdelay(25);
}
let mut extra_bound_keys = Vec::new();
for &(name, inp) in KNOWN_EXTRA_TERM_CAPABILITIES {
if let Some(description) = ext::get_terminfo_string(name.as_cstr()) {
if let Ok(code) = ext::key_code_for(description) {
extra_bound_keys.push((code, inp));
}
}
}
if ext::key_code_for(const_cstr!("\x1b[A").as_cstr()) == Ok(ncurses::KEY_UP) &&
ext::key_code_for(const_cstr!("\x1b[B").as_cstr()) == Ok(ncurses::KEY_DOWN) &&
ext::key_code_for(const_cstr!("\x1b[C").as_cstr()) == Ok(ncurses::KEY_RIGHT) &&
ext::key_code_for(const_cstr!("\x1b[D").as_cstr()) == Ok(ncurses::KEY_LEFT) &&
ext::key_code_for(const_cstr!("\x1b[c").as_cstr()) == Ok(ncurses::KEY_SRIGHT) &&
ext::key_code_for(const_cstr!("\x1b[d").as_cstr()) == Ok(ncurses::KEY_SLEFT) {
let _ = define_if_necessary(const_cstr!("\x1bOa").as_cstr(), 2340);
let _ = define_if_necessary(const_cstr!("\x1bOb").as_cstr(), 2341);
let _ = define_if_necessary(const_cstr!("\x1bOc").as_cstr(), 2342);
let _ = define_if_necessary(const_cstr!("\x1bOd").as_cstr(), 2343);
let _ = define_if_necessary(const_cstr!("\x1b\x1bOa").as_cstr(), 2360);
let _ = define_if_necessary(const_cstr!("\x1b\x1bOb").as_cstr(), 2361);
let _ = define_if_necessary(const_cstr!("\x1b\x1bOc").as_cstr(), 2362);
let _ = define_if_necessary(const_cstr!("\x1b\x1bOd").as_cstr(), 2363);
let _ = define_if_necessary(const_cstr!("\x1b\x1b[A").as_cstr(), 2320);
let _ = define_if_necessary(const_cstr!("\x1b\x1b[B").as_cstr(), 2321);
let _ = define_if_necessary(const_cstr!("\x1b\x1b[C").as_cstr(), 2322);
let _ = define_if_necessary(const_cstr!("\x1b\x1b[D").as_cstr(), 2323);
let _ = define_if_necessary(const_cstr!("\x1b\x1b[a").as_cstr(), 2330);
let _ = define_if_necessary(const_cstr!("\x1b\x1b[b").as_cstr(), 2331);
let _ = define_if_necessary(const_cstr!("\x1b\x1b[c").as_cstr(), 2332);
let _ = define_if_necessary(const_cstr!("\x1b\x1b[d").as_cstr(), 2333);
if ext::key_code_for(const_cstr!("\x1b[3~").as_cstr()) == Ok(ncurses::KEY_DC) {
let _ = define_if_necessary(const_cstr!("\x1b[3^").as_cstr(), 2348);
let _ = define_if_necessary(const_cstr!("\x1b\x1b[3^").as_cstr(), 2368);
}
}
if ext::key_code_for(const_cstr!("\x1bOA").as_cstr()) == Ok(ncurses::KEY_UP) &&
ext::key_code_for(const_cstr!("\x1bOB").as_cstr()) == Ok(ncurses::KEY_DOWN) &&
ext::key_code_for(const_cstr!("\x1bOC").as_cstr()) == Ok(ncurses::KEY_RIGHT) &&
ext::key_code_for(const_cstr!("\x1bOD").as_cstr()) == Ok(ncurses::KEY_LEFT) &&
ext::key_code_for(const_cstr!("\x1b[1;2C").as_cstr()) == Ok(ncurses::KEY_SRIGHT) &&
ext::key_code_for(const_cstr!("\x1b[1;2D").as_cstr()) == Ok(ncurses::KEY_SLEFT) {
for mode in 2..=7 {
for &(indicator, key) in &[(b'A', 0), (b'B', 1), (b'C', 2), (b'D', 3), (b'H', 4), (b'F', 5)] {
let _ = define_if_necessary(
CStr::from_bytes_with_nul(&[0x1b, b'[', b'1', b';', b'1' + mode as u8, indicator, 0]).unwrap(),
2300 + mode * 10 + key
);
}
}
if ext::key_code_for(const_cstr!("\x1b[3~").as_cstr()) == Ok(ncurses::KEY_DC) &&
ext::key_code_for(const_cstr!("\x1b[5~").as_cstr()) == Ok(ncurses::KEY_PPAGE) &&
ext::key_code_for(const_cstr!("\x1b[6~").as_cstr()) == Ok(ncurses::KEY_NPAGE) {
for mode in 2..=7 {
for &(indicator, key) in &[(b'3', 8), (b'5', 6), (b'6', 7)] {
let _ = define_if_necessary(
CStr::from_bytes_with_nul(&[0x1b, b'[', indicator, b';', b'1' + mode as u8, b'~', 0]).unwrap(),
2300 + mode * 10 + key
);
}
}
}
}
for byte in (1..=26).chain(48..=57).chain(65..=90).chain(97..=122) {
let _ = define_if_necessary(CStr::from_bytes_with_nul(&[0x1b, byte as u8, 0]).unwrap(), 3000 + byte);
}
ncurses::ll::ungetch(ncurses::KEY_RESIZE);
InputStream {
_bracketed_paste: bracketed_paste_guard,
_xterm_modify_keys: xterm_modify_other_keys_guard,
_kitty_full_mode: kitty_full_mode_guard,
extra_bound_keys: extra_bound_keys,
in_progress_codepoint: 0,
utf8_bytes_left: 0,
xterm_modify_key_state: XTermModifyKeyState::Off,
kitty_full_mode_state: KittyFullModeState::Off
}
}
pub fn next_event(&mut self, window: ncurses::WINDOW) -> Result<Event, ()> {
use crate::KeyInput::*;
const NONE: Modifiers = crate::Modifiers::NONE;
const CTRL: Modifiers = crate::Modifiers::CTRL;
const ALT: Modifiers = crate::Modifiers::ALT;
const SHIFT: Modifiers = crate::Modifiers::SHIFT;
loop {
let curses_input = unsafe { ncurses::ll::wgetch(window) };
if curses_input == ncurses::ERR {
return Err(());
}
let input;
if curses_input < 256 {
let byte = curses_input as u8;
if self.utf8_bytes_left == 0 {
if byte >> 7 == 0b0 {
self.utf8_bytes_left = 0;
self.in_progress_codepoint = (byte & 0x7f) as u32;
} else if byte >> 5 == 0b110 {
self.utf8_bytes_left = 1;
self.in_progress_codepoint = (byte & 0x1f) as u32;
} else if byte >> 4 == 0b1110 {
self.utf8_bytes_left = 2;
self.in_progress_codepoint = (byte & 0x0f) as u32;
} else if byte >> 3 == 0b11110 {
self.utf8_bytes_left = 3;
self.in_progress_codepoint = (byte & 0x07) as u32;
} else {
return Ok(KeyPress { modifiers: NONE, key: Byte(byte), is_repeat: false });
}
} else if byte >> 6 == 0b10 {
self.utf8_bytes_left -= 1;
self.in_progress_codepoint = (self.in_progress_codepoint << 6) | ((byte & 0x3f) as u32);
} else {
return Ok(KeyPress { modifiers: NONE, key: Byte(byte), is_repeat: false });
}
if self.utf8_bytes_left == 0 {
input = Codepoint(std::char::from_u32(self.in_progress_codepoint).expect("BUG: Bad char cast"));
} else {
continue;
}
} else {
input = Special(curses_input);
}
if let Special(code) = input {
for &(possible_code, possible_inp) in &self.extra_bound_keys {
if possible_code == code {
return Ok(possible_inp);
}
}
}
match input {
Special(ncurses::KEY_RESIZE) => {
let mut height = 0;
let mut width = 0;
ncurses::getmaxyx(window, &mut height, &mut width);
return Ok(Resize {
width: width as u32,
height: height as u32
});
},
Special(ncurses::KEY_MOUSE) => {
let mut event = ncurses::ll::MEVENT {
id: 0,
x: 0,
y: 0,
z: 0,
bstate: 0
};
if ncurses::getmouse(&mut event) != ncurses::OK {
return Err(());
}
return Ok(Mouse {
device_id: event.id as u16,
x: event.x as u32,
y: event.y as u32,
buttons: event.bstate & !((ncurses::BUTTON_CTRL | ncurses::BUTTON_ALT | ncurses::BUTTON_SHIFT) as u32),
modifiers: if event.bstate & (ncurses::BUTTON_CTRL as u32) != 0 { CTRL } else { NONE }
| if event.bstate & (ncurses::BUTTON_ALT as u32) != 0 { ALT } else { NONE }
| if event.bstate & (ncurses::BUTTON_SHIFT as u32) != 0 { SHIFT } else { NONE }
});
}
Special(2000) => return Ok(PasteBegin),
Special(2001) => return Ok(PasteEnd),
Special(ncurses::KEY_SLEFT) => return Ok(KeyPress { modifiers: SHIFT, key: Special(ncurses::KEY_LEFT), is_repeat: false }),
Special(ncurses::KEY_SRIGHT) => return Ok(KeyPress { modifiers: SHIFT, key: Special(ncurses::KEY_RIGHT), is_repeat: false }),
Special(ncurses::KEY_SR) => return Ok(KeyPress { modifiers: SHIFT, key: Special(ncurses::KEY_UP), is_repeat: false }),
Special(ncurses::KEY_SF) => return Ok(KeyPress { modifiers: SHIFT, key: Special(ncurses::KEY_DOWN), is_repeat: false }),
Special(ncurses::KEY_SHOME) => return Ok(KeyPress { modifiers: SHIFT, key: Special(ncurses::KEY_HOME), is_repeat: false }),
Special(ncurses::KEY_SEND) => return Ok(KeyPress { modifiers: SHIFT, key: Special(ncurses::KEY_END), is_repeat: false }),
Special(ncurses::KEY_SDC) => return Ok(KeyPress { modifiers: SHIFT, key: Special(ncurses::KEY_DC), is_repeat: false }),
Special(ncurses::KEY_BTAB) => return Ok(KeyPress { modifiers: SHIFT, key: Codepoint('\t'), is_repeat: false }),
Special(ncurses::KEY_SUSPEND) => return Ok(KeyPress { modifiers: CTRL, key: Codepoint('z'), is_repeat: false }),
Codepoint('\u{7f}') => return Ok(KeyPress { modifiers: NONE, key: Special(ncurses::KEY_BACKSPACE), is_repeat: false }),
Codepoint(chr) if (chr as u32) < 27 && chr != '\t' && chr != '\n' && chr != '\u{8}'
=> return Ok(KeyPress { modifiers: CTRL, key: Codepoint(std::char::from_u32(chr as u32 + 96).unwrap()), is_repeat: false }),
Codepoint(chr) if (chr as u32) > 128 && (chr as u32) < 155 => return Ok(KeyPress { modifiers: CTRL | ALT, key: Codepoint(std::char::from_u32(chr as u32 - 32).unwrap()), is_repeat: false }),
Special(code @ 3001..=3026) => return Ok(KeyPress { modifiers: CTRL | ALT, key: Codepoint(std::char::from_u32(code as u32 - 3000 + 96).unwrap()), is_repeat: false}),
Special(code @ 3000..=3255) => return Ok(KeyPress { modifiers: ALT, key: Codepoint(std::char::from_u32(code as u32 - 3000).unwrap()), is_repeat: false }),
Special(code @ 2300..=2399) => {
let base_code = code - 2300;
let modifiers = Modifiers((base_code / 10) as u8);
match base_code % 10 {
0 => return Ok(KeyPress { modifiers: modifiers, key: Special(ncurses::KEY_UP), is_repeat: false }),
1 => return Ok(KeyPress { modifiers: modifiers, key: Special(ncurses::KEY_DOWN), is_repeat: false }),
2 => return Ok(KeyPress { modifiers: modifiers, key: Special(ncurses::KEY_RIGHT), is_repeat: false }),
3 => return Ok(KeyPress { modifiers: modifiers, key: Special(ncurses::KEY_LEFT), is_repeat: false }),
4 => return Ok(KeyPress { modifiers: modifiers, key: Special(ncurses::KEY_HOME), is_repeat: false }),
5 => return Ok(KeyPress { modifiers: modifiers, key: Special(ncurses::KEY_END), is_repeat: false }),
6 => return Ok(KeyPress { modifiers: modifiers, key: Special(ncurses::KEY_PPAGE), is_repeat: false }),
7 => return Ok(KeyPress { modifiers: modifiers, key: Special(ncurses::KEY_NPAGE), is_repeat: false }),
8 => return Ok(KeyPress { modifiers: modifiers, key: Special(ncurses::KEY_DC), is_repeat: false }),
_ => { }
}
},
_ => { }
}
if let Special(2100) = input {
self.xterm_modify_key_state = XTermModifyKeyState::ParsingMode(0);
continue;
}
match self.xterm_modify_key_state {
XTermModifyKeyState::Off => { },
XTermModifyKeyState::ParsingMode(mode_so_far) => {
if let Codepoint(chr) = input {
if let Some(digit) = chr.to_digit(10) {
self.xterm_modify_key_state = XTermModifyKeyState::ParsingMode(mode_so_far * 10 + digit);
continue;
} else if chr == ';' {
self.xterm_modify_key_state = XTermModifyKeyState::ParsingChar(mode_so_far, 0);
continue;
}
}
},
XTermModifyKeyState::ParsingChar(mode, char_so_far) => {
if let Codepoint(chr) = input {
if let Some(digit) = chr.to_digit(10) {
self.xterm_modify_key_state = XTermModifyKeyState::ParsingChar(mode, char_so_far * 10 + digit);
continue;
} else if chr == '~' {
self.xterm_modify_key_state = XTermModifyKeyState::Off;
if 1 <= mode {
return Ok(KeyPress { modifiers: Modifiers((mode as u8) - 1), key: Codepoint(std::char::from_u32(char_so_far).unwrap()), is_repeat: false });
} else {
eprintln!("0 mode?");
continue;
}
}
}
}
}
if let Special(2200) = input {
self.kitty_full_mode_state = KittyFullModeState::ParsingType;
continue;
}
match self.kitty_full_mode_state {
KittyFullModeState::Off => { },
KittyFullModeState::ParsingType => match input {
Codepoint('p') => {
self.kitty_full_mode_state = KittyFullModeState::ParsingModifiers(KeyType::Press);
continue;
},
Codepoint('r') => {
self.kitty_full_mode_state = KittyFullModeState::ParsingModifiers(KeyType::Release);
continue;
},
Codepoint('t') => {
self.kitty_full_mode_state = KittyFullModeState::ParsingModifiers(KeyType::Repeat);
continue;
},
_ => { }
},
KittyFullModeState::ParsingModifiers(key_type) => {
if let Codepoint(chr) = input {
let decoded = if 'A' <= chr && chr <= 'Z' {
Some(chr as u32 - 'A' as u32)
} else if 'a' <= chr && chr <= 'z' {
Some(chr as u32 - 'a' as u32 + 26)
} else if '0' <= chr && chr <= '9' {
Some(chr as u32 - '0' as u32 + 52)
} else if chr == '+' {
Some(62)
} else if chr == '/' {
Some(63)
} else {
None
};
if let Some(mode) = decoded {
self.kitty_full_mode_state = KittyFullModeState::ParsingKey(key_type, mode as u8, 0);
continue;
}
}
},
KittyFullModeState::ParsingKey(key_type, mode, key_so_far) => {
if let Codepoint(chr) = input {
let decoded = if 'A' <= chr && chr <= 'Z' {
Some(chr as u32 - 'A' as u32)
} else if 'a' <= chr && chr <= 'z' {
Some(chr as u32 - 'a' as u32 + 26)
} else if '0' <= chr && chr <= '9' {
Some(chr as u32 - '0' as u32 + 52)
} else {
".-:+=^!/*?&<>()[]{}@%$#".chars().position(|c| c == chr).map(|i| i as u32 + 62)
};
if let Some(value) = decoded {
self.kitty_full_mode_state = KittyFullModeState::ParsingKey(key_type, mode, key_so_far * 85 + value);
continue;
}
} else if let Special(2201) = input {
self.kitty_full_mode_state = KittyFullModeState::Off;
let modifiers = Modifiers(mode);
let translated = match key_so_far {
6..=15 => if modifiers & SHIFT != NONE {
Special(key_so_far as i32 + 600)
} else {
Codepoint(std::char::from_u32('0' as u32 + key_so_far as u32 - 6).unwrap())
},
18..=43 => if modifiers & SHIFT != NONE { Codepoint(std::char::from_u32('A' as u32 + key_so_far as u32 - 18).unwrap())
} else {
Codepoint(std::char::from_u32('a' as u32 + key_so_far as u32 - 18).unwrap())
},
50 => Codepoint('\u{1b}'), 52 => Codepoint('\t'),
53 => Special(ncurses::KEY_BACKSPACE),
55 => Special(ncurses::KEY_DC),
56 => Special(ncurses::KEY_RIGHT),
57 => Special(ncurses::KEY_LEFT),
58 => Special(ncurses::KEY_DOWN),
59 => Special(ncurses::KEY_UP),
60 => Special(ncurses::KEY_PPAGE),
61 => Special(ncurses::KEY_NPAGE),
62 => Special(ncurses::KEY_HOME),
63 => Special(ncurses::KEY_END),
69 => Special(ncurses::KEY_F1),
_ => Special(key_so_far as i32 + 600)
};
return Ok(match key_type {
KeyType::Press => KeyPress { modifiers: modifiers, key: translated, is_repeat: false },
KeyType::Repeat => KeyPress { modifiers: modifiers, key: translated, is_repeat: true },
KeyType::Release => KeyRelease { modifiers: modifiers, key: translated },
});
}
}
}
return Ok(KeyPress { modifiers: NONE, key: input, is_repeat: false })
}
}
}