1use rustc_hash::FxHashMap;
2use winit::{
3 dpi::PhysicalPosition,
4 event::{ElementState, Modifiers, MouseButton, MouseScrollDelta, WindowEvent},
5 keyboard::{Key, KeyCode, ModifiersKeyState, NamedKey, PhysicalKey},
6};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
10pub struct KeyMods {
11 pub lshift: bool,
13 pub rshift: bool,
15 pub lalt: bool,
17 pub ralt: bool,
19 pub lcontrol: bool,
21 pub rcontrol: bool,
23 pub lsuper: bool,
25 pub rsuper: bool,
27}
28
29impl KeyMods {
30 fn update(&mut self, mods: &Modifiers) {
31 self.lshift = mods.lshift_state() == ModifiersKeyState::Pressed;
32 self.rshift = mods.rshift_state() == ModifiersKeyState::Pressed;
33 self.lalt = mods.lalt_state() == ModifiersKeyState::Pressed;
34 self.ralt = mods.ralt_state() == ModifiersKeyState::Pressed;
35 self.lcontrol = mods.lcontrol_state() == ModifiersKeyState::Pressed;
36 self.rcontrol = mods.rcontrol_state() == ModifiersKeyState::Pressed;
37 self.lsuper = mods.lsuper_state() == ModifiersKeyState::Pressed;
38 self.rsuper = mods.rsuper_state() == ModifiersKeyState::Pressed;
39 }
40}
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
44pub enum InputState {
45 Pressed,
47 Down,
49 Released,
53}
54
55impl InputState {
56 #[inline]
58 pub fn is_pressed(&self) -> bool {
59 matches!(self, InputState::Pressed)
60 }
61
62 #[inline]
64 pub fn is_any_down(&self) -> bool {
65 matches!(self, InputState::Pressed | InputState::Down)
66 }
67
68 #[inline]
70 pub fn is_released(&self) -> bool {
71 matches!(self, InputState::Released)
72 }
73}
74
75impl From<ElementState> for InputState {
76 #[inline]
77 fn from(value: ElementState) -> Self {
78 match value {
79 ElementState::Pressed => InputState::Pressed,
80 ElementState::Released => InputState::Released,
81 }
82 }
83}
84
85#[derive(Debug)]
87pub struct Input {
88 mods: KeyMods,
89 physical_keys: FxHashMap<KeyCode, InputState>,
90 logical_keys: FxHashMap<NamedKey, InputState>,
91 mouse_buttons: FxHashMap<MouseButton, InputState>,
92 cursor_pos: PhysicalPosition<f64>,
93 mouse_scroll: MouseScrollDelta,
94}
95
96impl Input {
97 #[inline]
98 pub(crate) fn new() -> Self {
99 Self {
100 mods: KeyMods::default(),
101 physical_keys: FxHashMap::default(),
102 logical_keys: FxHashMap::default(),
103 mouse_buttons: FxHashMap::default(),
104 cursor_pos: PhysicalPosition::new(0., 0.),
105 mouse_scroll: MouseScrollDelta::LineDelta(0., 0.),
106 }
107 }
108
109 #[inline]
111 pub fn cursor_pos(&self) -> PhysicalPosition<f64> {
112 self.cursor_pos
113 }
114
115 #[inline]
117 pub fn mouse_scroll(&self) -> MouseScrollDelta {
118 self.mouse_scroll
119 }
120
121 #[inline]
123 pub fn key_mods(&self) -> KeyMods {
124 self.mods
125 }
126
127 #[inline]
129 pub fn physical_keys(&self) -> &FxHashMap<KeyCode, InputState> {
130 &self.physical_keys
131 }
132
133 #[inline]
135 pub fn is_physical_key_pressed(&self, scancode: KeyCode) -> bool {
136 self.physical_keys
137 .get(&scancode)
138 .map_or(false, InputState::is_pressed)
139 }
140
141 #[inline]
143 pub fn is_physical_key_down(&self, scancode: KeyCode) -> bool {
144 self.physical_keys
145 .get(&scancode)
146 .map_or(false, InputState::is_any_down)
147 }
148
149 #[inline]
151 pub fn is_physical_key_released(&self, scancode: KeyCode) -> bool {
152 self.physical_keys
153 .get(&scancode)
154 .map_or(false, InputState::is_released)
155 }
156
157 #[inline]
159 pub fn logical_keys(&self) -> &FxHashMap<NamedKey, InputState> {
160 &self.logical_keys
161 }
162
163 #[inline]
165 pub fn is_logical_key_pressed(&self, key: NamedKey) -> bool {
166 self.logical_keys
167 .get(&key)
168 .map_or(false, InputState::is_pressed)
169 }
170
171 #[inline]
173 pub fn is_logical_key_down(&self, key: NamedKey) -> bool {
174 self.logical_keys
175 .get(&key)
176 .map_or(false, InputState::is_any_down)
177 }
178
179 #[inline]
181 pub fn is_logical_key_released(&self, key: NamedKey) -> bool {
182 self.logical_keys
183 .get(&key)
184 .map_or(false, InputState::is_released)
185 }
186
187 #[inline]
189 pub fn mouse_buttons(&self) -> &FxHashMap<MouseButton, InputState> {
190 &self.mouse_buttons
191 }
192
193 #[inline]
195 pub fn is_mouse_button_pressed(&self, button: MouseButton) -> bool {
196 self.mouse_buttons
197 .get(&button)
198 .map_or(false, InputState::is_pressed)
199 }
200
201 #[inline]
203 pub fn is_mouse_button_down(&self, button: MouseButton) -> bool {
204 self.mouse_buttons
205 .get(&button)
206 .map_or(false, InputState::is_any_down)
207 }
208
209 #[inline]
211 pub fn is_mouse_button_released(&self, button: MouseButton) -> bool {
212 self.mouse_buttons
213 .get(&button)
214 .map_or(false, InputState::is_released)
215 }
216
217 pub(crate) fn update_keys(&mut self) {
218 self.physical_keys.retain(|_, state| match state {
219 InputState::Pressed => {
220 *state = InputState::Down;
221 true
222 }
223 InputState::Down => true,
224 InputState::Released => false,
225 });
226
227 self.logical_keys.retain(|_, state| match state {
228 InputState::Pressed => {
229 *state = InputState::Down;
230 true
231 }
232 InputState::Down => true,
233 InputState::Released => false,
234 });
235
236 self.mouse_buttons.retain(|_, state| match state {
237 InputState::Pressed => {
238 *state = InputState::Down;
239 true
240 }
241 InputState::Down => true,
242 InputState::Released => false,
243 });
244
245 self.mouse_scroll = MouseScrollDelta::LineDelta(0., 0.);
246 }
247
248 pub(crate) fn process_event(&mut self, event: &WindowEvent) {
249 match event {
250 WindowEvent::KeyboardInput {
251 device_id: _,
252 event,
253 is_synthetic: false,
254 } if !event.repeat => {
255 if let PhysicalKey::Code(key_code) = event.physical_key {
256 self.physical_keys.insert(key_code, event.state.into());
257 }
258
259 if let Key::Named(key) = event.logical_key {
260 self.logical_keys.insert(key, event.state.into());
261 }
262 }
263 WindowEvent::ModifiersChanged(mods) => {
264 self.mods.update(mods);
265 }
266 WindowEvent::CursorMoved {
267 device_id: _,
268 position,
269 ..
270 } => {
271 self.cursor_pos = *position;
272 }
273 WindowEvent::MouseWheel {
274 device_id: _,
275 delta,
276 phase: _,
277 } => {
278 self.mouse_scroll = *delta;
279 }
280 WindowEvent::MouseInput {
281 device_id: _,
282 state,
283 button,
284 ..
285 } => {
286 self.mouse_buttons.insert(*button, (*state).into());
287 }
288 _ => {}
289 }
290 }
291}