Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement keyboard rebinding support #1301

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b2e81c9
Fabxx Nov 30, 2022
c8a8c5d
Empty previous keyboard config before remappig, store new config into…
Fabxx Dec 1, 2022
154bf73
update comments
Fabxx Dec 1, 2022
e345c02
remove highlight shader while binding, give names of buttons while bi…
Fabxx Dec 1, 2022
0880d67
don't clean array of bindings at each iteration. Don't use animations…
Fabxx Dec 1, 2022
d81bc5e
add checks for duplicated keybindings
Fabxx Dec 1, 2022
13490b0
Add notification message for duplicated warning
Fabxx Dec 1, 2022
5051911
Tell the already mapped button what is used for
Fabxx Dec 1, 2022
eb04bf5
check that auto binding is not enabled before remapping
Fabxx Dec 1, 2022
323f995
Fabxx Dec 1, 2022
eb3df6b
Remove FIXME
Fabxx Dec 1, 2022
a95cdb0
Adjust indentation, remove unnecessary checks, update auto-bind descr…
Fabxx Dec 2, 2022
b828dfd
Unbind keyboard focus from input UI while remapping
Fabxx Dec 2, 2022
1d3bb78
added more checks for mapping, using toggle style instead of buttons.…
Fabxx Dec 4, 2022
4a0c89f
make the user rebind button if input out of SDL range.
Fabxx Dec 4, 2022
0bea80b
disable rumble anim, add more checks for remapping. Distinguish warni…
Fabxx Dec 5, 2022
b18a1a5
update analog anim comment
Fabxx Dec 5, 2022
241aea6
remove redundant warning message when aborting remapping process
Fabxx Dec 5, 2022
de95d9c
update Toggle description
Fabxx Dec 5, 2022
46a0015
Update UI messages and restore window change check
Fabxx Dec 5, 2022
2668daf
remove window focus depency when remapping.
Fabxx Dec 6, 2022
ab396c7
remove boolean leftover
Fabxx Dec 6, 2022
159e571
adjust indentation
Fabxx Dec 6, 2022
a5be7a3
Fabxx Dec 6, 2022
d4199b3
Fix ESC button making exit from menu
Fabxx Dec 6, 2022
6883c4e
disable analog and trigger anims while mapping, remove useless new line
Fabxx Dec 6, 2022
c54a930
disable stick press animation while remapping
Fabxx Dec 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions ui/xemu-input.c
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,10 @@ void xemu_input_process_sdl_events(const SDL_Event *event)
}
} else if (event->type == SDL_CONTROLLERDEVICEREMAPPED) {
DPRINTF("Controller Remapped: %d\n", event->cdevice.which);
} else if (is_remapping_active) {
xemu_input_keyboard_rebind(event);
} else if (restore_controls) {
xemu_input_restore_defaults();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this done here instead of being called directly when the restore button is pressed?

}
}

Expand Down Expand Up @@ -353,6 +357,138 @@ void xemu_input_update_sdl_kbd_controller_state(ControllerState *state)
if (kbd[sdl_kbd_scancode_map[24]]) state->axis[CONTROLLER_AXIS_RTRIG] = 32767;
}

