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.
This commit is contained in:
Camilla Löwy 2017-03-01 23:27:20 +01:00
parent 368dec7ac7
commit 798d7c6d68
13 changed files with 282 additions and 82 deletions

View File

@ -127,11 +127,14 @@ information on what to include when reporting a bug.
- Added `glfwSetWindowMaximizeCallback` and `GLFWwindowmaximizefun` for - Added `glfwSetWindowMaximizeCallback` and `GLFWwindowmaximizefun` for
receiving window maximization events (#778) receiving window maximization events (#778)
- Added `glfwSetWindowAttrib` function for changing window attributes (#537) - 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 `glfwInitHint` function for setting library initialization hints
- Added headless [OSMesa](http://mesa3d.org/osmesa.html) backend (#850) - Added headless [OSMesa](http://mesa3d.org/osmesa.html) backend (#850)
- Added definition of `GLAPIENTRY` to public header - Added definition of `GLAPIENTRY` to public header
- Added `GLFW_CENTER_CURSOR` window hint for controlling cursor centering - Added `GLFW_CENTER_CURSOR` window hint for controlling cursor centering
(#749,#842) (#749,#842)
- Added `GLFW_JOYSTICK_HAT_BUTTONS` init hint (#889)
- Added macOS specific `GLFW_COCOA_RETINA_FRAMEBUFFER` window hint - Added macOS specific `GLFW_COCOA_RETINA_FRAMEBUFFER` window hint
- Added macOS specific `GLFW_COCOA_FRAME_AUTOSAVE` window hint (#195) - Added macOS specific `GLFW_COCOA_FRAME_AUTOSAVE` window hint (#195)
- Added macOS specific `GLFW_COCOA_GRAPHICS_SWITCHING` window hint (#377,#935) - Added macOS specific `GLFW_COCOA_GRAPHICS_SWITCHING` window hint (#377,#935)

View File

@ -204,7 +204,8 @@ ALIASES = "thread_safety=@par Thread safety\n" \
"x11=__X11:__" \ "x11=__X11:__" \
"wayland=__Wayland:__" \ "wayland=__Wayland:__" \
"win32=__Windows:__" \ "win32=__Windows:__" \
"macos=__macOS:__" "macos=__macOS:__" \
"linux=__Linux:__"
# This tag can be used to specify a number of word-keyword mappings (TCL only). # 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 # A mapping has the form "name=value". For example adding

View File

@ -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 The joystick functions expose connected joysticks and controllers, with both
referred to as joysticks. It supports up to sixteen joysticks, ranging from 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 `GLFW_JOYSTICK_1`, `GLFW_JOYSTICK_2` up to and including `GLFW_JOYSTICK_16` or
whether a [joystick](@ref joysticks) is present with @ref glfwJoystickPresent. `GLFW_JOYSTICK_LAST`. You can test whether a [joystick](@ref joysticks) is
present with @ref glfwJoystickPresent.
@code @code
int present = glfwJoystickPresent(GLFW_JOYSTICK_1); int present = glfwJoystickPresent(GLFW_JOYSTICK_1);
@endcode @endcode
When GLFW is initialized, detected joysticks are added to to the beginning of 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 the array. Once a joystick is detected, it keeps its assigned ID until it is
keeps its assigned index until it is disconnected, so as joysticks are connected disconnected or the library is terminated, so as joysticks are connected and
and disconnected, they will become spread out. disconnected, there may appear gaps in the IDs.
Joystick state is updated as needed when a joystick function is called and does Joystick axis, button and hat state is updated when polled and does not require
not require a window to be created or @ref glfwPollEvents or @ref glfwWaitEvents a window to be created or events to be processed. However, if you want joystick
to be called. 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 To see all the properties of all connected joysticks in real-time, run the
`joysticks` test program. `joysticks` test program.
@ -538,7 +541,7 @@ returned array.
@code @code
int count; int count;
const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &count); const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_5, &count);
@endcode @endcode
Each element in the returned array is a value between -1.0 and 1.0. Each element in the returned array is a value between -1.0 and 1.0.
@ -552,11 +555,55 @@ returned array.
@code @code
int count; int count;
const unsigned char* axes = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &count); const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_3, &count);
@endcode @endcode
Each element in the returned array is either `GLFW_PRESS` or `GLFW_RELEASE`. 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 @subsection joystick_name Joystick name
@ -565,7 +612,7 @@ glfwGetJoystickName. See the reference documentation for the lifetime of the
returned string. returned string.
@code @code
const char* name = glfwGetJoystickName(GLFW_JOYSTICK_1); const char* name = glfwGetJoystickName(GLFW_JOYSTICK_4);
@endcode @endcode
Joystick names are not guaranteed to be unique. Two joysticks of the same model Joystick names are not guaranteed to be unique. Two joysticks of the same model

View File

@ -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 will simply ignore them. Setting these hints requires no platform specific
headers or calls. 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 @subsubsection init_hints_osx macOS specific hints
@ -95,6 +100,7 @@ initialized.
Init hint | Default value | Supported values 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_CHDIR_RESOURCES | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
@ref GLFW_COCOA_MENUBAR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` @ref GLFW_COCOA_MENUBAR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`

View File

@ -25,6 +25,13 @@ GLFW now supports changing the [GLFW_DECORATED](@ref GLFW_DECORATED_attrib),
windows with @ref glfwSetWindowAttrib. 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 @subsection news_33_inithint Support for initialization hints
GLFW now supports setting library initialization hints with @ref glfwInitHint. GLFW now supports setting library initialization hints with @ref glfwInitHint.

View File

@ -297,7 +297,7 @@ extern "C" {
#define GLFW_REPEAT 2 #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. * See [joystick hat input](@ref joystick_hat) for how these are used.
* *
@ -945,6 +945,8 @@ extern "C" {
/*! @addtogroup init /*! @addtogroup init
* @{ */ * @{ */
#define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001
#define GLFW_COCOA_CHDIR_RESOURCES 0x00051001 #define GLFW_COCOA_CHDIR_RESOURCES 0x00051001
#define GLFW_COCOA_MENUBAR 0x00051002 #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. * 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. * 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 * cause this function to return `NULL`. Call @ref glfwJoystickPresent to
* check device presence. * 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. * This function returns the state of all buttons of the specified joystick.
* Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`. * 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 * cause this function to return `NULL`. Call @ref glfwJoystickPresent to
* check device presence. * 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. /*! @brief Returns the state of all hats of the specified joystick.
* *
* This function 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 * Name | Value
* GLFW_HAT_UP * --------------------- | --------------------------------
* GLFW_HAT_RIGHT * `GLFW_HAT_CENTERED` | 0
* GLFW_HAT_DOWN * `GLFW_HAT_UP` | 1
* GLFW_HAT_LEFT * `GLFW_HAT_RIGHT` | 2
* GLFW_HAT_RIGHT_UP * `GLFW_HAT_DOWN` | 4
* GLFW_HAT_RIGHT_DOWN * `GLFW_HAT_LEFT` | 8
* GLFW_HAT_LEFT_UP * `GLFW_HAT_RIGHT_UP` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_UP`
* GLFW_HAT_LEFT_DOWN * `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 * The diagonal directions are bitwise combinations of the primary (up, right,
* directions: * down and left) directions and you can test for these individually by ANDing
* it with the corresponding direction.
* *
* GLFW_HAT_UP * @code
* GLFW_HAT_RIGHT * if (hats[2] & GLFW_HAT_RIGHT)
* GLFW_HAT_DOWN * {
* GLFW_HAT_LEFT * // 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 * cause this function to return `NULL`. Call @ref glfwJoystickPresent to
* check device presence. * 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 * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * 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 * @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 * 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 * The returned string is allocated and freed by GLFW. You should not free it
* yourself. * 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 * cause this function to return `NULL`. Call @ref glfwJoystickPresent to
* check device presence. * check device presence.
* *

View File

@ -204,7 +204,8 @@ static void matchCallback(void* context,
js = _glfwAllocJoystick(name, js = _glfwAllocJoystick(name,
CFArrayGetCount(axes), CFArrayGetCount(axes),
CFArrayGetCount(buttons) + CFArrayGetCount(hats) * 4); CFArrayGetCount(buttons),
CFArrayGetCount(hats));
js->ns.device = device; js->ns.device = device;
js->ns.axes = axes; js->ns.axes = axes;
@ -358,33 +359,38 @@ int _glfwPlatformPollJoystick(int jid, int mode)
} }
else if (mode == _GLFW_POLL_BUTTONS) else if (mode == _GLFW_POLL_BUTTONS)
{ {
CFIndex i, bi = 0; CFIndex i;
for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++) for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++)
{ {
_GLFWjoyelementNS* button = (_GLFWjoyelementNS*) _GLFWjoyelementNS* button = (_GLFWjoyelementNS*)
CFArrayGetValueAtIndex(js->ns.buttons, i); CFArrayGetValueAtIndex(js->ns.buttons, i);
const char value = getElementValue(js, button) ? 1 : 0; 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++) 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*) _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*)
CFArrayGetValueAtIndex(js->ns.hats, i); CFArrayGetValueAtIndex(js->ns.hats, i);
long state = getElementValue(js, hat);
// 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) if (state < 0 || state > 8)
state = 8; state = 8;
for (j = 0; j < 4; j++) _glfwInputJoystickHat(jid, i, states[state]);
{
const char value = directions[state] & (1 << j) ? 1 : 0;
_glfwInputJoystickButton(jid, bi++, value);
}
} }
} }

View File

@ -46,6 +46,7 @@ _GLFWlibrary _glfw = { GLFW_FALSE };
static GLFWerrorfun _glfwErrorCallback; static GLFWerrorfun _glfwErrorCallback;
static _GLFWinitconfig _glfwInitHints = static _GLFWinitconfig _glfwInitHints =
{ {
GLFW_TRUE, // hat buttons
{ {
GLFW_TRUE, // menubar GLFW_TRUE, // menubar
GLFW_TRUE // chdir GLFW_TRUE // chdir
@ -188,6 +189,9 @@ GLFWAPI void glfwInitHint(int hint, int value)
{ {
switch (hint) switch (hint)
{ {
case GLFW_JOYSTICK_HAT_BUTTONS:
_glfwInitHints.hatButtons = value;
return;
case GLFW_COCOA_CHDIR_RESOURCES: case GLFW_COCOA_CHDIR_RESOURCES:
_glfwInitHints.ns.chdir = value; _glfwInitHints.ns.chdir = value;
return; return;

View File

@ -140,6 +140,19 @@ void _glfwInputJoystickButton(int jid, int button, char value)
_glfw.joysticks[jid].buttons[button] = 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 ////// ////// GLFW internal API //////
@ -152,7 +165,10 @@ GLFWbool _glfwIsPrintable(int key)
key == GLFW_KEY_KP_EQUAL; 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; int jid;
_GLFWjoystick* js; _GLFWjoystick* js;
@ -170,9 +186,11 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name, int axisCount, int buttonCou
js->present = GLFW_TRUE; js->present = GLFW_TRUE;
js->name = strdup(name); js->name = strdup(name);
js->axes = calloc(axisCount, sizeof(float)); 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->axisCount = axisCount;
js->buttonCount = buttonCount; js->buttonCount = buttonCount;
js->hatCount = hatCount;
return js; return js;
} }
@ -182,6 +200,7 @@ void _glfwFreeJoystick(_GLFWjoystick* js)
free(js->name); free(js->name);
free(js->axes); free(js->axes);
free(js->buttons); free(js->buttons);
free(js->hats);
memset(js, 0, sizeof(_GLFWjoystick)); memset(js, 0, sizeof(_GLFWjoystick));
} }
@ -663,13 +682,23 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count)
if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_BUTTONS)) if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_BUTTONS))
return NULL; 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; return _glfw.joysticks[jid].buttons;
} }
GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count) GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count)
{ {
assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST);
assert(count != NULL); assert(count != NULL);
*count = 0; *count = 0;
_GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
@ -680,7 +709,14 @@ GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count)
return NULL; 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) GLFWAPI const char* glfwGetJoystickName(int jid)

View File

@ -266,6 +266,7 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void);
*/ */
struct _GLFWinitconfig struct _GLFWinitconfig
{ {
GLFWbool hatButtons;
struct { struct {
GLFWbool menubar; GLFWbool menubar;
GLFWbool chdir; GLFWbool chdir;
@ -476,6 +477,8 @@ struct _GLFWjoystick
int axisCount; int axisCount;
unsigned char* buttons; unsigned char* buttons;
int buttonCount; int buttonCount;
unsigned char* hats;
int hatCount;
char* name; char* name;
// This is defined in the joystick API's joystick.h // 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); 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 // Utility functions
@ -912,7 +922,7 @@ void _glfwFreeMonitor(_GLFWmonitor* monitor);
/*! @brief Returns an available joystick object with arrays and name allocated. /*! @brief Returns an available joystick object with arrays and name allocated.
* @ingroup utility * @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. /*! @brief Frees arrays and name and flags the joystick object as unused.
* @ingroup utility * @ingroup utility

View File

@ -77,7 +77,7 @@ static GLFWbool openJoystickDevice(const char* path)
ioctl(fd, JSIOCGAXES, &axisCount); ioctl(fd, JSIOCGAXES, &axisCount);
ioctl(fd, JSIOCGBUTTONS, &buttonCount); ioctl(fd, JSIOCGBUTTONS, &buttonCount);
js = _glfwAllocJoystick(name, axisCount, buttonCount); js = _glfwAllocJoystick(name, axisCount, buttonCount, 0);
if (!js) if (!js)
{ {
close(fd); close(fd);

View File

@ -427,7 +427,8 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user)
js = _glfwAllocJoystick(name, js = _glfwAllocJoystick(name,
data.axisCount + data.sliderCount, data.axisCount + data.sliderCount,
data.buttonCount + data.povCount * 4); data.buttonCount,
data.povCount);
if (!js) if (!js)
{ {
IDirectInputDevice8_Release(device); IDirectInputDevice8_Release(device);
@ -512,7 +513,7 @@ void _glfwDetectJoystickConnectionWin32(void)
if (XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS) if (XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS)
continue; continue;
js = _glfwAllocJoystick(getDeviceDescription(&xic), 6, 14); js = _glfwAllocJoystick(getDeviceDescription(&xic), 6, 10, 1);
if (!js) if (!js)
continue; continue;
@ -561,7 +562,7 @@ int _glfwPlatformPollJoystick(int jid, int mode)
if (js->win32.device) if (js->win32.device)
{ {
int i, j, ai = 0, bi = 0; int i, ai = 0, bi = 0, pi = 0;
HRESULT result; HRESULT result;
DIJOYSTATE state; DIJOYSTATE state;
@ -612,19 +613,26 @@ int _glfwPlatformPollJoystick(int jid, int mode)
case _GLFW_TYPE_POV: 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 // Screams of horror are appropriate at this point
int state = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES); int state = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES);
if (state < 0 || state > 8) if (state < 0 || state > 8)
state = 8; state = 8;
for (j = 0; j < 4; j++) _glfwInputJoystickHat(jid, pi, states[state]);
{ pi++;
const char value = (directions[state] & (1 << j)) != 0;
_glfwInputJoystickButton(jid, bi, value);
bi++;
}
break; break;
} }
} }
@ -632,7 +640,7 @@ int _glfwPlatformPollJoystick(int jid, int mode)
} }
else else
{ {
int i; int i, dpad = 0;
DWORD result; DWORD result;
XINPUT_STATE xis; XINPUT_STATE xis;
float axes[6] = { 0.f, 0.f, 0.f, 0.f, -1.f, -1.f }; 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_BACK,
XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_START,
XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_LEFT_THUMB,
XINPUT_GAMEPAD_RIGHT_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); result = XInputGetState(js->win32.index, &xis);
@ -693,11 +697,22 @@ int _glfwPlatformPollJoystick(int jid, int mode)
for (i = 0; i < 6; i++) for (i = 0; i < 6; i++)
_glfwInputJoystickAxis(jid, i, axes[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; const char value = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0;
_glfwInputJoystickButton(jid, i, value); _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; return GLFW_TRUE;

View File

@ -98,6 +98,7 @@ int main(void)
memset(joysticks, 0, sizeof(joysticks)); memset(joysticks, 0, sizeof(joysticks));
glfwSetErrorCallback(error_callback); glfwSetErrorCallback(error_callback);
glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE);
if (!glfwInit()) if (!glfwInit())
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -142,6 +143,8 @@ int main(void)
{ {
nk_layout_row_dynamic(nk, 30, 1); nk_layout_row_dynamic(nk, 30, 1);
nk_label(nk, "Hat buttons disabled", NK_TEXT_LEFT);
if (joystick_count) if (joystick_count)
{ {
for (i = 0; i < joystick_count; i++) for (i = 0; i < joystick_count; i++)
@ -167,29 +170,77 @@ int main(void)
NK_WINDOW_MINIMIZABLE | NK_WINDOW_MINIMIZABLE |
NK_WINDOW_TITLE)) NK_WINDOW_TITLE))
{ {
int j, axis_count, button_count; int j, axis_count, button_count, hat_count;
const float* axes; const float* axes;
const unsigned char* buttons; const unsigned char* buttons;
const unsigned char* hats;
nk_layout_row_dynamic(nk, 30, 1); nk_layout_row_dynamic(nk, 30, 1);
axes = glfwGetJoystickAxes(joysticks[i], &axis_count); 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); nk_layout_row_dynamic(nk, 30, 8);
buttons = glfwGetJoystickButtons(joysticks[i], &button_count); 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]; const float angles[] =
snprintf(name, sizeof(name), "%i", j + 1); {
nk_select_label(nk, name, NK_TEXT_CENTERED, buttons[j]); 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));
} }
} }
} }