#include "InputManager.h" #include #include 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(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(); } }