void xemu_input_restore_defaults(void)
{
sdl_kbd_scancode_map[0] = SDL_SCANCODE_A;
sdl_kbd_scancode_map[1] = SDL_SCANCODE_B;
sdl_kbd_scancode_map[2] = SDL_SCANCODE_X;
sdl_kbd_scancode_map[3] = SDL_SCANCODE_Y;
sdl_kbd_scancode_map[4] = SDL_SCANCODE_LEFT;
sdl_kbd_scancode_map[5] = SDL_SCANCODE_UP;
sdl_kbd_scancode_map[6] = SDL_SCANCODE_RIGHT;
sdl_kbd_scancode_map[7] = SDL_SCANCODE_DOWN;
sdl_kbd_scancode_map[8] = SDL_SCANCODE_BACKSPACE;
sdl_kbd_scancode_map[9] = SDL_SCANCODE_RETURN;
sdl_kbd_scancode_map[10] = SDL_SCANCODE_1;
sdl_kbd_scancode_map[11] = SDL_SCANCODE_2;
sdl_kbd_scancode_map[12] = SDL_SCANCODE_3;
sdl_kbd_scancode_map[13] = SDL_SCANCODE_4;
sdl_kbd_scancode_map[14] = SDL_SCANCODE_5;
sdl_kbd_scancode_map[15] = SDL_SCANCODE_E;
sdl_kbd_scancode_map[16] = SDL_SCANCODE_S;
sdl_kbd_scancode_map[17] = SDL_SCANCODE_F;
sdl_kbd_scancode_map[18] = SDL_SCANCODE_D;
sdl_kbd_scancode_map[19] = SDL_SCANCODE_W;
sdl_kbd_scancode_map[20] = SDL_SCANCODE_I;
sdl_kbd_scancode_map[21] = SDL_SCANCODE_J;
sdl_kbd_scancode_map[22] = SDL_SCANCODE_L;
sdl_kbd_scancode_map[23] = SDL_SCANCODE_K;
sdl_kbd_scancode_map[24] = SDL_SCANCODE_O;

g_config.input.keyboard_controller_scancode_map.a = SDL_SCANCODE_A;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a function that takes sdl_kbd_scancode_map as a parameter and sets the g_config values like you do when binding is complete, then reuse that here to clean this up.

g_config.input.keyboard_controller_scancode_map.b = SDL_SCANCODE_B;
g_config.input.keyboard_controller_scancode_map.x = SDL_SCANCODE_X;
g_config.input.keyboard_controller_scancode_map.y = SDL_SCANCODE_Y;
g_config.input.keyboard_controller_scancode_map.dpad_left = SDL_SCANCODE_LEFT;
g_config.input.keyboard_controller_scancode_map.dpad_up = SDL_SCANCODE_UP;
g_config.input.keyboard_controller_scancode_map.dpad_right = SDL_SCANCODE_RIGHT;
g_config.input.keyboard_controller_scancode_map.dpad_down = SDL_SCANCODE_DOWN;
g_config.input.keyboard_controller_scancode_map.back = SDL_SCANCODE_BACKSPACE;
g_config.input.keyboard_controller_scancode_map.start = SDL_SCANCODE_RETURN;
g_config.input.keyboard_controller_scancode_map.white = SDL_SCANCODE_1;
g_config.input.keyboard_controller_scancode_map.black = SDL_SCANCODE_2;
g_config.input.keyboard_controller_scancode_map.lstick_btn = SDL_SCANCODE_3;
g_config.input.keyboard_controller_scancode_map.rstick_btn = SDL_SCANCODE_4;
g_config.input.keyboard_controller_scancode_map.guide = SDL_SCANCODE_5;
g_config.input.keyboard_controller_scancode_map.lstick_up = SDL_SCANCODE_E;
g_config.input.keyboard_controller_scancode_map.lstick_left = SDL_SCANCODE_S;
g_config.input.keyboard_controller_scancode_map.lstick_right = SDL_SCANCODE_F;
g_config.input.keyboard_controller_scancode_map.lstick_down = SDL_SCANCODE_D;
g_config.input.keyboard_controller_scancode_map.ltrigger = SDL_SCANCODE_W;
g_config.input.keyboard_controller_scancode_map.rstick_up = SDL_SCANCODE_I;
g_config.input.keyboard_controller_scancode_map.rstick_left = SDL_SCANCODE_J;
g_config.input.keyboard_controller_scancode_map.rstick_right = SDL_SCANCODE_L;
g_config.input.keyboard_controller_scancode_map.rstick_down = SDL_SCANCODE_K;
g_config.input.keyboard_controller_scancode_map.rtrigger = SDL_SCANCODE_O;

char *buf = g_strdup_printf("INFO: Keys restored to default.");
xemu_queue_notification(buf);
free(buf);

restore_controls = false;
}

