From cfa778c0fdc76717cdc322d9d98692f1ef305571 Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Wed, 17 Dec 2025 20:54:54 -0500 Subject: [PATCH] add "hold" button handling to overlays --- config.def.keybinds.h | 28 ++++++++++++++++++++++ configuration.c | 1 + input/input_defines.h | 2 ++ input/input_driver.c | 55 ++++++++++++++++++++++++++++++++++++++++++- input/input_driver.h | 1 + input/input_types.h | 9 +++++++ intl/msg_hash_us.h | 4 ++++ msg_hash.h | 1 + retroarch.c | 11 +++++++++ 9 files changed, 111 insertions(+), 1 deletion(-) diff --git a/config.def.keybinds.h b/config.def.keybinds.h index 0450af305fc4..85d3d51ec09a 100644 --- a/config.def.keybinds.h +++ b/config.def.keybinds.h @@ -275,6 +275,13 @@ static const struct retro_keybind retro_keybinds_1[] = { RARCH_TURBO_ENABLE, NO_BTN, NO_BTN, 0, true }, + { + NULL, NULL, + AXIS_NONE, AXIS_NONE, + MENU_ENUM_LABEL_VALUE_INPUT_HOLD, RETROK_UNKNOWN, + RARCH_HOLD_ENABLE, NO_BTN, NO_BTN, 0, + true + }, /* Hotkeys */ { NULL, NULL, @@ -935,6 +942,13 @@ static const struct retro_keybind retro_keybinds_1[] = { RARCH_TURBO_ENABLE, NO_BTN, NO_BTN, 0, true }, + { + NULL, NULL, + AXIS_NONE, AXIS_NONE, + MENU_ENUM_LABEL_VALUE_INPUT_HOLD, RETROK_UNKNOWN, + RARCH_HOLD_ENABLE, NO_BTN, NO_BTN, 0, + true + }, /* Hotkeys */ { NULL, NULL, @@ -1595,6 +1609,13 @@ static const struct retro_keybind retro_keybinds_1[] = { RARCH_TURBO_ENABLE, NO_BTN, NO_BTN, 0, true }, + { + NULL, NULL, + AXIS_NONE, AXIS_NONE, + MENU_ENUM_LABEL_VALUE_INPUT_HOLD, RETROK_UNKNOWN, + RARCH_HOLD_ENABLE, NO_BTN, NO_BTN, 0, + true + }, /* Hotkeys */ { NULL, NULL, @@ -2269,6 +2290,13 @@ static const struct retro_keybind retro_keybinds_rest[] = { RARCH_TURBO_ENABLE, NO_BTN, NO_BTN, 0, true }, + { + NULL, NULL, + AXIS_NONE, AXIS_NONE, + MENU_ENUM_LABEL_VALUE_INPUT_HOLD, RETROK_UNKNOWN, + RARCH_HOLD_ENABLE, NO_BTN, NO_BTN, 0, + true + }, /* Hotkeys */ { NULL, NULL, diff --git a/configuration.c b/configuration.c index b815b7c304a9..1df0be0c64d5 100644 --- a/configuration.c +++ b/configuration.c @@ -334,6 +334,7 @@ const struct input_bind_map input_config_bind_map[RARCH_BIND_LIST_END_NULL] = { DECLARE_BIND(gun_dpad_right, RARCH_LIGHTGUN_DPAD_RIGHT, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_RIGHT), DECLARE_BIND(turbo, RARCH_TURBO_ENABLE, MENU_ENUM_LABEL_VALUE_INPUT_TURBO), + DECLARE_BIND(hold, RARCH_HOLD_ENABLE, MENU_ENUM_LABEL_VALUE_INPUT_HOLD), DECLARE_META_BIND(2, enable_hotkey, RARCH_ENABLE_HOTKEY, MENU_ENUM_LABEL_VALUE_INPUT_META_ENABLE_HOTKEY), #ifdef HAVE_MENU diff --git a/input/input_defines.h b/input/input_defines.h index 06ad5b9bb307..9e7f580d387e 100644 --- a/input/input_defines.h +++ b/input/input_defines.h @@ -118,6 +118,8 @@ enum /* Turbo */ RARCH_TURBO_ENABLE = RARCH_FIRST_MISC_CUSTOM_BIND, + /* Hold */ + RARCH_HOLD_ENABLE, RARCH_CUSTOM_BIND_LIST_END, diff --git a/input/input_driver.c b/input/input_driver.c index 5334198024cc..1435719c9f0e 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -1515,11 +1515,40 @@ static int16_t input_state_device( if (id <= RETRO_DEVICE_ID_JOYPAD_R3) { - /* Apply turbo button if activated. */ uint8_t turbo_period = settings->uints.input_turbo_period; uint8_t turbo_duty_cycle = settings->uints.input_turbo_duty_cycle; uint8_t turbo_mode = settings->uints.input_turbo_mode; + /* Apply hold button logic. + * When hold modifier is pressed, tapping buttons toggles their held state. + * Held buttons report as pressed even when not physically touched. + * Physical presses pass through normally (no modification). */ + + /* Handle hold modifier state and toggle logic */ + if (!input_st->hold_btns.frame_enable[port]) + { + /* Hold modifier not pressed - clear edge detection state */ + input_st->hold_btns.hold_pressed[port] = 0; + } + else + { + /* Hold modifier is pressed - handle toggle on rising edge */ + if (!res) + input_st->hold_btns.hold_pressed[port] &= ~(1 << id); + else if (!(input_st->hold_btns.hold_pressed[port] & (1 << id))) + { + /* Rising edge - toggle hold for this button */ + input_st->hold_btns.hold_pressed[port] |= (1 << id); + input_st->hold_btns.enable[port] ^= (1 << id); + } + } + + /* Apply hold effect: if button is held and not physically pressed */ + if (!res && (input_st->hold_btns.enable[port] & (1 << id))) + res = 1; + + /* Apply turbo button if activated. */ + /* Don't allow classic mode turbo for D-pad unless explicitly allowed. */ if ( turbo_mode <= INPUT_TURBO_MODE_CLASSIC_TOGGLE && !settings->bools.input_turbo_allow_dpad @@ -6091,7 +6120,10 @@ void input_driver_poll(void) if (input_st->flags & INP_FLAG_BLOCK_LIBRETRO_INPUT) { for (i = 0; i < max_users; i++) + { input_st->turbo_btns.frame_enable[i] = 0; + input_st->hold_btns.frame_enable[i] = 0; + } return; } @@ -6130,6 +6162,27 @@ void input_driver_poll(void) #endif } + /* Poll hold button modifier state */ + for (i = 0; i < max_users; i++) + { + input_st->hold_btns.frame_enable[i] = + (*input_st->libretro_input_binds[i])[RARCH_HOLD_ENABLE].valid ? + 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, RARCH_HOLD_ENABLE) : 0; + +#ifdef HAVE_OVERLAY + if ( (i == 0) + && input_st->overlay_ptr + && (input_st->overlay_ptr->flags & INPUT_OVERLAY_ALIVE) + && BIT256_GET(input_st->overlay_ptr->overlay_state.buttons, RARCH_HOLD_ENABLE)) + input_st->hold_btns.frame_enable[i] = true; +#endif + } + #ifdef HAVE_MENU if (!(menu_state_get_ptr()->flags & MENU_ST_FLAG_ALIVE)) #endif diff --git a/input/input_driver.h b/input/input_driver.h index 3b54a0fba212..ab67c4495fe6 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -624,6 +624,7 @@ typedef struct #endif int osk_ptr; turbo_buttons_t turbo_btns; /* int32_t alignment */ + hold_buttons_t hold_btns; /* int32_t alignment */ input_mapper_t mapper; /* uint32_t alignment */ input_remap_cache_t remapping_cache; diff --git a/input/input_types.h b/input/input_types.h index b5b97ccb4d31..879a5c654aaa 100644 --- a/input/input_types.h +++ b/input/input_types.h @@ -62,6 +62,14 @@ struct turbo_buttons bool mode1_enable[MAX_USERS]; }; +/* Hold button support. */ +struct hold_buttons +{ + int32_t hold_pressed[MAX_USERS]; /* Edge detection for toggle */ + uint16_t enable[MAX_USERS]; /* Bitmask of held buttons */ + bool frame_enable[MAX_USERS]; /* Hold modifier pressed this frame */ +}; + struct retro_keybind { /* Human-readable label for the control. */ @@ -139,6 +147,7 @@ typedef struct rarch_joypad_info rarch_joypad_info_t; typedef struct input_driver input_driver_t; typedef struct input_keyboard_ctx_wait input_keyboard_ctx_wait_t; typedef struct turbo_buttons turbo_buttons_t; +typedef struct hold_buttons hold_buttons_t; typedef struct joypad_connection joypad_connection_t; #endif /* __INPUT_TYPES__H */ diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index e27b0b521e15..c729aa9b4762 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -4581,6 +4581,10 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_TURBO, "Turbo Fire" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_INPUT_HOLD, + "Hold" + ) /* Settings > Latency */ diff --git a/msg_hash.h b/msg_hash.h index 42012d692493..f0d941a015f8 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1129,6 +1129,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_LEFT, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_RIGHT, MENU_ENUM_LABEL_VALUE_INPUT_TURBO, + MENU_ENUM_LABEL_VALUE_INPUT_HOLD, MENU_ENUM_LABEL_VALUE_INPUT_META_ENABLE_HOTKEY, MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE, diff --git a/retroarch.c b/retroarch.c index d4678031c920..91d7251b624c 100644 --- a/retroarch.c +++ b/retroarch.c @@ -1930,6 +1930,7 @@ static void retroarch_deinit_drivers(struct retro_callbacks *cbs) | INP_FLAG_NONBLOCKING); memset(&input_st->turbo_btns, 0, sizeof(turbo_buttons_t)); + memset(&input_st->hold_btns, 0, sizeof(hold_buttons_t)); memset(&input_st->analog_requested, 0, sizeof(input_st->analog_requested)); input_st->current_driver = NULL; @@ -4262,6 +4263,16 @@ bool command_event(enum event_command cmd, void *data) #endif runloop_event_deinit_core(); + /* Clear turbo and hold button state on core unload */ + { + input_driver_state_t *input_st = input_state_get_ptr(); + if (input_st) + { + memset(&input_st->turbo_btns, 0, sizeof(turbo_buttons_t)); + memset(&input_st->hold_btns, 0, sizeof(hold_buttons_t)); + } + } + #ifdef HAVE_RUNAHEAD /* If 'runahead_available' is false, then * runahead is enabled by the user but an