diff --git a/Makefile.common b/Makefile.common index 439b56aaad..9b692fe951 100644 --- a/Makefile.common +++ b/Makefile.common @@ -511,6 +511,7 @@ ifeq ($(HAVE_MENU_COMMON), 1) menu/menu_shader.o \ menu/widgets/menu_dialog.o \ menu/widgets/menu_input_dialog.o \ + menu/widgets/menu_input_bind_dialog.o \ menu/widgets/menu_entry.o \ menu/widgets/menu_list.o \ menu/menu_cbs.o \ diff --git a/griffin/griffin.c b/griffin/griffin.c index 37f0c26a88..9fca97f2c0 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -895,6 +895,7 @@ MENU #include "../menu/widgets/menu_entry.c" #include "../menu/widgets/menu_dialog.c" #include "../menu/widgets/menu_input_dialog.c" +#include "../menu/widgets/menu_input_bind_dialog.c" #include "../menu/widgets/menu_list.c" #include "../menu/cbs/menu_cbs_ok.c" #include "../menu/cbs/menu_cbs_cancel.c" diff --git a/menu/drivers/menu_generic.c b/menu/drivers/menu_generic.c index 33f09b176b..1ae66039a6 100644 --- a/menu/drivers/menu_generic.c +++ b/menu/drivers/menu_generic.c @@ -24,6 +24,7 @@ #include "../menu_display.h" #include "../menu_navigation.h" #include "../widgets/menu_dialog.h" +#include "../widgets/menu_input_bind_dialog.h" #include "../../verbosity.h" #include "../../runloop.h" diff --git a/menu/menu_input.c b/menu/menu_input.c index 41d4bac4a4..2e349065f3 100644 --- a/menu/menu_input.c +++ b/menu/menu_input.c @@ -18,29 +18,26 @@ #include "../../config.h" #endif -#define MENU_MAX_BUTTONS 219 -#define MENU_MAX_AXES 32 -#define MENU_MAX_HATS 4 - #include #include #include #include #include -#include #include #ifdef HAVE_CONFIG_H #include "../config.h" #endif +#include "widgets/menu_entry.h" +#include "widgets/menu_input_dialog.h" +#include "widgets/menu_input_bind_dialog.h" + #include "menu_driver.h" #include "menu_input.h" #include "menu_animation.h" #include "menu_display.h" -#include "widgets/menu_entry.h" -#include "widgets/menu_input_dialog.h" #include "menu_setting.h" #include "menu_shader.h" #include "menu_navigation.h" @@ -66,39 +63,8 @@ enum menu_mouse_action MENU_MOUSE_ACTION_HORIZ_WHEEL_DOWN }; -struct menu_bind_state_port -{ - bool buttons[MENU_MAX_BUTTONS]; - int16_t axes[MENU_MAX_AXES]; - uint16_t hats[MENU_MAX_HATS]; -}; - -struct menu_bind_axis_state -{ - /* Default axis state. */ - int16_t rested_axes[MENU_MAX_AXES]; - /* Locked axis state. If we configured an axis, - * avoid having the same axis state trigger something again right away. */ - int16_t locked_axes[MENU_MAX_AXES]; -}; - -struct menu_bind_state -{ - struct retro_keybind *target; - /* For keyboard binding. */ - int64_t timeout_end; - unsigned begin; - unsigned last; - unsigned user; - struct menu_bind_state_port state[MAX_USERS]; - struct menu_bind_axis_state axis_state[MAX_USERS]; - bool skip; -}; - typedef struct menu_input { - struct menu_bind_state binds; - struct { unsigned ptr; @@ -125,8 +91,6 @@ typedef struct menu_input } delay; } menu_input_t; -static unsigned bind_port; - static menu_input_t *menu_input_get_ptr(void) { static menu_input_t menu_input_state; @@ -180,393 +144,6 @@ void menu_input_st_cheat_cb(void *userdata, const char *str) menu_input_dialog_end(); } -static bool menu_input_key_bind_custom_bind_keyboard_cb( - void *data, unsigned code) -{ - menu_input_t *menu_input = menu_input_get_ptr(); - settings_t *settings = config_get_ptr(); - - if (!menu_input) - return false; - - menu_input->binds.target->key = (enum retro_key)code; - menu_input->binds.begin++; - menu_input->binds.target++; - menu_input->binds.timeout_end = cpu_features_get_time_usec() + - settings->input.bind_timeout * 1000000; - - return (menu_input->binds.begin <= menu_input->binds.last); -} - -static int menu_input_key_bind_set_mode_common( - enum menu_input_ctl_state state, - rarch_setting_t *setting) -{ - size_t selection; - unsigned index_offset, bind_type; - menu_displaylist_info_t info = {0}; - struct retro_keybind *keybind = NULL; - file_list_t *menu_stack = NULL; - settings_t *settings = config_get_ptr(); - menu_input_t *menu_input = menu_input_get_ptr(); - - if (!setting) - return -1; - - index_offset = setting_get_index_offset(setting); - menu_stack = menu_entries_get_menu_stack_ptr(0); - - menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection); - - switch (state) - { - case MENU_INPUT_CTL_BIND_NONE: - return -1; - case MENU_INPUT_CTL_BIND_SINGLE: - keybind = (struct retro_keybind*)setting_get_ptr(setting); - - if (!keybind) - return -1; - - bind_type = setting_get_bind_type(setting); - - menu_input->binds.begin = bind_type; - menu_input->binds.last = bind_type; - menu_input->binds.target = keybind; - menu_input->binds.user = index_offset; - - info.list = menu_stack; - info.type = MENU_SETTINGS_CUSTOM_BIND_KEYBOARD; - info.directory_ptr = selection; - info.enum_idx = MENU_ENUM_LABEL_CUSTOM_BIND; - strlcpy(info.label, - msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND), sizeof(info.label)); - - if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info)) - menu_displaylist_ctl(DISPLAYLIST_PROCESS, &info); - break; - case MENU_INPUT_CTL_BIND_ALL: - menu_input->binds.target = &settings->input.binds - [index_offset][0]; - menu_input->binds.begin = MENU_SETTINGS_BIND_BEGIN; - menu_input->binds.last = MENU_SETTINGS_BIND_LAST; - - info.list = menu_stack; - info.type = MENU_SETTINGS_CUSTOM_BIND_KEYBOARD; - info.directory_ptr = selection; - info.enum_idx = MENU_ENUM_LABEL_CUSTOM_BIND_ALL; - strlcpy(info.label, - msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND_ALL), - sizeof(info.label)); - - if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info)) - menu_displaylist_ctl(DISPLAYLIST_PROCESS, &info); - break; - default: - case MENU_INPUT_CTL_NONE: - break; - } - - return 0; -} - -static void menu_input_key_bind_poll_bind_get_rested_axes( - struct menu_bind_state *state, unsigned port) -{ - unsigned a; - const input_device_driver_t *joypad = - input_driver_get_joypad_driver(); - const input_device_driver_t *sec_joypad = - input_driver_get_sec_joypad_driver(); - - if (!state || !joypad) - return; - - /* poll only the relevant port */ - for (a = 0; a < MENU_MAX_AXES; a++) - state->axis_state[port].rested_axes[a] = - input_joypad_axis_raw(joypad, port, a); - - if (sec_joypad) - { - /* poll only the relevant port */ - for (a = 0; a < MENU_MAX_AXES; a++) - state->axis_state[port].rested_axes[a] = - input_joypad_axis_raw(sec_joypad, port, a); - } -} - -static void menu_input_key_bind_poll_bind_state_internal( - const input_device_driver_t *joypad, - struct menu_bind_state *state, - unsigned port, - bool timed_out) -{ - unsigned b, a, h; - if (!joypad) - return; - - if (joypad->poll) - joypad->poll(); - - /* poll only the relevant port */ - /* for (i = 0; i < settings->input.max_users; i++) */ - for (b = 0; b < MENU_MAX_BUTTONS; b++) - state->state[port].buttons[b] = - input_joypad_button_raw(joypad, port, b); - - for (a = 0; a < MENU_MAX_AXES; a++) - state->state[port].axes[a] = - input_joypad_axis_raw(joypad, port, a); - - for (h = 0; h < MENU_MAX_HATS; h++) - { - if (input_joypad_hat_raw(joypad, port, HAT_UP_MASK, h)) - state->state[port].hats[h] |= HAT_UP_MASK; - if (input_joypad_hat_raw(joypad, port, HAT_DOWN_MASK, h)) - state->state[port].hats[h] |= HAT_DOWN_MASK; - if (input_joypad_hat_raw(joypad, port, HAT_LEFT_MASK, h)) - state->state[port].hats[h] |= HAT_LEFT_MASK; - if (input_joypad_hat_raw(joypad, port, HAT_RIGHT_MASK, h)) - state->state[port].hats[h] |= HAT_RIGHT_MASK; - } -} - -static void menu_input_key_bind_poll_bind_state( - struct menu_bind_state *state, - unsigned port, - bool timed_out) -{ - const input_device_driver_t *joypad = - input_driver_get_joypad_driver(); - const input_device_driver_t *sec_joypad = - input_driver_get_sec_joypad_driver(); - - if (!state) - return; - - memset(state->state, 0, sizeof(state->state)); - state->skip = timed_out || input_driver_state(NULL, 0, - RETRO_DEVICE_KEYBOARD, 0, RETROK_RETURN); - - menu_input_key_bind_poll_bind_state_internal( - joypad, state, port, timed_out); - - if (sec_joypad) - menu_input_key_bind_poll_bind_state_internal( - sec_joypad, state, port, timed_out); -} - -static bool menu_input_key_bind_set_mode( - enum menu_input_ctl_state state, void *data) -{ - unsigned index_offset; - input_keyboard_ctx_wait_t keys; - menu_handle_t *menu = NULL; - menu_input_t *menu_input = menu_input_get_ptr(); - rarch_setting_t *setting = (rarch_setting_t*)data; - settings_t *settings = config_get_ptr(); - - if (!setting) - return false; - if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) - return false; - if (menu_input_key_bind_set_mode_common(state, setting) == -1) - return false; - - index_offset = setting_get_index_offset(setting); - bind_port = settings->input.joypad_map[index_offset]; - - menu_input_key_bind_poll_bind_get_rested_axes( - &menu_input->binds, bind_port); - menu_input_key_bind_poll_bind_state( - &menu_input->binds, bind_port, false); - - menu_input->binds.timeout_end = cpu_features_get_time_usec() + - settings->input.bind_timeout * 1000000; - - keys.userdata = menu; - keys.cb = menu_input_key_bind_custom_bind_keyboard_cb; - - input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_START_WAIT_KEYS, &keys); - return true; -} - -static bool menu_input_key_bind_poll_find_trigger_pad( - struct menu_bind_state *state, - struct menu_bind_state *new_state, - unsigned p) -{ - unsigned a, b, h; - const struct menu_bind_state_port *n = (const struct menu_bind_state_port*) - &new_state->state[p]; - const struct menu_bind_state_port *o = (const struct menu_bind_state_port*) - &state->state[p]; - - for (b = 0; b < MENU_MAX_BUTTONS; b++) - { - bool iterate = n->buttons[b] && !o->buttons[b]; - - if (!iterate) - continue; - - state->target->joykey = b; - state->target->joyaxis = AXIS_NONE; - return true; - } - - /* Axes are a bit tricky ... */ - for (a = 0; a < MENU_MAX_AXES; a++) - { - int locked_distance = abs(n->axes[a] - - new_state->axis_state[p].locked_axes[a]); - int rested_distance = abs(n->axes[a] - - new_state->axis_state[p].rested_axes[a]); - - if (abs(n->axes[a]) >= 20000 && - locked_distance >= 20000 && - rested_distance >= 20000) - { - /* Take care of case where axis rests on +/- 0x7fff - * (e.g. 360 controller on Linux) */ - state->target->joyaxis = n->axes[a] > 0 - ? AXIS_POS(a) : AXIS_NEG(a); - state->target->joykey = NO_BTN; - - /* Lock the current axis */ - new_state->axis_state[p].locked_axes[a] = - n->axes[a] > 0 ? - 0x7fff : -0x7fff; - return true; - } - - if (locked_distance >= 20000) /* Unlock the axis. */ - new_state->axis_state[p].locked_axes[a] = 0; - } - - for (h = 0; h < MENU_MAX_HATS; h++) - { - uint16_t trigged = n->hats[h] & (~o->hats[h]); - uint16_t sane_trigger = 0; - - if (trigged & HAT_UP_MASK) - sane_trigger = HAT_UP_MASK; - else if (trigged & HAT_DOWN_MASK) - sane_trigger = HAT_DOWN_MASK; - else if (trigged & HAT_LEFT_MASK) - sane_trigger = HAT_LEFT_MASK; - else if (trigged & HAT_RIGHT_MASK) - sane_trigger = HAT_RIGHT_MASK; - - if (sane_trigger) - { - state->target->joykey = HAT_MAP(h, sane_trigger); - state->target->joyaxis = AXIS_NONE; - return true; - } - } - - return false; -} - -static bool menu_input_key_bind_poll_find_trigger( - struct menu_bind_state *state, - struct menu_bind_state *new_state) -{ - unsigned i; - settings_t *settings = config_get_ptr(); - - if (!state || !new_state) - return false; - - for (i = 0; i < settings->input.max_users; i++) - { - if (!menu_input_key_bind_poll_find_trigger_pad( - state, new_state, i)) - continue; - - /* Update the joypad mapping automatically. - * More friendly that way. */ -#if 0 - settings->input.joypad_map[state->user] = i; -#endif - return true; - } - return false; -} - - -static bool menu_input_key_bind_iterate(char *s, size_t len) -{ - struct menu_bind_state binds; - bool timed_out = false; - menu_input_t *menu_input = menu_input_get_ptr(); - settings_t *settings = config_get_ptr(); - int64_t current = cpu_features_get_time_usec(); - int timeout = - (menu_input->binds.timeout_end - current) / 1000000; - - if (timeout <= 0) - { - input_driver_keyboard_mapping_set_block(false); - - menu_input->binds.begin++; - menu_input->binds.target++; - menu_input->binds.timeout_end = cpu_features_get_time_usec() + - settings->input.bind_timeout * 1000000; - timed_out = true; - } - - snprintf(s, len, - "[%s]\npress keyboard or joypad\n(timeout %d %s)", - input_config_bind_map_get_desc( - menu_input->binds.begin - MENU_SETTINGS_BIND_BEGIN), - timeout, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SECONDS)); - - /* binds.begin is updated in keyboard_press callback. */ - if (menu_input->binds.begin > menu_input->binds.last) - { - /* Avoid new binds triggering things right away. */ - input_driver_set_flushing_input(); - - /* We won't be getting any key events, so just cancel early. */ - if (timed_out) - input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_CANCEL_WAIT_KEYS, NULL); - - return true; - } - - binds = menu_input->binds; - - input_driver_keyboard_mapping_set_block(true); - menu_input_key_bind_poll_bind_state(&binds, bind_port, timed_out); - - if ((binds.skip && !menu_input->binds.skip) || - menu_input_key_bind_poll_find_trigger(&menu_input->binds, &binds)) - { - input_driver_keyboard_mapping_set_block(false); - - /* Avoid new binds triggering things right away. */ - input_driver_set_flushing_input(); - - binds.begin++; - - if (binds.begin > binds.last) - { - input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_CANCEL_WAIT_KEYS, NULL); - return true; - } - - binds.target++; - binds.timeout_end = cpu_features_get_time_usec() + - settings->input.bind_timeout * 1000000; - } - menu_input->binds = binds; - - return false; -} - bool menu_input_mouse_check_vector_inside_hitbox(menu_input_ctx_hitbox_t *hitbox) { int16_t mouse_x = menu_input_mouse_state(MENU_MOUSE_X_AXIS); @@ -591,17 +168,6 @@ bool menu_input_ctl(enum menu_input_ctl_state state, void *data) switch (state) { - case MENU_INPUT_CTL_BIND_SET_MIN_MAX: - { - menu_input_ctx_bind_limits_t *lim = - (menu_input_ctx_bind_limits_t*)data; - if (!lim || !menu_input) - return false; - - menu_input->binds.begin = lim->min; - menu_input->binds.last = lim->max; - } - break; case MENU_INPUT_CTL_DEINIT: memset(menu_input, 0, sizeof(menu_input_t)); pointer_dragging = false; diff --git a/menu/menu_input.h b/menu/menu_input.h index a448cd2e64..5d3ea16b11 100644 --- a/menu/menu_input.h +++ b/menu/menu_input.h @@ -92,17 +92,6 @@ typedef struct menu_input_ctx_hitbox int32_t y2; } menu_input_ctx_hitbox_t; -typedef struct menu_input_ctx_bind -{ - char *s; - size_t len; -} menu_input_ctx_bind_t; - -typedef struct menu_input_ctx_bind_limits -{ - unsigned min; - unsigned max; -} menu_input_ctx_bind_limits_t; /* Keyboard input callbacks */ void menu_input_st_uint_cb (void *userdata, const char *str); diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 016c8aacb4..034462435a 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -42,6 +42,8 @@ #include "../frontend/frontend_driver.h" +#include "widgets/menu_input_bind_dialog.h" + #include "menu_setting.h" #include "menu_driver.h" #include "menu_animation.h" diff --git a/menu/widgets/menu_input_bind_dialog.c b/menu/widgets/menu_input_bind_dialog.c new file mode 100644 index 0000000000..9630807bba --- /dev/null +++ b/menu/widgets/menu_input_bind_dialog.c @@ -0,0 +1,454 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2016 - 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. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include + +#include + +#include "menu_input_bind_dialog.h" + +#include "../menu_driver.h" +#include "../menu_navigation.h" + +#include "../../input/input_config.h" + +#include "../../configuration.h" + +#define MENU_MAX_BUTTONS 219 +#define MENU_MAX_AXES 32 +#define MENU_MAX_HATS 4 + +struct menu_bind_state_port +{ + bool buttons[MENU_MAX_BUTTONS]; + int16_t axes[MENU_MAX_AXES]; + uint16_t hats[MENU_MAX_HATS]; +}; + +struct menu_bind_axis_state +{ + /* Default axis state. */ + int16_t rested_axes[MENU_MAX_AXES]; + /* Locked axis state. If we configured an axis, + * avoid having the same axis state trigger something again right away. */ + int16_t locked_axes[MENU_MAX_AXES]; +}; + +struct menu_bind_state +{ + struct retro_keybind *target; + /* For keyboard binding. */ + int64_t timeout_end; + unsigned begin; + unsigned last; + unsigned user; + struct menu_bind_state_port state[MAX_USERS]; + struct menu_bind_axis_state axis_state[MAX_USERS]; + bool skip; +}; + +static unsigned menu_bind_port = 0; +static struct menu_bind_state menu_input_binds; + +static bool menu_input_key_bind_custom_bind_keyboard_cb( + void *data, unsigned code) +{ + settings_t *settings = config_get_ptr(); + + menu_input_binds.target->key = (enum retro_key)code; + menu_input_binds.begin++; + menu_input_binds.target++; + menu_input_binds.timeout_end = cpu_features_get_time_usec() + + settings->input.bind_timeout * 1000000; + + return (menu_input_binds.begin <= menu_input_binds.last); +} + +static int menu_input_key_bind_set_mode_common( + enum menu_input_ctl_state state, + rarch_setting_t *setting) +{ + size_t selection; + unsigned index_offset, bind_type; + menu_displaylist_info_t info = {0}; + struct retro_keybind *keybind = NULL; + file_list_t *menu_stack = NULL; + settings_t *settings = config_get_ptr(); + + if (!setting) + return -1; + + index_offset = setting_get_index_offset(setting); + menu_stack = menu_entries_get_menu_stack_ptr(0); + + menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SELECTION, &selection); + + switch (state) + { + case MENU_INPUT_CTL_BIND_NONE: + return -1; + case MENU_INPUT_CTL_BIND_SINGLE: + keybind = (struct retro_keybind*)setting_get_ptr(setting); + + if (!keybind) + return -1; + + bind_type = setting_get_bind_type(setting); + + menu_input_binds.begin = bind_type; + menu_input_binds.last = bind_type; + menu_input_binds.target = keybind; + menu_input_binds.user = index_offset; + + info.list = menu_stack; + info.type = MENU_SETTINGS_CUSTOM_BIND_KEYBOARD; + info.directory_ptr = selection; + info.enum_idx = MENU_ENUM_LABEL_CUSTOM_BIND; + strlcpy(info.label, + msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND), sizeof(info.label)); + + if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info)) + menu_displaylist_ctl(DISPLAYLIST_PROCESS, &info); + break; + case MENU_INPUT_CTL_BIND_ALL: + menu_input_binds.target = &settings->input.binds + [index_offset][0]; + menu_input_binds.begin = MENU_SETTINGS_BIND_BEGIN; + menu_input_binds.last = MENU_SETTINGS_BIND_LAST; + + info.list = menu_stack; + info.type = MENU_SETTINGS_CUSTOM_BIND_KEYBOARD; + info.directory_ptr = selection; + info.enum_idx = MENU_ENUM_LABEL_CUSTOM_BIND_ALL; + strlcpy(info.label, + msg_hash_to_str(MENU_ENUM_LABEL_CUSTOM_BIND_ALL), + sizeof(info.label)); + + if (menu_displaylist_ctl(DISPLAYLIST_INFO, &info)) + menu_displaylist_ctl(DISPLAYLIST_PROCESS, &info); + break; + default: + case MENU_INPUT_CTL_NONE: + break; + } + + return 0; +} + +static void menu_input_key_bind_poll_bind_get_rested_axes( + struct menu_bind_state *state, unsigned port) +{ + unsigned a; + const input_device_driver_t *joypad = + input_driver_get_joypad_driver(); + const input_device_driver_t *sec_joypad = + input_driver_get_sec_joypad_driver(); + + if (!state || !joypad) + return; + + /* poll only the relevant port */ + for (a = 0; a < MENU_MAX_AXES; a++) + state->axis_state[port].rested_axes[a] = + input_joypad_axis_raw(joypad, port, a); + + if (sec_joypad) + { + /* poll only the relevant port */ + for (a = 0; a < MENU_MAX_AXES; a++) + state->axis_state[port].rested_axes[a] = + input_joypad_axis_raw(sec_joypad, port, a); + } +} + +static void menu_input_key_bind_poll_bind_state_internal( + const input_device_driver_t *joypad, + struct menu_bind_state *state, + unsigned port, + bool timed_out) +{ + unsigned b, a, h; + if (!joypad) + return; + + if (joypad->poll) + joypad->poll(); + + /* poll only the relevant port */ + /* for (i = 0; i < settings->input.max_users; i++) */ + for (b = 0; b < MENU_MAX_BUTTONS; b++) + state->state[port].buttons[b] = + input_joypad_button_raw(joypad, port, b); + + for (a = 0; a < MENU_MAX_AXES; a++) + state->state[port].axes[a] = + input_joypad_axis_raw(joypad, port, a); + + for (h = 0; h < MENU_MAX_HATS; h++) + { + if (input_joypad_hat_raw(joypad, port, HAT_UP_MASK, h)) + state->state[port].hats[h] |= HAT_UP_MASK; + if (input_joypad_hat_raw(joypad, port, HAT_DOWN_MASK, h)) + state->state[port].hats[h] |= HAT_DOWN_MASK; + if (input_joypad_hat_raw(joypad, port, HAT_LEFT_MASK, h)) + state->state[port].hats[h] |= HAT_LEFT_MASK; + if (input_joypad_hat_raw(joypad, port, HAT_RIGHT_MASK, h)) + state->state[port].hats[h] |= HAT_RIGHT_MASK; + } +} + +static void menu_input_key_bind_poll_bind_state( + struct menu_bind_state *state, + unsigned port, + bool timed_out) +{ + const input_device_driver_t *joypad = + input_driver_get_joypad_driver(); + const input_device_driver_t *sec_joypad = + input_driver_get_sec_joypad_driver(); + + if (!state) + return; + + memset(state->state, 0, sizeof(state->state)); + state->skip = timed_out || input_driver_state(NULL, 0, + RETRO_DEVICE_KEYBOARD, 0, RETROK_RETURN); + + menu_input_key_bind_poll_bind_state_internal( + joypad, state, port, timed_out); + + if (sec_joypad) + menu_input_key_bind_poll_bind_state_internal( + sec_joypad, state, port, timed_out); +} + +bool menu_input_key_bind_set_mode( + enum menu_input_ctl_state state, void *data) +{ + unsigned index_offset; + input_keyboard_ctx_wait_t keys; + menu_handle_t *menu = NULL; + rarch_setting_t *setting = (rarch_setting_t*)data; + settings_t *settings = config_get_ptr(); + + if (!setting) + return false; + if (!menu_driver_ctl(RARCH_MENU_CTL_DRIVER_DATA_GET, &menu)) + return false; + if (menu_input_key_bind_set_mode_common(state, setting) == -1) + return false; + + index_offset = setting_get_index_offset(setting); + menu_bind_port = settings->input.joypad_map[index_offset]; + + menu_input_key_bind_poll_bind_get_rested_axes( + &menu_input_binds, menu_bind_port); + menu_input_key_bind_poll_bind_state( + &menu_input_binds, menu_bind_port, false); + + menu_input_binds.timeout_end = cpu_features_get_time_usec() + + settings->input.bind_timeout * 1000000; + + keys.userdata = menu; + keys.cb = menu_input_key_bind_custom_bind_keyboard_cb; + + input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_START_WAIT_KEYS, &keys); + return true; +} + +static bool menu_input_key_bind_poll_find_trigger_pad( + struct menu_bind_state *state, + struct menu_bind_state *new_state, + unsigned p) +{ + unsigned a, b, h; + const struct menu_bind_state_port *n = (const struct menu_bind_state_port*) + &new_state->state[p]; + const struct menu_bind_state_port *o = (const struct menu_bind_state_port*) + &state->state[p]; + + for (b = 0; b < MENU_MAX_BUTTONS; b++) + { + bool iterate = n->buttons[b] && !o->buttons[b]; + + if (!iterate) + continue; + + state->target->joykey = b; + state->target->joyaxis = AXIS_NONE; + return true; + } + + /* Axes are a bit tricky ... */ + for (a = 0; a < MENU_MAX_AXES; a++) + { + int locked_distance = abs(n->axes[a] - + new_state->axis_state[p].locked_axes[a]); + int rested_distance = abs(n->axes[a] - + new_state->axis_state[p].rested_axes[a]); + + if (abs(n->axes[a]) >= 20000 && + locked_distance >= 20000 && + rested_distance >= 20000) + { + /* Take care of case where axis rests on +/- 0x7fff + * (e.g. 360 controller on Linux) */ + state->target->joyaxis = n->axes[a] > 0 + ? AXIS_POS(a) : AXIS_NEG(a); + state->target->joykey = NO_BTN; + + /* Lock the current axis */ + new_state->axis_state[p].locked_axes[a] = + n->axes[a] > 0 ? + 0x7fff : -0x7fff; + return true; + } + + if (locked_distance >= 20000) /* Unlock the axis. */ + new_state->axis_state[p].locked_axes[a] = 0; + } + + for (h = 0; h < MENU_MAX_HATS; h++) + { + uint16_t trigged = n->hats[h] & (~o->hats[h]); + uint16_t sane_trigger = 0; + + if (trigged & HAT_UP_MASK) + sane_trigger = HAT_UP_MASK; + else if (trigged & HAT_DOWN_MASK) + sane_trigger = HAT_DOWN_MASK; + else if (trigged & HAT_LEFT_MASK) + sane_trigger = HAT_LEFT_MASK; + else if (trigged & HAT_RIGHT_MASK) + sane_trigger = HAT_RIGHT_MASK; + + if (sane_trigger) + { + state->target->joykey = HAT_MAP(h, sane_trigger); + state->target->joyaxis = AXIS_NONE; + return true; + } + } + + return false; +} + +static bool menu_input_key_bind_poll_find_trigger( + struct menu_bind_state *state, + struct menu_bind_state *new_state) +{ + unsigned i; + settings_t *settings = config_get_ptr(); + + if (!state || !new_state) + return false; + + for (i = 0; i < settings->input.max_users; i++) + { + if (!menu_input_key_bind_poll_find_trigger_pad( + state, new_state, i)) + continue; + + /* Update the joypad mapping automatically. + * More friendly that way. */ +#if 0 + settings->input.joypad_map[state->user] = i; +#endif + return true; + } + return false; +} + +bool menu_input_key_bind_set_min_max(menu_input_ctx_bind_limits_t *lim) +{ + if (!lim) + return false; + + menu_input_binds.begin = lim->min; + menu_input_binds.last = lim->max; + + return true; +} + +bool menu_input_key_bind_iterate(char *s, size_t len) +{ + struct menu_bind_state binds; + bool timed_out = false; + settings_t *settings = config_get_ptr(); + int64_t current = cpu_features_get_time_usec(); + int timeout = + (menu_input_binds.timeout_end - current) / 1000000; + + if (timeout <= 0) + { + input_driver_keyboard_mapping_set_block(false); + + menu_input_binds.begin++; + menu_input_binds.target++; + menu_input_binds.timeout_end = cpu_features_get_time_usec() + + settings->input.bind_timeout * 1000000; + timed_out = true; + } + + snprintf(s, len, + "[%s]\npress keyboard or joypad\n(timeout %d %s)", + input_config_bind_map_get_desc( + menu_input_binds.begin - MENU_SETTINGS_BIND_BEGIN), + timeout, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SECONDS)); + + /* binds.begin is updated in keyboard_press callback. */ + if (menu_input_binds.begin > menu_input_binds.last) + { + /* Avoid new binds triggering things right away. */ + input_driver_set_flushing_input(); + + /* We won't be getting any key events, so just cancel early. */ + if (timed_out) + input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_CANCEL_WAIT_KEYS, NULL); + + return true; + } + + binds = menu_input_binds; + + input_driver_keyboard_mapping_set_block(true); + menu_input_key_bind_poll_bind_state(&binds, menu_bind_port, timed_out); + + if ((binds.skip && !menu_input_binds.skip) || + menu_input_key_bind_poll_find_trigger(&menu_input_binds, &binds)) + { + input_driver_keyboard_mapping_set_block(false); + + /* Avoid new binds triggering things right away. */ + input_driver_set_flushing_input(); + + binds.begin++; + + if (binds.begin > binds.last) + { + input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_CANCEL_WAIT_KEYS, NULL); + return true; + } + + binds.target++; + binds.timeout_end = cpu_features_get_time_usec() + + settings->input.bind_timeout * 1000000; + } + menu_input_binds = binds; + + return false; +} diff --git a/menu/widgets/menu_input_bind_dialog.h b/menu/widgets/menu_input_bind_dialog.h new file mode 100644 index 0000000000..607ad29f65 --- /dev/null +++ b/menu/widgets/menu_input_bind_dialog.h @@ -0,0 +1,52 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2016 - 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. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef _MENU_INPUT_BIND_DIALOG_H +#define _MENU_INPUT_BIND_DIALOG_H + +#include +#include + +#include + +#include + +#include "../menu_input.h" + +RETRO_BEGIN_DECLS + +typedef struct menu_input_ctx_bind +{ + char *s; + size_t len; +} menu_input_ctx_bind_t; + +typedef struct menu_input_ctx_bind_limits +{ + unsigned min; + unsigned max; +} menu_input_ctx_bind_limits_t; + +bool menu_input_key_bind_set_mode( + enum menu_input_ctl_state state, void *data); + +bool menu_input_key_bind_set_min_max(menu_input_ctx_bind_limits_t *lim); + +bool menu_input_key_bind_iterate(char *s, size_t len); + +RETRO_END_DECLS + +#endif