From 798d7c6d68bb17480fcda6074b1d5a946a05ed52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 1 Mar 2017 23:27:20 +0100 Subject: [PATCH] Implement glfwGetJoystickHats This moves the buttons-as-hats logic to shared code and adds the GLFW_JOYSTICK_HAT_BUTTONS input mode as a way to disable this legacy behavior. Fixes #889. --- README.md | 3 ++ docs/Doxyfile.in | 3 +- docs/input.dox | 69 ++++++++++++++++++++++++++++++++++------- docs/intro.dox | 6 ++++ docs/news.dox | 7 +++++ include/GLFW/glfw3.h | 58 ++++++++++++++++++++++------------- src/cocoa_joystick.m | 32 +++++++++++-------- src/init.c | 4 +++ src/input.c | 44 +++++++++++++++++++++++--- src/internal.h | 12 +++++++- src/linux_joystick.c | 2 +- src/win32_joystick.c | 51 ++++++++++++++++++++----------- tests/joysticks.c | 73 +++++++++++++++++++++++++++++++++++++------- 13 files changed, 282 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index 298a148e..a979061e 100644 --- a/README.md +++ b/README.md @@ -127,11 +127,14 @@ information on what to include when reporting a bug. - Added `glfwSetWindowMaximizeCallback` and `GLFWwindowmaximizefun` for receiving window maximization events (#778) - Added `glfwSetWindowAttrib` function for changing window attributes (#537) +- Added `glfwGetJoystickHats` function for querying joystick hats + (#889,#906,#934) - Added `glfwInitHint` function for setting library initialization hints - Added headless [OSMesa](http://mesa3d.org/osmesa.html) backend (#850) - Added definition of `GLAPIENTRY` to public header - Added `GLFW_CENTER_CURSOR` window hint for controlling cursor centering (#749,#842) +- Added `GLFW_JOYSTICK_HAT_BUTTONS` init hint (#889) - Added macOS specific `GLFW_COCOA_RETINA_FRAMEBUFFER` window hint - Added macOS specific `GLFW_COCOA_FRAME_AUTOSAVE` window hint (#195) - Added macOS specific `GLFW_COCOA_GRAPHICS_SWITCHING` window hint (#377,#935) diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index df840421..16562cc8 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -204,7 +204,8 @@ ALIASES = "thread_safety=@par Thread safety\n" \ "x11=__X11:__" \ "wayland=__Wayland:__" \ "win32=__Windows:__" \ - "macos=__macOS:__" + "macos=__macOS:__" \ + "linux=__Linux:__" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding diff --git a/docs/input.dox b/docs/input.dox index 8d6c46ad..b5b853aa 100644 --- a/docs/input.dox +++ b/docs/input.dox @@ -510,21 +510,24 @@ A simple mouse wheel, being vertical, provides offsets along the Y-axis. The joystick functions expose connected joysticks and controllers, with both referred to as joysticks. It supports up to sixteen joysticks, ranging from -`GLFW_JOYSTICK_1`, `GLFW_JOYSTICK_2` up to `GLFW_JOYSTICK_LAST`. You can test -whether a [joystick](@ref joysticks) is present with @ref glfwJoystickPresent. +`GLFW_JOYSTICK_1`, `GLFW_JOYSTICK_2` up to and including `GLFW_JOYSTICK_16` or +`GLFW_JOYSTICK_LAST`. You can test whether a [joystick](@ref joysticks) is +present with @ref glfwJoystickPresent. @code int present = glfwJoystickPresent(GLFW_JOYSTICK_1); @endcode When GLFW is initialized, detected joysticks are added to to the beginning of -the array, starting with `GLFW_JOYSTICK_1`. Once a joystick is detected, it -keeps its assigned index until it is disconnected, so as joysticks are connected -and disconnected, they will become spread out. +the array. Once a joystick is detected, it keeps its assigned ID until it is +disconnected or the library is terminated, so as joysticks are connected and +disconnected, there may appear gaps in the IDs. -Joystick state is updated as needed when a joystick function is called and does -not require a window to be created or @ref glfwPollEvents or @ref glfwWaitEvents -to be called. +Joystick axis, button and hat state is updated when polled and does not require +a window to be created or events to be processed. However, if you want joystick +connection and disconnection events reliably delivered to the +[joystick callback](@ref joystick_event) then you must +[process events](@ref events). To see all the properties of all connected joysticks in real-time, run the `joysticks` test program. @@ -538,7 +541,7 @@ returned array. @code int count; -const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &count); +const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_5, &count); @endcode Each element in the returned array is a value between -1.0 and 1.0. @@ -552,11 +555,55 @@ returned array. @code int count; -const unsigned char* axes = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &count); +const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_3, &count); @endcode Each element in the returned array is either `GLFW_PRESS` or `GLFW_RELEASE`. +For backward compatibility with earlier versions that did not have @ref +glfwGetJoystickHats, the button array by default also includes all hats. See +the reference documentation for @ref glfwGetJoystickButtons for details. + + +@subsection joystick_hat Joystick hat states + +The states of all hats are returned by @ref glfwGetJoystickHats. See the +reference documentation for the lifetime of the returned array. + +@code +int count; +const unsigned char* hats = glfwGetJoystickHats(GLFW_JOYSTICK_7, &count); +@endcode + +Each element in the returned array is one of the following: + +Name | Value +--------------------- | -------------------------------- +`GLFW_HAT_CENTERED` | 0 +`GLFW_HAT_UP` | 1 +`GLFW_HAT_RIGHT` | 2 +`GLFW_HAT_DOWN` | 4 +`GLFW_HAT_LEFT` | 8 +`GLFW_HAT_RIGHT_UP` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_UP` +`GLFW_HAT_RIGHT_DOWN` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_DOWN` +`GLFW_HAT_LEFT_UP` | `GLFW_HAT_LEFT` \| `GLFW_HAT_UP` +`GLFW_HAT_LEFT_DOWN` | `GLFW_HAT_LEFT` \| `GLFW_HAT_DOWN` + +The diagonal directions are bitwise combinations of the primary (up, right, down +and left) directions and you can test for these individually by ANDing it with +the corresponding direction. + +@code +if (hats[2] & GLFW_HAT_RIGHT) +{ + // State of hat 2 could be right-up, right or right-down +} +@endcode + +For backward compatibility with earlier versions that did not have @ref +glfwGetJoystickHats, all hats are by default also included in the button array. +See the reference documentation for @ref glfwGetJoystickButtons for details. + @subsection joystick_name Joystick name @@ -565,7 +612,7 @@ glfwGetJoystickName. See the reference documentation for the lifetime of the returned string. @code -const char* name = glfwGetJoystickName(GLFW_JOYSTICK_1); +const char* name = glfwGetJoystickName(GLFW_JOYSTICK_4); @endcode Joystick names are not guaranteed to be unique. Two joysticks of the same model diff --git a/docs/intro.dox b/docs/intro.dox index 57d0da82..16456e5c 100644 --- a/docs/intro.dox +++ b/docs/intro.dox @@ -77,6 +77,11 @@ platform but they will only affect their specific platform. Other platforms will simply ignore them. Setting these hints requires no platform specific headers or calls. +@anchor GLFW_JOYSTICK_HAT_BUTTONS +__GLFW_JOYSTICK_HAT_BUTTONS__ specifies whether to also expose joystick hats as +buttons, for compatibility with earlier versions of GLFW that did not have @ref +glfwGetJoystickHats. + @subsubsection init_hints_osx macOS specific hints @@ -95,6 +100,7 @@ initialized. Init hint | Default value | Supported values ------------------------------- | ------------- | ---------------- +@ref GLFW_JOYSTICK_HAT_BUTTONS | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` @ref GLFW_COCOA_CHDIR_RESOURCES | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` @ref GLFW_COCOA_MENUBAR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` diff --git a/docs/news.dox b/docs/news.dox index 6c089756..0be64b06 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -25,6 +25,13 @@ GLFW now supports changing the [GLFW_DECORATED](@ref GLFW_DECORATED_attrib), windows with @ref glfwSetWindowAttrib. +@subsection news_33_joyhats Support for joystick hats + +GLFW now supports querying the hats of a joystick with @ref glfwGetJoystickHats +and controlling whether hats are also exposed as buttons with the @ref +GLFW_JOYSTICK_HAT_BUTTONS init hint. + + @subsection news_33_inithint Support for initialization hints GLFW now supports setting library initialization hints with @ref glfwInitHint. diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index fcfc88f0..8b2efcec 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -297,7 +297,7 @@ extern "C" { #define GLFW_REPEAT 2 /*! @} */ -/*! @defgroup hat_directions Joystick hat directions +/*! @defgroup hat_state Joystick hat states * * See [joystick hat input](@ref joystick_hat) for how these are used. * @@ -945,6 +945,8 @@ extern "C" { /*! @addtogroup init * @{ */ +#define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001 + #define GLFW_COCOA_CHDIR_RESOURCES 0x00051001 #define GLFW_COCOA_MENUBAR 0x00051002 /*! @} */ @@ -4020,7 +4022,7 @@ GLFWAPI int glfwJoystickPresent(int jid); * This function returns the values of all axes of the specified joystick. * Each element in the array is a value between -1.0 and 1.0. * - * Querying a joystick slot with no device present is not an error, but will + * Querying a joystick ID with no device present is not an error, but will * cause this function to return `NULL`. Call @ref glfwJoystickPresent to * check device presence. * @@ -4053,7 +4055,14 @@ GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count); * This function returns the state of all buttons of the specified joystick. * Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`. * - * Querying a joystick slot with no device present is not an error, but will + * For backward compatibility with earlier versions that did not have @ref + * glfwGetJoystickHats, the button array also includes all hats, each + * represented as four buttons. The hats are in the same order as returned by + * __glfwGetJoystickHats__ and are in the order _up_, _right_, _down_ and + * _left_. To disable these extra buttons, set the @ref + * GLFW_JOYSTICK_HAT_BUTTONS init hint before initialization. + * + * Querying a joystick ID with no device present is not an error, but will * cause this function to return `NULL`. Call @ref glfwJoystickPresent to * check device presence. * @@ -4085,27 +4094,32 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); /*! @brief Returns the state of all hats of the specified joystick. * * This function returns the state of all hats of the specified joystick. - * Each element in the array is one of the following: + * Each element in the array is one of the following values: * - * GLFW_HAT_CENTERED - * GLFW_HAT_UP - * GLFW_HAT_RIGHT - * GLFW_HAT_DOWN - * GLFW_HAT_LEFT - * GLFW_HAT_RIGHT_UP - * GLFW_HAT_RIGHT_DOWN - * GLFW_HAT_LEFT_UP - * GLFW_HAT_LEFT_DOWN + * Name | Value + * --------------------- | -------------------------------- + * `GLFW_HAT_CENTERED` | 0 + * `GLFW_HAT_UP` | 1 + * `GLFW_HAT_RIGHT` | 2 + * `GLFW_HAT_DOWN` | 4 + * `GLFW_HAT_LEFT` | 8 + * `GLFW_HAT_RIGHT_UP` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_UP` + * `GLFW_HAT_RIGHT_DOWN` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_DOWN` + * `GLFW_HAT_LEFT_UP` | `GLFW_HAT_LEFT` \| `GLFW_HAT_UP` + * `GLFW_HAT_LEFT_DOWN` | `GLFW_HAT_LEFT` \| `GLFW_HAT_DOWN` * - * For masking purposes, the hat state may be ANDed with the following primary - * directions: + * The diagonal directions are bitwise combinations of the primary (up, right, + * down and left) directions and you can test for these individually by ANDing + * it with the corresponding direction. * - * GLFW_HAT_UP - * GLFW_HAT_RIGHT - * GLFW_HAT_DOWN - * GLFW_HAT_LEFT + * @code + * if (hats[2] & GLFW_HAT_RIGHT) + * { + * // State of hat 2 could be right-up, right or right-down + * } + * @endcode * - * Querying a joystick slot with no device present is not an error, but will + * Querying a joystick ID with no device present is not an error, but will * cause this function to return `NULL`. Call @ref glfwJoystickPresent to * check device presence. * @@ -4119,7 +4133,7 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * - * @remark @linux Linux does not currently support hats. + * @bug @linux Joystick hats are currently unimplemented. * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is @@ -4142,7 +4156,7 @@ GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count); * The returned string is allocated and freed by GLFW. You should not free it * yourself. * - * Querying a joystick slot with no device present is not an error, but will + * Querying a joystick ID with no device present is not an error, but will * cause this function to return `NULL`. Call @ref glfwJoystickPresent to * check device presence. * diff --git a/src/cocoa_joystick.m b/src/cocoa_joystick.m index 07aa32b6..e828002b 100644 --- a/src/cocoa_joystick.m +++ b/src/cocoa_joystick.m @@ -204,7 +204,8 @@ static void matchCallback(void* context, js = _glfwAllocJoystick(name, CFArrayGetCount(axes), - CFArrayGetCount(buttons) + CFArrayGetCount(hats) * 4); + CFArrayGetCount(buttons), + CFArrayGetCount(hats)); js->ns.device = device; js->ns.axes = axes; @@ -358,33 +359,38 @@ int _glfwPlatformPollJoystick(int jid, int mode) } else if (mode == _GLFW_POLL_BUTTONS) { - CFIndex i, bi = 0; + CFIndex i; 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); + _glfwInputJoystickButton(jid, i, value); } for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) { + const int states[9] = + { + GLFW_HAT_UP, + GLFW_HAT_RIGHT_UP, + GLFW_HAT_RIGHT, + GLFW_HAT_RIGHT_DOWN, + GLFW_HAT_DOWN, + GLFW_HAT_LEFT_DOWN, + GLFW_HAT_LEFT, + GLFW_HAT_LEFT_UP, + GLFW_HAT_CENTERED + }; + _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); + long 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); - } + _glfwInputJoystickHat(jid, i, states[state]); } } diff --git a/src/init.c b/src/init.c index 478b1211..b2da9a77 100644 --- a/src/init.c +++ b/src/init.c @@ -46,6 +46,7 @@ _GLFWlibrary _glfw = { GLFW_FALSE }; static GLFWerrorfun _glfwErrorCallback; static _GLFWinitconfig _glfwInitHints = { + GLFW_TRUE, // hat buttons { GLFW_TRUE, // menubar GLFW_TRUE // chdir @@ -188,6 +189,9 @@ GLFWAPI void glfwInitHint(int hint, int value) { switch (hint) { + case GLFW_JOYSTICK_HAT_BUTTONS: + _glfwInitHints.hatButtons = value; + return; case GLFW_COCOA_CHDIR_RESOURCES: _glfwInitHints.ns.chdir = value; return; diff --git a/src/input.c b/src/input.c index 4c174d25..115eefb2 100644 --- a/src/input.c +++ b/src/input.c @@ -140,6 +140,19 @@ void _glfwInputJoystickButton(int jid, int button, char value) _glfw.joysticks[jid].buttons[button] = value; } +void _glfwInputJoystickHat(int jid, int hat, char value) +{ + _GLFWjoystick* js = _glfw.joysticks + jid; + const int base = js->buttonCount + hat * 4; + + js->buttons[base + 0] = (value & 0x01) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 1] = (value & 0x02) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 2] = (value & 0x04) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 3] = (value & 0x08) ? GLFW_PRESS : GLFW_RELEASE; + + js->hats[hat] = value; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -152,7 +165,10 @@ GLFWbool _glfwIsPrintable(int key) key == GLFW_KEY_KP_EQUAL; } -_GLFWjoystick* _glfwAllocJoystick(const char* name, int axisCount, int buttonCount) +_GLFWjoystick* _glfwAllocJoystick(const char* name, + int axisCount, + int buttonCount, + int hatCount) { int jid; _GLFWjoystick* js; @@ -170,9 +186,11 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name, int axisCount, int buttonCou js->present = GLFW_TRUE; js->name = strdup(name); js->axes = calloc(axisCount, sizeof(float)); - js->buttons = calloc(buttonCount, 1); + js->buttons = calloc(buttonCount + hatCount * 4, 1); + js->hats = calloc(hatCount, 1); js->axisCount = axisCount; js->buttonCount = buttonCount; + js->hatCount = hatCount; return js; } @@ -182,6 +200,7 @@ void _glfwFreeJoystick(_GLFWjoystick* js) free(js->name); free(js->axes); free(js->buttons); + free(js->hats); memset(js, 0, sizeof(_GLFWjoystick)); } @@ -663,13 +682,23 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count) if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_BUTTONS)) return NULL; - *count = _glfw.joysticks[jid].buttonCount; + if (_glfw.hints.init.hatButtons) + { + *count = _glfw.joysticks[jid].buttonCount + + _glfw.joysticks[jid].hatCount * 4; + } + else + *count = _glfw.joysticks[jid].buttonCount; + return _glfw.joysticks[jid].buttons; } GLFWAPI const unsigned char* glfwGetJoystickHats(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); @@ -680,7 +709,14 @@ GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count) return NULL; } - return NULL; + if (!_glfw.joysticks[jid].present) + return NULL; + + if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_BUTTONS)) + return NULL; + + *count = _glfw.joysticks[jid].hatCount; + return _glfw.joysticks[jid].hats; } GLFWAPI const char* glfwGetJoystickName(int jid) diff --git a/src/internal.h b/src/internal.h index f43638df..13214b07 100644 --- a/src/internal.h +++ b/src/internal.h @@ -266,6 +266,7 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void); */ struct _GLFWinitconfig { + GLFWbool hatButtons; struct { GLFWbool menubar; GLFWbool chdir; @@ -476,6 +477,8 @@ struct _GLFWjoystick int axisCount; unsigned char* buttons; int buttonCount; + unsigned char* hats; + int hatCount; char* name; // This is defined in the joystick API's joystick.h @@ -823,6 +826,13 @@ void _glfwInputJoystickAxis(int jid, int axis, float value); */ void _glfwInputJoystickButton(int jid, int button, char value); +/*! @brief Notifies shared code of the new value of a joystick hat. + * @param[in] jid The joystick whose hat to update. + * @param[in] button The index of the hat to update. + * @param[in] value The new value of the hat. + */ +void _glfwInputJoystickHat(int jid, int hat, char value); + //======================================================================== // Utility functions @@ -912,7 +922,7 @@ void _glfwFreeMonitor(_GLFWmonitor* monitor); /*! @brief Returns an available joystick object with arrays and name allocated. * @ingroup utility */ -_GLFWjoystick* _glfwAllocJoystick(const char* name, int axisCount, int buttonCount); +_GLFWjoystick* _glfwAllocJoystick(const char* name, int axisCount, int buttonCount, int hatCount); /*! @brief Frees arrays and name and flags the joystick object as unused. * @ingroup utility diff --git a/src/linux_joystick.c b/src/linux_joystick.c index 04cca5f5..6e4b6a8c 100644 --- a/src/linux_joystick.c +++ b/src/linux_joystick.c @@ -77,7 +77,7 @@ static GLFWbool openJoystickDevice(const char* path) ioctl(fd, JSIOCGAXES, &axisCount); ioctl(fd, JSIOCGBUTTONS, &buttonCount); - js = _glfwAllocJoystick(name, axisCount, buttonCount); + js = _glfwAllocJoystick(name, axisCount, buttonCount, 0); if (!js) { close(fd); diff --git a/src/win32_joystick.c b/src/win32_joystick.c index 0a0d3ef0..ecbf4b90 100644 --- a/src/win32_joystick.c +++ b/src/win32_joystick.c @@ -427,7 +427,8 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) js = _glfwAllocJoystick(name, data.axisCount + data.sliderCount, - data.buttonCount + data.povCount * 4); + data.buttonCount, + data.povCount); if (!js) { IDirectInputDevice8_Release(device); @@ -512,7 +513,7 @@ void _glfwDetectJoystickConnectionWin32(void) if (XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS) continue; - js = _glfwAllocJoystick(getDeviceDescription(&xic), 6, 14); + js = _glfwAllocJoystick(getDeviceDescription(&xic), 6, 10, 1); if (!js) continue; @@ -561,7 +562,7 @@ int _glfwPlatformPollJoystick(int jid, int mode) if (js->win32.device) { - int i, j, ai = 0, bi = 0; + int i, ai = 0, bi = 0, pi = 0; HRESULT result; DIJOYSTATE state; @@ -612,19 +613,26 @@ int _glfwPlatformPollJoystick(int jid, int mode) case _GLFW_TYPE_POV: { - const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 }; + const int states[9] = + { + GLFW_HAT_UP, + GLFW_HAT_RIGHT_UP, + GLFW_HAT_RIGHT, + GLFW_HAT_RIGHT_DOWN, + GLFW_HAT_DOWN, + GLFW_HAT_LEFT_DOWN, + GLFW_HAT_LEFT, + GLFW_HAT_LEFT_UP, + GLFW_HAT_CENTERED + }; + // 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++; - } - + _glfwInputJoystickHat(jid, pi, states[state]); + pi++; break; } } @@ -632,7 +640,7 @@ int _glfwPlatformPollJoystick(int jid, int mode) } else { - int i; + int i, dpad = 0; DWORD result; XINPUT_STATE xis; float axes[6] = { 0.f, 0.f, 0.f, 0.f, -1.f, -1.f }; @@ -647,11 +655,7 @@ int _glfwPlatformPollJoystick(int jid, int mode) 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 + XINPUT_GAMEPAD_RIGHT_THUMB }; result = XInputGetState(js->win32.index, &xis); @@ -693,11 +697,22 @@ int _glfwPlatformPollJoystick(int jid, int mode) for (i = 0; i < 6; i++) _glfwInputJoystickAxis(jid, i, axes[i]); - for (i = 0; i < 14; i++) + for (i = 0; i < 10; i++) { const char value = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0; _glfwInputJoystickButton(jid, i, value); } + + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) + dpad |= GLFW_HAT_UP; + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) + dpad |= GLFW_HAT_RIGHT; + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) + dpad |= GLFW_HAT_DOWN; + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) + dpad |= GLFW_HAT_LEFT; + + _glfwInputJoystickHat(jid, 0, dpad); } return GLFW_TRUE; diff --git a/tests/joysticks.c b/tests/joysticks.c index 02d4f0e3..e00e9d95 100644 --- a/tests/joysticks.c +++ b/tests/joysticks.c @@ -98,6 +98,7 @@ int main(void) memset(joysticks, 0, sizeof(joysticks)); glfwSetErrorCallback(error_callback); + glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE); if (!glfwInit()) exit(EXIT_FAILURE); @@ -142,6 +143,8 @@ int main(void) { nk_layout_row_dynamic(nk, 30, 1); + nk_label(nk, "Hat buttons disabled", NK_TEXT_LEFT); + if (joystick_count) { for (i = 0; i < joystick_count; i++) @@ -167,29 +170,77 @@ int main(void) NK_WINDOW_MINIMIZABLE | NK_WINDOW_TITLE)) { - int j, axis_count, button_count; + int j, axis_count, button_count, hat_count; const float* axes; const unsigned char* buttons; + const unsigned char* hats; nk_layout_row_dynamic(nk, 30, 1); axes = glfwGetJoystickAxes(joysticks[i], &axis_count); - if (axis_count) - { - for (j = 0; j < axis_count; j++) - nk_slide_float(nk, -1.f, axes[j], 1.f, 0.1f); - } + for (j = 0; j < axis_count; j++) + nk_slide_float(nk, -1.f, axes[j], 1.f, 0.1f); nk_layout_row_dynamic(nk, 30, 8); buttons = glfwGetJoystickButtons(joysticks[i], &button_count); - if (button_count) + for (j = 0; j < button_count; j++) { - for (j = 0; j < button_count; j++) + char name[16]; + snprintf(name, sizeof(name), "%i", j + 1); + nk_select_label(nk, name, NK_TEXT_CENTERED, buttons[j]); + } + + nk_layout_row_dynamic(nk, 30, 8); + + hats = glfwGetJoystickHats(joysticks[i], &hat_count); + for (j = 0; j < hat_count; j++) + { + float radius; + struct nk_rect area; + struct nk_vec2 center; + + if (nk_widget(&area, nk) != NK_WIDGET_VALID) + continue; + + center = nk_vec2(area.x + area.w / 2.f, + area.y + area.h / 2.f); + radius = NK_MIN(area.w, area.h) / 2.f; + + nk_stroke_circle(nk_window_get_canvas(nk), + nk_rect(center.x - radius, + center.y - radius, + radius * 2.f, + radius * 2.f), + 1.f, + nk_rgb(175, 175, 175)); + + if (hats[j]) { - char name[16]; - snprintf(name, sizeof(name), "%i", j + 1); - nk_select_label(nk, name, NK_TEXT_CENTERED, buttons[j]); + const float angles[] = + { + 0.f, 0.f, + NK_PI * 1.5f, NK_PI * 1.75f, + NK_PI, 0.f, + NK_PI * 1.25f, 0.f, + NK_PI * 0.5f, NK_PI * 0.25f, + 0.f, 0.f, + NK_PI * 0.75f, 0.f, + }; + const float cosa = nk_cos(angles[hats[j]]); + const float sina = nk_sin(angles[hats[j]]); + const struct nk_vec2 p0 = nk_vec2(0.f, -radius); + const struct nk_vec2 p1 = nk_vec2( radius / 2.f, -radius / 3.f); + const struct nk_vec2 p2 = nk_vec2(-radius / 2.f, -radius / 3.f); + + nk_fill_triangle(nk_window_get_canvas(nk), + center.x + cosa * p0.x + sina * p0.y, + center.y + cosa * p0.y - sina * p0.x, + center.x + cosa * p1.x + sina * p1.y, + center.y + cosa * p1.y - sina * p1.x, + center.x + cosa * p2.x + sina * p2.y, + center.y + cosa * p2.y - sina * p2.x, + nk_rgb(175, 175, 175)); } } }