1use std::collections::HashMap;
4use std::sync::LazyLock;
5
6use terminfo::capability as cap;
7
8use crate::{error, interfaces};
9
10macro_rules! key {
11 ( $terminfo:expr , $our_key:expr, $terminfo_key:ty ) => {{
12 (
13 $our_key,
14 $terminfo
15 .get::<$terminfo_key>()
16 .map(|k| k.expand().to_vec()),
17 )
18 }};
19}
20
21fn build_terminfo_key_map() -> HashMap<Vec<u8>, interfaces::Key> {
22 let mut map: HashMap<Vec<u8>, interfaces::Key> = HashMap::new();
23
24 if let Ok(ti) = terminfo::Database::from_env() {
25 let key_capabilities = [
27 key!(ti, interfaces::Key::F(1), cap::KeyF1<'_>),
28 key!(ti, interfaces::Key::F(2), cap::KeyF2<'_>),
29 key!(ti, interfaces::Key::F(3), cap::KeyF3<'_>),
30 key!(ti, interfaces::Key::F(4), cap::KeyF4<'_>),
31 key!(ti, interfaces::Key::F(5), cap::KeyF5<'_>),
32 key!(ti, interfaces::Key::F(6), cap::KeyF6<'_>),
33 key!(ti, interfaces::Key::F(7), cap::KeyF7<'_>),
34 key!(ti, interfaces::Key::F(8), cap::KeyF8<'_>),
35 key!(ti, interfaces::Key::F(9), cap::KeyF9<'_>),
36 key!(ti, interfaces::Key::F(10), cap::KeyF10<'_>),
37 key!(ti, interfaces::Key::F(11), cap::KeyF11<'_>),
38 key!(ti, interfaces::Key::F(12), cap::KeyF12<'_>),
39 key!(ti, interfaces::Key::Backspace, cap::KeyBackspace<'_>),
40 key!(ti, interfaces::Key::Enter, cap::KeyEnter<'_>),
41 key!(ti, interfaces::Key::Left, cap::KeyLeft<'_>),
42 key!(ti, interfaces::Key::Right, cap::KeyRight<'_>),
43 key!(ti, interfaces::Key::Up, cap::KeyUp<'_>),
44 key!(ti, interfaces::Key::Down, cap::KeyDown<'_>),
45 key!(ti, interfaces::Key::Home, cap::KeyHome<'_>),
46 key!(ti, interfaces::Key::End, cap::KeyEnd<'_>),
47 key!(ti, interfaces::Key::PageUp, cap::KeyPPage<'_>),
48 key!(ti, interfaces::Key::PageDown, cap::KeyNPage<'_>),
49 key!(ti, interfaces::Key::BackTab, cap::BackTab<'_>),
50 key!(ti, interfaces::Key::Up, cap::CursorUp<'_>),
53 key!(ti, interfaces::Key::Down, cap::CursorDown<'_>),
54 key!(ti, interfaces::Key::Left, cap::CursorLeft<'_>),
55 key!(ti, interfaces::Key::Right, cap::CursorRight<'_>),
56 ];
57
58 for (key, v) in key_capabilities {
59 if let Some(Ok(v)) = v {
60 map.insert(v.clone(), key.clone());
61 }
62 }
63 }
64
65 map
66}
67
68pub(crate) static TERMINFO_KEY_MAP: LazyLock<HashMap<Vec<u8>, interfaces::Key>> =
69 LazyLock::new(build_terminfo_key_map);
70
71pub fn try_get_key_from_key_code(key_code: &[u8]) -> Option<interfaces::Key> {
78 if let Some(key) = TERMINFO_KEY_MAP.get(key_code) {
79 Some(key.clone())
80 } else if key_code.len() == 1 && !key_code[0].is_ascii_control() {
81 Some(interfaces::Key::Character(key_code[0] as char))
82 } else {
83 None
84 }
85}