#include <stdio.h> #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" #include "esp_log.h" #include "game_menu.h" #include "display_task.h" // To update the display with menu text #include "game_framework.h" // For OLED_WIDTH, OLED_HEIGHT #define MENU_TAG "GAME_MENU" // Define these locally or ensure they are accessible from display_task.h // Assuming N_DISPLAY_LINES and MAX_LINE_LENGTH are consistent with display_task.c #define N_DISPLAY_LINES (OLED_HEIGHT / 8) #define MAX_LINE_LENGTH (OLED_WIDTH / 8) + 1 // Helper function to draw the menu on the display static void draw_menu(const menu_item_t menu_items[], size_t num_items, int selected_index) { // Array to hold all the strings for the display char menu_display_buffers[N_DISPLAY_LINES][MAX_LINE_LENGTH]; const char *lines_to_send[N_DISPLAY_LINES]; // Initialize all buffers to empty strings (filled with spaces for OLED clearing effect) for (int i = 0; i < N_DISPLAY_LINES; i++) { memset(menu_display_buffers[i], ' ', MAX_LINE_LENGTH - 1); menu_display_buffers[i][MAX_LINE_LENGTH - 1] = '\0'; lines_to_send[i] = menu_display_buffers[i]; // Point to the buffer for sending } // Set menu title on the first line strncpy(menu_display_buffers[0], " SELECT GAME: ", MAX_LINE_LENGTH - 1); menu_display_buffers[0][MAX_LINE_LENGTH - 1] = '\0'; // Ensure null termination // Populate menu items starting from line 2 for (size_t i = 0; i < num_items; i++) { int line_num = 2 + i; // Start displaying items from line 2 if (line_num < N_DISPLAY_LINES) { // Ensure we don't write past screen const char *item_name = menu_items[i].name; size_t name_len = strlen(item_name); // Calculate starting position to center text int start_col = (MAX_LINE_LENGTH - 1 - name_len) / 2; if (start_col < 0) start_col = 0; // Prevent negative start_col for long names // Add selector if this is the selected item if (i == selected_index) { if (start_col >= 2) { // Ensure space for "> " menu_display_buffers[line_num][start_col - 2] = '>'; menu_display_buffers[line_num][start_col - 1] = ' '; } else { // If not enough space, just put '>' at start menu_display_buffers[line_num][0] = '>'; start_col = 1; // Adjust text start } } // Copy item name into buffer strncpy(&menu_display_buffers[line_num][start_col], item_name, MAX_LINE_LENGTH - 1 - start_col); menu_display_buffers[line_num][MAX_LINE_LENGTH - 1] = '\0'; // Ensure null termination } } // Send all prepared lines to the display task at once display_update_text(lines_to_send, N_DISPLAY_LINES); } const menu_item_t* run_game_menu(const menu_item_t menu_items[], size_t num_items, QueueHandle_t button_queue) { if (num_items == 0) { ESP_LOGE(MENU_TAG, "No menu items provided!"); return NULL; } if (button_queue == NULL) { ESP_LOGE(MENU_TAG, "Button queue is NULL! Cannot navigate menu."); return NULL; } int selected_index = 0; draw_menu(menu_items, num_items, selected_index); // Static variables to track previous button states for edge detection static bool left_button_pressed_prev = false; static bool right_button_pressed_prev = false; while (true) { int input_signal = 0; // -1 for left, 1 for right, 0 for no movement // Wait indefinitely for a button press if (xQueueReceive(button_queue, &input_signal, portMAX_DELAY) == pdTRUE) { ESP_LOGI(MENU_TAG, "Menu input received: %d", input_signal); // Determine current button states from the continuous input signal bool left_button_now_pressed = (input_signal == -1); bool right_button_now_pressed = (input_signal == 1); // Detect rising edge (button press event) bool left_press_event = left_button_now_pressed && !left_button_pressed_prev; bool right_press_event = right_button_now_pressed && !right_button_pressed_prev; if (left_press_event) { // Left button pressed: Move cursor DOWN selected_index++; if (selected_index >= num_items) { selected_index = 0; // Wrap around to the first item } draw_menu(menu_items, num_items, selected_index); } else if (right_press_event) { // Right button pressed: ENTER/SELECT game ESP_LOGI(MENU_TAG, "Game selected: %s", menu_items[selected_index].name); // Update previous button states before returning to prevent re-trigger on next menu entry left_button_pressed_prev = left_button_now_pressed; right_button_pressed_prev = right_button_now_pressed; return &menu_items[selected_index]; // Return pointer to the selected menu_item_t } // Update previous button states for the next iteration left_button_pressed_prev = left_button_now_pressed; right_button_pressed_prev = right_button_now_pressed; } } }