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/IOHIDKeys.h>
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \
_GLFWjoystickNS ns_js[GLFW_JOYSTICK_LAST + 1]
#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickNS ns
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE
// Cocoa-specific per-joystick data
//
typedef struct _GLFWjoystickNS
{
GLFWbool present;
char name[256];
IOHIDDeviceRef deviceRef;
CFMutableArrayRef axisElements;
CFMutableArrayRef buttonElements;
CFMutableArrayRef hatElements;
float* axes;
unsigned char* buttons;
IOHIDDeviceRef device;
CFMutableArrayRef axes;
CFMutableArrayRef buttons;
CFMutableArrayRef hats;
} _GLFWjoystickNS;

View File

@ -42,44 +42,127 @@
//
typedef struct _GLFWjoyelementNS
{
IOHIDElementRef elementRef;
long min;
long max;
long minReport;
long maxReport;
IOHIDElementRef native;
long minimum;
long maximum;
} _GLFWjoyelementNS;
static void getElementsCFArrayHandler(const void* value, void* parameter);
// Adds an element to the specified joystick
// Returns the value of the specified element of the specified joystick
//
static void addJoystickElement(_GLFWjoystickNS* js,
IOHIDElementRef elementRef)
static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element)
{
IOHIDElementType elementType;
long usagePage, usage;
CFMutableArrayRef elementsArray = NULL;
IOReturn result = kIOReturnSuccess;
IOHIDValueRef valueRef;
long value = 0;
elementType = IOHIDElementGetType(elementRef);
usagePage = IOHIDElementGetUsagePage(elementRef);
usage = IOHIDElementGetUsage(elementRef);
if ((elementType != kIOHIDElementTypeInput_Axis) &&
(elementType != kIOHIDElementTypeInput_Button) &&
(elementType != kIOHIDElementTypeInput_Misc))
if (js && element && js->ns.device)
{
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;
}
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:
{
switch (usage)
switch (IOHIDElementGetUsage(native))
{
case kHIDUsage_GD_X:
case kHIDUsage_GD_Y:
@ -90,10 +173,10 @@ static void addJoystickElement(_GLFWjoystickNS* js,
case kHIDUsage_GD_Slider:
case kHIDUsage_GD_Dial:
case kHIDUsage_GD_Wheel:
elementsArray = js->axisElements;
target = axes;
break;
case kHIDUsage_GD_Hatswitch:
elementsArray = js->hatElements;
target = hats;
break;
}
@ -101,230 +184,34 @@ static void addJoystickElement(_GLFWjoystickNS* js,
}
case kHIDPage_Button:
elementsArray = js->buttonElements;
target = buttons;
break;
default:
break;
}
if (elementsArray)
if (target)
{
_GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS));
CFArrayAppendValue(elementsArray, element);
element->elementRef = elementRef;
element->minReport = IOHIDElementGetLogicalMin(elementRef);
element->maxReport = IOHIDElementGetLogicalMax(elementRef);
element->native = native;
element->minimum = IOHIDElementGetLogicalMin(native);
element->maximum = IOHIDElementGetLogicalMax(native);
CFArrayAppendValue(target, element);
}
}
// Adds an element to the specified joystick
//
static void getElementsCFArrayHandler(const void* value, void* parameter)
{
if (CFGetTypeID(value) == IOHIDElementGetTypeID())
{
addJoystickElement((_GLFWjoystickNS*) parameter,
(IOHIDElementRef) value);
}
}
CFRelease(elements);
// Returns the value of the specified element of the specified joystick
//
static long getElementValue(_GLFWjoystickNS* js, _GLFWjoyelementNS* element)
{
IOReturn result = kIOReturnSuccess;
IOHIDValueRef valueRef;
long value = 0;
js = _glfwAllocJoystick(name,
CFArrayGetCount(axes),
CFArrayGetCount(buttons) + CFArrayGetCount(hats) * 4);
if (js && element && js->deviceRef)
{
result = IOHIDDeviceGetValue(js->deviceRef,
element->elementRef,
&valueRef);
js->ns.device = device;
js->ns.axes = axes;
js->ns.buttons = buttons;
js->ns.hats = hats;
if (kIOReturnSuccess == result)
{
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);
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_CONNECTED);
}
// Callback for user-initiated joystick removal
@ -332,15 +219,15 @@ static void matchCallback(void* context,
static void removeCallback(void* context,
IOReturn result,
void* sender,
IOHIDDeviceRef deviceRef)
IOHIDDeviceRef device)
{
int 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;
}
}
@ -459,10 +346,7 @@ void _glfwTerminateJoysticksNS(void)
int jid;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
_GLFWjoystickNS* js = _glfw.ns_js + jid;
removeJoystick(js);
}
closeJoystick(_glfw.joysticks + jid);
CFRelease(_glfw.ns.hidManager);
_glfw.ns.hidManager = NULL;
@ -473,39 +357,60 @@ void _glfwTerminateJoysticksNS(void)
////// 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;
}
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 <float.h>
#include <stdlib.h>
#include <string.h>
// Internal key state used for sticky keys
#define _GLFW_STICK 3
@ -123,12 +124,22 @@ void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths)
window->callbacks.drop((GLFWwindow*) window, count, paths);
}
void _glfwInputJoystickChange(int jid, int event)
void _glfwInputJoystick(int jid, int event)
{
if (_glfw.callbacks.joystick)
_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 //////
@ -141,6 +152,39 @@ GLFWbool _glfwIsPrintable(int key)
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 //////
@ -554,20 +598,29 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun)
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)
{
_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)
{
assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST);
assert(count != NULL);
*count = 0;
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
@ -578,12 +631,22 @@ GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count)
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)
{
assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST);
assert(count != NULL);
*count = 0;
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
@ -594,11 +657,21 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count)
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)
{
assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
@ -607,7 +680,13 @@ GLFWAPI const char* glfwGetJoystickName(int jid)
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)

