Fix/reimplement input flushing

This fixes:

- menu toggle erratically not working on Android
- stray input going to libretro core when resuming content
- bound keys triggering as soon as they're bound on Android
- menu key repeat also repeating keys which should not be repeated
- issues caused by relying on timeouts for flushing

Architectural changes:

- menu_ctx_driver_t::input_postprocess now takes state and old_state
  (this allows getting rid of menu_handle_t::trigger_state)

Related changes:

- remove some no-op input_postprocess handlers (same effect as NULL)
- menu_iterate now uses the parameters passed to it, instead of
  polling menu_input
- menu_input is now merged into meta_input_keys_pressed
This commit is contained in:
Vladimir Panteleev 2014-09-29 12:55:35 +00:00
parent 91557b34da
commit d0e970f175
13 changed files with 89 additions and 105 deletions

View File

@ -481,10 +481,9 @@ typedef struct driver
#endif
bool stdin_claimed;
bool block_hotkey;
unsigned block_hotkey_until;
bool block_input;
bool block_libretro_input;
unsigned block_libretro_input_until;
bool flushing_input;
bool nonblock_state;
/* Opaque handles to currently running window.

View File

@ -344,13 +344,6 @@ static void glui_free(void *data)
g_extern.core_info = NULL;
}
static int glui_input_postprocess(uint64_t old_state)
{
(void)old_state;
return 0;
}
static GLuint glui_png_texture_load(const char * file_name)
{
struct texture_image ti = {0};
@ -404,7 +397,7 @@ menu_ctx_driver_t menu_ctx_glui = {
NULL,
NULL,
NULL,
glui_input_postprocess,
NULL,
NULL,
NULL,
NULL,

View File

@ -984,10 +984,10 @@ static void lakka_free(void *data)
g_extern.core_info = NULL;
}
static int lakka_input_postprocess(uint64_t old_state)
static int lakka_input_postprocess(retro_input_t state, retro_input_t old_state)
{
if ((driver.menu && driver.menu->trigger_state
& (1ULL << RARCH_MENU_TOGGLE)) &&
retro_input_t trigger_state = state & ~old_state;
if ((driver.menu && check_enter_menu_func(trigger_state)) &&
g_extern.main_is_init &&
!g_extern.libretro_dummy)
global_alpha = 0;

View File

@ -18,7 +18,7 @@ typedef struct menu_ctx_driver
void (*populate_entries)(void*, const char *, const char *,
unsigned);
void (*iterate)(void*, unsigned);
int (*input_postprocess)(uint64_t);
int (*input_postprocess)(uint64_t, uint64_t);
void (*navigation_clear)(void *);
void (*navigation_decrement)(void *);
void (*navigation_increment)(void *);

View File

@ -447,11 +447,6 @@ static void rgui_free(void *data)
free((uint8_t*)menu->font);
}
static int rgui_input_postprocess(uint64_t old_state)
{
return 0;
}
void rgui_set_texture(void *data)
{
menu_handle_t *menu = (menu_handle_t*)data;
@ -473,7 +468,7 @@ menu_ctx_driver_t menu_ctx_rgui = {
NULL,
NULL,
NULL,
rgui_input_postprocess,
NULL,
NULL,
NULL,
NULL,

View File

@ -103,8 +103,6 @@ bool load_menu_content(void)
/* redraw menu frame */
if (driver.menu)
{
driver.menu->old_input_state = driver.menu->trigger_state = 0;
driver.menu->do_held = false;
driver.menu->msg_force = true;
}
@ -172,9 +170,6 @@ void *menu_init(const void *data)
menu_entries_push_list(menu, menu->selection_buf,
"", "mainmenu", 0);
menu->trigger_state = 0;
menu->old_input_state = 0;
menu->do_held = false;
menu->current_pad = 0;
update_libretro_info(&g_extern.menu.info);
@ -302,8 +297,14 @@ bool menu_iterate(retro_input_t input,
unsigned action = MENU_ACTION_NOOP;
static bool initial_held = true;
static bool first_held = false;
uint64_t input_state = 0;
int32_t ret = 0;
static const retro_input_t input_repeat =
(1ULL << RETRO_DEVICE_ID_JOYPAD_UP)
| (1ULL << RETRO_DEVICE_ID_JOYPAD_DOWN)
| (1ULL << RETRO_DEVICE_ID_JOYPAD_LEFT)
| (1ULL << RETRO_DEVICE_ID_JOYPAD_RIGHT)
| (1ULL << RETRO_DEVICE_ID_JOYPAD_L)
| (1ULL << RETRO_DEVICE_ID_JOYPAD_R);
if (!driver.menu)
return false;
@ -316,9 +317,7 @@ bool menu_iterate(retro_input_t input,
driver.retro_ctx.poll_cb();
input_state = menu_input();
if (driver.menu->do_held)
if (input & input_repeat)
{
if (!first_held)
{
@ -330,7 +329,7 @@ bool menu_iterate(retro_input_t input,
if (driver.menu->delay_count >= driver.menu->delay_timer)
{
first_held = false;
driver.menu->trigger_state = input_state;
trigger_input |= input & input_repeat;
driver.menu->scroll_accel = min(driver.menu->scroll_accel + 1, 64);
}
@ -344,15 +343,14 @@ bool menu_iterate(retro_input_t input,
}
driver.menu->delay_count++;
driver.menu->old_input_state = input_state;
if (driver.block_input)
driver.menu->trigger_state = 0;
trigger_input = 0;
/* don't run anything first frame, only capture held inputs
* for old_input_state.
*/
action = input_frame(driver.menu->trigger_state);
action = input_frame(trigger_input);
if (driver.menu_ctx && driver.menu_ctx->backend
&& driver.menu_ctx->backend->iterate)
@ -362,7 +360,7 @@ bool menu_iterate(retro_input_t input,
draw_frame(false);
if (driver.menu_ctx && driver.menu_ctx->input_postprocess)
driver.menu_ctx->input_postprocess(driver.menu->old_input_state);
driver.menu_ctx->input_postprocess(input, old_input);
#if 0
/* Go back to Main Menu when exiting */

View File

@ -67,18 +67,6 @@ struct menu_bind_state
typedef struct
{
/* Keys down last frame. Used for trigger_state */
uint64_t old_input_state;
/* New keys pressed down this frame */
uint64_t trigger_state;
/* Whether any repeating keys are being held down, */
/* and key repeat should be handled */
/* TODO: should be a mask of keys we are repeating, */
/* instead of repeating all keys being held down. */
bool do_held;
/* Used for key repeat */
unsigned delay_timer;
unsigned delay_count;

View File

@ -56,7 +56,7 @@ static void menu_key_end_line(void *data)
menu->keyboard.label_setting = NULL;
/* Avoid triggering states on pressing return. */
menu->old_input_state = -1ULL;
driver.flushing_input = true;
}
static void menu_search_callback(void *userdata, const char *str)
@ -298,43 +298,6 @@ bool menu_custom_bind_keyboard_cb(void *data, unsigned code)
return menu->binds.begin <= menu->binds.last;
}
uint64_t menu_input(void)
{
unsigned i;
retro_input_t input_state = 0;
static const struct retro_keybind *binds[] = { g_settings.input.binds[0] };
if (!driver.menu)
return 0;
input_push_analog_dpad((struct retro_keybind*)binds[0],
(g_settings.input.analog_dpad_mode[0] == ANALOG_DPAD_NONE) ?
ANALOG_DPAD_LSTICK : g_settings.input.analog_dpad_mode[0]);
for (i = 0; i < MAX_PLAYERS; i++)
input_push_analog_dpad(g_settings.input.autoconf_binds[i],
g_settings.input.analog_dpad_mode[i]);
input_state = input_keys_pressed(0, RARCH_FIRST_CUSTOM_BIND, binds);
input_pop_analog_dpad((struct retro_keybind*)binds[0]);
for (i = 0; i < MAX_PLAYERS; i++)
input_pop_analog_dpad(g_settings.input.autoconf_binds[i]);
driver.menu->trigger_state = input_state & ~driver.menu->old_input_state;
driver.menu->do_held = (input_state & (
(1ULL << RETRO_DEVICE_ID_JOYPAD_UP)
| (1ULL << RETRO_DEVICE_ID_JOYPAD_DOWN)
| (1ULL << RETRO_DEVICE_ID_JOYPAD_LEFT)
| (1ULL << RETRO_DEVICE_ID_JOYPAD_RIGHT)
| (1ULL << RETRO_DEVICE_ID_JOYPAD_L)
| (1ULL << RETRO_DEVICE_ID_JOYPAD_R)
));
return input_state;
}
int menu_input_bind_iterate(void *data)
{
char msg[PATH_MAX];
@ -358,22 +321,22 @@ int menu_input_bind_iterate(void *data)
&& driver.menu_ctx->render_messagebox)
driver.menu_ctx->render_messagebox(msg);
driver.block_input = true;
menu_poll_bind_state(&binds);
driver.block_hotkey_until = g_extern.frame_count + (15);
if ((binds.skip && !menu->binds.skip) ||
menu_poll_find_trigger(&menu->binds, &binds))
{
driver.block_input = false;
/* Avoid new binds triggering things right away. */
driver.flushing_input = true;
binds.begin++;
if (binds.begin <= binds.last)
binds.target++;
else
return 1;
/* Avoid new binds triggering things right away. */
menu->trigger_state = 0;
menu->old_input_state = -1ULL;
}
menu->binds = binds;
@ -416,14 +379,11 @@ int menu_input_bind_iterate_keyboard(void *data)
timed_out = true;
}
driver.block_hotkey_until = g_extern.frame_count + (15);
/* binds.begin is updated in keyboard_press callback. */
if (menu->binds.begin > menu->binds.last)
{
/* Avoid new binds triggering things right away. */
menu->trigger_state = 0;
menu->old_input_state = -1ULL;
driver.flushing_input = true;
/* We won't be getting any key events, so just cancel early. */
if (timed_out)

View File

@ -42,6 +42,4 @@ int menu_input_bind_iterate(void *data);
int menu_input_bind_iterate_keyboard(void *data);
uint64_t menu_input(void);
#endif

View File

@ -503,9 +503,7 @@ static int android_input_get_id(android_input_t *android, AInputEvent *event)
static bool pause_key_pressed(void)
{
retro_input_t old_input = 0;
retro_input_t input = meta_input_keys_pressed(RARCH_PAUSE_TOGGLE, RARCH_PAUSE_TOGGLE+1, &old_input);
return BIND_PRESSED(input, RARCH_PAUSE_TOGGLE);
return driver.input->key_pressed(driver.input_data, RARCH_PAUSE_TOGGLE);
}
/* Handle all events. If our activity is in pause state,

View File

@ -1625,6 +1625,10 @@ retro_input_t input_keys_pressed(unsigned key,
* from the specified key up until the last queryable key
* (key_end).
*
* Because this function keeps a copy of the old input state,
* it should only be called once per frame (currently in
* rarch_main_iterate);
*
* TODO: In case RARCH_BIND_LIST_END starts exceeding 64,
* and you need a bitmask of more than 64 entries, don't
* use this function.
@ -1634,20 +1638,29 @@ retro_input_t meta_input_keys_pressed(unsigned key,
unsigned key_end, retro_input_t *old_state)
{
static retro_input_t old_ret = 0;
static const struct retro_keybind *binds[] = { g_settings.input.binds[0] };
retro_input_t ret = 0;
*old_state = old_ret;
int i;
#ifdef RARCH_INTERNAL
rarch_check_block_hotkey(driver.input->key_pressed(driver.input_data,
RARCH_ENABLE_HOTKEY));
#endif
input_push_analog_dpad((struct retro_keybind*)binds[0],
(g_settings.input.analog_dpad_mode[0] == ANALOG_DPAD_NONE) ?
ANALOG_DPAD_LSTICK : g_settings.input.analog_dpad_mode[0]);
for (i = 0; i < MAX_PLAYERS; i++)
input_push_analog_dpad(g_settings.input.autoconf_binds[i],
g_settings.input.analog_dpad_mode[i]);
for (; key < key_end; key++)
{
bool state = false;
if (!driver.block_hotkey
&& g_extern.frame_count > driver.block_hotkey_until)
if (!driver.block_hotkey)
state = driver.input->key_pressed(driver.input_data, key);
#ifdef HAVE_OVERLAY
@ -1663,6 +1676,10 @@ retro_input_t meta_input_keys_pressed(unsigned key,
ret |= (1ULL << key);
}
input_pop_analog_dpad((struct retro_keybind*)binds[0]);
for (i = 0; i < MAX_PLAYERS; i++)
input_pop_analog_dpad(g_settings.input.autoconf_binds[i]);
old_ret = ret;
return ret;

View File

@ -183,8 +183,7 @@ static int16_t input_state(unsigned port, unsigned device,
if (!driver.block_libretro_input &&
((id < RARCH_FIRST_META_KEY ||
device == RETRO_DEVICE_KEYBOARD) &&
(g_extern.frame_count > driver.block_libretro_input_until))
device == RETRO_DEVICE_KEYBOARD))
)
res = driver.input->input_state(driver.input_data, binds, port,
device, index, id);
@ -203,6 +202,35 @@ static int16_t input_state(unsigned port, unsigned device,
}
#endif
{
/* Last frame input_state was called. */
static unsigned flush_frame = 0;
/* Last frame which had input. */
static unsigned flush_frame_input = 0;
if (driver.flushing_input)
{
if (flush_frame != g_extern.frame_count &&
flush_frame != flush_frame_input)
{
/* At least one entire frame has passed with no input. */
driver.flushing_input = false;
flush_frame = flush_frame_input = 0;
}
else
{
flush_frame = g_extern.frame_count;
if (res)
{
flush_frame_input = g_extern.frame_count;
res = 0;
}
}
}
else
flush_frame = flush_frame_input = 0;
}
/* Don't allow turbo for D-pad. */
if (device == RETRO_DEVICE_JOYPAD && (id < RETRO_DEVICE_ID_JOYPAD_UP ||
id > RETRO_DEVICE_ID_JOYPAD_RIGHT))

View File

@ -2838,7 +2838,9 @@ void rarch_main_set_state(unsigned cmd)
rarch_main_command(RARCH_CMD_AUDIO_START);
driver.block_libretro_input_until = g_extern.frame_count + (5);
/* Prevent stray input from going to libretro core */
driver.flushing_input = true;
/* Restore libretro keyboard callback. */
g_extern.system.key_event = g_extern.frontend_key_event;
break;
@ -3226,9 +3228,17 @@ bool rarch_main_iterate(void)
{
unsigned i;
retro_input_t old_input, trigger_input;
retro_input_t input = meta_input_keys_pressed(RARCH_FIRST_META_KEY,
retro_input_t input = meta_input_keys_pressed(0,
RARCH_BIND_LIST_END, &old_input);
if (driver.flushing_input)
{
if (input)
input = 0;
else
driver.flushing_input = false;
}
trigger_input = input & ~old_input;
/* Time to drop? */