185 lines
6.6 KiB
C++
185 lines
6.6 KiB
C++
#include "InputModule.h"
|
|
#include <grove/JsonDataNode.h>
|
|
#include <spdlog/spdlog.h>
|
|
|
|
namespace grove {
|
|
|
|
InputModule::InputModule() {
|
|
m_state = std::make_unique<InputState>();
|
|
m_config = std::make_unique<JsonDataNode>("config");
|
|
}
|
|
|
|
InputModule::~InputModule() {
|
|
shutdown();
|
|
}
|
|
|
|
void InputModule::setConfiguration(const IDataNode& config, IIO* io, ITaskScheduler* scheduler) {
|
|
m_io = io;
|
|
m_converter = std::make_unique<InputConverter>(io);
|
|
|
|
// Parse configuration
|
|
m_backend = config.getString("backend", "sdl");
|
|
m_enableMouse = config.getBool("enableMouse", true);
|
|
m_enableKeyboard = config.getBool("enableKeyboard", true);
|
|
m_enableGamepad = config.getBool("enableGamepad", false);
|
|
|
|
spdlog::info("[InputModule] Configured with backend={}, mouse={}, keyboard={}, gamepad={}",
|
|
m_backend, m_enableMouse, m_enableKeyboard, m_enableGamepad);
|
|
}
|
|
|
|
void InputModule::process(const IDataNode& input) {
|
|
m_frameCount++;
|
|
|
|
// 1. Lock and retrieve events from buffer
|
|
std::vector<SDL_Event> events;
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_bufferMutex);
|
|
events = std::move(m_eventBuffer);
|
|
m_eventBuffer.clear();
|
|
}
|
|
|
|
// 2. Convert SDL → Generic → IIO
|
|
for (const auto& sdlEvent : events) {
|
|
SDLBackend::InputEvent genericEvent;
|
|
|
|
if (!SDLBackend::convert(sdlEvent, genericEvent)) {
|
|
continue; // Event not supported, skip
|
|
}
|
|
|
|
// 3. Update state and publish to IIO
|
|
switch (genericEvent.type) {
|
|
case SDLBackend::InputEvent::MouseMove:
|
|
if (m_enableMouse) {
|
|
m_state->setMousePosition(genericEvent.mouseX, genericEvent.mouseY);
|
|
m_converter->publishMouseMove(genericEvent.mouseX, genericEvent.mouseY);
|
|
}
|
|
break;
|
|
|
|
case SDLBackend::InputEvent::MouseButton:
|
|
if (m_enableMouse) {
|
|
m_state->setMouseButton(genericEvent.button, genericEvent.pressed);
|
|
m_converter->publishMouseButton(genericEvent.button, genericEvent.pressed,
|
|
genericEvent.mouseX, genericEvent.mouseY);
|
|
}
|
|
break;
|
|
|
|
case SDLBackend::InputEvent::MouseWheel:
|
|
if (m_enableMouse) {
|
|
m_converter->publishMouseWheel(genericEvent.wheelDelta);
|
|
}
|
|
break;
|
|
|
|
case SDLBackend::InputEvent::KeyboardKey:
|
|
if (m_enableKeyboard) {
|
|
m_state->setKey(genericEvent.scancode, genericEvent.pressed);
|
|
m_state->updateModifiers(genericEvent.shift, genericEvent.ctrl, genericEvent.alt);
|
|
m_converter->publishKeyboardKey(genericEvent.scancode, genericEvent.pressed,
|
|
genericEvent.repeat, genericEvent.shift,
|
|
genericEvent.ctrl, genericEvent.alt);
|
|
}
|
|
break;
|
|
|
|
case SDLBackend::InputEvent::KeyboardText:
|
|
if (m_enableKeyboard) {
|
|
m_converter->publishKeyboardText(genericEvent.text);
|
|
}
|
|
break;
|
|
}
|
|
|
|
m_eventsProcessed++;
|
|
}
|
|
}
|
|
|
|
void InputModule::shutdown() {
|
|
spdlog::info("[InputModule] Shutdown - Processed {} events over {} frames",
|
|
m_eventsProcessed, m_frameCount);
|
|
m_io = nullptr;
|
|
}
|
|
|
|
std::unique_ptr<IDataNode> InputModule::getState() {
|
|
auto state = std::make_unique<JsonDataNode>("state");
|
|
|
|
// Mouse state
|
|
state->setInt("mouseX", m_state->mouseX);
|
|
state->setInt("mouseY", m_state->mouseY);
|
|
state->setBool("mouseButton0", m_state->mouseButtons[0]);
|
|
state->setBool("mouseButton1", m_state->mouseButtons[1]);
|
|
state->setBool("mouseButton2", m_state->mouseButtons[2]);
|
|
|
|
// Buffered events count (can't serialize SDL_Event, but track count)
|
|
std::lock_guard<std::mutex> lock(m_bufferMutex);
|
|
state->setInt("bufferedEventCount", static_cast<int>(m_eventBuffer.size()));
|
|
|
|
// Stats
|
|
state->setInt("frameCount", static_cast<int>(m_frameCount));
|
|
state->setInt("eventsProcessed", static_cast<int>(m_eventsProcessed));
|
|
|
|
return state;
|
|
}
|
|
|
|
void InputModule::setState(const IDataNode& state) {
|
|
// Restore mouse state
|
|
m_state->mouseX = state.getInt("mouseX", 0);
|
|
m_state->mouseY = state.getInt("mouseY", 0);
|
|
m_state->mouseButtons[0] = state.getBool("mouseButton0", false);
|
|
m_state->mouseButtons[1] = state.getBool("mouseButton1", false);
|
|
m_state->mouseButtons[2] = state.getBool("mouseButton2", false);
|
|
|
|
// Restore stats
|
|
m_frameCount = static_cast<uint64_t>(state.getInt("frameCount", 0));
|
|
m_eventsProcessed = static_cast<uint64_t>(state.getInt("eventsProcessed", 0));
|
|
|
|
// Note: We can't restore the event buffer (SDL_Event is not serializable)
|
|
// This is acceptable - we lose at most 1 frame of events during hot-reload
|
|
|
|
spdlog::info("[InputModule] State restored - mouse=({},{}), frames={}, events={}",
|
|
m_state->mouseX, m_state->mouseY, m_frameCount, m_eventsProcessed);
|
|
}
|
|
|
|
const IDataNode& InputModule::getConfiguration() {
|
|
if (!m_config) {
|
|
m_config = std::make_unique<JsonDataNode>("config");
|
|
}
|
|
|
|
// Rebuild config from current state
|
|
m_config->setString("backend", m_backend);
|
|
m_config->setBool("enableMouse", m_enableMouse);
|
|
m_config->setBool("enableKeyboard", m_enableKeyboard);
|
|
m_config->setBool("enableGamepad", m_enableGamepad);
|
|
|
|
return *m_config;
|
|
}
|
|
|
|
std::unique_ptr<IDataNode> InputModule::getHealthStatus() {
|
|
auto health = std::make_unique<JsonDataNode>("health");
|
|
health->setString("status", "healthy");
|
|
health->setInt("frameCount", static_cast<int>(m_frameCount));
|
|
health->setInt("eventsProcessed", static_cast<int>(m_eventsProcessed));
|
|
|
|
double eventsPerFrame = (m_frameCount > 0) ?
|
|
(static_cast<double>(m_eventsProcessed) / static_cast<double>(m_frameCount)) : 0.0;
|
|
health->setDouble("eventsPerFrame", eventsPerFrame);
|
|
|
|
return health;
|
|
}
|
|
|
|
void InputModule::feedEvent(const void* nativeEvent) {
|
|
const SDL_Event* sdlEvent = static_cast<const SDL_Event*>(nativeEvent);
|
|
|
|
std::lock_guard<std::mutex> lock(m_bufferMutex);
|
|
m_eventBuffer.push_back(*sdlEvent);
|
|
}
|
|
|
|
} // namespace grove
|
|
|
|
// Export functions for module loading
|
|
extern "C" {
|
|
grove::IModule* createModule() {
|
|
return new grove::InputModule();
|
|
}
|
|
|
|
void destroyModule(grove::IModule* module) {
|
|
delete module;
|
|
}
|
|
}
|