void xemu_input_keyboard_rebind(const SDL_Event *ev)
{
//Check if the user aborts the remapping process.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This comment adds no value

if (abort_rebinding) {
char *buf = g_strdup_printf("INFO: Remapping process aborted");
xemu_queue_notification(buf);
free(buf);
is_remapping_active = false;
abort_rebinding = false;
}

if (ev->type == SDL_KEYDOWN) {
sdl_kbd_scancode_map[currently_remapping] = ev->key.keysym.scancode;
//check for duplicated keybindings, if found, rebind that button.
for (size_t i = 0; i < currently_remapping; i++) {
if (sdl_kbd_scancode_map[currently_remapping] == sdl_kbd_scancode_map[i]) {
already_mapped = i;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable isn't used anywhere else, seems like it can be removed.

currently_remapping--;
char *buf = g_strdup_printf("WARNING: Keybind already in use for: %s. Try another key.", bindings[already_mapped]);
xemu_queue_notification(buf);
free(buf);
break;
}
}

if ((sdl_kbd_scancode_map[currently_remapping] < SDL_SCANCODE_UNKNOWN) ||
(sdl_kbd_scancode_map[currently_remapping] >= SDL_NUM_SCANCODES)) {
char *buf = g_strdup_printf("WARNING: Keyboard scancode out of range, try another key.");
xemu_queue_notification(buf);
free(buf);
currently_remapping--;
}

currently_remapping++;

//If the user has mapped all the buttons, store the new bindings.
if (currently_remapping == 25) {
g_config.input.keyboard_controller_scancode_map.a = sdl_kbd_scancode_map[0];
g_config.input.keyboard_controller_scancode_map.b = sdl_kbd_scancode_map[1];
g_config.input.keyboard_controller_scancode_map.x = sdl_kbd_scancode_map[2];
g_config.input.keyboard_controller_scancode_map.y = sdl_kbd_scancode_map[3];
g_config.input.keyboard_controller_scancode_map.dpad_left = sdl_kbd_scancode_map[4];
g_config.input.keyboard_controller_scancode_map.dpad_up = sdl_kbd_scancode_map[5];
g_config.input.keyboard_controller_scancode_map.dpad_right = sdl_kbd_scancode_map[6];
g_config.input.keyboard_controller_scancode_map.dpad_down = sdl_kbd_scancode_map[7];
g_config.input.keyboard_controller_scancode_map.back = sdl_kbd_scancode_map[8];
g_config.input.keyboard_controller_scancode_map.start = sdl_kbd_scancode_map[9];
g_config.input.keyboard_controller_scancode_map.white = sdl_kbd_scancode_map[10];
g_config.input.keyboard_controller_scancode_map.black = sdl_kbd_scancode_map[11];
g_config.input.keyboard_controller_scancode_map.lstick_btn = sdl_kbd_scancode_map[12];
g_config.input.keyboard_controller_scancode_map.rstick_btn = sdl_kbd_scancode_map[13];
g_config.input.keyboard_controller_scancode_map.guide = sdl_kbd_scancode_map[14];
g_config.input.keyboard_controller_scancode_map.lstick_up = sdl_kbd_scancode_map[15];
g_config.input.keyboard_controller_scancode_map.lstick_left = sdl_kbd_scancode_map[16];
g_config.input.keyboard_controller_scancode_map.lstick_right = sdl_kbd_scancode_map[17];
g_config.input.keyboard_controller_scancode_map.lstick_down = sdl_kbd_scancode_map[18];
g_config.input.keyboard_controller_scancode_map.ltrigger = sdl_kbd_scancode_map[19];
g_config.input.keyboard_controller_scancode_map.rstick_up = sdl_kbd_scancode_map[20];
g_config.input.keyboard_controller_scancode_map.rstick_left = sdl_kbd_scancode_map[21];
g_config.input.keyboard_controller_scancode_map.rstick_right = sdl_kbd_scancode_map[22];
g_config.input.keyboard_controller_scancode_map.rstick_down = sdl_kbd_scancode_map[23];
g_config.input.keyboard_controller_scancode_map.rtrigger = sdl_kbd_scancode_map[24];

char *buf = g_strdup_printf("INFO: Successfully remapped keyboard.");
xemu_queue_notification(buf);
free(buf);
is_remapping_active = false;
}
}
}

