input manager provides ground of truth on device input choise

This commit is contained in:
Adolfo Reyna
2026-01-30 22:07:31 -05:00
parent 436245a7a2
commit f860d4f5e6
10 changed files with 97 additions and 44 deletions

View File

@@ -321,17 +321,17 @@ int main()
InputManager input_manager(touch, &config); InputManager input_manager(touch, &config);
// Create GameLauncher // Create GameLauncher
GameLauncher launcher(V_WIDTH, V_HEIGHT, &renderer, &gui); GameLauncher launcher(V_WIDTH, V_HEIGHT, &renderer, &gui, &input_manager);
// Register available games // Register available games
launcher.register_game("Tic-Tac-Toe", "Classic 2-player game", launcher.register_game("Tic-Tac-Toe", "Classic 2-player game",
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g) -> Game* { [](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
return new TicTacToeGame(w, h, r, g); return new TicTacToeGame(w, h, r, g, im);
}); });
launcher.register_game("Demo Game", "Simple test game", launcher.register_game("Demo Game", "Simple test game",
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g) -> Game* { [](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
return new DemoGame(w, h, r, g); return new DemoGame(w, h, r, g, im);
}); });
// Draw launcher menu // Draw launcher menu
@@ -443,6 +443,13 @@ int main()
current_game = launcher.get_selected_game(); current_game = launcher.get_selected_game();
needs_refresh = current_game->update(input); needs_refresh = current_game->update(input);
// Check if game wants to exit
if (current_game->wants_to_exit()) {
printf("Game requested exit - returning to launcher\n");
launcher.reset();
needs_refresh = true;
}
// Check if player wants to exit (hold for 2+ seconds or special gesture) // Check if player wants to exit (hold for 2+ seconds or special gesture)
// For now, we'll add a simple long-press detection // For now, we'll add a simple long-press detection
if (input.type == INPUT_TOUCH_DOWN) { if (input.type == INPUT_TOUCH_DOWN) {

View File

@@ -4,14 +4,14 @@
// Simple demo game to test the launcher // Simple demo game to test the launcher
#include "demo_game.h" #include "demo_game.h"
#include "board_config.h" #include "input_manager.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
extern Font font_5x5_obj; extern Font font_5x5_obj;
DemoGame::DemoGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui) DemoGame::DemoGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager)
: Game(width, height, renderer, gui), tap_count(0), exit_requested(false) { : Game(width, height, renderer, gui, input_manager), tap_count(0), exit_requested(false) {
} }
void DemoGame::init() { void DemoGame::init() {
@@ -54,11 +54,11 @@ void DemoGame::draw() {
// Instructions // Instructions
if (tap_count < 3) { if (tap_count < 3) {
#ifdef BUTTON_KEY0_PIN if (input_manager->has_buttons()) {
renderer->draw_string(width/2 - 80, y + 80, "Tap or press 3 times", true); renderer->draw_string(width/2 - 80, y + 80, "Tap or press 3 times", true);
#else } else {
renderer->draw_string(width/2 - 80, y + 80, "Tap 3 times to exit", true); renderer->draw_string(width/2 - 80, y + 80, "Tap 3 times to exit", true);
#endif }
} else { } else {
renderer->draw_string(width/2 - 70, y + 80, "Exiting to menu...", true); renderer->draw_string(width/2 - 70, y + 80, "Exiting to menu...", true);
} }

View File

@@ -16,11 +16,12 @@
*/ */
class DemoGame : public Game { class DemoGame : public Game {
public: public:
DemoGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui); DemoGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager);
void init() override; void init() override;
bool update(const InputEvent& event) override; bool update(const InputEvent& event) override;
void draw() override; void draw() override;
bool wants_to_exit() const override { return exit_requested; }
private: private:
int tap_count; int tap_count;

View File

@@ -4,15 +4,15 @@
// Game logic, input handling, and rendering for Tic-Tac-Toe // Game logic, input handling, and rendering for Tic-Tac-Toe
#include "tic_tac_toe.h" #include "tic_tac_toe.h"
#include "board_config.h" #include "input_manager.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
// Font reference from display system // Font reference from display system
extern Font font_5x5_obj; extern Font font_5x5_obj;
TicTacToeGame::TicTacToeGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui) TicTacToeGame::TicTacToeGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager)
: Game(width, height, renderer, gui) { : Game(width, height, renderer, gui, input_manager) {
// Initialize statistics to zero // Initialize statistics to zero
state.x_wins = 0; state.x_wins = 0;
state.o_wins = 0; state.o_wins = 0;
@@ -220,20 +220,20 @@ void TicTacToeGame::draw() {
} else { } else {
renderer->draw_string(20, 40, "TIE GAME!", true); renderer->draw_string(20, 40, "TIE GAME!", true);
} }
#ifdef BUTTON_KEY0_PIN if (input_manager->has_buttons()) {
renderer->draw_string(20, 55, "Touch or KEY0 to restart", true); renderer->draw_string(20, 55, "Touch or KEY0 to restart", true);
#else } else {
renderer->draw_string(20, 55, "Touch to restart", true); renderer->draw_string(20, 55, "Touch to restart", true);
#endif }
} else { } else {
char turn_text[30]; char turn_text[30];
snprintf(turn_text, sizeof(turn_text), "Turn: %s", state.current_player == 1 ? "X" : "O"); snprintf(turn_text, sizeof(turn_text), "Turn: %s", state.current_player == 1 ? "X" : "O");
renderer->draw_string(20, 40, turn_text, true); renderer->draw_string(20, 40, turn_text, true);
#ifdef BUTTON_KEY0_PIN if (input_manager->has_buttons()) {
renderer->draw_string(20, 55, "Touch cell or use keys", true); renderer->draw_string(20, 55, "Touch cell or use keys", true);
#else } else {
renderer->draw_string(20, 55, "Touch cell to play", true); renderer->draw_string(20, 55, "Touch cell to play", true);
#endif }
} }
// Draw game board (use same layout as touch detection!) // Draw game board (use same layout as touch detection!)

