Move internals to shared joystick objects

Preparation for gamecontrollerdb support and the gamepad API.
This commit is contained in:
Camilla Löwy 2017-01-05 19:44:15 +01:00
parent 43c1910453
commit bfd564f257
10 changed files with 619 additions and 753 deletions

View File

@ -32,25 +32,18 @@
#include <IOKit/hid/IOHIDLib.h> #include <IOKit/hid/IOHIDLib.h>
#include <IOKit/hid/IOHIDKeys.h> #include <IOKit/hid/IOHIDKeys.h>
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \ #define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickNS ns
_GLFWjoystickNS ns_js[GLFW_JOYSTICK_LAST + 1] #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE
// Cocoa-specific per-joystick data // Cocoa-specific per-joystick data
// //
typedef struct _GLFWjoystickNS typedef struct _GLFWjoystickNS
{ {
GLFWbool present; IOHIDDeviceRef device;
char name[256]; CFMutableArrayRef axes;
CFMutableArrayRef buttons;
IOHIDDeviceRef deviceRef; CFMutableArrayRef hats;
CFMutableArrayRef axisElements;
CFMutableArrayRef buttonElements;
CFMutableArrayRef hatElements;
float* axes;
unsigned char* buttons;
} _GLFWjoystickNS; } _GLFWjoystickNS;

View File

