From 2040309d0cb715e9aa8abeac25a3f36761dbbb76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 13 Feb 2018 17:07:46 +0100 Subject: [PATCH] Add support for gamepad mapping input modifiers This adds support for the + and - and ~ input modifiers for joystick axes. It also changes how joystick axes are translated to buttons to more closely match SDL 2.0.7. Output modifiers are still not supported but have not yet been seen in the wild. --- docs/input.dox | 11 ++++++-- src/input.c | 77 +++++++++++++++++++++++++++++++++++++------------- src/internal.h | 2 ++ 3 files changed, 68 insertions(+), 22 deletions(-) diff --git a/docs/input.dox b/docs/input.dox index 1d8439d5..940a6827 100644 --- a/docs/input.dox +++ b/docs/input.dox @@ -804,6 +804,12 @@ a hat bitmask or empty. Joystick buttons are specified as `bN`, for example example `h0.8` for left on the first hat. More than one bit may be set in the mask. +Before an axis there may be a `+` or `-` range modifier, for example `+a3` for +the positive half of the fourth axis. This restricts input to only the positive +or negative halves of the joystick axis. After an axis or half-axis there may +be the `~` inversion modifier, for example `a2~` or `-a7~`. This negates the +values of the gamepad axis. + The hat bit mask match the [hat states](@ref hat_state) in the joystick functions. @@ -822,8 +828,9 @@ rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4, righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, @endcode -@note GLFW does not yet support the range and inversion modifiers `+`, `-` and -`~` that were recently added to SDL. +@note GLFW does not yet support the output range and modifiers `+` and `-` that +were recently added to SDL. The input modifiers `+`, `-` and `~` are supported +and described above. @section time Time input diff --git a/src/input.c b/src/input.c index f6fb38fd..d0cbeed8 100644 --- a/src/input.c +++ b/src/input.c @@ -167,6 +167,10 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string) while (*c) { + // TODO: Implement output modifiers + if (*c == '+' || *c == '-') + return GLFW_FALSE; + for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++) { length = strlen(fields[i].name); @@ -177,23 +181,50 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string) if (fields[i].element) { + _GLFWmapelement* e = fields[i].element; + int8_t minimum = -1; + int8_t maximum = 1; + + if (*c == '+') + { + minimum = 0; + c += 1; + } + else if (*c == '-') + { + maximum = 0; + c += 1; + } + if (*c == 'a') - fields[i].element->type = _GLFW_JOYSTICK_AXIS; + e->type = _GLFW_JOYSTICK_AXIS; else if (*c == 'b') - fields[i].element->type = _GLFW_JOYSTICK_BUTTON; + e->type = _GLFW_JOYSTICK_BUTTON; else if (*c == 'h') - fields[i].element->type = _GLFW_JOYSTICK_HATBIT; + e->type = _GLFW_JOYSTICK_HATBIT; else break; - if (fields[i].element->type == _GLFW_JOYSTICK_HATBIT) + if (e->type == _GLFW_JOYSTICK_HATBIT) { const unsigned long hat = strtoul(c + 1, (char**) &c, 10); const unsigned long bit = strtoul(c + 1, (char**) &c, 10); - fields[i].element->value = (uint8_t) ((hat << 4) | bit); + e->value = (uint8_t) ((hat << 4) | bit); } else - fields[i].element->value = (uint8_t) strtoul(c + 1, (char**) &c, 10); + e->value = (uint8_t) strtoul(c + 1, (char**) &c, 10); + + if (e->type == _GLFW_JOYSTICK_AXIS) + { + e->axisScale = 2 / (maximum - minimum); + e->axisOffset = -(maximum + minimum); + + if (*c == '~') + { + e->axisScale = -e->axisScale; + e->axisOffset = -e->axisOffset; + } + } } else { @@ -1188,35 +1219,41 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state) for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) { - if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_AXIS) + const _GLFWmapelement* e = js->mapping->buttons + i; + if (e->type == _GLFW_JOYSTICK_AXIS) { - if (fabsf(js->axes[js->mapping->buttons[i].value]) > 0.5f) + const float value = js->axes[e->value] * e->axisScale + e->axisOffset; + if (value > 0.f) state->buttons[i] = GLFW_PRESS; } - else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_HATBIT) + else if (e->type == _GLFW_JOYSTICK_HATBIT) { - const unsigned int hat = js->mapping->buttons[i].value >> 4; - const unsigned int bit = js->mapping->buttons[i].value & 0xf; + const unsigned int hat = e->value >> 4; + const unsigned int bit = e->value & 0xf; if (js->hats[hat] & bit) state->buttons[i] = GLFW_PRESS; } - else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON) - state->buttons[i] = js->buttons[js->mapping->buttons[i].value]; + else if (e->type == _GLFW_JOYSTICK_BUTTON) + state->buttons[i] = js->buttons[e->value]; } for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) { - if (js->mapping->axes[i].type == _GLFW_JOYSTICK_AXIS) - state->axes[i] = js->axes[js->mapping->axes[i].value]; - else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_HATBIT) + const _GLFWmapelement* e = js->mapping->axes + i; + if (e->type == _GLFW_JOYSTICK_AXIS) { - const unsigned int hat = js->mapping->axes[i].value >> 4; - const unsigned int bit = js->mapping->axes[i].value & 0xf; + const float value = js->axes[e->value] * e->axisScale + e->axisOffset; + state->axes[i] = fminf(fmaxf(value, -1.f), 1.f); + } + else if (e->type == _GLFW_JOYSTICK_HATBIT) + { + const unsigned int hat = e->value >> 4; + const unsigned int bit = e->value & 0xf; if (js->hats[hat] & bit) state->axes[i] = 1.f; } - else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON) - state->axes[i] = (float) js->buttons[js->mapping->axes[i].value]; + else if (e->type == _GLFW_JOYSTICK_BUTTON) + state->axes[i] = (float) js->buttons[e->value]; } return GLFW_TRUE; diff --git a/src/internal.h b/src/internal.h index 61f0a9c9..db952ed4 100644 --- a/src/internal.h +++ b/src/internal.h @@ -457,6 +457,8 @@ struct _GLFWmapelement { uint8_t type; uint8_t value; + int8_t axisScale; + int8_t axisOffset; }; // Gamepad mapping structure