From a98cf379f88a0b3fd301ca94c34e471cfa2910bb Mon Sep 17 00:00:00 2001 From: zoltanvb Date: Sat, 18 May 2024 16:48:17 +0200 Subject: [PATCH] Controller device reservation / preference. For each player, 2 new options are added: - a reservation type (no reservation, preferred, reserved) - a reserved device name When handling port - player assignments, reserved devices will be assigned to the respective player port. If reservation type is "reserved", no other device can take that port automatically. Reservation config option and matching function lifted from: PatrickStankard https://github.com/libretro/RetroArch/pull/16269 Test joypad driver was extended for more tests. Co-authored-by: Patrick Stankard --- configuration.c | 46 +++- configuration.h | 3 + input/drivers_joypad/test_joypad.c | 33 ++- input/input_defines.h | 8 + intl/msg_hash_lbl.h | 12 + intl/msg_hash_us.h | 28 +++ menu/cbs/menu_cbs_deferred_push.c | 2 + menu/cbs/menu_cbs_ok.c | 73 ++++++ menu/cbs/menu_cbs_sublabel.c | 10 + menu/cbs/menu_cbs_title.c | 11 + menu/drivers/ozone.c | 16 +- menu/drivers/xmb.c | 16 +- menu/menu_cbs.h | 1 + menu/menu_displaylist.c | 119 ++++++++++ menu/menu_displaylist.h | 1 + menu/menu_driver.h | 1 + menu/menu_setting.c | 223 +++++++++++++++++- msg_hash.h | 15 +- tasks/task_autodetect.c | 152 ++++++++++++ tests-other/autoconf/TestpadA.cfg | 27 +++ tests-other/autoconf/TestpadB.cfg | 27 +++ tests-other/autoconf/TestpadC.cfg | 27 +++ tests-other/test_input_joypad.ratst | 10 +- ...test_input_joypad_device_reservation.ratst | 41 ++++ tests-other/testinput.cfg | 6 + .../testinput_device_reservation_test.cfg | 30 +++ 26 files changed, 903 insertions(+), 35 deletions(-) create mode 100644 tests-other/autoconf/TestpadA.cfg create mode 100644 tests-other/autoconf/TestpadB.cfg create mode 100644 tests-other/autoconf/TestpadC.cfg create mode 100644 tests-other/test_input_joypad_device_reservation.ratst create mode 100644 tests-other/testinput_device_reservation_test.cfg diff --git a/configuration.c b/configuration.c index 989fa64bfab..eed3e8205c3 100644 --- a/configuration.c +++ b/configuration.c @@ -1485,6 +1485,7 @@ bool config_overlay_enable_default(void) static struct config_array_setting *populate_settings_array( settings_t *settings, int *size) { + unsigned i = 0; unsigned count = 0; struct config_array_setting *tmp = (struct config_array_setting*)calloc(1, (*size + 1) * sizeof(struct config_array_setting)); @@ -1515,6 +1516,24 @@ static struct config_array_setting *populate_settings_array( SETTING_ARRAY("input_android_physical_keyboard", settings->arrays.input_android_physical_keyboard, false, NULL, true); #endif + for (i = 0; i < MAX_USERS; i++) + { + size_t _len; + char formatted_number[4]; + char prefix[16]; + char key[32]; + + formatted_number[0] = '\0'; + + snprintf(formatted_number, sizeof(formatted_number), "%u", i + 1); + _len = strlcpy(prefix, "input_player", sizeof(prefix)); + strlcpy(prefix + _len, formatted_number, sizeof(prefix) - _len); + _len = strlcpy(key, prefix, sizeof(key)); + strlcpy(key + _len, "_reserved_device", sizeof(key) - _len); + + SETTING_ARRAY(strdup(key), settings->arrays.input_reserved_devices[i], false, NULL, true); + } + #ifdef HAVE_MENU SETTING_ARRAY("menu_driver", settings->arrays.menu_driver, false, NULL, true); #endif @@ -3732,6 +3751,10 @@ static bool config_load_file(global_t *global, strlcpy(buf + _len2, "_analog_dpad_mode", sizeof(buf) - _len2); CONFIG_GET_INT_BASE(conf, settings, uints.input_analog_dpad_mode[i], buf); + + strlcpy(buf + _len2, "_device_reservation_type", sizeof(buf) - _len2); + CONFIG_GET_INT_BASE(conf, settings, uints.input_device_reservation_type[i], buf); + } } @@ -5265,7 +5288,6 @@ bool config_save_file(const char *path) size_t _len; char cfg[64]; char formatted_number[4]; - formatted_number[0] = '\0'; snprintf(formatted_number, sizeof(formatted_number), "%u", i + 1); @@ -5285,6 +5307,9 @@ bool config_save_file(const char *path) strlcpy(cfg + _len, "_analog_dpad_mode", sizeof(cfg) - _len); config_set_int(conf, cfg, settings->uints.input_analog_dpad_mode[i]); + + strlcpy(cfg + _len, "_device_reservation_type", sizeof(cfg) - _len); + config_set_int(conf, cfg, settings->uints.input_device_reservation_type[i]); } /* Boolean settings */ @@ -5619,6 +5644,25 @@ int8_t config_save_overrides(enum override_type type, RARCH_DBG("[Overrides]: %s = \"%u\"\n", cfg, overrides->uints.input_analog_dpad_mode[i]); } + if (settings->uints.input_device_reservation_type[i] + != overrides->uints.input_device_reservation_type[i]) + { + strlcpy(cfg + _len, "_device_reservation_type", sizeof(cfg) - _len); + config_set_int(conf, cfg, overrides->uints.input_device_reservation_type[i]); + RARCH_DBG("[Overrides]: %s = \"%u\"\n", cfg, overrides->uints.input_device_reservation_type[i]); + } + + /* TODO: is this whole section really necessary? Does the loop above not do this? */ + if (!string_is_equal(settings->arrays.input_reserved_devices[i], overrides->arrays.input_reserved_devices[i])) + { + strlcpy(cfg + _len, "_device_reservation_type", sizeof(cfg) - _len); + + config_set_string(conf, cfg, + overrides->arrays.input_reserved_devices[i]); + RARCH_DBG("[Overrides]: %s = \"%s\"\n", + cfg, overrides->arrays.input_reserved_devices[i]); + } + for (j = 0; j < RARCH_BIND_LIST_END; j++) { const struct retro_keybind *override_bind = &input_override_binds[i][j]; diff --git a/configuration.h b/configuration.h index 71d194f8288..ed2f61caa3c 100644 --- a/configuration.h +++ b/configuration.h @@ -151,6 +151,7 @@ typedef struct settings unsigned input_libretro_device[MAX_USERS]; unsigned input_analog_dpad_mode[MAX_USERS]; + unsigned input_device_reservation_type[MAX_USERS]; unsigned input_remap_ports[MAX_USERS]; unsigned input_remap_ids[MAX_USERS][RARCH_CUSTOM_BIND_LIST_END]; @@ -476,6 +477,8 @@ typedef struct settings char input_android_physical_keyboard[255]; #endif + char input_reserved_devices[MAX_USERS][255]; + char audio_device[255]; char camera_device[255]; char netplay_mitm_server[255]; diff --git a/input/drivers_joypad/test_joypad.c b/input/drivers_joypad/test_joypad.c index 2c3433b5381..ac9a87a9f4c 100644 --- a/input/drivers_joypad/test_joypad.c +++ b/input/drivers_joypad/test_joypad.c @@ -44,6 +44,7 @@ #endif #define JOYPAD_TEST_COMMAND_ADD_CONTROLLER 1 +#define JOYPAD_TEST_COMMAND_REMOVE_CONTROLLER 2 #define JOYPAD_TEST_COMMAND_BUTTON_PRESS_FIRST 16 #define JOYPAD_TEST_COMMAND_BUTTON_PRESS_LAST 31 #define JOYPAD_TEST_COMMAND_BUTTON_RELEASE_FIRST 32 @@ -71,8 +72,8 @@ typedef struct static input_test_step_t input_test_steps[MAX_TEST_STEPS]; -static unsigned current_frame = 0; -static unsigned next_teststep_frame = 0; +static uint32_t current_frame = 0; +static uint32_t next_teststep_frame = 0; static unsigned current_test_step = 0; static unsigned last_test_step = MAX_TEST_STEPS + 1; static uint32_t input_state_validated = 0; @@ -286,22 +287,37 @@ static const char *test_joypad_name(unsigned pad) if (pad >= MAX_USERS || string_is_empty(test_joypads[pad].name)) return NULL; - return test_joypads[pad].name; + if (strstr(test_joypads[pad].name, ") ")) + return strstr(test_joypads[pad].name, ") ") + 2; + else + return test_joypads[pad].name; } - static void test_joypad_autodetect_add(unsigned autoconf_pad) { + int vid = 0; + int pid = 0; + + sscanf(strstr(test_joypads[autoconf_pad].name, "(") + 1, "%04x:%04x", &vid, &pid); + RARCH_DBG("[Test input driver]: Autoconf vid/pid %x:%x\n",vid,pid); + input_autoconfigure_connect( test_joypad_name(autoconf_pad), NULL, "test", autoconf_pad, - 0, - 0 + vid, + pid ); } +static void test_joypad_autodetect_remove(unsigned autoconf_pad) +{ + RARCH_DBG("[Test input driver]: Autoremove port %d\n", autoconf_pad); + + input_autoconfigure_disconnect(autoconf_pad, test_joypad_name(autoconf_pad)); +} + static void *test_joypad_init(void *data) { settings_t *settings = config_get_ptr(); @@ -405,6 +421,11 @@ static void test_joypad_poll(void) test_joypad_autodetect_add(input_test_steps[i].param_num); input_test_steps[i].handled = true; } + else if (input_test_steps[i].action == JOYPAD_TEST_COMMAND_REMOVE_CONTROLLER) + { + test_joypad_autodetect_remove(input_test_steps[i].param_num); + input_test_steps[i].handled = true; + } else if( input_test_steps[i].action >= JOYPAD_TEST_COMMAND_BUTTON_PRESS_FIRST && input_test_steps[i].action <= JOYPAD_TEST_COMMAND_BUTTON_PRESS_LAST) { diff --git a/input/input_defines.h b/input/input_defines.h index 44cf2c8284f..5ba2f88fe8d 100644 --- a/input/input_defines.h +++ b/input/input_defines.h @@ -245,6 +245,14 @@ enum input_turbo_default_button INPUT_TURBO_DEFAULT_BUTTON_LAST }; +enum input_device_reservation_type +{ + INPUT_DEVICE_RESERVATION_NONE = 0, + INPUT_DEVICE_RESERVATION_PREFERRED, + INPUT_DEVICE_RESERVATION_RESERVED, + INPUT_DEVICE_RESERVATION_LAST +}; + RETRO_END_DECLS #endif diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 2a773168992..24c5102e571 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1009,6 +1009,10 @@ MSG_HASH( MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DEVICE_TYPE, "deferred_dropdown_box_list_input_device_type" ) +MSG_HASH( + MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_RESERVED_DEVICE, + "deferred_dropdown_box_list_input_select_reserved_device" + ) #ifdef ANDROID MSG_HASH( MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD, @@ -1976,6 +1980,14 @@ MSG_HASH( MENU_ENUM_LABEL_INPUT_DEVICE_INDEX, "input_device_p%u" ) +MSG_HASH( + MENU_ENUM_LABEL_INPUT_DEVICE_RESERVED_DEVICE_NAME, + "input_device_reserved_device_p%u" + ) +MSG_HASH( + MENU_ENUM_LABEL_INPUT_DEVICE_RESERVATION_TYPE, + "input_device_reservation_type_p%u" + ) MSG_HASH( MENU_ENUM_LABEL_INPUT_MOUSE_INDEX, "input_player%u_mouse_index" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 887a643c013..86abf97919e 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -4125,6 +4125,34 @@ MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_DEVICE_INDEX, "The physical controller as recognized by RetroArch." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_RESERVED_DEVICE_NAME, + "Reserved Device for This Player" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_INPUT_DEVICE_RESERVED_DEVICE_NAME, + "This controller will be allocated for this player, according to reservation mode." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_DEVICE_RESERVATION_NONE, + "No Reservation" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_DEVICE_RESERVATION_PREFERRED, + "Preferred" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_DEVICE_RESERVATION_RESERVED, + "Reserved" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_RESERVATION_TYPE, + "Device Reservation Type" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_INPUT_DEVICE_RESERVATION_TYPE, + "Preferred: if specified device is present, it will be allocated for this player. Reserved: no other controller will be allocated for this player." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_REMAP_PORT, "Mapped Port" diff --git a/menu/cbs/menu_cbs_deferred_push.c b/menu/cbs/menu_cbs_deferred_push.c index f66510b0f9c..2ae177cda6a 100644 --- a/menu/cbs/menu_cbs_deferred_push.c +++ b/menu/cbs/menu_cbs_deferred_push.c @@ -680,6 +680,7 @@ GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_disk_index, PUSH_D GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_input_device_type, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_INPUT_DEVICE_TYPE) GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_input_description, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_INPUT_DESCRIPTION) GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_input_description_kbd, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_INPUT_DESCRIPTION_KBD) +GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_input_select_reserved_device, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_INPUT_SELECT_RESERVED_DEVICE) #ifdef ANDROID GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_input_select_physical_keyboard, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD) #endif @@ -726,6 +727,7 @@ static int menu_cbs_init_bind_deferred_push_compare_label( {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DEVICE_TYPE, deferred_push_dropdown_box_list_input_device_type}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION, deferred_push_dropdown_box_list_input_description}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION_KBD, deferred_push_dropdown_box_list_input_description_kbd}, + {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_RESERVED_DEVICE, deferred_push_dropdown_box_list_input_select_reserved_device}, #ifdef ANDROID {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD, deferred_push_dropdown_box_list_input_select_physical_keyboard}, #endif diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 5a5d69fef90..c425d880004 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -292,6 +292,8 @@ static enum msg_hash_enums action_ok_dl_to_enum(unsigned lbl) return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION; case ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION_KBD: return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION_KBD; + case ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_SELECT_RESERVED_DEVICE: + return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_RESERVED_DEVICE; #ifdef ANDROID case ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD: return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD; @@ -845,6 +847,15 @@ int generic_action_ok_displaylist_push( info.enum_idx = MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DEVICE_TYPE; dl_type = DISPLAYLIST_GENERIC; break; + case ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_SELECT_RESERVED_DEVICE: + info.type = type; + info.directory_ptr = idx; + info_path = path; + info_label = msg_hash_to_str( + MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_RESERVED_DEVICE); + info.enum_idx = MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_RESERVED_DEVICE; + dl_type = DISPLAYLIST_GENERIC; + break; #ifdef ANDROID case ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD: info.type = type; @@ -7310,6 +7321,65 @@ static int action_ok_push_dropdown_item_input_device_type(const char *path, return action_cancel_pop_default(NULL, NULL, 0, 0); } +static int action_ok_push_dropdown_item_input_select_reserved_device(const char *path, + const char *label, unsigned type, size_t idx, size_t entry_idx) +{ + char* device; + const char *no_device; + const char *reserved_device_name; + enum msg_hash_enums enum_idx; + rarch_setting_t *setting = NULL; + settings_t *settings = config_get_ptr(); + const char *menu_path = NULL; + struct menu_state *menu_st = menu_state_get_ptr(); + unsigned user; + + menu_entries_get_last_stack(&menu_path, NULL, NULL, NULL, NULL); + enum_idx = (enum msg_hash_enums)atoi(menu_path); + setting = menu_setting_find_enum(enum_idx); + user = enum_idx - MENU_ENUM_LABEL_INPUT_DEVICE_RESERVED_DEVICE_NAME; + + if (!setting) + return -1; + + reserved_device_name = path; + no_device = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE); + + if (string_is_equal(reserved_device_name, no_device)) + settings->arrays.input_reserved_devices[user][0] = '\0'; + else + { + int i; + for (i = 0; i < MAX_INPUT_DEVICES; i++) + { + const char* device_name = input_config_get_device_display_name(i) + ? input_config_get_device_display_name(i) + : input_config_get_device_name(i); + + if (string_is_equal(device_name, reserved_device_name)) + { + uint16_t vendor_id = input_config_get_device_vid(i); + uint16_t product_id = input_config_get_device_pid(i); + snprintf(settings->arrays.input_reserved_devices[user], + sizeof(settings->arrays.input_reserved_devices[user]), + "%04x:%04x %s", + vendor_id, product_id, reserved_device_name); + break; + } + } + } + settings->modified = true; + + command_event(CMD_EVENT_REINIT, NULL); + + /* Refresh menu */ + menu_st->flags |= MENU_ST_FLAG_ENTRIES_NEED_REFRESH + | MENU_ST_FLAG_PREVENT_POPULATE; + + return action_cancel_pop_default(NULL, NULL, 0, 0); +} + + #ifdef ANDROID static int action_ok_push_dropdown_item_input_select_physical_keyboard(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) @@ -9241,6 +9311,9 @@ static int menu_cbs_init_bind_ok_compare_type(menu_file_list_cbs_t *cbs, case MENU_SETTING_DROPDOWN_ITEM_INPUT_DEVICE_TYPE: BIND_ACTION_OK(cbs, action_ok_push_dropdown_item_input_device_type); break; + case MENU_SETTING_DROPDOWN_ITEM_INPUT_SELECT_RESERVED_DEVICE: + BIND_ACTION_OK(cbs, action_ok_push_dropdown_item_input_select_reserved_device); + break; #ifdef ANDROID case MENU_SETTING_DROPDOWN_ITEM_INPUT_SELECT_PHYSICAL_KEYBOARD: BIND_ACTION_OK(cbs, action_ok_push_dropdown_item_input_select_physical_keyboard); diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index a2a3a7f9b07..0f7f77f41a4 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -478,6 +478,8 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_device_type, ME DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_device_index, MENU_ENUM_SUBLABEL_INPUT_DEVICE_INDEX) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_mouse_index, MENU_ENUM_SUBLABEL_INPUT_MOUSE_INDEX) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_adc_type, MENU_ENUM_SUBLABEL_INPUT_ADC_TYPE) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_device_reservation_type, MENU_ENUM_SUBLABEL_INPUT_DEVICE_RESERVATION_TYPE) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_device_reserved_device_name, MENU_ENUM_SUBLABEL_INPUT_DEVICE_RESERVED_DEVICE_NAME) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_bind_all, MENU_ENUM_SUBLABEL_INPUT_BIND_ALL) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_save_autoconfig, MENU_ENUM_SUBLABEL_INPUT_SAVE_AUTOCONFIG) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_bind_defaults, MENU_ENUM_SUBLABEL_INPUT_BIND_DEFAULTS) @@ -5645,6 +5647,14 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, MENU_ENUM_LABEL_INPUT_DEVICE_INDEX, NULL },*/ + { + MENU_ENUM_LABEL_INPUT_DEVICE_RESERVATION_TYPE, + action_bind_sublabel_input_device_reservation_type + }, + { + MENU_ENUM_LABEL_INPUT_DEVICE_RESERVED_DEVICE_NAME, + action_bind_sublabel_input_device_reserved_device_name + }, { MENU_ENUM_LABEL_INPUT_MOUSE_INDEX, action_bind_sublabel_input_mouse_index diff --git a/menu/cbs/menu_cbs_title.c b/menu/cbs/menu_cbs_title.c index 82ea16c7bdf..f0a50f84a11 100644 --- a/menu/cbs/menu_cbs_title.c +++ b/menu/cbs/menu_cbs_title.c @@ -334,6 +334,16 @@ static int action_get_title_dropdown_item( (enum_idx <= MENU_ENUM_LABEL_INPUT_DEVICE_INDEX_LAST)) enum_idx = MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_INDEX; + /* Device Reservation Type */ + if ((enum_idx >= MENU_ENUM_LABEL_INPUT_DEVICE_RESERVATION_TYPE) && + (enum_idx <= MENU_ENUM_LABEL_INPUT_DEVICE_RESERVATION_TYPE_LAST)) + enum_idx = MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_RESERVATION_TYPE; + + /* Reserved Device Name */ + if ((enum_idx >= MENU_ENUM_LABEL_INPUT_DEVICE_RESERVED_DEVICE_NAME) && + (enum_idx <= MENU_ENUM_LABEL_INPUT_DEVICE_RESERVED_DEVICE_NAME_LAST)) + enum_idx = MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_RESERVED_DEVICE_NAME; + /* Mouse Index */ if ((enum_idx >= MENU_ENUM_LABEL_INPUT_MOUSE_INDEX) && (enum_idx <= MENU_ENUM_LABEL_INPUT_MOUSE_INDEX_LAST)) @@ -1832,6 +1842,7 @@ int menu_cbs_init_bind_title(menu_file_list_cbs_t *cbs, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DEVICE_TYPE, action_get_title_dropdown_item}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION, action_get_title_dropdown_input_description}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION_KBD, action_get_title_dropdown_input_description_kbd}, + {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_RESERVED_DEVICE, action_get_title_dropdown_item}, #ifdef ANDROID {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD, action_get_title_dropdown_item}, #endif diff --git a/menu/drivers/ozone.c b/menu/drivers/ozone.c index 865f1aebe9e..58d07bf9245 100644 --- a/menu/drivers/ozone.c +++ b/menu/drivers/ozone.c @@ -2369,22 +2369,22 @@ static uintptr_t ozone_entries_icon_get_texture( /* account for the additional split joycon option in Input User # Binds */ input_id++; #endif - if (type == input_id + 1) + if (type >= input_id + 1 && type <= input_id + 3) return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_SETTINGS]; - if (type == input_id + 2) + if (type == input_id + 4) return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_MOUSE]; - if (type == input_id + 3) + if (type == input_id + 5) return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_BIND_ALL]; - if (type == input_id + 4) + if (type == input_id + 6) return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD]; - if (type == input_id + 5) + if (type == input_id + 7) return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SAVING]; - if ((type > (input_id + 29)) && (type < (input_id + 41))) + if ((type > (input_id + 31)) && (type < (input_id + 43))) return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_LGUN]; - if (type == input_id + 41) + if (type == input_id + 43) return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INPUT_TURBO]; /* align to use the same code of Quickmenu controls*/ - input_id = input_id + 6; + input_id = input_id + 8; } else { diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index 787588a139e..f489b73ade2 100644 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -3532,22 +3532,22 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb, option in Input # Binds */ input_id++; #endif - if (type == input_id + 1) + if (type >= input_id + 1 && type <= input_id + 3) return xmb->textures.list[XMB_TEXTURE_INPUT_SETTINGS]; - if (type == input_id + 2) + if (type == input_id + 4) return xmb->textures.list[XMB_TEXTURE_INPUT_MOUSE]; - if (type == input_id + 3) + if (type == input_id + 5) return xmb->textures.list[XMB_TEXTURE_INPUT_BIND_ALL]; - if (type == input_id + 4) + if (type == input_id + 6) return xmb->textures.list[XMB_TEXTURE_RELOAD]; - if (type == input_id + 5) + if (type == input_id + 7) return xmb->textures.list[XMB_TEXTURE_SAVING]; - if ((type > (input_id + 29)) && (type < (input_id + 41))) + if ((type > (input_id + 31)) && (type < (input_id + 43))) return xmb->textures.list[XMB_TEXTURE_INPUT_LGUN]; - if (type == input_id + 41) + if (type == input_id + 43) return xmb->textures.list[XMB_TEXTURE_INPUT_TURBO]; /* Align to use the same code of Quickmenu controls */ - input_id = input_id + 6; + input_id = input_id + 8; } else { diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h index e5e6fe6834f..515803bbb32 100644 --- a/menu/menu_cbs.h +++ b/menu/menu_cbs.h @@ -57,6 +57,7 @@ enum ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_DEVICE_TYPE, ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION, ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION_KBD, + ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_SELECT_RESERVED_DEVICE, #ifdef ANDROID ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD, #endif diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 8f006cad5b2..b1d9123c38c 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5562,6 +5562,111 @@ static int menu_displaylist_parse_input_device_type_list( return count; } +static int menu_displaylist_parse_input_select_reserved_device_list( + file_list_t *info_list, const char *info_path, + settings_t *settings) +{ + char device_label[128]; + const char *val_disabled = NULL; + enum msg_hash_enums enum_idx = (enum msg_hash_enums)atoi(info_path); + struct menu_state *menu_st = menu_state_get_ptr(); + rarch_setting_t *setting = menu_setting_find_enum(enum_idx); + size_t menu_index = 0; + unsigned count = 0; + int i = 0; + char reserved_device_name[sizeof(settings->arrays.input_reserved_devices[0])]; + bool device_added = false; + + device_label[0] = '\0'; + + if (!settings || !setting) + return 0; + + val_disabled = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE); + if (string_is_empty(settings->arrays.input_reserved_devices[enum_idx - MENU_ENUM_LABEL_INPUT_DEVICE_RESERVED_DEVICE_NAME])) + strlcpy(reserved_device_name, val_disabled, sizeof(reserved_device_name)); + else + strlcpy(reserved_device_name, settings->arrays.input_reserved_devices[enum_idx - MENU_ENUM_LABEL_INPUT_DEVICE_RESERVED_DEVICE_NAME], sizeof(reserved_device_name)); + + /* List elements: none/disabled, all existing reservations, all existing devices */ + for (i = MAX_INPUT_DEVICES+MAX_USERS; i >= 0; --i) + { + device_label[0] = '\0'; + + if (i == MAX_INPUT_DEVICES + MAX_USERS) + strlcpy(device_label, val_disabled, sizeof(device_label)); + else if (i < MAX_INPUT_DEVICES) + { + const char *device_name = input_config_get_device_display_name(i) + ? input_config_get_device_display_name(i) + : input_config_get_device_name(i); + + if (!string_is_empty(device_name)) + { + unsigned idx = input_config_get_device_name_index(i); + size_t _len = strlcpy(device_label, device_name, + sizeof(device_label)); + /* If idx is non-zero, it's part of a set*/ + if (idx > 0) + snprintf(device_label + _len, + sizeof(device_label) - _len, " (#%u)", idx); + } + } + else + { + if (!string_is_empty(settings->arrays.input_reserved_devices[i-MAX_INPUT_DEVICES])) + { + unsigned int vendor_id; + unsigned int product_id; + if (sscanf(settings->arrays.input_reserved_devices[i-MAX_INPUT_DEVICES], "%04x:%04x ", &vendor_id, &product_id) != 2) + strlcpy(device_label, settings->arrays.input_reserved_devices[i-MAX_INPUT_DEVICES], sizeof(reserved_device_name)); + else + /* If the vendor_id:product_id is encoded in the name, ignore them. */ + strlcpy(device_label, &settings->arrays.input_reserved_devices[i-MAX_INPUT_DEVICES][10], sizeof(reserved_device_name)); + } + } + + if (!string_is_empty(device_label)) + { + size_t previous_position; + if (file_list_search(info_list, device_label, &previous_position)) + continue; + + /* Add menu entry */ + if (menu_entries_append(info_list, + device_label, + device_label, + MSG_UNKNOWN, + MENU_SETTING_DROPDOWN_ITEM_INPUT_SELECT_RESERVED_DEVICE, + 0, menu_index, NULL)) + { + /* Add checkmark if input is currently + * mapped to this entry */ + if (string_is_equal(device_label, reserved_device_name)) + { + menu_file_list_cbs_t *cbs = (menu_file_list_cbs_t*)info_list->list[menu_index].actiondata; + if (cbs) + cbs->checked = true; + menu_st->selection_ptr = menu_index; + device_added = true; + } + count++; + menu_index++; + } + } + } + + /* if nothing is configured, select None by default */ + if (!device_added) + { + menu_file_list_cbs_t *cbs = (menu_file_list_cbs_t*)info_list->list[0].actiondata; + if (cbs) + cbs->checked = true; + menu_st->selection_ptr = 0; + } + return count; +} + #ifdef ANDROID static int menu_displaylist_parse_input_select_physical_keyboard_list( file_list_t *info_list, const char *info_path, @@ -13983,6 +14088,20 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, menu_entries_clear(info->list); count = menu_displaylist_parse_input_device_type_list(info->list, info->path, settings); + if (count == 0) + if (menu_entries_append(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ENTRIES_TO_DISPLAY), + msg_hash_to_str(MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY), + MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY, + FILE_TYPE_NONE, 0, 0, NULL)) + count++; + info->flags |= MD_FLAG_NEED_REFRESH + | MD_FLAG_NEED_PUSH; + break; + case DISPLAYLIST_DROPDOWN_LIST_INPUT_SELECT_RESERVED_DEVICE: + menu_entries_clear(info->list); + count = menu_displaylist_parse_input_select_reserved_device_list(info->list, info->path, settings); + if (count == 0) if (menu_entries_append(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ENTRIES_TO_DISPLAY), diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h index d3bba901ccf..8a27ccbb9eb 100644 --- a/menu/menu_displaylist.h +++ b/menu/menu_displaylist.h @@ -78,6 +78,7 @@ enum menu_displaylist_ctl_state DISPLAYLIST_DROPDOWN_LIST_INPUT_DEVICE_TYPE, DISPLAYLIST_DROPDOWN_LIST_INPUT_DESCRIPTION, DISPLAYLIST_DROPDOWN_LIST_INPUT_DESCRIPTION_KBD, + DISPLAYLIST_DROPDOWN_LIST_INPUT_SELECT_RESERVED_DEVICE, #ifdef ANDROID DISPLAYLIST_DROPDOWN_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD, #endif diff --git a/menu/menu_driver.h b/menu/menu_driver.h index e56be9fdd75..254afafc1e2 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -110,6 +110,7 @@ enum menu_settings_type MENU_SETTING_DROPDOWN_ITEM_DISK_INDEX, MENU_SETTING_DROPDOWN_ITEM_INPUT_DEVICE_TYPE, MENU_SETTING_DROPDOWN_ITEM_INPUT_DEVICE_INDEX, + MENU_SETTING_DROPDOWN_ITEM_INPUT_SELECT_RESERVED_DEVICE, #ifdef ANDROID MENU_SETTING_DROPDOWN_ITEM_INPUT_SELECT_PHYSICAL_KEYBOARD, #endif diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 7dbc51b42b5..8ec65de5ae6 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -184,6 +184,10 @@ if (SETTINGS_LIST_APPEND(a, b)) \ config_string(a, b, c, d, e, f, g, h, i, j, k, l) +#define CONFIG_STRING_ALT(a, b, c, d, e, f, g, h, i, j, k, l) \ + if (SETTINGS_LIST_APPEND(a, b)) \ + config_string_alt(a, b, c, d, e, f, g, h, i, j, k, l) + #define CONFIG_FLOAT(a, b, c, d, e, f, g, h, i, j, k, l) \ if (SETTINGS_LIST_APPEND(a, b)) \ config_float(a, b, c, d, e, f, g, h, i, j, k, l) @@ -2398,6 +2402,26 @@ static void config_string( MENU_SETTINGS_LIST_CURRENT_ADD_ENUM_VALUE_IDX(list, list_info, SHORT_enum_idx); } +static void config_string_alt( + rarch_setting_t **list, + rarch_setting_info_t *list_info, + char *target, size_t len, + char *label, + char* shortname, + const char *default_value, + rarch_setting_group_info_t *group_info, + rarch_setting_group_info_t *subgroup_info, + const char *parent_group, + change_handler_t change_handler, change_handler_t read_handler) +{ + (*list)[list_info->index++] = setting_string_setting(ST_STRING, + label, + shortname, + target, (unsigned)len, default_value, "", + group_info->name, subgroup_info->name, parent_group, + change_handler, read_handler, true); +} + static void config_string_options( rarch_setting_t **list, rarch_setting_info_t *list_info, @@ -2750,6 +2774,20 @@ static int setting_action_ok_select_physical_keyboard( } #endif +static int setting_action_ok_select_reserved_device( + rarch_setting_t *setting, size_t idx, bool wraparound) +{ + char enum_idx[16]; + if (!setting) + return -1; + snprintf(enum_idx, sizeof(enum_idx), "%d", setting->enum_idx); + generic_action_ok_displaylist_push( + enum_idx, /* we will pass the enumeration index of the string as a path */ + NULL, NULL, 0, idx, 0, + ACTION_OK_DL_DROPDOWN_BOX_LIST_INPUT_SELECT_RESERVED_DEVICE); + return 0; +} + #if !defined(RARCH_CONSOLE) static int setting_string_action_ok_audio_device( rarch_setting_t *setting, size_t idx, bool wraparound) @@ -5705,6 +5743,27 @@ static int setting_action_left_input_device_index( return 0; } +static int setting_action_left_input_device_reservation_type( + rarch_setting_t *setting, size_t idx, bool wraparound) +{ + settings_t *settings = config_get_ptr(); + unsigned *p = NULL; + + if (!setting || !settings) + return -1; + + p = &settings->uints.input_device_reservation_type[setting->index_offset]; + + if (*p) + (*p)--; + else + *p = INPUT_DEVICE_RESERVATION_LAST - 1; + + settings->modified = true; + return 0; +} + + static int setting_action_left_input_mouse_index( rarch_setting_t *setting, size_t idx, bool wraparound) { @@ -7649,6 +7708,32 @@ static int setting_action_start_input_device_index(rarch_setting_t *setting) return 0; } +static int setting_action_start_input_device_reservation_type(rarch_setting_t *setting) +{ + settings_t *settings = config_get_ptr(); + + if (!setting || !settings) + return -1; + + configuration_set_uint(settings, + settings->uints.input_device_reservation_type[setting->index_offset], + INPUT_DEVICE_RESERVATION_NONE); + return 0; +} + +static int setting_action_start_input_device_reserved_device_name(rarch_setting_t *setting) +{ + settings_t *settings = config_get_ptr(); + + if (!setting || !settings) + return -1; + + configuration_set_string(settings, + settings->arrays.input_reserved_devices[setting->index_offset], + ""); + return 0; +} + static int setting_action_start_custom_viewport_width(rarch_setting_t *setting) { video_viewport_t vp; @@ -7913,6 +7998,26 @@ static int setting_action_right_input_device_index( return 0; } +static int setting_action_right_input_device_reservation_type( + rarch_setting_t *setting, size_t idx, bool wraparound) +{ + settings_t *settings = config_get_ptr(); + unsigned *p = NULL; + + if (!setting || !settings) + return -1; + + p = &settings->uints.input_device_reservation_type[setting->index_offset]; + + if (*p < INPUT_DEVICE_RESERVATION_LAST - 1) + (*p)++; + else + *p = 0; + + settings->modified = true; + return 0; +} + static int setting_action_right_input_mouse_index( rarch_setting_t *setting, size_t idx, bool wraparound) { @@ -8018,6 +8123,50 @@ static void get_string_representation_input_device_index( strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISABLED), len); } +static void get_string_representation_input_device_reservation_type( + rarch_setting_t *setting, char *s, size_t len) +{ + settings_t *settings = config_get_ptr(); + unsigned map = 0; + + if (!setting || !settings) + return; + + map = settings->uints.input_device_reservation_type[setting->index_offset]; + + if (map == INPUT_DEVICE_RESERVATION_NONE) + { + strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DEVICE_RESERVATION_NONE), len); + } + else if (map == INPUT_DEVICE_RESERVATION_PREFERRED) + { + strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DEVICE_RESERVATION_PREFERRED), len); + } + else if (map == INPUT_DEVICE_RESERVATION_RESERVED) + { + strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DEVICE_RESERVATION_RESERVED), len); + } + else + strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISABLED), len); +} + +static void setting_get_string_representation_input_device_reserved_device_name( + rarch_setting_t *setting, + char *s, size_t len) +{ + settings_t *settings = config_get_ptr(); + int dev_vendor_id; + int dev_product_id; + + if (!setting) + return; + + if (sscanf(setting->value.target.string, "%04x:%04x ", &dev_vendor_id, &dev_product_id) != 2) + strlcpy(s, setting->value.target.string, len); + else + strlcpy(s, &setting->value.target.string[10], len); +} + static void get_string_representation_input_mouse_index( rarch_setting_t *setting, char *s, size_t len) { @@ -9331,6 +9480,8 @@ static bool setting_append_list_input_player_options( { static char device_index[MAX_USERS][64]; + static char device_reservation_type[MAX_USERS][64]; + static char device_reserved_device[MAX_USERS][64]; static char mouse_index[MAX_USERS][64]; static char analog_to_digital[MAX_USERS][64]; static char bind_all[MAX_USERS][64]; @@ -9338,6 +9489,8 @@ static bool setting_append_list_input_player_options( static char bind_defaults[MAX_USERS][64]; static char label_device_index[MAX_USERS][64]; + static char label_device_reservation_type[MAX_USERS][64]; + static char label_device_reserved_device[MAX_USERS][64]; static char label_mouse_index[MAX_USERS][64]; static char label_analog_to_digital[MAX_USERS][64]; static char label_bind_all[MAX_USERS][64]; @@ -9350,17 +9503,21 @@ static bool setting_append_list_input_player_options( #endif snprintf(analog_to_digital[user], sizeof(analog_to_digital[user]), - msg_hash_to_str(MENU_ENUM_LABEL_INPUT_PLAYER_ANALOG_DPAD_MODE), user + 1); + msg_hash_to_str(MENU_ENUM_LABEL_INPUT_PLAYER_ANALOG_DPAD_MODE), user + 1); snprintf(device_index[user], sizeof(device_index[user]), - msg_hash_to_str(MENU_ENUM_LABEL_INPUT_JOYPAD_INDEX), user + 1); + msg_hash_to_str(MENU_ENUM_LABEL_INPUT_JOYPAD_INDEX), user + 1); + snprintf(device_reservation_type[user], sizeof(device_reservation_type[user]), + msg_hash_to_str(MENU_ENUM_LABEL_INPUT_DEVICE_RESERVATION_TYPE), user + 1); + snprintf(device_reserved_device[user], sizeof(device_reserved_device[user]), + msg_hash_to_str(MENU_ENUM_LABEL_INPUT_DEVICE_RESERVED_DEVICE_NAME), user + 1); snprintf(mouse_index[user], sizeof(mouse_index[user]), - msg_hash_to_str(MENU_ENUM_LABEL_INPUT_MOUSE_INDEX), user + 1); + msg_hash_to_str(MENU_ENUM_LABEL_INPUT_MOUSE_INDEX), user + 1); snprintf(bind_all[user], sizeof(bind_all[user]), - msg_hash_to_str(MENU_ENUM_LABEL_INPUT_BIND_ALL_INDEX), user + 1); + msg_hash_to_str(MENU_ENUM_LABEL_INPUT_BIND_ALL_INDEX), user + 1); snprintf(bind_all_save_autoconfig[user], sizeof(bind_all_save_autoconfig[user]), - msg_hash_to_str(MENU_ENUM_LABEL_INPUT_SAVE_AUTOCONFIG_INDEX), user + 1); + msg_hash_to_str(MENU_ENUM_LABEL_INPUT_SAVE_AUTOCONFIG_INDEX), user + 1); snprintf(bind_defaults[user], sizeof(bind_defaults[user]), - msg_hash_to_str(MENU_ENUM_LABEL_INPUT_BIND_DEFAULTS_INDEX), user + 1); + msg_hash_to_str(MENU_ENUM_LABEL_INPUT_BIND_DEFAULTS_INDEX), user + 1); strlcpy(label_analog_to_digital[user], msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_ADC_TYPE), @@ -9368,6 +9525,12 @@ static bool setting_append_list_input_player_options( strlcpy(label_device_index[user], msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_INDEX), sizeof(label_device_index[user])); + strlcpy(label_device_reservation_type[user], + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_RESERVATION_TYPE), + sizeof(label_device_reservation_type[user])); + strlcpy(label_device_reserved_device[user], + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_RESERVED_DEVICE_NAME), + sizeof(label_device_reserved_device[user])); strlcpy(label_mouse_index[user], msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_MOUSE_INDEX), sizeof(label_mouse_index[user])); @@ -9461,6 +9624,54 @@ static bool setting_append_list_input_player_options( MENU_SETTINGS_LIST_CURRENT_ADD_ENUM_IDX_PTR(list, list_info, (enum msg_hash_enums)(MENU_ENUM_LABEL_INPUT_DEVICE_INDEX + user)); + CONFIG_UINT_ALT( + list, list_info, + &settings->uints.input_device_reservation_type[user], + device_reservation_type[user], + label_device_reservation_type[user], + INPUT_DEVICE_RESERVATION_NONE, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].index = user + 1; + (*list)[list_info->index - 1].index_offset = user; + (*list)[list_info->index - 1].action_start = &setting_action_start_input_device_reservation_type; + (*list)[list_info->index - 1].action_left = &setting_action_left_input_device_reservation_type; + (*list)[list_info->index - 1].action_right = &setting_action_right_input_device_reservation_type; + (*list)[list_info->index - 1].action_select = &setting_action_right_input_device_reservation_type; + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + (*list)[list_info->index - 1].get_string_representation = + &get_string_representation_input_device_reservation_type; + menu_settings_list_current_add_range(list, list_info, 0, INPUT_DEVICE_RESERVATION_LAST - 1, 1.0, true, true); + MENU_SETTINGS_LIST_CURRENT_ADD_ENUM_IDX_PTR(list, list_info, + (enum msg_hash_enums)(MENU_ENUM_LABEL_INPUT_DEVICE_RESERVATION_TYPE + user)); + + CONFIG_STRING_ALT( + list, list_info, + settings->arrays.input_reserved_devices[user], + sizeof(settings->arrays.input_reserved_devices[user]), + device_reserved_device[user], + label_device_reserved_device[user], + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE), + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].index = user + 1; + (*list)[list_info->index - 1].index_offset = user; + (*list)[list_info->index - 1].action_ok = &setting_action_ok_select_reserved_device; + (*list)[list_info->index - 1].get_string_representation = + &setting_get_string_representation_input_device_reserved_device_name; + (*list)[list_info->index - 1].action_start = &setting_action_start_input_device_reserved_device_name; + + MENU_SETTINGS_LIST_CURRENT_ADD_ENUM_IDX_PTR(list, list_info, + (enum msg_hash_enums)(MENU_ENUM_LABEL_INPUT_DEVICE_RESERVED_DEVICE_NAME + user)); + MENU_SETTINGS_LIST_CURRENT_ADD_ENUM_VALUE_IDX(list, list_info, + (enum msg_hash_enums)(MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_RESERVED_DEVICE_NAME)); + CONFIG_UINT_ALT( list, list_info, &settings->uints.input_mouse_index[user], diff --git a/msg_hash.h b/msg_hash.h index 9b12f46e1cf..2920e25b567 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -871,6 +871,10 @@ enum msg_hash_enums MENU_ENUM_LABEL_INPUT_PLAYER_ANALOG_DPAD_MODE_LAST = MENU_ENUM_LABEL_INPUT_PLAYER_ANALOG_DPAD_MODE + MAX_USERS, MENU_ENUM_LABEL_INPUT_DEVICE_INDEX, MENU_ENUM_LABEL_INPUT_DEVICE_INDEX_LAST = MENU_ENUM_LABEL_INPUT_DEVICE_INDEX + MAX_USERS, + MENU_ENUM_LABEL_INPUT_DEVICE_RESERVATION_TYPE, + MENU_ENUM_LABEL_INPUT_DEVICE_RESERVATION_TYPE_LAST = MENU_ENUM_LABEL_INPUT_DEVICE_RESERVATION_TYPE + MAX_USERS, + MENU_ENUM_LABEL_INPUT_DEVICE_RESERVED_DEVICE_NAME, + MENU_ENUM_LABEL_INPUT_DEVICE_RESERVED_DEVICE_NAME_LAST = MENU_ENUM_LABEL_INPUT_DEVICE_RESERVED_DEVICE_NAME + MAX_USERS, MENU_ENUM_LABEL_INPUT_MOUSE_INDEX, MENU_ENUM_LABEL_INPUT_MOUSE_INDEX_LAST = MENU_ENUM_LABEL_INPUT_MOUSE_INDEX + MAX_USERS, MENU_ENUM_LABEL_INPUT_REMAP_PORT, @@ -1156,6 +1160,11 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_INPUT_SAVE_AUTOCONFIG, MENU_ENUM_LABEL_VALUE_INPUT_MOUSE_INDEX, MENU_ENUM_LABEL_INPUT_JOYPAD_INDEX, + MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_RESERVED_DEVICE_NAME, + MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_RESERVATION_TYPE, + MENU_ENUM_LABEL_VALUE_DEVICE_RESERVATION_NONE, + MENU_ENUM_LABEL_VALUE_DEVICE_RESERVATION_PREFERRED, + MENU_ENUM_LABEL_VALUE_DEVICE_RESERVATION_RESERVED, MENU_ENUM_LABEL_INPUT_BIND_ALL_INDEX, MENU_ENUM_LABEL_INPUT_SAVE_AUTOCONFIG_INDEX, MENU_ENUM_LABEL_INPUT_BIND_DEFAULTS_INDEX, @@ -1163,6 +1172,9 @@ enum msg_hash_enums MENU_ENUM_SUBLABEL_INPUT_DEVICE_TYPE, MENU_ENUM_SUBLABEL_INPUT_DEVICE_INDEX, + MENU_ENUM_SUBLABEL_INPUT_DEVICE_RESERVED_DEVICE_NAME, + MENU_ENUM_SUBLABEL_INPUT_DEVICE_RESERVATION_TYPE, + MENU_ENUM_LABEL_HELP_INPUT_DEVICE_RESERVATION_TYPE, MENU_ENUM_SUBLABEL_INPUT_MOUSE_INDEX, MENU_ENUM_SUBLABEL_INPUT_ADC_TYPE, MENU_ENUM_LABEL_HELP_INPUT_ADC_TYPE, @@ -1969,8 +1981,9 @@ enum msg_hash_enums MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DEVICE_TYPE, MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION, MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_DESCRIPTION_KBD, + MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_RESERVED_DEVICE, #ifdef ANDROID - MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD, + MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_INPUT_SELECT_PHYSICAL_KEYBOARD, #endif MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_NETPLAY_MITM_SERVER, MENU_ENUM_LABEL_DEFERRED_MIXER_STREAM_SETTINGS_LIST, diff --git a/tasks/task_autodetect.c b/tasks/task_autodetect.c index bf96d2a50a2..c200bd38f13 100644 --- a/tasks/task_autodetect.c +++ b/tasks/task_autodetect.c @@ -354,6 +354,155 @@ static bool input_autoconfigure_scan_config_files_internal( return false; } +/* Reallocate the automatically assigned player <-> port mapping if needed. + * Objectives: + * - if there is reservation for the device, assign it to the reserved player + * - when assigning a new device to a reserved port, move the previous entry + * to first free slot if it was occupied + * - use first free player port by default for new entries (overriding saved + * input_joypad_index, as it can + * get quite messy if reservations are done, due to the swaps above) + * - do not consider "reserved" ports free + * - if there is no reservation, do not change anything + * (not even the assignment to first free player port) + */ +static void reallocate_port_if_needed(unsigned detected_port, int vendor_id, + int product_id, const char *device_name) +{ + settings_t *settings = config_get_ptr(); + + unsigned player; + unsigned first_free_player_slot = MAX_USERS + 1; + unsigned prev_assigned_player_slots[MAX_USERS]; + bool device_has_reserved_slot = false; + bool no_reservation_at_all = true; + char settings_value[256] = {0}; + int settings_value_vendor_id; + int settings_value_product_id; + char settings_value_device_name[256]; + + for (player = 0; player < MAX_USERS; player++) + { + if (first_free_player_slot > settings->uints.input_max_users && + ( detected_port == settings->uints.input_joypad_index[player] || + !input_config_get_device_name(settings->uints.input_joypad_index[player])) && + settings->uints.input_device_reservation_type[player] != INPUT_DEVICE_RESERVATION_RESERVED ) + { + first_free_player_slot = player; + RARCH_DBG("[Autoconf]: First unconfigured / unreserved player is %d\n", + player+1); + } + prev_assigned_player_slots[settings->uints.input_joypad_index[player]] = player; + if (settings->uints.input_device_reservation_type[player] != INPUT_DEVICE_RESERVATION_NONE) + no_reservation_at_all = false; + } + if (first_free_player_slot > settings->uints.input_max_users) { + RARCH_ERR( "[Autoconf]: No free and unreserved player slots found for adding new device" + " \"%s\"! Detected port %d, max_users: %d, first free slot %d\n", + device_name, detected_port, + settings->uints.input_max_users, + first_free_player_slot+1); + RARCH_WARN("[Autoconf]: Leaving detected player slot in place: %d\n", + prev_assigned_player_slots[detected_port]); + return; + } + + for (player = 0; player < MAX_USERS; player++) + { + if (settings->uints.input_device_reservation_type[player] != INPUT_DEVICE_RESERVATION_NONE) + strlcpy(settings_value, settings->arrays.input_reserved_devices[player], + sizeof(settings_value)); + else + settings_value[0] = '\0'; + + if (!string_is_empty(settings_value)) + { + RARCH_DBG("[Autoconf]: Examining reserved device for player %d " + "type %d: %s against %04x:%04x\n", + player+1, + settings->uints.input_device_reservation_type[player], + settings_value, vendor_id, product_id); + + if (sscanf(settings_value, "%04x:%04x ", + &settings_value_vendor_id, + &settings_value_product_id) != 2) + { + strlcpy(settings_value_device_name, settings_value, + sizeof(settings_value_device_name)); + device_has_reserved_slot = string_is_equal(device_name, settings_value_device_name); + } + else + device_has_reserved_slot = (vendor_id == settings_value_vendor_id && + product_id == settings_value_product_id); + + if (device_has_reserved_slot) + { + RARCH_DBG("[Autoconf]: Reserved device matched\n"); + break; + } + } + } + + if (device_has_reserved_slot) + { + unsigned prev_assigned_port = settings->uints.input_joypad_index[player]; + if(detected_port != prev_assigned_port) + { + RARCH_LOG("[Autoconf]: Device \"%s\" (%x:%x) is reserved " + "for player %d, updating.\n", + device_name, vendor_id, product_id, player+1); + + /* todo: fix the pushed info message */ + settings->uints.input_joypad_index[player] = detected_port; + + RARCH_LOG("[Autoconf]: Preferred slot was taken earlier by " + "\"%s\", reassigning that to %d\n", + input_config_get_device_name(prev_assigned_port), + prev_assigned_player_slots[detected_port]+1); + settings->uints.input_joypad_index[prev_assigned_player_slots[detected_port]] = prev_assigned_port; + if (input_config_get_device_name(prev_assigned_port)) + { + unsigned prev_assigned_port_l2 = settings->uints.input_joypad_index[first_free_player_slot]; + + RARCH_LOG("[Autoconf]: 2nd level reassignment, moving " + "previously assigned port %d to first free player %d\n", + prev_assigned_port_l2, first_free_player_slot+1); + settings->uints.input_joypad_index[prev_assigned_player_slots[detected_port]] = prev_assigned_port_l2; + settings->uints.input_joypad_index[first_free_player_slot] = prev_assigned_port; + } + } + else + { + RARCH_DBG("[Autoconf]: Device \"%s\" (%x:%x) is reserved for " + "player %d, same as default assignment.\n", + device_name, vendor_id, product_id, player+1); + } + return; + } + else + { + RARCH_DBG("[Autoconf]: Device \"%s\" (%d:%d) is not reserved for " + "any player slot.\n", + device_name, vendor_id, product_id); + /* Fallback in case no reservation is set up at all - to preserve any previous setup where input_joypad_index may have been customized. */ + if (no_reservation_at_all || + prev_assigned_player_slots[detected_port] == first_free_player_slot) + { + return; + } + else + { + unsigned prev_assigned_port = settings->uints.input_joypad_index[first_free_player_slot]; + settings->uints.input_joypad_index[first_free_player_slot] = detected_port; + settings->uints.input_joypad_index[prev_assigned_player_slots[detected_port]] = prev_assigned_port; + RARCH_DBG("[Autoconf]: Earlier free player slot found, " + "reassigning to player %d.\n", + first_free_player_slot+1); + } + } + return; +} + /*************************/ /* Autoconfigure Connect */ /*************************/ @@ -427,6 +576,9 @@ static void cb_input_autoconfigure_connect( if (autoconfig_handle->device_info.autoconfigured) input_config_set_autoconfig_binds(port, autoconfig_handle->autoconfig_file); + + reallocate_port_if_needed(port,autoconfig_handle->device_info.vid, autoconfig_handle->device_info.pid,autoconfig_handle->device_info.name); + } static void input_autoconfigure_connect_handler(retro_task_t *task) diff --git a/tests-other/autoconf/TestpadA.cfg b/tests-other/autoconf/TestpadA.cfg new file mode 100644 index 00000000000..29f801fce3e --- /dev/null +++ b/tests-other/autoconf/TestpadA.cfg @@ -0,0 +1,27 @@ +input_driver = "test" +input_device = "Test joypad device A" +input_b_btn = "0" +input_y_btn = "1" +input_select_btn = "2" +input_start_btn = "3" +input_up_btn = "4" +input_down_btn = "5" +input_left_btn = "6" +input_right_btn = "7" +input_a_btn = "8" +input_x_btn = "9" +input_l_btn = "10" +input_r_btn = "11" +input_l2_btn = "12" +input_r2_btn = "13" +input_l3_btn = "14" +input_r3_btn = "15" +input_l_x_plus_axis = "+0" +input_l_x_minus_axis = "-0" +input_l_y_plus_axis = "+1" +input_l_y_minus_axis = "-1" +input_r_x_plus_axis = "+2" +input_r_x_minus_axis = "-2" +input_r_y_plus_axis = "+3" +input_r_y_minus_axis = "-3" + diff --git a/tests-other/autoconf/TestpadB.cfg b/tests-other/autoconf/TestpadB.cfg new file mode 100644 index 00000000000..9ace48b8a82 --- /dev/null +++ b/tests-other/autoconf/TestpadB.cfg @@ -0,0 +1,27 @@ +input_driver = "test" +input_device = "Test joypad device B" +input_b_btn = "0" +input_y_btn = "1" +input_select_btn = "2" +input_start_btn = "3" +input_up_btn = "4" +input_down_btn = "5" +input_left_btn = "6" +input_right_btn = "7" +input_a_btn = "8" +input_x_btn = "9" +input_l_btn = "10" +input_r_btn = "11" +input_l2_btn = "12" +input_r2_btn = "13" +input_l3_btn = "14" +input_r3_btn = "15" +input_l_x_plus_axis = "+0" +input_l_x_minus_axis = "-0" +input_l_y_plus_axis = "+1" +input_l_y_minus_axis = "-1" +input_r_x_plus_axis = "+2" +input_r_x_minus_axis = "-2" +input_r_y_plus_axis = "+3" +input_r_y_minus_axis = "-3" + diff --git a/tests-other/autoconf/TestpadC.cfg b/tests-other/autoconf/TestpadC.cfg new file mode 100644 index 00000000000..1fb363c3ca3 --- /dev/null +++ b/tests-other/autoconf/TestpadC.cfg @@ -0,0 +1,27 @@ +input_driver = "test" +input_device = "Test joypad device C" +input_b_btn = "0" +input_y_btn = "1" +input_select_btn = "2" +input_start_btn = "3" +input_up_btn = "4" +input_down_btn = "5" +input_left_btn = "6" +input_right_btn = "7" +input_a_btn = "8" +input_x_btn = "9" +input_l_btn = "10" +input_r_btn = "11" +input_l2_btn = "12" +input_r2_btn = "13" +input_l3_btn = "14" +input_r3_btn = "15" +input_l_x_plus_axis = "+0" +input_l_x_minus_axis = "-0" +input_l_y_plus_axis = "+1" +input_l_y_minus_axis = "-1" +input_r_x_plus_axis = "+2" +input_r_x_minus_axis = "-2" +input_r_y_plus_axis = "+3" +input_r_y_minus_axis = "-3" + diff --git a/tests-other/test_input_joypad.ratst b/tests-other/test_input_joypad.ratst index 1b8d08222c8..16ec28c04f2 100644 --- a/tests-other/test_input_joypad.ratst +++ b/tests-other/test_input_joypad.ratst @@ -2,18 +2,18 @@ { "action": 1, "param_num": 0, - "param_str": "Test joypad device" + "param_str": "(0001:0002) Test joypad device A", + "frame": 0 }, { "action": 1, "param_num": 1, - "param_str": "Test joypad device", - "frame": 0 + "param_str": "(0003:0004) Test joypad device B" }, { "action": 1, "param_num": 2, - "param_str": "Test joypad device" + "param_str": "(0005:0006) Test joypad device C" }, { "action": 16, @@ -224,4 +224,4 @@ "action": 1002, "param_num": 0 } -] \ No newline at end of file +] diff --git a/tests-other/test_input_joypad_device_reservation.ratst b/tests-other/test_input_joypad_device_reservation.ratst new file mode 100644 index 00000000000..5325d8ac91b --- /dev/null +++ b/tests-other/test_input_joypad_device_reservation.ratst @@ -0,0 +1,41 @@ +[ +{ + "action": 1, + "param_num": 0, + "param_str": "(0001:0002) Test joypad device A", + "frame": 0 +}, +{ + "action": 1, + "param_num": 1, + "param_str": "(0003:0004) Test joypad device B" +}, +{ + "action": 1, + "param_num": 2, + "param_str": "(0005:0006) Test joypad device C" +}, +{ + "action": 2, + "param_num": 1, + "frame": 1200 +}, +{ + "action": 2, + "param_num": 2 +}, +{ + "action": 1, + "param_num": 1, + "param_str": "(0005:0006) Test joypad device C" +}, +{ + "action": 2, + "param_num": 0 +}, +{ + "action": 1, + "param_num": 4, + "param_str": "(0001:0002) Test joypad device A" +} +] diff --git a/tests-other/testinput.cfg b/tests-other/testinput.cfg index 06e0784a1dc..494b04e197b 100644 --- a/tests-other/testinput.cfg +++ b/tests-other/testinput.cfg @@ -11,3 +11,9 @@ frontend_log_level = "0" libretro_log_level = "0" log_verbosity = "true" config_save_on_exit = "false" +input_player4_reserved_device = "Test joypad device B" +input_player4_device_reservation_type = "2" +input_player1_reserved_device = "Test joypad device C" +input_player1_device_reservation_type = "1" +input_player2_reserved_device = "fake reservation 2" +input_player3_reserved_device = "fake reservation 3" diff --git a/tests-other/testinput_device_reservation_test.cfg b/tests-other/testinput_device_reservation_test.cfg new file mode 100644 index 00000000000..4529555dcb6 --- /dev/null +++ b/tests-other/testinput_device_reservation_test.cfg @@ -0,0 +1,30 @@ +# Test configuration file to be used with --appendconfig. +# Sets up joypad driver, test input file for the joypad driver, +# logging and autoconfig dir, and prevents saving. +# Usage: +# retroarch --appendconfig tests_other/testinput_device_reservation_test.cfg\|tests_other/all_binds_empty.cfg + +# Test scenario: +# - Player 1 has preferred device C, Player 4 has reserved device B +# - Player 2 and 3 has some fake reserved device names, without reservation +# - Connect controller A, then B, then C +# - VALIDATE: check that Player 1 - C, Player 2 - A, Player 3 - none, Player 4 - B +# - Disconnect controller C, then B (after 20 sec) +# - Reconnect controller C to port 1 (instead of 2) +# - Reconnect controller A to port 4 (instead of 0) +# - VALIDATE: check that Player 1 is still assigned to C, and player 2 remained A +# - VALIDATE: due to reshuffle, Player 3 - N/A(4), Player 4 - N/A(3), Player 5 - N/A(1). + +input_joypad_driver = "test" +test_input_file_joypad = "tests-other/test_input_joypad_device_reservation.ratst" +joypad_autoconfig_dir = "tests-other/autoconf" +frontend_log_level = "0" +libretro_log_level = "0" +log_verbosity = "true" +config_save_on_exit = "false" +input_player1_reserved_device = "Test joypad device C" +input_player1_device_reservation_type = "1" +input_player2_reserved_device = "fake reservation 2" +input_player3_reserved_device = "fake reservation 3" +input_player4_reserved_device = "Test joypad device B" +input_player4_device_reservation_type = "2"