1
0
mirror of https://github.com/libretro/RetroArch synced 2025-04-02 16:20:39 +00:00

Implement changes to fix macOS controller duplication bug

This commit is contained in:
Adie 2021-08-06 22:02:43 -05:00
parent 1661d4e969
commit ce4fce12c0
3 changed files with 165 additions and 163 deletions

@ -26,9 +26,9 @@
static bool joypad_is_end_of_list(joypad_connection_t *pad) static bool joypad_is_end_of_list(joypad_connection_t *pad)
{ {
return pad return pad
&& !pad->connected && !pad->connected
&& !pad->iface && !pad->iface
&& (pad->data == (void *)0xdeadbeef); && (pad->data == (void *)0xdeadbeef);
} }
@ -183,8 +183,10 @@ int32_t pad_connection_pad_init(joypad_connection_t *joyconn,
if (name_match || (pad_map[i].vid == vid && pad_map[i].pid == pid)) if (name_match || (pad_map[i].vid == vid && pad_map[i].pid == pid))
{ {
RARCH_DBG("Pad was matched to \"%s\". Setting up an interface.\n", name_match);
s->iface = pad_map[i].iface; s->iface = pad_map[i].iface;
s->data = s->iface->init(data, pad, driver); s->data = data;
s->connection = s->iface->init(data, pad, driver);
s->connected = true; s->connected = true;
#if 0 #if 0
RARCH_LOG("%s found \n", pad_map[i].name); RARCH_LOG("%s found \n", pad_map[i].name);
@ -203,6 +205,7 @@ int32_t pad_connection_pad_init(joypad_connection_t *joyconn,
* set up one without an interface */ * set up one without an interface */
if (!s->connected) if (!s->connected)
{ {
RARCH_DBG("Pad was not matched. Setting up without an interface.\n");
s->iface = NULL; s->iface = NULL;
s->data = data; s->data = data;
s->connected = true; s->connected = true;
@ -219,15 +222,16 @@ void pad_connection_pad_deinit(joypad_connection_t *joyconn, uint32_t pad)
if (joyconn->iface) if (joyconn->iface)
{ {
joyconn->iface->set_rumble(joyconn->data, RETRO_RUMBLE_STRONG, 0); joyconn->iface->set_rumble(joyconn->connection, RETRO_RUMBLE_STRONG, 0);
joyconn->iface->set_rumble(joyconn->data, RETRO_RUMBLE_WEAK, 0); joyconn->iface->set_rumble(joyconn->connection, RETRO_RUMBLE_WEAK, 0);
if (joyconn->iface->deinit) if (joyconn->iface->deinit)
joyconn->iface->deinit(joyconn->data); joyconn->iface->deinit(joyconn->connection);
} }
joyconn->iface = NULL; joyconn->iface = NULL;
joyconn->connected = false; joyconn->connected = false;
joyconn->connection = NULL;
} }
void pad_connection_packet(joypad_connection_t *joyconn, uint32_t pad, void pad_connection_packet(joypad_connection_t *joyconn, uint32_t pad,
@ -235,17 +239,17 @@ void pad_connection_packet(joypad_connection_t *joyconn, uint32_t pad,
{ {
if (!joyconn || !joyconn->connected) if (!joyconn || !joyconn->connected)
return; return;
if (joyconn->iface && joyconn->data && joyconn->iface->packet_handler) if (joyconn->iface && joyconn->connection && joyconn->iface->packet_handler)
joyconn->iface->packet_handler(joyconn->data, data, length); joyconn->iface->packet_handler(joyconn->connection, data, length);
} }
void pad_connection_get_buttons(joypad_connection_t *joyconn, void pad_connection_get_buttons(joypad_connection_t *joyconn,
unsigned pad, input_bits_t *state) unsigned pad, input_bits_t *state)
{ {
if (joyconn && joyconn->iface) if (joyconn && joyconn->iface)
joyconn->iface->get_buttons(joyconn->data, state); joyconn->iface->get_buttons(joyconn->connection, state);
else else
BIT256_CLEAR_ALL_PTR( state ); BIT256_CLEAR_ALL_PTR( state );
} }
int16_t pad_connection_get_axis(joypad_connection_t *joyconn, int16_t pad_connection_get_axis(joypad_connection_t *joyconn,
@ -253,7 +257,7 @@ int16_t pad_connection_get_axis(joypad_connection_t *joyconn,
{ {
if (!joyconn || !joyconn->iface) if (!joyconn || !joyconn->iface)
return 0; return 0;
return joyconn->iface->get_axis(joyconn->data, i); return joyconn->iface->get_axis(joyconn->connection, i);
} }
bool pad_connection_has_interface(joypad_connection_t *joyconn, unsigned pad) bool pad_connection_has_interface(joypad_connection_t *joyconn, unsigned pad)
@ -283,7 +287,7 @@ bool pad_connection_rumble(joypad_connection_t *joyconn,
if (!joyconn->iface || !joyconn->iface->set_rumble) if (!joyconn->iface || !joyconn->iface->set_rumble)
return false; return false;
joyconn->iface->set_rumble(joyconn->data, effect, strength); joyconn->iface->set_rumble(joyconn->connection, effect, strength);
return true; return true;
} }
@ -291,5 +295,5 @@ const char* pad_connection_get_name(joypad_connection_t *joyconn, unsigned pad)
{ {
if (!joyconn || !joyconn->iface || !joyconn->iface->get_name) if (!joyconn || !joyconn->iface || !joyconn->iface->get_name)
return NULL; return NULL;
return joyconn->iface->get_name(joyconn->data); return joyconn->iface->get_name(joyconn->connection);
} }

@ -59,6 +59,7 @@ struct joypad_connection
{ {
struct pad_connection_interface *iface; struct pad_connection_interface *iface;
void* data; void* data;
void* connection;
bool connected; bool connected;
}; };

@ -33,9 +33,9 @@
typedef struct apple_input_rec typedef struct apple_input_rec
{ {
IOHIDElementCookie cookie; IOHIDElementCookie cookie;
uint32_t id; uint32_t id;
struct apple_input_rec *next; struct apple_input_rec *next;
} apple_input_rec_t; } apple_input_rec_t;
typedef struct apple_hid typedef struct apple_hid
@ -51,14 +51,12 @@ struct iohidmanager_hid_adapter
{ {
uint32_t slot; uint32_t slot;
IOHIDDeviceRef handle; IOHIDDeviceRef handle;
uint32_t locationId;
char name[PATH_MAX_LENGTH]; char name[PATH_MAX_LENGTH];
apple_input_rec_t *axes; apple_input_rec_t *axes;
apple_input_rec_t *hats; apple_input_rec_t *hats;
apple_input_rec_t *buttons; apple_input_rec_t *buttons;
uint8_t data[2048]; uint8_t data[2048];
#if !(defined(__ppc__) || defined(__ppc64__))
uint32_t uniqueId;
#endif
}; };
CFComparisonResult iohidmanager_sort_elements(const void *val1, const void *val2, void *context) CFComparisonResult iohidmanager_sort_elements(const void *val1, const void *val2, void *context)
@ -234,11 +232,11 @@ static int16_t iohidmanager_hid_joypad_state(
const uint32_t joyaxis = (binds[i].joyaxis != AXIS_NONE) const uint32_t joyaxis = (binds[i].joyaxis != AXIS_NONE)
? binds[i].joyaxis : joypad_info->auto_binds[i].joyaxis; ? binds[i].joyaxis : joypad_info->auto_binds[i].joyaxis;
if ( if (
(uint16_t)joykey != NO_BTN (uint16_t)joykey != NO_BTN
&& iohidmanager_hid_joypad_button(data, port_idx, (uint16_t)joykey)) && iohidmanager_hid_joypad_button(data, port_idx, (uint16_t)joykey))
ret |= ( 1 << i); ret |= ( 1 << i);
else if (joyaxis != AXIS_NONE && else if (joyaxis != AXIS_NONE &&
((float)abs(iohidmanager_hid_joypad_axis(data, port_idx, joyaxis)) ((float)abs(iohidmanager_hid_joypad_axis(data, port_idx, joyaxis))
/ 0x8000) > joypad_info->axis_threshold) / 0x8000) > joypad_info->axis_threshold)
ret |= (1 << i); ret |= (1 << i);
} }
@ -477,14 +475,29 @@ static void iohidmanager_hid_device_input_callback(void *data, IOReturn result,
} }
} }
static void iohidmanager_hid_device_remove(void *data, static void iohidmanager_hid_device_remove(IOHIDDeviceRef device, iohidmanager_hid_t* hid)
IOReturn result, void* sender)
{ {
struct iohidmanager_hid_adapter *adapter = struct iohidmanager_hid_adapter *adapter = NULL;
(struct iohidmanager_hid_adapter*)data; int i;
iohidmanager_hid_t *hid = (iohidmanager_hid_t*)
hid_driver_get_data(); /*loop though the controller ports and find the device with a matching IOHINDeviceRef*/
for (i=0; i<MAX_USERS; i++)
{
struct iohidmanager_hid_adapter *a = (struct iohidmanager_hid_adapter*)hid->slots[i].data;
if (!a)
continue;
if (a->handle == device)
{
adapter = a;
break;
}
}
if (!adapter)
{
RARCH_LOG("Error removing device %p\n",device, hid);
return;
}
int slot = adapter->slot;
if (hid && adapter && (adapter->slot < MAX_USERS)) if (hid && adapter && (adapter->slot < MAX_USERS))
{ {
input_autoconfigure_disconnect(adapter->slot, adapter->name); input_autoconfigure_disconnect(adapter->slot, adapter->name);
@ -520,6 +533,7 @@ static void iohidmanager_hid_device_remove(void *data,
} }
free(adapter); free(adapter);
} }
RARCH_LOG("Device removed from port %d\n", slot);
} }
static int32_t iohidmanager_hid_device_get_int_property( static int32_t iohidmanager_hid_device_get_int_property(
@ -555,15 +569,7 @@ static uint32_t iohidmanager_hid_device_get_location_id(IOHIDDeviceRef device)
CFSTR(kIOHIDLocationIDKey)); CFSTR(kIOHIDLocationIDKey));
} }
#if !(defined(__ppc__) || defined(__ppc64__))
static uint32_t iohidmanager_hid_device_get_unique_id(IOHIDDeviceRef device)
{
/* osx seems to assign an unique id to each device when they are plugged in
* the id change if device is unplugged/plugged, but it's unique amongst the
* other device plugged */
return iohidmanager_hid_device_get_int_property(device,CFSTR(kIOHIDUniqueIDKey));
}
#endif
static void iohidmanager_hid_device_get_product_string( static void iohidmanager_hid_device_get_product_string(
IOHIDDeviceRef device, char *buf, size_t len) IOHIDDeviceRef device, char *buf, size_t len)
@ -591,21 +597,11 @@ static void iohidmanager_hid_device_add_autodetect(unsigned idx,
RARCH_LOG("Port %d: %s.\n", idx, device_name); RARCH_LOG("Port %d: %s.\n", idx, device_name);
} }
#if defined(__ppc__) || defined(__ppc64__)
static void iohidmanager_hid_device_add(IOHIDDeviceRef device, static void iohidmanager_hid_device_add(IOHIDDeviceRef device, iohidmanager_hid_t* hid)
iohidmanager_hid_t* hid)
#else
static void iohidmanager_hid_device_add_device(
IOHIDDeviceRef device, iohidmanager_hid_t* hid)
#endif
{ {
int i; int i;
/* get device unique id */
#if !(defined(__ppc__) || defined(__ppc64__))
uint32_t deviceUniqueId = iohidmanager_hid_device_get_unique_id(device);
#endif
static const uint32_t axis_use_ids[11] = static const uint32_t axis_use_ids[11] =
{ {
kHIDUsage_GD_X, kHIDUsage_GD_X,
@ -621,18 +617,22 @@ static void iohidmanager_hid_device_add_device(
kHIDUsage_Sim_Brake kHIDUsage_Sim_Brake
}; };
#if !(defined(__ppc__) || defined(__ppc64__)) /* check if pad was already registered previously when the application was
/* check if pad was already registered previously (by deterministic method) started (by deterministic method). if so do not re-add the pad */
* if so do not re-add the pad */ uint32_t deviceLocationId = iohidmanager_hid_device_get_location_id(device);
for (i=0; i<MAX_USERS; i++) for (i=0; i<MAX_USERS; i++)
{ {
struct iohidmanager_hid_adapter *a = (struct iohidmanager_hid_adapter*)hid->slots[i].data; struct iohidmanager_hid_adapter *a = (struct iohidmanager_hid_adapter*)hid->slots[i].data;
if (!a) if (!a)
continue; continue;
if (a->uniqueId == deviceUniqueId) if (a->locationId == deviceLocationId)
return; {
} a->handle = device;
#endif /* while we're not re-adding the controller, we are re-assigning the
handle so it can be removed properly upon disconnect */
return;
}
}
IOReturn ret; IOReturn ret;
uint16_t dev_vid, dev_pid; uint16_t dev_vid, dev_pid;
@ -654,6 +654,7 @@ static void iohidmanager_hid_device_add_device(
goto error; goto error;
adapter->handle = device; adapter->handle = device;
adapter->locationId = deviceLocationId;
ret = IOHIDDeviceOpen(device, kIOHIDOptionsTypeNone); ret = IOHIDDeviceOpen(device, kIOHIDOptionsTypeNone);
@ -663,8 +664,6 @@ static void iohidmanager_hid_device_add_device(
/* Move the device's run loop to this thread. */ /* Move the device's run loop to this thread. */
IOHIDDeviceScheduleWithRunLoop(device, CFRunLoopGetCurrent(), IOHIDDeviceScheduleWithRunLoop(device, CFRunLoopGetCurrent(),
kCFRunLoopCommonModes); kCFRunLoopCommonModes);
IOHIDDeviceRegisterRemovalCallback(device,
iohidmanager_hid_device_remove, adapter);
#ifndef IOS #ifndef IOS
iohidmanager_hid_device_get_product_string(device, adapter->name, iohidmanager_hid_device_get_product_string(device, adapter->name,
@ -673,9 +672,6 @@ static void iohidmanager_hid_device_add_device(
dev_vid = iohidmanager_hid_device_get_vendor_id (device); dev_vid = iohidmanager_hid_device_get_vendor_id (device);
dev_pid = iohidmanager_hid_device_get_product_id (device); dev_pid = iohidmanager_hid_device_get_product_id (device);
#if !(defined(__ppc__) || defined(__ppc64__))
adapter->uniqueId = deviceUniqueId;
#endif
adapter->slot = pad_connection_pad_init(hid->slots, adapter->slot = pad_connection_pad_init(hid->slots,
adapter->name, dev_vid, dev_pid, adapter, adapter->name, dev_vid, dev_pid, adapter,
@ -900,7 +896,6 @@ static void iohidmanager_hid_device_add_device(
iohidmanager_hid_device_add_autodetect(adapter->slot, iohidmanager_hid_device_add_autodetect(adapter->slot,
adapter->name, iohidmanager_hid.ident, dev_vid, dev_pid); adapter->name, iohidmanager_hid.ident, dev_vid, dev_pid);
return; return;
error: error:
@ -940,14 +935,21 @@ error:
} }
} }
#if !(defined(__ppc__) || defined(__ppc64__))
static void iohidmanager_hid_device_add(void *data, IOReturn result, static void iohidmanager_hid_device_matched(void *data, IOReturn result,
void* sender, IOHIDDeviceRef device) void* sender, IOHIDDeviceRef device)
{ {
iohidmanager_hid_t *hid = (iohidmanager_hid_t*) hid_driver_get_data(); iohidmanager_hid_t *hid = (iohidmanager_hid_t*) hid_driver_get_data();
iohidmanager_hid_device_add_device(device, hid);
iohidmanager_hid_device_add(device, hid);
} }
#endif static void iohidmanager_hid_device_removed(void *data, IOReturn result,
void* sender, IOHIDDeviceRef device)
{
iohidmanager_hid_t *hid = (iohidmanager_hid_t*) hid_driver_get_data();
iohidmanager_hid_device_remove(device, hid);
}
static void iohidmanager_hid_append_matching_dictionary( static void iohidmanager_hid_append_matching_dictionary(
CFMutableArrayRef array, CFMutableArrayRef array,
@ -980,7 +982,6 @@ static int iohidmanager_hid_manager_init(iohidmanager_hid_t *hid)
if (!hid->ptr) if (!hid->ptr)
return -1; return -1;
IOHIDManagerSetDeviceMatching(hid->ptr, NULL); IOHIDManagerSetDeviceMatching(hid->ptr, NULL);
IOHIDManagerScheduleWithRunLoop(hid->ptr, CFRunLoopGetCurrent(), IOHIDManagerScheduleWithRunLoop(hid->ptr, CFRunLoopGetCurrent(),
kCFRunLoopDefaultMode); kCFRunLoopDefaultMode);
@ -1004,103 +1005,99 @@ static int iohidmanager_hid_manager_free(iohidmanager_hid_t *hid)
static int iohidmanager_hid_manager_set_device_matching( static int iohidmanager_hid_manager_set_device_matching(
iohidmanager_hid_t *hid) iohidmanager_hid_t *hid)
{ {
/* deterministically add all device currently plugged when lanching retroarch /* deterministically add all device currently plugged when lanching retroarch
* order by location id which seems to correspond to usb port number */ * order by location id which seems to correspond to usb port number */
CFSetRef set = IOHIDManagerCopyDevices(hid->ptr); CFSetRef set = IOHIDManagerCopyDevices(hid->ptr);
CFIndex num_devices = CFSetGetCount(set); CFIndex num_devices = CFSetGetCount(set);
IOHIDDeviceRef *device_array = (IOHIDDeviceRef*)calloc(num_devices, sizeof(IOHIDDeviceRef)); IOHIDDeviceRef *device_array = (IOHIDDeviceRef*)calloc(num_devices, sizeof(IOHIDDeviceRef));
CFSetGetValues(set, (const void **) device_array); CFSetGetValues(set, (const void **) device_array);
CFRelease(set); CFRelease(set);
/* re order device by location id */
typedef struct hid_list
{
IOHIDDeviceRef device;
uint32_t lid;
struct hid_list *next;
} hid_list_t;
/* re order device by location id */ hid_list_t* devList = NULL;
typedef struct hid_list for (long i=0; i<num_devices;i++)
{ {
IOHIDDeviceRef device; IOHIDDeviceRef dev = device_array[i];
uint32_t lid; /* filter gamepad/joystick devices */
struct hid_list *next; if ( IOHIDDeviceConformsTo(dev, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick)
} hid_list_t; || IOHIDDeviceConformsTo(dev, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad)
)
{
if (!devList)
{
devList = (hid_list_t *)malloc(sizeof(hid_list_t));
devList->device = dev;
devList->lid = iohidmanager_hid_device_get_location_id(dev);
devList->next = NULL;
}
else
{
hid_list_t * devnew = (hid_list_t *)malloc(sizeof(hid_list_t));
devnew->device = dev;
devnew->lid = iohidmanager_hid_device_get_location_id(dev);
devnew->next = NULL;
hid_list_t* devList = NULL; hid_list_t * ptr = devList;
for (long i=0; i<num_devices;i++) if (devnew->lid < ptr->lid)
{ {
IOHIDDeviceRef dev = device_array[i]; devnew->next = ptr;
/* filter gamepad/joystick devices */ devList = devnew;
if ( IOHIDDeviceConformsTo(dev, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick) }
|| IOHIDDeviceConformsTo(dev, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad) else
) {
{ while ( ( ptr->lid < devnew->lid ) && (ptr->next != NULL) )
if (!devList) ptr = ptr->next;
{ devnew->next = ptr->next;
devList = (hid_list_t *)malloc(sizeof(hid_list_t)); ptr->next = devnew;
devList->device = dev; }
devList->lid = iohidmanager_hid_device_get_location_id(dev); }
devList->next = NULL; }
} }
else
{
hid_list_t * devnew = (hid_list_t *)malloc(sizeof(hid_list_t));
devnew->device = dev;
devnew->lid = iohidmanager_hid_device_get_location_id(dev);
devnew->next = NULL;
hid_list_t * ptr = devList; /* register devices */
if (devnew->lid < ptr->lid) hid_list_t * ptr = devList;
{ while (ptr != NULL)
devnew->next = ptr; {
devList = devnew;
}
else
{
while ( ( ptr->lid < devnew->lid ) && (ptr->next != NULL) )
ptr = ptr->next;
devnew->next = ptr->next;
ptr->next = devnew;
}
}
}
}
/* register devices */
hid_list_t * ptr = devList;
while (ptr != NULL)
{
#if defined(__ppc__) || defined(__ppc64__)
iohidmanager_hid_device_add(ptr->device, hid); iohidmanager_hid_device_add(ptr->device, hid);
#else
iohidmanager_hid_device_add_device(ptr->device, hid);
#endif
//printf("%d\n",ptr->lid);
ptr = ptr->next;
free(devList);
devList = ptr;
}
free(device_array);
#if !(defined(__ppc__) || defined(__ppc64__)) ptr = ptr->next;
/* register call back to dynamically add device plugged when retroarch is free(devList);
* running devList = ptr;
* those will be added after the one plugged when retroarch was launched, }
* and by order they are plugged in (so not deterministic) */ free(device_array);
CFMutableArrayRef matcher = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!matcher)
return -1;
iohidmanager_hid_append_matching_dictionary(matcher, /* register call back to dynamically add device plugged when retroarch is
kHIDPage_GenericDesktop, * running
kHIDUsage_GD_Joystick); * those will be added after the one plugged when retroarch was launched,
iohidmanager_hid_append_matching_dictionary(matcher, * and by order they are plugged in (so not deterministic) */
kHIDPage_GenericDesktop, CFMutableArrayRef matcher = CFArrayCreateMutable(kCFAllocatorDefault, 0,
kHIDUsage_GD_GamePad); &kCFTypeArrayCallBacks);
IOHIDManagerSetDeviceMatchingMultiple(hid->ptr, matcher); if (!matcher)
IOHIDManagerRegisterDeviceMatchingCallback(hid->ptr, return -1;
iohidmanager_hid_device_add, 0);
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_matched, 0);
IOHIDManagerRegisterDeviceRemovalCallback(hid->ptr,iohidmanager_hid_device_removed, 0);
CFRelease(matcher);
CFRelease(matcher);
#endif
return 0; return 0;
} }