diff --git a/configuration.c b/configuration.c index c99b88bab7e..37ecace9921 100644 --- a/configuration.c +++ b/configuration.c @@ -1481,6 +1481,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)); @@ -1511,6 +1512,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 @@ -3718,6 +3737,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); + } } @@ -5251,7 +5274,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); @@ -5271,6 +5293,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 */ @@ -5605,6 +5630,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 6a164858055..b287043d3d1 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -316,6 +316,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; @@ -869,6 +871,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; @@ -7318,6 +7329,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) @@ -9249,6 +9319,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_scan.c b/menu/cbs/menu_cbs_scan.c index 50767265743..c8b55f2f841 100644 --- a/menu/cbs/menu_cbs_scan.c +++ b/menu/cbs/menu_cbs_scan.c @@ -231,7 +231,7 @@ static int action_scan_input_desc(const char *path, inp_desc_user = (unsigned)(player_no_str - 1); /* This hardcoded value may cause issues if any entries are added on top of the input binds */ - key = (unsigned)(idx - 6); + key = (unsigned)(idx - 8); /* Select the reorderer bind */ key = (key < RARCH_ANALOG_BIND_LIST_END) ? input_config_bind_order[key] : key; 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 cd1737ac6e2..58fde4ba414 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 e2fac7b8f1b..cce12078a9e 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 33bd9a7733c..d1b2bdb7e5c 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..d6797af8fd5 100644 --- a/tasks/task_autodetect.c +++ b/tasks/task_autodetect.c @@ -354,6 +354,168 @@ 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 > 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) + { + unsigned prev_assigned_port = settings->uints.input_joypad_index[player]; + if ( detected_port != prev_assigned_port && + !string_is_empty(input_config_get_device_name(prev_assigned_port)) && + (( settings_value_vendor_id == input_config_get_device_vid(prev_assigned_port) && + settings_value_product_id == input_config_get_device_pid(prev_assigned_port)) || + strcmp(input_config_get_device_name(prev_assigned_port), settings_value_device_name) == 0)) + { + RARCH_DBG("[Autoconf]: Same type of device already took this slot, continuing search\n"); + device_has_reserved_slot = false; + } + else + { + 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\" (%x:%x) 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 +589,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/test_input_joypad_device_reservation2.ratst b/tests-other/test_input_joypad_device_reservation2.ratst new file mode 100644 index 00000000000..afeaccf3964 --- /dev/null +++ b/tests-other/test_input_joypad_device_reservation2.ratst @@ -0,0 +1,33 @@ +[ +{ + "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": 1, + "param_num": 3, + "param_str": "(0005:0006) Test joypad device C" +}, +{ + "action": 1, + "param_num": 4, + "param_str": "(0005:0006) Test joypad device C" +}, +{ + "action": 1, + "param_num": 5, + "param_str": "(0005:0006) Test joypad device C" +} +] 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" diff --git a/tests-other/testinput_device_reservation_test2.cfg b/tests-other/testinput_device_reservation_test2.cfg new file mode 100644 index 00000000000..7be535a878b --- /dev/null +++ b/tests-other/testinput_device_reservation_test2.cfg @@ -0,0 +1,29 @@ +# 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 through 4 has preferred device C +# - Player 5 and 6 has no reserved device +# - Connect controller A, then B, then 4 instances of C +# - VALIDATE: check that Player 1 - C (1), Player 2 - C(2), Player 3 - C(3), Player 4 - C(4), +# Player 5 - A, Player 6 - B + +input_joypad_driver = "test" +test_input_file_joypad = "tests-other/test_input_joypad_device_reservation2.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 = "2" +input_player2_reserved_device = "Test joypad device C" +input_player2_device_reservation_type = "2" +input_player3_reserved_device = "0005:0006 Test joypad device C" +input_player3_device_reservation_type = "2" +input_player4_reserved_device = "0005:0006 " +input_player4_device_reservation_type = "2" +# input_max_users = "3" \ No newline at end of file