void xemu_input_update_sdl_controller_state(ControllerState *state)
{
state->buttons = 0;
Expand Down
9 changes: 9 additions & 0 deletions ui/xemu-input.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ typedef QTAILQ_HEAD(, ControllerState) ControllerStateList;
extern ControllerStateList available_controllers;
extern ControllerState *bound_controllers[4];

extern bool is_remapping_active;
extern bool abort_rebinding;
extern bool restore_controls;
extern int currently_remapping;
extern int already_mapped;
extern const char *bindings[25];

#ifdef __cplusplus
extern "C" {
#endif
Expand All @@ -107,6 +114,8 @@ void xemu_input_update_controller(ControllerState *state);
void xemu_input_update_sdl_kbd_controller_state(ControllerState *state);
void xemu_input_update_sdl_controller_state(ControllerState *state);
void xemu_input_update_rumble(ControllerState *state);
void xemu_input_keyboard_rebind(const SDL_Event *ev);
void xemu_input_restore_defaults(void);
ControllerState *xemu_input_get_bound(int index);
void xemu_input_bind(int index, ControllerState *state, int save);
int xemu_input_get_controller_default_bind_port(ControllerState *state, int start);
Expand Down
93 changes: 62 additions & 31 deletions ui/xui/gl-helpers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -501,25 +501,28 @@ void RenderController(float frame_x, float frame_y, uint32_t primary_color,

uint32_t jewel_color = secondary_color;

// Check to see if the guide button is pressed
const uint32_t animate_guide_button_duration = 2000;
if (state->buttons & CONTROLLER_BUTTON_GUIDE) {
state->animate_guide_button_end = now + animate_guide_button_duration;
}
// Check to see if the guide button is pressed only when not remapping.
if (!is_remapping_active) {
const uint32_t animate_guide_button_duration = 2000;
if (state->buttons & CONTROLLER_BUTTON_GUIDE) {
state->animate_guide_button_end = now + animate_guide_button_duration;
}

if (now < state->animate_guide_button_end) {
t = 1.0f - (float)(state->animate_guide_button_end-now)/(float)animate_guide_button_duration;
float sin_wav = (1-sin(M_PI * t / 2.0f));
if (now < state->animate_guide_button_end) {
t = 1.0f - (float)(state->animate_guide_button_end-now)/(float)animate_guide_button_duration;
float sin_wav = (1-sin(M_PI * t / 2.0f));

// Animate guide button by highlighting logo jewel and fading out over time
alpha = sin_wav * 255.0f;
jewel_color = primary_color + alpha;
// Animate guide button by highlighting logo jewel and fading out over time
alpha = sin_wav * 255.0f;
jewel_color = primary_color + alpha;

// Add a little extra flare: wiggle the frame around while we rumble
frame_x += ((float)(rand() % 5)-2.5) * (1-t);
frame_y += ((float)(rand() % 5)-2.5) * (1-t);
rumble_l = rumble_r = sin_wav;
// Add a little extra flare: wiggle the frame around while we rumble
frame_x += ((float)(rand() % 5)-2.5) * (1-t);
frame_y += ((float)(rand() % 5)-2.5) * (1-t);
rumble_l = rumble_r = sin_wav;
}
Fabxx marked this conversation as resolved.
Show resolved Hide resolved
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Extra newline


// Render controller texture
RenderDecal(g_decal_shader, frame_x + 0, frame_y + 0,
Expand All @@ -534,51 +537,70 @@ void RenderController(float frame_x, float frame_y, uint32_t primary_color,

// The controller has alpha cutouts where the buttons are. Draw a surface
// behind the buttons if they are activated
for (int i = 0; i < 12; i++) {
if (state->buttons & (1 << i)) {
RenderDecal(g_decal_shader, frame_x + buttons[i].x,
frame_y + buttons[i].y, buttons[i].w, buttons[i].h, 0,
0, 1, 1, 0, 0, primary_color + 0xff);
// Do not highlight the buttons while remapping.
if (!is_remapping_active) {
for (int i = 0; i < 12; i++) {
if (state->buttons & (1 << i)) {
RenderDecal(g_decal_shader, frame_x + buttons[i].x,
frame_y + buttons[i].y, buttons[i].w, buttons[i].h, 0,
0, 1, 1, 0, 0, primary_color + 0xff);
}
}
}

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Blend with controller

// Render left thumbstick
// Render left thumbstick, do not make move/highlight while remapping.
float w = tex_items[obj_lstick].w;
float h = tex_items[obj_lstick].h;
float c_x = frame_x+lstick_ctr.x;
float c_y = frame_y+lstick_ctr.y;
float lstick_x = (float)state->axis[CONTROLLER_AXIS_LSTICK_X]/32768.0;
float lstick_y = (float)state->axis[CONTROLLER_AXIS_LSTICK_Y]/32768.0;
int lstick_press_anim = CONTROLLER_BUTTON_LSTICK;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this take the remapping into account?


if (is_remapping_active) {
lstick_x = 0;
lstick_y = 0;
lstick_press_anim = SDL_SCANCODE_UNKNOWN;
}

RenderDecal(g_decal_shader, (int)(c_x - w / 2.0f + 10.0f * lstick_x),
(int)(c_y - h / 2.0f + 10.0f * lstick_y), w, h,
tex_items[obj_lstick].x, tex_items[obj_lstick].y, w, h,
(state->buttons & CONTROLLER_BUTTON_LSTICK) ? secondary_color :
(state->buttons & lstick_press_anim) ? secondary_color :
primary_color,
(state->buttons & CONTROLLER_BUTTON_LSTICK) ? primary_color :
(state->buttons & lstick_press_anim) ? primary_color :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using SDL_SCANCODE_UNKNOWN it'd be clearer to condition these on !is_remapping_active && (state->buttons & ...)

secondary_color,
0);

// Render right thumbstick
// Render right thumbstick, do not make move/highlight while remapping
w = tex_items[obj_rstick].w;
h = tex_items[obj_rstick].h;
c_x = frame_x+rstick_ctr.x;
c_y = frame_y+rstick_ctr.y;
float rstick_x = (float)state->axis[CONTROLLER_AXIS_RSTICK_X]/32768.0;
float rstick_y = (float)state->axis[CONTROLLER_AXIS_RSTICK_Y]/32768.0;
int rstick_press_anim = CONTROLLER_BUTTON_RSTICK;

if (is_remapping_active) {
rstick_x = 0;
rstick_y = 0;
rstick_press_anim = SDL_SCANCODE_UNKNOWN;
}

RenderDecal(g_decal_shader, (int)(c_x - w / 2.0f + 10.0f * rstick_x),
(int)(c_y - h / 2.0f + 10.0f * rstick_y), w, h,
tex_items[obj_rstick].x, tex_items[obj_rstick].y, w, h,
(state->buttons & CONTROLLER_BUTTON_RSTICK) ? secondary_color :
(state->buttons & rstick_press_anim) ? secondary_color :
primary_color,
(state->buttons & CONTROLLER_BUTTON_RSTICK) ? primary_color :
(state->buttons & rstick_press_anim) ? primary_color :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

secondary_color,
0);

glBlendFunc(GL_ONE, GL_ZERO); // Don't blend, just overwrite values in buffer

// Render trigger bars
// Render trigger bars, do not highlight when remapping.
float ltrig = state->axis[CONTROLLER_AXIS_LTRIG] / 32767.0;
float rtrig = state->axis[CONTROLLER_AXIS_RTRIG] / 32767.0;
const uint32_t animate_trigger_duration = 1000;
Expand All @@ -596,6 +618,12 @@ void RenderController(float frame_x, float frame_y, uint32_t primary_color,
alpha += fmin(sin_wav * 0x40, 0x80);
}

if (is_remapping_active) {
ltrig = 0;
rtrig = 0;
alpha = 0x80;
}

RenderMeter(g_decal_shader, original_frame_x + 10,
original_frame_y + tex_items[obj_controller].h + 20, 150, 5,
ltrig, primary_color + alpha, primary_color + 0xff);
Expand All @@ -604,9 +632,12 @@ void RenderController(float frame_x, float frame_y, uint32_t primary_color,
original_frame_y + tex_items[obj_controller].h + 20, 150, 5,
rtrig, primary_color + alpha, primary_color + 0xff);

// Apply rumble updates
state->rumble_l = (int)(rumble_l * (float)0xffff);
state->rumble_r = (int)(rumble_r * (float)0xffff);

// Apply rumble updates when not remapping
if (!is_remapping_active) {
state->rumble_l = (int)(rumble_l * (float)0xffff);
state->rumble_r = (int)(rumble_r * (float)0xffff);
}

glBindVertexArray(0);
glUseProgram(0);
Expand Down
Loading