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 pub struct Modifiers: u8 {
174 const CTRL = 1<<3;
176 const ALT = 1<<2;
178 const SHIFT = 1<<1;
180
181 const NONE = 0;
183 const CTRL_SHIFT = Self::CTRL.bits | Self::SHIFT.bits;
185 const ALT_SHIFT = Self::ALT.bits | Self::SHIFT.bits;
187 const CTRL_ALT = Self::CTRL.bits | Self::ALT.bits;
189 const CTRL_ALT_SHIFT = Self::CTRL.bits | Self::ALT.bits | Self::SHIFT.bits;
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::{KeyCode as K, KeyEvent as E, Modifiers as M};
197
198 #[test]
199 fn new() {
200 assert_eq!(E::ESC, E::new('\x1b', M::NONE));
201 }
202
203 #[test]
204 #[cfg(unix)]
205 fn from() {
206 assert_eq!(E(K::Tab, M::NONE), E::from('\t'));
207 }
208
209 #[test]
210 #[cfg(windows)]
211 fn from() {
212 assert_eq!(E(K::Char('I'), M::CTRL), E::from('\t'));
213 }
214
215 #[test]
216 fn normalize() {
217 assert_eq!(E::ctrl('A'), E::normalize(E(K::Char('\x01'), M::NONE)));
218 assert_eq!(E::ctrl('A'), E::normalize(E::ctrl('a')));
219 assert_eq!(E::from('A'), E::normalize(E(K::Char('A'), M::SHIFT)));
220 assert_eq!(E(K::BackTab, M::NONE), E::normalize(E(K::Tab, M::SHIFT)));
221 }
222}