#include "menu.h" #include "nes.h" #include "action.h" #include "port.h" #include "draw.h" static nese_Action wait_for_input(void* plat, uint8_t* new_buttons) { nes_Input input = {0}; nese_Action action = nese_update_input(plat, &input); uint8_t changed = 0; while (0 == changed && Action_OK == action) { uint8_t buttons = input.gamepads[0].buttons; // Sleep is implicit in nese_update_input action = nese_update_input(plat, &input); changed = (~buttons & input.gamepads[0].buttons); } if (NULL != new_buttons) *new_buttons = input.gamepads[0].buttons; return action; } static nese_Action wait_for_input_quiet(void* plat) { nes_Input input = {0}; nese_Action action = nese_update_input(plat, &input); uint8_t buttons = input.gamepads[0].buttons; while (Action_Quit != action && buttons) { action = nese_update_input(plat, &input); buttons = input.gamepads[0].buttons; } return action; } nese_Action modal_popup(void* plat, const char* message, uint32_t color) { int w = 0; int h = 0; nese_text_size(plat, message, &w, &h); int x = ((int)nes_ppu_render_w - w) / 2; int y = ((int)nes_ppu_render_h - h) / 2; if (x < 5) x = 5; if (y < 5) y = 5; nese_Action action = 0; uint8_t buttons = 0; while (0 == action && 0 == buttons) { nese_draw_begin(plat); nese_draw_text(plat, message, x, y, color); nese_draw_finish(plat); action = wait_for_input(plat, &buttons); } return action; } static void fix_filename(char* dst, int n, const char* src) { for ( int i = 0; i < n && *src && '.' != *src; ++i, ++dst, ++src) { *dst = ('_' == *src ? ' ' : *src); } *dst = '\0'; } static inline int n_visible(void) { return ((nes_ppu_render_h - 20) / 11); } static const uint32_t menu_colors[6] = { color_red, color_orange, color_yellow, color_green, color_blue, color_purple, }; static void show_menu(void* plat, const Menu_State* menu, const File_List* files, int x) { nese_draw_begin(plat); int bottom = menu->top + n_visible() - 1; int max = n_visible(); if (max > files->count) max = files->count; int y = (nes_ppu_render_h - (max * 11)) / 2; for ( int n = menu->top; n < files->count && n <= bottom; ++n, y += 11 ) { char filename[100]; fix_filename(filename, sizeof(filename) - 1, files->files[n]); nese_draw_text( plat, ( (menu->top == n && 0 != menu->top) || (bottom == n && files->count - 1 > bottom) ) ? "..." : filename, x, y, (menu->cursor == n) ? color_white : menu_colors[n % 6] ); if (menu->cursor == n) { nese_draw_text(plat, ">", x - 10, y, color_white); } } nese_draw_finish(plat); } nese_Action run_menu(void* plat, Menu_State* state, const File_List* files, int x) { Menu_State menu = {0}; if (NULL != state) { menu = *state; if (menu.cursor < 0) { menu.cursor = 0; } else if (menu.cursor >= files->count) { menu.cursor = files->count - 1; } } nese_Action action = Action_OK; while (Action_OK == action) { // Scrolling (do this first to ensure menu is valid) const int visible = n_visible() - 1; menu.top = menu.cursor - (visible / 2); if (menu.top <= 0) { // We use <= so we don't readjust the top from 0 menu.top = 0; } else if (menu.top + visible >= files->count) { menu.top = (files->count - 1) - visible; } show_menu(plat, &menu, files, x); uint8_t buttons = 0; action = wait_for_input(plat, &buttons); if (Action_Menu == action) { action = Action_Cancel; } else if (Action_OK == action) { if (buttons & (1 << Button_B)) { action = Action_Cancel; break; } else if (buttons & ( (1 << Button_A) | (1 << Button_Start) )) { // Select break; } else if (buttons & (1 << Button_Up)) { if (menu.cursor > 0) --menu.cursor; } else if (buttons & ( (1 << Button_Down) | (1 << Button_Select) )) { if (menu.cursor < (files->count - 1)) { ++menu.cursor; } else if (buttons & (1 << Button_Select)) { // Wrap around on Select menu.cursor = 0; } } } else if (Action_Quit != action && Action_Cancel != action) { // Ignore anything that isn't "Menu", "Cancel" or "Quit" action = Action_OK; } } if (NULL != state) *state = menu; wait_for_input_quiet(plat); return action; }