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 */
_, 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_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));
}

View File

@ -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

View File

@ -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;