win_loop/
input.rs

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/// Keyboard modifiers.
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
10pub struct KeyMods {
11    /// Left "shift" key.
12    pub lshift: bool,
13    /// Right "shift" key.
14    pub rshift: bool,
15    /// Left "alt" key.
16    pub lalt: bool,
17    /// Right "alt" key.
18    pub ralt: bool,
19    /// Left "control" key.
20    pub lcontrol: bool,
21    /// Right "control" key.
22    pub rcontrol: bool,
23    /// Left "super" key. This is the "windows" key on PC and "command" key on Mac.
24    pub lsuper: bool,
25    /// Right "super" key. This is the "windows" key on PC and "command" key on Mac.
26    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/// Input state of a mouse button/keyboard key.
43#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
44pub enum InputState {
45    /// The button has just been pressed.
46    Pressed,
47    /// The button is being held down.
48    Down,
49    /// The button has just been released.
50    ///
51    /// Note that it means that the key has **just** been released, **not** that it isn't held.
52    Released,
53}
54
55impl InputState {
56    /// The state is [`InputState::Pressed`].
57    #[inline]
58    pub fn is_pressed(&self) -> bool {
59        matches!(self, InputState::Pressed)
60    }
61
62    /// The state is [`InputState::Pressed`] or [`InputState::Down`].
63    #[inline]
64    pub fn is_any_down(&self) -> bool {
65        matches!(self, InputState::Pressed | InputState::Down)
66    }
67
68    /// The state is [`InputState::Released`].
69    #[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/// Input handler.
86#[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    /// Cursor position (from [`WindowEvent::CursorMoved`](https://docs.rs/winit/latest/winit/event/enum.WindowEvent.html#variant.CursorMoved)).
110    #[inline]
111    pub fn cursor_pos(&self) -> PhysicalPosition<f64> {
112        self.cursor_pos
113    }
114
115    /// Mouse scroll value.
116    #[inline]
117    pub fn mouse_scroll(&self) -> MouseScrollDelta {
118        self.mouse_scroll
119    }
120
121    /// Get current keyboard modifiers.
122    #[inline]
123    pub fn key_mods(&self) -> KeyMods {
124        self.mods
125    }
126
127    /// All input states of physical keys.
128    #[inline]
129    pub fn physical_keys(&self) -> &FxHashMap<KeyCode, InputState> {
130        &self.physical_keys
131    }
132
133    /// Returns `true` if a physical key has just been pressed.
134    #[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    /// Returns `true` if a physical key is down.
142    #[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    /// Returns `true` if a physical key has just been released.
150    #[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    /// All input states of logical keys.
158    #[inline]
159    pub fn logical_keys(&self) -> &FxHashMap<NamedKey, InputState> {
160        &self.logical_keys
161    }
162
163    /// Returns `true` if a logical key has just been pressed.
164    #[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    /// Returns `true` if a logical key is down.
172    #[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    /// Returns `true` if a logical key has just been released.
180    #[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    /// All input states of mouse buttons.
188    #[inline]
189    pub fn mouse_buttons(&self) -> &FxHashMap<MouseButton, InputState> {
190        &self.mouse_buttons
191    }
192
193    /// Returns `true` if a mouse button has just been pressed.
194    #[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    /// Returns `true` if a mouse button is down.
202    #[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    /// Returns `true` if a mouse button has just been released.
210    #[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}