yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
sdl3_input_backend.cc
Go to the documentation of this file.
2
3#include "imgui/imgui.h"
4#include "util/log.h"
5
6namespace yaze {
7namespace emu {
8namespace input {
9
11
13
15 if (initialized_) {
16 LOG_WARN("InputBackend", "SDL3 backend already initialized");
17 return true;
18 }
19
20 config_ = config;
21
23
24 // Initialize gamepad if enabled
27 if (gamepads_[0]) {
28 LOG_INFO("InputBackend", "SDL3 Gamepad connected for player 1");
29 }
30 }
31
32 initialized_ = true;
33 LOG_INFO("InputBackend", "SDL3 Input Backend initialized");
34 return true;
35}
36
38 if (initialized_) {
39 // Close all gamepads
40 for (int i = 0; i < 4; ++i) {
41 if (gamepads_[i]) {
43 gamepads_[i] = nullptr;
44 }
45 }
46 initialized_ = false;
47 LOG_INFO("InputBackend", "SDL3 Input Backend shut down");
48 }
49}
50
52 if (!initialized_) return ControllerState{};
53
54 ControllerState state;
55
57 // Pump events to update keyboard state - critical for edge detection
58 // when multiple emulated frames run per host frame.
59 // Without this, SDL_GetKeyboardState returns stale data.
60 SDL_PumpEvents();
61
62 // Continuous polling mode (for games)
63 // SDL3: SDL_GetKeyboardState returns const bool* instead of const Uint8*
64 platform::KeyboardState keyboard_state = SDL_GetKeyboardState(nullptr);
65
66 // Respect ImGui text capture unless explicitly overridden
67 ImGuiIO& io = ImGui::GetIO();
68 if (io.WantTextInput && !config_.ignore_imgui_text_input) {
69 return ControllerState{};
70 }
71
72 // Map keyboard to SNES buttons using SDL3 API
73 // Use platform::IsKeyPressed helper to handle bool* vs Uint8* difference
74 state.SetButton(
76 platform::IsKeyPressed(keyboard_state,
77 SDL_GetScancodeFromKey(config_.key_b, nullptr)));
78 state.SetButton(
80 platform::IsKeyPressed(keyboard_state,
81 SDL_GetScancodeFromKey(config_.key_y, nullptr)));
82 state.SetButton(
85 keyboard_state,
86 SDL_GetScancodeFromKey(config_.key_select, nullptr)));
87 state.SetButton(
90 keyboard_state,
91 SDL_GetScancodeFromKey(config_.key_start, nullptr)));
92 state.SetButton(
95 keyboard_state, SDL_GetScancodeFromKey(config_.key_up, nullptr)));
96 state.SetButton(
99 keyboard_state, SDL_GetScancodeFromKey(config_.key_down, nullptr)));
100 state.SetButton(
103 keyboard_state, SDL_GetScancodeFromKey(config_.key_left, nullptr)));
104 state.SetButton(
107 keyboard_state,
108 SDL_GetScancodeFromKey(config_.key_right, nullptr)));
109 state.SetButton(
111 platform::IsKeyPressed(keyboard_state,
112 SDL_GetScancodeFromKey(config_.key_a, nullptr)));
113 state.SetButton(
115 platform::IsKeyPressed(keyboard_state,
116 SDL_GetScancodeFromKey(config_.key_x, nullptr)));
117 state.SetButton(
119 platform::IsKeyPressed(keyboard_state,
120 SDL_GetScancodeFromKey(config_.key_l, nullptr)));
121 state.SetButton(
123 platform::IsKeyPressed(keyboard_state,
124 SDL_GetScancodeFromKey(config_.key_r, nullptr)));
125
126 // Poll gamepad if enabled
128 PollGamepad(state, player);
129 }
130 } else {
131 // Event-based mode (use cached event state)
132 state = event_state_;
133 }
134
135 return state;
136}
137
139 int gamepad_index = (player > 0 && player <= 4) ? player - 1 : 0;
140 platform::GamepadHandle gamepad = gamepads_[gamepad_index];
141
142 if (!gamepad) return;
143
144 // Map gamepad buttons to SNES buttons using SDL3 Gamepad API
145 // SDL3 uses SDL_GAMEPAD_BUTTON_* with directional naming (SOUTH, EAST, etc.)
147 state.SetButton(SnesButton::A, true);
148 }
150 state.SetButton(SnesButton::B, true);
151 }
153 state.SetButton(SnesButton::X, true);
154 }
156 state.SetButton(SnesButton::Y, true);
157 }
159 state.SetButton(SnesButton::L, true);
160 }
161 if (platform::GetGamepadButton(gamepad,
163 state.SetButton(SnesButton::R, true);
164 }
166 state.SetButton(SnesButton::START, true);
167 }
169 state.SetButton(SnesButton::SELECT, true);
170 }
171
172 // D-pad buttons
174 state.SetButton(SnesButton::UP, true);
175 }
177 state.SetButton(SnesButton::DOWN, true);
178 }
180 state.SetButton(SnesButton::LEFT, true);
181 }
183 state.SetButton(SnesButton::RIGHT, true);
184 }
185
186 // Left analog stick for D-pad (with deadzone)
187 int16_t axis_x = platform::GetGamepadAxis(gamepad, platform::kGamepadAxisLeftX);
188 int16_t axis_y = platform::GetGamepadAxis(gamepad, platform::kGamepadAxisLeftY);
189
190 if (axis_x < -kAxisDeadzone) {
191 state.SetButton(SnesButton::LEFT, true);
192 } else if (axis_x > kAxisDeadzone) {
193 state.SetButton(SnesButton::RIGHT, true);
194 }
195
196 if (axis_y < -kAxisDeadzone) {
197 state.SetButton(SnesButton::UP, true);
198 } else if (axis_y > kAxisDeadzone) {
199 state.SetButton(SnesButton::DOWN, true);
200 }
201}
202
204 if (!initialized_ || !event) return;
205
206 SDL_Event* sdl_event = static_cast<SDL_Event*>(event);
207
208 // Handle keyboard events
209 // SDL3: Uses SDL_EVENT_KEY_DOWN/UP instead of SDL_KEYDOWN/UP
210 // SDL3: Uses event.key.key instead of event.key.keysym.sym
211 if (sdl_event->type == platform::kEventKeyDown) {
213 } else if (sdl_event->type == platform::kEventKeyUp) {
215 }
216
217 // Handle gamepad connection/disconnection events
218 HandleGamepadEvent(*sdl_event);
219}
220
221void SDL3InputBackend::HandleGamepadEvent(const SDL_Event& event) {
222#ifdef YAZE_USE_SDL3
223 // SDL3 uses SDL_EVENT_GAMEPAD_ADDED/REMOVED
224 if (event.type == SDL_EVENT_GAMEPAD_ADDED) {
225 // Try to open the gamepad if we have a free slot
226 for (int i = 0; i < 4; ++i) {
227 if (!gamepads_[i]) {
228 gamepads_[i] = SDL_OpenGamepad(event.gdevice.which);
229 if (gamepads_[i]) {
230 LOG_INFO("InputBackend", "SDL3 Gamepad connected for player %d", i + 1);
231 }
232 break;
233 }
234 }
235 } else if (event.type == SDL_EVENT_GAMEPAD_REMOVED) {
236 // Find and close the disconnected gamepad
237 for (int i = 0; i < 4; ++i) {
238 if (gamepads_[i] &&
239 SDL_GetGamepadID(gamepads_[i]) == event.gdevice.which) {
240 SDL_CloseGamepad(gamepads_[i]);
241 gamepads_[i] = nullptr;
242 LOG_INFO("InputBackend", "SDL3 Gamepad disconnected for player %d", i + 1);
243 break;
244 }
245 }
246 }
247#else
248 // SDL2 uses SDL_CONTROLLERDEVICEADDED/REMOVED
249 if (event.type == SDL_CONTROLLERDEVICEADDED) {
250 for (int i = 0; i < 4; ++i) {
251 if (!gamepads_[i]) {
252 gamepads_[i] = platform::OpenGamepad(event.cdevice.which);
253 if (gamepads_[i]) {
254 LOG_INFO("InputBackend", "Gamepad connected for player " +
255 std::to_string(i + 1));
256 }
257 break;
258 }
259 }
260 } else if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
261 for (int i = 0; i < 4; ++i) {
262 if (gamepads_[i] && SDL_JoystickInstanceID(
263 SDL_GameControllerGetJoystick(gamepads_[i])) ==
264 event.cdevice.which) {
266 gamepads_[i] = nullptr;
267 LOG_INFO("InputBackend", "Gamepad disconnected for player " +
268 std::to_string(i + 1));
269 break;
270 }
271 }
272 }
273#endif
274}
275
276void SDL3InputBackend::UpdateEventState(int keycode, bool pressed) {
277 // Map keycode to button and update event state
278 if (keycode == config_.key_a)
280 else if (keycode == config_.key_b)
282 else if (keycode == config_.key_x)
284 else if (keycode == config_.key_y)
286 else if (keycode == config_.key_l)
288 else if (keycode == config_.key_r)
290 else if (keycode == config_.key_start)
292 else if (keycode == config_.key_select)
294 else if (keycode == config_.key_up)
296 else if (keycode == config_.key_down)
298 else if (keycode == config_.key_left)
300 else if (keycode == config_.key_right)
302}
303
305
307 config_ = config;
308
309 // Re-initialize gamepad if gamepad settings changed
310 if (config_.enable_gamepad && !gamepads_[0]) {
312 if (gamepads_[0]) {
313 LOG_INFO("InputBackend", "SDL3 Gamepad connected for player 1");
314 }
315 } else if (!config_.enable_gamepad && gamepads_[0]) {
317 gamepads_[0] = nullptr;
318 }
319}
320
321std::string SDL3InputBackend::GetBackendName() const { return "SDL3"; }
322
324
325} // namespace input
326} // namespace emu
327} // namespace yaze
void ProcessEvent(void *event) override
Process platform-specific events (optional)
void SetConfig(const InputConfig &config) override
Update configuration (hot-reload)
static constexpr int16_t kAxisDeadzone
platform::GamepadHandle gamepads_[4]
void PollGamepad(ControllerState &state, int player)
Poll gamepad state and update controller state.
InputConfig GetConfig() const override
Get current configuration.
std::string GetBackendName() const override
Get backend name for debugging.
void HandleGamepadEvent(const SDL_Event &event)
Handle gamepad connection/disconnection.
void Shutdown() override
Shutdown the input backend.
bool Initialize(const InputConfig &config) override
Initialize the input backend.
ControllerState Poll(int player=1) override
Poll current input state (call every frame)
void UpdateEventState(int keycode, bool pressed)
Update event state from keyboard event.
bool IsInitialized() const override
Check if backend is initialized.
#define LOG_WARN(category, format,...)
Definition log.h:107
#define LOG_INFO(category, format,...)
Definition log.h:105
void ApplyDefaultKeyBindings(InputConfig &config)
Apply default keyboard bindings if unset.
constexpr auto kGamepadButtonA
Definition sdl_compat.h:248
constexpr auto kEventKeyDown
Definition sdl_compat.h:53
constexpr auto kGamepadButtonLeftShoulder
Definition sdl_compat.h:254
int16_t GetGamepadAxis(GamepadHandle gamepad, SDL_GameControllerAxis axis)
Definition sdl_compat.h:211
constexpr auto kGamepadAxisLeftY
Definition sdl_compat.h:262
constexpr auto kGamepadButtonDpadDown
Definition sdl_compat.h:257
constexpr auto kGamepadAxisLeftX
Definition sdl_compat.h:261
SDL_GameController * GamepadHandle
Definition sdl_compat.h:193
constexpr auto kGamepadButtonDpadRight
Definition sdl_compat.h:259
constexpr auto kEventKeyUp
Definition sdl_compat.h:54
GamepadHandle OpenGamepad(int index)
Definition sdl_compat.h:195
constexpr auto kGamepadButtonX
Definition sdl_compat.h:250
SDL_Keycode GetKeyFromEvent(const SDL_Event &event)
Get keyboard state from SDL event.
Definition sdl_compat.h:115
constexpr auto kGamepadButtonDpadUp
Definition sdl_compat.h:256
bool IsKeyPressed(KeyboardState state, SDL_Scancode scancode)
Check if a key is pressed using the keyboard state.
Definition sdl_compat.h:142
bool GetGamepadButton(GamepadHandle gamepad, SDL_GameControllerButton button)
Definition sdl_compat.h:206
constexpr auto kGamepadButtonB
Definition sdl_compat.h:249
constexpr auto kGamepadButtonRightShoulder
Definition sdl_compat.h:255
constexpr auto kGamepadButtonStart
Definition sdl_compat.h:253
const Uint8 * KeyboardState
Definition sdl_compat.h:32
void CloseGamepad(GamepadHandle gamepad)
Definition sdl_compat.h:202
constexpr auto kGamepadButtonDpadLeft
Definition sdl_compat.h:258
constexpr auto kGamepadButtonBack
Definition sdl_compat.h:252
constexpr auto kGamepadButtonY
Definition sdl_compat.h:251
Controller state (16-bit SNES controller format)
void SetButton(SnesButton button, bool pressed)
Input configuration (platform-agnostic key codes)