diff --git a/config.def.h b/config.def.h index a2f36dab486..bdb680d1aba 100644 --- a/config.def.h +++ b/config.def.h @@ -1124,6 +1124,10 @@ * input remap file */ #define DEFAULT_NOTIFICATION_SHOW_REMAP_LOAD true +/* Display a notification when a user is mapped to a core + * port on first button press */ +#define DEFAULT_NOTIFICATION_SHOW_USER_MAPPED_TO_CORE_PORT true + /* Display a notification when loading a * configuration override file */ #define DEFAULT_NOTIFICATION_SHOW_CONFIG_OVERRIDE_LOAD true @@ -1620,6 +1624,10 @@ * gamepads, plug-and-play style. */ #define DEFAULT_INPUT_AUTODETECT_ENABLE true +/* Map user to core port on first button press. First person + * to press a button is player 1, second is player 2, etc. */ +#define DEFAULT_INPUT_REMAP_PORTS_ON_BUTTON_PRESS false + /* Enables accelerometer/gyroscope/illuminance * sensor input, if supported */ #if defined(ANDROID) diff --git a/configuration.c b/configuration.c index ad7e0db6784..9b8f1ebe356 100644 --- a/configuration.c +++ b/configuration.c @@ -1934,6 +1934,7 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("notification_show_cheats_applied", &settings->bools.notification_show_cheats_applied, true, DEFAULT_NOTIFICATION_SHOW_CHEATS_APPLIED, false); SETTING_BOOL("notification_show_patch_applied", &settings->bools.notification_show_patch_applied, true, DEFAULT_NOTIFICATION_SHOW_PATCH_APPLIED, false); SETTING_BOOL("notification_show_remap_load", &settings->bools.notification_show_remap_load, true, DEFAULT_NOTIFICATION_SHOW_REMAP_LOAD, false); + SETTING_BOOL("notification_show_user_mapped_to_core_port", &settings->bools.notification_show_user_mapped_to_core_port, true, DEFAULT_NOTIFICATION_SHOW_USER_MAPPED_TO_CORE_PORT, false); SETTING_BOOL("notification_show_config_override_load", &settings->bools.notification_show_config_override_load, true, DEFAULT_NOTIFICATION_SHOW_CONFIG_OVERRIDE_LOAD, false); SETTING_BOOL("notification_show_set_initial_disk", &settings->bools.notification_show_set_initial_disk, true, DEFAULT_NOTIFICATION_SHOW_SET_INITIAL_DISK, false); SETTING_BOOL("notification_show_disk_control", &settings->bools.notification_show_disk_control, true, DEFAULT_NOTIFICATION_SHOW_DISK_CONTROL, false); @@ -2177,6 +2178,7 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("input_turbo_allow_dpad", &settings->bools.input_turbo_allow_dpad, true, DEFAULT_TURBO_ALLOW_DPAD, false); SETTING_BOOL("input_auto_mouse_grab", &settings->bools.input_auto_mouse_grab, true, DEFAULT_INPUT_AUTO_MOUSE_GRAB, false); SETTING_BOOL("input_remap_binds_enable", &settings->bools.input_remap_binds_enable, true, true, false); + SETTING_BOOL("input_remap_ports_on_button_press", &settings->bools.input_remap_ports_on_button_press, true, DEFAULT_INPUT_REMAP_PORTS_ON_BUTTON_PRESS, false); SETTING_BOOL("input_remap_sort_by_controller_enable", &settings->bools.input_remap_sort_by_controller_enable, true, false, false); SETTING_BOOL("input_hotkey_device_merge", &settings->bools.input_hotkey_device_merge, true, DEFAULT_INPUT_HOTKEY_DEVICE_MERGE, false); SETTING_BOOL("input_hotkey_follows_player1", &settings->bools.input_hotkey_follows_player1, true, DEFAULT_INPUT_HOTKEY_FOLLOWS_PLAYER1, false); diff --git a/configuration.h b/configuration.h index 57e8f129050..79b38afdd07 100644 --- a/configuration.h +++ b/configuration.h @@ -702,6 +702,7 @@ typedef struct settings /* Input */ bool input_remap_binds_enable; bool input_remap_sort_by_controller_enable; + bool input_remap_ports_on_button_press; bool input_autodetect_enable; bool input_sensors_enable; bool input_overlay_enable; @@ -758,6 +759,7 @@ typedef struct settings bool notification_show_cheats_applied; bool notification_show_patch_applied; bool notification_show_remap_load; + bool notification_show_user_mapped_to_core_port; bool notification_show_config_override_load; bool notification_show_set_initial_disk; bool notification_show_disk_control; diff --git a/input/input_driver.c b/input/input_driver.c index beba85e1b20..155211ca348 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -6013,6 +6013,103 @@ static void input_keys_pressed( input_st->input_hotkey_block_counter = 0; } +/* Ignore d-pad buttons to avoid false positives. + * Some wireless gamepad receivers report 0 values for all axes when the + * gamepad is powered off or not yet connected. Since 0 represents the + * "left" and "up" positions for d-pad axes, this creates false button + * presses that could trigger unwanted input mapping. + */ +static bool any_button_ex_dpad_pressed(int32_t state) +{ + static const uint32_t button_mask = + (1 << RETRO_DEVICE_ID_JOYPAD_A) | + (1 << RETRO_DEVICE_ID_JOYPAD_B) | + (1 << RETRO_DEVICE_ID_JOYPAD_X) | + (1 << RETRO_DEVICE_ID_JOYPAD_Y) | + (1 << RETRO_DEVICE_ID_JOYPAD_L) | + (1 << RETRO_DEVICE_ID_JOYPAD_R) | + (1 << RETRO_DEVICE_ID_JOYPAD_L2) | + (1 << RETRO_DEVICE_ID_JOYPAD_R2) | + (1 << RETRO_DEVICE_ID_JOYPAD_L3) | + (1 << RETRO_DEVICE_ID_JOYPAD_R3) | + (1 << RETRO_DEVICE_ID_JOYPAD_START) | + (1 << RETRO_DEVICE_ID_JOYPAD_SELECT); + + return (state & button_mask) != 0; +} + +/* Returns MAX_USERS if all core ports are mapped to a user. */ +static unsigned get_first_free_core_port(settings_t *settings) +{ + unsigned core_port_idx; + for (core_port_idx = 0; core_port_idx < MAX_USERS; core_port_idx++) + { + unsigned *core_port_users = settings->uints.input_remap_port_map[core_port_idx]; + if (core_port_users[0] == MAX_USERS) + return core_port_idx; + } + + return MAX_USERS; +} + +static void map_user_to_first_free_core_port(unsigned user_idx, settings_t *settings) +{ + unsigned core_port_idx; + + if (!settings || user_idx >= MAX_USERS) + { + RARCH_ERR("[Automap] Invalid parameters\n"); + return; + } + + /* Check if user is already mapped */ + if (settings->uints.input_remap_ports[user_idx] < MAX_USERS) + { + RARCH_LOG("[Automap] User %u already mapped to core port %u\n", + user_idx, settings->uints.input_remap_ports[user_idx]); + return; + } + + core_port_idx = get_first_free_core_port(settings); + + if (core_port_idx < MAX_USERS) + { + configuration_set_uint(settings, + settings->uints.input_remap_ports[user_idx], core_port_idx); + configuration_set_uint(settings, + settings->uints.input_libretro_device[core_port_idx], RETRO_DEVICE_JOYPAD); + + input_remapping_update_port_map(); + + RARCH_LOG("[Automap] user %u mapped to core port %u\n", user_idx, core_port_idx); + + if (settings->bools.notification_show_user_mapped_to_core_port) + { + unsigned joypad_idx = settings->uints.input_joypad_index[user_idx]; + const char *device_display_name = input_config_get_device_display_name(joypad_idx); + char msg[128] = { '\0'}; + + if (string_is_empty(device_display_name)) + snprintf(msg, sizeof(msg), + msg_hash_to_str(MSG_PORT_MAPPED_TO_CORE_PORT_NR), + user_idx + 1, + core_port_idx + 1); + else + snprintf(msg, sizeof(msg), + msg_hash_to_str(MSG_DEVICE_MAPPED_TO_CORE_PORT_NR), + device_display_name, + core_port_idx + 1); + + runloop_msg_queue_push(msg, strlen(msg), 1, 180, true, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + } + } + else + { + RARCH_LOG("[Automap] No free core ports - user %u not mapped\n", user_idx); + } +} + void input_driver_poll(void) { size_t i, j; @@ -6029,9 +6126,27 @@ void input_driver_poll(void) *sec_joypad = NULL; #endif bool input_remap_binds_enable = settings->bools.input_remap_binds_enable; + bool input_remap_ports_on_button_press + = settings->bools.input_remap_ports_on_button_press; float input_axis_threshold = settings->floats.input_axis_threshold; uint8_t max_users = (uint8_t)settings->uints.input_max_users; + /* This rarch_joypad_info_t struct contains the device index + autoconfig binds for the + * controller to be queried, and also (for unknown reasons) the analog axis threshold + * when mapping analog stick to dpad input. */ + for (i = 0; i < max_users; i++) + { + joypad_info[i].axis_threshold = input_axis_threshold; + joypad_info[i].joy_idx = settings->uints.input_joypad_index[i]; + joypad_info[i].auto_binds = input_autoconf_binds[joypad_info[i].joy_idx]; + } + +#ifdef HAVE_MENU + input_remap_ports_on_button_press = + input_remap_ports_on_button_press + && !(menu_state_get_ptr()->flags & MENU_ST_FLAG_ALIVE); +#endif + if (joypad && joypad->poll) joypad->poll(); if (sec_joypad && sec_joypad->poll) @@ -6040,22 +6155,49 @@ void input_driver_poll(void) && input_st->current_driver->poll) input_st->current_driver->poll(input_st->current_data); + if (input_remap_ports_on_button_press) + { + for (i = 0; i < max_users; i++) + { + bool user_already_mapped = settings->uints.input_remap_ports[i] < MAX_USERS; + + if (!user_already_mapped) + { + int32_t buttons_st = input_state_wrap(input_st->current_driver, input_st->current_data, + joypad, sec_joypad, &joypad_info[i], + (*input_st->libretro_input_binds), + (input_st->flags & INP_FLAG_KB_MAPPING_BLOCKED) ? true : false, + (unsigned)i, + RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); + + if (any_button_ex_dpad_pressed(buttons_st)) + map_user_to_first_free_core_port(i, settings); + } + } + } + #ifdef HAVE_OVERLAY if ( input_st->overlay_ptr && (input_st->overlay_ptr->flags & INPUT_OVERLAY_ALIVE)) { unsigned input_analog_dpad_mode = settings->uints.input_analog_dpad_mode[0]; + unsigned mapped_port = settings->uints.input_remap_ports[0]; + bool first_user_is_mapped = mapped_port < MAX_USERS; float input_overlay_opacity = (input_st->overlay_ptr->flags & INPUT_OVERLAY_IS_OSK) ? settings->floats.input_osk_overlay_opacity : settings->floats.input_overlay_opacity; + /* If user 0 is not mapped yet, use the port they would be mapped + * to on overlay button press to get the input_analog_dpad_mode. */ + if (!first_user_is_mapped && input_remap_ports_on_button_press) + mapped_port = get_first_free_core_port(settings); + switch (input_analog_dpad_mode) { case ANALOG_DPAD_LSTICK: case ANALOG_DPAD_RSTICK: { - unsigned mapped_port = settings->uints.input_remap_ports[0]; - if (input_st->analog_requested[mapped_port]) + if ((mapped_port < MAX_USERS) && input_st->analog_requested[mapped_port]) input_analog_dpad_mode = ANALOG_DPAD_NONE; } break; @@ -6077,6 +6219,12 @@ void input_driver_poll(void) input_overlay_opacity, input_analog_dpad_mode, settings->floats.input_axis_threshold); + + if (!first_user_is_mapped && input_remap_ports_on_button_press) + { + if (input_st->overlay_ptr->overlay_state.buttons.data[0]) + map_user_to_first_free_core_port(0, settings); + } } #endif @@ -6089,9 +6237,6 @@ void input_driver_poll(void) return; } - /* This rarch_joypad_info_t struct contains the device index + autoconfig binds for the - * controller to be queried, and also (for unknown reasons) the analog axis threshold - * when mapping analog stick to dpad input. */ for (i = 0; i < max_users; i++) { uint16_t button_id = RARCH_TURBO_ENABLE; @@ -6100,10 +6245,6 @@ void input_driver_poll(void) if (settings->ints.input_turbo_bind != -1) button_id = settings->ints.input_turbo_bind; - joypad_info[i].axis_threshold = input_axis_threshold; - joypad_info[i].joy_idx = settings->uints.input_joypad_index[i]; - joypad_info[i].auto_binds = input_autoconf_binds[joypad_info[i].joy_idx]; - input_st->turbo_btns.frame_enable[i] = (*input_st->libretro_input_binds[i])[button_id].valid && turbo_enable ? @@ -6142,8 +6283,10 @@ void input_driver_poll(void) { input_bits_t current_inputs; unsigned mapped_port = settings->uints.input_remap_ports[i]; - unsigned device = settings->uints.input_libretro_device[mapped_port] - & RETRO_DEVICE_MASK; + unsigned device = ((mapped_port < MAX_USERS) + ? settings->uints.input_libretro_device[mapped_port] + : RETRO_DEVICE_NONE + ) & RETRO_DEVICE_MASK; input_bits_t *p_new_state = (input_bits_t*)¤t_inputs; unsigned input_analog_dpad_mode = settings->uints.input_analog_dpad_mode[i]; @@ -6151,7 +6294,7 @@ void input_driver_poll(void) { case ANALOG_DPAD_LSTICK: case ANALOG_DPAD_RSTICK: - if (input_st->analog_requested[mapped_port]) + if ((mapped_port < MAX_USERS) && input_st->analog_requested[mapped_port]) input_analog_dpad_mode = ANALOG_DPAD_NONE; break; case ANALOG_DPAD_LSTICK_FORCED: @@ -6705,6 +6848,11 @@ void input_remapping_update_port_map(void) port_map_index[remap_port]++; } } + + /* Changing mapped port may leave a core port unused; + * reinitialise controllers to ensure that any such + * ports are set to 'RETRO_DEVICE_NONE' */ + command_event(CMD_EVENT_CONTROLLER_INIT, NULL); } void input_remapping_deinit(bool save_remap) @@ -6723,6 +6871,29 @@ void input_remapping_deinit(bool save_remap) | RUNLOOP_FLAG_REMAPS_GAME_ACTIVE); } +void input_remapping_set_remap_ports_defaults(void) +{ + unsigned i; + settings_t *settings = config_get_ptr(); + bool remap_on_button_press = settings->bools.input_remap_ports_on_button_press; + + for (i = 0; i < MAX_USERS; i++) + { + configuration_set_uint(settings, + settings->uints.input_remap_ports[i], + remap_on_button_press ? MAX_USERS : i); + + configuration_set_uint(settings, + settings->uints.input_libretro_device[i], + remap_on_button_press ? RETRO_DEVICE_NONE : RETRO_DEVICE_JOYPAD); + } + + /* Need to call 'input_remapping_update_port_map()' + * whenever 'settings->uints.input_remap_ports' + * is modified */ + input_remapping_update_port_map(); +} + void input_remapping_set_defaults(bool clear_cache) { unsigned i, j; @@ -6747,16 +6918,9 @@ void input_remapping_set_defaults(bool clear_cache) for (j = RARCH_FIRST_CUSTOM_BIND; j < (RARCH_FIRST_CUSTOM_BIND + 8); j++) configuration_set_uint(settings, settings->uints.input_remap_ids[i][j], j); - - /* Controller port remaps */ - configuration_set_uint(settings, - settings->uints.input_remap_ports[i], i); } - /* Need to call 'input_remapping_update_port_map()' - * whenever 'settings->uints.input_remap_ports' - * is modified */ - input_remapping_update_port_map(); + input_remapping_set_remap_ports_defaults(); /* Restore 'global' settings that were cached on * the last core init diff --git a/input/input_remapping.h b/input/input_remapping.h index 064770448fe..896a1ed6270 100644 --- a/input/input_remapping.h +++ b/input/input_remapping.h @@ -92,6 +92,30 @@ void input_remapping_deinit(bool save_remap); */ void input_remapping_set_defaults(bool clear_cache); +/** + * Used to set the default port mapping values within the `settings` struct. + * Only sets port mapping related values, not button/keyboard/analog remaps. + * + * If settings->bools.input_remap_ports_on_button_press is true, leave all + * users/devices disconnected. Users will be mapped to a core port on first + * button press. + * + * user 0 -> core port MAX_USERS (RETRO_DEVICE_NONE) + * user 1 -> core port MAX_USERS (RETRO_DEVICE_NONE) + * ... + * user n -> core port MAX_USERS (RETRO_DEVICE_NONE) + * + * Else map each user to a core port and set all devices to RETRO_DEVICE_JOYPAD: + * + * user 0 -> core port 0 (RETRO_DEVICE_JOYPAD) + * user 1 -> core port 1 (RETRO_DEVICE_JOYPAD) + * ... + * user n -> core port n (RETRO_DEVICE_JOYPAD) + * + * (where n = MAX_USERS - 1) + */ +void input_remapping_set_remap_ports_defaults(void); + /** * Checks `input_config_bind_map` for the requested `input_bind_map`, and if * the bind has been registered, returns its base. diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 514bbe2c4a1..3df3004d4e7 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -2170,6 +2170,10 @@ MSG_HASH( MENU_ENUM_LABEL_INPUT_REMAP_BINDS_ENABLE, "input_remap_binds_enable" ) +MSG_HASH( + MENU_ENUM_LABEL_INPUT_REMAP_PORTS_ON_BUTTON_PRESS, + "input_remap_ports_on_button_press" + ) MSG_HASH( MENU_ENUM_LABEL_INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE, "input_remap_sort_by_controller_enable" @@ -6266,6 +6270,10 @@ MSG_HASH( MENU_ENUM_LABEL_NOTIFICATION_SHOW_REMAP_LOAD, "notification_show_remap_load" ) +MSG_HASH( + MENU_ENUM_LABEL_NOTIFICATION_SHOW_USER_MAPPED_TO_CORE_PORT, + "notification_show_user_mapped_to_core_port" + ) MSG_HASH( MENU_ENUM_LABEL_NOTIFICATION_SHOW_CONFIG_OVERRIDE_LOAD, "notification_show_config_override_load" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 2c72e41a950..70ad32fa7fd 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -3337,6 +3337,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_REMAP_BINDS_ENABLE, "Override the input binds with the remapped binds set for the current core." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_INPUT_REMAP_PORTS_ON_BUTTON_PRESS, + "Remap Ports on Button Press" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_INPUT_REMAP_PORTS_ON_BUTTON_PRESS, + "First user to press a button becomes player 1, second becomes player 2, etc." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE, "Sort Remaps By Gamepad" @@ -6001,6 +6009,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_REMAP_LOAD, "Display an on-screen message when loading input remap files." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_NOTIFICATION_SHOW_USER_MAPPED_TO_CORE_PORT, + "Remap Ports on Button Press Notifications" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_USER_MAPPED_TO_CORE_PORT, + "Display an on-screen message when a user is mapped to a core port." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_NOTIFICATION_SHOW_CONFIG_OVERRIDE_LOAD, "Config Override Loaded Notifications" @@ -15554,6 +15570,14 @@ MSG_HASH( MSG_DEVICE_NOT_CONFIGURED_FALLBACK_NR, "%s (%u/%u) not configured, using fallback" ) +MSG_HASH( + MSG_DEVICE_MAPPED_TO_CORE_PORT_NR, + "%s is Player %u" + ) +MSG_HASH( + MSG_PORT_MAPPED_TO_CORE_PORT_NR, + "Port %u is Player %u" + ) MSG_HASH( MSG_BLUETOOTH_SCAN_COMPLETE, "Bluetooth scan complete." diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 07a3962e76d..dadd22d7517 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -688,6 +688,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_notification_show_cheats_applied, M DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_notification_show_patch_applied, MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_PATCH_APPLIED) #endif DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_notification_show_remap_load, MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_REMAP_LOAD) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_notification_show_user_mapped_to_core_port, MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_USER_MAPPED_TO_CORE_PORT) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_notification_show_config_override_load, MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_CONFIG_OVERRIDE_LOAD) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_notification_show_set_initial_disk, MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_SET_INITIAL_DISK) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_notification_show_disk_control, MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_DISK_CONTROL) @@ -824,6 +825,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_replay_max_keep, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_replay_checkpoint_interval, MENU_ENUM_SUBLABEL_REPLAY_CHECKPOINT_INTERVAL) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_replay_checkpoint_deserialize, MENU_ENUM_SUBLABEL_REPLAY_CHECKPOINT_DESERIALIZE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_remap_binds_enable, MENU_ENUM_SUBLABEL_INPUT_REMAP_BINDS_ENABLE) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_remap_ports_on_button_press, MENU_ENUM_SUBLABEL_INPUT_REMAP_PORTS_ON_BUTTON_PRESS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_remap_sort_by_controller_enable, MENU_ENUM_SUBLABEL_INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_autodetect_enable, MENU_ENUM_SUBLABEL_INPUT_AUTODETECT_ENABLE) #if defined(HAVE_DINPUT) || defined(HAVE_WINRAWINPUT) @@ -4147,6 +4149,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_INPUT_REMAP_BINDS_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_remap_binds_enable); break; + case MENU_ENUM_LABEL_INPUT_REMAP_PORTS_ON_BUTTON_PRESS: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_remap_ports_on_button_press); + break; case MENU_ENUM_LABEL_INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_remap_sort_by_controller_enable); break; @@ -4535,6 +4540,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_NOTIFICATION_SHOW_REMAP_LOAD: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_notification_show_remap_load); break; + case MENU_ENUM_LABEL_NOTIFICATION_SHOW_USER_MAPPED_TO_CORE_PORT: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_notification_show_user_mapped_to_core_port); + break; case MENU_ENUM_LABEL_NOTIFICATION_SHOW_CONFIG_OVERRIDE_LOAD: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_notification_show_config_override_load); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 8b3de10cbd2..2a875a88631 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -8278,6 +8278,7 @@ unsigned menu_displaylist_build_list( {MENU_ENUM_LABEL_INPUT_BIND_HOLD, PARSE_ONLY_UINT, true}, {MENU_ENUM_LABEL_INPUT_AUTODETECT_ENABLE, PARSE_ONLY_BOOL, true}, {MENU_ENUM_LABEL_INPUT_REMAP_BINDS_ENABLE, PARSE_ONLY_BOOL, true}, + {MENU_ENUM_LABEL_INPUT_REMAP_PORTS_ON_BUTTON_PRESS, PARSE_ONLY_BOOL, true}, {MENU_ENUM_LABEL_INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE, PARSE_ONLY_BOOL, true}, {MENU_ENUM_LABEL_INPUT_ICADE_ENABLE, PARSE_ONLY_BOOL, true}, {MENU_ENUM_LABEL_INPUT_SMALL_KEYBOARD_ENABLE, PARSE_ONLY_BOOL, true}, @@ -10853,6 +10854,7 @@ unsigned menu_displaylist_build_list( {MENU_ENUM_LABEL_NOTIFICATION_SHOW_PATCH_APPLIED, PARSE_ONLY_BOOL, false }, #endif {MENU_ENUM_LABEL_NOTIFICATION_SHOW_REMAP_LOAD, PARSE_ONLY_BOOL, false }, + {MENU_ENUM_LABEL_NOTIFICATION_SHOW_USER_MAPPED_TO_CORE_PORT, PARSE_ONLY_BOOL, false }, {MENU_ENUM_LABEL_NOTIFICATION_SHOW_CONFIG_OVERRIDE_LOAD, PARSE_ONLY_BOOL, false }, {MENU_ENUM_LABEL_NOTIFICATION_SHOW_SET_INITIAL_DISK, PARSE_ONLY_BOOL, false }, {MENU_ENUM_LABEL_NOTIFICATION_SHOW_DISK_CONTROL, PARSE_ONLY_BOOL, false }, @@ -12464,8 +12466,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, { unsigned j; - unsigned device = settings->uints.input_libretro_device[mapped_port]; - device &= RETRO_DEVICE_MASK; + unsigned device = ((mapped_port < MAX_USERS) + ? settings->uints.input_libretro_device[mapped_port] + : RETRO_DEVICE_NONE + ) & RETRO_DEVICE_MASK; if (device == RETRO_DEVICE_JOYPAD || device == RETRO_DEVICE_ANALOG) { diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 207bd6cc524..0bc52f1538c 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -5566,17 +5566,12 @@ static int setting_action_left_input_remap_port( if (settings->uints.input_remap_ports[port] > 0) settings->uints.input_remap_ports[port]--; else - settings->uints.input_remap_ports[port] = MAX_USERS - 1; + settings->uints.input_remap_ports[port] = MAX_USERS; /* Must be called whenever settings->uints.input_remap_ports * is modified */ input_remapping_update_port_map(); - /* Changing mapped port may leave a core port unused; - * reinitialise controllers to ensure that any such - * ports are set to 'RETRO_DEVICE_NONE' */ - command_event(CMD_EVENT_CONTROLLER_INIT, NULL); - menu_st->flags |= MENU_ST_FLAG_PREVENT_POPULATE | MENU_ST_FLAG_ENTRIES_NEED_REFRESH; return 0; @@ -6688,8 +6683,12 @@ static size_t setting_get_string_representation_uint_input_remap_port( rarch_setting_t *setting, char *s, size_t len) { if (setting) + { + if (*setting->value.target.unsigned_integer == MAX_USERS) + return strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE), len); return snprintf(s, len, "%u", *setting->value.target.unsigned_integer + 1); + } return 0; } @@ -7706,11 +7705,6 @@ static int setting_action_start_input_remap_port(rarch_setting_t *setting) * is modified */ input_remapping_update_port_map(); - /* Changing mapped port may leave a core port unused; - * reinitialise controllers to ensure that any such - * ports are set to 'RETRO_DEVICE_NONE' */ - command_event(CMD_EVENT_CONTROLLER_INIT, NULL); - menu_st->flags |= MENU_ST_FLAG_PREVENT_POPULATE | MENU_ST_FLAG_ENTRIES_NEED_REFRESH; return 0; @@ -7817,7 +7811,7 @@ static int setting_action_right_input_remap_port( port = setting->index_offset; - if (settings->uints.input_remap_ports[port] < MAX_USERS - 1) + if (settings->uints.input_remap_ports[port] < MAX_USERS) settings->uints.input_remap_ports[port]++; else settings->uints.input_remap_ports[port] = 0; @@ -7826,11 +7820,6 @@ static int setting_action_right_input_remap_port( * is modified */ input_remapping_update_port_map(); - /* Changing mapped port may leave a core port unused; - * reinitialise controllers to ensure that any such - * ports are set to 'RETRO_DEVICE_NONE' */ - command_event(CMD_EVENT_CONTROLLER_INIT, NULL); - menu_st->flags |= MENU_ST_FLAG_PREVENT_POPULATE | MENU_ST_FLAG_ENTRIES_NEED_REFRESH; return 0; @@ -8955,6 +8944,11 @@ static void general_write_handler(rarch_setting_t *setting) NULL, menu_st->userdata); } break; + case MENU_ENUM_LABEL_INPUT_REMAP_PORTS_ON_BUTTON_PRESS: + { + input_remapping_set_defaults(false); + } + break; default: /* Special cases */ @@ -8967,11 +8961,6 @@ static void general_write_handler(rarch_setting_t *setting) /* Must be called whenever settings->uints.input_remap_ports * is modified */ input_remapping_update_port_map(); - - /* Changing mapped port may leave a core port unused; - * reinitialise controllers to ensure that any such - * ports are set to 'RETRO_DEVICE_NONE' */ - command_event(CMD_EVENT_CONTROLLER_INIT, NULL); } break; @@ -9802,7 +9791,7 @@ static bool setting_append_list_input_remap_port_options( (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; (*list)[list_info->index - 1].get_string_representation = &setting_get_string_representation_uint_input_remap_port; - menu_settings_list_current_add_range(list, list_info, 0, MAX_USERS-1, 1.0, true, true); + menu_settings_list_current_add_range(list, list_info, 0, MAX_USERS, 1.0, true, true); MENU_SETTINGS_LIST_CURRENT_ADD_ENUM_IDX_PTR(list, list_info, (enum msg_hash_enums)(MENU_ENUM_LABEL_INPUT_REMAP_PORT + user)); } @@ -15641,6 +15630,22 @@ static bool setting_append_list( general_read_handler, SD_FLAG_ADVANCED ); + + CONFIG_BOOL( + list, list_info, + &settings->bools.input_remap_ports_on_button_press, + MENU_ENUM_LABEL_INPUT_REMAP_PORTS_ON_BUTTON_PRESS, + MENU_ENUM_LABEL_VALUE_INPUT_REMAP_PORTS_ON_BUTTON_PRESS, + DEFAULT_INPUT_REMAP_PORTS_ON_BUTTON_PRESS, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_ADVANCED + ); CONFIG_BOOL( list, list_info, @@ -17060,6 +17065,21 @@ static bool setting_append_list( general_read_handler, SD_FLAG_NONE); + CONFIG_BOOL( + list, list_info, + &settings->bools.notification_show_user_mapped_to_core_port, + MENU_ENUM_LABEL_NOTIFICATION_SHOW_USER_MAPPED_TO_CORE_PORT, + MENU_ENUM_LABEL_VALUE_NOTIFICATION_SHOW_USER_MAPPED_TO_CORE_PORT, + DEFAULT_NOTIFICATION_SHOW_USER_MAPPED_TO_CORE_PORT, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE); + CONFIG_BOOL( list, list_info, &settings->bools.notification_show_config_override_load, diff --git a/msg_hash.h b/msg_hash.h index 8a58ffde44c..4d715c38485 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -262,6 +262,8 @@ enum msg_hash_enums MSG_DEVICE_NOT_CONFIGURED_FALLBACK_NR, MSG_DEVICE_DISCONNECTED_FROM_PORT, /* deprecated */ MSG_DEVICE_DISCONNECTED_FROM_PORT_NR, + MSG_DEVICE_MAPPED_TO_CORE_PORT_NR, + MSG_PORT_MAPPED_TO_CORE_PORT_NR, MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, MSG_COMPILER, MSG_NATIVE, @@ -1334,6 +1336,7 @@ enum msg_hash_enums MENU_LABEL(INPUT_BIND_TIMEOUT), MENU_LABEL(INPUT_BIND_HOLD), MENU_LABEL(INPUT_REMAP_BINDS_ENABLE), + MENU_LABEL(INPUT_REMAP_PORTS_ON_BUTTON_PRESS), MENU_LABEL(INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE), MENU_LABEL(INPUT_OVERLAY_ENABLE), MENU_LABEL(INPUT_OSK_OVERLAY_ENABLE), @@ -3841,6 +3844,7 @@ enum msg_hash_enums MENU_LABEL(NOTIFICATION_SHOW_CHEATS_APPLIED), MENU_LABEL(NOTIFICATION_SHOW_PATCH_APPLIED), MENU_LABEL(NOTIFICATION_SHOW_REMAP_LOAD), + MENU_LABEL(NOTIFICATION_SHOW_USER_MAPPED_TO_CORE_PORT), MENU_LABEL(NOTIFICATION_SHOW_CONFIG_OVERRIDE_LOAD), MENU_LABEL(NOTIFICATION_SHOW_SET_INITIAL_DISK), MENU_LABEL(NOTIFICATION_SHOW_DISK_CONTROL), diff --git a/retroarch.c b/retroarch.c index 40b44ab5edb..c45237e7a69 100644 --- a/retroarch.c +++ b/retroarch.c @@ -5541,7 +5541,11 @@ bool command_event(enum event_command cmd, void *data) case CMD_EVENT_CONTROLLER_INIT: { rarch_system_info_t *sys_info = &runloop_st->system; - if (sys_info) + bool core_inited = (runloop_st->current_core.flags & RETRO_CORE_FLAG_INITED) ? true : false; + + /* Only (re)init controllers once the core is initialized otherwise the + * core might try to use callbacks that are not yet set. */ + if (sys_info && core_inited) command_event_init_controllers(sys_info, settings, settings->uints.input_max_users); } diff --git a/runloop.c b/runloop.c index 42641c182b9..bf549e6a0e6 100644 --- a/runloop.c +++ b/runloop.c @@ -4797,7 +4797,12 @@ bool runloop_event_init_core( * at runtime should not 'bleed through' into the * master config file */ input_remapping_cache_global_config(); + #ifdef HAVE_CONFIGFILE + /* The input_remap_ports_on_button_press setting might have been changed + * in the config file or in the override files loaded above. */ + input_remapping_set_remap_ports_defaults(); + if (auto_remaps_enable) config_load_remap(dir_input_remapping, &runloop_st->system); #endif