@ -42,44 +42,127 @@
// //
typedef struct _GLFWjoyelementNS typedef struct _GLFWjoyelementNS
{ {
IOHIDElementRef elementRef; IOHIDElementRef native;
long minimum;
long min; long maximum;
long max;
long minReport;
long maxReport;
} _GLFWjoyelementNS; } _GLFWjoyelementNS;
static void getElementsCFArrayHandler(const void* value, void* parameter); // Returns the value of the specified element of the specified joystick
// Adds an element to the specified joystick
// //
static void addJoystickElement(_GLFWjoystickNS* js, static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element)
IOHIDElementRef elementRef)
{ {
IOHIDElementType elementType; IOReturn result = kIOReturnSuccess;
long usagePage, usage; IOHIDValueRef valueRef;
CFMutableArrayRef elementsArray = NULL; long value = 0;
elementType = IOHIDElementGetType(elementRef); if (js && element && js->ns.device)
usagePage = IOHIDElementGetUsagePage(elementRef);
usage = IOHIDElementGetUsage(elementRef);
if ((elementType != kIOHIDElementTypeInput_Axis) &&
(elementType != kIOHIDElementTypeInput_Button) &&
(elementType != kIOHIDElementTypeInput_Misc))
{ {
result = IOHIDDeviceGetValue(js->ns.device,
element->native,
&valueRef);
if (kIOReturnSuccess == result)
{
value = IOHIDValueGetIntegerValue(valueRef);
// Record min and max for auto calibration
if (value < element->minimum)
element->minimum = value;
if (value > element->maximum)
element->maximum = value;
}
}
// Auto user scale
return value;
}
// Removes the specified joystick
//
static void closeJoystick(_GLFWjoystick* js)
{
int i;
if (!js->present)
return;
for (i = 0; i < CFArrayGetCount(js->ns.axes); i++)
free((void*) CFArrayGetValueAtIndex(js->ns.axes, i));
CFRelease(js->ns.axes);
for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++)
free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i));
CFRelease(js->ns.buttons);
for (i = 0; i < CFArrayGetCount(js->ns.hats); i++)
free((void*) CFArrayGetValueAtIndex(js->ns.hats, i));
CFRelease(js->ns.hats);
_glfwFreeJoystick(js);
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_DISCONNECTED);
}
// Callback for user-initiated joystick addition
//
static void matchCallback(void* context,
IOReturn result,
void* sender,
IOHIDDeviceRef device)
{
int jid;
char name[256];
CFIndex i;
CFStringRef productKey;
_GLFWjoystick* js;
CFMutableArrayRef axes, buttons, hats;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
if (_glfw.joysticks[jid].ns.device == device)
return; return;
} }
switch (usagePage) axes = CFArrayCreateMutable(NULL, 0, NULL);
buttons = CFArrayCreateMutable(NULL, 0, NULL);
hats = CFArrayCreateMutable(NULL, 0, NULL);
productKey = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
if (productKey)
{
CFStringGetCString(productKey,
name,
sizeof(name),
kCFStringEncodingUTF8);
}
else
strncpy(name, "Unknown", sizeof(name));
CFArrayRef elements =
IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
for (i = 0; i < CFArrayGetCount(elements); i++)
{
IOHIDElementRef native = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, i);
if (CFGetTypeID(native) != IOHIDElementGetTypeID())
continue;
const IOHIDElementType type = IOHIDElementGetType(native);
if ((type != kIOHIDElementTypeInput_Axis) &&
(type != kIOHIDElementTypeInput_Button) &&
(type != kIOHIDElementTypeInput_Misc))
{
continue;
}
CFMutableArrayRef target = NULL;
switch (IOHIDElementGetUsagePage(native))
{ {
case kHIDPage_GenericDesktop: case kHIDPage_GenericDesktop:
{ {
switch (usage) switch (IOHIDElementGetUsage(native))
{ {
case kHIDUsage_GD_X: case kHIDUsage_GD_X:
case kHIDUsage_GD_Y: case kHIDUsage_GD_Y:
@ -90,10 +173,10 @@ static void addJoystickElement(_GLFWjoystickNS* js,
case kHIDUsage_GD_Slider: case kHIDUsage_GD_Slider:
case kHIDUsage_GD_Dial: case kHIDUsage_GD_Dial:
case kHIDUsage_GD_Wheel: case kHIDUsage_GD_Wheel:
elementsArray = js->axisElements; target = axes;
break; break;
case kHIDUsage_GD_Hatswitch: case kHIDUsage_GD_Hatswitch:
elementsArray = js->hatElements; target = hats;
break; break;
} }
@ -101,230 +184,34 @@ static void addJoystickElement(_GLFWjoystickNS* js,
} }
case kHIDPage_Button: case kHIDPage_Button:
elementsArray = js->buttonElements; target = buttons;
break; break;
default: default:
break; break;
} }
if (elementsArray) if (target)
{ {
_GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS)); _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS));
element->native = native;
CFArrayAppendValue(elementsArray, element); element->minimum = IOHIDElementGetLogicalMin(native);
element->maximum = IOHIDElementGetLogicalMax(native);
element->elementRef = elementRef; CFArrayAppendValue(target, element);
element->minReport = IOHIDElementGetLogicalMin(elementRef);
element->maxReport = IOHIDElementGetLogicalMax(elementRef);
} }
} }
// Adds an element to the specified joystick CFRelease(elements);
//
static void getElementsCFArrayHandler(const void* value, void* parameter)
{
if (CFGetTypeID(value) == IOHIDElementGetTypeID())
{
addJoystickElement((_GLFWjoystickNS*) parameter,
(IOHIDElementRef) value);
}
}
// Returns the value of the specified element of the specified joystick js = _glfwAllocJoystick(name,
// CFArrayGetCount(axes),
static long getElementValue(_GLFWjoystickNS* js, _GLFWjoyelementNS* element) CFArrayGetCount(buttons) + CFArrayGetCount(hats) * 4);
{
IOReturn result = kIOReturnSuccess;
IOHIDValueRef valueRef;
long value = 0;
if (js && element && js->deviceRef) js->ns.device = device;
{ js->ns.axes = axes;
result = IOHIDDeviceGetValue(js->deviceRef, js->ns.buttons = buttons;
element->elementRef, js->ns.hats = hats;
&valueRef);
if (kIOReturnSuccess == result) _glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_CONNECTED);
{
value = IOHIDValueGetIntegerValue(valueRef);
// Record min and max for auto calibration
if (value < element->minReport)
element->minReport = value;
if (value > element->maxReport)
element->maxReport = value;
}
}
// Auto user scale
return value;
}
// Removes the specified joystick
//
static void removeJoystick(_GLFWjoystickNS* js)
{
int i;
if (!js->present)
return;
for (i = 0; i < CFArrayGetCount(js->axisElements); i++)
free((void*) CFArrayGetValueAtIndex(js->axisElements, i));
CFArrayRemoveAllValues(js->axisElements);
CFRelease(js->axisElements);
for (i = 0; i < CFArrayGetCount(js->buttonElements); i++)
free((void*) CFArrayGetValueAtIndex(js->buttonElements, i));
CFArrayRemoveAllValues(js->buttonElements);
CFRelease(js->buttonElements);
for (i = 0; i < CFArrayGetCount(js->hatElements); i++)
free((void*) CFArrayGetValueAtIndex(js->hatElements, i));
CFArrayRemoveAllValues(js->hatElements);
CFRelease(js->hatElements);
free(js->axes);
free(js->buttons);
memset(js, 0, sizeof(_GLFWjoystickNS));
_glfwInputJoystickChange(js - _glfw.ns_js, GLFW_DISCONNECTED);
}
// Polls for joystick axis events and updates GLFW state
//
static GLFWbool pollJoystickAxisEvents(_GLFWjoystickNS* js)
{
CFIndex i;
if (!js->present)
return GLFW_FALSE;
for (i = 0; i < CFArrayGetCount(js->axisElements); i++)
{
_GLFWjoyelementNS* axis = (_GLFWjoyelementNS*)
CFArrayGetValueAtIndex(js->axisElements, i);
long value = getElementValue(js, axis);
long readScale = axis->maxReport - axis->minReport;
if (readScale == 0)
js->axes[i] = value;
else
js->axes[i] = (2.f * (value - axis->minReport) / readScale) - 1.f;
}
return GLFW_TRUE;
}
// Polls for joystick button events and updates GLFW state
//
static GLFWbool pollJoystickButtonEvents(_GLFWjoystickNS* js)
{
CFIndex i;
int buttonIndex = 0;
if (!js->present)
return GLFW_FALSE;
for (i = 0; i < CFArrayGetCount(js->buttonElements); i++)
{
_GLFWjoyelementNS* button = (_GLFWjoyelementNS*)
CFArrayGetValueAtIndex(js->buttonElements, i);
if (getElementValue(js, button))
js->buttons[buttonIndex++] = GLFW_PRESS;
else
js->buttons[buttonIndex++] = GLFW_RELEASE;
}
for (i = 0; i < CFArrayGetCount(js->hatElements); i++)
{
_GLFWjoyelementNS* hat = (_GLFWjoyelementNS*)
CFArrayGetValueAtIndex(js->hatElements, i);
// Bit fields of button presses for each direction, including nil
const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 };
long j, value = getElementValue(js, hat);
if (value < 0 || value > 8)
value = 8;
for (j = 0; j < 4; j++)
{
if (directions[value] & (1 << j))
js->buttons[buttonIndex++] = GLFW_PRESS;
else
js->buttons[buttonIndex++] = GLFW_RELEASE;
}
}
return GLFW_TRUE;
}
// Callback for user-initiated joystick addition
//
static void matchCallback(void* context,
IOReturn result,
void* sender,
IOHIDDeviceRef deviceRef)
{
_GLFWjoystickNS* js;
int jid;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
if (_glfw.ns_js[jid].present && _glfw.ns_js[jid].deviceRef == deviceRef)
return;
}
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
if (!_glfw.ns_js[jid].present)
break;
}
if (jid > GLFW_JOYSTICK_LAST)
return;
js = _glfw.ns_js + jid;
js->present = GLFW_TRUE;
js->deviceRef = deviceRef;
CFStringRef name = IOHIDDeviceGetProperty(deviceRef,
CFSTR(kIOHIDProductKey));
if (name)
{
CFStringGetCString(name,
js->name,
sizeof(js->name),
kCFStringEncodingUTF8);
}
else
strncpy(js->name, "Unknown", sizeof(js->name));
js->axisElements = CFArrayCreateMutable(NULL, 0, NULL);
js->buttonElements = CFArrayCreateMutable(NULL, 0, NULL);
js->hatElements = CFArrayCreateMutable(NULL, 0, NULL);
CFArrayRef arrayRef = IOHIDDeviceCopyMatchingElements(deviceRef,
NULL,
kIOHIDOptionsTypeNone);
CFRange range = { 0, CFArrayGetCount(arrayRef) };
CFArrayApplyFunction(arrayRef,
range,
getElementsCFArrayHandler,
(void*) js);
CFRelease(arrayRef);
js->axes = calloc(CFArrayGetCount(js->axisElements), sizeof(float));
js->buttons = calloc(CFArrayGetCount(js->buttonElements) +
CFArrayGetCount(js->hatElements) * 4, 1);
_glfwInputJoystickChange(jid, GLFW_CONNECTED);
} }
// Callback for user-initiated joystick removal // Callback for user-initiated joystick removal
@ -332,15 +219,15 @@ static void matchCallback(void* context,
static void removeCallback(void* context, static void removeCallback(void* context,
IOReturn result, IOReturn result,
void* sender, void* sender,
IOHIDDeviceRef deviceRef) IOHIDDeviceRef device)
{ {
int jid; int jid;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{ {
if (_glfw.ns_js[jid].deviceRef == deviceRef) if (_glfw.joysticks[jid].ns.device == device)
{ {
removeJoystick(_glfw.ns_js + jid); closeJoystick(_glfw.joysticks + jid);
break; break;
} }
} }
@ -459,10 +346,7 @@ void _glfwTerminateJoysticksNS(void)
int jid; int jid;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{ closeJoystick(_glfw.joysticks + jid);
_GLFWjoystickNS* js = _glfw.ns_js + jid;
removeJoystick(js);
}
CFRelease(_glfw.ns.hidManager); CFRelease(_glfw.ns.hidManager);
_glfw.ns.hidManager = NULL; _glfw.ns.hidManager = NULL;
@ -473,39 +357,60 @@ void _glfwTerminateJoysticksNS(void)
////// GLFW platform API ////// ////// GLFW platform API //////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
int _glfwPlatformJoystickPresent(int jid) int _glfwPlatformPollJoystick(int jid, int mode)
{ {
_GLFWjoystickNS* js = _glfw.ns_js + jid; _GLFWjoystick* js = _glfw.joysticks + jid;
if (mode == _GLFW_POLL_AXES)
{
CFIndex i;
for (i = 0; i < CFArrayGetCount(js->ns.axes); i++)
{
_GLFWjoyelementNS* axis = (_GLFWjoyelementNS*)
CFArrayGetValueAtIndex(js->ns.axes, i);
const long value = getElementValue(js, axis);
const long delta = axis->maximum - axis->minimum;
if (delta == 0)
_glfwInputJoystickAxis(jid, i, value);
else
_glfwInputJoystickAxis(jid, i, (2.f * (value - axis->minimum) / delta) - 1.f);
}
}
else if (mode == _GLFW_POLL_BUTTONS)
{
CFIndex i, bi = 0;
for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++)
{
_GLFWjoyelementNS* button = (_GLFWjoyelementNS*)
CFArrayGetValueAtIndex(js->ns.buttons, i);
const char value = getElementValue(js, button) ? 1 : 0;
_glfwInputJoystickButton(jid, bi++, value);
}
for (i = 0; i < CFArrayGetCount(js->ns.hats); i++)
{
_GLFWjoyelementNS* hat = (_GLFWjoyelementNS*)
CFArrayGetValueAtIndex(js->ns.hats, i);
// Bit fields of button presses for each direction, including nil
const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 };
long j, state = getElementValue(js, hat);
if (state < 0 || state > 8)
state = 8;
for (j = 0; j < 4; j++)
{
const char value = directions[state] & (1 << j) ? 1 : 0;
_glfwInputJoystickButton(jid, bi++, value);
}
}
}
return js->present; return js->present;
} }
const float* _glfwPlatformGetJoystickAxes(int jid, int* count)
{
_GLFWjoystickNS* js = _glfw.ns_js + jid;
if (!pollJoystickAxisEvents(js))
return NULL;
*count = (int) CFArrayGetCount(js->axisElements);
return js->axes;
}
const unsigned char* _glfwPlatformGetJoystickButtons(int jid, int* count)
{
_GLFWjoystickNS* js = _glfw.ns_js + jid;
if (!pollJoystickButtonEvents(js))
return NULL;
*count = (int) CFArrayGetCount(js->buttonElements) +
(int) CFArrayGetCount(js->hatElements) * 4;
return js->buttons;
}
const char* _glfwPlatformGetJoystickName(int jid)
{
_GLFWjoystickNS* js = _glfw.ns_js + jid;
if (!js->present)
return NULL;
return js->name;
}

View File

@ -30,6 +30,7 @@
#include <assert.h> #include <assert.h>
#include <float.h> #include <float.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
// Internal key state used for sticky keys // Internal key state used for sticky keys
#define _GLFW_STICK 3 #define _GLFW_STICK 3
@ -123,12 +124,22 @@ void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths)
window->callbacks.drop((GLFWwindow*) window, count, paths); window->callbacks.drop((GLFWwindow*) window, count, paths);
} }
void _glfwInputJoystickChange(int jid, int event) void _glfwInputJoystick(int jid, int event)
{ {
if (_glfw.callbacks.joystick) if (_glfw.callbacks.joystick)
_glfw.callbacks.joystick(jid, event); _glfw.callbacks.joystick(jid, event);
} }
void _glfwInputJoystickAxis(int jid, int axis, float value)
{
_glfw.joysticks[jid].axes[axis] = value;
}
void _glfwInputJoystickButton(int jid, int button, char value)
{
_glfw.joysticks[jid].buttons[button] = value;
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
////// GLFW internal API ////// ////// GLFW internal API //////
@ -141,6 +152,39 @@ GLFWbool _glfwIsPrintable(int key)
key == GLFW_KEY_KP_EQUAL; key == GLFW_KEY_KP_EQUAL;
} }
_GLFWjoystick* _glfwAllocJoystick(const char* name, int axisCount, int buttonCount)
{
int jid;
_GLFWjoystick* js;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
if (!_glfw.joysticks[jid].present)
break;
}
if (jid > GLFW_JOYSTICK_LAST)
return NULL;
js = _glfw.joysticks + jid;
js->present = GLFW_TRUE;
js->name = strdup(name);
js->axes = calloc(axisCount, sizeof(float));
js->buttons = calloc(buttonCount, 1);
js->axisCount = axisCount;
js->buttonCount = buttonCount;
return js;
}
void _glfwFreeJoystick(_GLFWjoystick* js)
{
free(js->name);
free(js->axes);
free(js->buttons);
memset(js, 0, sizeof(_GLFWjoystick));
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
////// GLFW public API ////// ////// GLFW public API //////
@ -554,20 +598,29 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun)
GLFWAPI int glfwJoystickPresent(int jid) GLFWAPI int glfwJoystickPresent(int jid)
{ {
_GLFW_REQUIRE_INIT_OR_RETURN(0); assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST);
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
if (jid < 0 || jid > GLFW_JOYSTICK_LAST) if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
{ {
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid);
return 0; return GLFW_FALSE;
} }
return _glfwPlatformJoystickPresent(jid); if (!_glfw.joysticks[jid].present)
return GLFW_FALSE;
return _glfwPlatformPollJoystick(jid, _GLFW_POLL_PRESENCE);
} }
GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count) GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count)
{ {
assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST);
assert(count != NULL); assert(count != NULL);
*count = 0; *count = 0;
_GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
@ -578,12 +631,22 @@ GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count)
return NULL; return NULL;
} }
return _glfwPlatformGetJoystickAxes(jid, count); if (!_glfw.joysticks[jid].present)
return NULL;
if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_AXES))
return NULL;
*count = _glfw.joysticks[jid].axisCount;
return _glfw.joysticks[jid].axes;
} }
GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count) GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count)
{ {
assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST);
assert(count != NULL); assert(count != NULL);
*count = 0; *count = 0;
_GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
@ -594,11 +657,21 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count)
return NULL; return NULL;
} }
return _glfwPlatformGetJoystickButtons(jid, count); if (!_glfw.joysticks[jid].present)
return NULL;
if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_BUTTONS))
return NULL;
*count = _glfw.joysticks[jid].buttonCount;
return _glfw.joysticks[jid].buttons;
} }
GLFWAPI const char* glfwGetJoystickName(int jid) GLFWAPI const char* glfwGetJoystickName(int jid)
{ {
assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
if (jid < 0 || jid > GLFW_JOYSTICK_LAST) if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
@ -607,7 +680,13 @@ GLFWAPI const char* glfwGetJoystickName(int jid)
return NULL; return NULL;
} }
return _glfwPlatformGetJoystickName(jid); if (!_glfw.joysticks[jid].present)
return NULL;
if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_PRESENCE))
return NULL;
return _glfw.joysticks[jid].name;
} }
GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun)

