diff --git a/SDL/font.c b/SDL/font.c index 6354818..93f3fa9 100644 --- a/SDL/font.c +++ b/SDL/font.c @@ -967,13 +967,13 @@ uint8_t font[] = { /* CTRL symbol */ - _, X, X, X, _, X, + _, X, X, _, _, X, + X, _, _, X, _, _, X, _, _, _, _, _, X, _, _, _, _, _, X, _, _, _, _, _, - X, _, _, _, _, _, - X, _, _, _, _, _, - _, X, X, X, _, _, + X, _, _, X, _, _, + _, X, X, _, _, _, _, _, _, _, _, _, X, X, _, X, X, X, diff --git a/SDL/gui.c b/SDL/gui.c index 1254d20..bb5e206 100644 --- a/SDL/gui.c +++ b/SDL/gui.c @@ -11,10 +11,8 @@ SDL_Window *window = NULL; SDL_Renderer *renderer = NULL; SDL_Texture *texture = NULL; SDL_PixelFormat *pixel_format = NULL; -enum scaling_mode scaling_mode = GB_SDL_SCALING_INTEGER_FACTOR; enum pending_command pending_command; unsigned command_parameter; -GB_color_correction_mode_t color_correction_mode = GB_COLOR_CORRECTION_EMULATE_HARDWARE; #ifdef __APPLE__ #define MODIFIER_NAME " " CMD_STRING @@ -22,36 +20,39 @@ GB_color_correction_mode_t color_correction_mode = GB_COLOR_CORRECTION_EMULATE_H #define MODIFIER_NAME CTRL_STRING #endif +configuration_t configuration = +{ + { SDL_SCANCODE_RIGHT, + SDL_SCANCODE_LEFT, + SDL_SCANCODE_UP, + SDL_SCANCODE_DOWN, + SDL_SCANCODE_X, + SDL_SCANCODE_Z, + SDL_SCANCODE_BACKSPACE, + SDL_SCANCODE_RETURN, + SDL_SCANCODE_SPACE + }, + GB_COLOR_CORRECTION_EMULATE_HARDWARE, + GB_SDL_SCALING_INTEGER_FACTOR, +}; + static const char *help[] ={ "Drop a GB or GBC ROM\n" "file to play.\n" "\n" - -"Controls:\n" -" D-Pad: Arrow Keys\n" -" A: X\n" -" B: Z\n" -" Start: Enter\n" -" Select: Backspace\n" -"\n" -" Turbo: Space\n" -" Menu: Escape\n", -"Keyboard Shortcuts: \n" +"Keyboard Shortcuts:\n" +" Open Menu: Escape\n" " Reset: " MODIFIER_NAME "+R\n" " Pause: " MODIFIER_NAME "+P\n" " Toggle DMG/CGB: " MODIFIER_NAME "+T\n" -"\n" " Save state: " MODIFIER_NAME "+(0-9)\n" " Load state: " MODIFIER_NAME "+" SHIFT_STRING "+(0-9)\n" -"\n" #ifdef __APPLE__ " Mute/Unmute: " MODIFIER_NAME "+" SHIFT_STRING "+M\n" #else " Mute/Unmute: " MODIFIER_NAME "+M\n" #endif -" Cycle scaling modes: Tab" -"\n" " Break Debugger: " CTRL_STRING "+C" }; @@ -62,12 +63,12 @@ void update_viewport(void) double x_factor = win_width / 160.0; double y_factor = win_height / 144.0; - if (scaling_mode == GB_SDL_SCALING_INTEGER_FACTOR) { + if (configuration.scaling_mode == GB_SDL_SCALING_INTEGER_FACTOR) { x_factor = (int)(x_factor); y_factor = (int)(y_factor); } - if (scaling_mode != GB_SDL_SCALING_ENTIRE_WINDOW) { + if (configuration.scaling_mode != GB_SDL_SCALING_ENTIRE_WINDOW) { if (x_factor > y_factor) { x_factor = y_factor; } @@ -160,9 +161,9 @@ static void draw_text_centered(uint32_t *buffer, unsigned y, const char *string, struct menu_item { const char *string; - void (*handler)(void); - const char *(*value_getter)(void); - void (*backwards_handler)(void); + void (*handler)(unsigned); + const char *(*value_getter)(unsigned); + void (*backwards_handler)(unsigned); }; static const struct menu_item *current_menu = NULL; static const struct menu_item *root_menu = NULL; @@ -172,25 +173,28 @@ static enum { SHOWING_DROP_MESSAGE, SHOWING_MENU, SHOWING_HELP, + WAITING_FOR_KEY, } gui_state; -static void item_exit(void) +static void item_exit(unsigned index) { pending_command = GB_SDL_QUIT_COMMAND; } static unsigned current_help_page = 0; -static void item_help(void) +static void item_help(unsigned index) { current_help_page = 0; gui_state = SHOWING_HELP; } -static void enter_graphics_menu(void); +static void enter_graphics_menu(unsigned index); +static void enter_controls_menu(unsigned index); static const struct menu_item paused_menu[] = { {"Resume", NULL}, {"Graphic Options", enter_graphics_menu}, + {"Controls", enter_controls_menu}, {"Help", item_help}, {"Exit", item_exit}, {NULL,} @@ -198,26 +202,29 @@ static const struct menu_item paused_menu[] = { static const struct menu_item nonpaused_menu[] = { {"Graphic Options", enter_graphics_menu}, + {"Controls", enter_controls_menu}, {"Help", item_help}, {"Exit", item_exit}, {NULL,} }; -const char *current_scaling_mode(void) +const char *current_scaling_mode(unsigned index) { - return (const char *[]){"Fill Entire Window", "Retain Aspect Ratio", "Retain Integer Factor"}[scaling_mode]; + return (const char *[]){"Fill Entire Window", "Retain Aspect Ratio", "Retain Integer Factor"} + [configuration.scaling_mode]; } -const char *current_color_correction_mode(void) +const char *current_color_correction_mode(unsigned index) { - return (const char *[]){"Disabled", "Correct Color Curves", "Emulate Hardware", "Preserve Brightness"}[color_correction_mode]; + return (const char *[]){"Disabled", "Correct Color Curves", "Emulate Hardware", "Preserve Brightness"} + [configuration.color_correction_mode]; } -void cycle_scaling(void) +void cycle_scaling(unsigned index) { - scaling_mode++; - if (scaling_mode == GB_SDL_SCALING_MAX) { - scaling_mode = 0; + configuration.scaling_mode++; + if (configuration.scaling_mode == GB_SDL_SCALING_MAX) { + configuration.scaling_mode = 0; } update_viewport(); SDL_RenderClear(renderer); @@ -225,13 +232,13 @@ void cycle_scaling(void) SDL_RenderPresent(renderer); } -void cycle_scaling_backwards(void) +void cycle_scaling_backwards(unsigned index) { - if (scaling_mode == 0) { - scaling_mode = GB_SDL_SCALING_MAX - 1; + if (configuration.scaling_mode == 0) { + configuration.scaling_mode = GB_SDL_SCALING_MAX - 1; } else { - scaling_mode--; + configuration.scaling_mode--; } update_viewport(); SDL_RenderClear(renderer); @@ -239,28 +246,28 @@ void cycle_scaling_backwards(void) SDL_RenderPresent(renderer); } -static void cycle_color_correction(void) +static void cycle_color_correction(unsigned index) { - if (color_correction_mode == GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS) { - color_correction_mode = GB_COLOR_CORRECTION_DISABLED; + if (configuration.color_correction_mode == GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS) { + configuration.color_correction_mode = GB_COLOR_CORRECTION_DISABLED; } else { - color_correction_mode++; + configuration.color_correction_mode++; } } -static void cycle_color_correction_backwards(void) +static void cycle_color_correction_backwards(unsigned index) { - if (color_correction_mode == GB_COLOR_CORRECTION_DISABLED) { - color_correction_mode = GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS; + if (configuration.color_correction_mode == GB_COLOR_CORRECTION_DISABLED) { + configuration.color_correction_mode = GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS; } else { - color_correction_mode--; + configuration.color_correction_mode--; } } -static void return_to_root_menu(void) +static void return_to_root_menu(unsigned index) { current_menu = root_menu; current_selection = 0; @@ -273,12 +280,42 @@ static const struct menu_item graphics_menu[] = { {NULL,} }; -static void enter_graphics_menu(void) +static void enter_graphics_menu(unsigned index) { current_menu = graphics_menu; current_selection = 0; } +static const char *key_name(unsigned index) +{ + return SDL_GetScancodeName(configuration.keys[index]); +} + +static void modify_key(unsigned index) +{ + gui_state = WAITING_FOR_KEY; +} + +static const struct menu_item controls_menu[] = { + {"Right:", modify_key, key_name,}, + {"Left:", modify_key, key_name,}, + {"Up:", modify_key, key_name,}, + {"Down:", modify_key, key_name,}, + {"A:", modify_key, key_name,}, + {"B:", modify_key, key_name,}, + {"Select:", modify_key, key_name,}, + {"Start:", modify_key, key_name,}, + {"Turbo:", modify_key, key_name,}, + {"Back", return_to_root_menu}, + {NULL,} +}; + +static void enter_controls_menu(unsigned index) +{ + current_menu = controls_menu; + current_selection = 0; +} + extern void set_filename(const char *new_filename, bool new_should_free); void run_gui(bool is_running) { @@ -297,45 +334,12 @@ void run_gui(bool is_running) } uint32_t pixels[160 * 144]; - SDL_Event event; + SDL_Event event = {0,}; gui_state = is_running? SHOWING_MENU : SHOWING_DROP_MESSAGE; bool should_render = true; current_menu = root_menu = is_running? paused_menu : nonpaused_menu; current_selection = 0; - while (SDL_WaitEvent(&event)) { - if (should_render) { - should_render = false; - memcpy(pixels, converted_background->pixels, sizeof(pixels)); - - switch (gui_state) { - case SHOWING_DROP_MESSAGE: - draw_text_centered(pixels, 116, "Drop a GB or GBC", gui_palette_native[3], gui_palette_native[0], false); - draw_text_centered(pixels, 128, "file to play", gui_palette_native[3], gui_palette_native[0], false); - break; - case SHOWING_MENU: - draw_text_centered(pixels, 16, "SameBoy", gui_palette_native[3], gui_palette_native[0], false); - unsigned i = 0, y = 40; - for (const struct menu_item *item = current_menu; item->string; item++, i++) { - draw_text_centered(pixels, y, item->string, gui_palette_native[3], gui_palette_native[0], - i == current_selection && !item->value_getter ? DECORATION_SELECTION : DECORATION_NONE); - y += 12; - if (item->value_getter) { - draw_text_centered(pixels, y, item->value_getter(), gui_palette_native[3], gui_palette_native[0], - i == current_selection ? DECORATION_ARROWS : DECORATION_NONE); - y += 12; - } - } - break; - case SHOWING_HELP: - draw_text(pixels, 2, 2, help[current_help_page], gui_palette_native[3], gui_palette_native[0]); - break; - } - - SDL_UpdateTexture(texture, NULL, pixels, 160 * sizeof (uint32_t)); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, NULL, NULL); - SDL_RenderPresent(renderer); - } + do { switch (event.type) { case SDL_QUIT: { if (!is_running) { @@ -362,10 +366,7 @@ void run_gui(bool is_running) return; } case SDL_KEYDOWN: - if (event.key.keysym.scancode == SDL_SCANCODE_TAB) { - cycle_scaling(); - } - else if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) { + if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) { if (is_running) { return; } @@ -376,6 +377,8 @@ void run_gui(bool is_running) else if (gui_state == SHOWING_MENU) { gui_state = SHOWING_DROP_MESSAGE; } + current_selection = 0; + current_menu = root_menu; should_render = true; } } @@ -391,7 +394,7 @@ void run_gui(bool is_running) } else if (event.key.keysym.scancode == SDL_SCANCODE_RETURN) { if (current_menu[current_selection].handler) { - current_menu[current_selection].handler(); + current_menu[current_selection].handler(current_selection); if (pending_command) { if (!is_running && pending_command == GB_SDL_QUIT_COMMAND) { exit(0); @@ -405,22 +408,74 @@ void run_gui(bool is_running) } } else if (event.key.keysym.scancode == SDL_SCANCODE_RIGHT && current_menu[current_selection].backwards_handler) { - current_menu[current_selection].handler(); + current_menu[current_selection].handler(current_selection); should_render = true; } else if (event.key.keysym.scancode == SDL_SCANCODE_LEFT && current_menu[current_selection].backwards_handler) { - current_menu[current_selection].backwards_handler(); + current_menu[current_selection].backwards_handler(current_selection); should_render = true; } } - else if(gui_state == SHOWING_HELP) { + else if (gui_state == SHOWING_HELP) { current_help_page++; if (current_help_page == sizeof(help) / sizeof(help[0])) { gui_state = SHOWING_MENU; } should_render = true; } + else if (gui_state == WAITING_FOR_KEY) { + configuration.keys[current_selection] = event.key.keysym.scancode; + gui_state = SHOWING_MENU; + should_render = true; + } break; } - } + + if (should_render) { + should_render = false; + memcpy(pixels, converted_background->pixels, sizeof(pixels)); + + switch (gui_state) { + case SHOWING_DROP_MESSAGE: + draw_text_centered(pixels, 116, "Drop a GB or GBC", gui_palette_native[3], gui_palette_native[0], false); + draw_text_centered(pixels, 128, "file to play", gui_palette_native[3], gui_palette_native[0], false); + break; + case SHOWING_MENU: + draw_text_centered(pixels, 8, "SameBoy", gui_palette_native[3], gui_palette_native[0], false); + unsigned i = 0, y = 24; + for (const struct menu_item *item = current_menu; item->string; item++, i++) { + if (item->value_getter && !item->backwards_handler) { + char line[25]; + snprintf(line, sizeof(line), "%s%*s", item->string, 24 - (int)strlen(item->string), item->value_getter(i)); + draw_text_centered(pixels, y, line, gui_palette_native[3], gui_palette_native[0], + i == current_selection ? DECORATION_SELECTION : DECORATION_NONE); + y += 12; + + } + else { + draw_text_centered(pixels, y, item->string, gui_palette_native[3], gui_palette_native[0], + i == current_selection && !item->value_getter ? DECORATION_SELECTION : DECORATION_NONE); + y += 12; + if (item->value_getter) { + draw_text_centered(pixels, y, item->value_getter(i), gui_palette_native[3], gui_palette_native[0], + i == current_selection ? DECORATION_ARROWS : DECORATION_NONE); + y += 12; + } + } + } + break; + case SHOWING_HELP: + draw_text(pixels, 2, 2, help[current_help_page], gui_palette_native[3], gui_palette_native[0]); + break; + case WAITING_FOR_KEY: + draw_text_centered(pixels, 68, "Press a Key", gui_palette_native[3], gui_palette_native[0], DECORATION_NONE); + break; + } + + SDL_UpdateTexture(texture, NULL, pixels, 160 * sizeof (uint32_t)); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); + } + } while (SDL_WaitEvent(&event)); } diff --git a/SDL/gui.h b/SDL/gui.h index 9182d10..3f82420 100644 --- a/SDL/gui.h +++ b/SDL/gui.h @@ -16,7 +16,6 @@ enum scaling_mode { GB_SDL_SCALING_MAX, }; -extern enum scaling_mode scaling_mode; enum pending_command { GB_SDL_NO_COMMAND, @@ -30,10 +29,15 @@ enum pending_command { extern enum pending_command pending_command; extern unsigned command_parameter; -extern GB_color_correction_mode_t color_correction_mode; +typedef struct { + SDL_Scancode keys[9]; + GB_color_correction_mode_t color_correction_mode; + enum scaling_mode scaling_mode; +} configuration_t; + +extern configuration_t configuration; void update_viewport(void); -void cycle_scaling(void); void run_gui(bool is_running); #endif diff --git a/SDL/main.c b/SDL/main.c index a047978..8bb23ba 100755 --- a/SDL/main.c +++ b/SDL/main.c @@ -102,7 +102,7 @@ static void handle_events(GB_gameboy_t *gb) switch (event.key.keysym.scancode) { case SDL_SCANCODE_ESCAPE: run_gui(true); - GB_set_color_correction_mode(gb, color_correction_mode); + GB_set_color_correction_mode(gb, configuration.color_correction_mode); break; case SDL_SCANCODE_C: @@ -142,10 +142,6 @@ static void handle_events(GB_gameboy_t *gb) } break; - case SDL_SCANCODE_TAB: - cycle_scaling(); - break; - default: /* Save states */ if (event.key.keysym.scancode >= SDL_SCANCODE_0 && event.key.keysym.scancode <= SDL_SCANCODE_9) { @@ -163,34 +159,15 @@ static void handle_events(GB_gameboy_t *gb) break; } case SDL_KEYUP: // Fallthrough - switch (event.key.keysym.scancode) { - case SDL_SCANCODE_RIGHT: - GB_set_key_state(gb, GB_KEY_RIGHT, event.type == SDL_KEYDOWN); - break; - case SDL_SCANCODE_LEFT: - GB_set_key_state(gb, GB_KEY_LEFT, event.type == SDL_KEYDOWN); - break; - case SDL_SCANCODE_UP: - GB_set_key_state(gb, GB_KEY_UP, event.type == SDL_KEYDOWN); - break; - case SDL_SCANCODE_DOWN: - GB_set_key_state(gb, GB_KEY_DOWN, event.type == SDL_KEYDOWN); - break; - case SDL_SCANCODE_X: - GB_set_key_state(gb, GB_KEY_A, event.type == SDL_KEYDOWN); - break; - case SDL_SCANCODE_Z: - GB_set_key_state(gb, GB_KEY_B, event.type == SDL_KEYDOWN); - break; - case SDL_SCANCODE_BACKSPACE: - GB_set_key_state(gb, GB_KEY_SELECT, event.type == SDL_KEYDOWN); - break; - case SDL_SCANCODE_RETURN: - GB_set_key_state(gb, GB_KEY_START, event.type == SDL_KEYDOWN); - break; - case SDL_SCANCODE_SPACE: - GB_set_turbo_mode(gb, event.type == SDL_KEYDOWN, false); - break; + if (event.key.keysym.scancode == configuration.keys[8]) { + GB_set_turbo_mode(gb, event.type == SDL_KEYDOWN, false); + } + else { + for (unsigned i = 0; i < GB_KEY_MAX; i++) { + if (event.key.keysym.scancode == configuration.keys[i]) { + GB_set_key_state(gb, i, event.type == SDL_KEYDOWN); + } + } } break; default: @@ -297,7 +274,7 @@ restart: GB_set_pixels_output(&gb, pixels); GB_set_rgb_encode_callback(&gb, rgb_encode); GB_set_sample_rate(&gb, have_aspec.freq); - GB_set_color_correction_mode(&gb, color_correction_mode); + GB_set_color_correction_mode(&gb, configuration.color_correction_mode); } bool error = false;