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 #endif
bool stdin_claimed; bool stdin_claimed;
bool block_hotkey; bool block_hotkey;
unsigned block_hotkey_until;
bool block_input; bool block_input;
bool block_libretro_input; bool block_libretro_input;
unsigned block_libretro_input_until; bool flushing_input;
bool nonblock_state; bool nonblock_state;
/* Opaque handles to currently running window. /* Opaque handles to currently running window.

View File

@ -344,13 +344,6 @@ static void glui_free(void *data)
g_extern.core_info = NULL; 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) static GLuint glui_png_texture_load(const char * file_name)
{ {
struct texture_image ti = {0}; struct texture_image ti = {0};
@ -404,7 +397,7 @@ menu_ctx_driver_t menu_ctx_glui = {
NULL, NULL,
NULL, NULL,
NULL, NULL,
glui_input_postprocess, NULL,
NULL, NULL,
NULL, NULL,
NULL, NULL,

View File

@ -984,10 +984,10 @@ static void lakka_free(void *data)
g_extern.core_info = NULL; 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 retro_input_t trigger_state = state & ~old_state;
& (1ULL << RARCH_MENU_TOGGLE)) && if ((driver.menu && check_enter_menu_func(trigger_state)) &&
g_extern.main_is_init && g_extern.main_is_init &&
!g_extern.libretro_dummy) !g_extern.libretro_dummy)
global_alpha = 0; global_alpha = 0;

View File

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

View File

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

View File

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

View File

@ -67,18 +67,6 @@ struct menu_bind_state
typedef struct 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 */ /* Used for key repeat */
unsigned delay_timer; unsigned delay_timer;
unsigned delay_count; unsigned delay_count;

View File