View File

@ -53,6 +53,10 @@
#define _GLFW_INSERT_FIRST 0 #define _GLFW_INSERT_FIRST 0
#define _GLFW_INSERT_LAST 1 #define _GLFW_INSERT_LAST 1
#define _GLFW_POLL_PRESENCE 0
#define _GLFW_POLL_AXES 1
#define _GLFW_POLL_BUTTONS 2
typedef int GLFWbool; typedef int GLFWbool;
typedef struct _GLFWwndconfig _GLFWwndconfig; typedef struct _GLFWwndconfig _GLFWwndconfig;
@ -63,6 +67,7 @@ typedef struct _GLFWwindow _GLFWwindow;
typedef struct _GLFWlibrary _GLFWlibrary; typedef struct _GLFWlibrary _GLFWlibrary;
typedef struct _GLFWmonitor _GLFWmonitor; typedef struct _GLFWmonitor _GLFWmonitor;
typedef struct _GLFWcursor _GLFWcursor; typedef struct _GLFWcursor _GLFWcursor;
typedef struct _GLFWjoystick _GLFWjoystick;
typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*); typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*);
typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*); typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*);
@ -246,6 +251,9 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void);
y = t; \ y = t; \
} }
// Maps a joystick pointer to an ID
#define _GLFW_JOYSTICK_ID(js) ((int) ((js) - _glfw.joysticks))
//======================================================================== //========================================================================
// Platform-independent structures // Platform-independent structures
@ -440,6 +448,21 @@ struct _GLFWcursor
_GLFW_PLATFORM_CURSOR_STATE; _GLFW_PLATFORM_CURSOR_STATE;
}; };
/*! @brief Joystick structure
*/
struct _GLFWjoystick
{
GLFWbool present;
float* axes;
int axisCount;
unsigned char* buttons;
int buttonCount;
char* name;
// This is defined in the joystick API's joystick.h
_GLFW_PLATFORM_JOYSTICK_STATE;
};
/*! @brief Library global data. /*! @brief Library global data.
*/ */
struct _GLFWlibrary struct _GLFWlibrary
@ -458,6 +481,8 @@ struct _GLFWlibrary
_GLFWmonitor** monitors; _GLFWmonitor** monitors;
int monitorCount; int monitorCount;
_GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1];
uint64_t timerOffset; uint64_t timerOffset;
struct { struct {
@ -605,25 +630,9 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string);
*/ */
const char* _glfwPlatformGetClipboardString(_GLFWwindow* window); const char* _glfwPlatformGetClipboardString(_GLFWwindow* window);
/*! @copydoc glfwJoystickPresent /*! @ingroup platform
* @ingroup platform
*/ */
int _glfwPlatformJoystickPresent(int jid); int _glfwPlatformPollJoystick(int jid, int mode);
/*! @copydoc glfwGetJoystickAxes
* @ingroup platform
*/
const float* _glfwPlatformGetJoystickAxes(int jid, int* count);
/*! @copydoc glfwGetJoystickButtons
* @ingroup platform
*/
const unsigned char* _glfwPlatformGetJoystickButtons(int jid, int* count);
/*! @copydoc glfwGetJoystickName
* @ingroup platform
*/
const char* _glfwPlatformGetJoystickName(int jid);
/*! @copydoc glfwGetTimerValue /*! @copydoc glfwGetTimerValue
* @ingroup platform * @ingroup platform
@ -980,7 +989,21 @@ void _glfwInputDrop(_GLFWwindow* window, int count, const char** names);
* @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.
* @ingroup event * @ingroup event
*/ */
void _glfwInputJoystickChange(int jid, int event); void _glfwInputJoystick(int jid, int event);
/*! @brief Notifies shared code of the new value of a joystick axis.
* @param[in] jid The joystick whose axis to update.
* @param[in] axis The index of the axis to update.
* @param[in] value The new value of the axis.
*/
void _glfwInputJoystickAxis(int jid, int axis, float value);
/*! @brief Notifies shared code of the new value of a joystick button.
* @param[in] jid The joystick whose button to update.
* @param[in] button The index of the button to update.
* @param[in] value The new value of the button.
*/
void _glfwInputJoystickButton(int jid, int button, char value);
//======================================================================== //========================================================================
@ -1065,6 +1088,14 @@ _GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM);
*/ */
void _glfwFreeMonitor(_GLFWmonitor* monitor); void _glfwFreeMonitor(_GLFWmonitor* monitor);
/*! @ingroup utility
*/
_GLFWjoystick* _glfwAllocJoystick(const char* name, int axisCount, int buttonCount);
/*! @ingroup utility
*/
void _glfwFreeJoystick(_GLFWjoystick* js);
/*! @ingroup utility /*! @ingroup utility
*/ */
GLFWbool _glfwIsPrintable(int key); GLFWbool _glfwIsPrintable(int key);