View File

@ -53,6 +53,10 @@
#define _GLFW_INSERT_FIRST 0
#define _GLFW_INSERT_LAST 1
#define _GLFW_POLL_PRESENCE 0
#define _GLFW_POLL_AXES 1
#define _GLFW_POLL_BUTTONS 2
typedef int GLFWbool;
typedef struct _GLFWwndconfig _GLFWwndconfig;
@ -63,6 +67,7 @@ typedef struct _GLFWwindow _GLFWwindow;
typedef struct _GLFWlibrary _GLFWlibrary;
typedef struct _GLFWmonitor _GLFWmonitor;
typedef struct _GLFWcursor _GLFWcursor;
typedef struct _GLFWjoystick _GLFWjoystick;
typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*);
typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*);
@ -246,6 +251,9 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void);
y = t; \
}
// Maps a joystick pointer to an ID
#define _GLFW_JOYSTICK_ID(js) ((int) ((js) - _glfw.joysticks))
//========================================================================
// Platform-independent structures
@ -440,6 +448,21 @@ struct _GLFWcursor
_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.
*/
struct _GLFWlibrary
@ -458,6 +481,8 @@ struct _GLFWlibrary
_GLFWmonitor** monitors;
int monitorCount;
_GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1];
uint64_t timerOffset;
struct {
@ -605,25 +630,9 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string);
*/
const char* _glfwPlatformGetClipboardString(_GLFWwindow* window);
/*! @copydoc glfwJoystickPresent
* @ingroup platform
/*! @ingroup platform
*/
int _glfwPlatformJoystickPresent(int jid);
/*! @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);
int _glfwPlatformPollJoystick(int jid, int mode);
/*! @copydoc glfwGetTimerValue
* @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`.
* @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);
/*! @ingroup utility
*/
_GLFWjoystick* _glfwAllocJoystick(const char* name, int axisCount, int buttonCount);
/*! @ingroup utility
*/
void _glfwFreeJoystick(_GLFWjoystick* js);
/*! @ingroup utility
*/
GLFWbool _glfwIsPrintable(int key);

View File

