Skip to main content

brush_core/sys/unix/
input.rs

1//! Terminal input utilities
2
3use std::collections::HashMap;
4use std::sync::LazyLock;
5use terminfo::capability as cap;
6
7use crate::{error, interfaces};
8
9macro_rules! key {
10    ( $terminfo:expr , $our_key:expr, $terminfo_key:ty ) => {{
11        (
12            $our_key,
13            $terminfo
14                .get::<$terminfo_key>()
15                .map(|k| k.expand().to_vec()),
16        )
17    }};
18}
19
20fn build_terminfo_key_map() -> HashMap<Vec<u8>, interfaces::Key> {
21    let mut map: HashMap<Vec<u8>, interfaces::Key> = HashMap::new();
22
23    if let Ok(ti) = terminfo::Database::from_env() {
24        // Iterate over key capabilities and populate the map
25        let key_capabilities = [
26            key!(ti, interfaces::Key::F(1), cap::KeyF1<'_>),
27            key!(ti, interfaces::Key::F(2), cap::KeyF2<'_>),
28            key!(ti, interfaces::Key::F(3), cap::KeyF3<'_>),
29            key!(ti, interfaces::Key::F(4), cap::KeyF4<'_>),
30            key!(ti, interfaces::Key::F(5), cap::KeyF5<'_>),
31            key!(ti, interfaces::Key::F(6), cap::KeyF6<'_>),
32            key!(ti, interfaces::Key::F(7), cap::KeyF7<'_>),
33            key!(ti, interfaces::Key::F(8), cap::KeyF8<'_>),
34            key!(ti, interfaces::Key::F(9), cap::KeyF9<'_>),
35            key!(ti, interfaces::Key::F(10), cap::KeyF10<'_>),
36            key!(ti, interfaces::Key::F(11), cap::KeyF11<'_>),
37            key!(ti, interfaces::Key::F(12), cap::KeyF12<'_>),
38            key!(ti, interfaces::Key::Backspace, cap::KeyBackspace<'_>),
39            key!(ti, interfaces::Key::Enter, cap::KeyEnter<'_>),
40            key!(ti, interfaces::Key::Left, cap::KeyLeft<'_>),
41            key!(ti, interfaces::Key::Right, cap::KeyRight<'_>),
42            key!(ti, interfaces::Key::Up, cap::KeyUp<'_>),
43            key!(ti, interfaces::Key::Down, cap::KeyDown<'_>),
44            key!(ti, interfaces::Key::Home, cap::KeyHome<'_>),
45            key!(ti, interfaces::Key::End, cap::KeyEnd<'_>),
46            key!(ti, interfaces::Key::PageUp, cap::KeyPPage<'_>),
47            key!(ti, interfaces::Key::PageDown, cap::KeyNPage<'_>),
48            key!(ti, interfaces::Key::BackTab, cap::BackTab<'_>),
49            // It's not clear if these belong here, because they're not
50            // strictly "key" capabilities.
51            key!(ti, interfaces::Key::Up, cap::CursorUp<'_>),
52            key!(ti, interfaces::Key::Down, cap::CursorDown<'_>),
53            key!(ti, interfaces::Key::Left, cap::CursorLeft<'_>),
54            key!(ti, interfaces::Key::Right, cap::CursorRight<'_>),
55        ];
56
57        for (key, v) in key_capabilities {
58            if let Some(Ok(v)) = v {
59                map.insert(v.clone(), key.clone());
60            }
61        }
62    }
63
64    map
65}
66
67pub(crate) static TERMINFO_KEY_MAP: LazyLock<HashMap<Vec<u8>, interfaces::Key>> =
68    LazyLock::new(build_terminfo_key_map);
69
70/// Translates a key code (byte sequence) into a `Key` enum value. Returns `None`
71/// if the key code is not recognized.
72///
73/// # Arguments
74///
75/// * `key_code`: The byte sequence representing the key code.
76pub fn try_get_key_from_key_code(key_code: &[u8]) -> Option<interfaces::Key> {
77    if let Some(key) = TERMINFO_KEY_MAP.get(key_code) {
78        Some(key.clone())
79    } else if key_code.len() == 1 && !key_code[0].is_ascii_control() {
80        Some(interfaces::Key::Character(key_code[0] as char))
81    } else {
82        None
83    }
84}