Skip to content

Commit

Permalink
Mac joystick: ignore duplicate HID elements.
Browse files Browse the repository at this point in the history
The DualShock 4 has all elements listed twice: once in the top-level list of
 elements, and once in an "Application Collection" element at the top-level.

Each element has a proper cookie with a unique value, so now we descend into
 each element collections, but before we add an element to the device's list,
 we make sure we don't already have one with that cookie, probably from
 another collection or a buggy device.
  • Loading branch information
icculus committed Feb 23, 2014
1 parent 9cd5f5c commit b67b970
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 18 deletions.
55 changes: 37 additions & 18 deletions src/joystick/darwin/SDL_sysjoystick.c
Expand Up @@ -149,6 +149,17 @@ AddHIDElements(CFArrayRef array, recDevice *pDevice)
CFArrayApplyFunction(array, range, AddHIDElement, pDevice);
}

static SDL_bool
ElementAlreadyAdded(const IOHIDElementCookie cookie, const recElement *listitem) {
while (listitem) {
if (listitem->cookie == cookie) {
return SDL_TRUE;
}
listitem = listitem->pNext;
}
return SDL_FALSE;
}

/* See if we care about this HID element, and if so, note it in our recDevice. */
static void
AddHIDElement(const void *value, void *parameter)
Expand All @@ -158,6 +169,7 @@ AddHIDElement(const void *value, void *parameter)
const CFTypeID elementTypeID = refElement ? CFGetTypeID(refElement) : 0;

if (refElement && (elementTypeID == IOHIDElementGetTypeID())) {
const IOHIDElementCookie cookie = IOHIDElementGetCookie(refElement);
const uint32_t usagePage = IOHIDElementGetUsagePage(refElement);
const uint32_t usage = IOHIDElementGetUsage(refElement);
recElement *element = NULL;
Expand All @@ -180,18 +192,22 @@ AddHIDElement(const void *value, void *parameter)
case kHIDUsage_GD_Slider:
case kHIDUsage_GD_Dial:
case kHIDUsage_GD_Wheel:
element = (recElement *) SDL_calloc(1, sizeof (recElement));
if (element) {
pDevice->axes++;
headElement = &(pDevice->firstAxis);
if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
element = (recElement *) SDL_calloc(1, sizeof (recElement));
if (element) {
pDevice->axes++;
headElement = &(pDevice->firstAxis);
}
}
break;

case kHIDUsage_GD_Hatswitch:
element = (recElement *) SDL_calloc(1, sizeof (recElement));
if (element) {
pDevice->hats++;
headElement = &(pDevice->firstHat);
if (!ElementAlreadyAdded(cookie, pDevice->firstHat)) {
element = (recElement *) SDL_calloc(1, sizeof (recElement));
if (element) {
pDevice->hats++;
headElement = &(pDevice->firstHat);
}
}
break;
}
Expand All @@ -201,10 +217,12 @@ AddHIDElement(const void *value, void *parameter)
switch (usage) {
case kHIDUsage_Sim_Rudder:
case kHIDUsage_Sim_Throttle:
element = (recElement *) SDL_calloc(1, sizeof (recElement));
if (element) {
pDevice->axes++;
headElement = &(pDevice->firstAxis);
if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
element = (recElement *) SDL_calloc(1, sizeof (recElement));
if (element) {
pDevice->axes++;
headElement = &(pDevice->firstAxis);
}
}
break;

Expand All @@ -214,10 +232,12 @@ AddHIDElement(const void *value, void *parameter)
break;

case kHIDPage_Button:
element = (recElement *) SDL_calloc(1, sizeof (recElement));
if (element) {
pDevice->buttons++;
headElement = &(pDevice->firstButton);
if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
element = (recElement *) SDL_calloc(1, sizeof (recElement));
if (element) {
pDevice->buttons++;
headElement = &(pDevice->firstButton);
}
}
break;

Expand All @@ -227,15 +247,13 @@ AddHIDElement(const void *value, void *parameter)
}
break;

#if 0 /* !!! FIXME: this causes everything to get added twice on a DualShock 4. */
case kIOHIDElementTypeCollection: {
CFArrayRef array = IOHIDElementGetChildren(refElement);
if (array) {
AddHIDElements(array, pDevice);
}
}
break;
#endif

default:
break;
Expand All @@ -261,6 +279,7 @@ AddHIDElement(const void *value, void *parameter)

element->minReport = element->min = (SInt32) IOHIDElementGetLogicalMin(refElement);
element->maxReport = element->max = (SInt32) IOHIDElementGetLogicalMax(refElement);
element->cookie = IOHIDElementGetCookie(refElement);

pDevice->elements++;
}
Expand Down
1 change: 1 addition & 0 deletions src/joystick/darwin/SDL_sysjoystick_c.h
Expand Up @@ -27,6 +27,7 @@
struct recElement
{
IOHIDElementRef elementRef;
IOHIDElementCookie cookie;
uint32_t usagePage, usage; /* HID usage */
SInt32 min; /* reported min value possible */
SInt32 max; /* reported max value possible */
Expand Down

0 comments on commit b67b970

Please sign in to comment.