diff --git a/src/joystick/hidapi/SDL_hidapi_8bitdo.c b/src/joystick/hidapi/SDL_hidapi_8bitdo.c index 6b1dece533..8c607c2fb3 100644 --- a/src/joystick/hidapi/SDL_hidapi_8bitdo.c +++ b/src/joystick/hidapi/SDL_hidapi_8bitdo.c @@ -42,6 +42,13 @@ enum SDL_GAMEPAD_NUM_8BITDO_BUTTONS, }; +enum +{ + SDL_GAMEPAD_BUTTON_8BITDO_SHARE = 15, + SDL_GAMEPAD_NUM_8BITDO_ULTIMATE3_BUTTONS, +}; + +#define SDL_8BITDO_FEATURE_REPORTID 0x30 #define SDL_8BITDO_FEATURE_REPORTID_ENABLE_SDL_REPORTID 0x06 #define SDL_8BITDO_REPORTID_SDL_REPORTID 0x04 #define SDL_8BITDO_REPORTID_NOT_SUPPORTED_SDL_REPORTID 0x03 @@ -65,6 +72,7 @@ typedef struct bool touchpad_01_supported; bool touchpad_02_supported; bool rumble_supported; + bool trigger_rumble_supported; bool rumble_type; bool rgb_supported; bool player_led_supported; @@ -78,6 +86,10 @@ typedef struct Uint8 last_state[USB_PACKET_LENGTH]; Uint64 sensor_timestamp; // Nanoseconds. Simulate onboard clock. Different models have different rates vs different connection styles. Uint64 sensor_timestamp_interval; + Uint8 rumble_left_value; + Uint8 rumble_right_value; + Uint8 trigger_rumble_left_value; + Uint8 trigger_rumble_right_value; Uint32 last_tick; } SDL_Driver8BitDo_Context; @@ -147,6 +159,7 @@ static bool HIDAPI_Driver8BitDo_IsSupportedDevice(SDL_HIDAPI_Device *device, con case USB_PRODUCT_8BITDO_PRO_2_BT: case USB_PRODUCT_8BITDO_PRO_3: case USB_PRODUCT_8BITDO_ULTIMATE2_WIRELESS: + case USB_PRODUCT_8BITDO_ULTIMATE3: return true; default: break; @@ -182,7 +195,41 @@ static bool HIDAPI_Driver8BitDo_InitDevice(SDL_HIDAPI_Device *device) } break; } - } else { + } else if (device->product_id == USB_PRODUCT_8BITDO_ULTIMATE3) { + // Supported by default + ctx->sensors_supported = true; + ctx->rumble_supported = true; + ctx->powerstate_supported = true; + ctx->sensor_timestamp_supported = true; + Uint8 data[USB_PACKET_LENGTH]; + const int MAX_ATTEMPTS = 5; + for (int attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) { + int size = ReadFeatureReport(device->dev, SDL_8BITDO_FEATURE_REPORTID, data, sizeof(data)); + if (size > 0) { + if (data[3] == 0) { + ctx->rumble_supported = false; + ctx->trigger_rumble_supported = false; + } + if (data[3] & 0x02) { + ctx->trigger_rumble_supported = true; + } + + if (data[4] == 0) { + ctx->sensors_supported = false; + ctx->sensor_timestamp_supported = false; + } + if (size >= 17 && data[11] != 0) { + char serial[18]; + (void)SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", + data[16], data[15], data[14], data[13], data[12], data[11]); + HIDAPI_SetDeviceSerial(device, serial); + } + break; + } + SDL_Delay(10); + } + } + else { Uint8 data[USB_PACKET_LENGTH]; const int MAX_ATTEMPTS = 5; for (int attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) { @@ -277,6 +324,8 @@ static Uint64 HIDAPI_Driver8BitDo_GetIMURateForProductID(SDL_HIDAPI_Device *devi // This firmware appears to update at 1000 Hz over USB dongle return 1000; } + case USB_PRODUCT_8BITDO_ULTIMATE3: + return 120; default: return 120; } @@ -299,8 +348,10 @@ static bool HIDAPI_Driver8BitDo_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joys device->product_id == USB_PRODUCT_8BITDO_PRO_2_BT || device->product_id == USB_PRODUCT_8BITDO_PRO_3 || device->product_id == USB_PRODUCT_8BITDO_ULTIMATE2_WIRELESS) { - // This controller has additional buttons + // This controller has additional buttons joystick->nbuttons = SDL_GAMEPAD_NUM_8BITDO_BUTTONS; + } else if (device->product_id == USB_PRODUCT_8BITDO_ULTIMATE3) { + joystick->nbuttons = SDL_GAMEPAD_NUM_8BITDO_ULTIMATE3_BUTTONS; } else { joystick->nbuttons = 11; } @@ -331,6 +382,12 @@ static bool HIDAPI_Driver8BitDo_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Jo Uint8 rumble_packet[5] = { 0x05, 0x00, 0x00, 0x00, 0x00 }; rumble_packet[1] = low_frequency_rumble >> 8; rumble_packet[2] = high_frequency_rumble >> 8; + if (ctx->trigger_rumble_supported) { + rumble_packet[3] = ctx->trigger_rumble_left_value; + rumble_packet[4] = ctx->trigger_rumble_right_value; + ctx->rumble_left_value = rumble_packet[1]; + ctx->rumble_right_value = rumble_packet[2]; + } if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { return SDL_SetError("Couldn't send rumble packet"); @@ -343,7 +400,25 @@ static bool HIDAPI_Driver8BitDo_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Jo static bool HIDAPI_Driver8BitDo_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) { - return SDL_Unsupported(); + SDL_Driver8BitDo_Context* ctx = (SDL_Driver8BitDo_Context*)device->context; + if (ctx->trigger_rumble_supported) { + Uint8 rumble_packet[5] = { 0x05, 0x00, 0x00, 0x00, 0x00 }; + rumble_packet[3] = left_rumble >> 8; + rumble_packet[4] = right_rumble >> 8; + if (ctx->rumble_supported) { + rumble_packet[1] = ctx->rumble_left_value; + rumble_packet[2] = ctx->rumble_right_value; + ctx->trigger_rumble_left_value = rumble_packet[3]; + ctx->trigger_rumble_right_value = rumble_packet[4]; + } + + if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { + return SDL_SetError("Couldn't send rumble packet"); + } + return true; + } else { + return SDL_Unsupported(); + } } static Uint32 HIDAPI_Driver8BitDo_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) @@ -525,6 +600,9 @@ static void HIDAPI_Driver8BitDo_HandleStatePacket(SDL_Joystick *joystick, SDL_Dr SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[9] & 0x08) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[9] & 0x20) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[9] & 0x40) != 0)); + if (joystick->nbuttons >= SDL_GAMEPAD_NUM_8BITDO_ULTIMATE3_BUTTONS) { + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_8BITDO_SHARE, ((data[9] & 0x80) != 0)); + } } if (size > 10 && ctx->last_state[10] != data[10]) { diff --git a/src/joystick/usb_ids.h b/src/joystick/usb_ids.h index 826f07cffc..05ae6812b4 100644 --- a/src/joystick/usb_ids.h +++ b/src/joystick/usb_ids.h @@ -78,6 +78,7 @@ #define USB_PRODUCT_8BITDO_PRO_2_BT 0x6006 // mode switch to D #define USB_PRODUCT_8BITDO_PRO_3 0x6009 // mode switch to D #define USB_PRODUCT_8BITDO_ULTIMATE2_WIRELESS 0x6012 // mode switch to BT +#define USB_PRODUCT_8BITDO_ULTIMATE3 0x202f // mode switch to BT #define USB_PRODUCT_AMAZON_LUNA_CONTROLLER 0x0419 #define USB_PRODUCT_ASTRO_C40_XBOX360 0x0024 #define USB_PRODUCT_BACKBONE_ONE_IOS 0x0103