Skip to content

Commit

Permalink
Improved GCController handling on Apple platforms
Browse files Browse the repository at this point in the history
Automatically map controllers as gamepads when using the GCController framework and prefer the physicalInputProfile when possible.

Testing with macOS 13.4.1, macOS 14.1.1, iOS 15.7.4, tvOS 17.1:
* iBuffalo Classic USB Gamepad (macOS only)
* Logitech F310 (macOS only)
* Nimbus MFi controller
* PS4 DualShock controller
* PS5 DualSense controller
* Xbox Series X controller
* Xbox Elite Series 2 controller
* Nintendo Switch Pro controller
* Nintendo Switch Joy-Con controllers
  • Loading branch information
slouken committed Nov 16, 2023
1 parent 34392db commit 997710c
Show file tree
Hide file tree
Showing 5 changed files with 446 additions and 157 deletions.
9 changes: 7 additions & 2 deletions src/joystick/SDL_gamepad.c
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,7 @@ static GamepadMapping_t *SDL_PrivateGetGamepadMappingForGUID(SDL_JoystickGUID gu

/* Try harder to get the best match, or create a mapping */

if (vendor && product) {
if (SDL_JoystickGUIDUsesVersion(guid)) {
/* Try again, ignoring the version */
if (crc) {
mapping = SDL_PrivateMatchGamepadMappingForGUID(guid, SDL_TRUE, SDL_FALSE);
Expand Down Expand Up @@ -1720,7 +1720,11 @@ static void SDL_PrivateAppendToMappingString(char *mapping_string,
(void)SDL_snprintf(buffer, sizeof(buffer), "b%i", mapping->target);
break;
case EMappingKind_Axis:
(void)SDL_snprintf(buffer, sizeof(buffer), "a%i", mapping->target);
(void)SDL_snprintf(buffer, sizeof(buffer), "%sa%i%s",
mapping->half_axis_positive ? "+" :
mapping->half_axis_negative ? "-" : "",
mapping->target,
mapping->axis_reversed ? "~" : "");
break;
case EMappingKind_Hat:
(void)SDL_snprintf(buffer, sizeof(buffer), "h%i.%i", mapping->target >> 4, mapping->target & 0x0F);
Expand Down Expand Up @@ -1780,6 +1784,7 @@ static GamepadMapping_t *SDL_PrivateGenerateAutomaticGamepadMapping(const char *
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "righty", &raw_map->righty);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "lefttrigger", &raw_map->lefttrigger);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "righttrigger", &raw_map->righttrigger);
SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "touchpad", &raw_map->touchpad);

return SDL_PrivateAddMappingForGUID(guid, mapping, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT);
}
Expand Down
21 changes: 21 additions & 0 deletions src/joystick/SDL_joystick.c
Original file line number Diff line number Diff line change
Expand Up @@ -2436,6 +2436,22 @@ SDL_GamepadType SDL_GetGamepadTypeFromGUID(SDL_JoystickGUID guid, const char *na
return type;
}

SDL_bool SDL_JoystickGUIDUsesVersion(SDL_JoystickGUID guid)
{
Uint16 vendor, product;

if (SDL_IsJoystickMFI(guid)) {
/* The version bits are used as button capability mask */
return SDL_FALSE;
}

SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
if (vendor && product) {
return SDL_TRUE;
}
return SDL_FALSE;
}

SDL_bool SDL_IsJoystickXboxOne(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
Expand Down Expand Up @@ -2656,6 +2672,11 @@ SDL_bool SDL_IsJoystickHIDAPI(SDL_JoystickGUID guid)
return (guid.data[14] == 'h') ? SDL_TRUE : SDL_FALSE;
}

SDL_bool SDL_IsJoystickMFI(SDL_JoystickGUID guid)
{
return (guid.data[14] == 'm') ? SDL_TRUE : SDL_FALSE;
}

SDL_bool SDL_IsJoystickRAWINPUT(SDL_JoystickGUID guid)
{
return (guid.data[14] == 'r') ? SDL_TRUE : SDL_FALSE;
Expand Down
18 changes: 14 additions & 4 deletions src/joystick/SDL_joystick_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ extern void SDL_SetJoystickGUIDCRC(SDL_JoystickGUID *guid, Uint16 crc);
extern SDL_GamepadType SDL_GetGamepadTypeFromVIDPID(Uint16 vendor, Uint16 product, const char *name, SDL_bool forUI);
extern SDL_GamepadType SDL_GetGamepadTypeFromGUID(SDL_JoystickGUID guid, const char *name);

/* Function to return whether a joystick GUID uses the version field */
extern SDL_bool SDL_JoystickGUIDUsesVersion(SDL_JoystickGUID guid);

/* Function to return whether a joystick is an Xbox One controller */
extern SDL_bool SDL_IsJoystickXboxOne(Uint16 vendor_id, Uint16 product_id);

Expand Down Expand Up @@ -132,6 +135,9 @@ extern SDL_bool SDL_IsJoystickWGI(SDL_JoystickGUID guid);
/* Function to return whether a joystick guid comes from the HIDAPI driver */
extern SDL_bool SDL_IsJoystickHIDAPI(SDL_JoystickGUID guid);

/* Function to return whether a joystick guid comes from the MFI driver */
extern SDL_bool SDL_IsJoystickMFI(SDL_JoystickGUID guid);

/* Function to return whether a joystick guid comes from the RAWINPUT driver */
extern SDL_bool SDL_IsJoystickRAWINPUT(SDL_JoystickGUID guid);

Expand Down Expand Up @@ -166,16 +172,19 @@ extern SDL_bool SDL_IsJoystickValid(SDL_Joystick *joystick);

typedef enum
{
EMappingKind_None = 0,
EMappingKind_Button = 1,
EMappingKind_Axis = 2,
EMappingKind_Hat = 3
EMappingKind_None,
EMappingKind_Button,
EMappingKind_Axis,
EMappingKind_Hat,
} EMappingKind;

typedef struct SDL_InputMapping
{
EMappingKind kind;
Uint8 target;
SDL_bool axis_reversed;
SDL_bool half_axis_positive;
SDL_bool half_axis_negative;
} SDL_InputMapping;

typedef struct SDL_GamepadMapping
Expand Down Expand Up @@ -206,6 +215,7 @@ typedef struct SDL_GamepadMapping
SDL_InputMapping righty;
SDL_InputMapping lefttrigger;
SDL_InputMapping righttrigger;
SDL_InputMapping touchpad;
} SDL_GamepadMapping;

/* Function to get autodetected gamepad controller mapping from the driver */
Expand Down
Loading

0 comments on commit 997710c

Please sign in to comment.