View File

@@ -26,8 +26,9 @@ public:
* @param height Display height in pixels * @param height Display height in pixels
* @param renderer Pointer to low-level rendering interface * @param renderer Pointer to low-level rendering interface
* @param gui Pointer to GUI drawing primitives * @param gui Pointer to GUI drawing primitives
* @param input_manager Pointer to input manager for capability queries
*/ */
TicTacToeGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui); TicTacToeGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager);
/** /**
* @brief Initialize game state (reset board, keep statistics) * @brief Initialize game state (reset board, keep statistics)

View File

@@ -12,6 +12,9 @@
#include "display/low_level_render.h" #include "display/low_level_render.h"
#include "display/low_level_gui.h" #include "display/low_level_gui.h"
// Forward declaration
class InputManager;
/** /**
* @brief Abstract base class for all games * @brief Abstract base class for all games
* *
@@ -31,9 +34,10 @@ public:
* @param height Display height in pixels * @param height Display height in pixels
* @param renderer Pointer to low-level rendering interface * @param renderer Pointer to low-level rendering interface
* @param gui Pointer to GUI drawing primitives * @param gui Pointer to GUI drawing primitives
* @param input_manager Pointer to input manager for capability queries
*/ */
Game(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui) Game(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager)
: width(width), height(height), renderer(renderer), gui(gui) { : width(width), height(height), renderer(renderer), gui(gui), input_manager(input_manager) {
} }
/** /**
@@ -69,6 +73,16 @@ public:
*/ */
virtual void draw() = 0; virtual void draw() = 0;
/**
* @brief Check if game wants to exit back to launcher
*
* Games can override this to signal they want to return to the menu.
* Default implementation returns false.
*
* @return true if game wants to exit, false to continue playing
*/
virtual bool wants_to_exit() const { return false; }
protected: protected:
// Display dimensions // Display dimensions
uint16_t width; uint16_t width;
@@ -77,6 +91,9 @@ protected:
// Rendering interfaces (provided by basic1.cpp) // Rendering interfaces (provided by basic1.cpp)
LowLevelRenderer* renderer; LowLevelRenderer* renderer;
LowLevelGUI* gui; LowLevelGUI* gui;
// Input manager for capability queries
InputManager* input_manager;
}; };
#endif // GAME_H #endif // GAME_H

View File

