284 lines
10 KiB
C++
284 lines
10 KiB
C++
/**
|
|
* Test: InputModule Basic Visual Test
|
|
*
|
|
* Tests the InputModule Phase 1 implementation:
|
|
* - SDL event capture
|
|
* - Mouse move/button/wheel events
|
|
* - Keyboard key/text events
|
|
* - IIO message publishing
|
|
*
|
|
* Instructions:
|
|
* - Move mouse to test mouse:move events
|
|
* - Click buttons to test mouse:button events
|
|
* - Scroll wheel to test mouse:wheel events
|
|
* - Press keys to test keyboard:key events
|
|
* - Type text to test keyboard:text events
|
|
* - Press ESC to exit
|
|
*/
|
|
|
|
#include <SDL2/SDL.h>
|
|
#include <grove/ModuleLoader.h>
|
|
#include <grove/IntraIOManager.h>
|
|
#include <grove/IntraIO.h>
|
|
#include <grove/JsonDataNode.h>
|
|
#include "modules/InputModule/InputModule.h"
|
|
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
|
|
int main(int argc, char* argv[]) {
|
|
std::cout << "========================================\n";
|
|
std::cout << "InputModule Visual Test\n";
|
|
std::cout << "========================================\n\n";
|
|
std::cout << "Instructions:\n";
|
|
std::cout << " - Move mouse to see mouse:move events\n";
|
|
std::cout << " - Click to see mouse:button events\n";
|
|
std::cout << " - Scroll to see mouse:wheel events\n";
|
|
std::cout << " - Press keys to see keyboard:key events\n";
|
|
std::cout << " - Type to see keyboard:text events\n";
|
|
std::cout << " - Press ESC to exit\n";
|
|
std::cout << "========================================\n\n";
|
|
|
|
// Initialize SDL
|
|
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
|
std::cerr << "SDL_Init failed: " << SDL_GetError() << "\n";
|
|
return 1;
|
|
}
|
|
|
|
// Create window
|
|
int width = 800;
|
|
int height = 600;
|
|
|
|
SDL_Window* window = SDL_CreateWindow(
|
|
"InputModule Test - Press ESC to exit",
|
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
|
width, height,
|
|
SDL_WINDOW_SHOWN
|
|
);
|
|
|
|
if (!window) {
|
|
std::cerr << "SDL_CreateWindow failed: " << SDL_GetError() << "\n";
|
|
SDL_Quit();
|
|
return 1;
|
|
}
|
|
|
|
// Enable text input for keyboard:text events
|
|
SDL_StartTextInput();
|
|
|
|
std::cout << "Window created: " << width << "x" << height << "\n\n";
|
|
|
|
// ========================================
|
|
// Setup GroveEngine systems
|
|
// ========================================
|
|
|
|
auto& ioManager = grove::IntraIOManager::getInstance();
|
|
|
|
auto inputIO = ioManager.createInstance("input_module");
|
|
auto testIO = ioManager.createInstance("test_controller");
|
|
|
|
std::cout << "IIO Manager setup complete\n";
|
|
|
|
// ========================================
|
|
// Load InputModule
|
|
// ========================================
|
|
|
|
grove::ModuleLoader inputLoader;
|
|
|
|
std::string inputPath = "../modules/libInputModule.so";
|
|
#ifdef _WIN32
|
|
inputPath = "../modules/InputModule.dll";
|
|
#endif
|
|
|
|
std::unique_ptr<grove::IModule> inputModuleBase;
|
|
try {
|
|
inputModuleBase = inputLoader.load(inputPath, "input_module");
|
|
} catch (const std::exception& e) {
|
|
std::cerr << "Failed to load InputModule: " << e.what() << "\n";
|
|
SDL_DestroyWindow(window);
|
|
SDL_Quit();
|
|
return 1;
|
|
}
|
|
|
|
if (!inputModuleBase) {
|
|
std::cerr << "Failed to load InputModule\n";
|
|
SDL_DestroyWindow(window);
|
|
SDL_Quit();
|
|
return 1;
|
|
}
|
|
|
|
// Cast to InputModule to access feedEvent()
|
|
grove::InputModule* inputModule = dynamic_cast<grove::InputModule*>(inputModuleBase.get());
|
|
if (!inputModule) {
|
|
std::cerr << "Failed to cast to InputModule\n";
|
|
SDL_DestroyWindow(window);
|
|
SDL_Quit();
|
|
return 1;
|
|
}
|
|
|
|
std::cout << "InputModule loaded\n";
|
|
|
|
// Configure InputModule
|
|
grove::JsonDataNode inputConfig("config");
|
|
inputConfig.setString("backend", "sdl");
|
|
inputConfig.setBool("enableMouse", true);
|
|
inputConfig.setBool("enableKeyboard", true);
|
|
inputConfig.setBool("enableGamepad", false);
|
|
|
|
inputModule->setConfiguration(inputConfig, inputIO.get(), nullptr);
|
|
std::cout << "InputModule configured\n\n";
|
|
|
|
// ========================================
|
|
// Subscribe to input events
|
|
// ========================================
|
|
|
|
testIO->subscribe("input:mouse:move");
|
|
testIO->subscribe("input:mouse:button");
|
|
testIO->subscribe("input:mouse:wheel");
|
|
testIO->subscribe("input:keyboard:key");
|
|
testIO->subscribe("input:keyboard:text");
|
|
|
|
std::cout << "Subscribed to all input topics\n";
|
|
std::cout << "========================================\n\n";
|
|
|
|
// ========================================
|
|
// Main loop
|
|
// ========================================
|
|
|
|
bool running = true;
|
|
uint32_t frameCount = 0;
|
|
uint32_t lastTime = SDL_GetTicks();
|
|
|
|
// Track last mouse move to avoid spam
|
|
int lastMouseX = -1;
|
|
int lastMouseY = -1;
|
|
|
|
while (running) {
|
|
frameCount++;
|
|
|
|
// 1. Poll SDL events and feed to InputModule
|
|
SDL_Event event;
|
|
while (SDL_PollEvent(&event)) {
|
|
if (event.type == SDL_QUIT) {
|
|
running = false;
|
|
}
|
|
|
|
if (event.type == SDL_KEYDOWN && event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) {
|
|
running = false;
|
|
}
|
|
|
|
// Feed event to InputModule (thread-safe)
|
|
inputModule->feedEvent(&event);
|
|
}
|
|
|
|
// 2. Process InputModule (converts buffered events to IIO messages)
|
|
grove::JsonDataNode input("input");
|
|
inputModule->process(input);
|
|
|
|
// 3. Process IIO messages from InputModule
|
|
while (testIO->hasMessages() > 0) {
|
|
auto msg = testIO->pullMessage();
|
|
|
|
if (msg.topic == "input:mouse:move") {
|
|
int x = msg.data->getInt("x", 0);
|
|
int y = msg.data->getInt("y", 0);
|
|
|
|
// Only print if position changed (reduce spam)
|
|
if (x != lastMouseX || y != lastMouseY) {
|
|
std::cout << "[MOUSE MOVE] x=" << std::setw(4) << x
|
|
<< ", y=" << std::setw(4) << y << "\n";
|
|
lastMouseX = x;
|
|
lastMouseY = y;
|
|
}
|
|
}
|
|
else if (msg.topic == "input:mouse:button") {
|
|
int button = msg.data->getInt("button", 0);
|
|
bool pressed = msg.data->getBool("pressed", false);
|
|
int x = msg.data->getInt("x", 0);
|
|
int y = msg.data->getInt("y", 0);
|
|
|
|
const char* buttonNames[] = { "LEFT", "MIDDLE", "RIGHT" };
|
|
const char* buttonName = (button >= 0 && button < 3) ? buttonNames[button] : "UNKNOWN";
|
|
|
|
std::cout << "[MOUSE BUTTON] " << buttonName
|
|
<< " " << (pressed ? "PRESSED" : "RELEASED")
|
|
<< " at (" << x << ", " << y << ")\n";
|
|
}
|
|
else if (msg.topic == "input:mouse:wheel") {
|
|
double delta = msg.data->getDouble("delta", 0.0);
|
|
std::cout << "[MOUSE WHEEL] delta=" << delta
|
|
<< " (" << (delta > 0 ? "UP" : "DOWN") << ")\n";
|
|
}
|
|
else if (msg.topic == "input:keyboard:key") {
|
|
int scancode = msg.data->getInt("scancode", 0);
|
|
bool pressed = msg.data->getBool("pressed", false);
|
|
bool repeat = msg.data->getBool("repeat", false);
|
|
bool shift = msg.data->getBool("shift", false);
|
|
bool ctrl = msg.data->getBool("ctrl", false);
|
|
bool alt = msg.data->getBool("alt", false);
|
|
|
|
const char* keyName = SDL_GetScancodeName(static_cast<SDL_Scancode>(scancode));
|
|
|
|
std::cout << "[KEYBOARD KEY] " << keyName
|
|
<< " " << (pressed ? "PRESSED" : "RELEASED");
|
|
|
|
if (repeat) std::cout << " (REPEAT)";
|
|
if (shift || ctrl || alt) {
|
|
std::cout << " [";
|
|
if (shift) std::cout << "SHIFT ";
|
|
if (ctrl) std::cout << "CTRL ";
|
|
if (alt) std::cout << "ALT";
|
|
std::cout << "]";
|
|
}
|
|
|
|
std::cout << "\n";
|
|
}
|
|
else if (msg.topic == "input:keyboard:text") {
|
|
std::string text = msg.data->getString("text", "");
|
|
std::cout << "[KEYBOARD TEXT] \"" << text << "\"\n";
|
|
}
|
|
}
|
|
|
|
// 4. Cap at ~60 FPS
|
|
SDL_Delay(16);
|
|
|
|
// Print stats every 5 seconds
|
|
uint32_t currentTime = SDL_GetTicks();
|
|
if (currentTime - lastTime >= 5000) {
|
|
auto health = inputModule->getHealthStatus();
|
|
std::cout << "\n--- Stats (5s) ---\n";
|
|
std::cout << "Frames: " << health->getInt("frameCount", 0) << "\n";
|
|
std::cout << "Events processed: " << health->getInt("eventsProcessed", 0) << "\n";
|
|
std::cout << "Events/frame: " << std::fixed << std::setprecision(2)
|
|
<< health->getDouble("eventsPerFrame", 0.0) << "\n";
|
|
std::cout << "Status: " << health->getString("status", "unknown") << "\n";
|
|
std::cout << "-------------------\n\n";
|
|
lastTime = currentTime;
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// Cleanup
|
|
// ========================================
|
|
|
|
std::cout << "\n========================================\n";
|
|
std::cout << "Final stats:\n";
|
|
|
|
auto finalHealth = inputModule->getHealthStatus();
|
|
std::cout << "Total frames: " << finalHealth->getInt("frameCount", 0) << "\n";
|
|
std::cout << "Total events: " << finalHealth->getInt("eventsProcessed", 0) << "\n";
|
|
std::cout << "Avg events/frame: " << std::fixed << std::setprecision(2)
|
|
<< finalHealth->getDouble("eventsPerFrame", 0.0) << "\n";
|
|
|
|
inputModule->shutdown();
|
|
inputLoader.unload();
|
|
|
|
SDL_StopTextInput();
|
|
SDL_DestroyWindow(window);
|
|
SDL_Quit();
|
|
|
|
std::cout << "========================================\n";
|
|
std::cout << "Test completed successfully!\n";
|
|
|
|
return 0;
|
|
}
|