yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
input_backend.cc
Go to the documentation of this file.
2
3#include "SDL.h"
4#include "imgui/imgui.h"
5#include "util/log.h"
6
7namespace yaze {
8namespace emu {
9namespace input {
10
15 public:
16 SDL2InputBackend() = default;
17 ~SDL2InputBackend() override { Shutdown(); }
18
19 bool Initialize(const InputConfig& config) override {
20 if (initialized_) {
21 LOG_WARN("InputBackend", "Already initialized");
22 return true;
23 }
24
25 config_ = config;
26
27 // Set default SDL2 keycodes if not configured
28 if (config_.key_a == 0) {
29 config_.key_a = SDLK_x;
30 config_.key_b = SDLK_z;
31 config_.key_x = SDLK_s;
32 config_.key_y = SDLK_a;
33 config_.key_l = SDLK_d;
34 config_.key_r = SDLK_c;
35 config_.key_start = SDLK_RETURN;
36 config_.key_select = SDLK_RSHIFT;
37 config_.key_up = SDLK_UP;
38 config_.key_down = SDLK_DOWN;
39 config_.key_left = SDLK_LEFT;
40 config_.key_right = SDLK_RIGHT;
41 }
42
43 initialized_ = true;
44 LOG_INFO("InputBackend", "SDL2 Input Backend initialized");
45 return true;
46 }
47
48 void Shutdown() override {
49 if (initialized_) {
50 initialized_ = false;
51 LOG_INFO("InputBackend", "SDL2 Input Backend shut down");
52 }
53 }
54
55 ControllerState Poll(int player) override {
56 if (!initialized_) return ControllerState{};
57
58 ControllerState state;
59
61 // Continuous polling mode (for games)
62 const uint8_t* keyboard_state = SDL_GetKeyboardState(nullptr);
63
64 // IMPORTANT: Only block input when actively typing in text fields
65 // Allow game input even when ImGui windows are open/focused
66 ImGuiIO& io = ImGui::GetIO();
67
68 // Only block if user is actively typing in a text input field
69 // WantTextInput is true only when an InputText widget is active
70 if (io.WantTextInput) {
71 // User is typing in a text field
72 // Return empty state to prevent game from processing input
73 static int text_input_log_count = 0;
74 if (text_input_log_count++ < 5) {
75 LOG_DEBUG("InputBackend", "Blocking game input - WantTextInput=true");
76 }
77 return ControllerState{};
78 }
79
80 // Map keyboard to SNES buttons
81 state.SetButton(SnesButton::B, keyboard_state[SDL_GetScancodeFromKey(config_.key_b)]);
82 state.SetButton(SnesButton::Y, keyboard_state[SDL_GetScancodeFromKey(config_.key_y)]);
83 state.SetButton(SnesButton::SELECT, keyboard_state[SDL_GetScancodeFromKey(config_.key_select)]);
84 state.SetButton(SnesButton::START, keyboard_state[SDL_GetScancodeFromKey(config_.key_start)]);
85 state.SetButton(SnesButton::UP, keyboard_state[SDL_GetScancodeFromKey(config_.key_up)]);
86 state.SetButton(SnesButton::DOWN, keyboard_state[SDL_GetScancodeFromKey(config_.key_down)]);
87 state.SetButton(SnesButton::LEFT, keyboard_state[SDL_GetScancodeFromKey(config_.key_left)]);
88 state.SetButton(SnesButton::RIGHT, keyboard_state[SDL_GetScancodeFromKey(config_.key_right)]);
89 state.SetButton(SnesButton::A, keyboard_state[SDL_GetScancodeFromKey(config_.key_a)]);
90 state.SetButton(SnesButton::X, keyboard_state[SDL_GetScancodeFromKey(config_.key_x)]);
91 state.SetButton(SnesButton::L, keyboard_state[SDL_GetScancodeFromKey(config_.key_l)]);
92 state.SetButton(SnesButton::R, keyboard_state[SDL_GetScancodeFromKey(config_.key_r)]);
93 } else {
94 // Event-based mode (use cached event state)
95 state = event_state_;
96 }
97
98 // TODO: Add gamepad support
99 // if (config_.enable_gamepad) { ... }
100
101 return state;
102 }
103
104 void ProcessEvent(void* event) override {
105 if (!initialized_ || !event) return;
106
107 SDL_Event* sdl_event = static_cast<SDL_Event*>(event);
108
109 // Cache keyboard events for event-based mode
110 if (sdl_event->type == SDL_KEYDOWN) {
111 UpdateEventState(sdl_event->key.keysym.sym, true);
112 } else if (sdl_event->type == SDL_KEYUP) {
113 UpdateEventState(sdl_event->key.keysym.sym, false);
114 }
115
116 // TODO: Handle gamepad events
117 }
118
119 InputConfig GetConfig() const override { return config_; }
120
121 void SetConfig(const InputConfig& config) override {
122 config_ = config;
123 }
124
125 std::string GetBackendName() const override { return "SDL2"; }
126
127 bool IsInitialized() const override { return initialized_; }
128
129 private:
130 void UpdateEventState(int keycode, bool pressed) {
131 // Map keycode to button and update event state
132 if (keycode == config_.key_a) event_state_.SetButton(SnesButton::A, pressed);
133 else if (keycode == config_.key_b) event_state_.SetButton(SnesButton::B, pressed);
134 else if (keycode == config_.key_x) event_state_.SetButton(SnesButton::X, pressed);
135 else if (keycode == config_.key_y) event_state_.SetButton(SnesButton::Y, pressed);
136 else if (keycode == config_.key_l) event_state_.SetButton(SnesButton::L, pressed);
137 else if (keycode == config_.key_r) event_state_.SetButton(SnesButton::R, pressed);
138 else if (keycode == config_.key_start) event_state_.SetButton(SnesButton::START, pressed);
139 else if (keycode == config_.key_select) event_state_.SetButton(SnesButton::SELECT, pressed);
140 else if (keycode == config_.key_up) event_state_.SetButton(SnesButton::UP, pressed);
141 else if (keycode == config_.key_down) event_state_.SetButton(SnesButton::DOWN, pressed);
142 else if (keycode == config_.key_left) event_state_.SetButton(SnesButton::LEFT, pressed);
143 else if (keycode == config_.key_right) event_state_.SetButton(SnesButton::RIGHT, pressed);
144 }
145
147 bool initialized_ = false;
148 ControllerState event_state_; // Cached state for event-based mode
149};
150
155 public:
156 bool Initialize(const InputConfig& config) override {
157 config_ = config;
158 return true;
159 }
160 void Shutdown() override {}
161 ControllerState Poll(int player) override { return replay_state_; }
162 void ProcessEvent(void* event) override {}
163 InputConfig GetConfig() const override { return config_; }
164 void SetConfig(const InputConfig& config) override { config_ = config; }
165 std::string GetBackendName() const override { return "NULL"; }
166 bool IsInitialized() const override { return true; }
167
168 // For replay/testing - set controller state directly
169 void SetReplayState(const ControllerState& state) { replay_state_ = state; }
170
171 private:
174};
175
176// Factory implementation
177std::unique_ptr<IInputBackend> InputBackendFactory::Create(BackendType type) {
178 switch (type) {
180 return std::make_unique<SDL2InputBackend>();
181
183 // TODO: Implement SDL3 backend when SDL3 is stable
184 LOG_WARN("InputBackend", "SDL3 backend not yet implemented, using SDL2");
185 return std::make_unique<SDL2InputBackend>();
186
188 return std::make_unique<NullInputBackend>();
189
190 default:
191 LOG_ERROR("InputBackend", "Unknown backend type, using SDL2");
192 return std::make_unique<SDL2InputBackend>();
193 }
194}
195
196} // namespace input
197} // namespace emu
198} // namespace yaze
199
Abstract input backend interface.
static std::unique_ptr< IInputBackend > Create(BackendType type)
Null input backend for testing/replay.
bool IsInitialized() const override
Check if backend is initialized.
InputConfig GetConfig() const override
Get current configuration.
bool Initialize(const InputConfig &config) override
Initialize the input backend.
ControllerState Poll(int player) override
Poll current input state (call every frame)
void SetReplayState(const ControllerState &state)
void SetConfig(const InputConfig &config) override
Update configuration (hot-reload)
void ProcessEvent(void *event) override
Process platform-specific events (optional)
void Shutdown() override
Shutdown the input backend.
std::string GetBackendName() const override
Get backend name for debugging.
SDL2 input backend implementation.
InputConfig GetConfig() const override
Get current configuration.
void ProcessEvent(void *event) override
Process platform-specific events (optional)
void UpdateEventState(int keycode, bool pressed)
void SetConfig(const InputConfig &config) override
Update configuration (hot-reload)
void Shutdown() override
Shutdown the input backend.
bool IsInitialized() const override
Check if backend is initialized.
std::string GetBackendName() const override
Get backend name for debugging.
bool Initialize(const InputConfig &config) override
Initialize the input backend.
ControllerState Poll(int player) override
Poll current input state (call every frame)
#define LOG_DEBUG(category, format,...)
Definition log.h:104
#define LOG_ERROR(category, format,...)
Definition log.h:110
#define LOG_WARN(category, format,...)
Definition log.h:108
#define LOG_INFO(category, format,...)
Definition log.h:106
Main namespace for the application.
Definition controller.cc:20
Controller state (16-bit SNES controller format)
void SetButton(SnesButton button, bool pressed)
Input configuration (platform-agnostic key codes)