diff --git a/gui/main.c b/gui/main.c index 483478a..d7bcd33 100644 --- a/gui/main.c +++ b/gui/main.c @@ -17,6 +17,8 @@ #include "pipemgmt.h" +int select_controller = 0; + void display_cli_help(const char **argv); int SDL_main(int argc, const char **argv) @@ -31,7 +33,10 @@ int SDL_main(int argc, const char **argv) } else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--fullscreen")) { override_fs = 1; consumed = 1; - } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { + } else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--controller")) { + select_controller = 1; + consumed = 1; + } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { display_cli_help(argv); return 0; } @@ -92,5 +97,6 @@ void display_cli_help(const char **argv) { vpilog("Options:\n"); vpilog(" -w, --window Run Vanilla in a window (overrides config)\n"); vpilog(" -f, --fullscreen Run Vanilla full screen (overrides config)\n"); + vpilog(" -c, --controller Lets you select your controller from a list of detected controllers\n"); vpilog(" -h, --help Show this help message\n"); } diff --git a/gui/menu/menu_gamepad.c b/gui/menu/menu_gamepad.c index 04dc57d..6b879b0 100644 --- a/gui/menu/menu_gamepad.c +++ b/gui/menu/menu_gamepad.c @@ -1,11 +1,14 @@ #include "menu_gamepad.h" +#include + #include "config.h" #include "lang.h" #include "menu/menu.h" #include "menu/menu_common.h" #include "menu/menu_settings.h" #include "ui/ui_anim.h" +#include "ui/ui_sdl.h" #include "ui/ui_keyboard.h" #include "lib/vanilla.h" @@ -262,6 +265,69 @@ void vpi_menu_key_bindings(vui_context_t *vui, void *v) vui_start_passive_animation(vui, animate_button, 0); } +void vpi_menu_controller_select(vui_context_t *vui, void *v); + +static void transition_to_controller_select(vui_context_t *vui, int btn, void *v) +{ + vui_clear_key_listener(vui); + int layer = (intptr_t) v; + vui_transition_fade_layer_out(vui, layer, vpi_menu_controller_select, 0); +} + +static void on_controller_selected(vui_context_t *vui, int btn, void *v) +{ + int selected_index = (intptr_t)v; + vpilog("Changing to game controller \"%s\"\n", SDL_GameControllerNameForIndex(selected_index)); + + vui_sdl_set_controller(vui, selected_index); + + int first_btn_id = btn - selected_index; + for (int i = 0; i < SDL_NumJoysticks(); i++) { + vui_button_update_checked(vui, first_btn_id + i, (i == selected_index) ? 1 : 0); + } +} + +void vpi_menu_controller_select(vui_context_t *vui, void *v) +{ + vui_reset(vui); + int layer = vui_layer_create(vui); + + int SCREEN_WIDTH, SCREEN_HEIGHT; + vui_get_screen_size(vui, &SCREEN_WIDTH, &SCREEN_HEIGHT); + + int num_joysticks = SDL_NumJoysticks(); + int entries = num_joysticks > 0 ? num_joysticks : 1; + + int list_item_width = SCREEN_WIDTH - BTN_SZ - BTN_SZ; + int list_item_height = (SCREEN_HEIGHT - BTN_SZ - BTN_SZ) / entries; + + if (list_item_height > 100) list_item_height = 100; + + if (num_joysticks == 0) { + vui_button_create(vui, BTN_SZ, BTN_SZ, list_item_width, list_item_height, "No Controllers Found", NULL, VUI_BUTTON_STYLE_LIST, layer, NULL, NULL); + } else { + for (int i = 0; i < num_joysticks; i++) { + const char *name = SDL_GameControllerNameForIndex(i); + if (!name) name = "Unknown Controller"; + + int btn_id = vui_button_create(vui, BTN_SZ, BTN_SZ + (list_item_height * i), list_item_width, list_item_height, name, NULL, VUI_BUTTON_STYLE_LIST, layer, on_controller_selected, (void *)(intptr_t)i); + + vui_button_update_checkable(vui, btn_id, 1); + + if (i == active_controller_index) { + vui_button_update_checked(vui, btn_id, 1); + } else { + vui_button_update_checked(vui, btn_id, 0); + } + } + } + + vpi_menu_create_back_button(vui, layer, return_to_gamepad, (void *) (intptr_t) layer); + + vui_transition_fade_layer_in(vui, layer, 0, 0); +} + + void vpi_menu_gamepad(vui_context_t *vui, void *v) { vui_reset(vui); @@ -280,8 +346,11 @@ void vpi_menu_gamepad(vui_context_t *vui, void *v) int keyboard_bind_button = vui_button_create(vui, BTN_SZ, BTN_SZ + list_item_height * 1, list_item_width, list_item_height, lang(VPI_LANG_KEYBOARD_CONTROLS), NULL, VUI_BUTTON_STYLE_LIST, layer, transition_to_keybinds, (void *)(intptr_t)layer); + int select_controller_button = vui_button_create(vui, BTN_SZ, BTN_SZ + list_item_height * 2, list_item_width, list_item_height, "Select Controller", NULL, VUI_BUTTON_STYLE_LIST, layer, transition_to_controller_select, (void *)(intptr_t)layer); + // Back button vpi_menu_create_back_button(vui, layer, return_to_settings, (void *) (intptr_t) layer); vui_transition_fade_layer_in(vui, layer, 0, 0); } + diff --git a/gui/ui/ui_sdl.c b/gui/ui/ui_sdl.c index e4ab16f..3cdd24f 100644 --- a/gui/ui/ui_sdl.c +++ b/gui/ui/ui_sdl.c @@ -158,6 +158,10 @@ void init_gamepad(vui_context_t *ctx) ctx->default_key_map[SDL_SCANCODE_ESCAPE] = VPI_ACTION_DISCONNECT; } +int active_controller_index = -1; + +extern int select_controller; + void find_valid_controller(vui_sdl_context_t *sdl_ctx) { // HACK: This is just for the Steam Deck. The Steam Deck provides a "virtual gamepad" @@ -170,17 +174,37 @@ void find_valid_controller(vui_sdl_context_t *sdl_ctx) // and then use the direct hardware access JUST for the gyros. int controller = -1; int steam_virtual_gamepad_index = -1; + int num_joysticks = SDL_NumJoysticks(); vpilog("Looking for game controllers...\n"); - for (int i = 0; i < SDL_NumJoysticks(); i++) { - const char *ctrl_name = SDL_GameControllerNameForIndex(i); - vpilog(" Found %i: %s\n", i, ctrl_name); - if (ctrl_name != NULL && !strcmp(ctrl_name, "Steam Virtual Gamepad")) { - steam_virtual_gamepad_index = i; - } else if (controller == -1) { - controller = i; + if (select_controller == 1 && num_joysticks > 0) { + for (int i = 0; i < num_joysticks; i++) { + vpilog(" Found %i: %s\n", i, SDL_GameControllerNameForIndex(i)); } - } + vpilog("Select your controller (use the number assigned to it): "); + char input[10]; + if (fgets(input, sizeof(input), stdin)) { + int selected = atoi(input); + if (selected >= 0 && selected < num_joysticks) { + controller = selected; + } else { + vpilog("\nSelected controller is invalid. Falling back to auto-select.\n"); + select_controller = 0; + } + } + } + + if (select_controller == 0) { + for (int i = 0; i < num_joysticks; i++) { + const char *ctrl_name = SDL_GameControllerNameForIndex(i); + vpilog(" Found %i: %s\n", i, ctrl_name); + if (ctrl_name != NULL && !strcmp(ctrl_name, "Steam Virtual Gamepad")) { + steam_virtual_gamepad_index = i; + } else if (controller == -1) { + controller = i; + } + } + } SDL_GameController *steam_virtual_gamepad = NULL; SDL_GameController *c = NULL; @@ -209,8 +233,59 @@ void find_valid_controller(vui_sdl_context_t *sdl_ctx) if (steam_virtual_gamepad) { sdl_ctx->controller = steam_virtual_gamepad; sdl_ctx->controller_gyros = c; + active_controller_index = steam_virtual_gamepad_index; } else { sdl_ctx->controller = c; + active_controller_index = controller; + } +} + +void vui_sdl_set_controller(vui_context_t *ctx, int index) +{ + vui_sdl_context_t *sdl_ctx = (vui_sdl_context_t *) ctx->platform_data; + + if (sdl_ctx->controller) { + SDL_GameControllerClose(sdl_ctx->controller); + } + if (sdl_ctx->controller_gyros && sdl_ctx->controller_gyros != sdl_ctx->controller) { + SDL_GameControllerClose(sdl_ctx->controller_gyros); + } + + sdl_ctx->controller = NULL; + sdl_ctx->controller_gyros = NULL; + active_controller_index = index; + + if (index >= 0) { + SDL_GameController *primary = SDL_GameControllerOpen(index); + SDL_GameController *gyros = NULL; + + if (primary) { + const char *selected_name = SDL_GameControllerName(primary); + if (selected_name && !strcmp(selected_name, "Steam Virtual Gamepad")) { + for (int i = 0; i < SDL_NumJoysticks(); i++) { + const char *search_name = SDL_GameControllerNameForIndex(i); + if (search_name && strcmp(search_name, "Steam Virtual Gamepad") != 0) { + gyros = SDL_GameControllerOpen(i); + if (gyros) { + SDL_GameControllerSetSensorEnabled(gyros, SDL_SENSOR_ACCEL, 1); + SDL_GameControllerSetSensorEnabled(gyros, SDL_SENSOR_GYRO, 1); + break; + } + } + } + } else { + SDL_GameControllerSetSensorEnabled(primary, SDL_SENSOR_ACCEL, 1); + SDL_GameControllerSetSensorEnabled(primary, SDL_SENSOR_GYRO, 1); + } + + sdl_ctx->controller = primary; + sdl_ctx->controller_gyros = gyros; + + vpilog("Now using game controller \"%s\"\n", SDL_GameControllerName(primary)); + if (gyros) { + vpilog(" with \"%s\" for accelerometer/gyroscope\n", SDL_GameControllerName(gyros)); + } + } } } diff --git a/gui/ui/ui_sdl.h b/gui/ui/ui_sdl.h index cb29204..b63a22c 100644 --- a/gui/ui/ui_sdl.h +++ b/gui/ui/ui_sdl.h @@ -3,6 +3,9 @@ #include "ui.h" +extern int active_controller_index; +void vui_sdl_set_controller(vui_context_t *ctx, int index); + /** * System-related functions */