1use macroquad::miniquad::{EventHandler, KeyMods, MouseButton};
6use macroquad::prelude::{
7 KeyCode, get_char_pressed, get_keys_pressed, get_keys_released, is_key_down,
8};
9use shuyi_ui::RawInputEvent;
10use shuyi_ui::input::{Chord, Key};
11
12use crate::focus::Heard;
13
14pub fn to_key(code: KeyCode) -> Option<Key> {
20 Some(match code {
21 KeyCode::A => Key::A,
22 KeyCode::B => Key::B,
23 KeyCode::C => Key::C,
24 KeyCode::D => Key::D,
25 KeyCode::E => Key::E,
26 KeyCode::F => Key::F,
27 KeyCode::G => Key::G,
28 KeyCode::H => Key::H,
29 KeyCode::I => Key::I,
30 KeyCode::J => Key::J,
31 KeyCode::K => Key::K,
32 KeyCode::L => Key::L,
33 KeyCode::M => Key::M,
34 KeyCode::N => Key::N,
35 KeyCode::O => Key::O,
36 KeyCode::P => Key::P,
37 KeyCode::Q => Key::Q,
38 KeyCode::R => Key::R,
39 KeyCode::S => Key::S,
40 KeyCode::T => Key::T,
41 KeyCode::U => Key::U,
42 KeyCode::V => Key::V,
43 KeyCode::W => Key::W,
44 KeyCode::X => Key::X,
45 KeyCode::Y => Key::Y,
46 KeyCode::Z => Key::Z,
47 KeyCode::Key0 => Key::Digit0,
48 KeyCode::Key1 => Key::Digit1,
49 KeyCode::Key2 => Key::Digit2,
50 KeyCode::Key3 => Key::Digit3,
51 KeyCode::Key4 => Key::Digit4,
52 KeyCode::Key5 => Key::Digit5,
53 KeyCode::Key6 => Key::Digit6,
54 KeyCode::Key7 => Key::Digit7,
55 KeyCode::Key8 => Key::Digit8,
56 KeyCode::Key9 => Key::Digit9,
57 KeyCode::Semicolon => Key::Semicolon,
58 KeyCode::Up => Key::Up,
59 KeyCode::Down => Key::Down,
60 KeyCode::Left => Key::Left,
61 KeyCode::Right => Key::Right,
62 KeyCode::Enter | KeyCode::KpEnter => Key::Enter,
63 KeyCode::Escape => Key::Escape,
64 KeyCode::Space => Key::Space,
65 KeyCode::Tab => Key::Tab,
66 KeyCode::Backspace => Key::Backspace,
67 KeyCode::F1 => Key::F1,
68 KeyCode::F2 => Key::F2,
69 KeyCode::F3 => Key::F3,
70 KeyCode::F4 => Key::F4,
71 KeyCode::F5 => Key::F5,
72 KeyCode::F6 => Key::F6,
73 KeyCode::F7 => Key::F7,
74 KeyCode::F8 => Key::F8,
75 KeyCode::F9 => Key::F9,
76 KeyCode::F10 => Key::F10,
77 KeyCode::F11 => Key::F11,
78 KeyCode::F12 => Key::F12,
79 KeyCode::Comma => Key::Comma,
80 KeyCode::Period => Key::Period,
81 KeyCode::Slash => Key::Slash,
82 KeyCode::Apostrophe => Key::Apostrophe,
83 KeyCode::LeftBracket => Key::LeftBracket,
84 KeyCode::RightBracket => Key::RightBracket,
85 KeyCode::Backslash => Key::Backslash,
86 KeyCode::Minus => Key::Minus,
87 KeyCode::Equal => Key::Equal,
88 KeyCode::GraveAccent => Key::Backquote,
89 KeyCode::Insert => Key::Insert,
90 KeyCode::Delete => Key::Delete,
91 KeyCode::Home => Key::Home,
92 KeyCode::End => Key::End,
93 KeyCode::PageUp => Key::PageUp,
94 KeyCode::PageDown => Key::PageDown,
95 KeyCode::Kp0 => Key::Kp0,
96 KeyCode::Kp1 => Key::Kp1,
97 KeyCode::Kp2 => Key::Kp2,
98 KeyCode::Kp3 => Key::Kp3,
99 KeyCode::Kp4 => Key::Kp4,
100 KeyCode::Kp5 => Key::Kp5,
101 KeyCode::Kp6 => Key::Kp6,
102 KeyCode::Kp7 => Key::Kp7,
103 KeyCode::Kp8 => Key::Kp8,
104 KeyCode::Kp9 => Key::Kp9,
105 KeyCode::KpAdd => Key::KpAdd,
106 KeyCode::KpSubtract => Key::KpSubtract,
107 KeyCode::KpMultiply => Key::KpMultiply,
108 KeyCode::KpDivide => Key::KpDivide,
109 KeyCode::KpDecimal => Key::KpDecimal,
110 _ => return None,
111 })
112}
113
114pub fn fullscreen_shortcut() -> bool {
118 let alt = is_key_down(KeyCode::LeftAlt) || is_key_down(KeyCode::RightAlt);
119 get_keys_pressed().into_iter().any(|code| {
120 code == KeyCode::F11 || (alt && matches!(code, KeyCode::Enter | KeyCode::KpEnter))
121 })
122}
123
124pub fn poll() -> Vec<RawInputEvent> {
127 let shift = is_key_down(KeyCode::LeftShift) || is_key_down(KeyCode::RightShift);
128 let keys = |codes: Vec<KeyCode>| -> Vec<(KeyCode, Key)> {
129 codes
130 .into_iter()
131 .filter_map(|code| Some((code, to_key(code)?)))
132 .collect()
133 };
134 let released = keys(get_keys_released().into_iter().collect());
135 let pressed = keys(get_keys_pressed().into_iter().collect());
136 let down = |key: Key| {
137 pressed
138 .iter()
139 .any(|&(code, k)| k == key && is_key_down(code))
140 };
141 let just = |list: &[(KeyCode, Key)]| list.iter().map(|&(_, key)| key).collect::<Vec<_>>();
142 let mut events = ordered(&just(&released), &just(&pressed), down, shift);
143 events.extend(std::iter::from_fn(get_char_pressed).map(RawInputEvent::Text));
144 events
145}
146
147pub fn ordered(
156 released: &[Key],
157 pressed: &[Key],
158 down: impl Fn(Key) -> bool,
159 shift: bool,
160) -> Vec<RawInputEvent> {
161 let tapped = |key: Key| released.contains(&key) && pressed.contains(&key) && !down(key);
162 let ups = released
163 .iter()
164 .filter(|&&key| !tapped(key))
165 .map(|&key| RawInputEvent::Up(key));
166 let downs = pressed
167 .iter()
168 .map(|&key| RawInputEvent::Down(Chord { key, shift }));
169 let taps = pressed
170 .iter()
171 .filter(|&&key| tapped(key))
172 .map(|&key| RawInputEvent::Up(key));
173 ups.chain(downs).chain(taps).collect()
174}
175
176#[derive(Debug, Default)]
180pub(crate) struct WindowHeard(pub(crate) Vec<Heard>);
181
182impl EventHandler for WindowHeard {
183 fn update(&mut self) {}
184
185 fn draw(&mut self) {}
186
187 fn key_down_event(&mut self, _: KeyCode, _: KeyMods, _: bool) {
188 self.0.push(Heard::Pressed);
189 }
190
191 fn mouse_button_down_event(&mut self, _: MouseButton, _: f32, _: f32) {
192 self.0.push(Heard::Pressed);
193 }
194
195 fn window_minimized_event(&mut self) {
196 self.0.push(Heard::Minimized);
197 }
198
199 fn window_restored_event(&mut self) {
200 self.0.push(Heard::Restored);
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 fn code_for(key: Key) -> KeyCode {
211 match key {
212 Key::A => KeyCode::A,
213 Key::B => KeyCode::B,
214 Key::C => KeyCode::C,
215 Key::D => KeyCode::D,
216 Key::E => KeyCode::E,
217 Key::F => KeyCode::F,
218 Key::G => KeyCode::G,
219 Key::H => KeyCode::H,
220 Key::I => KeyCode::I,
221 Key::J => KeyCode::J,
222 Key::K => KeyCode::K,
223 Key::L => KeyCode::L,
224 Key::M => KeyCode::M,
225 Key::N => KeyCode::N,
226 Key::O => KeyCode::O,
227 Key::P => KeyCode::P,
228 Key::Q => KeyCode::Q,
229 Key::R => KeyCode::R,
230 Key::S => KeyCode::S,
231 Key::T => KeyCode::T,
232 Key::U => KeyCode::U,
233 Key::V => KeyCode::V,
234 Key::W => KeyCode::W,
235 Key::X => KeyCode::X,
236 Key::Y => KeyCode::Y,
237 Key::Z => KeyCode::Z,
238 Key::Digit0 => KeyCode::Key0,
239 Key::Digit1 => KeyCode::Key1,
240 Key::Digit2 => KeyCode::Key2,
241 Key::Digit3 => KeyCode::Key3,
242 Key::Digit4 => KeyCode::Key4,
243 Key::Digit5 => KeyCode::Key5,
244 Key::Digit6 => KeyCode::Key6,
245 Key::Digit7 => KeyCode::Key7,
246 Key::Digit8 => KeyCode::Key8,
247 Key::Digit9 => KeyCode::Key9,
248 Key::Semicolon => KeyCode::Semicolon,
249 Key::Up => KeyCode::Up,
250 Key::Down => KeyCode::Down,
251 Key::Left => KeyCode::Left,
252 Key::Right => KeyCode::Right,
253 Key::Enter => KeyCode::Enter,
254 Key::Escape => KeyCode::Escape,
255 Key::Space => KeyCode::Space,
256 Key::Tab => KeyCode::Tab,
257 Key::Backspace => KeyCode::Backspace,
258 Key::F1 => KeyCode::F1,
259 Key::F2 => KeyCode::F2,
260 Key::F3 => KeyCode::F3,
261 Key::F4 => KeyCode::F4,
262 Key::F5 => KeyCode::F5,
263 Key::F6 => KeyCode::F6,
264 Key::F7 => KeyCode::F7,
265 Key::F8 => KeyCode::F8,
266 Key::F9 => KeyCode::F9,
267 Key::F10 => KeyCode::F10,
268 Key::F11 => KeyCode::F11,
269 Key::F12 => KeyCode::F12,
270 Key::Comma => KeyCode::Comma,
271 Key::Period => KeyCode::Period,
272 Key::Slash => KeyCode::Slash,
273 Key::Apostrophe => KeyCode::Apostrophe,
274 Key::LeftBracket => KeyCode::LeftBracket,
275 Key::RightBracket => KeyCode::RightBracket,
276 Key::Backslash => KeyCode::Backslash,
277 Key::Minus => KeyCode::Minus,
278 Key::Equal => KeyCode::Equal,
279 Key::Backquote => KeyCode::GraveAccent,
280 Key::Insert => KeyCode::Insert,
281 Key::Delete => KeyCode::Delete,
282 Key::Home => KeyCode::Home,
283 Key::End => KeyCode::End,
284 Key::PageUp => KeyCode::PageUp,
285 Key::PageDown => KeyCode::PageDown,
286 Key::Kp0 => KeyCode::Kp0,
287 Key::Kp1 => KeyCode::Kp1,
288 Key::Kp2 => KeyCode::Kp2,
289 Key::Kp3 => KeyCode::Kp3,
290 Key::Kp4 => KeyCode::Kp4,
291 Key::Kp5 => KeyCode::Kp5,
292 Key::Kp6 => KeyCode::Kp6,
293 Key::Kp7 => KeyCode::Kp7,
294 Key::Kp8 => KeyCode::Kp8,
295 Key::Kp9 => KeyCode::Kp9,
296 Key::KpAdd => KeyCode::KpAdd,
297 Key::KpSubtract => KeyCode::KpSubtract,
298 Key::KpMultiply => KeyCode::KpMultiply,
299 Key::KpDivide => KeyCode::KpDivide,
300 Key::KpDecimal => KeyCode::KpDecimal,
301 }
302 }
303
304 #[test]
305 fn a_frames_key_events_come_in_the_order_they_happened() {
306 let none = |_: Key| false;
307 let down = |c: Chord| RawInputEvent::Down(c);
308 let up = RawInputEvent::Up;
309 let plain = Chord::plain;
310 assert_eq!(
312 ordered(&[Key::A], &[Key::B], |k| k == Key::B, false),
313 [up(Key::A), down(plain(Key::B))]
314 );
315 assert_eq!(
317 ordered(&[Key::Right], &[Key::Right], none, false),
318 [down(plain(Key::Right)), up(Key::Right)]
319 );
320 assert_eq!(
322 ordered(&[Key::Right], &[Key::Right], |k| k == Key::Right, false),
323 [up(Key::Right), down(plain(Key::Right))]
324 );
325 assert_eq!(
327 ordered(&[], &[Key::E, Key::F], none, true),
328 [down(Chord::shifted(Key::E)), down(Chord::shifted(Key::F))]
329 );
330 let found = ordered(&[], &[], none, false);
331 assert!(found.is_empty(), "{found:?}");
332 }
333
334 #[test]
335 fn every_key_is_mapped_from_a_key_code() {
336 for &key in Key::ALL {
337 assert_eq!(to_key(code_for(key)), Some(key), "{key}");
338 }
339 }
340
341 #[test]
342 fn every_game_key_has_a_key_code() {
343 let codes = [
344 KeyCode::A,
345 KeyCode::Z,
346 KeyCode::Key0,
347 KeyCode::Key9,
348 KeyCode::Semicolon,
349 KeyCode::Up,
350 KeyCode::Right,
351 KeyCode::Enter,
352 KeyCode::Escape,
353 KeyCode::Space,
354 KeyCode::Tab,
355 KeyCode::Backspace,
356 KeyCode::F1,
357 KeyCode::F12,
358 ];
359 let keys: Vec<String> = codes
360 .into_iter()
361 .filter_map(to_key)
362 .map(|k| k.to_string())
363 .collect();
364 assert_eq!(
365 keys,
366 [
367 "a",
368 "z",
369 "0",
370 "9",
371 ";",
372 "Up",
373 "Right",
374 "Enter",
375 "Escape",
376 "Space",
377 "Tab",
378 "Backspace",
379 "F1",
380 "F12"
381 ]
382 );
383 assert_eq!(to_key(KeyCode::KpEnter), Some(Key::Enter));
384 }
385
386 #[test]
387 fn other_key_codes_are_ignored() {
388 for code in [
389 KeyCode::LeftShift,
390 KeyCode::RightShift,
391 KeyCode::LeftControl,
392 KeyCode::LeftAlt,
393 KeyCode::LeftSuper,
394 KeyCode::CapsLock,
395 KeyCode::KpEqual,
396 KeyCode::F13,
397 ] {
398 assert_eq!(to_key(code), None);
399 }
400 }
401}