1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub struct KeyEvent(pub KeyCode, pub Modifiers);
6
7impl KeyEvent {
8 pub(crate) const BACKSPACE: Self = Self(KeyCode::Backspace, Modifiers::NONE);
10 pub(crate) const ENTER: Self = Self(KeyCode::Enter, Modifiers::NONE);
12 pub(crate) const ESC: Self = Self(KeyCode::Esc, Modifiers::NONE);
14
15 #[must_use]
17 pub fn new(c: char, mut mods: Modifiers) -> Self {
18 use {KeyCode as K, KeyEvent as E, Modifiers as M};
19
20 if !c.is_control() {
21 if !mods.is_empty() {
22 mods.remove(M::SHIFT); }
25 return E(K::Char(c), mods);
26 }
27 #[allow(clippy::match_same_arms)]
28 match c {
29 '\x00' => E(K::Char('@'), mods | M::CTRL), '\x01' => E(K::Char('A'), mods | M::CTRL),
31 '\x02' => E(K::Char('B'), mods | M::CTRL),
32 '\x03' => E(K::Char('C'), mods | M::CTRL),
33 '\x04' => E(K::Char('D'), mods | M::CTRL),
34 '\x05' => E(K::Char('E'), mods | M::CTRL),
35 '\x06' => E(K::Char('F'), mods | M::CTRL),
36 '\x07' => E(K::Char('G'), mods | M::CTRL), #[cfg(unix)]
38 '\x08' => E(K::Backspace, mods), #[cfg(windows)]
40 '\x08' => E(K::Char('H'), mods | M::CTRL),
41 #[cfg(unix)]
42 '\x09' => {
43 if mods.contains(M::SHIFT) {
45 mods.remove(M::SHIFT);
46 E(K::BackTab, mods)
47 } else {
48 E(K::Tab, mods)
49 }
50 }
51 #[cfg(windows)]
52 '\x09' => E(K::Char('I'), mods | M::CTRL),
53 '\x0a' => E(K::Char('J'), mods | M::CTRL), '\x0b' => E(K::Char('K'), mods | M::CTRL),
55 '\x0c' => E(K::Char('L'), mods | M::CTRL),
56 #[cfg(unix)]
57 '\x0d' => E(K::Enter, mods), #[cfg(windows)]
59 '\x0d' => E(K::Char('M'), mods | M::CTRL),
60 '\x0e' => E(K::Char('N'), mods | M::CTRL),
61 '\x0f' => E(K::Char('O'), mods | M::CTRL),
62 '\x10' => E(K::Char('P'), mods | M::CTRL),
63 '\x11' => E(K::Char('Q'), mods | M::CTRL),
64 '\x12' => E(K::Char('R'), mods | M::CTRL),
65 '\x13' => E(K::Char('S'), mods | M::CTRL),
66 '\x14' => E(K::Char('T'), mods | M::CTRL),
67 '\x15' => E(K::Char('U'), mods | M::CTRL),
68 '\x16' => E(K::Char('V'), mods | M::CTRL),
69 '\x17' => E(K::Char('W'), mods | M::CTRL),
70 '\x18' => E(K::Char('X'), mods | M::CTRL),
71 '\x19' => E(K::Char('Y'), mods | M::CTRL),
72 '\x1a' => E(K::Char('Z'), mods | M::CTRL),
73 '\x1b' => E(K::Esc, mods), '\x1c' => E(K::Char('\\'), mods | M::CTRL),
75 '\x1d' => E(K::Char(']'), mods | M::CTRL),
76 '\x1e' => E(K::Char('^'), mods | M::CTRL),
77 '\x1f' => E(K::Char('_'), mods | M::CTRL),
78 '\x7f' => E(K::Backspace, mods), '\u{9b}' => E(K::Esc, mods | M::SHIFT),
80 _ => E(K::Null, mods),
81 }
82 }
83
84 #[must_use]
86 pub fn ctrl(c: char) -> Self {
87 Self::new(c, Modifiers::CTRL)
88 }
89
90 #[must_use]
92 pub fn alt(c: char) -> Self {
93 Self::new(c, Modifiers::ALT)
94 }
95
96 #[must_use]
100 pub fn normalize(e: Self) -> Self {
101 use {KeyCode as K, KeyEvent as E, Modifiers as M};
102
103 match e {
104 E(K::Char(c), m) if c.is_ascii_control() => Self::new(c, m),
105 E(K::Char(c), m) if c.is_ascii_lowercase() && m.contains(M::CTRL) => {
106 E(K::Char(c.to_ascii_uppercase()), m)
107 }
108 E(K::Char(c), m) if c.is_ascii_uppercase() && m.contains(M::SHIFT) => {
109 E(K::Char(c), m ^ M::SHIFT)
110 }
111 E(K::Tab, m) if m.contains(M::SHIFT) => E(K::BackTab, m ^ M::SHIFT),
112 _ => e,
113 }
114 }
115}
116
117impl From<char> for KeyEvent {
118 fn from(c: char) -> Self {
119 Self::new(c, Modifiers::NONE)
120 }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
125#[non_exhaustive]
126pub enum KeyCode {
127 UnknownEscSeq,
129 Backspace,
131 BackTab,
133 BracketedPasteStart,
135 BracketedPasteEnd,
137 Char(char),
139 Delete,
141 Down,
143 End,
145 Enter,
147 Esc,
149 F(u8),
151 Home,
153 Insert,
155 Left,
157 Null,
159 PageDown,
161 PageUp,
163 Right,
165 Tab,
167 Up,
169}
170
171bitflags::bitflags! {
172 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
174 pub struct Modifiers: u8 {
175 const CTRL = 1<<3;
177 const ALT = 1<<2;
179 const SHIFT = 1<<1;
181
182 const NONE = 0;
184 const CTRL_SHIFT = Self::CTRL.bits() | Self::SHIFT.bits();
186 const ALT_SHIFT = Self::ALT.bits() | Self::SHIFT.bits();
188 const CTRL_ALT = Self::CTRL.bits() | Self::ALT.bits();
190 const CTRL_ALT_SHIFT = Self::CTRL.bits() | Self::ALT.bits() | Self::SHIFT.bits();
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::{KeyCode as K, KeyEvent as E, Modifiers as M};
198
199 #[test]
200 fn new() {
201 assert_eq!(E::ESC, E::new('\x1b', M::NONE));
202 }
203
204 #[test]
205 #[cfg(unix)]
206 fn from() {
207 assert_eq!(E(K::Tab, M::NONE), E::from('\t'));
208 }
209
210 #[test]
211 #[cfg(windows)]
212 fn from() {
213 assert_eq!(E(K::Char('I'), M::CTRL), E::from('\t'));
214 }
215
216 #[test]
217 fn normalize() {
218 assert_eq!(E::ctrl('A'), E::normalize(E(K::Char('\x01'), M::NONE)));
219 assert_eq!(E::ctrl('A'), E::normalize(E::ctrl('a')));
220 assert_eq!(E::from('A'), E::normalize(E(K::Char('A'), M::SHIFT)));
221 assert_eq!(E(K::BackTab, M::NONE), E::normalize(E(K::Tab, M::SHIFT)));
222 }
223}