@ -56,7 +56,7 @@ static void menu_key_end_line(void *data)
menu->keyboard.label_setting = NULL; menu->keyboard.label_setting = NULL;
/* Avoid triggering states on pressing return. */ /* 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) 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; 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) int menu_input_bind_iterate(void *data)
{ {
char msg[PATH_MAX]; 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)
driver.menu_ctx->render_messagebox(msg); driver.menu_ctx->render_messagebox(msg);
driver.block_input = true;
menu_poll_bind_state(&binds); menu_poll_bind_state(&binds);
driver.block_hotkey_until = g_extern.frame_count + (15);
if ((binds.skip && !menu->binds.skip) || if ((binds.skip && !menu->binds.skip) ||
menu_poll_find_trigger(&menu->binds, &binds)) menu_poll_find_trigger(&menu->binds, &binds))
{ {
driver.block_input = false;
/* Avoid new binds triggering things right away. */
driver.flushing_input = true;
binds.begin++; binds.begin++;
if (binds.begin <= binds.last) if (binds.begin <= binds.last)
binds.target++; binds.target++;
else else
return 1; return 1;
/* Avoid new binds triggering things right away. */
menu->trigger_state = 0;
menu->old_input_state = -1ULL;
} }
menu->binds = binds; menu->binds = binds;
@ -416,14 +379,11 @@ int menu_input_bind_iterate_keyboard(void *data)
timed_out = true; timed_out = true;
} }
driver.block_hotkey_until = g_extern.frame_count + (15);
/* binds.begin is updated in keyboard_press callback. */ /* binds.begin is updated in keyboard_press callback. */
if (menu->binds.begin > menu->binds.last) if (menu->binds.begin > menu->binds.last)
{ {
/* Avoid new binds triggering things right away. */ /* Avoid new binds triggering things right away. */
menu->trigger_state = 0; driver.flushing_input = true;
menu->old_input_state = -1ULL;
/* We won't be getting any key events, so just cancel early. */ /* We won't be getting any key events, so just cancel early. */
if (timed_out) if (timed_out)

View File

@ -42,6 +42,4 @@ int menu_input_bind_iterate(void *data);
int menu_input_bind_iterate_keyboard(void *data); int menu_input_bind_iterate_keyboard(void *data);
uint64_t menu_input(void);
#endif #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) static bool pause_key_pressed(void)
{ {
retro_input_t old_input = 0; return driver.input->key_pressed(driver.input_data, RARCH_PAUSE_TOGGLE);
retro_input_t input = meta_input_keys_pressed(RARCH_PAUSE_TOGGLE, RARCH_PAUSE_TOGGLE+1, &old_input);
return BIND_PRESSED(input, RARCH_PAUSE_TOGGLE);
} }
/* Handle all events. If our activity is in pause state, /* 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 * from the specified key up until the last queryable key
* (key_end). * (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, * TODO: In case RARCH_BIND_LIST_END starts exceeding 64,
* and you need a bitmask of more than 64 entries, don't * and you need a bitmask of more than 64 entries, don't
* use this function. * use this function.
@ -1634,20 +1638,29 @@ retro_input_t meta_input_keys_pressed(unsigned key,
unsigned key_end, retro_input_t *old_state) unsigned key_end, retro_input_t *old_state)
{ {
static retro_input_t old_ret = 0; static retro_input_t old_ret = 0;
static const struct retro_keybind *binds[] = { g_settings.input.binds[0] };
retro_input_t ret = 0; retro_input_t ret = 0;
*old_state = old_ret; *old_state = old_ret;
int i;
#ifdef RARCH_INTERNAL #ifdef RARCH_INTERNAL
rarch_check_block_hotkey(driver.input->key_pressed(driver.input_data, rarch_check_block_hotkey(driver.input->key_pressed(driver.input_data,
RARCH_ENABLE_HOTKEY)); RARCH_ENABLE_HOTKEY));
#endif #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++) for (; key < key_end; key++)
{ {
bool state = false; bool state = false;
if (!driver.block_hotkey if (!driver.block_hotkey)
&& g_extern.frame_count > driver.block_hotkey_until)
state = driver.input->key_pressed(driver.input_data, key); state = driver.input->key_pressed(driver.input_data, key);
#ifdef HAVE_OVERLAY #ifdef HAVE_OVERLAY
@ -1663,6 +1676,10 @@ retro_input_t meta_input_keys_pressed(unsigned key,
ret |= (1ULL << 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; old_ret = ret;
return ret; return ret;

View File

@ -183,8 +183,7 @@ static int16_t input_state(unsigned port, unsigned device,
if (!driver.block_libretro_input && if (!driver.block_libretro_input &&
((id < RARCH_FIRST_META_KEY || ((id < RARCH_FIRST_META_KEY ||
device == RETRO_DEVICE_KEYBOARD) && device == RETRO_DEVICE_KEYBOARD))
(g_extern.frame_count > driver.block_libretro_input_until))
) )
res = driver.input->input_state(driver.input_data, binds, port, res = driver.input->input_state(driver.input_data, binds, port,
device, index, id); device, index, id);
@ -203,6 +202,35 @@ static int16_t input_state(unsigned port, unsigned device,
} }
#endif #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. */ /* Don't allow turbo for D-pad. */
if (device == RETRO_DEVICE_JOYPAD && (id < RETRO_DEVICE_ID_JOYPAD_UP || if (device == RETRO_DEVICE_JOYPAD && (id < RETRO_DEVICE_ID_JOYPAD_UP ||
id > RETRO_DEVICE_ID_JOYPAD_RIGHT)) 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); 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. */ /* Restore libretro keyboard callback. */
g_extern.system.key_event = g_extern.frontend_key_event; g_extern.system.key_event = g_extern.frontend_key_event;
break; break;
@ -3226,9 +3228,17 @@ bool rarch_main_iterate(void)
{ {
unsigned i; unsigned i;
retro_input_t old_input, trigger_input; 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); RARCH_BIND_LIST_END, &old_input);
if (driver.flushing_input)
{
if (input)
input = 0;
else
driver.flushing_input = false;
}
trigger_input = input & ~old_input; trigger_input = input & ~old_input;
/* Time to drop? */ /* Time to drop? */