Skip to content

Commit

Permalink
Implement glfwGetJoystickHats
Browse files Browse the repository at this point in the history
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 glfw#889.
  • Loading branch information
elmindreda committed Mar 6, 2017
1 parent 368dec7 commit 798d7c6
Show file tree
Hide file tree
Showing 13 changed files with 285 additions and 85 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion docs/Doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
69 changes: 58 additions & 11 deletions docs/input.dox
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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

Expand All @@ -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
Expand Down
6 changes: 6 additions & 0 deletions docs/intro.dox
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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`

Expand Down
7 changes: 7 additions & 0 deletions docs/news.dox
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
66 changes: 40 additions & 26 deletions include/GLFW/glfw3.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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
/*! @} */
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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:
*
* 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
*
* For masking purposes, the hat state may be ANDed with the following primary
* directions:
*
* GLFW_HAT_UP
* GLFW_HAT_RIGHT
* GLFW_HAT_DOWN
* GLFW_HAT_LEFT
*
* Querying a joystick slot with no device present is not an error, but will
* Each element in the array is one of the following values:
*
* 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
*
* 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.
*
Expand All @@ -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
Expand All @@ -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.
*
Expand Down
32 changes: 19 additions & 13 deletions src/cocoa_joystick.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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]);
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ _GLFWlibrary _glfw = { GLFW_FALSE };
static GLFWerrorfun _glfwErrorCallback;
static _GLFWinitconfig _glfwInitHints =
{
GLFW_TRUE, // hat buttons
{
GLFW_TRUE, // menubar
GLFW_TRUE // chdir
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 798d7c6

Please sign in to comment.