mirror of
https://github.com/glfw/glfw.git
synced 2026-09-17 22:32:50 +00:00
Add support for SDL_GameControllerDB
This adds support for importing and applying mappings from the SDL_GameControllerDB database. Related to #900.
This commit is contained in:
+307
@@ -29,12 +29,144 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
// Internal key state used for sticky keys
|
||||
#define _GLFW_STICK 3
|
||||
|
||||
// Internal constants for gamepad mapping source types
|
||||
#define _GLFW_JOYSTICK_AXIS 1
|
||||
#define _GLFW_JOYSTICK_BUTTON 2
|
||||
#define _GLFW_JOYSTICK_HATBIT 3
|
||||
|
||||
// Finds a mapping based on joystick GUID
|
||||
//
|
||||
static _GLFWmapping* findMapping(const char* guid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < _glfw.mappingCount; i++)
|
||||
{
|
||||
if (strcmp(_glfw.mappings[i].guid, guid) == 0)
|
||||
return _glfw.mappings + i;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Parses an SDL_GameControllerDB line and adds it to the mapping list
|
||||
//
|
||||
static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
{
|
||||
const char* c = string;
|
||||
size_t i, length;
|
||||
struct
|
||||
{
|
||||
const char* name;
|
||||
_GLFWmapelement* element;
|
||||
} fields[] =
|
||||
{
|
||||
{ "platform", NULL },
|
||||
{ "a", mapping->buttons + GLFW_GAMEPAD_BUTTON_A },
|
||||
{ "b", mapping->buttons + GLFW_GAMEPAD_BUTTON_B },
|
||||
{ "x", mapping->buttons + GLFW_GAMEPAD_BUTTON_X },
|
||||
{ "y", mapping->buttons + GLFW_GAMEPAD_BUTTON_Y },
|
||||
{ "back", mapping->buttons + GLFW_GAMEPAD_BUTTON_BACK },
|
||||
{ "start", mapping->buttons + GLFW_GAMEPAD_BUTTON_START },
|
||||
{ "guide", mapping->buttons + GLFW_GAMEPAD_BUTTON_GUIDE },
|
||||
{ "leftshoulder", mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_BUMPER },
|
||||
{ "rightshoulder", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER },
|
||||
{ "leftstick", mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_THUMB },
|
||||
{ "rightstick", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_THUMB },
|
||||
{ "dpup", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_UP },
|
||||
{ "dpright", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_RIGHT },
|
||||
{ "dpdown", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_DOWN },
|
||||
{ "dpleft", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_LEFT },
|
||||
{ "lefttrigger", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_TRIGGER },
|
||||
{ "righttrigger", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER },
|
||||
{ "leftx", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_X },
|
||||
{ "lefty", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_Y },
|
||||
{ "rightx", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_X },
|
||||
{ "righty", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_Y }
|
||||
};
|
||||
|
||||
length = strcspn(c, ",");
|
||||
if (length != 32 || c[length] != ',')
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_VALUE, NULL);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
memcpy(mapping->guid, c, length);
|
||||
c += length + 1;
|
||||
|
||||
length = strcspn(c, ",");
|
||||
if (length >= sizeof(mapping->name) || c[length] != ',')
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_VALUE, NULL);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
memcpy(mapping->name, c, length);
|
||||
c += length + 1;
|
||||
|
||||
while (*c)
|
||||
{
|
||||
for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++)
|
||||
{
|
||||
length = strlen(fields[i].name);
|
||||
if (strncmp(c, fields[i].name, length) != 0 || c[length] != ':')
|
||||
continue;
|
||||
|
||||
c += length + 1;
|
||||
|
||||
if (fields[i].element)
|
||||
{
|
||||
if (*c == 'a')
|
||||
fields[i].element->type = _GLFW_JOYSTICK_AXIS;
|
||||
else if (*c == 'b')
|
||||
fields[i].element->type = _GLFW_JOYSTICK_BUTTON;
|
||||
else if (*c == 'h')
|
||||
fields[i].element->type = _GLFW_JOYSTICK_HATBIT;
|
||||
else
|
||||
break;
|
||||
|
||||
if (fields[i].element->type == _GLFW_JOYSTICK_HATBIT)
|
||||
{
|
||||
const unsigned int hat = strtoul(c + 1, (char**) &c, 10);
|
||||
const unsigned int bit = strtoul(c + 1, (char**) &c, 10);
|
||||
fields[i].element->value = (hat << 4) | bit;
|
||||
}
|
||||
else
|
||||
fields[i].element->value = (uint8_t) strtoul(c + 1, (char**) &c, 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
length = strlen(_GLFW_PLATFORM_MAPPING_NAME);
|
||||
if (strncmp(c, _GLFW_PLATFORM_MAPPING_NAME, length) != 0)
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
c += strcspn(c, ",");
|
||||
c += strspn(c, ",");
|
||||
}
|
||||
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
if (mapping->guid[i] >= 'A' && mapping->guid[i] <= 'F')
|
||||
mapping->guid[i] += 'a' - 'A';
|
||||
}
|
||||
|
||||
_glfwPlatformUpdateGamepadGUID(mapping->guid);
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW event API //////
|
||||
@@ -166,6 +298,7 @@ GLFWbool _glfwIsPrintable(int key)
|
||||
}
|
||||
|
||||
_GLFWjoystick* _glfwAllocJoystick(const char* name,
|
||||
const char* guid,
|
||||
int axisCount,
|
||||
int buttonCount,
|
||||
int hatCount)
|
||||
@@ -191,6 +324,9 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name,
|
||||
js->axisCount = axisCount;
|
||||
js->buttonCount = buttonCount;
|
||||
js->hatCount = hatCount;
|
||||
js->mapping = findMapping(guid);
|
||||
|
||||
strcpy(js->guid, guid);
|
||||
|
||||
return js;
|
||||
}
|
||||
@@ -748,6 +884,177 @@ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun)
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI int glfwUpdateGamepadMappings(const char* string)
|
||||
{
|
||||
int jid;
|
||||
const char* c = string;
|
||||
|
||||
assert(string != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||
|
||||
while (*c)
|
||||
{
|
||||
if (isxdigit(*c))
|
||||
{
|
||||
char line[1024];
|
||||
|
||||
const size_t length = strcspn(c, "\r\n");
|
||||
if (length < sizeof(line))
|
||||
{
|
||||
_GLFWmapping mapping = {{0}};
|
||||
|
||||
memcpy(line, c, length);
|
||||
line[length] = '\0';
|
||||
|
||||
if (parseMapping(&mapping, line))
|
||||
{
|
||||
_GLFWmapping* previous = findMapping(mapping.guid);
|
||||
if (previous)
|
||||
*previous = mapping;
|
||||
else
|
||||
{
|
||||
_glfw.mappingCount++;
|
||||
_glfw.mappings =
|
||||
realloc(_glfw.mappings,
|
||||
sizeof(_GLFWmapping) * _glfw.mappingCount);
|
||||
_glfw.mappings[_glfw.mappingCount - 1] = mapping;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c += length;
|
||||
}
|
||||
else
|
||||
{
|
||||
c += strcspn(c, "\r\n");
|
||||
c += strspn(c, "\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
|
||||
{
|
||||
_GLFWjoystick* js = _glfw.joysticks + jid;
|
||||
if (js->present)
|
||||
js->mapping = findMapping(js->guid);
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
GLFWAPI int glfwJoystickIsGamepad(int jid)
|
||||
{
|
||||
assert(jid >= GLFW_JOYSTICK_1);
|
||||
assert(jid <= GLFW_JOYSTICK_LAST);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||
|
||||
if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (!_glfw.joysticks[jid].present)
|
||||
return GLFW_FALSE;
|
||||
|
||||
if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_PRESENCE))
|
||||
return GLFW_FALSE;
|
||||
|
||||
return _glfw.joysticks[jid].mapping != NULL;
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetGamepadName(int jid)
|
||||
{
|
||||
assert(jid >= GLFW_JOYSTICK_1);
|
||||
assert(jid <= GLFW_JOYSTICK_LAST);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!_glfw.joysticks[jid].present)
|
||||
return NULL;
|
||||
|
||||
if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_PRESENCE))
|
||||
return NULL;
|
||||
|
||||
if (!_glfw.joysticks[jid].mapping)
|
||||
return NULL;
|
||||
|
||||
return _glfw.joysticks[jid].mapping->name;
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
|
||||
{
|
||||
int i;
|
||||
_GLFWjoystick* js;
|
||||
|
||||
assert(jid >= GLFW_JOYSTICK_1);
|
||||
assert(jid <= GLFW_JOYSTICK_LAST);
|
||||
assert(state != NULL);
|
||||
|
||||
memset(state, 0, sizeof(GLFWgamepadstate));
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||
|
||||
if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
|
||||
if (!js->present)
|
||||
return GLFW_FALSE;
|
||||
|
||||
if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_ALL))
|
||||
return GLFW_FALSE;
|
||||
|
||||
if (!js->mapping)
|
||||
return GLFW_FALSE;
|
||||
|
||||
for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++)
|
||||
{
|
||||
if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_AXIS)
|
||||
{
|
||||
if (fabs(js->axes[js->mapping->buttons[i].value]) > 0.5)
|
||||
state->buttons[i] = GLFW_PRESS;
|
||||
}
|
||||
else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_HATBIT)
|
||||
{
|
||||
const unsigned int hat = js->mapping->buttons[i].value >> 4;
|
||||
const unsigned int bit = js->mapping->buttons[i].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];
|
||||
}
|
||||
|
||||
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 unsigned int hat = js->mapping->buttons[i].value >> 4;
|
||||
const unsigned int bit = js->mapping->buttons[i].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];
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
|
||||
Reference in New Issue
Block a user