diff --git a/SDL/gui.c b/SDL/gui.c index bb5e206..1a12e28 100644 --- a/SDL/gui.c +++ b/SDL/gui.c @@ -174,8 +174,12 @@ static enum { SHOWING_MENU, SHOWING_HELP, WAITING_FOR_KEY, + WAITING_FOR_JBUTTON, } gui_state; +unsigned auto_detect_progress = 0; +unsigned auto_detect_inputs[3]; + static void item_exit(unsigned index) { pending_command = GB_SDL_QUIT_COMMAND; @@ -190,11 +194,13 @@ static void item_help(unsigned index) static void enter_graphics_menu(unsigned index); static void enter_controls_menu(unsigned index); +static void enter_joypad_menu(unsigned index); static const struct menu_item paused_menu[] = { {"Resume", NULL}, {"Graphic Options", enter_graphics_menu}, - {"Controls", enter_controls_menu}, + {"Keyboard", enter_controls_menu}, + {"Joypad", enter_joypad_menu}, {"Help", item_help}, {"Exit", item_exit}, {NULL,} @@ -202,7 +208,8 @@ static const struct menu_item paused_menu[] = { static const struct menu_item nonpaused_menu[] = { {"Graphic Options", enter_graphics_menu}, - {"Controls", enter_controls_menu}, + {"Keyboard", enter_controls_menu}, + {"Joypad", enter_joypad_menu}, {"Help", item_help}, {"Exit", item_exit}, {NULL,} @@ -316,10 +323,102 @@ static void enter_controls_menu(unsigned index) current_selection = 0; } +static unsigned joypad_index = 0; +SDL_Joystick *joystick = NULL; +const char *current_joypad_name(unsigned index) +{ + static char name[23] = {0,}; + const char *orig_name = joystick? SDL_JoystickName(joystick) : NULL; + if (!orig_name) return "Not Found"; + unsigned i = 0; + + // SDL returns a name with repeated and trailing spaces + while (*orig_name && i < sizeof(name) - 2) { + if (orig_name[0] != ' ' || orig_name[1] != ' ') { + name[i++] = *orig_name; + } + orig_name++; + } + if (i && name[i - 1] == ' ') { + i--; + } + name[i] = 0; + + return name; +} + +static void cycle_joypads(unsigned index) +{ + joypad_index++; + if (joypad_index >= SDL_NumJoysticks()) { + joypad_index = 0; + } + if (joystick) { + SDL_JoystickClose(joystick); + } + joystick = SDL_JoystickOpen(joypad_index); +} + +unsigned fix_joypad_button(unsigned button) +{ + if (configuration.div_joystick) { + button >>= 1; + } + + if (button < 4) { + if (configuration.swap_joysticks_bits_1_and_2) { + button = (int[]){0, 2, 1, 3}[button]; + } + + if (configuration.flip_joystick_bit_1) { + button ^= 1; + } + } + + return button; +} + +static void cycle_joypads_backwards(unsigned index) +{ + joypad_index++; + if (joypad_index >= SDL_NumJoysticks()) { + joypad_index = SDL_NumJoysticks() - 1; + } + if (joystick) { + SDL_JoystickClose(joystick); + } + joystick = SDL_JoystickOpen(joypad_index); +} + +static void detect_joypad_layout(unsigned index) +{ + gui_state = WAITING_FOR_JBUTTON; + auto_detect_progress = 0; +} + +static const struct menu_item joypad_menu[] = { + {"Joypad:", cycle_joypads, current_joypad_name, cycle_joypads_backwards}, + {"Detect layout", detect_joypad_layout}, + {NULL,} +}; + +static void enter_joypad_menu(unsigned index) +{ + current_menu = joypad_menu; + current_selection = 0; +} + extern void set_filename(const char *new_filename, bool new_should_free); void run_gui(bool is_running) { - /* Draw the "Drop file" screen */ + if (joystick && !SDL_NumJoysticks()) { + SDL_JoystickClose(joystick); + joystick = NULL; + } + else if (!joystick && SDL_NumJoysticks()) { + joystick = SDL_JoystickOpen(0); + } + /* Draw the background screen */ static SDL_Surface *converted_background = NULL; if (!converted_background) { SDL_Surface *background = SDL_LoadBMP(executable_relative_path("background.bmp")); @@ -340,6 +439,63 @@ void run_gui(bool is_running) current_menu = root_menu = is_running? paused_menu : nonpaused_menu; current_selection = 0; do { + /* Convert Joypad events (We only generate down events) */ + if (gui_state != WAITING_FOR_KEY && gui_state != WAITING_FOR_JBUTTON) { + switch (event.type) { + case SDL_JOYBUTTONDOWN: + event.type = SDL_KEYDOWN; + event.jbutton.button = fix_joypad_button(event.jbutton.button); + if (event.jbutton.button < 4) { + event.key.keysym.scancode = (event.jbutton.button & 1) ? SDL_SCANCODE_RETURN : SDL_SCANCODE_ESCAPE; + } + else if (event.jbutton.button == 8 || event.jbutton.button == 9) { + event.key.keysym.scancode = SDL_SCANCODE_ESCAPE; + } + break; + + case SDL_JOYAXISMOTION: { + static bool axis_active[2] = {false, false}; + if ((event.jaxis.axis >> configuration.div_joystick) & 1) { + if (event.jaxis.value > 0x4000) { + if (!axis_active[1]) { + event.type = SDL_KEYDOWN; + event.key.keysym.scancode = SDL_SCANCODE_DOWN; + } + axis_active[1] = true; + } + else if (event.jaxis.value < -0x4000) { + if (!axis_active[0]) { + event.type = SDL_KEYDOWN; + event.key.keysym.scancode = SDL_SCANCODE_UP; + } + axis_active[1] = true; + } + else { + axis_active[1] = false; + } + } + else { + if (event.jaxis.value > 0x4000) { + if (!axis_active[0]) { + event.type = SDL_KEYDOWN; + event.key.keysym.scancode = SDL_SCANCODE_RIGHT; + } + axis_active[0] = true; + } + else if (event.jaxis.value < -0x4000) { + if (!axis_active[0]) { + event.type = SDL_KEYDOWN; + event.key.keysym.scancode = SDL_SCANCODE_LEFT; + } + axis_active[0] = true; + } + else { + axis_active[0] = false; + } + } + } + } + } switch (event.type) { case SDL_QUIT: { if (!is_running) { @@ -365,6 +521,36 @@ void run_gui(bool is_running) pending_command = GB_SDL_NEW_FILE_COMMAND; return; } + case SDL_JOYBUTTONDOWN: + { + if (gui_state == WAITING_FOR_JBUTTON) { + should_render = true; + auto_detect_inputs[auto_detect_progress++] = event.jbutton.button; + if (auto_detect_progress == 3) { + gui_state = SHOWING_MENU; + + configuration.div_joystick = + ((auto_detect_inputs[0] | auto_detect_inputs[1] | auto_detect_inputs[2]) & 1) == 0 && + auto_detect_inputs[0] > 9; + + if (configuration.div_joystick) { + auto_detect_inputs[0] >>= 1; + auto_detect_inputs[1] >>= 1; + auto_detect_inputs[2] >>= 1; + } + + configuration.swap_joysticks_bits_1_and_2 = + (auto_detect_inputs[1] & 1) == (auto_detect_inputs[2] & 1); + + if (configuration.swap_joysticks_bits_1_and_2) { + auto_detect_inputs[1] = (int[]){0, 2, 1, 3}[auto_detect_inputs[1]]; + auto_detect_inputs[2] = (int[]){0, 2, 1, 3}[auto_detect_inputs[2]]; + } + + configuration.flip_joystick_bit_1 = auto_detect_inputs[2] & 1; + } + } + } case SDL_KEYDOWN: if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) { if (is_running) { @@ -470,6 +656,15 @@ void run_gui(bool is_running) case WAITING_FOR_KEY: draw_text_centered(pixels, 68, "Press a Key", gui_palette_native[3], gui_palette_native[0], DECORATION_NONE); break; + case WAITING_FOR_JBUTTON: + draw_text_centered(pixels, 68, (const char *[]) + { + "Press button for Start", + "Press button for A", + "Press button for B", + } [auto_detect_progress], + gui_palette_native[3], gui_palette_native[0], DECORATION_NONE); + break; } SDL_UpdateTexture(texture, NULL, pixels, 160 * sizeof (uint32_t)); diff --git a/SDL/gui.h b/SDL/gui.h index 3f82420..6e08c6b 100644 --- a/SDL/gui.h +++ b/SDL/gui.h @@ -33,11 +33,16 @@ typedef struct { SDL_Scancode keys[9]; GB_color_correction_mode_t color_correction_mode; enum scaling_mode scaling_mode; + + bool div_joystick; + bool flip_joystick_bit_1; + bool swap_joysticks_bits_1_and_2; } configuration_t; extern configuration_t configuration; void update_viewport(void); - void run_gui(bool is_running); +unsigned fix_joypad_button(unsigned button); + #endif diff --git a/SDL/main.c b/SDL/main.c index 8bb23ba..8320b67 100755 --- a/SDL/main.c +++ b/SDL/main.c @@ -96,8 +96,42 @@ static void handle_events(GB_gameboy_t *gb) if (event.window.event == SDL_WINDOWEVENT_RESIZED) { update_viewport(); } + break; } + case SDL_JOYBUTTONUP: + case SDL_JOYBUTTONDOWN: + event.jbutton.button = fix_joypad_button(event.jbutton.button); + if (event.jbutton.button < 4) { + GB_set_key_state(gb, (event.jbutton.button & 1) ? GB_KEY_A : GB_KEY_B, + event.type == SDL_JOYBUTTONDOWN); + } + else if (event.jbutton.button == 8) { + GB_set_key_state(gb, GB_KEY_SELECT, event.type == SDL_JOYBUTTONDOWN); + } + else if (event.jbutton.button == 9) { + GB_set_key_state(gb, GB_KEY_START, event.type == SDL_JOYBUTTONDOWN); + } + else if (event.jbutton.button & 1) { + GB_set_turbo_mode(gb, event.type == SDL_JOYBUTTONDOWN, false); + } + else { + run_gui(true); + GB_set_color_correction_mode(gb, configuration.color_correction_mode); + } + break; + + case SDL_JOYAXISMOTION: + if ((event.jaxis.axis >> configuration.div_joystick) & 1) { + GB_set_key_state(gb, GB_KEY_DOWN, event.jaxis.value > 0x4000); + GB_set_key_state(gb, GB_KEY_UP, event.jaxis.value < -0x4000); + } + else { + GB_set_key_state(gb, GB_KEY_RIGHT, event.jaxis.value > 0x4000); + GB_set_key_state(gb, GB_KEY_LEFT, event.jaxis.value < -0x4000); + } + break; + case SDL_KEYDOWN: switch (event.key.keysym.scancode) { case SDL_SCANCODE_ESCAPE: