diff --git a/src/cocoa_joystick.h b/src/cocoa_joystick.h index 9b8ff3c3..9cb12e65 100644 --- a/src/cocoa_joystick.h +++ b/src/cocoa_joystick.h @@ -32,25 +32,18 @@ #include #include -#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; diff --git a/src/cocoa_joystick.m b/src/cocoa_joystick.m index 557108fc..cec07336 100644 --- a/src/cocoa_joystick.m +++ b/src/cocoa_joystick.m @@ -42,107 +42,25 @@ // 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 -// -static void addJoystickElement(_GLFWjoystickNS* js, - IOHIDElementRef elementRef) -{ - IOHIDElementType elementType; - long usagePage, usage; - CFMutableArrayRef elementsArray = NULL; - - elementType = IOHIDElementGetType(elementRef); - usagePage = IOHIDElementGetUsagePage(elementRef); - usage = IOHIDElementGetUsage(elementRef); - - if ((elementType != kIOHIDElementTypeInput_Axis) && - (elementType != kIOHIDElementTypeInput_Button) && - (elementType != kIOHIDElementTypeInput_Misc)) - { - return; - } - - switch (usagePage) - { - case kHIDPage_GenericDesktop: - { - switch (usage) - { - case kHIDUsage_GD_X: - case kHIDUsage_GD_Y: - case kHIDUsage_GD_Z: - case kHIDUsage_GD_Rx: - case kHIDUsage_GD_Ry: - case kHIDUsage_GD_Rz: - case kHIDUsage_GD_Slider: - case kHIDUsage_GD_Dial: - case kHIDUsage_GD_Wheel: - elementsArray = js->axisElements; - break; - case kHIDUsage_GD_Hatswitch: - elementsArray = js->hatElements; - break; - } - - break; - } - - case kHIDPage_Button: - elementsArray = js->buttonElements; - break; - default: - break; - } - - if (elementsArray) - { - _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS)); - - CFArrayAppendValue(elementsArray, element); - - element->elementRef = elementRef; - - element->minReport = IOHIDElementGetLogicalMin(elementRef); - element->maxReport = IOHIDElementGetLogicalMax(elementRef); - } -} - -// Adds an element to the specified joystick -// -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 // -static long getElementValue(_GLFWjoystickNS* js, _GLFWjoyelementNS* element) +static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element) { IOReturn result = kIOReturnSuccess; IOHIDValueRef valueRef; long value = 0; - if (js && element && js->deviceRef) + if (js && element && js->ns.device) { - result = IOHIDDeviceGetValue(js->deviceRef, - element->elementRef, + result = IOHIDDeviceGetValue(js->ns.device, + element->native, &valueRef); if (kIOReturnSuccess == result) @@ -150,10 +68,10 @@ static long getElementValue(_GLFWjoystickNS* js, _GLFWjoyelementNS* element) value = IOHIDValueGetIntegerValue(valueRef); // Record min and max for auto calibration - if (value < element->minReport) - element->minReport = value; - if (value > element->maxReport) - element->maxReport = value; + if (value < element->minimum) + element->minimum = value; + if (value > element->maximum) + element->maximum = value; } } @@ -163,105 +81,27 @@ static long getElementValue(_GLFWjoystickNS* js, _GLFWjoyelementNS* element) // Removes the specified joystick // -static void removeJoystick(_GLFWjoystickNS* js) +static void closeJoystick(_GLFWjoystick* 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->ns.axes); i++) + free((void*) CFArrayGetValueAtIndex(js->ns.axes, i)); + CFRelease(js->ns.axes); - 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->ns.buttons); i++) + free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i)); + CFRelease(js->ns.buttons); - for (i = 0; i < CFArrayGetCount(js->hatElements); i++) - free((void*) CFArrayGetValueAtIndex(js->hatElements, i)); - CFArrayRemoveAllValues(js->hatElements); - CFRelease(js->hatElements); + for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) + free((void*) CFArrayGetValueAtIndex(js->ns.hats, i)); + CFRelease(js->ns.hats); - 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; + _glfwFreeJoystick(js); + _glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_DISCONNECTED); } // Callback for user-initiated joystick addition @@ -269,62 +109,109 @@ static GLFWbool pollJoystickButtonEvents(_GLFWjoystickNS* js) static void matchCallback(void* context, IOReturn result, void* sender, - IOHIDDeviceRef deviceRef) + IOHIDDeviceRef device) { - _GLFWjoystickNS* js; 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.ns_js[jid].present && _glfw.ns_js[jid].deviceRef == deviceRef) + if (_glfw.joysticks[jid].ns.device == device) return; } - for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + axes = CFArrayCreateMutable(NULL, 0, NULL); + buttons = CFArrayCreateMutable(NULL, 0, NULL); + hats = CFArrayCreateMutable(NULL, 0, NULL); + + productKey = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); + if (productKey) { - 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), + CFStringGetCString(productKey, + name, + sizeof(name), kCFStringEncodingUTF8); } else - strncpy(js->name, "Unknown", sizeof(js->name)); + strncpy(name, "Unknown", sizeof(name)); - js->axisElements = CFArrayCreateMutable(NULL, 0, NULL); - js->buttonElements = CFArrayCreateMutable(NULL, 0, NULL); - js->hatElements = CFArrayCreateMutable(NULL, 0, NULL); + CFArrayRef elements = + IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); - CFArrayRef arrayRef = IOHIDDeviceCopyMatchingElements(deviceRef, - NULL, - kIOHIDOptionsTypeNone); - CFRange range = { 0, CFArrayGetCount(arrayRef) }; - CFArrayApplyFunction(arrayRef, - range, - getElementsCFArrayHandler, - (void*) js); + for (i = 0; i < CFArrayGetCount(elements); i++) + { + IOHIDElementRef native = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, i); + if (CFGetTypeID(native) != IOHIDElementGetTypeID()) + continue; - CFRelease(arrayRef); + const IOHIDElementType type = IOHIDElementGetType(native); + if ((type != kIOHIDElementTypeInput_Axis) && + (type != kIOHIDElementTypeInput_Button) && + (type != kIOHIDElementTypeInput_Misc)) + { + continue; + } - js->axes = calloc(CFArrayGetCount(js->axisElements), sizeof(float)); - js->buttons = calloc(CFArrayGetCount(js->buttonElements) + - CFArrayGetCount(js->hatElements) * 4, 1); + CFMutableArrayRef target = NULL; - _glfwInputJoystickChange(jid, GLFW_CONNECTED); + switch (IOHIDElementGetUsagePage(native)) + { + case kHIDPage_GenericDesktop: + { + switch (IOHIDElementGetUsage(native)) + { + case kHIDUsage_GD_X: + case kHIDUsage_GD_Y: + case kHIDUsage_GD_Z: + case kHIDUsage_GD_Rx: + case kHIDUsage_GD_Ry: + case kHIDUsage_GD_Rz: + case kHIDUsage_GD_Slider: + case kHIDUsage_GD_Dial: + case kHIDUsage_GD_Wheel: + target = axes; + break; + case kHIDUsage_GD_Hatswitch: + target = hats; + break; + } + + break; + } + + case kHIDPage_Button: + target = buttons; + break; + default: + break; + } + + if (target) + { + _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS)); + element->native = native; + element->minimum = IOHIDElementGetLogicalMin(native); + element->maximum = IOHIDElementGetLogicalMax(native); + CFArrayAppendValue(target, element); + } + } + + CFRelease(elements); + + js = _glfwAllocJoystick(name, + CFArrayGetCount(axes), + CFArrayGetCount(buttons) + CFArrayGetCount(hats) * 4); + + js->ns.device = device; + js->ns.axes = axes; + js->ns.buttons = buttons; + js->ns.hats = hats; + + _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; -} - diff --git a/src/input.c b/src/input.c index f35b606c..923e3fca 100644 --- a/src/input.c +++ b/src/input.c @@ -30,6 +30,7 @@ #include #include #include +#include // 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) diff --git a/src/internal.h b/src/internal.h index cf14b7df..f231df71 100644 --- a/src/internal.h +++ b/src/internal.h @@ -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); diff --git a/src/linux_joystick.c b/src/linux_joystick.c index d4d9e1e2..7a7b4561 100644 --- a/src/linux_joystick.c +++ b/src/linux_joystick.c @@ -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 (;;) - { - 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; - } -#endif // __linux__ - return js->present; +static void closeJoystick(_GLFWjoystick* js) +{ + close(js->linjs.fd); + free(js->linjs.path); + _glfwFreeJoystick(js); + _glfwInputJoystick(_GLFW_JOYSTICK_ID(js), GLFW_DISCONNECTED); } +#endif // __linux__ // 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, - dirname, - IN_CREATE | IN_ATTRIB); - if (_glfw.linux_js.watch == -1) + _glfw.linjs.watch = inotify_add_watch(_glfw.linjs.inotify, + dirname, + IN_CREATE | IN_ATTRIB); + 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); -} - -const float* _glfwPlatformGetJoystickAxes(int jid, int* count) -{ - _GLFWjoystickLinux* js = _glfw.linux_js.js + jid; - if (!pollJoystickEvents(js)) - return NULL; - - *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 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; + } + + // Clear the initial-state bit + e.type &= ~JS_EVENT_INIT; + + 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; } diff --git a/src/linux_joystick.h b/src/linux_joystick.h index 4187b137..e61a3c6a 100644 --- a/src/linux_joystick.h +++ b/src/linux_joystick.h @@ -29,40 +29,32 @@ #include -#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_ diff --git a/src/osmesa_window.c b/src/osmesa_window.c index 33649246..20838f55 100644 --- a/src/osmesa_window.c +++ b/src/osmesa_window.c @@ -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) { } diff --git a/src/win32_joystick.c b/src/win32_joystick.c index 9e536e56..ea20e8c6 100644 --- a/src/win32_joystick.c +++ b/src/win32_joystick.c @@ -31,9 +31,6 @@ #include -#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) @@ -719,8 +543,11 @@ void _glfwDetectJoystickDisconnectionWin32(void) { int jid; - for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) - pollJoystickState(_glfw.win32_js + jid, _GLFW_PRESENCE_ONLY); + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + 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); -} - -const float* _glfwPlatformGetJoystickAxes(int jid, int* count) -{ - _GLFWjoystickWin32* js = _glfw.win32_js + jid; - if (!pollJoystickState(js, _GLFW_UPDATE_STATE)) - return NULL; - - *count = js->axisCount; - return js->axes; -} - -const unsigned char* _glfwPlatformGetJoystickButtons(int jid, int* count) -{ - _GLFWjoystickWin32* js = _glfw.win32_js + jid; - if (!pollJoystickState(js, _GLFW_UPDATE_STATE)) - return NULL; - - *count = js->buttonCount; - return js->buttons; -} - -const char* _glfwPlatformGetJoystickName(int jid) -{ - _GLFWjoystickWin32* js = _glfw.win32_js + jid; - if (!pollJoystickState(js, _GLFW_PRESENCE_ONLY)) - return NULL; - - return js->name; + _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); + } + + if (FAILED(result)) + { + closeJoystick(js); + return GLFW_FALSE; + } + + if (mode == _GLFW_POLL_PRESENCE) + return GLFW_TRUE; + + 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; + } + + case _GLFW_TYPE_BUTTON: + { + 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; } diff --git a/src/win32_joystick.h b/src/win32_joystick.h index a0346833..8070a425 100644 --- a/src/win32_joystick.h +++ b/src/win32_joystick.h @@ -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; diff --git a/src/x11_window.c b/src/x11_window.c index 980ae149..ab2c0dc2 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -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--)