arcane_engine/platform/
input.rs1use std::collections::HashSet;
2
3#[derive(Debug, Default)]
5pub struct InputState {
6 pub keys_down: HashSet<String>,
8 pub keys_pressed: HashSet<String>,
10 pub keys_released: HashSet<String>,
12 pub mouse_x: f32,
14 pub mouse_y: f32,
15 pub mouse_buttons: HashSet<u8>,
17}
18
19impl InputState {
20 pub fn begin_frame(&mut self) {
22 self.keys_pressed.clear();
23 self.keys_released.clear();
24 }
25
26 pub fn key_down(&mut self, key: &str) {
28 if self.keys_down.insert(key.to_string()) {
29 self.keys_pressed.insert(key.to_string());
30 }
31 }
32
33 pub fn key_up(&mut self, key: &str) {
35 if self.keys_down.remove(key) {
36 self.keys_released.insert(key.to_string());
37 }
38 }
39
40 pub fn mouse_move(&mut self, x: f32, y: f32) {
42 self.mouse_x = x;
43 self.mouse_y = y;
44 }
45
46 pub fn is_key_down(&self, key: &str) -> bool {
48 self.keys_down.contains(key)
49 }
50
51 pub fn is_key_pressed(&self, key: &str) -> bool {
53 self.keys_pressed.contains(key)
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
62 fn key_pressed_survives_until_read() {
63 let mut input = InputState::default();
68
69 input.key_down("ArrowUp");
71 assert!(input.is_key_pressed("ArrowUp"));
72 assert!(input.is_key_down("ArrowUp"));
73
74 assert!(input.is_key_pressed("ArrowUp"));
76
77 input.begin_frame();
79 assert!(!input.is_key_pressed("ArrowUp"));
80 assert!(input.is_key_down("ArrowUp")); }
82
83 #[test]
84 fn begin_frame_before_read_loses_input() {
85 let mut input = InputState::default();
88
89 input.key_down("w");
90 assert!(input.is_key_pressed("w"));
91
92 input.begin_frame();
94 assert!(!input.is_key_pressed("w")); }
96
97 #[test]
98 fn held_key_does_not_re_trigger_pressed() {
99 let mut input = InputState::default();
100
101 input.key_down("a");
102 assert!(input.is_key_pressed("a"));
103
104 input.begin_frame();
105
106 input.key_down("a");
108 assert!(!input.is_key_pressed("a"));
109 assert!(input.is_key_down("a"));
110 }
111
112 #[test]
113 fn key_release_tracked() {
114 let mut input = InputState::default();
115
116 input.key_down("Space");
117 input.begin_frame();
118 input.key_up("Space");
119
120 assert!(!input.is_key_down("Space"));
121 assert!(input.keys_released.contains("Space"));
122
123 input.begin_frame();
124 assert!(!input.keys_released.contains("Space"));
125 }
126
127 #[test]
128 fn mouse_position_is_tracked() {
129 let mut input = InputState::default();
130 assert_eq!(input.mouse_x, 0.0);
131 assert_eq!(input.mouse_y, 0.0);
132
133 input.mouse_x = 100.5;
134 input.mouse_y = 200.75;
135
136 assert_eq!(input.mouse_x, 100.5);
137 assert_eq!(input.mouse_y, 200.75);
138 }
139
140 #[test]
141 fn multiple_keys_can_be_down_simultaneously() {
142 let mut input = InputState::default();
143
144 input.key_down("w");
145 input.key_down("a");
146 input.key_down("d");
147
148 assert!(input.is_key_down("w"));
149 assert!(input.is_key_down("a"));
150 assert!(input.is_key_down("d"));
151 assert!(input.is_key_pressed("w"));
152 assert!(input.is_key_pressed("a"));
153 assert!(input.is_key_pressed("d"));
154 }
155
156 #[test]
157 fn releasing_one_key_does_not_affect_others() {
158 let mut input = InputState::default();
159
160 input.key_down("w");
161 input.key_down("a");
162 input.begin_frame();
163
164 input.key_up("w");
165
166 assert!(!input.is_key_down("w"));
167 assert!(input.is_key_down("a"));
168 }
169
170 #[test]
171 fn key_pressed_works_with_special_keys() {
172 let mut input = InputState::default();
173
174 input.key_down("Escape");
175 input.key_down("Return");
176 input.key_down("ArrowLeft");
177
178 assert!(input.is_key_pressed("Escape"));
179 assert!(input.is_key_pressed("Return"));
180 assert!(input.is_key_pressed("ArrowLeft"));
181 }
182
183 #[test]
184 fn default_input_state_has_no_keys_down() {
185 let input = InputState::default();
186
187 assert!(!input.is_key_down("a"));
188 assert!(!input.is_key_down("Space"));
189 assert!(!input.is_key_pressed("w"));
190 assert_eq!(input.keys_down.len(), 0);
191 assert_eq!(input.keys_pressed.len(), 0);
192 }
193}