Skip to main content

goud_engine/core/input_manager/
manager.rs

1//! Core `InputManager` struct: construction, frame update, keyboard, mouse, and clear.
2
3#[cfg(feature = "native")]
4use glfw::{Key, MouseButton};
5use std::collections::{HashMap, HashSet, VecDeque};
6use std::time::{Duration, Instant};
7
8use crate::core::math::Vec2;
9
10use super::types::{BufferedInput, GamepadState, InputBinding};
11
12/// Input management resource for ECS integration.
13///
14/// Tracks keyboard, mouse, and gamepad input state across frames,
15/// enabling queries for current state, just pressed, and just released.
16/// Also supports action mapping for semantic input handling.
17#[derive(Debug, Clone)]
18pub struct InputManager {
19    // Current frame state
20    pub(super) keys_current: HashSet<Key>,
21    pub(super) mouse_buttons_current: HashSet<MouseButton>,
22    /// Deprecated — use `gamepads` for new code.
23    pub(super) gamepad_buttons_current: Vec<HashSet<u32>>,
24    pub(super) mouse_position: Vec2,
25    pub(super) mouse_delta: Vec2,
26
27    // Previous frame state (for just_pressed/just_released detection)
28    pub(super) keys_previous: HashSet<Key>,
29    pub(super) mouse_buttons_previous: HashSet<MouseButton>,
30    /// Deprecated — use `gamepads_previous` for new code.
31    pub(super) gamepad_buttons_previous: Vec<HashSet<u32>>,
32
33    // Gamepad state (current and previous)
34    pub(super) gamepads: Vec<GamepadState>,
35    pub(super) gamepads_previous: Vec<GamepadState>,
36
37    // Mouse scroll
38    pub(super) scroll_delta: Vec2,
39
40    // Action mappings (action_name -> list of bindings)
41    pub(super) action_mappings: HashMap<String, Vec<InputBinding>>,
42
43    // Input buffering for sequences and combos
44    pub(super) input_buffer: VecDeque<BufferedInput>,
45    pub(super) buffer_duration: Duration,
46    pub(super) last_update: Instant,
47
48    // Analog deadzone threshold (default 0.1)
49    pub(super) analog_deadzone: f32,
50}
51
52impl InputManager {
53    /// Creates a new InputManager with no inputs pressed.
54    ///
55    /// Default buffer duration is 200ms, suitable for most combo detection.
56    pub fn new() -> Self {
57        Self::with_buffer_duration(Duration::from_millis(200))
58    }
59
60    /// Creates a new InputManager with custom buffer duration.
61    ///
62    /// The buffer duration determines how long inputs are remembered for sequence detection.
63    /// - Short durations (50-100ms): Strict, requires fast input
64    /// - Medium durations (200-300ms): Balanced, works for most games
65    /// - Long durations (500ms+): Lenient, easier for casual players
66    pub fn with_buffer_duration(buffer_duration: Duration) -> Self {
67        Self {
68            keys_current: HashSet::new(),
69            mouse_buttons_current: HashSet::new(),
70            gamepad_buttons_current: vec![HashSet::new(); 4], // Deprecated, for backward compat
71            mouse_position: Vec2::zero(),
72            mouse_delta: Vec2::zero(),
73            keys_previous: HashSet::new(),
74            mouse_buttons_previous: HashSet::new(),
75            gamepad_buttons_previous: vec![HashSet::new(); 4], // Deprecated
76            gamepads: vec![GamepadState::new(); 4], // Support up to 4 gamepads by default
77            gamepads_previous: vec![GamepadState::new(); 4],
78            scroll_delta: Vec2::zero(),
79            action_mappings: HashMap::new(),
80            input_buffer: VecDeque::with_capacity(32),
81            buffer_duration,
82            last_update: Instant::now(),
83            analog_deadzone: 0.1, // 10% deadzone by default
84        }
85    }
86
87    /// Updates the input state for the next frame.
88    ///
89    /// This should be called at the start of each frame, before any input queries.
90    /// It copies current state to previous state and resets deltas.
91    pub fn update(&mut self) {
92        let now = Instant::now();
93
94        // Copy current to previous
95        self.keys_previous = self.keys_current.clone();
96        self.mouse_buttons_previous = self.mouse_buttons_current.clone();
97        self.gamepad_buttons_previous = self.gamepad_buttons_current.clone();
98        self.gamepads_previous = self.gamepads.clone();
99
100        // Reset deltas
101        self.mouse_delta = Vec2::zero();
102        self.scroll_delta = Vec2::zero();
103
104        // Clean up expired inputs from buffer
105        self.input_buffer
106            .retain(|input| !input.is_expired(now, self.buffer_duration));
107
108        self.last_update = now;
109    }
110
111    // === Keyboard Input ===
112
113    /// Sets a key as pressed.
114    pub fn press_key(&mut self, key: Key) {
115        // Only buffer if this is a new press (not already held)
116        if !self.keys_current.contains(&key) {
117            self.buffer_input(InputBinding::Key(key));
118        }
119        self.keys_current.insert(key);
120    }
121
122    /// Sets a key as released.
123    pub fn release_key(&mut self, key: Key) {
124        self.keys_current.remove(&key);
125    }
126
127    /// Returns true if the key is currently pressed.
128    pub fn key_pressed(&self, key: Key) -> bool {
129        self.keys_current.contains(&key)
130    }
131
132    /// Returns true if the key was just pressed this frame.
133    ///
134    /// True only on the first frame the key is pressed.
135    pub fn key_just_pressed(&self, key: Key) -> bool {
136        self.keys_current.contains(&key) && !self.keys_previous.contains(&key)
137    }
138
139    /// Returns true if the key was just released this frame.
140    ///
141    /// True only on the first frame the key is released.
142    pub fn key_just_released(&self, key: Key) -> bool {
143        !self.keys_current.contains(&key) && self.keys_previous.contains(&key)
144    }
145
146    /// Returns an iterator over all currently pressed keys.
147    pub fn keys_pressed(&self) -> impl Iterator<Item = &Key> {
148        self.keys_current.iter()
149    }
150
151    // === Mouse Input ===
152
153    /// Sets a mouse button as pressed.
154    pub fn press_mouse_button(&mut self, button: MouseButton) {
155        // Only buffer if this is a new press
156        if !self.mouse_buttons_current.contains(&button) {
157            self.buffer_input(InputBinding::MouseButton(button));
158        }
159        self.mouse_buttons_current.insert(button);
160    }
161
162    /// Sets a mouse button as released.
163    pub fn release_mouse_button(&mut self, button: MouseButton) {
164        self.mouse_buttons_current.remove(&button);
165    }
166
167    /// Returns true if the mouse button is currently pressed.
168    pub fn mouse_button_pressed(&self, button: MouseButton) -> bool {
169        self.mouse_buttons_current.contains(&button)
170    }
171
172    /// Returns true if the mouse button was just pressed this frame.
173    pub fn mouse_button_just_pressed(&self, button: MouseButton) -> bool {
174        self.mouse_buttons_current.contains(&button)
175            && !self.mouse_buttons_previous.contains(&button)
176    }
177
178    /// Returns true if the mouse button was just released this frame.
179    pub fn mouse_button_just_released(&self, button: MouseButton) -> bool {
180        !self.mouse_buttons_current.contains(&button)
181            && self.mouse_buttons_previous.contains(&button)
182    }
183
184    /// Returns an iterator over all currently pressed mouse buttons.
185    pub fn mouse_buttons_pressed(&self) -> impl Iterator<Item = &MouseButton> {
186        self.mouse_buttons_current.iter()
187    }
188
189    // === Mouse Position ===
190
191    /// Updates the mouse position.
192    pub fn set_mouse_position(&mut self, position: Vec2) {
193        self.mouse_delta = position - self.mouse_position;
194        self.mouse_position = position;
195    }
196
197    /// Returns the current mouse position in screen coordinates.
198    pub fn mouse_position(&self) -> Vec2 {
199        self.mouse_position
200    }
201
202    /// Returns the mouse movement delta since last frame.
203    pub fn mouse_delta(&self) -> Vec2 {
204        self.mouse_delta
205    }
206
207    // === Mouse Scroll ===
208
209    /// Updates the mouse scroll delta.
210    pub fn add_scroll_delta(&mut self, delta: Vec2) {
211        self.scroll_delta = self.scroll_delta + delta;
212    }
213
214    /// Returns the mouse scroll delta for this frame.
215    pub fn scroll_delta(&self) -> Vec2 {
216        self.scroll_delta
217    }
218
219    /// Clears all input state (useful for focus loss or pausing).
220    pub fn clear(&mut self) {
221        self.keys_current.clear();
222        self.keys_previous.clear();
223        self.mouse_buttons_current.clear();
224        self.mouse_buttons_previous.clear();
225        for buttons in &mut self.gamepad_buttons_current {
226            buttons.clear();
227        }
228        for buttons in &mut self.gamepad_buttons_previous {
229            buttons.clear();
230        }
231        // Clear new gamepad state (buttons and axes)
232        for gamepad in &mut self.gamepads {
233            gamepad.buttons.clear();
234            gamepad.axes.clear();
235            // Don't clear connection status or vibration
236        }
237        for gamepad in &mut self.gamepads_previous {
238            gamepad.buttons.clear();
239            gamepad.axes.clear();
240        }
241        self.mouse_delta = Vec2::zero();
242        self.scroll_delta = Vec2::zero();
243    }
244
245    /// Adds an input to the buffer for sequence detection.
246    pub(super) fn buffer_input(&mut self, binding: InputBinding) {
247        let now = Instant::now();
248        self.input_buffer
249            .push_back(BufferedInput::new(binding, now));
250
251        // Keep buffer size reasonable (prevent memory growth from rapid inputs)
252        while self.input_buffer.len() > 32 {
253            self.input_buffer.pop_front();
254        }
255    }
256}
257
258impl Default for InputManager {
259    fn default() -> Self {
260        Self::new()
261    }
262}