Atlas - SDL_hidapi_flydigi.c

Home / ext / SDL / src / joystick / hidapi Lines: 1 | Size: 39849 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#ifdef SDL_JOYSTICK_HIDAPI 24 25#include "../SDL_sysjoystick.h" 26#include "SDL_hidapijoystick_c.h" 27#include "SDL_hidapi_rumble.h" 28#include "SDL_hidapi_flydigi.h" 29 30#ifdef SDL_JOYSTICK_HIDAPI_FLYDIGI 31 32// Define this if you want to log all packets from the controller 33#if 0 34#define DEBUG_FLYDIGI_PROTOCOL 35#endif 36 37#ifndef DEG2RAD 38#define DEG2RAD(x) ((float)(x) * (float)(SDL_PI_F / 180.f)) 39#endif 40 41enum 42{ 43 SDL_GAMEPAD_BUTTON_FLYDIGI_M1 = 11, 44 SDL_GAMEPAD_BUTTON_FLYDIGI_M2, 45 SDL_GAMEPAD_BUTTON_FLYDIGI_M3, 46 SDL_GAMEPAD_BUTTON_FLYDIGI_M4, 47 SDL_GAMEPAD_NUM_BASE_FLYDIGI_BUTTONS 48}; 49 50/* Rate of IMU Sensor Packets over wireless dongle observed in testcontroller at 1000hz */ 51#define SENSOR_INTERVAL_VADER4_PRO_DONGLE_RATE_HZ 1000 52#define SENSOR_INTERVAL_VADER4_PRO_DONGLE_NS (SDL_NS_PER_SECOND / SENSOR_INTERVAL_VADER4_PRO_DONGLE_RATE_HZ) 53/* Rate of IMU Sensor Packets over wired connection observed in testcontroller at 500hz */ 54#define SENSOR_INTERVAL_VADER4_PRO_WIRED_RATE_HZ 500 55#define SENSOR_INTERVAL_VADER4_PRO_WIRED_NS (SDL_NS_PER_SECOND / SENSOR_INTERVAL_VADER4_PRO_WIRED_RATE_HZ) 56/* Rate of IMU Sensor Packets over wired connection observed in testcontroller at 500hz */ 57#define SENSOR_INTERVAL_VADER5_PRO_RATE_HZ 500 58#define SENSOR_INTERVAL_VADER5_PRO_NS (SDL_NS_PER_SECOND / SENSOR_INTERVAL_VADER5_PRO_RATE_HZ) 59 60/* Rate of IMU Sensor Packets over wireless dongle observed in testcontroller at 295hz */ 61#define SENSOR_INTERVAL_APEX5_DONGLE_RATE_HZ 295 62#define SENSOR_INTERVAL_APEX5_DONGLE_NS (SDL_NS_PER_SECOND / SENSOR_INTERVAL_APEX5_DONGLE_RATE_HZ) 63/* Rate of IMU Sensor Packets over wired connection observed in testcontroller at 970hz */ 64#define SENSOR_INTERVAL_APEX5_WIRED_RATE_HZ 970 65#define SENSOR_INTERVAL_APEX5_WIRED_NS (SDL_NS_PER_SECOND / SENSOR_INTERVAL_APEX5_WIRED_RATE_HZ) 66 67#define FLYDIGI_ACQUIRE_CONTROLLER_HEARTBEAT_TIME 1000 * 30 68 69#define FLYDIGI_V1_CMD_REPORT_ID 0x05 70#define FLYDIGI_V1_HAPTIC_COMMAND 0x0F 71#define FLYDIGI_V1_GET_INFO_COMMAND 0xEC 72 73#define FLYDIGI_V2_CMD_REPORT_ID 0x03 74#define FLYDIGI_V2_MAGIC1 0x5A 75#define FLYDIGI_V2_MAGIC2 0xA5 76#define FLYDIGI_V2_GET_INFO_COMMAND 0x01 77#define FLYDIGI_V2_GET_STATUS_COMMAND 0x10 78#define FLYDIGI_V2_SET_STATUS_COMMAND 0x11 79#define FLYDIGI_V2_HAPTIC_COMMAND 0x12 80#define FLYDIGI_V2_ACQUIRE_CONTROLLER_COMMAND 0x1C 81#define FLYDIGI_V2_INPUT_REPORT 0xEF 82 83typedef struct 84{ 85 SDL_HIDAPI_Device *device; 86 Uint8 deviceID; 87 bool available; 88 bool has_cz; 89 bool has_lmrm; 90 bool has_circle; 91 bool wireless; 92 bool sensors_supported; 93 bool sensors_enabled; 94 Uint16 firmware_version; 95 Uint64 sensor_timestamp_ns; // Simulate onboard clock. Advance by known time step. Nanoseconds. 96 Uint64 sensor_timestamp_step_ns; // Based on observed rate of receipt of IMU sensor packets. 97 float accelScale; 98 float gyroScale; 99 Uint64 next_heartbeat; 100 Uint64 last_packet; 101 Uint8 last_state[USB_PACKET_LENGTH]; 102} SDL_DriverFlydigi_Context; 103 104 105static void HIDAPI_DriverFlydigi_RegisterHints(SDL_HintCallback callback, void *userdata) 106{ 107 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_FLYDIGI, callback, userdata); 108} 109 110static void HIDAPI_DriverFlydigi_UnregisterHints(SDL_HintCallback callback, void *userdata) 111{ 112 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_FLYDIGI, callback, userdata); 113} 114 115static bool HIDAPI_DriverFlydigi_IsEnabled(void) 116{ 117 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_FLYDIGI, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT)); 118} 119 120static 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) 121{ 122 if (SDL_IsJoystickFlydigiController(vendor_id, product_id)) { 123 if (vendor_id == USB_VENDOR_FLYDIGI_V1) { 124 if (interface_number == 2) { 125 // Early controllers have their custom protocol on interface 2 126 return true; 127 } 128 } else { 129 // Newer controllers have their custom protocol on interface 1 or 2, but 130 // only expose one HID interface, so we'll accept any interface we see. 131 return true; 132 } 133 } 134 return false; 135} 136 137static void HIDAPI_DriverFlydigi_UpdateDeviceIdentity(SDL_HIDAPI_Device *device) 138{ 139 SDL_DriverFlydigi_Context *ctx = (SDL_DriverFlydigi_Context *)device->context; 140 141 Uint8 controller_type = SDL_FLYDIGI_UNKNOWN; 142 switch (ctx->deviceID) { 143 case 19: 144 controller_type = SDL_FLYDIGI_APEX2; 145 break; 146 case 24: 147 case 26: 148 case 29: 149 controller_type = SDL_FLYDIGI_APEX3; 150 break; 151 case 84: 152 controller_type = SDL_FLYDIGI_APEX4; 153 break; 154 case 20: 155 case 21: 156 case 23: 157 controller_type = SDL_FLYDIGI_VADER2; 158 break; 159 case 22: 160 controller_type = SDL_FLYDIGI_VADER2_PRO; 161 break; 162 case 28: 163 controller_type = SDL_FLYDIGI_VADER3; 164 break; 165 case 80: 166 case 81: 167 controller_type = SDL_FLYDIGI_VADER3_PRO; 168 break; 169 case 85: 170 case 91: 171 case 105: 172 controller_type = SDL_FLYDIGI_VADER4_PRO; 173 break; 174 case 128: 175 case 129: 176 controller_type = SDL_FLYDIGI_APEX5; 177 break; 178 case 130: 179 controller_type = SDL_FLYDIGI_VADER5_PRO; 180 break; 181 case 133: 182 case 134: 183 controller_type = SDL_FLYDIGI_APEX5; 184 break; 185 default: 186 // Try to guess from the name of the controller 187 if (SDL_strcasestr(device->name, "VADER") != NULL) { 188 if (SDL_strstr(device->name, "VADER2") != NULL) { 189 controller_type = SDL_FLYDIGI_VADER2; 190 } else if (SDL_strstr(device->name, "VADER3") != NULL) { 191 controller_type = SDL_FLYDIGI_VADER3; 192 } else if (SDL_strstr(device->name, "VADER4") != NULL) { 193 controller_type = SDL_FLYDIGI_VADER4_PRO; 194 } else if (SDL_strstr(device->name, "Vader 5") != NULL) { 195 controller_type = SDL_FLYDIGI_VADER5_PRO; 196 } 197 } else if (SDL_strstr(device->name, "APEX") != NULL) { 198 if (SDL_strstr(device->name, "APEX2") != NULL) { 199 controller_type = SDL_FLYDIGI_APEX2; 200 } else if (SDL_strstr(device->name, "APEX3") != NULL) { 201 controller_type = SDL_FLYDIGI_APEX3; 202 } else if (SDL_strstr(device->name, "APEX4") != NULL) { 203 controller_type = SDL_FLYDIGI_APEX4; 204 } else if (SDL_strstr(device->name, "APEX5") != NULL) { 205 controller_type = SDL_FLYDIGI_APEX5; 206 } 207 } 208 break; 209 } 210 device->guid.data[15] = controller_type; 211 212 // This is the previous sensor default of 125hz. 213 // Override this in the switch statement below based on observed sensor packet rate. 214 ctx->sensor_timestamp_step_ns = SDL_NS_PER_SECOND / 125; 215 216 switch (controller_type) { 217 case SDL_FLYDIGI_APEX2: 218 HIDAPI_SetDeviceName(device, "Flydigi Apex 2"); 219 break; 220 case SDL_FLYDIGI_APEX3: 221 HIDAPI_SetDeviceName(device, "Flydigi Apex 3"); 222 break; 223 case SDL_FLYDIGI_APEX4: 224 // The Apex 4 controller has sensors, but they're only reported when gyro mouse is enabled 225 HIDAPI_SetDeviceName(device, "Flydigi Apex 4"); 226 break; 227 case SDL_FLYDIGI_APEX5: 228 HIDAPI_SetDeviceName(device, "Flydigi Apex 5"); 229 ctx->has_lmrm = true; 230 ctx->sensors_supported = true; 231 ctx->accelScale = SDL_STANDARD_GRAVITY / 4096.0f; 232 ctx->gyroScale = DEG2RAD(2000.0f); 233 ctx->sensor_timestamp_step_ns = ctx->wireless ? SENSOR_INTERVAL_APEX5_DONGLE_NS : SENSOR_INTERVAL_APEX5_WIRED_NS; 234 break; 235 case SDL_FLYDIGI_VADER2: 236 // The Vader 2 controller has sensors, but they're only reported when gyro mouse is enabled 237 HIDAPI_SetDeviceName(device, "Flydigi Vader 2"); 238 ctx->has_cz = true; 239 break; 240 case SDL_FLYDIGI_VADER2_PRO: 241 HIDAPI_SetDeviceName(device, "Flydigi Vader 2 Pro"); 242 ctx->has_cz = true; 243 break; 244 case SDL_FLYDIGI_VADER3: 245 HIDAPI_SetDeviceName(device, "Flydigi Vader 3"); 246 ctx->has_cz = true; 247 break; 248 case SDL_FLYDIGI_VADER3_PRO: 249 HIDAPI_SetDeviceName(device, "Flydigi Vader 3 Pro"); 250 ctx->has_cz = true; 251 ctx->sensors_supported = true; 252 ctx->accelScale = SDL_STANDARD_GRAVITY / 256.0f; 253 ctx->sensor_timestamp_step_ns = ctx->wireless ? SENSOR_INTERVAL_VADER4_PRO_DONGLE_NS : SENSOR_INTERVAL_VADER4_PRO_WIRED_NS; 254 break; 255 case SDL_FLYDIGI_VADER4_PRO: 256 HIDAPI_SetDeviceName(device, "Flydigi Vader 4 Pro"); 257 ctx->has_cz = true; 258 ctx->sensors_supported = true; 259 ctx->accelScale = SDL_STANDARD_GRAVITY / 256.0f; 260 ctx->sensor_timestamp_step_ns = ctx->wireless ? SENSOR_INTERVAL_VADER4_PRO_DONGLE_NS : SENSOR_INTERVAL_VADER4_PRO_WIRED_NS; 261 break; 262 case SDL_FLYDIGI_VADER5_PRO: 263 HIDAPI_SetDeviceName(device, "Flydigi Vader 5 Pro"); 264 ctx->has_cz = true; 265 ctx->has_lmrm = true; 266 ctx->has_circle = true; 267 ctx->sensors_supported = true; 268 ctx->accelScale = SDL_STANDARD_GRAVITY / 4096.0f; 269 ctx->gyroScale = DEG2RAD(2000.0f); 270 ctx->sensor_timestamp_step_ns = SENSOR_INTERVAL_VADER5_PRO_NS; 271 break; 272 default: 273 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "Unknown FlyDigi controller with ID %d, name '%s'", ctx->deviceID, device->name); 274 break; 275 } 276} 277 278static void HIDAPI_DriverFlydigi_SetAvailable(SDL_HIDAPI_Device *device, bool available) 279{ 280 SDL_DriverFlydigi_Context *ctx = (SDL_DriverFlydigi_Context *)device->context; 281 282 if (available == ctx->available) { 283 return; 284 } 285 286 if (available) { 287 if (device->num_joysticks == 0) { 288 HIDAPI_JoystickConnected(device, NULL); 289 } 290 } else { 291 if (device->num_joysticks > 0) { 292 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 293 } 294 } 295 ctx->available = available; 296} 297 298static bool HIDAPI_DriverFlydigi_InitControllerV1(SDL_HIDAPI_Device *device) 299{ 300 SDL_DriverFlydigi_Context *ctx = (SDL_DriverFlydigi_Context *)device->context; 301 302 // Detecting the Vader 2 can take over 1000 read retries, so be generous here 303 for (int attempt = 0; ctx->deviceID == 0 && attempt < 30; ++attempt) { 304 const Uint8 request[] = { FLYDIGI_V1_CMD_REPORT_ID, FLYDIGI_V1_GET_INFO_COMMAND, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 305 // This write will occasionally return -1, so ignore failure here and try again 306 (void)SDL_hid_write(device->dev, request, sizeof(request)); 307 308 // Read the reply 309 for (int i = 0; i < 100; ++i) { 310 SDL_Delay(1); 311 312 Uint8 data[USB_PACKET_LENGTH]; 313 int size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0); 314 if (size < 0) { 315 break; 316 } 317 if (size == 0) { 318 continue; 319 } 320 321#ifdef DEBUG_FLYDIGI_PROTOCOL 322 HIDAPI_DumpPacket("Flydigi packet: size = %d", data, size); 323#endif 324 if (size == 32 && data[15] == 236) { 325 ctx->deviceID = data[3]; 326 ctx->firmware_version = LOAD16(data[9], data[10]); 327 328 char serial[9]; 329 (void)SDL_snprintf(serial, sizeof(serial), "%.2x%.2x%.2x%.2x", data[5], data[6], data[7], data[8]); 330 HIDAPI_SetDeviceSerial(device, serial); 331 332 // The Vader 2 with firmware 6.0.4.9 doesn't report the connection state 333 if (ctx->firmware_version >= 0x6400) { 334 switch (data[13]) { 335 case 0: 336 // Wireless connection 337 ctx->wireless = true; 338 break; 339 case 1: 340 // Wired connection 341 ctx->wireless = false; 342 break; 343 default: 344 break; 345 } 346 } 347 348 // Done! 349 break; 350 } 351 } 352 } 353 354 HIDAPI_DriverFlydigi_UpdateDeviceIdentity(device); 355 356 HIDAPI_DriverFlydigi_SetAvailable(device, true); 357 358 return true; 359} 360 361static bool GetReply(SDL_HIDAPI_Device* device, Uint8 command, Uint8* data, size_t length) 362{ 363 for (int i = 0; i < 100; ++i) { 364 SDL_Delay(1); 365 366 int size = SDL_hid_read_timeout(device->dev, data, length, 0); 367 if (size < 0) { 368 break; 369 } 370 if (size == 0) { 371 continue; 372 } 373 374#ifdef DEBUG_FLYDIGI_PROTOCOL 375 HIDAPI_DumpPacket("Flydigi packet: size = %d", data, size); 376#endif 377 378 if (size == 32) { 379 if (data[1] == FLYDIGI_V2_MAGIC1 && data[2] == FLYDIGI_V2_MAGIC2) { 380 // Skip the report ID 381 SDL_memmove(&data[0], &data[1], size - 1); 382 data[size - 1] = 0; 383 } 384 if (data[0] == FLYDIGI_V2_MAGIC1 && data[1] == FLYDIGI_V2_MAGIC2 && data[2] == command) { 385 return true; 386 } 387 } 388 } 389 return false; 390} 391 392static bool SDL_HIDAPI_Flydigi_SendInfoRequest(SDL_HIDAPI_Device *device) 393{ 394 const Uint8 cmd[] = { 395 FLYDIGI_V2_CMD_REPORT_ID, 396 FLYDIGI_V2_MAGIC1, 397 FLYDIGI_V2_MAGIC2, 398 FLYDIGI_V2_GET_INFO_COMMAND, 399 2, 400 0 401 }; 402 if (SDL_hid_write(device->dev, cmd, sizeof(cmd)) < 0) { 403 return SDL_SetError("Couldn't query controller info"); 404 } 405 return true; 406} 407 408static void HIDAPI_DriverFlydigi_HandleInfoResponse(SDL_Joystick *joystick, SDL_DriverFlydigi_Context *ctx, Uint8 *data, int size) 409{ 410 SDL_PowerState state; 411 int percent; 412 Uint8 status = (data[11] >> 4) & 0x0F; 413 Uint8 level = (data[11] & 0x0F); 414 415 switch (status) { 416 case 0: 417 state = SDL_POWERSTATE_ON_BATTERY; 418 percent = level * 20; 419 break; 420 case 1: 421 state = SDL_POWERSTATE_CHARGING; 422 percent = level * 20; 423 break; 424 case 2: 425 state = SDL_POWERSTATE_CHARGED; 426 percent = 100; 427 break; 428 default: 429 state = SDL_POWERSTATE_UNKNOWN; 430 percent = 0; 431 break; 432 } 433 SDL_SendJoystickPowerInfo(joystick, state, percent); 434} 435 436static bool SDL_HIDAPI_Flydigi_SendStatusRequest(SDL_HIDAPI_Device *device) 437{ 438 const Uint8 cmd[] = { 439 FLYDIGI_V2_CMD_REPORT_ID, 440 FLYDIGI_V2_MAGIC1, 441 FLYDIGI_V2_MAGIC2, 442 FLYDIGI_V2_GET_STATUS_COMMAND 443 }; 444 if (SDL_hid_write(device->dev, cmd, sizeof(cmd)) < 0) { 445 return SDL_SetError("Couldn't query controller status"); 446 } 447 return true; 448} 449 450static void HIDAPI_DriverFlydigi_HandleStatusResponse(SDL_HIDAPI_Device *device, Uint8 *data, int size) 451{ 452 if (data[9] == 1) { 453 HIDAPI_DriverFlydigi_SetAvailable(device, true); 454 } else { 455 // Click "Allow third-party apps to take over mappings" in the FlyDigi Space Station app 456 HIDAPI_DriverFlydigi_SetAvailable(device, false); 457 } 458} 459 460static bool SDL_HIDAPI_Flydigi_SendAcquireRequest(SDL_HIDAPI_Device *device, bool acquire) 461{ 462 const Uint8 cmd[32] = { 463 FLYDIGI_V2_CMD_REPORT_ID, 464 FLYDIGI_V2_MAGIC1, 465 FLYDIGI_V2_MAGIC2, 466 FLYDIGI_V2_ACQUIRE_CONTROLLER_COMMAND, 467 23, 468 acquire ? 1 : 0, 469 'S', 'D', 'L', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 470 }; 471 472 if (SDL_hid_write(device->dev, cmd, sizeof(cmd)) < 0) { 473 return SDL_SetError("Couldn't send acquire command"); 474 } 475 return true; 476} 477 478static void HIDAPI_DriverFlydigi_HandleAcquireResponse(SDL_HIDAPI_Device *device, Uint8 *data, int size) 479{ 480 if (data[5] != 1 && data[6] == 0) { 481 // Controller acquiring failed or has been disabled 482 HIDAPI_DriverFlydigi_SetAvailable(device, false); 483 } 484} 485 486static bool HIDAPI_DriverFlydigi_InitControllerV2(SDL_HIDAPI_Device *device) 487{ 488 SDL_DriverFlydigi_Context *ctx = (SDL_DriverFlydigi_Context *)device->context; 489 490 Uint8 data[USB_PACKET_LENGTH]; 491 if (!SDL_HIDAPI_Flydigi_SendInfoRequest(device)) { 492 return false; 493 } 494 if (!GetReply(device, FLYDIGI_V2_GET_INFO_COMMAND, data, sizeof(data))) { 495 return SDL_SetError("Couldn't get controller info"); 496 } 497 498 // Check the firmware version 499 Uint16 min_firmware_version; 500 ctx->firmware_version = LOAD16(data[16], data[15]); 501 switch (device->product_id) { 502 case USB_PRODUCT_FLYDIGI_V2_APEX: 503 // Minimum supported firmware version, Apex 5 504 min_firmware_version = 0x7031; 505 break; 506 case USB_PRODUCT_FLYDIGI_V2_VADER: 507 // Minimum supported firmware version, Vader 5 Pro 508 min_firmware_version = 0x7141; 509 break; 510 default: 511 // Unknown product, presumably this version is okay? 512 min_firmware_version = 0; 513 break; 514 } 515 if (ctx->firmware_version < min_firmware_version) { 516 return SDL_SetError("Unsupported firmware version"); 517 } 518 519 switch (data[6]) { 520 case 1: 521 // Wired connection 522 ctx->wireless = false; 523 break; 524 case 2: 525 // Wireless connection 526 ctx->wireless = true; 527 break; 528 default: 529 break; 530 } 531 ctx->deviceID = data[5]; 532 533 HIDAPI_DriverFlydigi_UpdateDeviceIdentity(device); 534 535 // See whether we can acquire the controller 536 SDL_HIDAPI_Flydigi_SendStatusRequest(device); 537 538 return true; 539} 540 541static bool HIDAPI_DriverFlydigi_InitDevice(SDL_HIDAPI_Device *device) 542{ 543 SDL_DriverFlydigi_Context *ctx = (SDL_DriverFlydigi_Context *)SDL_calloc(1, sizeof(*ctx)); 544 if (!ctx) { 545 return false; 546 } 547 ctx->device = device; 548 549 device->context = ctx; 550 551 if (device->vendor_id == USB_VENDOR_FLYDIGI_V1) { 552 return HIDAPI_DriverFlydigi_InitControllerV1(device); 553 } else { 554 return HIDAPI_DriverFlydigi_InitControllerV2(device); 555 } 556} 557 558static int HIDAPI_DriverFlydigi_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) 559{ 560 return -1; 561} 562 563static void HIDAPI_DriverFlydigi_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) 564{ 565} 566 567static bool HIDAPI_DriverFlydigi_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 568{ 569 SDL_DriverFlydigi_Context *ctx = (SDL_DriverFlydigi_Context *)device->context; 570 571 SDL_AssertJoysticksLocked(); 572 573 SDL_zeroa(ctx->last_state); 574 575 // Initialize the joystick capabilities 576 joystick->nbuttons = SDL_GAMEPAD_NUM_BASE_FLYDIGI_BUTTONS; 577 if (ctx->has_cz) { 578 joystick->nbuttons += 2; 579 } 580 if (ctx->has_lmrm) { 581 joystick->nbuttons += 2; 582 } 583 if (ctx->has_circle) { 584 joystick->nbuttons += 1; 585 } 586 joystick->naxes = SDL_GAMEPAD_AXIS_COUNT; 587 joystick->nhats = 1; 588 589 if (ctx->wireless) { 590 joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS; 591 } 592 593 if (ctx->sensors_supported) { 594 const float flSensorRate = ctx->wireless ? (float)SENSOR_INTERVAL_VADER4_PRO_DONGLE_RATE_HZ : (float)SENSOR_INTERVAL_VADER4_PRO_WIRED_RATE_HZ; 595 SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, flSensorRate); 596 SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, flSensorRate); 597 } 598 return true; 599} 600 601static bool HIDAPI_DriverFlydigi_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 602{ 603 if (device->vendor_id == USB_VENDOR_FLYDIGI_V1) { 604 Uint8 rumble_packet[] = { FLYDIGI_V1_CMD_REPORT_ID, FLYDIGI_V1_HAPTIC_COMMAND, 0x00, 0x00 }; 605 rumble_packet[2] = low_frequency_rumble >> 8; 606 rumble_packet[3] = high_frequency_rumble >> 8; 607 608 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { 609 return SDL_SetError("Couldn't send rumble packet"); 610 } 611 } else { 612 Uint8 rumble_packet[] = { FLYDIGI_V2_CMD_REPORT_ID, FLYDIGI_V2_MAGIC1, FLYDIGI_V2_MAGIC2, FLYDIGI_V2_HAPTIC_COMMAND, 6, 0, 0, 0, 0, 0 }; 613 rumble_packet[5] = low_frequency_rumble >> 8; 614 rumble_packet[6] = high_frequency_rumble >> 8; 615 616 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { 617 return SDL_SetError("Couldn't send rumble packet"); 618 } 619 } 620 return true; 621} 622 623static bool HIDAPI_DriverFlydigi_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 624{ 625 return SDL_Unsupported(); 626} 627 628static Uint32 HIDAPI_DriverFlydigi_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 629{ 630 return SDL_JOYSTICK_CAP_RUMBLE; 631} 632 633static bool HIDAPI_DriverFlydigi_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 634{ 635 return SDL_Unsupported(); 636} 637 638static bool HIDAPI_DriverFlydigi_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size) 639{ 640 return SDL_Unsupported(); 641} 642 643static bool HIDAPI_DriverFlydigi_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled) 644{ 645 SDL_DriverFlydigi_Context *ctx = (SDL_DriverFlydigi_Context *)device->context; 646 if (ctx->sensors_supported) { 647 ctx->sensors_enabled = enabled; 648 return true; 649 } 650 return SDL_Unsupported(); 651} 652 653static void HIDAPI_DriverFlydigi_HandleStatePacketV1(SDL_Joystick *joystick, SDL_DriverFlydigi_Context *ctx, Uint8 *data, int size) 654{ 655 Sint16 axis; 656 Uint64 timestamp = SDL_GetTicksNS(); 657 658 Uint8 extra_button_index = SDL_GAMEPAD_NUM_BASE_FLYDIGI_BUTTONS; 659 660 if (ctx->last_state[9] != data[9]) { 661 Uint8 hat; 662 663 switch (data[9] & 0x0F) { 664 case 0x01u: 665 hat = SDL_HAT_UP; 666 break; 667 case 0x02u | 0x01u: 668 hat = SDL_HAT_RIGHTUP; 669 break; 670 case 0x02u: 671 hat = SDL_HAT_RIGHT; 672 break; 673 case 0x02u | 0x04u: 674 hat = SDL_HAT_RIGHTDOWN; 675 break; 676 case 0x04u: 677 hat = SDL_HAT_DOWN; 678 break; 679 case 0x08u | 0x04u: 680 hat = SDL_HAT_LEFTDOWN; 681 break; 682 case 0x08u: 683 hat = SDL_HAT_LEFT; 684 break; 685 case 0x08u | 0x01u: 686 hat = SDL_HAT_LEFTUP; 687 break; 688 default: 689 hat = SDL_HAT_CENTERED; 690 break; 691 } 692 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 693 694 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[9] & 0x10) != 0)); 695 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[9] & 0x20) != 0)); 696 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[9] & 0x40) != 0)); 697 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[9] & 0x80) != 0)); 698 } 699 700 if (ctx->last_state[10] != data[10]) { 701 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[10] & 0x01) != 0)); 702 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[10] & 0x02) != 0)); 703 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[10] & 0x04) != 0)); 704 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[10] & 0x08) != 0)); 705 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[10] & 0x40) != 0)); 706 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[10] & 0x80) != 0)); 707 } 708 709 if (ctx->last_state[7] != data[7]) { 710 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_FLYDIGI_M1, ((data[7] & 0x04) != 0)); 711 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_FLYDIGI_M2, ((data[7] & 0x08) != 0)); 712 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_FLYDIGI_M3, ((data[7] & 0x10) != 0)); 713 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_FLYDIGI_M4, ((data[7] & 0x20) != 0)); 714 if (ctx->has_cz) { 715 SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[7] & 0x01) != 0)); 716 SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[7] & 0x02) != 0)); 717 } 718 } 719 720 if (ctx->last_state[8] != data[8]) { 721 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[8] & 0x08) != 0)); 722 // The '+' button is used to toggle gyro mouse mode, so don't pass that to the application 723 // SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[8] & 0x01) != 0)); 724 // The '-' button is only available on the Vader 2, for simplicity let's ignore that 725 // SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[8] & 0x10) != 0)); 726 } 727 728#define READ_STICK_AXIS(offset) \ 729 (data[offset] == 0x7f ? 0 : (Sint16)HIDAPI_RemapVal((float)((int)data[offset] - 0x7f), -0x7f, 0xff - 0x7f, SDL_MIN_SINT16, SDL_MAX_SINT16)) 730 { 731 axis = READ_STICK_AXIS(17); 732 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis); 733 axis = READ_STICK_AXIS(19); 734 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis); 735 axis = READ_STICK_AXIS(21); 736 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis); 737 axis = READ_STICK_AXIS(22); 738 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis); 739 } 740#undef READ_STICK_AXIS 741 742#define READ_TRIGGER_AXIS(offset) \ 743 (Sint16)(((int)data[offset] * 257) - 32768) 744 { 745 axis = READ_TRIGGER_AXIS(23); 746 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis); 747 axis = READ_TRIGGER_AXIS(24); 748 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis); 749 } 750#undef READ_TRIGGER_AXIS 751 752 if (ctx->sensors_enabled) { 753 Uint64 sensor_timestamp; 754 float values[3]; 755 756 // Advance the imu sensor time stamp based on the observed rate of receipt of packets in the testcontroller app. 757 // This varies between Product ID and connection type. 758 sensor_timestamp = ctx->sensor_timestamp_ns; 759 ctx->sensor_timestamp_ns += ctx->sensor_timestamp_step_ns; 760 761 // Pitch and yaw scales may be receiving extra filtering for the sake of bespoke direct mouse output. 762 // As result, roll has a different scaling factor than pitch and yaw. 763 // These values were estimated using the testcontroller tool in lieux of hard data sheet references. 764 const float flPitchAndYawScale = DEG2RAD(72000.0f); 765 const float flRollScale = DEG2RAD(1200.0f); 766 767 values[0] = HIDAPI_RemapVal(-1.0f * LOAD16(data[26], data[27]), INT16_MIN, INT16_MAX, -flPitchAndYawScale, flPitchAndYawScale); 768 values[1] = HIDAPI_RemapVal(-1.0f * LOAD16(data[18], data[20]), INT16_MIN, INT16_MAX, -flPitchAndYawScale, flPitchAndYawScale); 769 values[2] = HIDAPI_RemapVal(-1.0f * LOAD16(data[29], data[30]), INT16_MIN, INT16_MAX, -flRollScale, flRollScale); 770 SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, sensor_timestamp, values, 3); 771 772 const float flAccelScale = ctx->accelScale; 773 values[0] = -LOAD16(data[11], data[12]) * flAccelScale; // Acceleration along pitch axis 774 values[1] = LOAD16(data[15], data[16]) * flAccelScale; // Acceleration along yaw axis 775 values[2] = LOAD16(data[13], data[14]) * flAccelScale; // Acceleration along roll axis 776 SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, sensor_timestamp, values, 3); 777 } 778 779 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); 780} 781 782static void HIDAPI_DriverFlydigi_HandlePacketV1(SDL_Joystick *joystick, SDL_DriverFlydigi_Context *ctx, Uint8 *data, int size) 783{ 784 if (data[0] != 0x04 || data[1] != 0xFE) { 785 // We don't know how to handle this report, ignore it 786 return; 787 } 788 789 if (joystick) { 790 HIDAPI_DriverFlydigi_HandleStatePacketV1(joystick, ctx, data, size); 791 } 792} 793 794static void HIDAPI_DriverFlydigi_HandleStatePacketV2(SDL_Joystick *joystick, SDL_DriverFlydigi_Context *ctx, Uint8 *data, int size) 795{ 796 Sint16 axis; 797 Uint64 timestamp = SDL_GetTicksNS(); 798 799 Uint8 extra_button_index = SDL_GAMEPAD_NUM_BASE_FLYDIGI_BUTTONS; 800 801 if (ctx->last_state[11] != data[11]) { 802 Uint8 hat; 803 804 switch (data[11] & 0x0F) { 805 case 0x01u: 806 hat = SDL_HAT_UP; 807 break; 808 case 0x02u | 0x01u: 809 hat = SDL_HAT_RIGHTUP; 810 break; 811 case 0x02u: 812 hat = SDL_HAT_RIGHT; 813 break; 814 case 0x02u | 0x04u: 815 hat = SDL_HAT_RIGHTDOWN; 816 break; 817 case 0x04u: 818 hat = SDL_HAT_DOWN; 819 break; 820 case 0x08u | 0x04u: 821 hat = SDL_HAT_LEFTDOWN; 822 break; 823 case 0x08u: 824 hat = SDL_HAT_LEFT; 825 break; 826 case 0x08u | 0x01u: 827 hat = SDL_HAT_LEFTUP; 828 break; 829 default: 830 hat = SDL_HAT_CENTERED; 831 break; 832 } 833 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 834 835 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[11] & 0x10) != 0)); 836 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[11] & 0x20) != 0)); 837 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[11] & 0x40) != 0)); 838 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[11] & 0x80) != 0)); 839 } 840 841 if (ctx->last_state[12] != data[12]) { 842 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[12] & 0x01) != 0)); 843 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[12] & 0x02) != 0)); 844 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[12] & 0x04) != 0)); 845 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[12] & 0x08) != 0)); 846 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[12] & 0x40) != 0)); 847 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[12] & 0x80) != 0)); 848 } 849 850 if (ctx->last_state[13] != data[13]) { 851 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_FLYDIGI_M1, ((data[13] & 0x04) != 0)); 852 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_FLYDIGI_M2, ((data[13] & 0x08) != 0)); 853 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_FLYDIGI_M3, ((data[13] & 0x10) != 0)); 854 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_FLYDIGI_M4, ((data[13] & 0x20) != 0)); 855 if (ctx->has_cz) { 856 SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[13] & 0x01) != 0)); 857 SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[13] & 0x02) != 0)); 858 } 859 if (ctx->has_lmrm) { 860 SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[13] & 0x40) != 0)); 861 SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[13] & 0x80) != 0)); 862 } 863 } else { 864 if (ctx->has_cz) { 865 extra_button_index += 2; 866 } 867 if (ctx->has_lmrm) { 868 extra_button_index += 2; 869 } 870 } 871 872 if (ctx->last_state[14] != data[14]) { 873 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[14] & 0x08) != 0)); 874 if (ctx->has_circle) { 875 SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[14] & 0x01) != 0)); 876 } 877 } 878 879 axis = LOAD16(data[3], data[4]); 880 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis); 881 axis = -LOAD16(data[5], data[6]); 882 if (axis <= -32768) { 883 axis = 32767; 884 } 885 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis); 886 axis = LOAD16(data[7], data[8]); 887 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis); 888 axis = -LOAD16(data[9], data[10]); 889 if (axis <= -32768) { 890 axis = 32767; 891 } 892 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis); 893 894#define READ_TRIGGER_AXIS(offset) \ 895 (Sint16)(((int)data[offset] * 257) - 32768) 896 { 897 axis = READ_TRIGGER_AXIS(15); 898 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis); 899 axis = READ_TRIGGER_AXIS(16); 900 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis); 901 } 902#undef READ_TRIGGER_AXIS 903 904 if (ctx->sensors_enabled) { 905 Uint64 sensor_timestamp; 906 float values[3]; 907 908 // Advance the imu sensor time stamp based on the observed rate of receipt of packets in the testcontroller app. 909 // This varies between Product ID and connection type. 910 sensor_timestamp = ctx->sensor_timestamp_ns; 911 ctx->sensor_timestamp_ns += ctx->sensor_timestamp_step_ns; 912 913 const float flGyroScale = ctx->gyroScale; 914 values[0] = HIDAPI_RemapVal((float)LOAD16(data[17], data[18]), INT16_MIN, INT16_MAX, -flGyroScale, flGyroScale); 915 values[1] = HIDAPI_RemapVal((float)LOAD16(data[21], data[22]), INT16_MIN, INT16_MAX, -flGyroScale, flGyroScale); 916 values[2] = HIDAPI_RemapVal(-(float)LOAD16(data[19], data[20]), INT16_MIN, INT16_MAX, -flGyroScale, flGyroScale); 917 SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, sensor_timestamp, values, 3); 918 919 const float flAccelScale = ctx->accelScale; 920 values[0] = LOAD16(data[23], data[24]) * flAccelScale; // Acceleration along pitch axis 921 values[1] = LOAD16(data[27], data[28]) * flAccelScale; // Acceleration along yaw axis 922 values[2] = -LOAD16(data[25], data[26]) * flAccelScale; // Acceleration along roll axis 923 SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, sensor_timestamp, values, 3); 924 } 925 926 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); 927} 928 929static void HIDAPI_DriverFlydigi_HandleStatusUpdate(SDL_HIDAPI_Device *device, Uint8 *data, int size) 930{ 931 // The status changed, see if we can acquire the controller now 932 SDL_HIDAPI_Flydigi_SendStatusRequest(device); 933} 934 935static void HIDAPI_DriverFlydigi_HandlePacketV2(SDL_Joystick *joystick, SDL_DriverFlydigi_Context *ctx, Uint8 *data, int size) 936{ 937 if (size > 0 && data[0] != 0x5A) { 938 // If first byte is not 0x5A, it must be REPORT_ID, we need to remove it. 939 ++data; 940 --size; 941 } 942 if (size < 31 || data[0] != FLYDIGI_V2_MAGIC1 || data[1] != FLYDIGI_V2_MAGIC2) { 943 // We don't know how to handle this report, ignore it 944 return; 945 } 946 947 switch (data[2]) { 948 case FLYDIGI_V2_GET_INFO_COMMAND: 949 if (joystick) { 950 HIDAPI_DriverFlydigi_HandleInfoResponse(joystick, ctx, data, size); 951 } 952 break; 953 case FLYDIGI_V2_SET_STATUS_COMMAND: 954 HIDAPI_DriverFlydigi_HandleStatusUpdate(ctx->device, data, size); 955 break; 956 case FLYDIGI_V2_GET_STATUS_COMMAND: 957 HIDAPI_DriverFlydigi_HandleStatusResponse(ctx->device, data, size); 958 break; 959 case FLYDIGI_V2_ACQUIRE_CONTROLLER_COMMAND: 960 HIDAPI_DriverFlydigi_HandleAcquireResponse(ctx->device, data, size); 961 break; 962 case FLYDIGI_V2_INPUT_REPORT: 963 if (joystick) { 964 HIDAPI_DriverFlydigi_HandleStatePacketV2(joystick, ctx, data, size); 965 } 966 break; 967 default: 968 // We don't recognize this command, ignore it 969 break; 970 } 971} 972 973static bool HIDAPI_DriverFlydigi_UpdateDevice(SDL_HIDAPI_Device *device) 974{ 975 SDL_DriverFlydigi_Context *ctx = (SDL_DriverFlydigi_Context *)device->context; 976 SDL_Joystick *joystick = NULL; 977 Uint8 data[USB_PACKET_LENGTH]; 978 int size = 0; 979 Uint64 now = SDL_GetTicks(); 980 981 if (device->num_joysticks > 0) { 982 joystick = SDL_GetJoystickFromID(device->joysticks[0]); 983 } 984 985 if (device->vendor_id == USB_VENDOR_FLYDIGI_V2 && joystick) { 986 if (!ctx->next_heartbeat || now >= ctx->next_heartbeat) { 987 SDL_HIDAPI_Flydigi_SendAcquireRequest(device, true); 988 SDL_HIDAPI_Flydigi_SendInfoRequest(device); 989 ctx->next_heartbeat = now + FLYDIGI_ACQUIRE_CONTROLLER_HEARTBEAT_TIME; 990 } 991 } 992 993 while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { 994#ifdef DEBUG_FLYDIGI_PROTOCOL 995 HIDAPI_DumpPacket("Flydigi packet: size = %d", data, size); 996#endif 997 ctx->last_packet = now; 998 999 if (device->vendor_id == USB_VENDOR_FLYDIGI_V1) { 1000 HIDAPI_DriverFlydigi_HandlePacketV1(joystick, ctx, data, size); 1001 } else { 1002 HIDAPI_DriverFlydigi_HandlePacketV2(joystick, ctx, data, size); 1003 } 1004 } 1005 1006 if (device->vendor_id == USB_VENDOR_FLYDIGI_V2) { 1007 // If we haven't gotten a packet in a while, check to make sure we can still acquire it 1008 const int INPUT_TIMEOUT_MS = 100; 1009 if (now >= (ctx->last_packet + INPUT_TIMEOUT_MS)) { 1010 ctx->next_heartbeat = now; 1011 } 1012 } 1013 1014 if (size < 0 && device->num_joysticks > 0) { 1015 // Read error, device is disconnected 1016 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 1017 } 1018 return (size >= 0); 1019} 1020 1021static void HIDAPI_DriverFlydigi_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 1022{ 1023 // Don't unacquire the controller, someone else might be using it too. 1024 // The controller will automatically unacquire itself after a little while 1025 //SDL_HIDAPI_Flydigi_SendAcquireRequest(device, false); 1026} 1027 1028static void HIDAPI_DriverFlydigi_FreeDevice(SDL_HIDAPI_Device *device) 1029{ 1030} 1031 1032SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverFlydigi = { 1033 SDL_HINT_JOYSTICK_HIDAPI_FLYDIGI, 1034 true, 1035 HIDAPI_DriverFlydigi_RegisterHints, 1036 HIDAPI_DriverFlydigi_UnregisterHints, 1037 HIDAPI_DriverFlydigi_IsEnabled, 1038 HIDAPI_DriverFlydigi_IsSupportedDevice, 1039 HIDAPI_DriverFlydigi_InitDevice, 1040 HIDAPI_DriverFlydigi_GetDevicePlayerIndex, 1041 HIDAPI_DriverFlydigi_SetDevicePlayerIndex, 1042 HIDAPI_DriverFlydigi_UpdateDevice, 1043 HIDAPI_DriverFlydigi_OpenJoystick, 1044 HIDAPI_DriverFlydigi_RumbleJoystick, 1045 HIDAPI_DriverFlydigi_RumbleJoystickTriggers, 1046 HIDAPI_DriverFlydigi_GetJoystickCapabilities, 1047 HIDAPI_DriverFlydigi_SetJoystickLED, 1048 HIDAPI_DriverFlydigi_SendJoystickEffect, 1049 HIDAPI_DriverFlydigi_SetJoystickSensorsEnabled, 1050 HIDAPI_DriverFlydigi_CloseJoystick, 1051 HIDAPI_DriverFlydigi_FreeDevice, 1052}; 1053 1054#endif // SDL_JOYSTICK_HIDAPI_FLYDIGI 1055 1056#endif // SDL_JOYSTICK_HIDAPI 1057
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.