diff --git a/frontend/menu/disp/rgui.c b/frontend/menu/disp/rgui.c index ee22fa0dae..8d4667075f 100644 --- a/frontend/menu/disp/rgui.c +++ b/frontend/menu/disp/rgui.c @@ -2,7 +2,7 @@ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2014 - Daniel De Matteis * Copyright (C) 2012-2014 - Michael Lelli - * + * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. @@ -230,7 +230,7 @@ static void rgui_render_messagebox(void *data, const char *message) unsigned height = FONT_HEIGHT_STRIDE * list->size + 6 + 10; int x = (rgui->width - width) / 2; int y = (rgui->height - height) / 2; - + fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, x + 5, y + 5, width - 10, height - 10, gray_filler); @@ -261,7 +261,7 @@ static void rgui_render(void *data) { rgui_handle_t *rgui = (rgui_handle_t*)data; - if (rgui->need_refresh && + if (rgui->need_refresh && (g_extern.lifecycle_state & (1ULL << MODE_MENU)) && !rgui->msg_force) return; @@ -270,7 +270,7 @@ static void rgui_render(void *data) rgui->selection_ptr - RGUI_TERM_HEIGHT / 2 : 0; size_t end = rgui->selection_ptr + RGUI_TERM_HEIGHT <= rgui->selection_buf->size ? rgui->selection_ptr + RGUI_TERM_HEIGHT : rgui->selection_buf->size; - + // Do not scroll if all items are visible. if (rgui->selection_buf->size <= RGUI_TERM_HEIGHT) begin = 0; @@ -295,7 +295,9 @@ static void rgui_render(void *data) snprintf(title, sizeof(title), "DISK APPEND %s", dir); else if (menu_type == RGUI_SETTINGS_VIDEO_OPTIONS) strlcpy(title, "VIDEO OPTIONS", sizeof(title)); - else if (menu_type == RGUI_SETTINGS_INPUT_OPTIONS) + else if (menu_type == RGUI_SETTINGS_INPUT_OPTIONS || + menu_type == RGUI_SETTINGS_CUSTOM_BIND || + menu_type == RGUI_SETTINGS_CUSTOM_BIND_KEYBOARD) strlcpy(title, "INPUT OPTIONS", sizeof(title)); else if (menu_type == RGUI_SETTINGS_OVERLAY_OPTIONS) strlcpy(title, "OVERLAY OPTIONS", sizeof(title)); @@ -322,18 +324,17 @@ static void rgui_render(void *data) else if (menu_type == RGUI_SETTINGS_CORE_OPTIONS) strlcpy(title, "CORE OPTIONS", sizeof(title)); else if (menu_type == RGUI_SETTINGS_CORE_INFO) - strlcpy(title, "CORE INFO", sizeof(title)); + strlcpy(title, "CORE INFO", sizeof(title)); else if (menu_type == RGUI_SETTINGS_PRIVACY_OPTIONS) - strlcpy(title, "PRIVACY OPTIONS", sizeof(title)); + strlcpy(title, "PRIVACY OPTIONS", sizeof(title)); #ifdef HAVE_SHADER_MANAGER else if (menu_type_is(menu_type) == RGUI_SETTINGS_SHADER_OPTIONS) snprintf(title, sizeof(title), "SHADER %s", dir); #endif - else if ((menu_type == RGUI_SETTINGS_INPUT_OPTIONS) || - (menu_type == RGUI_SETTINGS_PATH_OPTIONS) || - (menu_type == RGUI_SETTINGS_OPTIONS) || - (menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT || menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT_2) || - menu_type == RGUI_SETTINGS_CUSTOM_BIND || + else if (menu_type == RGUI_SETTINGS_PATH_OPTIONS || + menu_type == RGUI_SETTINGS_OPTIONS || + menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT || + menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT_2 || menu_type == RGUI_START_SCREEN || menu_type == RGUI_SETTINGS) snprintf(title, sizeof(title), "MENU %s", dir); @@ -417,7 +418,7 @@ static void rgui_render(void *data) char type_str[256]; unsigned w = 19; - if (menu_type == RGUI_SETTINGS_INPUT_OPTIONS || menu_type == RGUI_SETTINGS_CUSTOM_BIND) + if (menu_type == RGUI_SETTINGS_INPUT_OPTIONS || menu_type == RGUI_SETTINGS_CUSTOM_BIND || menu_type == RGUI_SETTINGS_CUSTOM_BIND_KEYBOARD) w = 21; else if (menu_type == RGUI_SETTINGS_PATH_OPTIONS) w = 24; diff --git a/frontend/menu/menu_common.c b/frontend/menu/menu_common.c index e51e7d3f39..674e2252f3 100644 --- a/frontend/menu/menu_common.c +++ b/frontend/menu/menu_common.c @@ -1,7 +1,7 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2014 - Daniel De Matteis - * + * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. @@ -29,6 +29,7 @@ #include "../../file.h" #include "../../file_ext.h" #include "../../input/input_common.h" +#include "../../input/keyboard_line.h" #include "../../compat/posix_string.h" @@ -594,6 +595,55 @@ static int menu_custom_bind_iterate(void *data, unsigned action) return 0; } +bool menu_custom_bind_keyboard_cb(void *data, unsigned code) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; + rgui->binds.target->key = code; + rgui->binds.begin++; + rgui->binds.target++; + rgui->binds.timeout_end = rarch_get_time_usec() + RGUI_KEYBOARD_BIND_TIMEOUT_SECONDS * 1000000; + return rgui->binds.begin <= rgui->binds.last; +} + +static int menu_custom_bind_iterate_keyboard(void *data, unsigned action) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; + (void)action; // Have to ignore action here. + + if (driver.video_data && driver.menu_ctx && driver.menu_ctx->render) + driver.menu_ctx->render(rgui); + + int64_t current = rarch_get_time_usec(); + int timeout = (rgui->binds.timeout_end - current) / 1000000; + + char msg[256]; + snprintf(msg, sizeof(msg), "[%s]\npress keyboard\n(timeout %d seconds)", + input_config_bind_map[rgui->binds.begin - RGUI_SETTINGS_BIND_BEGIN].desc, timeout); + + if (driver.video_data && driver.menu_ctx && driver.menu_ctx->render_messagebox) + driver.menu_ctx->render_messagebox(rgui, msg); + + if (timeout <= 0) + { + rgui->binds.begin++; + rgui->binds.target->key = RETROK_UNKNOWN; // Could be unsafe, but whatever. + rgui->binds.target++; + rgui->binds.timeout_end = rarch_get_time_usec() + RGUI_KEYBOARD_BIND_TIMEOUT_SECONDS * 1000000; + } + + // binds.begin is updated in keyboard_press callback. + if (rgui->binds.begin > rgui->binds.last) + { + file_list_pop(rgui->menu_stack, &rgui->selection_ptr); + + // Avoid new binds triggering things right away. + rgui->trigger_state = 0; + rgui->old_input_state = -1ULL; + input_keyboard_wait_keys_cancel(); + } + return 0; +} + static int menu_start_screen_iterate(void *data, unsigned action) { unsigned i; @@ -808,7 +858,7 @@ static int menu_viewport_iterate(void *data, unsigned action) base_msg, custom->width, custom->height, custom->width / geom->base_width, - custom->height / geom->base_height); + custom->height / geom->base_height); } else { @@ -818,7 +868,7 @@ static int menu_viewport_iterate(void *data, unsigned action) base_msg = "Set Bottom-Right Corner"; snprintf(msg, sizeof(msg), "%s (%d, %d : %4ux%4u)", - base_msg, custom->x, custom->y, custom->width, custom->height); + base_msg, custom->x, custom->y, custom->width, custom->height); } if (driver.video_data && driver.menu_ctx && driver.menu_ctx->render_messagebox) @@ -970,8 +1020,8 @@ static int menu_settings_iterate(void *data, unsigned action) || menu_type == RGUI_SETTINGS_AUDIO_OPTIONS || menu_type == RGUI_SETTINGS_DISK_OPTIONS || menu_type == RGUI_SETTINGS_PRIVACY_OPTIONS - || menu_type == RGUI_SETTINGS_GENERAL_OPTIONS - || menu_type == RGUI_SETTINGS_VIDEO_OPTIONS + || menu_type == RGUI_SETTINGS_GENERAL_OPTIONS + || menu_type == RGUI_SETTINGS_VIDEO_OPTIONS || menu_type == RGUI_SETTINGS_FONT_OPTIONS || menu_type == RGUI_SETTINGS_SHADER_OPTIONS ) @@ -1054,6 +1104,8 @@ static int menu_iterate_func(void *data, unsigned action) return menu_viewport_iterate(rgui, action); else if (menu_type == RGUI_SETTINGS_CUSTOM_BIND) return menu_custom_bind_iterate(rgui, action); + else if (menu_type == RGUI_SETTINGS_CUSTOM_BIND_KEYBOARD) + return menu_custom_bind_iterate_keyboard(rgui, action); if (rgui->need_refresh && action != RGUI_ACTION_MESSAGE) action = RGUI_ACTION_NOOP; @@ -1097,7 +1149,7 @@ static int menu_iterate_func(void *data, unsigned action) case RGUI_ACTION_SCROLL_DOWN: menu_ascend_alphabet(rgui, &rgui->selection_ptr); break; - + case RGUI_ACTION_CANCEL: if (rgui->menu_stack->size > 1) { @@ -1367,7 +1419,7 @@ static int menu_iterate_func(void *data, unsigned action) if (rgui->need_refresh && (menu_type == RGUI_FILE_DIRECTORY || menu_type_is(menu_type) == RGUI_SETTINGS_SHADER_OPTIONS || - menu_type_is(menu_type) == RGUI_FILE_DIRECTORY || + menu_type_is(menu_type) == RGUI_FILE_DIRECTORY || menu_type == RGUI_SETTINGS_OVERLAY_PRESET || menu_type == RGUI_SETTINGS_DEFERRED_CORE || menu_type == RGUI_SETTINGS_CORE || @@ -1396,7 +1448,7 @@ bool menu_iterate(void) static bool first_held = false; uint64_t input_state = 0; int32_t input_entry_ret, ret; - + input_entry_ret = 0; ret = 0; @@ -1700,7 +1752,7 @@ static bool menu_poll_find_trigger_pad(struct rgui_bind_state *state, struct rgu state->target->joykey = NO_BTN; // Lock the current axis. - new_state->axis_state[p].locked_axes[a] = n->axes[a] > 0 ? 0x7fff : -0x7fff; + new_state->axis_state[p].locked_axes[a] = n->axes[a] > 0 ? 0x7fff : -0x7fff; return true; } @@ -1751,7 +1803,7 @@ static inline int menu_list_get_first_char(file_list_t *buf, unsigned offset) const char *path = NULL; file_list_get_alt_at_offset(buf, offset, &path); int ret = tolower(*path); - + // "Normalize" non-alphabetical entries so they are lumped together for purposes of jumping. if (ret < 'a') ret = 'a' - 1; @@ -1925,7 +1977,7 @@ void menu_populate_entries(void *data, unsigned menu_type) } else file_list_push(rgui->selection_buf, "No options available.", RGUI_SETTINGS_CORE_OPTION_NONE, 0); - break; + break; case RGUI_SETTINGS_CORE_INFO: file_list_clear(rgui->selection_buf); if (rgui->core_info_current.data) @@ -1964,7 +2016,7 @@ void menu_populate_entries(void *data, unsigned menu_type) for (i = 0; i < rgui->core_info_current.firmware_count; i++) { if (rgui->core_info_current.firmware[i].desc) - { + { snprintf(tmp, sizeof(tmp), " name: %s", rgui->core_info_current.firmware[i].desc ? rgui->core_info_current.firmware[i].desc : ""); file_list_push(rgui->selection_buf, tmp, RGUI_SETTINGS_CORE_INFO_NONE, 0); @@ -1991,7 +2043,7 @@ void menu_populate_entries(void *data, unsigned menu_type) } else file_list_push(rgui->selection_buf, "No information available.", RGUI_SETTINGS_CORE_OPTION_NONE, 0); - break; + break; case RGUI_SETTINGS_OPTIONS: file_list_clear(rgui->selection_buf); file_list_push(rgui->selection_buf, "General Options", RGUI_SETTINGS_GENERAL_OPTIONS, 0); @@ -2136,7 +2188,7 @@ void menu_populate_entries(void *data, unsigned menu_type) } file_list_push(rgui->selection_buf, "Core Options", RGUI_SETTINGS_CORE_OPTIONS, 0); - file_list_push(rgui->selection_buf, "Core Information", RGUI_SETTINGS_CORE_INFO, 0); + file_list_push(rgui->selection_buf, "Core Information", RGUI_SETTINGS_CORE_INFO, 0); file_list_push(rgui->selection_buf, "Settings", RGUI_SETTINGS_OPTIONS, 0); file_list_push(rgui->selection_buf, "Drivers", RGUI_SETTINGS_DRIVERS, 0); diff --git a/frontend/menu/menu_common.h b/frontend/menu/menu_common.h index 0c7a85ed0a..29bef0ce21 100644 --- a/frontend/menu/menu_common.h +++ b/frontend/menu/menu_common.h @@ -1,7 +1,7 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2014 - Daniel De Matteis - * + * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. @@ -249,6 +249,7 @@ typedef enum RGUI_SETTINGS_BIND_GRAB_MOUSE_TOGGLE, RGUI_SETTINGS_BIND_MENU_TOGGLE, RGUI_SETTINGS_CUSTOM_BIND, + RGUI_SETTINGS_CUSTOM_BIND_KEYBOARD, RGUI_SETTINGS_CUSTOM_BIND_ALL, RGUI_SETTINGS_CUSTOM_BIND_DEFAULT_ALL, RGUI_SETTINGS_ONSCREEN_KEYBOARD_ENABLE, @@ -294,9 +295,11 @@ struct rgui_bind_axis_state int16_t locked_axes[RGUI_MAX_AXES]; }; +#define RGUI_KEYBOARD_BIND_TIMEOUT_SECONDS 3 struct rgui_bind_state { struct retro_keybind *target; + int64_t timeout_end; // For keyboard binding. unsigned begin; unsigned last; unsigned player; @@ -308,6 +311,7 @@ struct rgui_bind_state void menu_poll_bind_get_rested_axes(struct rgui_bind_state *state); void menu_poll_bind_state(struct rgui_bind_state *state); bool menu_poll_find_trigger(struct rgui_bind_state *state, struct rgui_bind_state *new_state); +bool menu_custom_bind_keyboard_cb(void *data, unsigned code); #ifdef GEKKO enum @@ -337,12 +341,12 @@ enum GX_RESOLUTIONS_448_448, GX_RESOLUTIONS_480_448, GX_RESOLUTIONS_512_448, - GX_RESOLUTIONS_576_448, - GX_RESOLUTIONS_608_448, - GX_RESOLUTIONS_640_448, - GX_RESOLUTIONS_340_464, - GX_RESOLUTIONS_512_464, - GX_RESOLUTIONS_512_472, + GX_RESOLUTIONS_576_448, + GX_RESOLUTIONS_608_448, + GX_RESOLUTIONS_640_448, + GX_RESOLUTIONS_340_464, + GX_RESOLUTIONS_512_464, + GX_RESOLUTIONS_512_472, GX_RESOLUTIONS_384_480, GX_RESOLUTIONS_512_480, GX_RESOLUTIONS_530_480, diff --git a/frontend/menu/menu_input_line_cb.c b/frontend/menu/menu_input_line_cb.c index 6d0d67b053..a2b2d39fd0 100644 --- a/frontend/menu/menu_input_line_cb.c +++ b/frontend/menu/menu_input_line_cb.c @@ -1,7 +1,7 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2014 - Daniel De Matteis - * + * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. diff --git a/frontend/menu/menu_settings.c b/frontend/menu/menu_settings.c index bdea6861c3..d1da59e6e9 100644 --- a/frontend/menu/menu_settings.c +++ b/frontend/menu/menu_settings.c @@ -1001,6 +1001,15 @@ int menu_set_settings(void *data, unsigned setting, unsigned action) menu_poll_bind_get_rested_axes(&rgui->binds); menu_poll_bind_state(&rgui->binds); } + else if (action == RGUI_ACTION_RIGHT) // Hack + { + rgui->binds.target = &g_settings.input.binds[port][0]; + rgui->binds.begin = RGUI_SETTINGS_BIND_BEGIN; + rgui->binds.last = RGUI_SETTINGS_BIND_LAST; + file_list_push(rgui->menu_stack, "", RGUI_SETTINGS_CUSTOM_BIND_KEYBOARD, rgui->selection_ptr); + rgui->binds.timeout_end = rarch_get_time_usec() + RGUI_KEYBOARD_BIND_TIMEOUT_SECONDS * 1000000; + input_keyboard_wait_keys(rgui, menu_custom_bind_keyboard_cb); + } break; case RGUI_SETTINGS_CUSTOM_BIND_DEFAULT_ALL: if (action == RGUI_ACTION_OK) @@ -1087,15 +1096,26 @@ int menu_set_settings(void *data, unsigned setting, unsigned action) else { struct retro_keybind *bind = &g_settings.input.binds[port][setting - RGUI_SETTINGS_BIND_BEGIN]; - if (action == RGUI_ACTION_OK) + // FIXME: Hack, use RIGHT action to signal keyboard bind. Need something more sane. + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_RIGHT) { rgui->binds.begin = setting; rgui->binds.last = setting; rgui->binds.target = bind; rgui->binds.player = port; - file_list_push(rgui->menu_stack, "", RGUI_SETTINGS_CUSTOM_BIND, rgui->selection_ptr); - menu_poll_bind_get_rested_axes(&rgui->binds); - menu_poll_bind_state(&rgui->binds); + file_list_push(rgui->menu_stack, "", + action == RGUI_ACTION_OK ? RGUI_SETTINGS_CUSTOM_BIND : RGUI_SETTINGS_CUSTOM_BIND_KEYBOARD, rgui->selection_ptr); + + if (action == RGUI_ACTION_OK) + { + menu_poll_bind_get_rested_axes(&rgui->binds); + menu_poll_bind_state(&rgui->binds); + } + else + { + rgui->binds.timeout_end = rarch_get_time_usec() + RGUI_KEYBOARD_BIND_TIMEOUT_SECONDS * 1000000; + input_keyboard_wait_keys(rgui, menu_custom_bind_keyboard_cb); + } } else if (action == RGUI_ACTION_START) { diff --git a/input/keyboard_line.c b/input/keyboard_line.c index 178fad6167..0e31efb828 100644 --- a/input/keyboard_line.c +++ b/input/keyboard_line.c @@ -1,6 +1,6 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen - * + * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. @@ -98,6 +98,9 @@ const char **input_keyboard_line_get_buffer(const input_keyboard_line_t *state) static input_keyboard_line_t *g_keyboard_line; +static input_keyboard_press_t g_keyboard_press_cb; +static void *g_keyboard_press_data; + const char **input_keyboard_start_line(void *userdata, input_keyboard_line_complete_t cb) { if (g_keyboard_line) @@ -110,9 +113,29 @@ const char **input_keyboard_start_line(void *userdata, input_keyboard_line_compl return input_keyboard_line_get_buffer(g_keyboard_line); } +void input_keyboard_wait_keys(void *userdata, input_keyboard_press_t cb) +{ + g_keyboard_press_cb = cb; + g_keyboard_press_data = userdata; + // While waiting for input, we have to block all hotkeys. + driver.block_input = true; +} + +void input_keyboard_wait_keys_cancel(void) +{ + g_keyboard_press_cb = NULL; + g_keyboard_press_data = NULL; + driver.block_input = false; +} + void input_keyboard_event(bool down, unsigned code, uint32_t character, uint16_t mod) { - if (g_keyboard_line) + if (g_keyboard_press_cb) + { + if (down && code != RETROK_UNKNOWN && !g_keyboard_press_cb(g_keyboard_press_data, code)) + input_keyboard_wait_keys_cancel(); + } + else if (g_keyboard_line) { if (input_keyboard_line_event(g_keyboard_line, character)) { diff --git a/input/keyboard_line.h b/input/keyboard_line.h index 997e864ab2..a01e0eb08e 100644 --- a/input/keyboard_line.h +++ b/input/keyboard_line.h @@ -1,6 +1,6 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen - * + * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. @@ -31,6 +31,8 @@ typedef struct input_keyboard_line input_keyboard_line_t; // line can be NULL. typedef void (*input_keyboard_line_complete_t)(void *userdata, const char *line); +typedef bool (*input_keyboard_press_t)(void *userdata, unsigned code); + input_keyboard_line_t *input_keyboard_line_new(void *userdata, input_keyboard_line_complete_t cb); @@ -46,6 +48,11 @@ void input_keyboard_line_free(input_keyboard_line_t *state); void input_keyboard_event(bool down, unsigned code, uint32_t character, uint16_t mod); const char **input_keyboard_start_line(void *userdata, input_keyboard_line_complete_t cb); +// Wait for keys to be pressed (used for binding keys in RGUI). +// Callback returns false when all polling is done. +void input_keyboard_wait_keys(void *userdata, input_keyboard_press_t cb); +void input_keyboard_wait_keys_cancel(void); + #ifdef __cplusplus } #endif