Added configurable controls

This commit is contained in:
Lior Halphon 2017-12-22 22:25:53 +02:00
parent 8e61098fec
commit f48194f480
4 changed files with 168 additions and 132 deletions

View File

@ -967,13 +967,13 @@ uint8_t font[] = {
/* CTRL symbol */ /* 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, _, X, X, X, X, X, _, X, X, X,

237
SDL/gui.c
View File

@ -11,10 +11,8 @@ SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL; SDL_Renderer *renderer = NULL;
SDL_Texture *texture = NULL; SDL_Texture *texture = NULL;
SDL_PixelFormat *pixel_format = NULL; SDL_PixelFormat *pixel_format = NULL;
enum scaling_mode scaling_mode = GB_SDL_SCALING_INTEGER_FACTOR;
enum pending_command pending_command; enum pending_command pending_command;
unsigned command_parameter; unsigned command_parameter;
GB_color_correction_mode_t color_correction_mode = GB_COLOR_CORRECTION_EMULATE_HARDWARE;
#ifdef __APPLE__ #ifdef __APPLE__
#define MODIFIER_NAME " " CMD_STRING #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 #define MODIFIER_NAME CTRL_STRING
#endif #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[] ={ static const char *help[] ={
"Drop a GB or GBC ROM\n" "Drop a GB or GBC ROM\n"
"file to play.\n" "file to play.\n"
"\n" "\n"
"Keyboard Shortcuts:\n"
"Controls:\n" " Open Menu: Escape\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"
" Reset: " MODIFIER_NAME "+R\n" " Reset: " MODIFIER_NAME "+R\n"
" Pause: " MODIFIER_NAME "+P\n" " Pause: " MODIFIER_NAME "+P\n"
" Toggle DMG/CGB: " MODIFIER_NAME "+T\n" " Toggle DMG/CGB: " MODIFIER_NAME "+T\n"
"\n"
" Save state: " MODIFIER_NAME "+(0-9)\n" " Save state: " MODIFIER_NAME "+(0-9)\n"
" Load state: " MODIFIER_NAME "+" SHIFT_STRING "+(0-9)\n" " Load state: " MODIFIER_NAME "+" SHIFT_STRING "+(0-9)\n"
"\n"
#ifdef __APPLE__ #ifdef __APPLE__
" Mute/Unmute: " MODIFIER_NAME "+" SHIFT_STRING "+M\n" " Mute/Unmute: " MODIFIER_NAME "+" SHIFT_STRING "+M\n"
#else #else
" Mute/Unmute: " MODIFIER_NAME "+M\n" " Mute/Unmute: " MODIFIER_NAME "+M\n"
#endif #endif
" Cycle scaling modes: Tab"
"\n"
" Break Debugger: " CTRL_STRING "+C" " Break Debugger: " CTRL_STRING "+C"
}; };
@ -62,12 +63,12 @@ void update_viewport(void)
double x_factor = win_width / 160.0; double x_factor = win_width / 160.0;
double y_factor = win_height / 144.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); x_factor = (int)(x_factor);
y_factor = (int)(y_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) { if (x_factor > y_factor) {
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 { struct menu_item {
const char *string; const char *string;
void (*handler)(void); void (*handler)(unsigned);
const char *(*value_getter)(void); const char *(*value_getter)(unsigned);
void (*backwards_handler)(void); void (*backwards_handler)(unsigned);
}; };
static const struct menu_item *current_menu = NULL; static const struct menu_item *current_menu = NULL;
static const struct menu_item *root_menu = NULL; static const struct menu_item *root_menu = NULL;
@ -172,25 +173,28 @@ static enum {
SHOWING_DROP_MESSAGE, SHOWING_DROP_MESSAGE,
SHOWING_MENU, SHOWING_MENU,
SHOWING_HELP, SHOWING_HELP,
WAITING_FOR_KEY,
} gui_state; } gui_state;
static void item_exit(void) static void item_exit(unsigned index)
{ {
pending_command = GB_SDL_QUIT_COMMAND; pending_command = GB_SDL_QUIT_COMMAND;
} }
static unsigned current_help_page = 0; static unsigned current_help_page = 0;
static void item_help(void) static void item_help(unsigned index)
{ {
current_help_page = 0; current_help_page = 0;
gui_state = SHOWING_HELP; 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[] = { static const struct menu_item paused_menu[] = {
{"Resume", NULL}, {"Resume", NULL},
{"Graphic Options", enter_graphics_menu}, {"Graphic Options", enter_graphics_menu},
{"Controls", enter_controls_menu},
{"Help", item_help}, {"Help", item_help},
{"Exit", item_exit}, {"Exit", item_exit},
{NULL,} {NULL,}
@ -198,26 +202,29 @@ static const struct menu_item paused_menu[] = {
static const struct menu_item nonpaused_menu[] = { static const struct menu_item nonpaused_menu[] = {
{"Graphic Options", enter_graphics_menu}, {"Graphic Options", enter_graphics_menu},
{"Controls", enter_controls_menu},
{"Help", item_help}, {"Help", item_help},
{"Exit", item_exit}, {"Exit", item_exit},
{NULL,} {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++; configuration.scaling_mode++;
if (scaling_mode == GB_SDL_SCALING_MAX) { if (configuration.scaling_mode == GB_SDL_SCALING_MAX) {
scaling_mode = 0; configuration.scaling_mode = 0;
} }
update_viewport(); update_viewport();
SDL_RenderClear(renderer); SDL_RenderClear(renderer);
@ -225,13 +232,13 @@ void cycle_scaling(void)
SDL_RenderPresent(renderer); SDL_RenderPresent(renderer);
} }
void cycle_scaling_backwards(void) void cycle_scaling_backwards(unsigned index)
{ {
if (scaling_mode == 0) { if (configuration.scaling_mode == 0) {
scaling_mode = GB_SDL_SCALING_MAX - 1; configuration.scaling_mode = GB_SDL_SCALING_MAX - 1;
} }
else { else {
scaling_mode--; configuration.scaling_mode--;
} }
update_viewport(); update_viewport();
SDL_RenderClear(renderer); SDL_RenderClear(renderer);
@ -239,28 +246,28 @@ void cycle_scaling_backwards(void)
SDL_RenderPresent(renderer); 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) { if (configuration.color_correction_mode == GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS) {
color_correction_mode = GB_COLOR_CORRECTION_DISABLED; configuration.color_correction_mode = GB_COLOR_CORRECTION_DISABLED;
} }
else { 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) { if (configuration.color_correction_mode == GB_COLOR_CORRECTION_DISABLED) {
color_correction_mode = GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS; configuration.color_correction_mode = GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS;
} }
else { 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_menu = root_menu;
current_selection = 0; current_selection = 0;
@ -273,12 +280,42 @@ static const struct menu_item graphics_menu[] = {
{NULL,} {NULL,}
}; };
static void enter_graphics_menu(void) static void enter_graphics_menu(unsigned index)
{ {
current_menu = graphics_menu; current_menu = graphics_menu;
current_selection = 0; 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); extern void set_filename(const char *new_filename, bool new_should_free);
void run_gui(bool is_running) void run_gui(bool is_running)
{ {
@ -297,45 +334,12 @@ void run_gui(bool is_running)
} }
uint32_t pixels[160 * 144]; uint32_t pixels[160 * 144];
SDL_Event event; SDL_Event event = {0,};
gui_state = is_running? SHOWING_MENU : SHOWING_DROP_MESSAGE; gui_state = is_running? SHOWING_MENU : SHOWING_DROP_MESSAGE;
bool should_render = true; bool should_render = true;
current_menu = root_menu = is_running? paused_menu : nonpaused_menu; current_menu = root_menu = is_running? paused_menu : nonpaused_menu;
current_selection = 0; current_selection = 0;
while (SDL_WaitEvent(&event)) { do {
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);
}
switch (event.type) { switch (event.type) {
case SDL_QUIT: { case SDL_QUIT: {
if (!is_running) { if (!is_running) {
@ -362,10 +366,7 @@ void run_gui(bool is_running)
return; return;
} }
case SDL_KEYDOWN: case SDL_KEYDOWN:
if (event.key.keysym.scancode == SDL_SCANCODE_TAB) { if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) {
cycle_scaling();
}
else if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) {
if (is_running) { if (is_running) {
return; return;
} }
@ -376,6 +377,8 @@ void run_gui(bool is_running)
else if (gui_state == SHOWING_MENU) { else if (gui_state == SHOWING_MENU) {
gui_state = SHOWING_DROP_MESSAGE; gui_state = SHOWING_DROP_MESSAGE;
} }
current_selection = 0;
current_menu = root_menu;
should_render = true; should_render = true;
} }
} }
@ -391,7 +394,7 @@ void run_gui(bool is_running)
} }
else if (event.key.keysym.scancode == SDL_SCANCODE_RETURN) { else if (event.key.keysym.scancode == SDL_SCANCODE_RETURN) {
if (current_menu[current_selection].handler) { if (current_menu[current_selection].handler) {
current_menu[current_selection].handler(); current_menu[current_selection].handler(current_selection);
if (pending_command) { if (pending_command) {
if (!is_running && pending_command == GB_SDL_QUIT_COMMAND) { if (!is_running && pending_command == GB_SDL_QUIT_COMMAND) {
exit(0); 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) { 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; should_render = true;
} }
else if (event.key.keysym.scancode == SDL_SCANCODE_LEFT && current_menu[current_selection].backwards_handler) { 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; should_render = true;
} }
} }
else if(gui_state == SHOWING_HELP) { else if (gui_state == SHOWING_HELP) {
current_help_page++; current_help_page++;
if (current_help_page == sizeof(help) / sizeof(help[0])) { if (current_help_page == sizeof(help) / sizeof(help[0])) {
gui_state = SHOWING_MENU; gui_state = SHOWING_MENU;
} }
should_render = true; 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; 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));
} }

View File

@ -16,7 +16,6 @@ enum scaling_mode {
GB_SDL_SCALING_MAX, GB_SDL_SCALING_MAX,
}; };
extern enum scaling_mode scaling_mode;
enum pending_command { enum pending_command {
GB_SDL_NO_COMMAND, GB_SDL_NO_COMMAND,
@ -30,10 +29,15 @@ enum pending_command {
extern enum pending_command pending_command; extern enum pending_command pending_command;
extern unsigned command_parameter; 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 update_viewport(void);
void cycle_scaling(void);
void run_gui(bool is_running); void run_gui(bool is_running);
#endif #endif

View File

@ -102,7 +102,7 @@ static void handle_events(GB_gameboy_t *gb)
switch (event.key.keysym.scancode) { switch (event.key.keysym.scancode) {
case SDL_SCANCODE_ESCAPE: case SDL_SCANCODE_ESCAPE:
run_gui(true); run_gui(true);
GB_set_color_correction_mode(gb, color_correction_mode); GB_set_color_correction_mode(gb, configuration.color_correction_mode);
break; break;
case SDL_SCANCODE_C: case SDL_SCANCODE_C:
@ -142,10 +142,6 @@ static void handle_events(GB_gameboy_t *gb)
} }
break; break;
case SDL_SCANCODE_TAB:
cycle_scaling();
break;
default: default:
/* Save states */ /* Save states */
if (event.key.keysym.scancode >= SDL_SCANCODE_0 && event.key.keysym.scancode <= SDL_SCANCODE_9) { 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; break;
} }
case SDL_KEYUP: // Fallthrough case SDL_KEYUP: // Fallthrough
switch (event.key.keysym.scancode) { if (event.key.keysym.scancode == configuration.keys[8]) {
case SDL_SCANCODE_RIGHT: GB_set_turbo_mode(gb, event.type == SDL_KEYDOWN, false);
GB_set_key_state(gb, GB_KEY_RIGHT, event.type == SDL_KEYDOWN); }
break; else {
case SDL_SCANCODE_LEFT: for (unsigned i = 0; i < GB_KEY_MAX; i++) {
GB_set_key_state(gb, GB_KEY_LEFT, event.type == SDL_KEYDOWN); if (event.key.keysym.scancode == configuration.keys[i]) {
break; GB_set_key_state(gb, i, event.type == SDL_KEYDOWN);
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;
} }
break; break;
default: default:
@ -297,7 +274,7 @@ restart:
GB_set_pixels_output(&gb, pixels); GB_set_pixels_output(&gb, pixels);
GB_set_rgb_encode_callback(&gb, rgb_encode); GB_set_rgb_encode_callback(&gb, rgb_encode);
GB_set_sample_rate(&gb, have_aspec.freq); 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; bool error = false;