1pub use coord_2d::Coord;
2#[cfg(feature = "serialize")]
3use serde::{Deserialize, Serialize};
4
5#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
6#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
7pub enum ScrollDirection {
8 Up,
9 Down,
10 Left,
11 Right,
12}
13
14#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
15#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
16pub enum MouseButton {
17 Left,
18 Right,
19 Middle,
20}
21
22#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
23#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
24pub struct NotSupported;
25
26#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
27pub enum KeyboardInput {
28 Char(char),
29 Function(u8),
30 Up,
31 Down,
32 Left,
33 Right,
34 Home,
35 End,
36 PageUp,
37 PageDown,
38 Delete,
39}
40
41#[cfg(feature = "serialize")]
42mod key_names {
43 pub const UP: &str = "up";
44 pub const DOWN: &str = "down";
45 pub const LEFT: &str = "left";
46 pub const RIGHT: &str = "right";
47 pub const HOME: &str = "home";
48 pub const END: &str = "end";
49 pub const PAGE_UP: &str = "page-up";
50 pub const PAGE_DOWN: &str = "page-down";
51 pub const DELETE: &str = "delete";
52}
53
54#[cfg(feature = "serialize")]
55impl KeyboardInput {
56 fn try_from_str(s: &str) -> Option<Self> {
57 if s.chars().count() == 1 {
58 let c = s.chars().next().unwrap();
59 return Some(KeyboardInput::Char(c));
60 }
61 if s.starts_with('f') || s.starts_with('F') {
62 let (_, maybe_number_str) = s.split_at(1);
63 if let Ok(number) = maybe_number_str.parse::<u8>() {
64 return Some(KeyboardInput::Function(number));
65 }
66 }
67 use key_names::*;
68 match s {
69 UP => Some(KeyboardInput::Up),
70 DOWN => Some(KeyboardInput::Down),
71 LEFT => Some(KeyboardInput::Left),
72 RIGHT => Some(KeyboardInput::Right),
73 HOME => Some(KeyboardInput::Home),
74 END => Some(KeyboardInput::End),
75 PAGE_UP => Some(KeyboardInput::PageUp),
76 PAGE_DOWN => Some(KeyboardInput::PageDown),
77 DELETE => Some(KeyboardInput::Delete),
78 _ => None,
79 }
80 }
81}
82
83#[cfg(feature = "serialize")]
84impl serde::Serialize for KeyboardInput {
85 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
86 where
87 S: serde::Serializer,
88 {
89 use key_names::*;
90 use KeyboardInput::*;
91 match self {
92 Char(c) => serializer.serialize_char(*c),
93 Function(n) => serializer.serialize_str(&format!("f{}", n)),
94 Up => serializer.serialize_str(UP),
95 Down => serializer.serialize_str(DOWN),
96 Left => serializer.serialize_str(LEFT),
97 Right => serializer.serialize_str(RIGHT),
98 Home => serializer.serialize_str(HOME),
99 End => serializer.serialize_str(END),
100 PageUp => serializer.serialize_str(PAGE_UP),
101 PageDown => serializer.serialize_str(PAGE_DOWN),
102 Delete => serializer.serialize_str(DELETE),
103 }
104 }
105}
106
107#[cfg(feature = "serialize")]
108impl<'de> serde::Deserialize<'de> for KeyboardInput {
109 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
110 where
111 D: serde::Deserializer<'de>,
112 {
113 struct Visitor;
114 impl<'de> serde::de::Visitor<'de> for Visitor {
115 type Value = KeyboardInput;
116
117 fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
118 formatter.write_str("a keyboard input description")
119 }
120
121 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
122 where
123 E: serde::de::Error,
124 {
125 KeyboardInput::try_from_str(s)
126 .ok_or_else(|| E::custom(format!("couldn't parse {}", s)))
127 }
128 }
129 deserializer.deserialize_str(Visitor)
130 }
131}
132
133#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
134#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
135pub enum MouseInput {
136 MouseMove {
137 button: Option<MouseButton>,
138 coord: Coord,
139 },
140 MousePress {
141 button: MouseButton,
142 coord: Coord,
143 },
144 MouseRelease {
145 button: Result<MouseButton, NotSupported>,
147 coord: Coord,
148 },
149 MouseScroll {
150 direction: ScrollDirection,
151 coord: Coord,
152 },
153}
154
155impl MouseInput {
156 pub fn coord(&self) -> Coord {
157 match self {
158 Self::MouseMove { coord, .. }
159 | Self::MousePress { coord, .. }
160 | Self::MouseRelease { coord, .. }
161 | Self::MouseScroll { coord, .. } => *coord,
162 }
163 }
164
165 fn coord_mut(&mut self) -> &mut Coord {
166 match self {
167 Self::MouseMove { coord, .. }
168 | Self::MousePress { coord, .. }
169 | Self::MouseRelease { coord, .. }
170 | Self::MouseScroll { coord, .. } => coord,
171 }
172 }
173
174 pub fn relative_to_coord(&self, coord: Coord) -> Self {
175 let mut ret = *self;
176 *ret.coord_mut() -= coord;
177 ret
178 }
179}
180
181#[cfg(feature = "gamepad")]
182mod gamepad {
183 #[cfg(feature = "serialize")]
184 use serde::{Deserialize, Serialize};
185
186 #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
187 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
188 pub enum GamepadButton {
189 DPadUp,
190 DPadRight,
191 DPadDown,
192 DPadLeft,
193 North,
194 East,
195 South,
196 West,
197 Start,
198 Select,
199 LeftBumper,
200 RightBumper,
201 }
202
203 #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
204 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
205 pub struct GamepadInput {
206 pub button: GamepadButton,
207 pub id: u64,
208 }
209}
210
211#[cfg(feature = "gamepad")]
212pub use gamepad::{GamepadButton, GamepadInput};
213
214#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
216pub enum InputPolicy {
217 Up,
218 Down,
219 Left,
220 Right,
221 Select,
222}
223
224#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
226#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
227pub enum Input {
228 Keyboard(KeyboardInput),
229 Mouse(MouseInput),
230 #[cfg(feature = "gamepad")]
231 Gamepad(GamepadInput),
232}
233
234impl Input {
235 pub fn is_keyboard(&self) -> bool {
236 match self {
237 Input::Keyboard(_) => true,
238 Input::Mouse(_) => false,
239 #[cfg(feature = "gamepad")]
240 Input::Gamepad(_) => false,
241 }
242 }
243
244 pub fn is_mouse(&self) -> bool {
245 match self {
246 Input::Keyboard(_) => false,
247 Input::Mouse(_) => true,
248 #[cfg(feature = "gamepad")]
249 Input::Gamepad(_) => false,
250 }
251 }
252
253 #[cfg(feature = "gamepad")]
254 pub fn is_gamepad(&self) -> bool {
255 match self {
256 Input::Keyboard(_) => false,
257 Input::Mouse(_) => false,
258 Input::Gamepad(_) => true,
259 }
260 }
261
262 pub fn keyboard(self) -> Option<KeyboardInput> {
263 match self {
264 Input::Keyboard(keyboard_input) => Some(keyboard_input),
265 Input::Mouse(_) => None,
266 #[cfg(feature = "gamepad")]
267 Input::Gamepad(_) => None,
268 }
269 }
270
271 pub fn mouse(self) -> Option<MouseInput> {
272 match self {
273 Input::Keyboard(_) => None,
274 Input::Mouse(mouse_input) => Some(mouse_input),
275 #[cfg(feature = "gamepad")]
276 Input::Gamepad(_) => None,
277 }
278 }
279
280 #[cfg(feature = "gamepad")]
281 pub fn gamepad(self) -> Option<GamepadInput> {
282 match self {
283 Input::Keyboard(_) | Input::Mouse(_) => None,
284 Input::Gamepad(gamepad_input) => Some(gamepad_input),
285 }
286 }
287
288 pub fn policy(self) -> Option<InputPolicy> {
289 match self {
290 Input::Keyboard(KeyboardInput::Left) => Some(InputPolicy::Left),
291 Input::Keyboard(KeyboardInput::Right) => Some(InputPolicy::Right),
292 Input::Keyboard(keys::RETURN) => Some(InputPolicy::Select),
293 Input::Mouse(MouseInput::MousePress { .. }) => Some(InputPolicy::Select),
294 Input::Mouse(MouseInput::MouseScroll { direction, .. }) => Some(match direction {
295 ScrollDirection::Up => InputPolicy::Up,
296 ScrollDirection::Down => InputPolicy::Down,
297 ScrollDirection::Left => InputPolicy::Left,
298 ScrollDirection::Right => InputPolicy::Right,
299 }),
300 #[cfg(feature = "gamepad")]
301 Input::Gamepad(GamepadInput { button, .. }) => match button {
302 GamepadButton::DPadLeft => Some(InputPolicy::Left),
303 GamepadButton::DPadRight => Some(InputPolicy::Right),
304 GamepadButton::Start | GamepadButton::South => Some(InputPolicy::Select),
305 _ => None,
306 },
307 _ => None,
308 }
309 }
310}
311
312pub mod keys {
313 use super::KeyboardInput;
314
315 pub const ESCAPE: KeyboardInput = KeyboardInput::Char('\u{1b}');
316 pub const ETX: KeyboardInput = KeyboardInput::Char('\u{3}');
317 pub const BACKSPACE: KeyboardInput = KeyboardInput::Char('\u{7f}');
318 pub const TAB: KeyboardInput = KeyboardInput::Char('\u{9}');
319 pub const RETURN: KeyboardInput = KeyboardInput::Char('\u{d}');
320}
321
322#[cfg(feature = "serialize")]
323#[cfg(test)]
324mod serde_test {
325 #[test]
326 fn reversable() {
327 use super::KeyboardInput;
328 fn t(input: KeyboardInput) {
329 let s = serde_json::to_string(&input).unwrap();
330 assert_eq!(serde_json::from_str::<KeyboardInput>(&s).unwrap(), input);
331 }
332 t(KeyboardInput::Up);
333 t(KeyboardInput::Down);
334 t(KeyboardInput::Left);
335 t(KeyboardInput::Right);
336 t(KeyboardInput::Home);
337 t(KeyboardInput::End);
338 t(KeyboardInput::PageUp);
339 t(KeyboardInput::PageDown);
340 t(KeyboardInput::Delete);
341 t(KeyboardInput::Function(42));
342 t(KeyboardInput::Char('a'));
343 t(KeyboardInput::Char('☃'));
344 }
345
346 #[test]
347 fn example() {
348 use super::KeyboardInput;
349 use std::collections::BTreeMap;
350 let mut map = BTreeMap::new();
351 map.insert(KeyboardInput::Up, "UP");
352 map.insert(KeyboardInput::Down, "DOWN");
353 map.insert(KeyboardInput::Function(42), "F42");
354 map.insert(KeyboardInput::Char('a'), "A");
355 map.insert(KeyboardInput::Char('☃'), "SNOWMAN");
356 let pretty_json_string = serde_json::to_string_pretty(&map).unwrap();
357 assert_eq!(
358 pretty_json_string,
359 "{
360 \"a\": \"A\",
361 \"☃\": \"SNOWMAN\",
362 \"f42\": \"F42\",
363 \"up\": \"UP\",
364 \"down\": \"DOWN\"
365}",
366 );
367 }
368}