Related to #1005.
This commit is contained in:
Camilla Löwy 2017-06-15 17:13:23 +02:00
parent 206f9ca4bc
commit d2952e4e92
4 changed files with 81 additions and 69 deletions

View File

@ -184,6 +184,7 @@ information on what to include when reporting a bug.
- [X11] Bugfix: IM-duplicated key events would leak at low polling rates (#747) - [X11] Bugfix: IM-duplicated key events would leak at low polling rates (#747)
- [X11] Bugfix: Gamma ramp setting via RandR did not validate ramp size - [X11] Bugfix: Gamma ramp setting via RandR did not validate ramp size
- [X11] Bugfix: Key name string encoding depended on current locale (#981,#983) - [X11] Bugfix: Key name string encoding depended on current locale (#981,#983)
- [Linux] Moved to evdev for joystick input (#906,#1005)
- [Linux] Bugfix: Event processing did not detect joystick disconnection (#932) - [Linux] Bugfix: Event processing did not detect joystick disconnection (#932)
- [Linux] Bugfix: The joystick device path could be truncated (#1025) - [Linux] Bugfix: The joystick device path could be truncated (#1025)
- [Cocoa] Added support for Vulkan window surface creation via - [Cocoa] Added support for Vulkan window surface creation via
@ -314,6 +315,7 @@ skills.
- Peoro - Peoro
- Braden Pellett - Braden Pellett
- Arturo J. Pérez - Arturo J. Pérez
- Anthony Pesch
- Orson Peters - Orson Peters
- Emmanuel Gil Peyrot - Emmanuel Gil Peyrot
- Cyril Pichard - Cyril Pichard

View File

@ -4234,8 +4234,6 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count);
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
* *
* @bug @linux Joystick hats are currently unimplemented.
*
* @pointer_lifetime The returned array is allocated and freed by GLFW. You * @pointer_lifetime The returned array is allocated and freed by GLFW. You
* should not free it yourself. It is valid until the specified joystick is * should not free it yourself. It is valid until the specified joystick is
* disconnected, this function is called again for that joystick or the library * disconnected, this function is called again for that joystick or the library

View File

@ -38,36 +38,37 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#define TEST_BIT(bit, arr) (arr[(bit) / 8] & (1 << ((bit) % 8))) // Apply an EV_KEY event to the specified joystick
//
static void handleKeyEvent(_GLFWjoystick* js, int code, int value) static void handleKeyEvent(_GLFWjoystick* js, int code, int value)
{ {
int jid = js - _glfw.joysticks; _glfwInputJoystickButton(_GLFW_JOYSTICK_ID(js),
int button = js->linjs.keyMap[code]; js->linjs.keyMap[code - BTN_MISC],
value ? GLFW_PRESS : GLFW_RELEASE);
_glfwInputJoystickButton(jid, button, value ? 1 : 0);
} }
// Apply an EV_ABS event to the specified joystick
//
static void handleAbsEvent(_GLFWjoystick* js, int code, int value) static void handleAbsEvent(_GLFWjoystick* js, int code, int value)
{ {
int jid = js - _glfw.joysticks; const int jid = _GLFW_JOYSTICK_ID(js);
int index = js->linjs.absMap[code]; const int index = js->linjs.absMap[code];
if (code >= ABS_HAT0X && code <= ABS_HAT3Y) if (code >= ABS_HAT0X && code <= ABS_HAT3Y)
{ {
static const char stateMap[3][3] = static const char stateMap[3][3] =
{ {
{GLFW_HAT_CENTERED, GLFW_HAT_UP, GLFW_HAT_DOWN}, { GLFW_HAT_CENTERED, GLFW_HAT_UP, GLFW_HAT_DOWN },
{GLFW_HAT_LEFT, GLFW_HAT_LEFT_UP, GLFW_HAT_LEFT_DOWN}, { GLFW_HAT_LEFT, GLFW_HAT_LEFT_UP, GLFW_HAT_LEFT_DOWN },
{GLFW_HAT_RIGHT, GLFW_HAT_RIGHT_UP, GLFW_HAT_RIGHT_DOWN}, { GLFW_HAT_RIGHT, GLFW_HAT_RIGHT_UP, GLFW_HAT_RIGHT_DOWN },
}; };
int hat = (code - ABS_HAT0X) / 2; const int hat = (code - ABS_HAT0X) / 2;
int axis = (code - ABS_HAT0X) % 2; const int axis = (code - ABS_HAT0X) % 2;
int *state = js->linjs.hats[hat]; int* state = js->linjs.hats[hat];
// Looking at several input drivers, it seems all hat events use // NOTE: Looking at several input drivers, it seems all hat events use
// -1 for left / up, 0 for centered and 1 for right / down // -1 for left / up, 0 for centered and 1 for right / down
if (value == 0) if (value == 0)
state[axis] = 0; state[axis] = 0;
else if (value < 0) else if (value < 0)
@ -79,15 +80,15 @@ static void handleAbsEvent(_GLFWjoystick* js, int code, int value)
} }
else else
{ {
struct input_absinfo *info = &js->linjs.absInfo[code]; const struct input_absinfo* info = &js->linjs.absInfo[code];
int range = info->maximum - info->minimum;
float normalized = value; float normalized = value;
if (range != 0) const int range = info->maximum - info->minimum;
if (range)
{ {
// Normalize from 0.0 -> 1.0 // Normalize to 0.0 -> 1.0
normalized = (normalized - info->minimum) / range; normalized = (normalized - info->minimum) / range;
// Normalize from -1.0 -> 1.0 // Normalize to -1.0 -> 1.0
normalized = normalized * 2.0f - 1.0f; normalized = normalized * 2.0f - 1.0f;
} }
@ -95,36 +96,38 @@ static void handleAbsEvent(_GLFWjoystick* js, int code, int value)
} }
} }
static void pollJoystick(_GLFWjoystick* js) // Poll state of absolute axes
//
static void pollAbsState(_GLFWjoystick* js)
{ {
int i; int code;
for (i = 0; i < ABS_CNT; i++) for (code = 0; code < ABS_CNT; code++)
{ {
if (js->linjs.absMap[i] < 0) if (js->linjs.absMap[code] < 0)
continue; continue;
struct input_absinfo *info = &js->linjs.absInfo[i]; struct input_absinfo* info = &js->linjs.absInfo[code];
if (ioctl(js->linjs.fd, EVIOCGABS(i), info) < 0) if (ioctl(js->linjs.fd, EVIOCGABS(code), info) < 0)
continue; continue;
handleAbsEvent(js, i, info->value); handleAbsEvent(js, code, info->value);
} }
} }
#define isBitSet(bit, arr) (arr[(bit) / 8] & (1 << ((bit) % 8)))
// Attempt to open the specified joystick device // Attempt to open the specified joystick device
// //
static GLFWbool openJoystickDevice(const char* path) static GLFWbool openJoystickDevice(const char* path)
{ {
int jid, fd, i; int jid, code;
char name[256] = ""; char name[256] = "";
char evBits[(EV_CNT + 7) / 8] = {0}; char evBits[(EV_CNT + 7) / 8] = {0};
char keyBits[(KEY_CNT + 7) / 8] = {0}; char keyBits[(KEY_CNT + 7) / 8] = {0};
char absBits[(ABS_CNT + 7) / 8] = {0}; char absBits[(ABS_CNT + 7) / 8] = {0};
int axisCount = 0; int axisCount = 0, buttonCount = 0, hatCount = 0;
int buttonCount = 0;
int hatCount = 0;
_GLFWjoystickLinux linjs = {0}; _GLFWjoystickLinux linjs = {0};
_GLFWjoystick* js = NULL; _GLFWjoystick* js = NULL;
@ -136,71 +139,81 @@ static GLFWbool openJoystickDevice(const char* path)
return GLFW_FALSE; return GLFW_FALSE;
} }
fd = open(path, O_RDONLY | O_NONBLOCK); linjs.fd = open(path, O_RDONLY | O_NONBLOCK);
if (fd == -1) if (linjs.fd == -1)
return GLFW_FALSE; return GLFW_FALSE;
if (ioctl(linjs.fd, EVIOCGBIT(0, sizeof(evBits)), evBits) < 0 ||
ioctl(linjs.fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 ||
ioctl(linjs.fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Linux: Failed to query input device elements: %s",
strerror(errno));
close(linjs.fd);
return GLFW_FALSE;
}
// Ensure this device supports the events expected of a joystick // Ensure this device supports the events expected of a joystick
if (ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits) < 0 || if (!isBitSet(EV_KEY, evBits) || !isBitSet(EV_ABS, evBits))
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 ||
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0 ||
!TEST_BIT(EV_KEY, evBits) || !TEST_BIT(EV_ABS, evBits))
{ {
close(fd); close(linjs.fd);
return GLFW_FALSE; return GLFW_FALSE;
} }
if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) if (ioctl(linjs.fd, EVIOCGNAME(sizeof(name)), name) < 0)
strncpy(name, "Unknown", sizeof(name)); strncpy(name, "Unknown", sizeof(name));
for (i = BTN_MISC; i < KEY_CNT; i++) for (code = BTN_MISC; code < KEY_CNT; code++)
{ {
if (!TEST_BIT(i, keyBits)) if (!isBitSet(code, keyBits))
continue; continue;
linjs.keyMap[i] = buttonCount++; linjs.keyMap[code - BTN_MISC] = buttonCount;
buttonCount++;
} }
for (i = 0; i < ABS_CNT; i++) for (code = 0; code < ABS_CNT; code++)
{ {
linjs.absMap[i] = -1; linjs.absMap[code] = -1;
if (!TEST_BIT(i, absBits)) if (!isBitSet(code, absBits))
continue; continue;
if (i >= ABS_HAT0X && i <= ABS_HAT3Y) if (code >= ABS_HAT0X && code <= ABS_HAT3Y)
{ {
linjs.absMap[i] = hatCount++; linjs.absMap[code] = hatCount;
hatCount++;
// Skip the Y axis // Skip the Y axis
i++; code++;
} }
else else
{ {
if (ioctl(fd, EVIOCGABS(i), &linjs.absInfo[i]) < 0) if (ioctl(linjs.fd, EVIOCGABS(code), &linjs.absInfo[code]) < 0)
continue; continue;
linjs.absMap[i] = axisCount++; linjs.absMap[code] = axisCount;
axisCount++;
} }
} }
js = _glfwAllocJoystick(name, axisCount, buttonCount, hatCount); js = _glfwAllocJoystick(name, axisCount, buttonCount, hatCount);
if (!js) if (!js)
{ {
close(fd); close(linjs.fd);
return GLFW_FALSE; return GLFW_FALSE;
} }
linjs.fd = fd;
strncpy(linjs.path, path, sizeof(linjs.path)); strncpy(linjs.path, path, sizeof(linjs.path));
memcpy(&js->linjs, &linjs, sizeof(linjs)); memcpy(&js->linjs, &linjs, sizeof(linjs));
// Set initial values for absolute axes pollAbsState(js);
pollJoystick(js);
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_CONNECTED); _glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_CONNECTED);
return GLFW_TRUE; return GLFW_TRUE;
} }
#undef isBitSet
// Frees all resources associated with the specified joystick // Frees all resources associated with the specified joystick
// //
static void closeJoystick(_GLFWjoystick* js) static void closeJoystick(_GLFWjoystick* js)
@ -391,8 +404,7 @@ int _glfwPlatformPollJoystick(int jid, int mode)
else if (e.type == EV_ABS) else if (e.type == EV_ABS)
handleAbsEvent(js, e.code, e.value); handleAbsEvent(js, e.code, e.value);
else if (e.type == EV_SYN && e.code == SYN_DROPPED) else if (e.type == EV_SYN && e.code == SYN_DROPPED)
// Refresh axes pollAbsState(js);
pollJoystick(js);
} }
return js->present; return js->present;

View File

@ -35,21 +35,21 @@
// //
typedef struct _GLFWjoystickLinux typedef struct _GLFWjoystickLinux
{ {
int fd; int fd;
char path[PATH_MAX]; char path[PATH_MAX];
int keyMap[KEY_CNT]; int keyMap[KEY_CNT - BTN_MISC];
int absMap[ABS_CNT]; int absMap[ABS_CNT];
struct input_absinfo absInfo[ABS_CNT]; struct input_absinfo absInfo[ABS_CNT];
int hats[4][2]; int hats[4][2];
} _GLFWjoystickLinux; } _GLFWjoystickLinux;
// Linux-specific joystick API data // Linux-specific joystick API data
// //
typedef struct _GLFWlibraryLinux typedef struct _GLFWlibraryLinux
{ {
int inotify; int inotify;
int watch; int watch;
regex_t regex; regex_t regex;
} _GLFWlibraryLinux; } _GLFWlibraryLinux;