mirror of
https://github.com/libretro/RetroArch.git
synced 2024-12-03 22:03:04 +00:00
0ae7ffe0d3
== DETAILS - the free() method of the hid_driver_t interface needs its parameter defined as const in order for the compiler to stop complaining about losing const-ness. - if a joypad list is created with <MAX_USERS slots in it, the destroy() function will crash because it assumes there are MAX_USERS entries. To do this, the allocate function creates n+1 slots, and gives the last slot a canary value that the destroy() method can then watch for when iterating through the list.
865 lines
26 KiB
C
865 lines
26 KiB
C
/* RetroArch - A frontend for libretro.
|
|
* Copyright (C) 2013-2014 - Jason Fetters
|
|
* Copyright (C) 2011-2017 - Daniel De Matteis
|
|
*
|
|
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with RetroArch.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string/stdstring.h>
|
|
|
|
#include <IOKit/hid/IOHIDManager.h>
|
|
#include <IOKit/hid/IOHIDKeys.h>
|
|
|
|
#include <retro_miscellaneous.h>
|
|
|
|
#include "../input_defines.h"
|
|
#include "../input_driver.h"
|
|
|
|
#include "../connect/joypad_connection.h"
|
|
#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
|
|
{
|
|
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 = (uint32_t)IOHIDElementGetUsagePage((IOHIDElementRef)val1);
|
|
uint32_t page2 = (uint32_t)IOHIDElementGetUsagePage((IOHIDElementRef)val2);
|
|
uint32_t use1 = (uint32_t)IOHIDElementGetUsage((IOHIDElementRef)val1);
|
|
uint32_t use2 = (uint32_t)IOHIDElementGetUsage((IOHIDElementRef)val2);
|
|
uint32_t cookie1 = (uint32_t)IOHIDElementGetCookie((IOHIDElementRef)val1);
|
|
uint32_t cookie2 = (uint32_t)IOHIDElementGetCookie((IOHIDElementRef)val2);
|
|
|
|
if (page1 != page2)
|
|
return (CFComparisonResult)(page1 > page2);
|
|
|
|
if(use1 != use2)
|
|
return (CFComparisonResult)(use1 > use2);
|
|
|
|
return (CFComparisonResult)(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 *b)
|
|
{
|
|
apple_input_rec_t *tmp = rec;
|
|
while(tmp->next)
|
|
tmp = tmp->next;
|
|
tmp->next = b;
|
|
}
|
|
|
|
static bool iohidmanager_hid_joypad_query(void *data, unsigned pad)
|
|
{
|
|
return pad < MAX_USERS;
|
|
}
|
|
|
|
static const char *iohidmanager_hid_joypad_name(void *data, unsigned pad)
|
|
{
|
|
/* TODO/FIXME - implement properly */
|
|
if (pad >= MAX_USERS)
|
|
return NULL;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void iohidmanager_hid_joypad_get_buttons(void *data, unsigned port, retro_bits_t *state)
|
|
{
|
|
iohidmanager_hid_t *hid = (iohidmanager_hid_t*)data;
|
|
if (hid)
|
|
return pad_connection_get_buttons(&hid->slots[port], port, state);
|
|
else
|
|
BIT256_CLEAR_ALL_PTR(state);
|
|
}
|
|
|
|
static bool iohidmanager_hid_joypad_button(void *data,
|
|
unsigned port, uint16_t joykey)
|
|
{
|
|
retro_bits_t buttons;
|
|
iohidmanager_hid_t *hid = (iohidmanager_hid_t*)data;
|
|
unsigned hat_dir = GET_HAT_DIR(joykey);
|
|
|
|
iohidmanager_hid_joypad_get_buttons(data, port, &buttons);
|
|
|
|
/* Check hat. */
|
|
if (hat_dir)
|
|
{
|
|
unsigned h = GET_HAT(joykey);
|
|
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))
|
|
return (BIT256_GET(buttons, joykey) != 0)
|
|
|| ((hid->buttons[port] & (1 << joykey)) != 0);
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool iohidmanager_hid_joypad_rumble(void *data, unsigned pad,
|
|
enum retro_rumble_effect effect, uint16_t strength)
|
|
{
|
|
iohidmanager_hid_t *hid = (iohidmanager_hid_t*)data;
|
|
if (!hid)
|
|
return false;
|
|
return pad_connection_rumble(&hid->slots[pad], pad, effect, strength);
|
|
}
|
|
|
|
static int16_t iohidmanager_hid_joypad_axis(void *data,
|
|
unsigned port, uint32_t joyaxis)
|
|
{
|
|
iohidmanager_hid_t *hid = (iohidmanager_hid_t*)data;
|
|
int16_t val = 0;
|
|
|
|
if (joyaxis == AXIS_NONE)
|
|
return 0;
|
|
|
|
if (AXIS_NEG_GET(joyaxis) < 6)
|
|
{
|
|
val += hid->axes[port][AXIS_NEG_GET(joyaxis)];
|
|
val += pad_connection_get_axis(&hid->slots[port],
|
|
port, AXIS_NEG_GET(joyaxis));
|
|
|
|
if (val >= 0)
|
|
val = 0;
|
|
}
|
|
else if(AXIS_POS_GET(joyaxis) < 6)
|
|
{
|
|
val += hid->axes[port][AXIS_POS_GET(joyaxis)];
|
|
val += pad_connection_get_axis(&hid->slots[port],
|
|
port, AXIS_POS_GET(joyaxis));
|
|
|
|
if (val <= 0)
|
|
val = 0;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
static void iohidmanager_hid_device_send_control(void *data,
|
|
uint8_t* data_buf, size_t size)
|
|
{
|
|
struct iohidmanager_hid_adapter *adapter =
|
|
(struct iohidmanager_hid_adapter*)data;
|
|
|
|
if (adapter)
|
|
IOHIDDeviceSetReport(adapter->handle,
|
|
kIOHIDReportTypeOutput, 0x01, data_buf + 1, size - 1);
|
|
}
|
|
|
|
static void iohidmanager_hid_device_report(void *data,
|
|
IOReturn result, void *sender,
|
|
IOHIDReportType type, uint32_t reportID, uint8_t *report,
|
|
CFIndex reportLength)
|
|
{
|
|
struct iohidmanager_hid_adapter *adapter =
|
|
(struct iohidmanager_hid_adapter*)data;
|
|
iohidmanager_hid_t *hid = (iohidmanager_hid_t*)hid_driver_get_data();
|
|
|
|
if (hid && adapter)
|
|
pad_connection_packet(&hid->slots[adapter->slot], adapter->slot,
|
|
adapter->data, reportLength + 1);
|
|
}
|
|
|
|
/* NOTE: I pieced this together through trial and error,
|
|
* any corrections are welcome. */
|
|
|
|
static void iohidmanager_hid_device_input_callback(void *data, IOReturn result,
|
|
void* sender, IOHIDValueRef value)
|
|
{
|
|
iohidmanager_hid_t *hid = (iohidmanager_hid_t*)
|
|
hid_driver_get_data();
|
|
struct iohidmanager_hid_adapter *adapter =
|
|
(struct iohidmanager_hid_adapter*)data;
|
|
IOHIDElementRef element = IOHIDValueGetElement(value);
|
|
uint32_t type = (uint32_t)IOHIDElementGetType(element);
|
|
uint32_t page = (uint32_t)IOHIDElementGetUsagePage(element);
|
|
uint32_t use = (uint32_t)IOHIDElementGetUsage(element);
|
|
uint32_t cookie = (uint32_t)IOHIDElementGetCookie(element);
|
|
apple_input_rec_t *tmp = NULL;
|
|
|
|
if (type != kIOHIDElementTypeInput_Misc)
|
|
if (type != kIOHIDElementTypeInput_Button)
|
|
if (type != kIOHIDElementTypeInput_Axis)
|
|
return;
|
|
|
|
/* Joystick handler.
|
|
* TODO: Can GamePad work the same? */
|
|
|
|
switch (page)
|
|
{
|
|
case kHIDPage_GenericDesktop:
|
|
switch (type)
|
|
{
|
|
case kIOHIDElementTypeInput_Misc:
|
|
switch (use)
|
|
{
|
|
case kHIDUsage_GD_Hatswitch:
|
|
{
|
|
tmp = adapter->hats;
|
|
|
|
while(tmp && tmp->cookie != (IOHIDElementCookie)cookie)
|
|
tmp = tmp->next;
|
|
|
|
if(tmp->cookie == (IOHIDElementCookie)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:
|
|
tmp = adapter->axes;
|
|
|
|
while(tmp && tmp->cookie != (IOHIDElementCookie)cookie)
|
|
tmp = tmp->next;
|
|
|
|
if(tmp->cookie == (IOHIDElementCookie)cookie)
|
|
{
|
|
CFIndex min = IOHIDElementGetPhysicalMin(element);
|
|
CFIndex state = IOHIDValueGetIntegerValue(value) - min;
|
|
CFIndex max = IOHIDElementGetPhysicalMax(element) - min;
|
|
float val = (float)state / (float)max;
|
|
|
|
hid->axes[adapter->slot][tmp->id] =
|
|
((val * 2.0f) - 1.0f) * 32767.0f;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case kHIDPage_Button:
|
|
switch (type)
|
|
{
|
|
case kIOHIDElementTypeInput_Button:
|
|
tmp = adapter->buttons;
|
|
|
|
while(tmp && tmp->cookie != (IOHIDElementCookie)cookie)
|
|
tmp = tmp->next;
|
|
|
|
if(tmp->cookie == (IOHIDElementCookie)cookie)
|
|
{
|
|
CFIndex state = IOHIDValueGetIntegerValue(value);
|
|
|
|
if (state)
|
|
BIT64_SET(hid->buttons[adapter->slot], tmp->id);
|
|
else
|
|
BIT64_CLEAR(hid->buttons[adapter->slot], tmp->id);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void iohidmanager_hid_device_remove(void *data,
|
|
IOReturn result, void* sender)
|
|
{
|
|
struct iohidmanager_hid_adapter *adapter =
|
|
(struct iohidmanager_hid_adapter*)data;
|
|
iohidmanager_hid_t *hid = (iohidmanager_hid_t*)
|
|
hid_driver_get_data();
|
|
|
|
if (hid && adapter && (adapter->slot < MAX_USERS))
|
|
{
|
|
input_autoconfigure_disconnect(adapter->slot, adapter->name);
|
|
|
|
hid->buttons[adapter->slot] = 0;
|
|
memset(hid->axes[adapter->slot], 0, sizeof(hid->axes));
|
|
|
|
pad_connection_pad_deinit(&hid->slots[adapter->slot], adapter->slot);
|
|
}
|
|
|
|
if (adapter)
|
|
{
|
|
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);
|
|
}
|
|
free(adapter);
|
|
}
|
|
}
|
|
|
|
static int32_t iohidmanager_hid_device_get_int_property(
|
|
IOHIDDeviceRef device, CFStringRef key)
|
|
{
|
|
int32_t value;
|
|
CFNumberRef ref = (CFNumberRef)IOHIDDeviceGetProperty(device, key);
|
|
|
|
if (ref && (CFGetTypeID(ref) == CFNumberGetTypeID()))
|
|
{
|
|
CFNumberGetValue((CFNumberRef)ref, kCFNumberIntType, &value);
|
|
return value;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint16_t iohidmanager_hid_device_get_vendor_id(IOHIDDeviceRef device)
|
|
{
|
|
return iohidmanager_hid_device_get_int_property(device,
|
|
CFSTR(kIOHIDVendorIDKey));
|
|
}
|
|
|
|
static uint16_t iohidmanager_hid_device_get_product_id(IOHIDDeviceRef device)
|
|
{
|
|
return iohidmanager_hid_device_get_int_property(device,
|
|
CFSTR(kIOHIDProductIDKey));
|
|
}
|
|
|
|
static void iohidmanager_hid_device_get_product_string(
|
|
IOHIDDeviceRef device, char *buf, size_t len)
|
|
{
|
|
CFStringRef ref = (CFStringRef)
|
|
IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
|
|
|
|
if (ref)
|
|
CFStringGetCString(ref, buf, len, kCFStringEncodingUTF8);
|
|
}
|
|
|
|
static void iohidmanager_hid_device_add_autodetect(unsigned idx,
|
|
const char *device_name, const char *driver_name,
|
|
uint16_t dev_vid, uint16_t dev_pid)
|
|
{
|
|
if (!input_autoconfigure_connect(
|
|
device_name,
|
|
NULL,
|
|
driver_name,
|
|
idx,
|
|
dev_vid,
|
|
dev_pid
|
|
))
|
|
input_config_set_device_name(idx, device_name);
|
|
|
|
RARCH_LOG("Port %d: %s.\n", idx, device_name);
|
|
}
|
|
|
|
static void iohidmanager_hid_device_add(void *data, IOReturn result,
|
|
void* sender, IOHIDDeviceRef device)
|
|
{
|
|
int i;
|
|
IOReturn ret;
|
|
uint16_t dev_vid, dev_pid;
|
|
CFArrayRef elements_raw;
|
|
int count;
|
|
CFMutableArrayRef elements;
|
|
CFRange range;
|
|
bool found_axis[6] =
|
|
{ false, false, false, false, false, false };
|
|
apple_input_rec_t *tmp = NULL;
|
|
apple_input_rec_t *tmpButtons = NULL;
|
|
apple_input_rec_t *tmpAxes = NULL;
|
|
iohidmanager_hid_t *hid = (iohidmanager_hid_t*)
|
|
hid_driver_get_data();
|
|
struct iohidmanager_hid_adapter *adapter = (struct iohidmanager_hid_adapter*)
|
|
calloc(1, sizeof(*adapter));
|
|
|
|
if (!adapter)
|
|
return;
|
|
if (!hid)
|
|
goto error;
|
|
|
|
adapter->handle = device;
|
|
|
|
ret = IOHIDDeviceOpen(device, kIOHIDOptionsTypeNone);
|
|
|
|
if (ret != kIOReturnSuccess)
|
|
goto error;
|
|
|
|
/* Move the device's run loop to this thread. */
|
|
IOHIDDeviceScheduleWithRunLoop(device, CFRunLoopGetCurrent(),
|
|
kCFRunLoopCommonModes);
|
|
IOHIDDeviceRegisterRemovalCallback(device,
|
|
iohidmanager_hid_device_remove, adapter);
|
|
|
|
#ifndef IOS
|
|
iohidmanager_hid_device_get_product_string(device, adapter->name,
|
|
sizeof(adapter->name));
|
|
#endif
|
|
|
|
dev_vid = iohidmanager_hid_device_get_vendor_id (device);
|
|
dev_pid = iohidmanager_hid_device_get_product_id (device);
|
|
|
|
adapter->slot = pad_connection_pad_init(hid->slots,
|
|
adapter->name, dev_vid, dev_pid, adapter,
|
|
&iohidmanager_hid);
|
|
|
|
if (adapter->slot == -1)
|
|
goto error;
|
|
|
|
if (pad_connection_has_interface(hid->slots, adapter->slot))
|
|
IOHIDDeviceRegisterInputReportCallback(device,
|
|
adapter->data + 1, sizeof(adapter->data) - 1,
|
|
iohidmanager_hid_device_report, adapter);
|
|
else
|
|
IOHIDDeviceRegisterInputValueCallback(device,
|
|
iohidmanager_hid_device_input_callback, adapter);
|
|
|
|
if (string_is_empty(adapter->name))
|
|
goto error;
|
|
|
|
/* scan for buttons, axis, hats */
|
|
elements_raw = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
|
|
count = (int)CFArrayGetCount(elements_raw);
|
|
elements = CFArrayCreateMutableCopy(
|
|
kCFAllocatorDefault,(CFIndex)count,elements_raw);
|
|
range = CFRangeMake(0,count);
|
|
|
|
CFArraySortValues(elements,
|
|
range, iohidmanager_sort_elements, NULL);
|
|
|
|
for(i=0; i<count; i++)
|
|
{
|
|
IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
|
|
|
|
if (!element)
|
|
continue;
|
|
|
|
IOHIDElementType type = IOHIDElementGetType(element);
|
|
uint32_t page = (uint32_t)IOHIDElementGetUsagePage(element);
|
|
uint32_t use = (uint32_t)IOHIDElementGetUsage(element);
|
|
uint32_t cookie = (uint32_t)IOHIDElementGetCookie(element);
|
|
|
|
switch (page)
|
|
{
|
|
case kHIDPage_GenericDesktop:
|
|
switch (type)
|
|
{
|
|
case kIOHIDElementTypeCollection:
|
|
case kIOHIDElementTypeInput_ScanCodes:
|
|
case kIOHIDElementTypeFeature:
|
|
case kIOHIDElementTypeInput_Button:
|
|
case kIOHIDElementTypeOutput:
|
|
case kIOHIDElementTypeInput_Axis:
|
|
/* TODO/FIXME */
|
|
break;
|
|
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 = (IOHIDElementCookie)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 = (IOHIDElementCookie)cookie;
|
|
axis->next = NULL;
|
|
|
|
if(iohidmanager_check_for_id(adapter->axes,i))
|
|
{
|
|
/* axis ID already exists, save to tmp for appending later */
|
|
if(tmpAxes)
|
|
iohidmanager_append_record(tmpAxes, axis);
|
|
else
|
|
tmpAxes = axis;
|
|
}
|
|
else
|
|
{
|
|
found_axis[axis->id] = true;
|
|
if(adapter->axes)
|
|
iohidmanager_append_record(adapter->axes, axis);
|
|
else
|
|
adapter->axes = axis;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case kHIDPage_Button:
|
|
switch (type)
|
|
{
|
|
case kIOHIDElementTypeCollection:
|
|
case kIOHIDElementTypeFeature:
|
|
case kIOHIDElementTypeInput_ScanCodes:
|
|
case kIOHIDElementTypeInput_Misc:
|
|
case kIOHIDElementTypeInput_Axis:
|
|
case kIOHIDElementTypeOutput:
|
|
/* TODO/FIXME */
|
|
break;
|
|
case kIOHIDElementTypeInput_Button:
|
|
{
|
|
apple_input_rec_t *btn = (apple_input_rec_t *)malloc(sizeof(apple_input_rec_t));
|
|
btn->id = (uint32_t)(use - 1);
|
|
btn->cookie = (IOHIDElementCookie)cookie;
|
|
btn->next = NULL;
|
|
|
|
if(iohidmanager_check_for_id(adapter->buttons,btn->id))
|
|
{
|
|
if(tmpButtons)
|
|
iohidmanager_append_record(tmpButtons, btn);
|
|
else
|
|
tmpButtons = btn;
|
|
}
|
|
else
|
|
{
|
|
if(adapter->buttons)
|
|
iohidmanager_append_record(adapter->buttons, btn);
|
|
else
|
|
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;
|
|
}
|
|
}
|
|
|
|
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:
|
|
{
|
|
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(
|
|
CFMutableArrayRef array,
|
|
uint32_t page, uint32_t use)
|
|
{
|
|
CFMutableDictionaryRef matcher = CFDictionaryCreateMutable(
|
|
kCFAllocatorDefault, 0,
|
|
&kCFTypeDictionaryKeyCallBacks,
|
|
&kCFTypeDictionaryValueCallBacks);
|
|
CFNumberRef pagen = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
|
|
CFNumberRef usen = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &use);
|
|
|
|
CFDictionarySetValue(matcher, CFSTR(kIOHIDDeviceUsagePageKey), pagen);
|
|
CFDictionarySetValue(matcher, CFSTR(kIOHIDDeviceUsageKey), usen);
|
|
CFArrayAppendValue(array, matcher);
|
|
|
|
CFRelease(pagen);
|
|
CFRelease(usen);
|
|
CFRelease(matcher);
|
|
}
|
|
|
|
static int iohidmanager_hid_manager_init(iohidmanager_hid_t *hid)
|
|
{
|
|
if (!hid)
|
|
return -1;
|
|
if (hid->ptr) /* already initialized. */
|
|
return 0;
|
|
|
|
hid->ptr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
|
|
|
|
if (!hid->ptr)
|
|
return -1;
|
|
|
|
IOHIDManagerSetDeviceMatching(hid->ptr, NULL);
|
|
IOHIDManagerScheduleWithRunLoop(hid->ptr, CFRunLoopGetCurrent(),
|
|
kCFRunLoopDefaultMode);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int iohidmanager_hid_manager_free(iohidmanager_hid_t *hid)
|
|
{
|
|
if (!hid || !hid->ptr)
|
|
return -1;
|
|
|
|
IOHIDManagerUnscheduleFromRunLoop(hid->ptr,
|
|
CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
|
|
IOHIDManagerClose(hid->ptr, kIOHIDOptionsTypeNone);
|
|
CFRelease(hid->ptr);
|
|
hid->ptr = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int iohidmanager_hid_manager_set_device_matching(
|
|
iohidmanager_hid_t *hid)
|
|
{
|
|
CFMutableArrayRef matcher = CFArrayCreateMutable(kCFAllocatorDefault, 0,
|
|
&kCFTypeArrayCallBacks);
|
|
|
|
if (!matcher)
|
|
return -1;
|
|
|
|
iohidmanager_hid_append_matching_dictionary(matcher,
|
|
kHIDPage_GenericDesktop,
|
|
kHIDUsage_GD_Joystick);
|
|
iohidmanager_hid_append_matching_dictionary(matcher,
|
|
kHIDPage_GenericDesktop,
|
|
kHIDUsage_GD_GamePad);
|
|
|
|
IOHIDManagerSetDeviceMatchingMultiple(hid->ptr, matcher);
|
|
IOHIDManagerRegisterDeviceMatchingCallback(hid->ptr,
|
|
iohidmanager_hid_device_add, 0);
|
|
|
|
CFRelease(matcher);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void *iohidmanager_hid_init(void)
|
|
{
|
|
iohidmanager_hid_t *hid_apple = (iohidmanager_hid_t*)
|
|
calloc(1, sizeof(*hid_apple));
|
|
|
|
if (!hid_apple)
|
|
goto error;
|
|
hid_apple->slots = pad_connection_init(MAX_USERS);
|
|
|
|
if (!hid_apple->slots)
|
|
goto error;
|
|
if (iohidmanager_hid_manager_init(hid_apple) == -1)
|
|
goto error;
|
|
if (iohidmanager_hid_manager_set_device_matching(hid_apple) == -1)
|
|
goto error;
|
|
|
|
return hid_apple;
|
|
|
|
error:
|
|
if (hid_apple->slots)
|
|
free(hid_apple->slots);
|
|
hid_apple->slots = NULL;
|
|
if (hid_apple)
|
|
free(hid_apple);
|
|
return NULL;
|
|
}
|
|
|
|
static void iohidmanager_hid_free(const void *data)
|
|
{
|
|
iohidmanager_hid_t *hid_apple = (iohidmanager_hid_t*)data;
|
|
|
|
if (!hid_apple || !hid_apple->ptr)
|
|
return;
|
|
|
|
pad_connection_destroy(hid_apple->slots);
|
|
iohidmanager_hid_manager_free(hid_apple);
|
|
|
|
if (hid_apple)
|
|
free(hid_apple);
|
|
}
|
|
|
|
static void iohidmanager_hid_poll(void *data)
|
|
{
|
|
(void)data;
|
|
}
|
|
|
|
hid_driver_t iohidmanager_hid = {
|
|
iohidmanager_hid_init,
|
|
iohidmanager_hid_joypad_query,
|
|
iohidmanager_hid_free,
|
|
iohidmanager_hid_joypad_button,
|
|
iohidmanager_hid_joypad_get_buttons,
|
|
iohidmanager_hid_joypad_axis,
|
|
iohidmanager_hid_poll,
|
|
iohidmanager_hid_joypad_rumble,
|
|
iohidmanager_hid_joypad_name,
|
|
"iohidmanager",
|
|
iohidmanager_hid_device_send_control,
|
|
};
|