diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-14 01:04:51 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-14 01:04:51 +0300 |
| commit | edaa3a1a092763cb25fd9e21772c95ee56a75a15 (patch) | |
| tree | 88d54f6a3aa2a43c1c6dd4c7aaabd78ddfc03466 /src/input | |
| parent | 5ae1cea94bcd77c8a75abfbe6c996e5cf2a91ab7 (diff) | |
fix unify app key normalization for enhanced keyboard reporting
Diffstat (limited to 'src/input')
| -rw-r--r-- | src/input/mod.rs | 5 | ||||
| -rw-r--r-- | src/input/model.rs | 52 |
2 files changed, 56 insertions, 1 deletions
diff --git a/src/input/mod.rs b/src/input/mod.rs index 109fd312..67e8a14d 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -8,7 +8,10 @@ pub use encode::encode_key; pub use encode::{ encode_cursor_key, encode_mouse_button, encode_mouse_scroll, encode_terminal_key, }; -pub use model::{KeyboardProtocol, MouseProtocolEncoding, MouseProtocolMode, TerminalKey}; +pub use model::{ + normalize_app_key_binding, KeyboardProtocol, MouseProtocolEncoding, MouseProtocolMode, + TerminalKey, +}; pub use parse::parse_terminal_key_sequence; #[cfg(test)] diff --git a/src/input/model.rs b/src/input/model.rs index 9339b965..5721f82b 100644 --- a/src/input/model.rs +++ b/src/input/model.rs @@ -1,5 +1,57 @@ use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +pub fn normalize_app_key_binding( + code: KeyCode, + modifiers: KeyModifiers, + shifted_codepoint: Option<u32>, +) -> (KeyCode, KeyModifiers) { + let KeyCode::Char(raw_char) = code else { + return (code, modifiers); + }; + + let shifted_char = shifted_codepoint + .and_then(char::from_u32) + .or_else(|| modifiers.contains(KeyModifiers::SHIFT).then_some(raw_char)) + .and_then(shifted_printable_char); + + let normalized_char = shifted_char.unwrap_or(raw_char); + let mut normalized_modifiers = modifiers; + if shifted_char.is_some() { + normalized_modifiers.remove(KeyModifiers::SHIFT); + } + + (KeyCode::Char(normalized_char), normalized_modifiers) +} + +fn shifted_printable_char(ch: char) -> Option<char> { + match ch { + 'a'..='z' => Some(ch.to_ascii_uppercase()), + 'A'..='Z' => Some(ch), + '`' | '~' => Some('~'), + '1' | '!' => Some('!'), + '2' | '@' => Some('@'), + '3' | '#' => Some('#'), + '4' | '$' => Some('$'), + '5' | '%' => Some('%'), + '6' | '^' => Some('^'), + '7' | '&' => Some('&'), + '8' | '*' => Some('*'), + '9' | '(' => Some('('), + '0' | ')' => Some(')'), + '-' | '_' => Some('_'), + '=' | '+' => Some('+'), + '[' | '{' => Some('{'), + ']' | '}' => Some('}'), + '\\' | '|' => Some('|'), + ';' | ':' => Some(':'), + '\'' | '"' => Some('"'), + ',' | '<' => Some('<'), + '.' | '>' => Some('>'), + '/' | '?' => Some('?'), + _ => None, + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct TerminalKey { pub code: KeyCode, |
