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.
This commit is contained in:
Camilla Löwy 2018-02-13 17:07:46 +01:00
parent 58cc4b2c5c
commit 2040309d0c
3 changed files with 68 additions and 22 deletions

View File

@ -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 example `h0.8` for left on the first hat. More than one bit may be set in the
mask. 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 The hat bit mask match the [hat states](@ref hat_state) in the joystick
functions. 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, righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,
@endcode @endcode
@note GLFW does not yet support the range and inversion modifiers `+`, `-` and @note GLFW does not yet support the output range and modifiers `+` and `-` that
`~` that were recently added to SDL. were recently added to SDL. The input modifiers `+`, `-` and `~` are supported
and described above.
@section time Time input @section time Time input

View File

@ -167,6 +167,10 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
while (*c) while (*c)
{ {
// TODO: Implement output modifiers
if (*c == '+' || *c == '-')
return GLFW_FALSE;
for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++) for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++)
{ {
length = strlen(fields[i].name); length = strlen(fields[i].name);
@ -177,23 +181,50 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
if (fields[i].element) 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') if (*c == 'a')
fields[i].element->type = _GLFW_JOYSTICK_AXIS; e->type = _GLFW_JOYSTICK_AXIS;
else if (*c == 'b') else if (*c == 'b')
fields[i].element->type = _GLFW_JOYSTICK_BUTTON; e->type = _GLFW_JOYSTICK_BUTTON;
else if (*c == 'h') else if (*c == 'h')
fields[i].element->type = _GLFW_JOYSTICK_HATBIT; e->type = _GLFW_JOYSTICK_HATBIT;
else else
break; 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 hat = strtoul(c + 1, (char**) &c, 10);
const unsigned long bit = 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 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 else
{ {
@ -1188,35 +1219,41 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) 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; 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 hat = e->value >> 4;
const unsigned int bit = js->mapping->buttons[i].value & 0xf; const unsigned int bit = e->value & 0xf;
if (js->hats[hat] & bit) if (js->hats[hat] & bit)
state->buttons[i] = GLFW_PRESS; state->buttons[i] = GLFW_PRESS;
} }
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON) else if (e->type == _GLFW_JOYSTICK_BUTTON)
state->buttons[i] = js->buttons[js->mapping->buttons[i].value]; state->buttons[i] = js->buttons[e->value];
} }
for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++)
{ {
if (js->mapping->axes[i].type == _GLFW_JOYSTICK_AXIS) const _GLFWmapelement* e = js->mapping->axes + i;
state->axes[i] = js->axes[js->mapping->axes[i].value]; if (e->type == _GLFW_JOYSTICK_AXIS)
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_HATBIT)
{ {
const unsigned int hat = js->mapping->axes[i].value >> 4; const float value = js->axes[e->value] * e->axisScale + e->axisOffset;
const unsigned int bit = js->mapping->axes[i].value & 0xf; 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) if (js->hats[hat] & bit)
state->axes[i] = 1.f; state->axes[i] = 1.f;
} }
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON) else if (e->type == _GLFW_JOYSTICK_BUTTON)
state->axes[i] = (float) js->buttons[js->mapping->axes[i].value]; state->axes[i] = (float) js->buttons[e->value];
} }
return GLFW_TRUE; return GLFW_TRUE;

View File

@ -457,6 +457,8 @@ struct _GLFWmapelement
{ {
uint8_t type; uint8_t type;
uint8_t value; uint8_t value;
int8_t axisScale;
int8_t axisOffset;
}; };
// Gamepad mapping structure // Gamepad mapping structure