@ -51,26 +51,16 @@ static GLFWbool openJoystickDevice(const char* path)
char axisCount, buttonCount;
char name[256] = "";
int jid, fd, version;
_GLFWjoystickLinux* js;
_GLFWjoystick* js;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
{
if (!_glfw.linux_js.js[jid].present)
if (!_glfw.joysticks[jid].present)
continue;
if (strcmp(_glfw.linux_js.js[jid].path, path) == 0)
if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0)
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);
if (fd == -1)
return GLFW_FALSE;
@ -87,80 +77,44 @@ static GLFWbool openJoystickDevice(const char* path)
if (ioctl(fd, JSIOCGNAME(sizeof(name)), name) < 0)
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);
js->axisCount = (int) axisCount;
js->axes = calloc(axisCount, sizeof(float));
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;
}
#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__)
_glfwPollJoystickEvents();
if (!js->present)
return GLFW_FALSE;
// Read all queued events (non-blocking)
for (;;)
static void closeJoystick(_GLFWjoystick* js)
{
struct js_event e;
errno = 0;
if (read(js->fd, &e, sizeof(e)) < 0)
{
// 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;
close(js->linjs.fd);
free(js->linjs.path);
_glfwFreeJoystick(js);
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_DISCONNECTED);
}
#endif // __linux__
return js->present;
}
// Lexically compare joysticks by name; used by qsort
//
#if defined(__linux__)
static int compareJoysticks(const void* fp, const void* sp)
{
const _GLFWjoystickLinux* fj = fp;
const _GLFWjoystickLinux* sj = sp;
return strcmp(fj->path, sj->path);
const _GLFWjoystick* fj = fp;
const _GLFWjoystick* sj = sp;
return strcmp(fj->linjs.path, sj->linjs.path);
}
#endif // __linux__
@ -178,8 +132,8 @@ GLFWbool _glfwInitJoysticksLinux(void)
int count = 0;
const char* dirname = "/dev/input";
_glfw.linux_js.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (_glfw.linux_js.inotify == -1)
_glfw.linjs.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (_glfw.linjs.inotify == -1)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"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
// 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,
IN_CREATE | IN_ATTRIB);
if (_glfw.linux_js.watch == -1)
if (_glfw.linjs.watch == -1)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Linux: Failed to watch for joystick connections in %s: %s",
@ -202,7 +156,7 @@ GLFWbool _glfwInitJoysticksLinux(void)
// 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");
return GLFW_FALSE;
@ -218,7 +172,7 @@ GLFWbool _glfwInitJoysticksLinux(void)
char path[20];
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;
snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name);
@ -237,7 +191,7 @@ GLFWbool _glfwInitJoysticksLinux(void)
// Continue with no joysticks detected
}
qsort(_glfw.linux_js.js, count, sizeof(_GLFWjoystickLinux), compareJoysticks);
qsort(_glfw.joysticks, count, sizeof(_GLFWjoystick), compareJoysticks);
#endif // __linux__
return GLFW_TRUE;
@ -248,46 +202,41 @@ GLFWbool _glfwInitJoysticksLinux(void)
void _glfwTerminateJoysticksLinux(void)
{
#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)
{
close(_glfw.linux_js.js[i].fd);
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);
}
_GLFWjoystick* js = _glfw.joysticks + jid;
if (js->present)
closeJoystick(js);
}
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)
inotify_rm_watch(_glfw.linux_js.inotify, _glfw.linux_js.watch);
if (_glfw.linjs.watch > 0)
inotify_rm_watch(_glfw.linjs.inotify, _glfw.linjs.watch);
close(_glfw.linux_js.inotify);
close(_glfw.linjs.inotify);
}
#endif // __linux__
}
void _glfwPollJoystickEvents(void)
void _glfwDetectJoystickConnectionLinux(void)
{
#if defined(__linux__)
ssize_t offset = 0;
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)
{
regmatch_t match;
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];
snprintf(path, sizeof(path), "/dev/input/%s", e->name);
@ -304,38 +253,35 @@ void _glfwPollJoystickEvents(void)
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
int _glfwPlatformJoystickPresent(int jid)
int _glfwPlatformPollJoystick(int jid, int mode)
{
_GLFWjoystickLinux* js = _glfw.linux_js.js + jid;
return pollJoystickEvents(js);
#if defined(__linux__)
_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)
{
_GLFWjoystickLinux* js = _glfw.linux_js.js + jid;
if (!pollJoystickEvents(js))
return NULL;
// Clear the initial-state bit
e.type &= ~JS_EVENT_INIT;
*count = js->axisCount;
return js->axes;
}
const unsigned char* _glfwPlatformGetJoystickButtons(int jid, int* count)
{
_GLFWjoystickLinux* js = _glfw.linux_js.js + jid;
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;
if (e.type == JS_EVENT_AXIS)
_glfwInputJoystickAxis(jid, e.number, e.value / 32767.0f);
else if (e.type == JS_EVENT_BUTTON)
_glfwInputJoystickButton(jid, e.number, e.value ? 1 : 0);
}
#endif // __linux__
return js->present;
}

View File

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

View File

@ -269,26 +269,11 @@ int _glfwPlatformGetKeyScancode(int key)
return -1;
}
int _glfwPlatformJoystickPresent(int joy)
int _glfwPlatformPollJoystick(int jid, int mode)
{
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)
{
}

View File

