diff --git a/config.def.h b/config.def.h index abd1cc25b8..9a10753805 100644 --- a/config.def.h +++ b/config.def.h @@ -979,6 +979,8 @@ static const float analog_sensitivity = 1.0f; /* Describes speed of which turbo-enabled buttons toggle. */ static const unsigned turbo_period = 6; static const unsigned turbo_duty_cycle = 3; +static const unsigned turbo_mode = 0; +static const unsigned turbo_default_btn = RETRO_DEVICE_ID_JOYPAD_B; /* Enable input auto-detection. Will attempt to autoconfigure * gamepads, plug-and-play style. */ diff --git a/configuration.c b/configuration.c index 7249a4c035..3cf434e697 100644 --- a/configuration.c +++ b/configuration.c @@ -1735,6 +1735,8 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings, SETTING_UINT("input_bind_hold", &settings->uints.input_bind_hold, true, input_bind_hold, false); SETTING_UINT("input_turbo_period", &settings->uints.input_turbo_period, true, turbo_period, false); SETTING_UINT("input_duty_cycle", &settings->uints.input_turbo_duty_cycle, true, turbo_duty_cycle, false); + SETTING_UINT("input_turbo_mode", &settings->uints.input_turbo_mode, true, turbo_mode, false); + SETTING_UINT("input_turbo_default_button", &settings->uints.input_turbo_default_button, true, turbo_default_btn, false); SETTING_UINT("input_max_users", input_driver_get_uint(INPUT_ACTION_MAX_USERS), true, input_max_users, false); SETTING_UINT("fps_update_interval", &settings->uints.fps_update_interval, true, DEFAULT_FPS_UPDATE_INTERVAL, false); SETTING_UINT("input_menu_toggle_gamepad_combo", &settings->uints.input_menu_toggle_gamepad_combo, true, menu_toggle_gamepad_combo, false); diff --git a/configuration.h b/configuration.h index 97648b3361..c14c307741 100644 --- a/configuration.h +++ b/configuration.h @@ -464,6 +464,8 @@ typedef struct settings unsigned input_turbo_period; unsigned input_turbo_duty_cycle; + unsigned input_turbo_mode; + unsigned input_turbo_default_button; unsigned input_bind_timeout; unsigned input_bind_hold; diff --git a/input/input_driver.h b/input/input_driver.h index 02cb1d15e8..a71e20a655 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -64,6 +64,28 @@ enum input_toggle_type INPUT_TOGGLE_LAST }; +enum input_turbo_mode +{ + INPUT_TURBO_MODE_CLASSIC = 0, + INPUT_TURBO_MODE_SINGLEBUTTON, + INPUT_TURBO_MODE_LAST +}; + +enum input_turbo_default_button +{ + INPUT_TURBO_DEFAULT_BUTTON_B = 0, + INPUT_TURBO_DEFAULT_BUTTON_Y, + INPUT_TURBO_DEFAULT_BUTTON_A, + INPUT_TURBO_DEFAULT_BUTTON_X, + INPUT_TURBO_DEFAULT_BUTTON_L, + INPUT_TURBO_DEFAULT_BUTTON_R, + INPUT_TURBO_DEFAULT_BUTTON_L2, + INPUT_TURBO_DEFAULT_BUTTON_R2, + INPUT_TURBO_DEFAULT_BUTTON_L3, + INPUT_TURBO_DEFAULT_BUTTON_R3, + INPUT_TURBO_DEFAULT_BUTTON_LAST +}; + enum input_action { INPUT_ACTION_NONE = 0, diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index f33f5e2a8e..075cb36624 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -2191,3 +2191,7 @@ MSG_HASH(MENU_ENUM_LABEL_INPUT_MENU_SETTINGS, "input_menu_settings") MSG_HASH(MENU_ENUM_LABEL_INPUT_HAPTIC_FEEDBACK_SETTINGS, "input_haptic_feedback_settings") +MSG_HASH(MENU_ENUM_LABEL_INPUT_TURBO_MODE, + "input_turbo_mode") +MSG_HASH(MENU_ENUM_LABEL_INPUT_TURBO_DEFAULT_BUTTON, + "input_turbo_default_button") diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index 8b6d204c25..e74180ace1 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -1613,6 +1613,20 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) "Numbers are described in frames." ); break; + case MENU_ENUM_LABEL_INPUT_TURBO_MODE: + snprintf(s, len, + "Turbo Mode.\n" + " \n" + "Selects the general behavior of turbo mode." + ); + break; + case MENU_ENUM_LABEL_INPUT_TURBO_DEFAULT_BUTTON: + snprintf(s, len, + "Turbo Default Button.\n" + " \n" + "Default active button for Turbo Mode 'Single Button'.\n" + ); + break; case MENU_ENUM_LABEL_INPUT_DUTY_CYCLE: snprintf(s, len, "Duty cycle.\n" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 143e9aa6b6..0ac897ebc7 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1565,6 +1565,14 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_TURBO_PERIOD, "Turbo Period" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_INPUT_TURBO_MODE, + "Turbo Mode" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_INPUT_TURBO_DEFAULT_BUTTON, + "Turbo Default Button" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_USER_BINDS, "Port %u Binds" @@ -5615,6 +5623,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_DUTY_CYCLE, "Describes how long the period of a turbo-enabled button should be. Numbers are described in frames." ) +MSG_HASH( + MENU_ENUM_SUBLABEL_INPUT_TURBO_MODE, + "Selects the general behavior of turbo mode." + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_INPUT_TURBO_DEFAULT_BUTTON, + "Default active button for Turbo Mode 'Single Button'." + ) MSG_HASH( MENU_ENUM_SUBLABEL_VIDEO_VSYNC, "Synchronizes the output video of the graphics card to the refresh rate of the screen. Recommended." diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 22e156a12e..b75dff87c6 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -262,6 +262,8 @@ default_sublabel_macro(action_bind_sublabel_input_mouse_scale, MENU_ENUM_SUBLABE default_sublabel_macro(action_bind_sublabel_axis_threshold, MENU_ENUM_SUBLABEL_INPUT_BUTTON_AXIS_THRESHOLD) default_sublabel_macro(action_bind_sublabel_input_turbo_period, MENU_ENUM_SUBLABEL_INPUT_TURBO_PERIOD) default_sublabel_macro(action_bind_sublabel_input_duty_cycle, MENU_ENUM_SUBLABEL_INPUT_DUTY_CYCLE) + default_sublabel_macro(action_bind_sublabel_input_turbo_mode, MENU_ENUM_SUBLABEL_INPUT_TURBO_MODE) +default_sublabel_macro(action_bind_sublabel_input_turbo_default_button, MENU_ENUM_SUBLABEL_INPUT_TURBO_DEFAULT_BUTTON) default_sublabel_macro(action_bind_sublabel_video_vertical_sync, MENU_ENUM_SUBLABEL_VIDEO_VSYNC) default_sublabel_macro(action_bind_sublabel_video_adaptive_vsync, MENU_ENUM_SUBLABEL_VIDEO_ADAPTIVE_VSYNC) default_sublabel_macro(action_bind_sublabel_core_allow_rotate, MENU_ENUM_SUBLABEL_VIDEO_ALLOW_ROTATE) @@ -2490,6 +2492,12 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_INPUT_TURBO_PERIOD: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_turbo_period); break; + case MENU_ENUM_LABEL_INPUT_TURBO_MODE: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_turbo_mode); + break; + case MENU_ENUM_LABEL_INPUT_TURBO_DEFAULT_BUTTON: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_turbo_default_button); + break; case MENU_ENUM_LABEL_INPUT_BIND_TIMEOUT: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_bind_timeout); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 03b6a68431..18e35aabab 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -4591,6 +4591,14 @@ unsigned menu_displaylist_build_list(file_list_t *list, enum menu_displaylist_ct MENU_ENUM_LABEL_INPUT_DUTY_CYCLE, PARSE_ONLY_UINT, false) == 0) count++; + if (menu_displaylist_parse_settings_enum(list, + MENU_ENUM_LABEL_INPUT_TURBO_MODE, + PARSE_ONLY_UINT, false) == 0) + count++; + if (menu_displaylist_parse_settings_enum(list, + MENU_ENUM_LABEL_INPUT_TURBO_DEFAULT_BUTTON, + PARSE_ONLY_UINT, false) == 0) + count++; if (menu_displaylist_parse_settings_enum(list, MENU_ENUM_LABEL_INPUT_BIND_MODE, PARSE_ONLY_UINT, false) == 0) diff --git a/menu/menu_setting.c b/menu/menu_setting.c index cae490ab04..6a6650f2fa 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -5475,6 +5475,66 @@ static void setting_get_string_representation_toggle_gamepad_combo( } } +static void setting_get_string_representation_turbo_mode( + rarch_setting_t *setting, + char *s, size_t len) +{ + if (!setting) + return; + + switch (*setting->value.target.unsigned_integer) + { + case INPUT_TURBO_MODE_CLASSIC: + strlcpy(s, "Classic", len); + break; + case INPUT_TURBO_MODE_SINGLEBUTTON: + strlcpy(s, "Single Button", len); + break; + } +} + +static void setting_get_string_representation_turbo_default_button( + rarch_setting_t *setting, + char *s, size_t len) +{ + if (!setting) + return; + + switch (*setting->value.target.unsigned_integer) + { + case INPUT_TURBO_DEFAULT_BUTTON_B: + strlcpy(s, "B / Fire", len); + break; + case INPUT_TURBO_DEFAULT_BUTTON_Y: + strlcpy(s, "Y", len); + break; + case INPUT_TURBO_DEFAULT_BUTTON_A: + strlcpy(s, "A", len); + break; + case INPUT_TURBO_DEFAULT_BUTTON_X: + strlcpy(s, "X", len); + break; + case INPUT_TURBO_DEFAULT_BUTTON_L: + strlcpy(s, "L", len); + break; + case INPUT_TURBO_DEFAULT_BUTTON_R: + strlcpy(s, "R", len); + break; + case INPUT_TURBO_DEFAULT_BUTTON_L2: + strlcpy(s, "L2", len); + break; + case INPUT_TURBO_DEFAULT_BUTTON_R2: + strlcpy(s, "R2", len); + break; + case INPUT_TURBO_DEFAULT_BUTTON_L3: + strlcpy(s, "L3", len); + break; + case INPUT_TURBO_DEFAULT_BUTTON_R3: + strlcpy(s, "R3", len); + break; + } +} + #ifdef HAVE_NETWORKING static void setting_get_string_representation_netplay_share_digital( rarch_setting_t *setting, @@ -11215,6 +11275,40 @@ static bool setting_append_list( &subgroup_info, parent_group); + CONFIG_UINT( + list, list_info, + &settings->uints.input_turbo_mode, + MENU_ENUM_LABEL_INPUT_TURBO_MODE, + MENU_ENUM_LABEL_VALUE_INPUT_TURBO_MODE, + turbo_mode, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_UINT_COMBOBOX; + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + (*list)[list_info->index - 1].get_string_representation = + &setting_get_string_representation_turbo_mode; + menu_settings_list_current_add_range(list, list_info, 0, (INPUT_TURBO_MODE_LAST-1), 1, true, true); + + CONFIG_UINT( + list, list_info, + &settings->uints.input_turbo_default_button, + MENU_ENUM_LABEL_INPUT_TURBO_DEFAULT_BUTTON, + MENU_ENUM_LABEL_VALUE_INPUT_TURBO_DEFAULT_BUTTON, + turbo_default_btn, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_UINT_COMBOBOX; + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + (*list)[list_info->index - 1].get_string_representation = + &setting_get_string_representation_turbo_default_button; + menu_settings_list_current_add_range(list, list_info, 0, (INPUT_TURBO_DEFAULT_BUTTON_LAST-1), 1, true, true); + END_SUB_GROUP(list, list_info, parent_group); START_SUB_GROUP(list, list_info, "Binds", &group_info, &subgroup_info, parent_group); diff --git a/msg_hash.h b/msg_hash.h index 87f6f3eb69..8376bcafe1 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -691,6 +691,8 @@ enum msg_hash_enums MENU_ENUM_LABEL_INPUT_HOTKEY_BINDS_BEGIN, MENU_LABEL(INPUT_TURBO_PERIOD), + MENU_LABEL(INPUT_TURBO_MODE), + MENU_LABEL(INPUT_TURBO_DEFAULT_BUTTON), MENU_ENUM_LABEL_INPUT_PLAYER1_JOYPAD_INDEX, MENU_ENUM_LABEL_INPUT_PLAYER2_JOYPAD_INDEX, diff --git a/retroarch.c b/retroarch.c index 788fb94c8e..e0303c05a9 100644 --- a/retroarch.c +++ b/retroarch.c @@ -3216,6 +3216,8 @@ struct turbo_buttons { bool frame_enable[MAX_USERS]; uint16_t enable[MAX_USERS]; + bool mode1_enable[MAX_USERS]; + int32_t turbo_pressed[MAX_USERS]; unsigned count; }; @@ -13630,23 +13632,86 @@ static int16_t input_state_device( { /* * Apply turbo button if activated. - * - * If turbo button is held, all buttons pressed except - * for D-pad will go into a turbo mode. Until the button is - * released again, the input state will be modulated by a - * periodic pulse defined by the configured duty cycle. */ - if (res && input_driver_turbo_btns.frame_enable[port]) - input_driver_turbo_btns.enable[port] |= (1 << id); - else if (!res) - input_driver_turbo_btns.enable[port] &= ~(1 << id); - - if (input_driver_turbo_btns.enable[port] & (1 << id)) + if (settings->uints.input_turbo_mode == 1) { - /* if turbo button is enabled for this key ID */ - res = res && ((input_driver_turbo_btns.count - % settings->uints.input_turbo_period) - < settings->uints.input_turbo_duty_cycle); + /* Pressing turbo button toggles turbo mode on or off. + * Holding the button will + * pass through, else the pressed state will be modulated by a + * periodic pulse defined by the configured duty cycle. + */ + + /* Avoid detecting the turbo button being held as multiple toggles */ + if (!input_driver_turbo_btns.frame_enable[port]) + input_driver_turbo_btns.turbo_pressed[port] &= ~(1<<31); + else if (input_driver_turbo_btns.turbo_pressed[port]>=0) + { + input_driver_turbo_btns.turbo_pressed[port] |= (1<<31); + /* Toggle turbo for selected buttons. */ + if (!input_driver_turbo_btns.enable[port]) + { + static const int button_map[]={ + RETRO_DEVICE_ID_JOYPAD_B, + RETRO_DEVICE_ID_JOYPAD_Y, + RETRO_DEVICE_ID_JOYPAD_A, + RETRO_DEVICE_ID_JOYPAD_X, + RETRO_DEVICE_ID_JOYPAD_L, + RETRO_DEVICE_ID_JOYPAD_R, + RETRO_DEVICE_ID_JOYPAD_L2, + RETRO_DEVICE_ID_JOYPAD_R2, + RETRO_DEVICE_ID_JOYPAD_L3, + RETRO_DEVICE_ID_JOYPAD_R3}; + input_driver_turbo_btns.enable[port] = 1 << button_map[ + min( + sizeof(button_map)/sizeof(button_map[0])-1, + settings->uints.input_turbo_default_button)]; + } + input_driver_turbo_btns.mode1_enable[port] ^= 1; + } + + if (input_driver_turbo_btns.turbo_pressed[port] & 1<<31) + { + /* Avoid detecting buttons being held as multiple toggles */ + if (!res) + input_driver_turbo_btns.turbo_pressed[port] &= ~(1 << id); + else if (!(input_driver_turbo_btns.turbo_pressed[port] & (1 << id))) + { + input_driver_turbo_btns.turbo_pressed[port] |= 1 << id; + /* Toggle turbo for pressed button but make sure at least one button has turbo */ + uint16_t enable_new = input_driver_turbo_btns.enable[port] ^ (1 << id); + if (enable_new) + input_driver_turbo_btns.enable[port] = enable_new; + } + } + + if (!res && input_driver_turbo_btns.mode1_enable[port] && + input_driver_turbo_btns.enable[port] & (1 << id)) + { + /* if turbo button is enabled for this key ID */ + res = ((input_driver_turbo_btns.count + % settings->uints.input_turbo_period) + < settings->uints.input_turbo_duty_cycle); + } + } + else + { + /* If turbo button is held, all buttons pressed except + * for D-pad will go into a turbo mode. Until the button is + * released again, the input state will be modulated by a + * periodic pulse defined by the configured duty cycle. + */ + if (res && input_driver_turbo_btns.frame_enable[port]) + input_driver_turbo_btns.enable[port] |= (1 << id); + else if (!res) + input_driver_turbo_btns.enable[port] &= ~(1 << id); + + if (input_driver_turbo_btns.enable[port] & (1 << id)) + { + /* if turbo button is enabled for this key ID */ + res = res && ((input_driver_turbo_btns.count + % settings->uints.input_turbo_period) + < settings->uints.input_turbo_duty_cycle); + } } }