Ember
A C++ 20 'game engine' built with SDL3 with wide platform support.
input_manager.h
Go to the documentation of this file.
1 #pragma once
2 #include "core/engine_structs.h"
3 
4 enum class InputState {
5  RELEASED = 0,
6  PRESSED = 1,
7  HELD = 2,
8  JUST_RELEASED = 3
9 };
10 
11 enum class MouseButton {
12  LEFT = SDL_BUTTON_LEFT,
13  MIDDLE = SDL_BUTTON_MIDDLE,
14  RIGHT = SDL_BUTTON_RIGHT,
15  BUTTON_X1 = SDL_BUTTON_X1,
16  BUTTON_X2 = SDL_BUTTON_X2
17 };
18 
19 struct InputAction {
20  std::string name;
21  std::vector<SDL_Scancode> keys;
22  std::vector<MouseButton> mouse_buttons;
23  std::vector<int> gamepad_buttons;
24 
25  InputAction() = default;
26 
27  explicit InputAction(const std::string& action_name) : name(action_name) {}
28 
29  InputAction& bind_key(SDL_Scancode key) {
30  keys.push_back(key);
31  return *this;
32  }
33 
35  mouse_buttons.push_back(button);
36  return *this;
37  }
38 
40  gamepad_buttons.push_back(button);
41  return *this;
42  }
43 };
44 
45 struct GamepadInfo {
46  SDL_Joystick* joystick = nullptr;
47  SDL_Gamepad* controller = nullptr;
48  SDL_JoystickID instance_id;
49  std::string name;
50  bool is_controller = false;
51 
52  // Button states (current and previous frame)
53  std::unordered_map<int, InputState> button_states;
54  std::unordered_map<int, bool> prev_button_states;
55 
56  // Axis values (-1.0 to 1.0)
57  std::vector<float> axis_values;
58 
59  // Deadzone for analog sticks
60  float deadzone = 0.15f;
61 
62  GamepadInfo() = default;
63  ~GamepadInfo();
64 
65  bool is_connected() const { return joystick != nullptr; }
66  float get_axis_value(int axis) const;
67  glm::vec2 get_stick_value(int stick_index) const; // 0 = left stick, 1 = right stick
68 };
69 
70 struct TouchPoint {
71  SDL_FingerID finger_id;
72  bool active = false;
73  glm::vec2 position{0.0f};
74  glm::vec2 pressure{0.0f}; // x = pressure, y = unused
75  glm::vec2 start_position{0.0f};
76  float start_time = 0.0f;
77 
78  TouchPoint() = default;
79  TouchPoint(SDL_FingerID id, glm::vec2 pos, float press = 1.0f)
80  : finger_id(id), active(true), position(pos), pressure(press, 0.0f), start_position(pos) {}
81 };
82 
84 public:
85  void start_input(SDL_Window* window);
86  void stop_input(SDL_Window* window);
87  void process_event(const SDL_Event& event);
88  void clear_text();
89 
90  [[nodiscard]] bool is_active() const { return _active; }
91  [[nodiscard]] const std::string& get_text() const { return _text; }
92  [[nodiscard]] const std::string& get_composition() const { return _composition; }
93  [[nodiscard]] int get_cursor_position() const { return _cursor_pos; }
94 
95  // Text manipulation
96  void set_text(const std::string& text);
97  void insert_text(const std::string& text);
98  void delete_char_at_cursor();
99  void move_cursor(int offset);
100 
101 private:
102  bool _active = false;
103  std::string _text;
104  std::string _composition;
105  int _cursor_pos = 0;
106  int _composition_start = 0;
107  int _composition_length = 0;
108 };
109 
110 /* !
111  * @brief InputManager class
112  *
113  * @details Manages keyboard, mouse, gamepad, and touch input.
114  *
115  * @version 1.0.0
116  */
118 public:
119  InputManager() = default;
120  explicit InputManager(SDL_Window* window);
121  ~InputManager();
122 
123  // Core update functions
124  void process_event(const SDL_Event& event);
125  void update(); // Call once per frame after processing all events
126  void late_update(); // Call at end of frame to transition states
127 
128  // === KEYBOARD INPUT ===
129  [[nodiscard]] bool is_key_pressed(SDL_Scancode key) const;
130  [[nodiscard]] bool is_key_held(SDL_Scancode key) const;
131  [[nodiscard]] bool is_key_released(SDL_Scancode key) const;
132  [[nodiscard]] InputState get_key_state(SDL_Scancode key) const;
133 
134  // Multiple key combinations
135  [[nodiscard]] bool are_keys_held(const std::vector<SDL_Scancode>& keys) const;
136  [[nodiscard]] bool any_key_pressed(const std::vector<SDL_Scancode>& keys) const;
137 
138  // === MOUSE INPUT ===
139  [[nodiscard]] glm::vec2 get_mouse_position() const { return _mouse_position; }
140  [[nodiscard]] glm::vec2 get_mouse_delta() const { return _mouse_delta; }
141  [[nodiscard]] glm::vec2 get_mouse_wheel() const { return _mouse_wheel; }
142 
143  [[nodiscard]] bool is_mouse_button_pressed(MouseButton button) const;
144  [[nodiscard]] bool is_mouse_button_held(MouseButton button) const;
145  [[nodiscard]] bool is_mouse_button_released(MouseButton button) const;
146  [[nodiscard]] InputState get_mouse_button_state(MouseButton button) const;
147 
148  // Mouse utilities
149  void set_mouse_position(int x, int y);
150  void set_relative_mouse_mode(bool enabled);
151  [[nodiscard]] bool is_relative_mouse_mode() const;
152  glm::vec2 screen_to_world(const glm::vec2& screen_pos) const;
153  glm::vec2 world_to_screen(const glm::vec2& world_pos) const;
154 
155 
156  // === GAMEPAD INPUT ===
157  [[nodiscard]] size_t get_gamepad_count() const { return _gamepads.size(); }
158  [[nodiscard]] const GamepadInfo* get_gamepad(int index) const;
159  [[nodiscard]] bool is_gamepad_connected(int index) const;
160 
161  [[nodiscard]] bool is_gamepad_button_pressed(int gamepad_index, int button) const;
162  [[nodiscard]] bool is_gamepad_button_held(int gamepad_index, int button) const;
163  [[nodiscard]] bool is_gamepad_button_released(int gamepad_index, int button) const;
164 
165  [[nodiscard]] float get_gamepad_axis(int gamepad_index, int axis) const;
166  [[nodiscard]] glm::vec2 get_gamepad_stick(int gamepad_index, int stick_index) const;
167 
168  void set_gamepad_deadzone(int gamepad_index, float deadzone);
169 
170  // === TOUCH INPUT ===
171  [[nodiscard]] const std::unordered_map<SDL_FingerID, TouchPoint>& get_touch_points() const { return _touch_points; }
172  [[nodiscard]] bool is_touch_active(SDL_FingerID finger_id) const;
173  [[nodiscard]] glm::vec2 get_touch_position(SDL_FingerID finger_id) const;
174  [[nodiscard]] size_t get_active_touch_count() const;
175 
176  // === TEXT INPUT ===
177  TextInputManager& get_text_input() { return _text_input; }
178  void start_text_input() { _text_input.start_input(_window); }
179  void stop_text_input() { _text_input.stop_input(_window); }
180 
181  // === INPUT ACTIONS ===
182  void register_action(const InputAction& action);
183  void unregister_action(const std::string& action_name);
184  [[nodiscard]] bool is_action_pressed(const std::string& action_name) const;
185  [[nodiscard]] bool is_action_held(const std::string& action_name) const;
186  [[nodiscard]] bool is_action_released(const std::string& action_name) const;
187 
188  // === UTILITIES ===
189  [[nodiscard]] bool position_in_rect(glm::vec2 position, const Rect2& rect) const;
190  [[nodiscard]] bool mouse_in_rect(const Rect2& rect) const;
191 
192  // Input blocking (useful for UI)
193  void set_input_blocked(bool blocked) { _input_blocked = blocked; }
194  [[nodiscard]] bool is_input_blocked() const { return _input_blocked; }
195 
196  // Debug
197  void print_debug_info() const;
198  [[nodiscard]] std::vector<SDL_Scancode> get_pressed_keys() const;
199 
200  // Configuration
201  void set_mouse_sensitivity(float sensitivity) { _mouse_sensitivity = sensitivity; }
202  [[nodiscard]] float get_mouse_sensitivity() const { return _mouse_sensitivity; }
203 
204 private:
205  // Internal helper functions
206  void update_key_states();
207  void update_mouse_states();
208  void update_gamepad_states();
209  void handle_gamepad_connection(const SDL_Event& event);
210  void handle_gamepad_disconnection(const SDL_Event& event);
211  InputState calculate_input_state(bool current, bool previous) const;
212 
213  // Core data
214  SDL_Window* _window = nullptr;
215  bool _input_blocked = false;
216  float _mouse_sensitivity = 1.0f;
217 
218  // Keyboard state
219  std::unordered_map<SDL_Scancode, InputState> _key_states;
220  std::unordered_map<SDL_Scancode, bool> _prev_key_states;
221  std::unordered_set<SDL_Scancode> _keys_this_frame;
222 
223  // Mouse state
224  glm::vec2 _mouse_position{0.0f};
225  glm::vec2 _prev_mouse_position{0.0f};
226  glm::vec2 _mouse_delta{0.0f};
227  glm::vec2 _mouse_wheel{0.0f};
228  std::unordered_map<MouseButton, InputState> _mouse_button_states;
229  std::unordered_map<MouseButton, bool> _prev_mouse_button_states;
230 
231  // Gamepad state
232  std::unordered_map<int, std::unique_ptr<GamepadInfo>> _gamepads;
233  int _next_gamepad_index = 0;
234 
235  // Touch state
236  std::unordered_map<SDL_FingerID, TouchPoint> _touch_points;
237 
238  // Text input
239  TextInputManager _text_input;
240 
241  // Input actions
242  std::unordered_map<std::string, InputAction> _input_actions;
243 };
Definition: input_manager.h:117
void start_text_input()
Definition: input_manager.h:178
void update()
Definition: input_manager.cpp:229
TextInputManager & get_text_input()
Definition: input_manager.h:177
bool is_key_held(SDL_Scancode key) const
Definition: input_manager.cpp:277
bool is_mouse_button_released(MouseButton button) const
Definition: input_manager.cpp:336
std::vector< SDL_Scancode > get_pressed_keys() const
Definition: input_manager.cpp:732
bool any_key_pressed(const std::vector< SDL_Scancode > &keys) const
Definition: input_manager.cpp:310
size_t get_gamepad_count() const
Definition: input_manager.h:157
size_t get_active_touch_count() const
Definition: input_manager.cpp:465
bool is_relative_mouse_mode() const
Definition: input_manager.cpp:363
bool is_touch_active(SDL_FingerID finger_id) const
Definition: input_manager.cpp:455
glm::vec2 get_mouse_position() const
Definition: input_manager.h:139
void print_debug_info() const
Definition: input_manager.cpp:717
bool is_gamepad_button_held(int gamepad_index, int button) const
Definition: input_manager.cpp:411
InputState get_key_state(SDL_Scancode key) const
Definition: input_manager.cpp:293
void register_action(const InputAction &action)
Definition: input_manager.cpp:476
glm::vec2 get_mouse_delta() const
Definition: input_manager.h:140
bool is_action_pressed(const std::string &action_name) const
Definition: input_manager.cpp:485
void unregister_action(const std::string &action_name)
Definition: input_manager.cpp:481
bool is_gamepad_button_released(int gamepad_index, int button) const
Definition: input_manager.cpp:424
bool mouse_in_rect(const Rect2 &rect) const
Definition: input_manager.cpp:713
const std::unordered_map< SDL_FingerID, TouchPoint > & get_touch_points() const
Definition: input_manager.h:171
glm::vec2 get_touch_position(SDL_FingerID finger_id) const
Definition: input_manager.cpp:460
bool position_in_rect(glm::vec2 position, const Rect2 &rect) const
Definition: input_manager.cpp:709
bool is_key_released(SDL_Scancode key) const
Definition: input_manager.cpp:285
void late_update()
Definition: input_manager.cpp:245
void set_input_blocked(bool blocked)
Definition: input_manager.h:193
glm::vec2 get_gamepad_stick(int gamepad_index, int stick_index) const
Definition: input_manager.cpp:442
const GamepadInfo * get_gamepad(int index) const
Definition: input_manager.cpp:388
float get_gamepad_axis(int gamepad_index, int axis) const
Definition: input_manager.cpp:437
void stop_text_input()
Definition: input_manager.h:179
void set_mouse_position(int x, int y)
Definition: input_manager.cpp:352
void set_gamepad_deadzone(int gamepad_index, float deadzone)
Definition: input_manager.cpp:447
InputState get_mouse_button_state(MouseButton button) const
Definition: input_manager.cpp:344
bool is_mouse_button_pressed(MouseButton button) const
Definition: input_manager.cpp:320
bool is_gamepad_button_pressed(int gamepad_index, int button) const
Definition: input_manager.cpp:398
bool is_action_released(const std::string &action_name) const
Definition: input_manager.cpp:557
glm::vec2 get_mouse_wheel() const
Definition: input_manager.h:141
void process_event(const SDL_Event &event)
Definition: input_manager.cpp:174
InputManager()=default
float get_mouse_sensitivity() const
Definition: input_manager.h:202
void set_relative_mouse_mode(bool enabled)
Definition: input_manager.cpp:359
void set_mouse_sensitivity(float sensitivity)
Definition: input_manager.h:201
glm::vec2 screen_to_world(const glm::vec2 &screen_pos) const
Definition: input_manager.cpp:367
bool is_gamepad_connected(int index) const
Definition: input_manager.cpp:393
glm::vec2 world_to_screen(const glm::vec2 &world_pos) const
Definition: input_manager.cpp:377
bool is_input_blocked() const
Definition: input_manager.h:194
bool is_action_held(const std::string &action_name) const
Definition: input_manager.cpp:521
~InputManager()
Definition: input_manager.cpp:169
bool is_mouse_button_held(MouseButton button) const
Definition: input_manager.cpp:328
bool are_keys_held(const std::vector< SDL_Scancode > &keys) const
Definition: input_manager.cpp:301
bool is_key_pressed(SDL_Scancode key) const
Definition: input_manager.cpp:269
Definition: input_manager.h:83
void insert_text(const std::string &text)
Definition: input_manager.cpp:107
int get_cursor_position() const
Definition: input_manager.h:93
void set_text(const std::string &text)
Definition: input_manager.cpp:102
void clear_text()
Definition: input_manager.cpp:96
void move_cursor(int offset)
Definition: input_manager.cpp:119
void process_event(const SDL_Event &event)
Definition: input_manager.cpp:54
bool is_active() const
Definition: input_manager.h:90
void stop_input(SDL_Window *window)
Definition: input_manager.cpp:47
const std::string & get_text() const
Definition: input_manager.h:91
const std::string & get_composition() const
Definition: input_manager.h:92
void delete_char_at_cursor()
Definition: input_manager.cpp:112
void start_input(SDL_Window *window)
Definition: input_manager.cpp:40
MouseButton
Definition: input_manager.h:11
InputState
Definition: input_manager.h:4
Definition: input_manager.h:45
float get_axis_value(int axis) const
Definition: input_manager.cpp:18
float deadzone
Definition: input_manager.h:60
std::unordered_map< int, InputState > button_states
Definition: input_manager.h:53
std::string name
Definition: input_manager.h:49
~GamepadInfo()
Definition: input_manager.cpp:7
SDL_Gamepad * controller
Definition: input_manager.h:47
bool is_controller
Definition: input_manager.h:50
GamepadInfo()=default
SDL_JoystickID instance_id
Definition: input_manager.h:48
std::vector< float > axis_values
Definition: input_manager.h:57
SDL_Joystick * joystick
Definition: input_manager.h:46
std::unordered_map< int, bool > prev_button_states
Definition: input_manager.h:54
bool is_connected() const
Definition: input_manager.h:65
glm::vec2 get_stick_value(int stick_index) const
Definition: input_manager.cpp:27
Definition: input_manager.h:19
std::vector< int > gamepad_buttons
Definition: input_manager.h:23
std::vector< MouseButton > mouse_buttons
Definition: input_manager.h:22
InputAction(const std::string &action_name)
Definition: input_manager.h:27
InputAction & bind_key(SDL_Scancode key)
Definition: input_manager.h:29
std::vector< SDL_Scancode > keys
Definition: input_manager.h:21
InputAction()=default
InputAction & bind_mouse(MouseButton button)
Definition: input_manager.h:34
InputAction & bind_gamepad_button(int button)
Definition: input_manager.h:39
std::string name
Definition: input_manager.h:20
Float rectangle struct.
Definition: engine_structs.h:33
Definition: input_manager.h:70
glm::vec2 position
Definition: input_manager.h:73
glm::vec2 pressure
Definition: input_manager.h:74
TouchPoint()=default
TouchPoint(SDL_FingerID id, glm::vec2 pos, float press=1.0f)
Definition: input_manager.h:79
float start_time
Definition: input_manager.h:76
bool active
Definition: input_manager.h:72
glm::vec2 start_position
Definition: input_manager.h:75
SDL_FingerID finger_id
Definition: input_manager.h:71