362 lines
11 KiB
C++
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();
|
|
}
|
|
}
|