/** * 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 #include #include #include #include #include "modules/InputModule/InputModule.h" #include #include 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 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(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(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; }