diff --git a/input/drivers_hid/iohidmanager_hid.c b/input/drivers_hid/iohidmanager_hid.c index 356c026457..8cc5f06f92 100644 --- a/input/drivers_hid/iohidmanager_hid.c +++ b/input/drivers_hid/iohidmanager_hid.c @@ -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; iid = 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(