From 9e2349cea10c5bfe07fadfa9c06b8569bac9f315 Mon Sep 17 00:00:00 2001 From: sonninnos Date: Mon, 25 Aug 2025 21:53:52 +0300 Subject: [PATCH] Allow dupebinding menu toggle and hotkey enabler --- input/input_driver.c | 268 ++++++++++++++++++++++++++----------------- input/input_driver.h | 4 +- menu/menu_driver.c | 62 ++++++++-- 3 files changed, 216 insertions(+), 118 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index f8275d6cc123..a74da50bde4f 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -5577,7 +5577,7 @@ void input_pad_connect(unsigned port, input_device_driver_t *driver) static bool input_keys_pressed_other_sources( input_driver_state_t *input_st, unsigned i, - input_bits_t* p_new_state) + input_bits_t *p_new_state) { #ifdef HAVE_COMMAND int j; @@ -5623,8 +5623,10 @@ static void input_keys_pressed( bool input_hotkey_device_merge) { unsigned i; + int32_t ret = 0; input_driver_state_t *input_st = &input_driver_st; bool block_hotkey[RARCH_BIND_LIST_END]; + bool any_pressed = false; bool libretro_hotkey_set = binds[port][RARCH_ENABLE_HOTKEY].joykey != NO_BTN || binds[port][RARCH_ENABLE_HOTKEY].joyaxis != AXIS_NONE @@ -5660,75 +5662,71 @@ static void input_keys_pressed( input_st->flags |= INP_FLAG_BLOCK_LIBRETRO_INPUT; } else - { - input_st->input_hotkey_block_counter = 0; input_st->flags |= INP_FLAG_BLOCK_HOTKEY; - } } - if (!is_menu && binds[port][RARCH_GAME_FOCUS_TOGGLE].valid) - { - const struct retro_keybind *focus_binds_auto = - &input_autoconf_binds[port][RARCH_GAME_FOCUS_TOGGLE]; - const struct retro_keybind *focus_normal = - &binds[port][RARCH_GAME_FOCUS_TOGGLE]; + /* Prevent triggering menu actions after binding */ + if ( !(input_st->flags & INP_FLAG_MENU_PRESS_PENDING) + && menu_state_get_ptr()->input_driver_flushing_input) + input_st->flags |= INP_FLAG_WAIT_INPUT_RELEASE; - /* Allows Game Focus toggle hotkey to still work - * even though every hotkey is blocked */ - if (CHECK_INPUT_DRIVER_BLOCK_HOTKEY(focus_normal, focus_binds_auto)) - { - if (input_state_wrap( - input_st->current_driver, - input_st->current_data, - input_st->primary_joypad, - sec_joypad, - joypad_info, - binds, - (input_st->flags & INP_FLAG_KB_MAPPING_BLOCKED) ? true : false, - port, RETRO_DEVICE_JOYPAD, 0, - RARCH_GAME_FOCUS_TOGGLE)) - input_st->flags &= ~INP_FLAG_BLOCK_HOTKEY; - } - } + /* Check libretro input if emulated device type is active, + * except device type must be always active in menu. */ + if ( !(input_st->flags & INP_FLAG_BLOCK_LIBRETRO_INPUT) + && !(!is_menu && !input_config_get_device(port))) + ret = input_state_wrap( + input_st->current_driver, + input_st->current_data, + input_st->primary_joypad, + sec_joypad, + joypad_info, + binds, + (input_st->flags & INP_FLAG_KB_MAPPING_BLOCKED) ? true : false, + port, RETRO_DEVICE_JOYPAD, 0, + RETRO_DEVICE_ID_JOYPAD_MASK); + for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++) { - int32_t ret = 0; - bool libretro_input_pressed = false; - - /* Check libretro input if emulated device type is active, - * except device type must be always active in menu. */ - if ( !(input_st->flags & INP_FLAG_BLOCK_LIBRETRO_INPUT) - && !(!is_menu && !input_config_get_device(port))) - ret = input_state_wrap( - input_st->current_driver, - input_st->current_data, - input_st->primary_joypad, - sec_joypad, - joypad_info, - binds, - (input_st->flags & INP_FLAG_KB_MAPPING_BLOCKED) ? true : false, - port, RETRO_DEVICE_JOYPAD, 0, - RETRO_DEVICE_ID_JOYPAD_MASK); - - for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++) + if ( (ret & (UINT64_C(1) << i)) + || input_keys_pressed_other_sources(input_st, i, p_new_state)) { - if ( (ret & (UINT64_C(1) << i)) - || input_keys_pressed_other_sources(input_st, i, p_new_state)) - { - BIT256_SET_PTR(p_new_state, i); - libretro_input_pressed = true; - } + any_pressed = true; + if (input_st->flags & INP_FLAG_WAIT_INPUT_RELEASE) + continue; + + BIT256_SET_PTR(p_new_state, i); } + } - if (!libretro_input_pressed) + /* Read autoconf menu toggle regardless of 'enable_hotkey' + * unless 'enable_hotkey' is set in autoconf. */ + if ( !any_pressed + && !(input_st->flags & INP_FLAG_WAIT_INPUT_RELEASE) + && (input_autoconf_binds[port][RARCH_MENU_TOGGLE].joykey != NO_BTN) + && ( input_autoconf_binds[port][RARCH_ENABLE_HOTKEY].joykey == input_autoconf_binds[port][RARCH_MENU_TOGGLE].joykey + || input_autoconf_binds[port][RARCH_ENABLE_HOTKEY].joykey == NO_BTN) + && ( binds[port][RARCH_MENU_TOGGLE].joykey == input_autoconf_binds[port][RARCH_MENU_TOGGLE].joykey + || binds[port][RARCH_MENU_TOGGLE].joykey == NO_BTN)) + { + /* Ignore keyboard menu toggle button and check + * joypad menu toggle button for pressing + * it without 'enable_hotkey', because Guide button + * is not part of the usual buttons. */ + i = RARCH_MENU_TOGGLE; + + if (!(binds[port][i].valid + && input_state_wrap( + input_st->current_driver, + input_st->current_data, + input_st->primary_joypad, + sec_joypad, + joypad_info, + binds, + (input_st->flags & INP_FLAG_KB_MAPPING_BLOCKED) ? true : false, + port, RETRO_DEVICE_KEYBOARD, 0, + input_config_binds[port][i].key))) { - /* Ignore keyboard menu toggle button and check - * joypad menu toggle button for pressing - * it without 'enable_hotkey', because Guide button - * is not part of the usual buttons. */ - i = RARCH_MENU_TOGGLE; - - if (!(binds[port][i].valid + bool bit_pressed = binds[port][i].valid && input_state_wrap( input_st->current_driver, input_st->current_data, @@ -5737,31 +5735,42 @@ static void input_keys_pressed( joypad_info, binds, (input_st->flags & INP_FLAG_KB_MAPPING_BLOCKED) ? true : false, - port, RETRO_DEVICE_KEYBOARD, 0, - input_config_binds[port][i].key))) - { - bool bit_pressed = binds[port][i].valid - && input_state_wrap( - input_st->current_driver, - input_st->current_data, - input_st->primary_joypad, - sec_joypad, - joypad_info, - binds, - (input_st->flags & INP_FLAG_KB_MAPPING_BLOCKED) ? true : false, - port, RETRO_DEVICE_JOYPAD, 0, i); + port, RETRO_DEVICE_JOYPAD, 0, i); - if ( - bit_pressed - || BIT64_GET(lifecycle_state, i) - || input_keys_pressed_other_sources(input_st, i, p_new_state)) - { - BIT256_SET_PTR(p_new_state, i); - } - } + if ( bit_pressed + || BIT64_GET(lifecycle_state, i) + || input_keys_pressed_other_sources(input_st, i, p_new_state)) + input_st->flags |= INP_FLAG_MENU_PRESS_PENDING; + else if (input_st->flags & INP_FLAG_MENU_PRESS_PENDING) + /* Also set 'enable_hotkey' to prevent hotkey delay untrigger */ + BIT256_SET_PTR(p_new_state, RARCH_ENABLE_HOTKEY); } } + /* Ignore hotkey block delay when menu toggle and hotkey enabler share the same key */ + if ( !any_pressed + && !(input_st->flags & INP_FLAG_WAIT_INPUT_RELEASE) + && binds[port][RARCH_MENU_TOGGLE].key == binds[port][RARCH_ENABLE_HOTKEY].key) + { + i = RARCH_MENU_TOGGLE; + + if ( binds[port][i].valid + && input_state_wrap( + input_st->current_driver, + input_st->current_data, + input_st->primary_joypad, + sec_joypad, + joypad_info, + binds, + (input_st->flags & INP_FLAG_KB_MAPPING_BLOCKED) ? true : false, + port, RETRO_DEVICE_KEYBOARD, 0, + input_config_binds[port][i].key)) + input_st->flags |= INP_FLAG_MENU_PRESS_PENDING; + else if (input_st->flags & INP_FLAG_MENU_PRESS_PENDING) + /* Also set 'enable_hotkey' to prevent hotkey delay untrigger */ + BIT256_SET_PTR(p_new_state, RARCH_ENABLE_HOTKEY); + } + /* Hotkeys are only relevant for first port */ if (port > 0) return; @@ -5869,7 +5878,6 @@ static void input_keys_pressed( port, RETRO_DEVICE_JOYPAD, 0, i)) { - /* Only block if keyboard is not pressed */ if (!keyboard_hotkey_pressed) block_hotkey[i] = true; @@ -5885,41 +5893,86 @@ static void input_keys_pressed( block_hotkey[i] = false; } + if (!is_menu && binds[port][RARCH_GAME_FOCUS_TOGGLE].valid) { - for (i = RARCH_FIRST_META_KEY; i < RARCH_BIND_LIST_END; i++) + /* Never block Game Focus toggle hotkey */ + block_hotkey[RARCH_GAME_FOCUS_TOGGLE] = false; + } + + for (i = RARCH_FIRST_META_KEY; i < RARCH_BIND_LIST_END; i++) + { + bool other_pressed = input_keys_pressed_other_sources(input_st, i, p_new_state); + bool bit_pressed = binds[port][i].valid + && input_state_wrap( + input_st->current_driver, + input_st->current_data, + input_st->primary_joypad, + sec_joypad, + joypad_info, + binds, + (input_st->flags & INP_FLAG_KB_MAPPING_BLOCKED) ? true : false, + port, RETRO_DEVICE_JOYPAD, 0, + i); + + if ( bit_pressed + || other_pressed + || BIT64_GET(lifecycle_state, i)) { - bool other_pressed = input_keys_pressed_other_sources(input_st, i, p_new_state); - bool bit_pressed = binds[port][i].valid - && input_state_wrap( - input_st->current_driver, - input_st->current_data, - input_st->primary_joypad, - sec_joypad, - joypad_info, - binds, - (input_st->flags & INP_FLAG_KB_MAPPING_BLOCKED) ? true : false, - port, RETRO_DEVICE_JOYPAD, 0, - i); + any_pressed = true; + if (input_st->flags & INP_FLAG_WAIT_INPUT_RELEASE) + continue; - if ( bit_pressed - || other_pressed - || BIT64_GET(lifecycle_state, i)) + if (libretro_hotkey_set || keyboard_hotkey_set) + { + /* Do not block "other source" (input overlay) presses */ + if (block_hotkey[i] && !other_pressed) + continue; + } + + /* Set menu toggle on release */ + if (i == RARCH_MENU_TOGGLE) { - if (libretro_hotkey_set || keyboard_hotkey_set) + if (!(input_st->flags & INP_FLAG_MENU_PRESS_PENDING)) { - /* Do not block "other source" (input overlay) presses */ - if (block_hotkey[i] && !other_pressed) - continue; + input_st->flags |= INP_FLAG_MENU_PRESS_PENDING; + input_st->flags &= ~INP_FLAG_MENU_PRESS_CANCEL; } + continue; + } + else if (i != RARCH_ENABLE_HOTKEY) + input_st->flags |= INP_FLAG_MENU_PRESS_CANCEL; - BIT256_SET_PTR(p_new_state, i); + BIT256_SET_PTR(p_new_state, i); + } + else + { + if (i == RARCH_MENU_TOGGLE) + { + /* Untrigger menu if press was shorter than hotkey block delay */ + if ( (input_st->flags & INP_FLAG_MENU_PRESS_PENDING) + && !(input_st->flags & INP_FLAG_MENU_PRESS_CANCEL) + && !BIT256_GET_PTR(p_new_state, RARCH_ENABLE_HOTKEY) + && input_st->input_hotkey_block_counter + && input_st->input_hotkey_block_counter < input_hotkey_block_delay) + input_st->flags &= ~INP_FLAG_MENU_PRESS_PENDING; + + if (input_st->flags & INP_FLAG_MENU_PRESS_PENDING) + { + /* Forget menu press if any other hotkey was pressed */ + if (!(input_st->flags & INP_FLAG_MENU_PRESS_CANCEL)) + BIT256_SET_PTR(p_new_state, i); - /* Ignore all other hotkeys if menu toggle is pressed */ - if (i == RARCH_MENU_TOGGLE) - break; + input_st->flags &= ~(INP_FLAG_MENU_PRESS_PENDING | INP_FLAG_MENU_PRESS_CANCEL); + } } } } + + if ((input_st->flags & INP_FLAG_WAIT_INPUT_RELEASE) && !any_pressed) + input_st->flags &= ~INP_FLAG_WAIT_INPUT_RELEASE; + + if (input_st->flags & INP_FLAG_BLOCK_HOTKEY) + input_st->input_hotkey_block_counter = 0; } #ifdef HAVE_BSV_MOVIE @@ -7745,12 +7798,12 @@ void input_driver_collect_system_input(input_driver_state_t *input_st, {RETROK_PAGEDOWN, RETRO_DEVICE_ID_JOYPAD_R }, {RETROK_HOME, RETRO_DEVICE_ID_JOYPAD_L3 }, {RETROK_END, RETRO_DEVICE_ID_JOYPAD_R3 }, + /* Extra keys read regardless of 'enable_hotkey' */ {0, RARCH_QUIT_KEY }, /* 14 */ {0, RARCH_FULLSCREEN_TOGGLE_KEY }, {0, RARCH_UI_COMPANION_TOGGLE }, {0, RARCH_FPS_TOGGLE }, {0, RARCH_NETPLAY_HOST_TOGGLE }, - {0, RARCH_MENU_TOGGLE }, }; ids[14][0] = input_config_binds[0][RARCH_QUIT_KEY].key; @@ -7758,7 +7811,6 @@ void input_driver_collect_system_input(input_driver_state_t *input_st, ids[16][0] = input_config_binds[0][RARCH_UI_COMPANION_TOGGLE].key; ids[17][0] = input_config_binds[0][RARCH_FPS_TOGGLE].key; ids[18][0] = input_config_binds[0][RARCH_NETPLAY_HOST_TOGGLE].key; - ids[19][0] = input_config_binds[0][RARCH_MENU_TOGGLE].key; if (settings->bools.input_menu_swap_ok_cancel_buttons) { diff --git a/input/input_driver.h b/input/input_driver.h index 9d24b7f37a33..6fb2f4e62b99 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -181,7 +181,9 @@ enum input_driver_state_flags INP_FLAG_GRAB_MOUSE_STATE = (1 << 6), INP_FLAG_REMAPPING_CACHE_ACTIVE = (1 << 7), INP_FLAG_DEFERRED_WAIT_KEYS = (1 << 8), - INP_FLAG_WAIT_INPUT_RELEASE = (1 << 9) + INP_FLAG_WAIT_INPUT_RELEASE = (1 << 9), + INP_FLAG_MENU_PRESS_PENDING = (1 << 10), + INP_FLAG_MENU_PRESS_CANCEL = (1 << 11) }; #ifdef HAVE_BSV_MOVIE diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 8a0d04e52c69..9d8c99238f1a 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -97,8 +97,8 @@ extern u32 __nx_applet_type; void libnx_apply_overclock(void); #endif -/* Accelerated navigation buttons */ -#define NAVIGATION_BUTTONS 9 +/* Accelerated and latched navigation buttons */ +#define NAVIGATION_BUTTONS 12 struct key_desc key_descriptors[RARCH_MAX_KEYS] = { @@ -5229,9 +5229,10 @@ unsigned menu_event( static retro_time_t last_time_us = 0; static float delay_timer = 0.0f; static float delay_count = 0.0f; + static unsigned ok_old = 0; + static bool navigation_reset_delay = true; static bool hold_initial = true; static bool hold_reset = true; - static unsigned ok_old = 0; unsigned ret = MENU_ACTION_NOOP; bool set_scroll = false; unsigned new_scroll_accel = 0; @@ -5272,6 +5273,7 @@ unsigned menu_event( RETRO_DEVICE_ID_JOYPAD_A : RETRO_DEVICE_ID_JOYPAD_B; unsigned ok_current = BIT256_GET_PTR(p_input, menu_ok_btn); unsigned ok_trigger = ok_current & ~ok_old; + unsigned ok_trigger_release = !ok_current && ok_old; static unsigned navigation_initial = 0; unsigned navigation_current = 0; unsigned navigation_buttons[NAVIGATION_BUTTONS] = @@ -5280,10 +5282,13 @@ unsigned menu_event( RETRO_DEVICE_ID_JOYPAD_DOWN, RETRO_DEVICE_ID_JOYPAD_LEFT, RETRO_DEVICE_ID_JOYPAD_RIGHT, + RETRO_DEVICE_ID_JOYPAD_SELECT, + RETRO_DEVICE_ID_JOYPAD_START, RETRO_DEVICE_ID_JOYPAD_L, RETRO_DEVICE_ID_JOYPAD_R, RETRO_DEVICE_ID_JOYPAD_L2, RETRO_DEVICE_ID_JOYPAD_R2, + RETRO_DEVICE_ID_JOYPAD_X, RETRO_DEVICE_ID_JOYPAD_Y }; @@ -5424,6 +5429,7 @@ unsigned menu_event( float delta_time = (float)(menu_st->current_time_us - last_time_us) / 1000; last_time_us = menu_st->current_time_us; + navigation_reset_delay = true; /* Store first direction in order to block "diagonals" */ if (!navigation_initial) @@ -5459,7 +5465,12 @@ unsigned menu_event( set_scroll = true; hold_reset = true; hold_initial = true; - navigation_initial = 0; + + /* Buffer for keyboard combo jitter */ + if (navigation_reset_delay) + navigation_reset_delay = false; + else + navigation_initial = 0; } if (set_scroll) @@ -5575,6 +5586,7 @@ unsigned menu_event( } else { + static size_t ok_enum_idx = 0; static uint8_t switch_old = 0; static bool keydown[RARCH_FIRST_CUSTOM_BIND] = {false}; unsigned onkeyup = @@ -5585,6 +5597,27 @@ unsigned menu_event( switch_old = switch_current; + /* Always process Select and Start on release */ + onkeyup |= (1 << RETRO_DEVICE_ID_JOYPAD_SELECT) + | (1 << RETRO_DEVICE_ID_JOYPAD_START); + + /* Handle OK on release with specific items */ + if (ok_current || ok_trigger_release) + { + menu_entry_t entry; + MENU_ENTRY_INITIALIZE(entry); + menu_entry_get(&entry, 0, menu_st->selection_ptr, NULL, true); + + /* Due to navigation animations changing current entry between + * keypress, require OK trigger enum match for release action */ + if (ok_trigger) + ok_enum_idx = entry.enum_idx; + + if ( ok_enum_idx == entry.enum_idx + && ok_enum_idx == MENU_ENUM_LABEL_RESUME_CONTENT) + ok_trigger = ok_trigger_release; + } + /* Prevent holding down left/right with boolean settings */ if (switch_current) { @@ -5660,10 +5693,13 @@ unsigned menu_event( MENU_ACTION_RET(RETRO_DEVICE_ID_JOYPAD_L3, MENU_ACTION_SCROLL_HOME); MENU_ACTION_RET(RETRO_DEVICE_ID_JOYPAD_R3, MENU_ACTION_SCROLL_END); - MENU_ACTION_RET(RETRO_DEVICE_ID_JOYPAD_X, MENU_ACTION_SEARCH); - MENU_ACTION_RET(RETRO_DEVICE_ID_JOYPAD_Y, MENU_ACTION_SCAN); - MENU_ACTION_RET(RETRO_DEVICE_ID_JOYPAD_START, MENU_ACTION_START); - MENU_ACTION_RET(RETRO_DEVICE_ID_JOYPAD_SELECT, MENU_ACTION_INFO); + if (ret == MENU_ACTION_NOOP) + { + MENU_ACTION_RET(RETRO_DEVICE_ID_JOYPAD_START, MENU_ACTION_START); + MENU_ACTION_RET(RETRO_DEVICE_ID_JOYPAD_SELECT, MENU_ACTION_INFO); + MENU_ACTION_RET(RETRO_DEVICE_ID_JOYPAD_X, MENU_ACTION_SEARCH); + MENU_ACTION_RET(RETRO_DEVICE_ID_JOYPAD_Y, MENU_ACTION_SCAN); + } if (ok_trigger) ret = MENU_ACTION_OK; @@ -5673,6 +5709,14 @@ unsigned menu_event( if (BIT256_GET_PTR(p_trigger_input, RARCH_MENU_TOGGLE)) ret = MENU_ACTION_TOGGLE; + /* Prevent simultaneous hotkey actions according to hotkey block delay */ + if (input_config_binds[0][RARCH_ENABLE_HOTKEY].joykey != NO_BTN) + { + if ( (input_st->flags & INP_FLAG_BLOCK_LIBRETRO_INPUT) + || !(input_st->flags & INP_FLAG_BLOCK_HOTKEY)) + ret = MENU_ACTION_NOOP; + } + if (ret != MENU_ACTION_NOOP) menu_st->input_last_time_us = menu_st->current_time_us; } @@ -5680,7 +5724,7 @@ unsigned menu_event( /* Menu must be alive, and input must be released after menu toggle. */ if ( !(menu_st->flags & MENU_ST_FLAG_ALIVE) || menu_st->input_driver_flushing_input > 0) - return MENU_ACTION_NOOP; + ret = MENU_ACTION_NOOP; return ret; }