Files
spacetris/src/core/input/InputManager.cpp

362 lines
11 KiB
C++

#include "InputManager.h"
#include <SDL3/SDL.h>
#include <algorithm>
InputManager::InputManager() {
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "InputManager initialized");
}
void InputManager::processEvents() {
// Update previous state before processing new events
updateInputState();
// Reset mouse delta
m_mouseDeltaX = 0.0f;
m_mouseDeltaY = 0.0f;
SDL_Event event;
while (SDL_PollEvent(&event)) {
// Trace every polled event type for debugging abrupt termination
{
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "InputManager: polled event type=%d\n", (int)event.type); fclose(f); }
}
switch (event.type) {
case SDL_EVENT_QUIT:
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
handleQuitEvent();
break;
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
handleKeyEvent(event.key);
break;
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP:
handleMouseButtonEvent(event.button);
break;
case SDL_EVENT_MOUSE_MOTION:
handleMouseMotionEvent(event.motion);
break;
case SDL_EVENT_WINDOW_RESIZED:
case SDL_EVENT_WINDOW_MOVED:
case SDL_EVENT_WINDOW_MINIMIZED:
case SDL_EVENT_WINDOW_MAXIMIZED:
case SDL_EVENT_WINDOW_RESTORED:
handleWindowEvent(event.window);
break;
default:
// Unhandled event types
break;
}
}
}
void InputManager::update(float deltaTime) {
updateDAS(deltaTime);
}
bool InputManager::isKeyPressed(SDL_Scancode key) const {
auto current = m_currentKeyState.find(key);
auto previous = m_previousKeyState.find(key);
bool currentlyPressed = (current != m_currentKeyState.end() && current->second);
bool previouslyPressed = (previous != m_previousKeyState.end() && previous->second);
return currentlyPressed && !previouslyPressed;
}
bool InputManager::isKeyReleased(SDL_Scancode key) const {
auto current = m_currentKeyState.find(key);
auto previous = m_previousKeyState.find(key);
bool currentlyPressed = (current != m_currentKeyState.end() && current->second);
bool previouslyPressed = (previous != m_previousKeyState.end() && previous->second);
return !currentlyPressed && previouslyPressed;
}
bool InputManager::isKeyHeld(SDL_Scancode key) const {
auto it = m_currentKeyState.find(key);
return (it != m_currentKeyState.end() && it->second);
}
bool InputManager::isMouseButtonPressed(int button) const {
auto current = m_currentMouseState.find(button);
auto previous = m_previousMouseState.find(button);
bool currentlyPressed = (current != m_currentMouseState.end() && current->second);
bool previouslyPressed = (previous != m_previousMouseState.end() && previous->second);
return currentlyPressed && !previouslyPressed;
}
bool InputManager::isMouseButtonReleased(int button) const {
auto current = m_currentMouseState.find(button);
auto previous = m_previousMouseState.find(button);
bool currentlyPressed = (current != m_currentMouseState.end() && current->second);
bool previouslyPressed = (previous != m_previousMouseState.end() && previous->second);
return !currentlyPressed && previouslyPressed;
}
bool InputManager::isMouseButtonHeld(int button) const {
auto it = m_currentMouseState.find(button);
return (it != m_currentMouseState.end() && it->second);
}
void InputManager::getMousePosition(float& x, float& y) const {
x = m_mouseX;
y = m_mouseY;
}
void InputManager::getMouseDelta(float& deltaX, float& deltaY) const {
deltaX = m_mouseDeltaX;
deltaY = m_mouseDeltaY;
}
void InputManager::registerKeyHandler(KeyHandler handler) {
m_keyHandlers.push_back(std::move(handler));
}
void InputManager::registerMouseButtonHandler(MouseButtonHandler handler) {
m_mouseButtonHandlers.push_back(std::move(handler));
}
void InputManager::registerMouseMotionHandler(MouseMotionHandler handler) {
m_mouseMotionHandlers.push_back(std::move(handler));
}
void InputManager::registerWindowEventHandler(WindowEventHandler handler) {
m_windowEventHandlers.push_back(std::move(handler));
}
void InputManager::registerQuitHandler(QuitHandler handler) {
m_quitHandlers.push_back(std::move(handler));
}
void InputManager::configureDAS(float delayMs, float repeatMs) {
m_dasDelay = delayMs;
m_dasRepeat = repeatMs;
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "DAS configured: delay=%.1fms, repeat=%.1fms", delayMs, repeatMs);
}
bool InputManager::checkDASMovement(SDL_Scancode leftKey, SDL_Scancode rightKey, int& direction) {
bool leftHeld = isKeyHeld(leftKey);
bool rightHeld = isKeyHeld(rightKey);
// Determine current direction
int currentDirection = 0;
if (leftHeld && !rightHeld) {
currentDirection = -1;
} else if (rightHeld && !leftHeld) {
currentDirection = 1;
}
// If no direction or direction changed, reset DAS
if (currentDirection == 0 ||
(m_dasState.isActive &&
((currentDirection == -1 && m_dasState.activeKey != leftKey) ||
(currentDirection == 1 && m_dasState.activeKey != rightKey)))) {
resetDAS();
direction = 0;
return false;
}
// Check for initial key press (immediate movement)
if ((currentDirection == -1 && isKeyPressed(leftKey)) ||
(currentDirection == 1 && isKeyPressed(rightKey))) {
m_dasState.isActive = true;
m_dasState.delayTimer = m_dasDelay;
m_dasState.repeatTimer = 0.0f;
m_dasState.activeKey = (currentDirection == -1) ? leftKey : rightKey;
direction = currentDirection;
return true;
}
// If DAS is active and delay/repeat timers expired, trigger movement
if (m_dasState.isActive && m_dasState.delayTimer <= 0.0f && m_dasState.repeatTimer <= 0.0f) {
m_dasState.repeatTimer = m_dasRepeat;
direction = currentDirection;
return true;
}
direction = 0;
return false;
}
void InputManager::handleKeyEvent(const SDL_KeyboardEvent& event) {
SDL_Scancode scancode = event.scancode;
bool pressed = (event.type == SDL_EVENT_KEY_DOWN) && !event.repeat;
bool released = (event.type == SDL_EVENT_KEY_UP);
if (pressed) {
m_currentKeyState[scancode] = true;
} else if (released) {
m_currentKeyState[scancode] = false;
}
// Notify handlers
for (auto& handler : m_keyHandlers) {
try {
handler(scancode, pressed);
} catch (const std::exception& e) {
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Exception in key handler: %s", e.what());
}
}
}
void InputManager::handleMouseButtonEvent(const SDL_MouseButtonEvent& event) {
int button = event.button;
bool pressed = (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN);
m_currentMouseState[button] = pressed;
// Notify handlers
for (auto& handler : m_mouseButtonHandlers) {
try {
handler(button, pressed, event.x, event.y);
} catch (const std::exception& e) {
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Exception in mouse button handler: %s", e.what());
}
}
}
void InputManager::handleMouseMotionEvent(const SDL_MouseMotionEvent& event) {
float newX = event.x;
float newY = event.y;
m_mouseDeltaX = newX - m_mouseX;
m_mouseDeltaY = newY - m_mouseY;
m_mouseX = newX;
m_mouseY = newY;
// Notify handlers
for (auto& handler : m_mouseMotionHandlers) {
try {
handler(m_mouseX, m_mouseY, m_mouseDeltaX, m_mouseDeltaY);
} catch (const std::exception& e) {
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Exception in mouse motion handler: %s", e.what());
}
}
}
void InputManager::handleWindowEvent(const SDL_WindowEvent& event) {
if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
handleQuitEvent();
}
// Notify handlers
for (auto& handler : m_windowEventHandlers) {
try {
handler(event);
} catch (const std::exception& e) {
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Exception in window event handler: %s", e.what());
}
}
}
void InputManager::updateInputState() {
// Copy current state to previous state
m_previousKeyState = m_currentKeyState;
m_previousMouseState = m_currentMouseState;
}
void InputManager::updateDAS(float deltaTime) {
if (!m_dasState.isActive) {
return;
}
float deltaMs = deltaTime * 1000.0f; // Convert to milliseconds
// Update delay timer
if (m_dasState.delayTimer > 0.0f) {
m_dasState.delayTimer -= deltaMs;
}
// Update repeat timer (only if delay has expired)
if (m_dasState.delayTimer <= 0.0f && m_dasState.repeatTimer > 0.0f) {
m_dasState.repeatTimer -= deltaMs;
}
}
void InputManager::resetDAS() {
m_dasState.isActive = false;
m_dasState.delayTimer = 0.0f;
m_dasState.repeatTimer = 0.0f;
m_dasState.activeKey = SDL_SCANCODE_UNKNOWN;
}
// IInputHandler interface implementation
bool InputManager::processEvent(const SDL_Event& event) {
// Process individual event and return if it was handled
switch (event.type) {
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
handleKeyEvent(event.key);
return true;
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP:
handleMouseButtonEvent(event.button);
return true;
case SDL_EVENT_MOUSE_MOTION:
handleMouseMotionEvent(event.motion);
return true;
case SDL_EVENT_WINDOW_RESIZED:
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
case SDL_EVENT_WINDOW_MINIMIZED:
case SDL_EVENT_WINDOW_RESTORED:
handleWindowEvent(event.window);
return true;
case SDL_EVENT_QUIT:
handleQuitEvent();
return true;
default:
return false;
}
}
void InputManager::update(double deltaTime) {
update(static_cast<float>(deltaTime));
}
bool InputManager::isKeyCurrentlyPressed(SDL_Scancode scancode) const {
return isKeyHeld(scancode);
}
bool InputManager::isKeyJustPressed(SDL_Scancode scancode) const {
return isKeyPressed(scancode);
}
bool InputManager::isKeyJustReleased(SDL_Scancode scancode) const {
return isKeyReleased(scancode);
}
bool InputManager::isQuitRequested() const {
return shouldQuit();
}
void InputManager::reset() {
// Clear pressed/released states, keep held states
// In the current InputManager implementation, we use previous/current state
// so we just copy current to previous to reset the "just pressed/released" states
m_previousKeyState = m_currentKeyState;
m_previousMouseState = m_currentMouseState;
}
void InputManager::handleQuitEvent() {
FILE* f = fopen("tetris_trace.log", "a");
if (f) {
fprintf(f, "InputManager::handleQuitEvent invoked\n");
fclose(f);
}
m_shouldQuit = true;
for (auto& handler : m_quitHandlers) {
handler();
}
}