Merge pull request #4426 from jprjr/osx-iohidmanager

OSX Joypad Improvements
This commit is contained in:
Twinaphex 2017-01-15 19:49:06 +01:00 committed by GitHub
commit ddc11e2d17

View File

@ -31,12 +31,20 @@
#include "../../tasks/tasks_internal.h"
#include "../../verbosity.h"
typedef struct apple_input_rec
{
IOHIDElementCookie cookie;
uint32_t id;
struct apple_input_rec *next;
} apple_input_rec_t;
typedef struct apple_hid
{
IOHIDManagerRef ptr;
joypad_connection_t *slots;
uint32_t buttons[MAX_USERS];
int16_t axes[MAX_USERS][6];
int8_t hats[MAX_USERS][2]; /* MacOS only supports 1 hat AFAICT */
} iohidmanager_hid_t;
struct iohidmanager_hid_adapter
@ -44,9 +52,53 @@ struct iohidmanager_hid_adapter
uint32_t slot;
IOHIDDeviceRef handle;
char name[PATH_MAX_LENGTH];
apple_input_rec_t *axes;
apple_input_rec_t *hats;
apple_input_rec_t *buttons;
uint8_t data[2048];
};
CFComparisonResult iohidmanager_sort_elements(const void *val1, const void *val2, void *context)
{
uint32_t page1 = IOHIDElementGetUsagePage((IOHIDElementRef)val1);
uint32_t page2 = IOHIDElementGetUsagePage((IOHIDElementRef)val2);
uint32_t use1 = IOHIDElementGetUsage((IOHIDElementRef)val1);
uint32_t use2 = IOHIDElementGetUsage((IOHIDElementRef)val2);
uint32_t cookie1 = IOHIDElementGetCookie((IOHIDElementRef)val1);
uint32_t cookie2 = IOHIDElementGetCookie((IOHIDElementRef)val2);
if(page1 != page2)
{
return page1 > page2;
}
if(use1 != use2)
{
return use1 > use2;
}
return cookie1 > cookie2;
}
static bool iohidmanager_check_for_id(apple_input_rec_t *rec, uint32_t id)
{
while(rec) {
if(rec->id == id) return true;
rec = rec->next;
}
return false;
}
static void iohidmanager_append_record(apple_input_rec_t *rec, apple_input_rec_t *new)
{
apple_input_rec_t *tmp = rec;
while(tmp->next)
{
tmp = tmp->next;
}
tmp->next = new;
}
static bool iohidmanager_hid_joypad_query(void *data, unsigned pad)
{
return pad < MAX_USERS;
@ -75,10 +127,28 @@ static bool iohidmanager_hid_joypad_button(void *data,
uint64_t buttons =
iohidmanager_hid_joypad_get_buttons(data, port);
iohidmanager_hid_t *hid = (iohidmanager_hid_t*)data;
unsigned h = GET_HAT(joykey);
unsigned hat_dir = GET_HAT_DIR(joykey);
/* Check hat. */
if (GET_HAT_DIR(joykey))
return false;
if (hat_dir)
{
if(h >= 1) {
return false;
}
switch(hat_dir)
{
case HAT_LEFT_MASK:
return hid->hats[port][0] < 0;
case HAT_RIGHT_MASK:
return hid->hats[port][0] > 0;
case HAT_UP_MASK:
return hid->hats[port][1] < 0;
case HAT_DOWN_MASK:
return hid->hats[port][1] > 0;
}
return 0;
}
/* Check the button. */
if ((port < MAX_USERS) && (joykey < 32))
@ -166,6 +236,8 @@ static void iohidmanager_hid_device_input_callback(void *data, IOReturn result,
uint32_t type = IOHIDElementGetType(element);
uint32_t page = IOHIDElementGetUsagePage(element);
uint32_t use = IOHIDElementGetUsage(element);
uint32_t cookie = IOHIDElementGetCookie(element);
apple_input_rec_t *tmp = NULL;
if (type != kIOHIDElementTypeInput_Misc)
if (type != kIOHIDElementTypeInput_Button)
@ -184,32 +256,86 @@ static void iohidmanager_hid_device_input_callback(void *data, IOReturn result,
switch (use)
{
case kHIDUsage_GD_Hatswitch:
break;
{
tmp = adapter->hats;
while(tmp && tmp->cookie != cookie)
{
tmp = tmp->next;
}
if(tmp->cookie == cookie)
{
CFIndex range = IOHIDElementGetLogicalMax(element) - IOHIDElementGetLogicalMin(element);
CFIndex val = IOHIDValueGetIntegerValue(value);
if(range == 3)
{
val *= 2;
}
switch(val)
{
case 0:
/* pos = up */
hid->hats[adapter->slot][0] = 0;
hid->hats[adapter->slot][1] = -1;
break;
case 1:
/* pos = up+right */
hid->hats[adapter->slot][0] = 1;
hid->hats[adapter->slot][1] = -1;
break;
case 2:
/* pos = right */
hid->hats[adapter->slot][0] = 1;
hid->hats[adapter->slot][1] = 0;
break;
case 3:
/* pos = down+right */
hid->hats[adapter->slot][0] = 1;
hid->hats[adapter->slot][1] = 1;
break;
case 4:
/* pos = down */
hid->hats[adapter->slot][0] = 0;
hid->hats[adapter->slot][1] = 1;
break;
case 5:
/* pos = down+left */
hid->hats[adapter->slot][0] = -1;
hid->hats[adapter->slot][1] = 1;
break;
case 6:
/* pos = left */
hid->hats[adapter->slot][0] = -1;
hid->hats[adapter->slot][1] = 0;
break;
case 7:
/* pos = up_left */
hid->hats[adapter->slot][0] = -1;
hid->hats[adapter->slot][1] = -1;
break;
default:
/* pos = centered */
hid->hats[adapter->slot][0] = 0;
hid->hats[adapter->slot][1] = 0;
break;
}
}
}
break;
default:
{
int i;
static const uint32_t axis_use_ids[6] =
{ 48, 49, 51, 52, 50, 53 };
/* +0/-0 => Left Stick Horizontal => 48
* +1/-1 => Left Stick Vertical => 49
* +2/-2 => Right Stick Horizontal => 51
* +3/-3 => Right Stick Vertical => 52
* +4/-4 => Left Trigger (if exists) => 50
* +5/-5 => Right Trigger (if exists) => 53
*/
for (i = 0; i < 6; i ++)
tmp = adapter->axes;
while(tmp && tmp->cookie != cookie)
{
tmp = tmp->next;
}
if(tmp->cookie == cookie)
{
CFIndex min = IOHIDElementGetPhysicalMin(element);
CFIndex state = IOHIDValueGetIntegerValue(value) - min;
CFIndex max = IOHIDElementGetPhysicalMax(element) - min;
float val = (float)state / (float)max;
if (use != axis_use_ids[i])
continue;
hid->axes[adapter->slot][i] =
hid->axes[adapter->slot][tmp->id] =
((val * 2.0f) - 1.0f) * 32767.0f;
}
}
@ -223,13 +349,20 @@ static void iohidmanager_hid_device_input_callback(void *data, IOReturn result,
{
case kIOHIDElementTypeInput_Button:
{
CFIndex state = IOHIDValueGetIntegerValue(value);
unsigned id = use - 1;
tmp = adapter->buttons;
while(tmp && tmp->cookie != cookie)
{
tmp = tmp->next;
}
if(tmp->cookie == cookie)
{
CFIndex state = IOHIDValueGetIntegerValue(value);
if (state)
BIT64_SET(hid->buttons[adapter->slot], id);
else
BIT64_CLEAR(hid->buttons[adapter->slot], id);
if (state)
BIT64_SET(hid->buttons[adapter->slot], tmp->id);
else
BIT64_CLEAR(hid->buttons[adapter->slot], tmp->id);
}
}
break;
}
@ -256,7 +389,28 @@ static void iohidmanager_hid_device_remove(void *data,
}
if (adapter)
{
struct apple_input_rec_t* tmp;
while(adapter->hats != NULL)
{
tmp = adapter->hats;
adapter->hats = adapter->hats->next;
free(tmp);
}
while(adapter->axes != NULL)
{
tmp = adapter->axes;
adapter->axes = adapter->axes->next;
free(tmp);
}
while(adapter->buttons != NULL)
{
tmp = adapter->buttons;
adapter->buttons = adapter->buttons->next;
free(tmp);
}
free(adapter);
}
}
static int32_t iohidmanager_hid_device_get_int_property(
@ -367,13 +521,205 @@ static void iohidmanager_hid_device_add(void *data, IOReturn result,
if (string_is_empty(adapter->name))
goto error;
/* scan for buttons, axis, hats */
CFArrayRef elements_raw = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
int count = (int)CFArrayGetCount(elements_raw);
CFMutableArrayRef elements = CFArrayCreateMutableCopy(kCFAllocatorDefault,(CFIndex)count,elements_raw);
CFRange range = CFRangeMake(0,count);
CFArraySortValues(elements,range,iohidmanager_sort_elements,NULL);
int i;
bool found_axis[6] =
{ false, false, false, false, false, false };
apple_input_rec_t *tmpButtons = NULL;
apple_input_rec_t *tmpAxes = NULL;
for(i=0; i<count; i++) {
IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
if (!element) continue;
IOHIDElementType type = IOHIDElementGetType(element);
uint32_t page = IOHIDElementGetUsagePage(element);
uint32_t use = IOHIDElementGetUsage(element);
uint32_t cookie = IOHIDElementGetCookie(element);
switch (page)
{
case kHIDPage_GenericDesktop:
switch (type)
{
case kIOHIDElementTypeInput_Misc:
switch (use)
{
case kHIDUsage_GD_Hatswitch:
{
/* as far as I can tell, OSX only reports one Hat */
apple_input_rec_t *hat = (apple_input_rec_t *)malloc(sizeof(apple_input_rec_t));
hat->id = 0;
hat->cookie = cookie;
hat->next = NULL;
adapter->hats = hat;
}
break;
default:
{
uint32_t i = 0;
static const uint32_t axis_use_ids[6] =
{ 48, 49, 51, 52, 50, 53 };
while(axis_use_ids[i] != use)
{
i++;
}
if (i < 6)
{
apple_input_rec_t *axis = (apple_input_rec_t *)malloc(sizeof(apple_input_rec_t));
axis->id = i;
axis->cookie = cookie;
axis->next = NULL;
if(iohidmanager_check_for_id(adapter->axes,i))
{
/* axis ID already exists, save to tmp for appending later */
if(tmpAxes == NULL)
{
tmpAxes = axis;
}
else
{
iohidmanager_append_record(tmpAxes,axis);
}
}
else
{
found_axis[axis->id] = true;
if(adapter->axes == NULL)
{
adapter->axes = axis;
}
else
{
iohidmanager_append_record(adapter->axes,axis);
}
}
}
}
break;
}
break;
}
break;
case kHIDPage_Button:
switch (type)
{
case kIOHIDElementTypeInput_Button:
{
apple_input_rec_t *btn = (apple_input_rec_t *)malloc(sizeof(apple_input_rec_t));
btn->id = use - 1;
btn->cookie = cookie;
btn->next = NULL;
if(iohidmanager_check_for_id(adapter->buttons,btn->id))
{
if(tmpButtons == NULL)
{
tmpButtons = btn;
}
else
{
iohidmanager_append_record(tmpButtons,btn);
}
}
else
{
if(adapter->buttons == NULL)
{
adapter->buttons = btn;
}
else
{
iohidmanager_append_record(adapter->buttons,btn);
}
}
}
break;
}
break;
}
}
/* take care of buttons/axes with duplicate 'use' values */
for(i=0; i<6; i++)
{
if(found_axis[i] == false && tmpAxes)
{
apple_input_rec_t *next = tmpAxes->next;
tmpAxes->id = i;
tmpAxes->next = NULL;
iohidmanager_append_record(adapter->axes, tmpAxes);
tmpAxes = next;
}
}
apple_input_rec_t *tmp = adapter->buttons;
while(tmp->next)
{
tmp = tmp->next;
}
while(tmpButtons)
{
apple_input_rec_t *next = tmpButtons->next;
tmpButtons->id = tmp->id + 1;
tmpButtons->next = NULL;
tmp->next = tmpButtons;
tmp = tmp->next;
tmpButtons = next;
}
iohidmanager_hid_device_add_autodetect(adapter->slot,
adapter->name, iohidmanager_hid.ident, dev_vid, dev_pid);
return;
error:
free(adapter);
{
struct apple_input_rec_t *tmp = NULL;
while(adapter->hats != NULL)
{
tmp = adapter->hats;
adapter->hats = adapter->hats->next;
free(tmp);
}
while(adapter->axes != NULL)
{
tmp = adapter->axes;
adapter->axes = adapter->axes->next;
free(tmp);
}
while(adapter->buttons != NULL)
{
tmp = adapter->buttons;
adapter->buttons = adapter->buttons->next;
free(tmp);
}
while(tmpAxes != NULL)
{
tmp = tmpAxes;
tmpAxes = tmpAxes->next;
free(tmp);
}
while(tmpButtons != NULL)
{
tmp = tmpButtons;
tmpButtons = tmpButtons->next;
free(tmp);
}
free(adapter);
}
}
static void iohidmanager_hid_append_matching_dictionary(