@ -31,9 +31,6 @@
#include <initguid.h>
#define _GLFW_PRESENCE_ONLY 1
#define _GLFW_UPDATE_STATE 2
#define _GLFW_TYPE_AXIS 0
#define _GLFW_TYPE_SLIDER 1
#define _GLFW_TYPE_BUTTON 2
@ -238,21 +235,16 @@ static GLFWbool supportsXInput(const GUID* guid)
// 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_Release(js->device);
IDirectInputDevice8_Unacquire(js->win32.device);
IDirectInputDevice8_Release(js->win32.device);
}
free(js->name);
free(js->axes);
free(js->buttons);
free(js->objects);
memset(js, 0, sizeof(_GLFWjoystickWin32));
_glfwInputJoystickChange((int) (js - _glfw.win32_js), GLFW_DISCONNECTED);
_glfwFreeJoystick(js);
_glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_DISCONNECTED);
}
// DirectInput device object enumeration callback
@ -337,23 +329,17 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user)
DIPROPDWORD dipd;
IDirectInputDevice8* device;
_GLFWobjenumWin32 data;
_GLFWjoystickWin32* js;
_GLFWjoystick* js;
char name[256];
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;
}
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))
return DIENUM_CONTINUE;
@ -426,228 +412,38 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user)
sizeof(_GLFWjoyobjectWin32),
compareJoystickObjects);
js = _glfw.win32_js + jid;
js->device = device;
js->guid = di->guidInstance;
js->axisCount = data.axisCount + data.sliderCount;
js->axes = calloc(js->axisCount, sizeof(float));
js->buttonCount += data.buttonCount + data.povCount * 4;
js->buttons = calloc(js->buttonCount, 1);
js->objects = data.objects;
js->objectCount = data.objectCount;
js->name = _glfwCreateUTF8FromWideStringWin32(di->tszInstanceName);
js->present = GLFW_TRUE;
if (!WideCharToMultiByte(CP_UTF8, 0,
di->tszInstanceName, -1,
name, sizeof(name),
NULL, NULL))
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Win32: Failed to convert joystick name to UTF-8");
_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;
}
// 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 //////
@ -679,8 +475,8 @@ void _glfwTerminateJoysticksWin32(void)
{
int jid;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
closeJoystick(_glfw.win32_js + jid);
for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++)
closeJoystick(_glfw.joysticks + jid);
if (_glfw.win32.dinput8.api)
IDirectInput8_Release(_glfw.win32.dinput8.api);
@ -692,10 +488,38 @@ void _glfwDetectJoystickConnectionWin32(void)
{
if (_glfw.win32.xinput.instance)
{
DWORD i;
DWORD index;
for (i = 0; i < XUSER_MAX_COUNT; i++)
openXinputDevice(i);
for (index = 0; index < XUSER_MAX_COUNT; index++)
{
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)
@ -720,7 +544,10 @@ void _glfwDetectJoystickDisconnectionWin32(void)
int 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 //////
//////////////////////////////////////////////////////////////////////////
int _glfwPlatformJoystickPresent(int jid)
int _glfwPlatformPollJoystick(int jid, int mode)
{
_GLFWjoystickWin32* js = _glfw.win32_js + jid;
return pollJoystickState(js, _GLFW_PRESENCE_ONLY);
_GLFWjoystick* js = _glfw.joysticks + jid;
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;
if (!pollJoystickState(js, _GLFW_UPDATE_STATE))
return NULL;
*count = js->axisCount;
return js->axes;
closeJoystick(js);
return GLFW_FALSE;
}
const unsigned char* _glfwPlatformGetJoystickButtons(int jid, int* count)
{
_GLFWjoystickWin32* js = _glfw.win32_js + jid;
if (!pollJoystickState(js, _GLFW_UPDATE_STATE))
return NULL;
if (mode == _GLFW_POLL_PRESENCE)
return GLFW_TRUE;
*count = js->buttonCount;
return js->buttons;
for (i = 0; i < js->win32.objectCount; i++)
{
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;
if (!pollJoystickState(js, _GLFW_PRESENCE_ONLY))
return NULL;
return js->name;
const char value = (*((BYTE*) data) & 0x80) != 0;
_glfwInputJoystickButton(jid, bi, value);
bi++;
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 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_
#define _glfw3_win32_joystick_h_
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \
_GLFWjoystickWin32 win32_js[GLFW_JOYSTICK_LAST + 1]
#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickWin32 win32
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE int dummy
// Joystick element (axis, button or slider)
//
@ -42,14 +43,8 @@ typedef struct _GLFWjoyobjectWin32
//
typedef struct _GLFWjoystickWin32
{
GLFWbool present;
float* axes;
int axisCount;
unsigned char* buttons;
int buttonCount;
_GLFWjoyobjectWin32* objects;
int objectCount;
char* name;
IDirectInputDevice8W* device;
DWORD index;
GUID guid;

View File

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