mod decode;
mod encode;
mod keyspec;
#[cfg(test)]
mod tests;
pub use decode::InputParser;
pub use encode::{PaneInputModes, encode_alternate_scroll, encode_key, encode_mouse, encode_paste, encode_paste_after};
pub use keyspec::KeySpec;
const ESC: u8 = 0x1b;
const PASTE_START: &[u8] = b"\x1b[200~";
const PASTE_END: &[u8] = b"\x1b[201~";
const F_TILDE: [u32; 16] = [15, 17, 18, 19, 20, 21, 23, 24, 25, 26, 28, 29, 31, 32, 33, 34];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Mods {
pub shift: bool,
pub alt: bool,
pub ctrl: bool,
}
impl Mods {
pub const NONE: Mods = Mods { shift: false, alt: false, ctrl: false };
#[cfg(test)]
pub const SHIFT: Mods = Mods { shift: true, alt: false, ctrl: false };
pub const ALT: Mods = Mods { shift: false, alt: true, ctrl: false };
pub const CTRL: Mods = Mods { shift: false, alt: false, ctrl: true };
pub fn is_empty(self) -> bool {
!(self.shift || self.alt || self.ctrl)
}
pub fn from_xterm_param(param: u32) -> Mods {
let bits = param.saturating_sub(1);
Mods { shift: bits & 1 != 0, alt: bits & (2 | 8) != 0, ctrl: bits & 4 != 0 }
}
pub fn xterm_param(self) -> u32 {
1 + u32::from(self.shift) + 2 * u32::from(self.alt) + 4 * u32::from(self.ctrl)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum KeyCode {
Char(char),
Enter,
Tab,
BackTab,
Backspace,
Esc,
Up,
Down,
Left,
Right,
Home,
End,
PageUp,
PageDown,
Insert,
Delete,
F(u8),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Key {
pub code: KeyCode,
pub mods: Mods,
pub raw: Vec<u8>,
}
impl Key {
#[cfg(test)]
pub fn new(code: KeyCode, mods: Mods) -> Key {
Key { code, mods, raw: Vec::new() }
}
pub fn has_place(&self) -> bool {
decode::kitty_fields(&self.raw).is_some_and(|f| f.base != 0)
}
pub fn by_place(&self) -> Option<Key> {
let KeyCode::Char(c) = self.code else { return None };
let base = decode::kitty_fields(&self.raw).and_then(|f| char::from_u32(f.base))?;
let code = if base.is_ascii_digit() {
if self.mods.shift || c.is_ascii_digit() {
return None;
}
base
} else if base.is_ascii_alphabetic() && !c.is_ascii() {
if c.is_uppercase() && !self.mods.ctrl { base.to_ascii_uppercase() } else { base.to_ascii_lowercase() }
} else {
return None;
};
Some(Key { code: KeyCode::Char(code), mods: self.mods, raw: self.raw.clone() })
}
}
pub fn kitty_key_flag(raw: &[u8], release: bool) -> u8 {
if release {
return 2;
}
match decode::kitty_fields(raw).map(|f| f.key) {
Some(57358..=57360 | 57441..=57454) => 8,
_ => 0,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MouseButton {
Left,
Middle,
Right,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MouseKind {
Press(MouseButton),
Release(MouseButton),
Drag(MouseButton),
Move,
ScrollUp,
ScrollDown,
ScrollLeft,
ScrollRight,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MouseEvent {
pub kind: MouseKind,
pub col: u16,
pub row: u16,
pub mods: Mods,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InputEvent {
Key(Key),
Mouse(MouseEvent),
Paste(Vec<u8>),
PastePiece(Vec<u8>),
FocusGained,
FocusLost,
Raw(Vec<u8>),
KittyKey {
raw: Vec<u8>,
release: bool,
},
}