View File

@ -51,26 +51,16 @@ static GLFWbool openJoystickDevice(const char* path)
char axisCount, buttonCount; char axisCount, buttonCount;
char name[256] = ""; char name[256] = "";
int jid, fd, version; int jid, fd, version;
_GLFWjoystickLinux* js; _GLFWjoystick* js;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{ {
if (!_glfw.linux_js.js[jid].present) if (!_glfw.joysticks[jid].present)
continue; continue;
if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0)
if (strcmp(_glfw.linux_js.js[jid].path, path) == 0)
return GLFW_FALSE; return GLFW_FALSE;
} }
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
if (!_glfw.linux_js.js[jid].present)
break;
}
if (jid > GLFW_JOYSTICK_LAST)
return GLFW_FALSE;
fd = open(path, O_RDONLY | O_NONBLOCK); fd = open(path, O_RDONLY | O_NONBLOCK);
if (fd == -1) if (fd == -1)
return GLFW_FALSE; return GLFW_FALSE;
@ -87,80 +77,44 @@ static GLFWbool openJoystickDevice(const char* path)
if (ioctl(fd, JSIOCGNAME(sizeof(name)), name) < 0) if (ioctl(fd, JSIOCGNAME(sizeof(name)), name) < 0)
strncpy(name, "Unknown", sizeof(name)); strncpy(name, "Unknown", sizeof(name));
js = _glfw.linux_js.js + jid;
js->present = GLFW_TRUE;
js->name = strdup(name);
js->path = strdup(path);
js->fd = fd;
ioctl(fd, JSIOCGAXES, &axisCount); ioctl(fd, JSIOCGAXES, &axisCount);
js->axisCount = (int) axisCount;
js->axes = calloc(axisCount, sizeof(float));
ioctl(fd, JSIOCGBUTTONS, &buttonCount); ioctl(fd, JSIOCGBUTTONS, &buttonCount);
js->buttonCount = (int) buttonCount;
js->buttons = calloc(buttonCount, 1);
_glfwInputJoystickChange(jid, GLFW_CONNECTED); js = _glfwAllocJoystick(name, axisCount, buttonCount);
if (!js)
{
close(fd);
return GLFW_FALSE;
}
js->linjs.path = strdup(path);
js->linjs.fd = fd;
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_CONNECTED);
return GLFW_TRUE; return GLFW_TRUE;
} }
#endif // __linux__ #endif // __linux__
// Polls for and processes events the specified joystick // Frees all resources associated with the specified joystick
// //
static GLFWbool pollJoystickEvents(_GLFWjoystickLinux* js)
{
#if defined(__linux__) #if defined(__linux__)
_glfwPollJoystickEvents(); static void closeJoystick(_GLFWjoystick* js)
if (!js->present)
return GLFW_FALSE;
// Read all queued events (non-blocking)
for (;;)
{ {
struct js_event e; close(js->linjs.fd);
free(js->linjs.path);
errno = 0; _glfwFreeJoystick(js);
if (read(js->fd, &e, sizeof(e)) < 0) _glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_DISCONNECTED);
{
// Reset the joystick slot if the device was disconnected
if (errno == ENODEV)
{
free(js->axes);
free(js->buttons);
free(js->name);
free(js->path);
memset(js, 0, sizeof(_GLFWjoystickLinux));
_glfwInputJoystickChange(js - _glfw.linux_js.js,
GLFW_DISCONNECTED);
}
break;
}
// Clear the initial-state bit
e.type &= ~JS_EVENT_INIT;
if (e.type == JS_EVENT_AXIS)
js->axes[e.number] = (float) e.value / 32767.0f;
else if (e.type == JS_EVENT_BUTTON)
js->buttons[e.number] = e.value ? GLFW_PRESS : GLFW_RELEASE;
} }
#endif // __linux__ #endif // __linux__
return js->present;
}
// Lexically compare joysticks by name; used by qsort // Lexically compare joysticks by name; used by qsort
// //
#if defined(__linux__) #if defined(__linux__)
static int compareJoysticks(const void* fp, const void* sp) static int compareJoysticks(const void* fp, const void* sp)
{ {
const _GLFWjoystickLinux* fj = fp; const _GLFWjoystick* fj = fp;
const _GLFWjoystickLinux* sj = sp; const _GLFWjoystick* sj = sp;
return strcmp(fj->path, sj->path); return strcmp(fj->linjs.path, sj->linjs.path);
} }
#endif // __linux__ #endif // __linux__
@ -178,8 +132,8 @@ GLFWbool _glfwInitJoysticksLinux(void)
int count = 0; int count = 0;
const char* dirname = "/dev/input"; const char* dirname = "/dev/input";
_glfw.linux_js.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); _glfw.linjs.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (_glfw.linux_js.inotify == -1) if (_glfw.linjs.inotify == -1)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
"Linux: Failed to initialize inotify: %s", "Linux: Failed to initialize inotify: %s",
@ -190,10 +144,10 @@ GLFWbool _glfwInitJoysticksLinux(void)
// HACK: Register for IN_ATTRIB as well to get notified when udev is done // HACK: Register for IN_ATTRIB as well to get notified when udev is done
// This works well in practice but the true way is libudev // This works well in practice but the true way is libudev
_glfw.linux_js.watch = inotify_add_watch(_glfw.linux_js.inotify, _glfw.linjs.watch = inotify_add_watch(_glfw.linjs.inotify,
dirname, dirname,
IN_CREATE | IN_ATTRIB); IN_CREATE | IN_ATTRIB);
if (_glfw.linux_js.watch == -1) if (_glfw.linjs.watch == -1)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
"Linux: Failed to watch for joystick connections in %s: %s", "Linux: Failed to watch for joystick connections in %s: %s",
@ -202,7 +156,7 @@ GLFWbool _glfwInitJoysticksLinux(void)
// Continue without device connection notifications // Continue without device connection notifications
} }
if (regcomp(&_glfw.linux_js.regex, "^js[0-9]\\+$", 0) != 0) if (regcomp(&_glfw.linjs.regex, "^js[0-9]\\+$", 0) != 0)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, "Linux: Failed to compile regex"); _glfwInputError(GLFW_PLATFORM_ERROR, "Linux: Failed to compile regex");
return GLFW_FALSE; return GLFW_FALSE;
@ -218,7 +172,7 @@ GLFWbool _glfwInitJoysticksLinux(void)
char path[20]; char path[20];
regmatch_t match; regmatch_t match;
if (regexec(&_glfw.linux_js.regex, entry->d_name, 1, &match, 0) != 0) if (regexec(&_glfw.linjs.regex, entry->d_name, 1, &match, 0) != 0)
continue; continue;
snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name); snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name);
@ -237,7 +191,7 @@ GLFWbool _glfwInitJoysticksLinux(void)
// Continue with no joysticks detected // Continue with no joysticks detected
} }
qsort(_glfw.linux_js.js, count, sizeof(_GLFWjoystickLinux), compareJoysticks); qsort(_glfw.joysticks, count, sizeof(_GLFWjoystick), compareJoysticks);
#endif // __linux__ #endif // __linux__
return GLFW_TRUE; return GLFW_TRUE;
@ -248,46 +202,41 @@ GLFWbool _glfwInitJoysticksLinux(void)
void _glfwTerminateJoysticksLinux(void) void _glfwTerminateJoysticksLinux(void)
{ {
#if defined(__linux__) #if defined(__linux__)
int i; int jid;
for (i = 0; i <= GLFW_JOYSTICK_LAST; i++) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{ {
if (_glfw.linux_js.js[i].present) _GLFWjoystick* js = _glfw.joysticks + jid;
{ if (js->present)
close(_glfw.linux_js.js[i].fd); closeJoystick(js);
free(_glfw.linux_js.js[i].axes);
free(_glfw.linux_js.js[i].buttons);
free(_glfw.linux_js.js[i].name);
free(_glfw.linux_js.js[i].path);
}
} }
regfree(&_glfw.linux_js.regex); regfree(&_glfw.linjs.regex);
if (_glfw.linux_js.inotify > 0) if (_glfw.linjs.inotify > 0)
{ {
if (_glfw.linux_js.watch > 0) if (_glfw.linjs.watch > 0)
inotify_rm_watch(_glfw.linux_js.inotify, _glfw.linux_js.watch); inotify_rm_watch(_glfw.linjs.inotify, _glfw.linjs.watch);
close(_glfw.linux_js.inotify); close(_glfw.linjs.inotify);
} }
#endif // __linux__ #endif // __linux__
} }
void _glfwPollJoystickEvents(void) void _glfwDetectJoystickConnectionLinux(void)
{ {
#if defined(__linux__) #if defined(__linux__)
ssize_t offset = 0; ssize_t offset = 0;
char buffer[16384]; char buffer[16384];
const ssize_t size = read(_glfw.linux_js.inotify, buffer, sizeof(buffer)); const ssize_t size = read(_glfw.linjs.inotify, buffer, sizeof(buffer));
while (size > offset) while (size > offset)
{ {
regmatch_t match; regmatch_t match;
const struct inotify_event* e = (struct inotify_event*) (buffer + offset); const struct inotify_event* e = (struct inotify_event*) (buffer + offset);
if (regexec(&_glfw.linux_js.regex, e->name, 1, &match, 0) == 0) if (regexec(&_glfw.linjs.regex, e->name, 1, &match, 0) == 0)
{ {
char path[20]; char path[20];
snprintf(path, sizeof(path), "/dev/input/%s", e->name); snprintf(path, sizeof(path), "/dev/input/%s", e->name);
@ -304,38 +253,35 @@ void _glfwPollJoystickEvents(void)
////// GLFW platform API ////// ////// GLFW platform API //////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
int _glfwPlatformJoystickPresent(int jid) int _glfwPlatformPollJoystick(int jid, int mode)
{ {
_GLFWjoystickLinux* js = _glfw.linux_js.js + jid; #if defined(__linux__)
return pollJoystickEvents(js); _GLFWjoystick* js = _glfw.joysticks + jid;
// Read all queued events (non-blocking)
for (;;)
{
struct js_event e;
errno = 0;
if (read(js->linjs.fd, &e, sizeof(e)) < 0)
{
// Reset the joystick slot if the device was disconnected
if (errno == ENODEV)
closeJoystick(js);
break;
} }
const float* _glfwPlatformGetJoystickAxes(int jid, int* count) // Clear the initial-state bit
{ e.type &= ~JS_EVENT_INIT;
_GLFWjoystickLinux* js = _glfw.linux_js.js + jid;
if (!pollJoystickEvents(js))
return NULL;
*count = js->axisCount; if (e.type == JS_EVENT_AXIS)
return js->axes; _glfwInputJoystickAxis(jid, e.number, e.value / 32767.0f);
} else if (e.type == JS_EVENT_BUTTON)
_glfwInputJoystickButton(jid, e.number, e.value ? 1 : 0);
const unsigned char* _glfwPlatformGetJoystickButtons(int jid, int* count) }
{ #endif // __linux__
_GLFWjoystickLinux* js = _glfw.linux_js.js + jid; return js->present;
if (!pollJoystickEvents(js))
return NULL;
*count = js->buttonCount;
return js->buttons;
}
const char* _glfwPlatformGetJoystickName(int jid)
{
_GLFWjoystickLinux* js = _glfw.linux_js.js + jid;
if (!pollJoystickEvents(js))
return NULL;
return js->name;
} }

View File

@ -29,40 +29,32 @@
#include <regex.h> #include <regex.h>
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWjoylistLinux linux_js #define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickLinux linjs
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWlibraryLinux linjs
// Linux-specific joystick data // Linux-specific joystick data
// //
typedef struct _GLFWjoystickLinux typedef struct _GLFWjoystickLinux
{ {
GLFWbool present;
int fd; int fd;
float* axes;
int axisCount;
unsigned char* buttons;
int buttonCount;
char* name;
char* path; char* path;
} _GLFWjoystickLinux; } _GLFWjoystickLinux;
// Linux-specific joystick API data // Linux-specific joystick API data
// //
typedef struct _GLFWjoylistLinux typedef struct _GLFWlibraryLinux
{ {
_GLFWjoystickLinux js[GLFW_JOYSTICK_LAST + 1];
#if defined(__linux__) #if defined(__linux__)
int inotify; int inotify;
int watch; int watch;
regex_t regex; regex_t regex;
#endif /*__linux__*/ #endif /*__linux__*/
} _GLFWjoylistLinux; } _GLFWlibraryLinux;
GLFWbool _glfwInitJoysticksLinux(void); GLFWbool _glfwInitJoysticksLinux(void);
void _glfwTerminateJoysticksLinux(void); void _glfwTerminateJoysticksLinux(void);
void _glfwDetectJoystickConnectionLinux(void);
void _glfwPollJoystickEvents(void);
#endif // _glfw3_linux_joystick_h_ #endif // _glfw3_linux_joystick_h_

View File

@ -269,26 +269,11 @@ int _glfwPlatformGetKeyScancode(int key)
return -1; return -1;
} }
int _glfwPlatformJoystickPresent(int joy) int _glfwPlatformPollJoystick(int jid, int mode)
{ {
return GLFW_FALSE; return GLFW_FALSE;
} }
const float* _glfwPlatformGetJoystickAxes(int joy, int* count)
{
return NULL;
}
const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count)
{
return NULL;
}
const char* _glfwPlatformGetJoystickName(int joy)
{
return NULL;
}
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
{ {
} }

View File

@ -31,9 +31,6 @@
#include <initguid.h> #include <initguid.h>
#define _GLFW_PRESENCE_ONLY 1
#define _GLFW_UPDATE_STATE 2
#define _GLFW_TYPE_AXIS 0 #define _GLFW_TYPE_AXIS 0
#define _GLFW_TYPE_SLIDER 1 #define _GLFW_TYPE_SLIDER 1
#define _GLFW_TYPE_BUTTON 2 #define _GLFW_TYPE_BUTTON 2
@ -238,21 +235,16 @@ static GLFWbool supportsXInput(const GUID* guid)
// Frees all resources associated with the specified joystick // Frees all resources associated with the specified joystick
// //
static void closeJoystick(_GLFWjoystickWin32* js) static void closeJoystick(_GLFWjoystick* js)
{ {
if (js->device) if (js->win32.device)
{ {
IDirectInputDevice8_Unacquire(js->device); IDirectInputDevice8_Unacquire(js->win32.device);
IDirectInputDevice8_Release(js->device); IDirectInputDevice8_Release(js->win32.device);
} }
free(js->name); _glfwFreeJoystick(js);
free(js->axes); _glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_DISCONNECTED);
free(js->buttons);
free(js->objects);
memset(js, 0, sizeof(_GLFWjoystickWin32));
_glfwInputJoystickChange((int) (js - _glfw.win32_js), GLFW_DISCONNECTED);
} }
// DirectInput device object enumeration callback // DirectInput device object enumeration callback
@ -337,23 +329,17 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user)
DIPROPDWORD dipd; DIPROPDWORD dipd;
IDirectInputDevice8* device; IDirectInputDevice8* device;
_GLFWobjenumWin32 data; _GLFWobjenumWin32 data;
_GLFWjoystickWin32* js; _GLFWjoystick* js;
char name[256];
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{ {
if (memcmp(&_glfw.win32_js[jid].guid, &di->guidInstance, sizeof(GUID)) == 0) if (!_glfw.joysticks[jid].present)
continue;
if (memcmp(&_glfw.joysticks[jid].win32.guid, &di->guidInstance, sizeof(GUID)) == 0)
return DIENUM_CONTINUE; return DIENUM_CONTINUE;
} }
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
if (!_glfw.win32_js[jid].present)
break;
}
if (jid > GLFW_JOYSTICK_LAST)
return DIENUM_STOP;
if (supportsXInput(&di->guidProduct)) if (supportsXInput(&di->guidProduct))
return DIENUM_CONTINUE; return DIENUM_CONTINUE;
@ -426,228 +412,38 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user)
sizeof(_GLFWjoyobjectWin32), sizeof(_GLFWjoyobjectWin32),
compareJoystickObjects); compareJoystickObjects);
js = _glfw.win32_js + jid; if (!WideCharToMultiByte(CP_UTF8, 0,
js->device = device; di->tszInstanceName, -1,
js->guid = di->guidInstance; name, sizeof(name),
js->axisCount = data.axisCount + data.sliderCount; NULL, NULL))
js->axes = calloc(js->axisCount, sizeof(float)); {
js->buttonCount += data.buttonCount + data.povCount * 4; _glfwInputError(GLFW_PLATFORM_ERROR,
js->buttons = calloc(js->buttonCount, 1); "Win32: Failed to convert joystick name to UTF-8");
js->objects = data.objects;
js->objectCount = data.objectCount;
js->name = _glfwCreateUTF8FromWideStringWin32(di->tszInstanceName);
js->present = GLFW_TRUE;
_glfwInputJoystickChange(jid, GLFW_CONNECTED); IDirectInputDevice8_Release(device);
free(data.objects);
return DIENUM_STOP;
}
js = _glfwAllocJoystick(name,
data.axisCount + data.sliderCount,
data.buttonCount + data.povCount * 4);
if (!js)
{
IDirectInputDevice8_Release(device);
free(data.objects);
return DIENUM_STOP;
}
js->win32.device = device;
js->win32.guid = di->guidInstance;
js->win32.objects = data.objects;
js->win32.objectCount = data.objectCount;
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_CONNECTED);
return DIENUM_CONTINUE; return DIENUM_CONTINUE;
} }
// Attempt to open the specified joystick device
// TODO: Pack state arrays for non-gamepad devices
//
static GLFWbool openXinputDevice(DWORD index)
{
int jid;
XINPUT_CAPABILITIES xic;
_GLFWjoystickWin32* js;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
if (_glfw.win32_js[jid].present &&
_glfw.win32_js[jid].device == NULL &&
_glfw.win32_js[jid].index == index)
{
return GLFW_FALSE;
}
}
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
if (!_glfw.win32_js[jid].present)
break;
}
if (jid > GLFW_JOYSTICK_LAST)
return GLFW_FALSE;
if (XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS)
return GLFW_FALSE;
js = _glfw.win32_js + jid;
js->axisCount = 6;
js->axes = calloc(js->axisCount, sizeof(float));
js->buttonCount = 14;
js->buttons = calloc(js->buttonCount, 1);
js->present = GLFW_TRUE;
js->name = strdup(getDeviceDescription(&xic));
js->index = index;
_glfwInputJoystickChange(jid, GLFW_CONNECTED);
return GLFW_TRUE;
}
// Polls for and processes events the specified joystick
//
static GLFWbool pollJoystickState(_GLFWjoystickWin32* js, int mode)
{
if (!js->present)
return GLFW_FALSE;
if (js->device)
{
int i, j, ai = 0, bi = 0;
HRESULT result;
DIJOYSTATE state;
IDirectInputDevice8_Poll(js->device);
result = IDirectInputDevice8_GetDeviceState(js->device,
sizeof(state),
&state);
if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST)
{
IDirectInputDevice8_Acquire(js->device);
IDirectInputDevice8_Poll(js->device);
result = IDirectInputDevice8_GetDeviceState(js->device,
sizeof(state),
&state);
}
if (FAILED(result))
{
closeJoystick(js);
return GLFW_FALSE;
}
if (mode == _GLFW_PRESENCE_ONLY)
return GLFW_TRUE;
for (i = 0; i < js->objectCount; i++)
{
const void* data = (char*) &state + js->objects[i].offset;
switch (js->objects[i].type)
{
case _GLFW_TYPE_AXIS:
case _GLFW_TYPE_SLIDER:
{
js->axes[ai++] = (*((LONG*) data) + 0.5f) / 32767.5f;
break;
}
case _GLFW_TYPE_BUTTON:
{
if (*((BYTE*) data) & 0x80)
js->buttons[bi++] = GLFW_PRESS;
else
js->buttons[bi++] = GLFW_RELEASE;
break;
}
case _GLFW_TYPE_POV:
{
const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 };
// Screams of horror are appropriate at this point
int value = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES);
if (value < 0 || value > 8)
value = 8;
for (j = 0; j < 4; j++)
{
if (directions[value] & (1 << j))
js->buttons[bi++] = GLFW_PRESS;
else
js->buttons[bi++] = GLFW_RELEASE;
}
break;
}
}
}
return GLFW_TRUE;
}
else
{
int i;
DWORD result;
XINPUT_STATE xis;
const WORD buttons[14] =
{
XINPUT_GAMEPAD_A,
XINPUT_GAMEPAD_B,
XINPUT_GAMEPAD_X,
XINPUT_GAMEPAD_Y,
XINPUT_GAMEPAD_LEFT_SHOULDER,
XINPUT_GAMEPAD_RIGHT_SHOULDER,
XINPUT_GAMEPAD_BACK,
XINPUT_GAMEPAD_START,
XINPUT_GAMEPAD_LEFT_THUMB,
XINPUT_GAMEPAD_RIGHT_THUMB,
XINPUT_GAMEPAD_DPAD_UP,
XINPUT_GAMEPAD_DPAD_RIGHT,
XINPUT_GAMEPAD_DPAD_DOWN,
XINPUT_GAMEPAD_DPAD_LEFT
};
result = XInputGetState(js->index, &xis);
if (result != ERROR_SUCCESS)
{
if (result == ERROR_DEVICE_NOT_CONNECTED)
closeJoystick(js);
return GLFW_FALSE;
}
if (mode == _GLFW_PRESENCE_ONLY)
return GLFW_TRUE;
if ((float) xis.Gamepad.sThumbLX * xis.Gamepad.sThumbLX +
(float) xis.Gamepad.sThumbLY * xis.Gamepad.sThumbLY >
(float) XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE *
XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
{
js->axes[0] = (xis.Gamepad.sThumbLX + 0.5f) / 32767.f;
js->axes[1] = (xis.Gamepad.sThumbLY + 0.5f) / 32767.f;
}
else
{
js->axes[0] = 0.f;
js->axes[1] = 0.f;
}
if ((float) xis.Gamepad.sThumbRX * xis.Gamepad.sThumbRX +
(float) xis.Gamepad.sThumbRY * xis.Gamepad.sThumbRY >
(float) XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE *
XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
{
js->axes[2] = (xis.Gamepad.sThumbRX + 0.5f) / 32767.f;
js->axes[3] = (xis.Gamepad.sThumbRY + 0.5f) / 32767.f;
}
else
{
js->axes[2] = 0.f;
js->axes[3] = 0.f;
}
if (xis.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
js->axes[4] = xis.Gamepad.bLeftTrigger / 127.5f - 1.f;
else
js->axes[4] = -1.f;
if (xis.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
js->axes[5] = xis.Gamepad.bRightTrigger / 127.5f - 1.f;
else
js->axes[5] = -1.f;
for (i = 0; i < 14; i++)
js->buttons[i] = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0;
return GLFW_TRUE;
}
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
////// GLFW internal API ////// ////// GLFW internal API //////
@ -679,8 +475,8 @@ void _glfwTerminateJoysticksWin32(void)
{ {
int jid; int jid;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++)
closeJoystick(_glfw.win32_js + jid); closeJoystick(_glfw.joysticks + jid);
if (_glfw.win32.dinput8.api) if (_glfw.win32.dinput8.api)
IDirectInput8_Release(_glfw.win32.dinput8.api); IDirectInput8_Release(_glfw.win32.dinput8.api);
@ -692,10 +488,38 @@ void _glfwDetectJoystickConnectionWin32(void)
{ {
if (_glfw.win32.xinput.instance) if (_glfw.win32.xinput.instance)
{ {
DWORD i; DWORD index;
for (i = 0; i < XUSER_MAX_COUNT; i++) for (index = 0; index < XUSER_MAX_COUNT; index++)
openXinputDevice(i); {
int jid;
XINPUT_CAPABILITIES xic;
_GLFWjoystick* js;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
if (_glfw.joysticks[jid].present &&
_glfw.joysticks[jid].win32.device == NULL &&
_glfw.joysticks[jid].win32.index == index)
{
break;
}
}
if (jid <= GLFW_JOYSTICK_LAST)
continue;
if (XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS)
continue;
js = _glfwAllocJoystick(getDeviceDescription(&xic), 6, 14);
if (!js)
continue;
js->win32.index = index;
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_CONNECTED);
}
} }
if (_glfw.win32.dinput8.api) if (_glfw.win32.dinput8.api)
@ -720,7 +544,10 @@ void _glfwDetectJoystickDisconnectionWin32(void)
int jid; int jid;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
pollJoystickState(_glfw.win32_js + jid, _GLFW_PRESENCE_ONLY); {
if (_glfw.joysticks[jid].present)
_glfwPlatformPollJoystick(jid, _GLFW_POLL_PRESENCE);
}
} }
@ -728,38 +555,151 @@ void _glfwDetectJoystickDisconnectionWin32(void)
////// GLFW platform API ////// ////// GLFW platform API //////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
int _glfwPlatformJoystickPresent(int jid) int _glfwPlatformPollJoystick(int jid, int mode)
{ {
_GLFWjoystickWin32* js = _glfw.win32_js + jid; _GLFWjoystick* js = _glfw.joysticks + jid;
return pollJoystickState(js, _GLFW_PRESENCE_ONLY);
if (js->win32.device)
{
int i, j, ai = 0, bi = 0;
HRESULT result;
DIJOYSTATE state;
IDirectInputDevice8_Poll(js->win32.device);
result = IDirectInputDevice8_GetDeviceState(js->win32.device,
sizeof(state),
&state);
if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST)
{
IDirectInputDevice8_Acquire(js->win32.device);
IDirectInputDevice8_Poll(js->win32.device);
result = IDirectInputDevice8_GetDeviceState(js->win32.device,
sizeof(state),
&state);
} }
const float* _glfwPlatformGetJoystickAxes(int jid, int* count) if (FAILED(result))
{ {
_GLFWjoystickWin32* js = _glfw.win32_js + jid; closeJoystick(js);
if (!pollJoystickState(js, _GLFW_UPDATE_STATE)) return GLFW_FALSE;
return NULL;
*count = js->axisCount;
return js->axes;
} }
const unsigned char* _glfwPlatformGetJoystickButtons(int jid, int* count) if (mode == _GLFW_POLL_PRESENCE)
{ return GLFW_TRUE;
_GLFWjoystickWin32* js = _glfw.win32_js + jid;
if (!pollJoystickState(js, _GLFW_UPDATE_STATE))
return NULL;
*count = js->buttonCount; for (i = 0; i < js->win32.objectCount; i++)
return js->buttons; {
const void* data = (char*) &state + js->win32.objects[i].offset;
switch (js->win32.objects[i].type)
{
case _GLFW_TYPE_AXIS:
case _GLFW_TYPE_SLIDER:
{
const float value = (*((LONG*) data) + 0.5f) / 32767.5f;
_glfwInputJoystickAxis(jid, ai, value);
ai++;
break;
} }
const char* _glfwPlatformGetJoystickName(int jid) case _GLFW_TYPE_BUTTON:
{ {
_GLFWjoystickWin32* js = _glfw.win32_js + jid; const char value = (*((BYTE*) data) & 0x80) != 0;
if (!pollJoystickState(js, _GLFW_PRESENCE_ONLY)) _glfwInputJoystickButton(jid, bi, value);
return NULL; bi++;
break;
return js->name; }
case _GLFW_TYPE_POV:
{
const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 };
// Screams of horror are appropriate at this point
int state = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES);
if (state < 0 || state > 8)
state = 8;
for (j = 0; j < 4; j++)
{
const char value = (directions[state] & (1 << j)) != 0;
_glfwInputJoystickButton(jid, bi, value);
bi++;
}
break;
}
}
}
}
else
{
int i;
DWORD result;
XINPUT_STATE xis;
float axes[6] = { 0.f, 0.f, 0.f, 0.f, -1.f, -1.f };
const WORD buttons[14] =
{
XINPUT_GAMEPAD_A,
XINPUT_GAMEPAD_B,
XINPUT_GAMEPAD_X,
XINPUT_GAMEPAD_Y,
XINPUT_GAMEPAD_LEFT_SHOULDER,
XINPUT_GAMEPAD_RIGHT_SHOULDER,
XINPUT_GAMEPAD_BACK,
XINPUT_GAMEPAD_START,
XINPUT_GAMEPAD_LEFT_THUMB,
XINPUT_GAMEPAD_RIGHT_THUMB,
XINPUT_GAMEPAD_DPAD_UP,
XINPUT_GAMEPAD_DPAD_RIGHT,
XINPUT_GAMEPAD_DPAD_DOWN,
XINPUT_GAMEPAD_DPAD_LEFT
};
result = XInputGetState(js->win32.index, &xis);
if (result != ERROR_SUCCESS)
{
if (result == ERROR_DEVICE_NOT_CONNECTED)
closeJoystick(js);
return GLFW_FALSE;
}
if (mode == _GLFW_POLL_PRESENCE)
return GLFW_TRUE;
if ((float) xis.Gamepad.sThumbLX * xis.Gamepad.sThumbLX +
(float) xis.Gamepad.sThumbLY * xis.Gamepad.sThumbLY >
(float) XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE *
XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
{
axes[0] = (xis.Gamepad.sThumbLX + 0.5f) / 32767.f;
axes[1] = (xis.Gamepad.sThumbLY + 0.5f) / 32767.f;
}
if ((float) xis.Gamepad.sThumbRX * xis.Gamepad.sThumbRX +
(float) xis.Gamepad.sThumbRY * xis.Gamepad.sThumbRY >
(float) XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE *
XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
{
axes[2] = (xis.Gamepad.sThumbRX + 0.5f) / 32767.f;
axes[3] = (xis.Gamepad.sThumbRY + 0.5f) / 32767.f;
}
if (xis.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
axes[4] = xis.Gamepad.bLeftTrigger / 127.5f - 1.f;
if (xis.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
axes[5] = xis.Gamepad.bRightTrigger / 127.5f - 1.f;
for (i = 0; i < 6; i++)
_glfwInputJoystickAxis(jid, i, axes[i]);
for (i = 0; i < 14; i++)
{
const char value = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0;
_glfwInputJoystickButton(jid, i, value);
}
}
return GLFW_TRUE;
} }

View File

@ -27,8 +27,9 @@
#ifndef _glfw3_win32_joystick_h_ #ifndef _glfw3_win32_joystick_h_
#define _glfw3_win32_joystick_h_ #define _glfw3_win32_joystick_h_
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \ #define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickWin32 win32
_GLFWjoystickWin32 win32_js[GLFW_JOYSTICK_LAST + 1] #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE int dummy
// Joystick element (axis, button or slider) // Joystick element (axis, button or slider)
// //
@ -42,14 +43,8 @@ typedef struct _GLFWjoyobjectWin32
// //
typedef struct _GLFWjoystickWin32 typedef struct _GLFWjoystickWin32
{ {
GLFWbool present;
float* axes;
int axisCount;
unsigned char* buttons;
int buttonCount;
_GLFWjoyobjectWin32* objects; _GLFWjoyobjectWin32* objects;
int objectCount; int objectCount;
char* name;
IDirectInputDevice8W* device; IDirectInputDevice8W* device;
DWORD index; DWORD index;
GUID guid; GUID guid;

View File

@ -62,10 +62,10 @@ static GLFWbool waitForEvent(double* timeout)
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(fd, &fds); FD_SET(fd, &fds);
#if defined(__linux__) #if defined(__linux__)
FD_SET(_glfw.linux_js.inotify, &fds); FD_SET(_glfw.linjs.inotify, &fds);
if (fd < _glfw.linux_js.inotify) if (fd < _glfw.linjs.inotify)
count = _glfw.linux_js.inotify + 1; count = _glfw.linjs.inotify + 1;
#endif #endif
for (;;) for (;;)
{ {
@ -2153,7 +2153,7 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
void _glfwPlatformPollEvents(void) void _glfwPlatformPollEvents(void)
{ {
_glfwPollJoystickEvents(); _glfwDetectJoystickConnectionLinux();
int count = XPending(_glfw.x11.display); int count = XPending(_glfw.x11.display);
while (count--) while (count--)