diff --git a/input/input_driver.c b/input/input_driver.c index 757f3f7fd7..cc23402d66 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -3049,9 +3049,25 @@ static int16_t input_state_device( } -/* Set while the poll reads what the core's ports have down for the core - * hold: that read is not to be held back itself. */ -static bool input_core_hold_reading; +/* What the core hold holds back of a mouse and a lightgun: their + * buttons, not their positions and not a wheel's notches, which are + * over as they come. */ +#define CORE_HOLD_MOUSE_IDS ( (1u << RETRO_DEVICE_ID_MOUSE_LEFT) \ + | (1u << RETRO_DEVICE_ID_MOUSE_RIGHT) \ + | (1u << RETRO_DEVICE_ID_MOUSE_MIDDLE) \ + | (1u << RETRO_DEVICE_ID_MOUSE_BUTTON_4) \ + | (1u << RETRO_DEVICE_ID_MOUSE_BUTTON_5)) +#define CORE_HOLD_GUN_IDS ( (1u << RETRO_DEVICE_ID_LIGHTGUN_TRIGGER) \ + | (1u << RETRO_DEVICE_ID_LIGHTGUN_AUX_A) \ + | (1u << RETRO_DEVICE_ID_LIGHTGUN_AUX_B) \ + | (1u << RETRO_DEVICE_ID_LIGHTGUN_START) \ + | (1u << RETRO_DEVICE_ID_LIGHTGUN_SELECT) \ + | (1u << RETRO_DEVICE_ID_LIGHTGUN_AUX_C) \ + | (1u << RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP) \ + | (1u << RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN) \ + | (1u << RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT) \ + | (1u << RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT) \ + | (1u << RETRO_DEVICE_ID_LIGHTGUN_RELOAD)) static int16_t input_state_internal( input_driver_state_t *input_st, @@ -3078,14 +3094,9 @@ static int16_t input_state_internal( #endif uint8_t mapped_port = 0; int16_t result = 0; -#ifdef HAVE_MENU - struct menu_state *menu_st = menu_state_get_ptr(); - bool input_blocked = ( (menu_st->input_driver_flushing_input > 0) - && !input_core_hold_reading) - || (input_st->flags & INP_FLAG_BLOCK_LIBRETRO_INPUT); -#else + /* What the menu leaves held as it closes is the core hold's to keep + * back, control by control; the core's input is not blocked for it. */ bool input_blocked = (input_st->flags & INP_FLAG_BLOCK_LIBRETRO_INPUT) ? true : false; -#endif bool input_driver_analog_requested = input_st->analog_requested[port]; bool bitmask_enabled = false; @@ -9167,7 +9178,7 @@ static void input_keys_pressed( #ifdef HAVE_MENU /* 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->held_bits_phase) input_st->flags |= INP_FLAG_WAIT_INPUT_RELEASE; #endif @@ -10035,15 +10046,75 @@ bool input_driver_game_focus_core_requested(void) * until each is let go. They are found at the next poll: until then * every button is held back, which is at most the frame the menu * closed in. */ -void input_driver_hold_core_input(void) +static void input_driver_hold_core_input(void) { memset(input_driver_st.core_hold_mask, 0xff, sizeof(input_driver_st.core_hold_mask)); memset(input_driver_st.core_hold_keys, 0xff, sizeof(input_driver_st.core_hold_keys)); + { + unsigned port; + for (port = 0; port < MAX_USERS; port++) + { + input_driver_st.core_hold_mouse[port] = CORE_HOLD_MOUSE_IDS; + input_driver_st.core_hold_gun[port] = CORE_HOLD_GUN_IDS; + input_driver_st.core_hold_pointer[port] = 1; + } + } input_driver_st.core_hold_armed = true; } +void input_driver_hold_held_input(void) +{ + input_driver_hold_core_input(); + input_driver_st.held_bits_phase = 1; +} + +void input_driver_hold_clear(void) +{ + memset(input_driver_st.core_hold_mask, 0, + sizeof(input_driver_st.core_hold_mask)); + memset(input_driver_st.core_hold_keys, 0, + sizeof(input_driver_st.core_hold_keys)); + memset(input_driver_st.core_hold_mouse, 0, + sizeof(input_driver_st.core_hold_mouse)); + memset(input_driver_st.core_hold_gun, 0, + sizeof(input_driver_st.core_hold_gun)); + memset(input_driver_st.core_hold_pointer, 0, + sizeof(input_driver_st.core_hold_pointer)); + input_driver_st.core_hold_armed = false; + input_driver_st.held_bits_phase = 0; +} + +/* The first frame takes what is down; after that each bit drops out of + * the hold as it is let go. The hotkey-enable button is never held + * back: holding it is how other hotkeys are reached. */ +bool input_driver_hold_bits(input_bits_t *bits) +{ + input_driver_state_t *input_st = &input_driver_st; + bool held = false; + unsigned w; + + if (!input_st->held_bits_phase) + return false; + if (input_st->held_bits_phase == 1) + { + input_st->held_bits = *bits; + BIT256_CLEAR(input_st->held_bits, RARCH_ENABLE_HOTKEY); + input_st->held_bits_phase = 2; + } + for (w = 0; w < ARRAY_SIZE(bits->data); w++) + { + input_st->held_bits.data[w] &= bits->data[w]; + bits->data[w] &= ~input_st->held_bits.data[w]; + if (input_st->held_bits.data[w]) + held = true; + } + if (!held) + input_st->held_bits_phase = 0; + return held; +} + /* Input polled without blocking, for netplay catching up. */ void input_driver_set_nonblocking(bool on) { @@ -11239,9 +11310,29 @@ void input_driver_poll(void) { unsigned port; uint16_t any = 0; - input_core_hold_reading = true; for (port = 0; port < max_users; port++) { + unsigned id; + /* the mouse's buttons, the lightgun's, the pointer pressed */ + for (id = 0; id < 32 && input_st->core_hold_mouse[port]; id++) + if ( (input_st->core_hold_mouse[port] & (1u << id)) + && !input_state_internal(input_st, settings, port, + RETRO_DEVICE_MOUSE, 0, id)) + input_st->core_hold_mouse[port] &= (uint16_t)~(1u << id); + for (id = 0; id < 32 && input_st->core_hold_gun[port]; id++) + if ( (input_st->core_hold_gun[port] & (1u << id)) + && !input_state_internal(input_st, settings, port, + RETRO_DEVICE_LIGHTGUN, 0, id)) + input_st->core_hold_gun[port] &= ~(1u << id); + if ( input_st->core_hold_pointer[port] + && !input_state_internal(input_st, settings, port, + RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED)) + input_st->core_hold_pointer[port] = 0; + if ( input_st->core_hold_mouse[port] + || input_st->core_hold_gun[port] + || input_st->core_hold_pointer[port]) + any |= 1; + if (!input_st->core_hold_mask[port]) continue; input_st->core_hold_mask[port] &= (uint16_t)input_state_internal( @@ -11250,7 +11341,12 @@ void input_driver_poll(void) any |= input_st->core_hold_mask[port]; } for (; port < MAX_USERS; port++) - input_st->core_hold_mask[port] = 0; + { + input_st->core_hold_mask[port] = 0; + input_st->core_hold_mouse[port] = 0; + input_st->core_hold_gun[port] = 0; + input_st->core_hold_pointer[port] = 0; + } /* the keys: each one still held back is read, a word of them * skipped at a time once it is clear */ { @@ -11275,7 +11371,6 @@ void input_driver_poll(void) any |= (word != 0); } } - input_core_hold_reading = false; if (!any) input_st->core_hold_armed = false; } @@ -11509,6 +11604,27 @@ int16_t input_driver_state_wrapper(unsigned port, unsigned device, && id < RETROK_LAST && (input_st->core_hold_keys[id >> 5] & (1u << (id & 31)))) result = 0; + else if (port < MAX_USERS) + { + switch (device & RETRO_DEVICE_MASK) + { + case RETRO_DEVICE_MOUSE: + if (id < 16 && (input_st->core_hold_mouse[port] & (1u << id))) + result = 0; + break; + case RETRO_DEVICE_LIGHTGUN: + if (id < 32 && (input_st->core_hold_gun[port] & (1u << id))) + result = 0; + break; + case RETRO_DEVICE_POINTER: + if ( id == RETRO_DEVICE_ID_POINTER_PRESSED + && input_st->core_hold_pointer[port]) + result = 0; + break; + default: + break; + } + } } /* Register any analog stick input requests for diff --git a/input/input_driver.h b/input/input_driver.h index ee78b755cb..cc73952745 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -764,13 +764,23 @@ typedef struct * frame's held set. */ uint16_t wait_release_mask[MAX_USERS]; /* The core's buttons held back after the menu closes: those that - * were down, each until it is let go (input_driver_hold_core_input()). + * were down, each until it is let go (input_driver_hold_held_input()). * One bit per RetroPad button, per port; core_hold_armed while any * is set. */ uint16_t core_hold_mask[MAX_USERS]; /* and the keyboard's keys, one bit per RETROK_ code */ uint32_t core_hold_keys[(RETROK_LAST + 31) / 32]; + /* and per port, the mouse's buttons and the lightgun's, one bit per + * RETRO_DEVICE_ID_, and the pointer being pressed (bit 0) */ + uint16_t core_hold_mouse[MAX_USERS]; + uint32_t core_hold_gun[MAX_USERS]; + uint8_t core_hold_pointer[MAX_USERS]; bool core_hold_armed; + /* The same for what the run loop and the menu see - the RetroPad + * bits and the hotkeys: 1 takes what is down at the next frame, 2 + * holds it back, 0 is no hold. */ + input_bits_t held_bits; + uint8_t held_bits_phase; /* The frame's view of each port's RetroPad buttons, as a core is * given them: after port mapping, remaps, turbo, hold, overlays and @@ -1110,10 +1120,18 @@ bool input_driver_game_focus_core_requested(void); bool input_driver_overlay_alive(void); bool input_driver_overlay_takes_input(void); bool input_driver_overlay_active_page(void); -/* The buttons the core's ports have down now are kept from the core - * until each is let go: for the menu as it closes, so that the press - * that closed it does nothing in the content. */ -void input_driver_hold_core_input(void); +/* Whatever is down now is held back, control by control, until each is + * let go: from the core (its buttons, keys, mouse and lightgun buttons + * and the pointer press) and from what the run loop and the menu see + * (the RetroPad bits and the hotkeys). Everything else gets through at + * once. For the menu as it opens and closes, a bind or a text entry + * ends, or the mouse clicks: the press that did it does nothing more. */ +void input_driver_hold_held_input(void); +/* Nothing is held back any more: for the menu going away. */ +void input_driver_hold_clear(void); +/* The run loop's bits for the frame: what the hold holds back is taken + * out. True while it is holding something back. */ +bool input_driver_hold_bits(input_bits_t *bits); void input_driver_set_nonblocking(bool on); void input_driver_set_remapping_cache_active(void); void input_driver_device_info_save(input_device_info_t *dst); diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 96683087dd..6b292a3d78 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -4698,7 +4698,7 @@ void menu_driver_destroy( | MENU_ST_FLAG_ALIVE); menu_st->driver_ctx = NULL; menu_st->userdata = NULL; - menu_st->input_driver_flushing_input = 0; + input_driver_hold_clear(); } void menu_input_get_pointer_state(menu_input_pointer_t *copy_target) @@ -4769,7 +4769,7 @@ void menu_input_dialog_end(void) /* Avoid triggering states on pressing return. */ /* Held back until everything held now is let go. */ - menu_st->input_driver_flushing_input = 1; + input_driver_hold_held_input(); #ifdef HAVE_COCOATOUCH /* Dismiss iOS/tvOS native keyboard if it's currently open */ @@ -5151,7 +5151,7 @@ MENU_NOINLINE static bool menu_input_key_bind_iterate( { /* Avoid new binds triggering things right away. */ /* Held back until everything held now is let go. */ - menu_st->input_driver_flushing_input = 1; + input_driver_hold_held_input(); /* We won't be getting any key events, so just cancel early. */ if (timed_out) @@ -5283,7 +5283,7 @@ MENU_NOINLINE static bool menu_input_key_bind_iterate( /* Avoid new binds triggering things right away. */ /* Held back until everything held now is let go. */ - menu_st->input_driver_flushing_input = 1; + input_driver_hold_held_input(); /* Use human readable order instead */ new_binds.order++; @@ -5644,7 +5644,7 @@ unsigned menu_event( { menu_st->input_last_time_us = menu_st->current_time_us; /* Prevent double trigger when OK/Cancel has mouse binds */ - menu_st->input_driver_flushing_input = 1; + input_driver_hold_held_input(); } } @@ -7040,7 +7040,7 @@ void retroarch_menu_running(void) } /* Prevent stray input */ - menu_st->input_driver_flushing_input = 1; + input_driver_hold_held_input(); #ifdef HAVE_AUDIOMIXER if (audio_enable_menu && audio_enable_menu_bgm) @@ -7117,11 +7117,9 @@ void retroarch_menu_running_finished(bool quit) false); } - /* Prevent stray input */ - menu_st->input_driver_flushing_input = 1; - /* and the core's buttons held as the menu closes stay out of the - * content until each is let go */ - input_driver_hold_core_input(); + /* What is held as the menu closes does nothing more, in the menu or + * in the content, until it is let go */ + input_driver_hold_held_input(); if (!quit) { diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 905d6704bf..6d969b03de 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -543,14 +543,6 @@ struct menu_state /* unsigned alignment */ unsigned input_dialog_kb_idx; - /* The menu's hold. Set to 1 when the menu opens or closes, a bind or - * a text entry ends, or the mouse clicks: the run loop then takes - * what is down and holds it back from the menu and the hotkeys, each - * button until it is let go (2), and clears this when nothing of it - * is down. The core's buttons and keys have a hold of their own - * (input_driver_hold_core_input()); while this is set the core's - * other input - pointer, mouse, sticks - is kept from it as well. */ - unsigned input_driver_flushing_input; menu_dialog_t dialog_st; enum menu_action prev_action; enum menu_input_dialog_kb_text_type input_dialog_kb_text_type; diff --git a/runloop.c b/runloop.c index 9b3791cf07..b00a685685 100644 --- a/runloop.c +++ b/runloop.c @@ -7350,51 +7350,25 @@ static enum runloop_state_enum runloop_check_state( &last_input)) BIT256_SET(current_bits, RARCH_MENU_TOGGLE); - /* The menu's hold: what was down when the menu opened or closed, a - * bind or a text entry ended or the mouse clicked is held back, each - * button and hotkey until it is let go; everything else gets through - * at once. 1 asks for what is down to be taken this frame; 2 is the - * hold, which ends when nothing of it is down. The hotkey-enable - * button is never held back: holding it is how other hotkeys are - * reached. */ - if (menu_st->input_driver_flushing_input > 0) + /* What was down when the menu opened or closed, a bind or a text + * entry ended or the mouse clicked is held back, each control until + * it is let go (input_driver_hold_held_input()). While it is, the + * pause is kept as it was. */ + if (input_driver_hold_bits(¤t_bits)) { - static input_bits_t held_back; - unsigned w; - bool input_active = false; - - if (menu_st->input_driver_flushing_input == 1) + if ( runloop_paused + && !runloop_st->paused_hotkey + && menu_pause_libretro) + BIT256_SET(current_bits, RARCH_PAUSE_TOGGLE); + else if (runloop_st->paused_hotkey) { - held_back = current_bits; - BIT256_CLEAR(held_back, RARCH_ENABLE_HOTKEY); - menu_st->input_driver_flushing_input = 2; - } - for (w = 0; w < ARRAY_SIZE(current_bits.data); w++) - { - held_back.data[w] &= current_bits.data[w]; - current_bits.data[w] &= ~held_back.data[w]; - if (held_back.data[w]) - input_active = true; - } - - if (!input_active) - menu_st->input_driver_flushing_input = 0; - else - { - if ( runloop_paused - && !runloop_st->paused_hotkey - && menu_pause_libretro) - BIT256_SET(current_bits, RARCH_PAUSE_TOGGLE); - else if (runloop_st->paused_hotkey) - { - /* Restore pause if pause is triggered with both hotkey and menu, - * and restore cached video frame to continue properly to - * paused state from non-paused menu */ - if (menu_pause_libretro) - command_event(CMD_EVENT_PAUSE, NULL); - else - video_driver_cached_frame(); - } + /* Restore pause if pause is triggered with both hotkey and menu, + * and restore cached video frame to continue properly to + * paused state from non-paused menu */ + if (menu_pause_libretro) + command_event(CMD_EVENT_PAUSE, NULL); + else + video_driver_cached_frame(); } } #endif diff --git a/samples/runloop/frontend_overhead/frontend_overhead_test.c b/samples/runloop/frontend_overhead/frontend_overhead_test.c index 5a4ba9dfda..39ee7c1d85 100644 --- a/samples/runloop/frontend_overhead/frontend_overhead_test.c +++ b/samples/runloop/frontend_overhead/frontend_overhead_test.c @@ -2746,9 +2746,12 @@ static bool pm_menu_settled(void) return false; } -/* an input driver that has one key down, RETROK_RETURN, when told to */ +/* an input driver that has one key down, RETROK_RETURN, the mouse's + * left button down, or the pointer pressed, when told to */ static input_driver_t pm_input; static bool pm_return_down; +static bool pm_click_down; +static bool pm_touch_down; static int16_t pm_input_state(void *data, const input_device_driver_t *joypad, const input_device_driver_t *sec_joypad, @@ -2758,9 +2761,37 @@ static int16_t pm_input_state(void *data, unsigned port, unsigned device, unsigned idx, unsigned id) { (void)data; (void)joypad; (void)sec_joypad; (void)joypad_info; - (void)binds; (void)keyboard_mapping_blocked; (void)idx; - return ( pm_return_down && port == 0 - && device == RETRO_DEVICE_KEYBOARD && id == RETROK_RETURN) ? 1 : 0; + (void)binds; (void)keyboard_mapping_blocked; + if (port != 0) + return 0; + switch (device & RETRO_DEVICE_MASK) + { + case RETRO_DEVICE_KEYBOARD: + return (pm_return_down && id == RETROK_RETURN) ? 1 : 0; + case RETRO_DEVICE_MOUSE: + return (pm_click_down && id == RETRO_DEVICE_ID_MOUSE_LEFT) ? 1 : 0; + case RETRO_DEVICE_POINTER: + return (pm_touch_down && idx == 0 + && id == RETRO_DEVICE_ID_POINTER_PRESSED) ? 1 : 0; + default: + break; + } + return 0; +} + +/* frames run one at a time; whether a control reached the core's view + * in any of them, read as the core reads it */ +static bool pm_frames_saw(unsigned n, unsigned device, unsigned id) +{ + bool saw = false; + unsigned i; + for (i = 0; i < n; i++) + { + run_loop_frames(1); + if (input_driver_state_wrapper(0, device, 0, id)) + saw = true; + } + return saw; } /* frames run one at a time; whether Return reached the core's view in @@ -2867,6 +2898,34 @@ static void lane_menu_pause(void) pm_return_down = false; run_loop_frames(3); + /* closed with the mouse's left button held, and with the pointer + * pressed: the same, for a click and a touch */ + { + static const struct { const char *what; unsigned device, id; bool *flag; } c[2] = { + { "the left click", RETRO_DEVICE_MOUSE, RETRO_DEVICE_ID_MOUSE_LEFT, &pm_click_down }, + { "the touch", RETRO_DEVICE_POINTER, RETRO_DEVICE_ID_POINTER_PRESSED, &pm_touch_down } + }; + unsigned k; + for (k = 0; k < 2; k++) + { + snprintf(msg, sizeof(msg), "menu pause, %s: the menu would not stay open", with); + CHECK(pm_menu_settled(), msg); + *c[k].flag = true; + run_loop_frames(2); + pm_set_menu(false); + snprintf(msg, sizeof(msg), "menu pause, %s: the core saw %s that closed the menu, while it was held", with, c[k].what); + CHECK(!pm_frames_saw(10, c[k].device, c[k].id), msg); + *c[k].flag = false; + snprintf(msg, sizeof(msg), "menu pause, %s: the core saw %s after it was let go", with, c[k].what); + CHECK(!pm_frames_saw(4, c[k].device, c[k].id), msg); + *c[k].flag = true; + snprintf(msg, sizeof(msg), "menu pause, %s: the core did not see %s made again after the menu", with, c[k].what); + CHECK(pm_frames_saw(4, c[k].device, c[k].id), msg); + *c[k].flag = false; + run_loop_frames(3); + } + } + snprintf(msg, sizeof(msg), "menu pause, %s: the menu would not stay open", with); CHECK(pm_menu_settled(), msg); /* closed with B held: the core runs, and does not see that B */ @@ -2989,6 +3048,8 @@ static void lane_menu_pause(void) input_config_binds[0][RARCH_MENU_TOGGLE] = saved_toggle; input_st->current_driver = saved_input; pm_return_down = false; + pm_click_down = false; + pm_touch_down = false; input_st->primary_joypad = joypad_real; run_loop_frames(3); if (failures == had) diff --git a/ui/drivers/ui_cocoa.m b/ui/drivers/ui_cocoa.m index b856642a86..708ace402f 100644 --- a/ui/drivers/ui_cocoa.m +++ b/ui/drivers/ui_cocoa.m @@ -625,7 +625,7 @@ static ui_application_t ui_application_cocoa = { * a panel. Every key that is down right now will be released into that * window, so this window never sees the key-up: the key stays "held" in * apple_key_state, and a held key is exactly what the menu's flush-and- - * wait-for-release (menu_st->input_driver_flushing_input) waits on - + * wait-for-release (input_driver_hold_held_input()) waits on - * for ever, since the release never arrives. That was the desktop * companion's "keyboard does not come back after closing it": the hotkey * that opened it was still down when the companion took the keyboard.