diff --git a/src/SDL_utils.c b/src/SDL_utils.c index 399c3f5157..f860b910d1 100644 --- a/src/SDL_utils.c +++ b/src/SDL_utils.c @@ -445,6 +445,7 @@ char *SDL_CreateDeviceName(Uint16 vendor, Uint16 product, const char *vendor_nam const char *prefix; const char *replacement; } replacements[] = { + { "(Standard system devices) ", "" }, { "8BitDo Tech Ltd", "8BitDo" }, { "ASTRO Gaming", "ASTRO" }, { "Bensussen Deutsch & Associates,Inc.(BDA)", "BDA" }, diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index 4f688028fd..f01986b1eb 100644 --- a/src/joystick/SDL_gamepad.c +++ b/src/joystick/SDL_gamepad.c @@ -1258,6 +1258,10 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid) if (guid.data[15] >= SDL_FLYDIGI_VADER2) { // Vader series of controllers have C/Z buttons SDL_strlcat(mapping_string, "misc2:b15,misc3:b16,", sizeof(mapping_string)); + if (guid.data[15] == SDL_FLYDIGI_VADER5_PRO) { + // Vader 5 has additional shoulder macro buttons and a circle button + SDL_strlcat(mapping_string, "misc4:b17,misc5:b18,misc6:b19", sizeof(mapping_string)); + } } else if (guid.data[15] == SDL_FLYDIGI_APEX5) { // Apex 5 has additional shoulder macro buttons SDL_strlcat(mapping_string, "misc2:b15,misc3:b16,", sizeof(mapping_string)); diff --git a/src/joystick/hidapi/SDL_hidapi_flydigi.c b/src/joystick/hidapi/SDL_hidapi_flydigi.c index 4b42673761..e260eb2105 100644 --- a/src/joystick/hidapi/SDL_hidapi_flydigi.c +++ b/src/joystick/hidapi/SDL_hidapi_flydigi.c @@ -53,6 +53,9 @@ enum /* Rate of IMU Sensor Packets over wired connection observed in testcontroller at 500hz */ #define SENSOR_INTERVAL_VADER4_PRO_WIRED_RATE_HZ 500 #define SENSOR_INTERVAL_VADER4_PRO_WIRED_NS (SDL_NS_PER_SECOND / SENSOR_INTERVAL_VADER4_PRO_WIRED_RATE_HZ) +/* Rate of IMU Sensor Packets over wired connection observed in testcontroller at 500hz */ +#define SENSOR_INTERVAL_VADER5_PRO_RATE_HZ 500 +#define SENSOR_INTERVAL_VADER5_PRO_NS (SDL_NS_PER_SECOND / SENSOR_INTERVAL_VADER5_PRO_RATE_HZ) /* Rate of IMU Sensor Packets over wireless dongle observed in testcontroller at 295hz */ #define SENSOR_INTERVAL_APEX5_DONGLE_RATE_HZ 295 @@ -86,6 +89,7 @@ typedef struct bool available; bool has_cz; bool has_lmrm; + bool has_circle; bool wireless; bool sensors_supported; bool sensors_enabled; @@ -117,7 +121,19 @@ static bool HIDAPI_DriverFlydigi_IsEnabled(void) static bool HIDAPI_DriverFlydigi_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol) { - return SDL_IsJoystickFlydigiController(vendor_id, product_id) && interface_number == 2; + if (SDL_IsJoystickFlydigiController(vendor_id, product_id)) { + if (vendor_id == USB_VENDOR_FLYDIGI_V1) { + if (interface_number == 2) { + // Early controllers have their custom protocol on interface 2 + return true; + } + } else { + // Newer controllers have their custom protocol on interface 1 or 2, but + // only expose one HID interface, so we'll accept any interface we see. + return true; + } + } + return false; } static bool HIDAPI_DriverFlydigi_InitControllerV1(SDL_HIDAPI_Device *device) @@ -217,8 +233,15 @@ static bool GetReply(SDL_HIDAPI_Device* device, Uint8 command, Uint8* data, size HIDAPI_DumpPacket("Flydigi packet: size = %d", data, size); #endif - if (size == 32 && data[1] == FLYDIGI_V2_MAGIC1 && data[2] == FLYDIGI_V2_MAGIC2 && data[3] == command) { - return true; + if (size == 32) { + if (data[1] == FLYDIGI_V2_MAGIC1 && data[2] == FLYDIGI_V2_MAGIC2) { + // Skip the report ID + SDL_memmove(&data[0], &data[1], size - 1); + data[size - 1] = 0; + } + if (data[0] == FLYDIGI_V2_MAGIC1 && data[1] == FLYDIGI_V2_MAGIC2 && data[2] == command) { + return true; + } } } return false; @@ -264,12 +287,27 @@ static bool HIDAPI_DriverFlydigi_InitControllerV2(SDL_HIDAPI_Device *device) } // Check the firmware version - ctx->firmware_version = LOAD16(data[17], data[16]); - if (ctx->firmware_version < 0x7031) { + Uint16 min_firmware_version; + ctx->firmware_version = LOAD16(data[16], data[15]); + switch (device->product_id) { + case USB_PRODUCT_FLYDIGI_V2_APEX: + // Minimum supported firmware version, Apex 5 + min_firmware_version = 0x7031; + break; + case USB_PRODUCT_FLYDIGI_V2_VADER: + // Minimum supported firmware version, Vader 5 Pro + min_firmware_version = 0x7141; + break; + default: + // Unknown product, presumably this version is okay? + min_firmware_version = 0; + break; + } + if (ctx->firmware_version < min_firmware_version) { return SDL_SetError("Unsupported firmware version"); } - switch (data[7]) { + switch (data[6]) { case 1: // Wired connection ctx->wireless = false; @@ -281,7 +319,7 @@ static bool HIDAPI_DriverFlydigi_InitControllerV2(SDL_HIDAPI_Device *device) default: break; } - ctx->deviceID = data[6]; + ctx->deviceID = data[5]; // See whether we can acquire the controller const Uint8 query_status[] = { FLYDIGI_V2_CMD_REPORT_ID, FLYDIGI_V2_MAGIC1, FLYDIGI_V2_MAGIC2, FLYDIGI_V2_GET_STATUS_COMMAND }; @@ -291,7 +329,7 @@ static bool HIDAPI_DriverFlydigi_InitControllerV2(SDL_HIDAPI_Device *device) if (!GetReply(device, FLYDIGI_V2_GET_STATUS_COMMAND, data, sizeof(data))) { return SDL_SetError("Couldn't get controller status"); } - if (data[10] == 1) { + if (data[9] == 1) { ctx->available = true; } else { // Click "Allow third-party apps to take over mappings" in the FlyDigi Space Station app @@ -338,19 +376,26 @@ static void HIDAPI_DriverFlydigi_UpdateDeviceIdentity(SDL_HIDAPI_Device *device) break; case 128: case 129: + controller_type = SDL_FLYDIGI_APEX5; + break; + case 130: + controller_type = SDL_FLYDIGI_VADER5_PRO; + break; case 133: case 134: controller_type = SDL_FLYDIGI_APEX5; break; default: // Try to guess from the name of the controller - if (SDL_strstr(device->name, "VADER") != NULL) { + if (SDL_strcasestr(device->name, "VADER") != NULL) { if (SDL_strstr(device->name, "VADER2") != NULL) { controller_type = SDL_FLYDIGI_VADER2; } else if (SDL_strstr(device->name, "VADER3") != NULL) { controller_type = SDL_FLYDIGI_VADER3; } else if (SDL_strstr(device->name, "VADER4") != NULL) { - controller_type = SDL_FLYDIGI_VADER4; + controller_type = SDL_FLYDIGI_VADER4_PRO; + } else if (SDL_strstr(device->name, "Vader 5") != NULL) { + controller_type = SDL_FLYDIGI_VADER5_PRO; } } else if (SDL_strstr(device->name, "APEX") != NULL) { if (SDL_strstr(device->name, "APEX2") != NULL) { @@ -410,7 +455,6 @@ static void HIDAPI_DriverFlydigi_UpdateDeviceIdentity(SDL_HIDAPI_Device *device) ctx->accelScale = SDL_STANDARD_GRAVITY / 256.0f; ctx->sensor_timestamp_step_ns = ctx->wireless ? SENSOR_INTERVAL_VADER4_PRO_DONGLE_NS : SENSOR_INTERVAL_VADER4_PRO_WIRED_NS; break; - case SDL_FLYDIGI_VADER4: case SDL_FLYDIGI_VADER4_PRO: HIDAPI_SetDeviceName(device, "Flydigi Vader 4 Pro"); ctx->has_cz = true; @@ -418,6 +462,16 @@ static void HIDAPI_DriverFlydigi_UpdateDeviceIdentity(SDL_HIDAPI_Device *device) ctx->accelScale = SDL_STANDARD_GRAVITY / 256.0f; ctx->sensor_timestamp_step_ns = ctx->wireless ? SENSOR_INTERVAL_VADER4_PRO_DONGLE_NS : SENSOR_INTERVAL_VADER4_PRO_WIRED_NS; break; + case SDL_FLYDIGI_VADER5_PRO: + HIDAPI_SetDeviceName(device, "Flydigi Vader 5 Pro"); + ctx->has_cz = true; + ctx->has_lmrm = true; + ctx->has_circle = true; + ctx->sensors_supported = true; + ctx->accelScale = SDL_STANDARD_GRAVITY / 4096.0f; + ctx->gyroScale = DEG2RAD(2000.0f); + ctx->sensor_timestamp_step_ns = SENSOR_INTERVAL_VADER5_PRO_NS; + break; default: SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "Unknown FlyDigi controller with ID %d, name '%s'", ctx->deviceID, device->name); break; @@ -479,6 +533,9 @@ static bool HIDAPI_DriverFlydigi_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy if (ctx->has_lmrm) { joystick->nbuttons += 2; } + if (ctx->has_circle) { + joystick->nbuttons += 1; + } joystick->naxes = SDL_GAMEPAD_AXIS_COUNT; joystick->nhats = 1; @@ -748,17 +805,28 @@ static void HIDAPI_DriverFlydigi_HandleStatePacketV2(SDL_Joystick *joystick, SDL SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_FLYDIGI_M2, ((data[13] & 0x08) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_FLYDIGI_M3, ((data[13] & 0x10) != 0)); SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_FLYDIGI_M4, ((data[13] & 0x20) != 0)); + if (ctx->has_cz) { + SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[13] & 0x01) != 0)); + SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[13] & 0x02) != 0)); + } if (ctx->has_lmrm) { SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[13] & 0x40) != 0)); SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[13] & 0x80) != 0)); } + } else { + if (ctx->has_cz) { + extra_button_index += 2; + } + if (ctx->has_lmrm) { + extra_button_index += 2; + } } if (ctx->last_state[14] != data[14]) { SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[14] & 0x08) != 0)); - SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[14] & 0x01) != 0)); - // The '-' button is only available on the Vader 2, for simplicity let's ignore that - SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[8] & 0x10) != 0)); + if (ctx->has_circle) { + SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[14] & 0x01) != 0)); + } } axis = LOAD16(data[3], data[4]); @@ -813,7 +881,7 @@ static void HIDAPI_DriverFlydigi_HandleStatePacketV2(SDL_Joystick *joystick, SDL static void HIDAPI_DriverFlydigi_HandleStatusUpdate(SDL_HIDAPI_Device *device, Uint8 *data, int size) { - if (data[9] == 1) { + if (data[9] & 0x01) { // We can now acquire the controller HIDAPI_DriverFlydigi_SetAvailable(device, true); } else { diff --git a/src/joystick/hidapi/SDL_hidapi_flydigi.h b/src/joystick/hidapi/SDL_hidapi_flydigi.h index 42d6ef7ee2..91a15b506b 100644 --- a/src/joystick/hidapi/SDL_hidapi_flydigi.h +++ b/src/joystick/hidapi/SDL_hidapi_flydigi.h @@ -32,7 +32,7 @@ typedef enum SDL_FLYDIGI_VADER2_PRO, SDL_FLYDIGI_VADER3, SDL_FLYDIGI_VADER3_PRO, - SDL_FLYDIGI_VADER4, SDL_FLYDIGI_VADER4_PRO, + SDL_FLYDIGI_VADER5_PRO, } SDL_FlyDigiControllerType;