From 3bee851242045c4ea7539ce682f8cf769b218c3d Mon Sep 17 00:00:00 2001 From: IntellectualKitty Date: Wed, 7 Sep 2016 15:04:56 -0600 Subject: [PATCH 1/5] Added support for joystick button and axis callbacks --- include/GLFW/glfw3.h | 80 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index e8f831768..e18497614 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1121,6 +1121,42 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); */ typedef void (* GLFWjoystickfun)(int,int); +/*! @brief The function signature for joystick button callbacks. + * + * This is the function signature for joystick button callback + * functions. + * + * @param[in] joy The joystick that had a button pressed or released. + * @param[in] button The button that was pressed or released. + * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. + * + * @sa @ref joystick_event + * @sa glfwSetJoystickButtonCallback + * + * @since Added in version 3.2. + * + * @ingroup input + */ +typedef void (* GLFWjoystickbuttonfun)(int,int,int); + +/*! @brief The function signature for joystick axis callbacks. + * + * This is the function signature for joystick axis callback + * functions. + * + * @param[in] joy The joystick that had an axis moved. + * @param[in] axis The axis that was moved. + * @param[in] value The axis value. + * + * @sa @ref joystick_event + * @sa glfwSetJoystickAxisCallback + * + * @since Added in version 3.2. + * + * @ingroup input + */ +typedef void (* GLFWjoystickaxisfun)(int,int,float); + /*! @brief Video mode type. * * This describes a single video mode. @@ -3680,6 +3716,50 @@ GLFWAPI const char* glfwGetJoystickName(int joy); */ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun); +/*! @brief Sets the joystick button callback. + * + * This function sets the joystick button callback, or removes the currently + * set callback. This is called when a joystick button is pressed or released. + * + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_event + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI GLFWjoystickbuttonfun glfwSetJoystickButtonCallback(GLFWjoystickbuttonfun cbfun); + +/*! @brief Sets the joystick axis callback. + * + * This function sets the joystick axis callback, or removes the currently + * set callback. This is called when a joystick axis is moved. + * + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_event + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI GLFWjoystickaxisfun glfwSetJoystickAxisCallback(GLFWjoystickaxisfun cbfun); + /*! @brief Sets the clipboard to the specified string. * * This function sets the system clipboard to the specified, UTF-8 encoded From f63824b250fa28582af932cfbeacd06dbe9a3967 Mon Sep 17 00:00:00 2001 From: IntellectualKitty Date: Wed, 7 Sep 2016 15:06:16 -0600 Subject: [PATCH 2/5] Added support for joystick button and axis callbacks --- src/internal.h | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/internal.h b/src/internal.h index a13dd1e4b..73b8a6e72 100644 --- a/src/internal.h +++ b/src/internal.h @@ -463,8 +463,10 @@ struct _GLFWlibrary } vk; struct { - GLFWmonitorfun monitor; - GLFWjoystickfun joystick; + GLFWmonitorfun monitor; + GLFWjoystickfun joystick; + GLFWjoystickbuttonfun joystick_button; + GLFWjoystickaxisfun joystick_axis; } callbacks; // This is defined in the window API's platform.h @@ -953,6 +955,22 @@ void _glfwInputDrop(_GLFWwindow* window, int count, const char** names); */ void _glfwInputJoystickChange(int joy, int event); +/*! @brief Notifies shared code of a joystick button pressed/release action. + * @param[in] joy The joystick that had a button pressed or released. + * @param[in] button The button that was pressed or released. + * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. + * @ingroup event + */ +void _glfwInputJoystickButtonState(int joy, int button, int action); + +/*! @brief Notifies shared code of joystick axis motion. + * @param[in] joy The joystick that had an axis moved. + * @param[in] axis The axis that was moved. + * @param[in] value The current position of the axis. + * @ingroup event + */ +void _glfwInputJoystickAxisMoved(int joy, int axis, float value); + //======================================================================== // Utility functions From 4e8d5ded78413162a38c2fd429f6ddaba69c1fa7 Mon Sep 17 00:00:00 2001 From: IntellectualKitty Date: Wed, 7 Sep 2016 15:07:19 -0600 Subject: [PATCH 3/5] Added support for joystick button and axis callbacks --- src/input.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/input.c b/src/input.c index c09599212..af4e49151 100644 --- a/src/input.c +++ b/src/input.c @@ -130,6 +130,18 @@ void _glfwInputJoystickChange(int joy, int event) _glfw.callbacks.joystick(joy, event); } +void _glfwInputJoystickButtonState(int joy, int button, int action) +{ + if (_glfw.callbacks.joystick_button) + _glfw.callbacks.joystick_button(joy, button, action); +} + +void _glfwInputJoystickAxisMoved(int joy, int axis, float value) +{ + if (_glfw.callbacks.joystick_axis) + _glfw.callbacks.joystick_axis(joy, axis, value); +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -618,6 +630,20 @@ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) return cbfun; } +GLFWAPI GLFWjoystickbuttonfun glfwSetJoystickButtonCallback(GLFWjoystickbuttonfun cbfun) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(_glfw.callbacks.joystick_button, cbfun); + return cbfun; +} + +GLFWAPI GLFWjoystickaxisfun glfwSetJoystickAxisCallback(GLFWjoystickaxisfun cbfun) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(_glfw.callbacks.joystick_axis, cbfun); + return cbfun; +} + GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string) { _GLFWwindow* window = (_GLFWwindow*) handle; From ed211f35b58852107d11941d9f55ae191c82ceae Mon Sep 17 00:00:00 2001 From: IntellectualKitty Date: Wed, 7 Sep 2016 15:08:15 -0600 Subject: [PATCH 4/5] Added support for joystick button and axis callbacks --- tests/events.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/events.c b/tests/events.c index 674b584e7..35d79610e 100644 --- a/tests/events.c +++ b/tests/events.c @@ -474,6 +474,18 @@ static void joystick_callback(int joy, int event) } } +static void joystick_button_callback(int joy, int button, int action) +{ + printf("%08x at %0.3f: Joystick %i button %i was %s\n", + counter++, glfwGetTime(), joy, button, get_action_name(action)); +} + +static void joystick_axis_callback(int joy, int axis, float value) +{ + printf("%08x at %0.3f: Joystick %i axis %i was moved to %f\n", + counter++, glfwGetTime(), joy, axis, value); +} + int main(int argc, char** argv) { Slot* slots; @@ -491,6 +503,8 @@ int main(int argc, char** argv) glfwSetMonitorCallback(monitor_callback); glfwSetJoystickCallback(joystick_callback); + glfwSetJoystickButtonCallback(joystick_button_callback); + glfwSetJoystickAxisCallback(joystick_axis_callback); while ((ch = getopt(argc, argv, "hfn:")) != -1) { From d3eaf9a9fa53fec789b975b3c9068cab7df8cfc1 Mon Sep 17 00:00:00 2001 From: IntellectualKitty Date: Wed, 7 Sep 2016 15:16:20 -0600 Subject: [PATCH 5/5] Added support for joystick button and axis callbacks The major change is the addition of the inputValueCallback function. This callback function is registered with the HID Manager using the HIDDeviceRef when a joystick is connected. Whenever an axis, button, or hat switch value is changed for that HID device, the inputValueCallback function is called: the appropriate value will be updated (which alleviates the need for polling, hence the removal of the polling functions) and, if a user callback has been registered, event notification will be provided. (Note that in the case of hat switches, which are treated as quad sets of buttons, one for each direction, user notification is provided only if the value for that direction has actually changed). --- src/cocoa_joystick.m | 119 ++++++++++++++++++++++++++----------------- 1 file changed, 72 insertions(+), 47 deletions(-) diff --git a/src/cocoa_joystick.m b/src/cocoa_joystick.m index 439918fd7..8a8404474 100644 --- a/src/cocoa_joystick.m +++ b/src/cocoa_joystick.m @@ -37,7 +37,6 @@ #include #include - // Joystick element information // typedef struct _GLFWjoyelementNS @@ -193,75 +192,96 @@ static void removeJoystick(_GLFWjoystickNS* js) _glfwInputJoystickChange(js - _glfw.ns_js, GLFW_DISCONNECTED); } -// Polls for joystick axis events and updates GLFW state +// Input value callback // -static GLFWbool pollJoystickAxisEvents(_GLFWjoystickNS* js) +static void inputValueCallback (void* context, + IOReturn valueResult, + void* deviceRef, + IOHIDValueRef valueRef) { + _GLFWjoystickNS* js; CFIndex i; + int buttonIndex = 0; + if (kIOReturnSuccess != valueResult) + return; + + js = (_GLFWjoystickNS*)context; + if (!js) + return; if (!js->present) - return GLFW_FALSE; + return; + + IOHIDElementRef elementRef = IOHIDValueGetElement(valueRef); 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 (elementRef == axis->elementRef) + { + 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; + if (readScale == 0) + js->axes[i] = value; + else + js->axes[i] = (2.f * (value - axis->minReport) / readScale) - 1.f; + + _glfwInputJoystickAxisMoved(js-_glfw.ns_js,i,js->axes[i]); + return; + } } - 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++) + for (i = 0; i < CFArrayGetCount(js->buttonElements); i++, buttonIndex++) { _GLFWjoyelementNS* button = (_GLFWjoyelementNS*) CFArrayGetValueAtIndex(js->buttonElements, i); - if (getElementValue(js, button)) - js->buttons[buttonIndex++] = GLFW_PRESS; - else - js->buttons[buttonIndex++] = GLFW_RELEASE; + if (elementRef == button->elementRef) + { + if (getElementValue(js, button)) + js->buttons[buttonIndex] = GLFW_PRESS; + else + js->buttons[buttonIndex] = GLFW_RELEASE; + + _glfwInputJoystickButtonState(js-_glfw.ns_js,buttonIndex,js->buttons[buttonIndex]); + return; + } } - for (i = 0; i < CFArrayGetCount(js->hatElements); i++) + for (i = 0; i < CFArrayGetCount(js->hatElements); i++, buttonIndex += 4) { _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 (elementRef == hat->elementRef) { - if (directions[value] & (1 << j)) - js->buttons[buttonIndex++] = GLFW_PRESS; - else - js->buttons[buttonIndex++] = GLFW_RELEASE; - } - } + // Bit fields of button presses for each direction, including nil + const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 }; - return GLFW_TRUE; + long j, value = getElementValue(js, hat); + if (value < 0 || value > 8) + value = 8; + + for (j = 0; j < 4; j++) + { + const unsigned char oldValue = js->buttons[buttonIndex+j]; + + if (directions[value] & (1 << j)) + js->buttons[buttonIndex+j] = GLFW_PRESS; + else + js->buttons[buttonIndex+j] = GLFW_RELEASE; + + // Since we're treating hat switches as buttons, only send events for + // the values that have actually changed. + if (oldValue != js->buttons[buttonIndex+j]) + _glfwInputJoystickButtonState(js-_glfw.ns_js,buttonIndex+j,js->buttons[buttonIndex+j]); + } + } + return; + } } // Callback for user-initiated joystick addition @@ -324,6 +344,10 @@ static void matchCallback(void* context, js->buttons = calloc(CFArrayGetCount(js->buttonElements) + CFArrayGetCount(js->hatElements) * 4, 1); + IOHIDDeviceRegisterInputValueCallback(deviceRef, + inputValueCallback, + (void*)js); + _glfwInputJoystickChange(joy, GLFW_CONNECTED); } @@ -399,7 +423,6 @@ void _glfwInitJoysticksNS(void) _glfw.ns.hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); - matchingCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); @@ -482,7 +505,8 @@ int _glfwPlatformJoystickPresent(int joy) const float* _glfwPlatformGetJoystickAxes(int joy, int* count) { _GLFWjoystickNS* js = _glfw.ns_js + joy; - if (!pollJoystickAxisEvents(js)) + + if (!js->present) return NULL; *count = (int) CFArrayGetCount(js->axisElements); @@ -492,7 +516,8 @@ const float* _glfwPlatformGetJoystickAxes(int joy, int* count) const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) { _GLFWjoystickNS* js = _glfw.ns_js + joy; - if (!pollJoystickButtonEvents(js)) + + if (!js->present) return NULL; *count = (int) CFArrayGetCount(js->buttonElements) +