@@ -4,19 +4,19 @@
// Menu system for selecting and launching games // Menu system for selecting and launching games
#include "game_launcher.h" #include "game_launcher.h"
#include "board_config.h" #include "input_manager.h"
#include "display/low_level_render.h" #include "display/low_level_render.h"
#include "display/low_level_gui.h" #include "display/low_level_gui.h"
#include <stdio.h> #include <stdio.h>
GameLauncher::GameLauncher(uint16_t width, uint16_t height, GameLauncher::GameLauncher(uint16_t width, uint16_t height,
LowLevelRenderer* renderer, LowLevelGUI* gui) LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager)
: width(width), height(height), renderer(renderer), gui(gui), : width(width), height(height), renderer(renderer), gui(gui), input_manager(input_manager),
selected_index(0), selected_game(nullptr) { selected_index(0), selected_game(nullptr) {
} }
void GameLauncher::register_game(const char* name, const char* description, void GameLauncher::register_game(const char* name, const char* description,
Game* (*factory)(uint16_t, uint16_t, LowLevelRenderer*, LowLevelGUI*)) { Game* (*factory)(uint16_t, uint16_t, LowLevelRenderer*, LowLevelGUI*, InputManager*)) {
GameEntry entry; GameEntry entry;
entry.name = name; entry.name = name;
entry.description = description; entry.description = description;
@@ -51,11 +51,11 @@ void GameLauncher::draw() {
// Draw instructions at bottom // Draw instructions at bottom
const char* instructions; const char* instructions;
#ifdef BUTTON_KEY0_PIN if (input_manager->has_buttons()) {
instructions = "Touch game or use KEY0/KEY1"; instructions = "Touch game or use KEY0/KEY1";
#else } else {
instructions = "Touch game to play"; instructions = "Touch game to play";
#endif }
renderer->set_font(&font_5x5_obj); renderer->set_font(&font_5x5_obj);
renderer->draw_string_scaled(30, height - 35, instructions, 2); renderer->draw_string_scaled(30, height - 35, instructions, 2);
} }
@@ -75,7 +75,7 @@ bool GameLauncher::update(const InputEvent& event) {
if (event.y >= y - 5 && event.y < y + MENU_ITEM_HEIGHT - 5) { if (event.y >= y - 5 && event.y < y + MENU_ITEM_HEIGHT - 5) {
// Game selected - create instance // Game selected - create instance
printf("Selected game: %s\n", games[i].name); printf("Selected game: %s\n", games[i].name);
selected_game = games[i].factory(width, height, renderer, gui); selected_game = games[i].factory(width, height, renderer, gui, input_manager);
if (selected_game) { if (selected_game) {
selected_game->init(); selected_game->init();
return true; // Signal game selected return true; // Signal game selected
@@ -100,7 +100,7 @@ bool GameLauncher::update(const InputEvent& event) {
// Select current game // Select current game
if (selected_index >= 0 && selected_index < (int)games.size()) { if (selected_index >= 0 && selected_index < (int)games.size()) {
printf("Selected game: %s\n", games[selected_index].name); printf("Selected game: %s\n", games[selected_index].name);
selected_game = games[selected_index].factory(width, height, renderer, gui); selected_game = games[selected_index].factory(width, height, renderer, gui, input_manager);
if (selected_game) { if (selected_game) {
selected_game->init(); selected_game->init();
return true; // Signal game selected return true; // Signal game selected

View File

@@ -14,6 +14,7 @@
// Forward declarations // Forward declarations
class LowLevelRenderer; class LowLevelRenderer;
class LowLevelGUI; class LowLevelGUI;
class InputManager;
/** /**
* @brief Game entry in launcher menu * @brief Game entry in launcher menu
@@ -22,7 +23,7 @@ struct GameEntry {
const char* name; // Display name const char* name; // Display name
const char* description; // Short description const char* description; // Short description
Game* (*factory)(uint16_t width, uint16_t height, Game* (*factory)(uint16_t width, uint16_t height,
LowLevelRenderer* renderer, LowLevelGUI* gui); // Factory function LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager); // Factory function
}; };
/** /**
@@ -42,8 +43,9 @@ public:
* @param height Display height in pixels * @param height Display height in pixels
* @param renderer Pointer to low-level rendering interface * @param renderer Pointer to low-level rendering interface
* @param gui Pointer to GUI drawing primitives * @param gui Pointer to GUI drawing primitives
* @param input_manager Pointer to input manager for capability queries
*/ */
GameLauncher(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui); GameLauncher(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager);
/** /**
* @brief Register a game in the launcher * @brief Register a game in the launcher
@@ -52,7 +54,7 @@ public:
* @param factory Function pointer to create game instance * @param factory Function pointer to create game instance
*/ */
void register_game(const char* name, const char* description, void register_game(const char* name, const char* description,
Game* (*factory)(uint16_t, uint16_t, LowLevelRenderer*, LowLevelGUI*)); Game* (*factory)(uint16_t, uint16_t, LowLevelRenderer*, LowLevelGUI*, InputManager*));
/** /**
* @brief Draw the launcher menu * @brief Draw the launcher menu
@@ -88,6 +90,7 @@ private:
uint16_t height; uint16_t height;
LowLevelRenderer* renderer; LowLevelRenderer* renderer;
LowLevelGUI* gui; LowLevelGUI* gui;
InputManager* input_manager;
std::vector<GameEntry> games; std::vector<GameEntry> games;
int selected_index; // Currently highlighted game int selected_index; // Currently highlighted game

View File

@@ -27,6 +27,18 @@ InputManager::InputManager(LowLevelTouch* touch, const GameConfig* config)
: touch(touch), config(config) { : touch(touch), config(config) {
} }
bool InputManager::has_touch() const {
return touch != nullptr;
}
bool InputManager::has_buttons() const {
#ifdef BUTTON_KEY0_PIN
return true;
#else
return false;
#endif
}
InputEvent InputManager::process_touch_input(uint32_t* last_time) { InputEvent InputManager::process_touch_input(uint32_t* last_time) {
InputEvent event = {INPUT_NONE, 0, 0, 0, 0, 0, false}; InputEvent event = {INPUT_NONE, 0, 0, 0, 0, 0, false};

View File

@@ -56,6 +56,18 @@ public:
*/ */
static const char* get_gesture_name(uint8_t gesture_code); static const char* get_gesture_name(uint8_t gesture_code);
/**
* @brief Check if touch input is available
* @return true if touch controller is present, false otherwise
*/
bool has_touch() const;
/**
* @brief Check if hardware buttons are available
* @return true if physical buttons are present, false otherwise
*/
bool has_buttons() const;
private: private:
LowLevelTouch* touch; LowLevelTouch* touch;
const GameConfig* config; const GameConfig* config;