SDL_hidapi_flydigi.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. #ifdef SDL_JOYSTICK_HIDAPI
  20. #include "../SDL_sysjoystick.h"
  21. #include "SDL_hidapijoystick_c.h"
  22. #include "SDL_hidapi_rumble.h"
  23. #ifdef SDL_JOYSTICK_HIDAPI_FLYDIGI
  24. // Define this if you want to log all packets from the controller
  25. #if 0
  26. #define DEBUG_FLYDIGI_PROTOCOL
  27. #endif
  28. enum
  29. {
  30. SDL_GAMEPAD_BUTTON_FLYDIGI_M1 = 11,
  31. SDL_GAMEPAD_BUTTON_FLYDIGI_M2,
  32. SDL_GAMEPAD_BUTTON_FLYDIGI_M3,
  33. SDL_GAMEPAD_BUTTON_FLYDIGI_M4,
  34. SDL_GAMEPAD_NUM_BASE_FLYDIGI_BUTTONS
  35. };
  36. /* Rate of IMU Sensor Packets over wireless dongle observed in testcontroller at 1000hz */
  37. #define SENSOR_INTERVAL_VADER4_PRO_DONGLE_RATE_HZ 1000
  38. #define SENSOR_INTERVAL_VADER4_PRO_DONGLE_NS (SDL_NS_PER_SECOND / SENSOR_INTERVAL_VADER4_PRO_DONGLE_RATE_HZ)
  39. /* Rate of IMU Sensor Packets over wired connection observed in testcontroller at 500hz */
  40. #define SENSOR_INTERVAL_VADER_PRO4_WIRED_RATE_HZ 500
  41. #define SENSOR_INTERVAL_VADER_PRO4_WIRED_NS (SDL_NS_PER_SECOND / SENSOR_INTERVAL_VADER_PRO4_WIRED_RATE_HZ)
  42. #define FLYDIGI_CMD_REPORT_ID 0x05
  43. #define FLYDIGI_HAPTIC_COMMAND 0x0F
  44. #define FLYDIGI_GET_CONFIG_COMMAND 0xEB
  45. #define FLYDIGI_GET_INFO_COMMAND 0xEC
  46. #define LOAD16(A, B) (Sint16)((Uint16)(A) | (((Uint16)(B)) << 8))
  47. typedef struct
  48. {
  49. Uint8 deviceID;
  50. bool has_cz;
  51. bool wireless;
  52. bool sensors_supported;
  53. bool sensors_enabled;
  54. Uint16 firmware_version;
  55. Uint64 sensor_timestamp_ns; // Simulate onboard clock. Advance by known time step. Nanoseconds.
  56. Uint64 sensor_timestamp_step_ns; // Based on observed rate of receipt of IMU sensor packets.
  57. float accelScale;
  58. Uint8 last_state[USB_PACKET_LENGTH];
  59. } SDL_DriverFlydigi_Context;
  60. static void HIDAPI_DriverFlydigi_RegisterHints(SDL_HintCallback callback, void *userdata)
  61. {
  62. SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_FLYDIGI, callback, userdata);
  63. }
  64. static void HIDAPI_DriverFlydigi_UnregisterHints(SDL_HintCallback callback, void *userdata)
  65. {
  66. SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_FLYDIGI, callback, userdata);
  67. }
  68. static bool HIDAPI_DriverFlydigi_IsEnabled(void)
  69. {
  70. return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_FLYDIGI, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));
  71. }
  72. 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)
  73. {
  74. return SDL_IsJoystickFlydigiController(vendor_id, product_id) && interface_number == 2;
  75. }
  76. static void UpdateDeviceIdentity(SDL_HIDAPI_Device *device)
  77. {
  78. SDL_DriverFlydigi_Context *ctx = (SDL_DriverFlydigi_Context *)device->context;
  79. // Detecting the Vader 2 can take over 1000 read retries, so be generous here
  80. for (int attempt = 0; ctx->deviceID == 0 && attempt < 30; ++attempt) {
  81. const Uint8 request[] = { FLYDIGI_CMD_REPORT_ID, FLYDIGI_GET_INFO_COMMAND, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  82. // This write will occasionally return -1, so ignore failure here and try again
  83. (void)SDL_hid_write(device->dev, request, sizeof(request));
  84. // Read the reply
  85. for (int i = 0; i < 100; ++i) {
  86. SDL_Delay(1);
  87. Uint8 data[USB_PACKET_LENGTH];
  88. int size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0);
  89. if (size < 0) {
  90. break;
  91. }
  92. if (size == 0) {
  93. continue;
  94. }
  95. #ifdef DEBUG_FLYDIGI_PROTOCOL
  96. HIDAPI_DumpPacket("Flydigi packet: size = %d", data, size);
  97. #endif
  98. if (size == 32 && data[15] == 236) {
  99. ctx->deviceID = data[3];
  100. ctx->firmware_version = data[9] | (data[10] << 8);
  101. char serial[9];
  102. (void)SDL_snprintf(serial, sizeof(serial), "%.2x%.2x%.2x%.2x", data[5], data[6], data[7], data[8]);
  103. HIDAPI_SetDeviceSerial(device, serial);
  104. // The Vader 2 with firmware 6.0.4.9 doesn't report the connection state
  105. if (ctx->firmware_version >= 0x6400) {
  106. switch (data[13]) {
  107. case 0:
  108. // Wireless connection
  109. ctx->wireless = true;
  110. break;
  111. case 1:
  112. // Wired connection
  113. ctx->wireless = false;
  114. break;
  115. default:
  116. break;
  117. }
  118. }
  119. // Done!
  120. break;
  121. }
  122. }
  123. }
  124. if (ctx->deviceID == 0) {
  125. // Try to guess from the name of the controller
  126. if (SDL_strstr(device->name, "VADER") != NULL) {
  127. if (SDL_strstr(device->name, "VADER2") != NULL) {
  128. ctx->deviceID = 20;
  129. } else if (SDL_strstr(device->name, "VADER3") != NULL) {
  130. ctx->deviceID = 28;
  131. } else if (SDL_strstr(device->name, "VADER4") != NULL) {
  132. ctx->deviceID = 85;
  133. }
  134. } else if (SDL_strstr(device->name, "APEX") != NULL) {
  135. if (SDL_strstr(device->name, "APEX2") != NULL) {
  136. ctx->deviceID = 19;
  137. } else if (SDL_strstr(device->name, "APEX3") != NULL) {
  138. ctx->deviceID = 24;
  139. } else if (SDL_strstr(device->name, "APEX4") != NULL) {
  140. ctx->deviceID = 84;
  141. }
  142. }
  143. }
  144. device->guid.data[15] = ctx->deviceID;
  145. // This is the previous sensor default of 125hz.
  146. // Override this in the switch statement below based on observed sensor packet rate.
  147. ctx->sensor_timestamp_step_ns = SDL_NS_PER_SECOND / 125;
  148. switch (ctx->deviceID) {
  149. case 19:
  150. HIDAPI_SetDeviceName(device, "Flydigi Apex 2");
  151. break;
  152. case 24:
  153. case 26:
  154. case 29:
  155. HIDAPI_SetDeviceName(device, "Flydigi Apex 3");
  156. break;
  157. case 84:
  158. // The Apex 4 controller has sensors, but they're only reported when gyro mouse is enabled
  159. HIDAPI_SetDeviceName(device, "Flydigi Apex 4");
  160. break;
  161. case 20:
  162. case 21:
  163. case 23:
  164. // The Vader 2 controller has sensors, but they're only reported when gyro mouse is enabled
  165. HIDAPI_SetDeviceName(device, "Flydigi Vader 2");
  166. ctx->has_cz = true;
  167. break;
  168. case 22:
  169. HIDAPI_SetDeviceName(device, "Flydigi Vader 2 Pro");
  170. ctx->has_cz = true;
  171. break;
  172. case 28:
  173. HIDAPI_SetDeviceName(device, "Flydigi Vader 3");
  174. ctx->has_cz = true;
  175. break;
  176. case 80:
  177. case 81:
  178. HIDAPI_SetDeviceName(device, "Flydigi Vader 3 Pro");
  179. ctx->has_cz = true;
  180. ctx->sensors_supported = true;
  181. ctx->accelScale = SDL_STANDARD_GRAVITY / 256.0f;
  182. ctx->sensor_timestamp_step_ns = ctx->wireless ? SENSOR_INTERVAL_VADER4_PRO_DONGLE_NS : SENSOR_INTERVAL_VADER_PRO4_WIRED_NS;
  183. break;
  184. case 85:
  185. case 105:
  186. HIDAPI_SetDeviceName(device, "Flydigi Vader 4 Pro");
  187. ctx->has_cz = true;
  188. ctx->sensors_supported = true;
  189. ctx->accelScale = SDL_STANDARD_GRAVITY / 256.0f;
  190. ctx->sensor_timestamp_step_ns = ctx->wireless ? SENSOR_INTERVAL_VADER4_PRO_DONGLE_NS : SENSOR_INTERVAL_VADER_PRO4_WIRED_NS;
  191. break;
  192. default:
  193. SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "Unknown FlyDigi controller with ID %d, name '%s'", ctx->deviceID, device->name);
  194. break;
  195. }
  196. }
  197. static bool HIDAPI_DriverFlydigi_InitDevice(SDL_HIDAPI_Device *device)
  198. {
  199. SDL_DriverFlydigi_Context *ctx = (SDL_DriverFlydigi_Context *)SDL_calloc(1, sizeof(*ctx));
  200. if (!ctx) {
  201. return false;
  202. }
  203. device->context = ctx;
  204. UpdateDeviceIdentity(device);
  205. return HIDAPI_JoystickConnected(device, NULL);
  206. }
  207. static int HIDAPI_DriverFlydigi_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
  208. {
  209. return -1;
  210. }
  211. static void HIDAPI_DriverFlydigi_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
  212. {
  213. }
  214. #ifndef DEG2RAD
  215. #define DEG2RAD(x) ((float)(x) * (float)(SDL_PI_F / 180.f))
  216. #endif
  217. static bool HIDAPI_DriverFlydigi_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
  218. {
  219. SDL_DriverFlydigi_Context *ctx = (SDL_DriverFlydigi_Context *)device->context;
  220. SDL_AssertJoysticksLocked();
  221. SDL_zeroa(ctx->last_state);
  222. // Initialize the joystick capabilities
  223. joystick->nbuttons = SDL_GAMEPAD_NUM_BASE_FLYDIGI_BUTTONS;
  224. if (ctx->has_cz) {
  225. joystick->nbuttons += 2;
  226. }
  227. joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
  228. joystick->nhats = 1;
  229. if (ctx->wireless) {
  230. joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;
  231. }
  232. if (ctx->sensors_supported) {
  233. const float flSensorRate = ctx->wireless ? (float)SENSOR_INTERVAL_VADER4_PRO_DONGLE_RATE_HZ : (float)SENSOR_INTERVAL_VADER_PRO4_WIRED_RATE_HZ;
  234. SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, flSensorRate);
  235. SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, flSensorRate);
  236. }
  237. return true;
  238. }
  239. static bool HIDAPI_DriverFlydigi_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
  240. {
  241. Uint8 rumble_packet[4] = { FLYDIGI_CMD_REPORT_ID, FLYDIGI_HAPTIC_COMMAND, 0x00, 0x00 };
  242. rumble_packet[2] = low_frequency_rumble >> 8;
  243. rumble_packet[3] = high_frequency_rumble >> 8;
  244. if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
  245. return SDL_SetError("Couldn't send rumble packet");
  246. }
  247. return true;
  248. }
  249. static bool HIDAPI_DriverFlydigi_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
  250. {
  251. return SDL_Unsupported();
  252. }
  253. static Uint32 HIDAPI_DriverFlydigi_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
  254. {
  255. return SDL_JOYSTICK_CAP_RUMBLE;
  256. }
  257. static bool HIDAPI_DriverFlydigi_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
  258. {
  259. return SDL_Unsupported();
  260. }
  261. static bool HIDAPI_DriverFlydigi_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
  262. {
  263. return SDL_Unsupported();
  264. }
  265. static bool HIDAPI_DriverFlydigi_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)
  266. {
  267. SDL_DriverFlydigi_Context *ctx = (SDL_DriverFlydigi_Context *)device->context;
  268. if (ctx->sensors_supported) {
  269. ctx->sensors_enabled = enabled;
  270. return true;
  271. }
  272. return SDL_Unsupported();
  273. }
  274. static void HIDAPI_DriverFlydigi_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverFlydigi_Context *ctx, Uint8 *data, int size)
  275. {
  276. Sint16 axis;
  277. Uint64 timestamp = SDL_GetTicksNS();
  278. if (data[0] != 0x04 || data[1] != 0xFE) {
  279. // We don't know how to handle this report
  280. return;
  281. }
  282. Uint8 extra_button_index = SDL_GAMEPAD_NUM_BASE_FLYDIGI_BUTTONS;
  283. if (ctx->last_state[9] != data[9]) {
  284. Uint8 hat;
  285. switch (data[9] & 0x0F) {
  286. case 0x01u:
  287. hat = SDL_HAT_UP;
  288. break;
  289. case 0x02u | 0x01u:
  290. hat = SDL_HAT_RIGHTUP;
  291. break;
  292. case 0x02u:
  293. hat = SDL_HAT_RIGHT;
  294. break;
  295. case 0x02u | 0x04u:
  296. hat = SDL_HAT_RIGHTDOWN;
  297. break;
  298. case 0x04u:
  299. hat = SDL_HAT_DOWN;
  300. break;
  301. case 0x08u | 0x04u:
  302. hat = SDL_HAT_LEFTDOWN;
  303. break;
  304. case 0x08u:
  305. hat = SDL_HAT_LEFT;
  306. break;
  307. case 0x08u | 0x01u:
  308. hat = SDL_HAT_LEFTUP;
  309. break;
  310. default:
  311. hat = SDL_HAT_CENTERED;
  312. break;
  313. }
  314. SDL_SendJoystickHat(timestamp, joystick, 0, hat);
  315. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[9] & 0x10) != 0));
  316. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[9] & 0x20) != 0));
  317. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[9] & 0x40) != 0));
  318. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[9] & 0x80) != 0));
  319. }
  320. if (ctx->last_state[10] != data[10]) {
  321. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[10] & 0x01) != 0));
  322. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[10] & 0x02) != 0));
  323. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[10] & 0x04) != 0));
  324. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[10] & 0x08) != 0));
  325. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[10] & 0x40) != 0));
  326. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[10] & 0x80) != 0));
  327. }
  328. if (ctx->last_state[7] != data[7]) {
  329. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_FLYDIGI_M1, ((data[7] & 0x04) != 0));
  330. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_FLYDIGI_M2, ((data[7] & 0x08) != 0));
  331. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_FLYDIGI_M3, ((data[7] & 0x10) != 0));
  332. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_FLYDIGI_M4, ((data[7] & 0x20) != 0));
  333. if (ctx->has_cz) {
  334. SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[7] & 0x01) != 0));
  335. SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[7] & 0x02) != 0));
  336. }
  337. }
  338. if (ctx->last_state[8] != data[8]) {
  339. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[8] & 0x08) != 0));
  340. // The '+' button is used to toggle gyro mouse mode, so don't pass that to the application
  341. //SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[8] & 0x01) != 0));
  342. // The '-' button is only available on the Vader 2, for simplicity let's ignore that
  343. //SDL_SendJoystickButton(timestamp, joystick, extra_button_index++, ((data[8] & 0x10) != 0));
  344. }
  345. #define READ_STICK_AXIS(offset) \
  346. (data[offset] == 0x7f ? 0 : (Sint16)HIDAPI_RemapVal((float)((int)data[offset] - 0x7f), -0x7f, 0xff - 0x7f, SDL_MIN_SINT16, SDL_MAX_SINT16))
  347. {
  348. axis = READ_STICK_AXIS(17);
  349. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
  350. axis = READ_STICK_AXIS(19);
  351. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);
  352. axis = READ_STICK_AXIS(21);
  353. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
  354. axis = READ_STICK_AXIS(22);
  355. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
  356. }
  357. #undef READ_STICK_AXIS
  358. #define READ_TRIGGER_AXIS(offset) \
  359. (Sint16)(((int)data[offset] * 257) - 32768)
  360. {
  361. axis = READ_TRIGGER_AXIS(23);
  362. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
  363. axis = READ_TRIGGER_AXIS(24);
  364. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
  365. }
  366. #undef READ_TRIGGER_AXIS
  367. if (ctx->sensors_enabled) {
  368. Uint64 sensor_timestamp;
  369. float values[3];
  370. // Advance the imu sensor time stamp based on the observed rate of receipt of packets in the testcontroller app.
  371. // This varies between Product ID and connection type.
  372. sensor_timestamp = ctx->sensor_timestamp_ns;
  373. ctx->sensor_timestamp_ns += ctx->sensor_timestamp_step_ns;
  374. // Pitch and yaw scales may be receiving extra filtering for the sake of bespoke direct mouse output.
  375. // As result, roll has a different scaling factor than pitch and yaw.
  376. // These values were estimated using the testcontroller tool in lieux of hard data sheet references.
  377. const float flPitchAndYawScale = DEG2RAD(72000.0f);
  378. const float flRollScale = DEG2RAD(1200.0f);
  379. values[0] = HIDAPI_RemapVal(-1.0f * LOAD16(data[26], data[27]), INT16_MIN, INT16_MAX, -flPitchAndYawScale, flPitchAndYawScale);
  380. values[1] = HIDAPI_RemapVal(-1.0f * LOAD16(data[18], data[20]), INT16_MIN, INT16_MAX, -flPitchAndYawScale, flPitchAndYawScale);
  381. values[2] = HIDAPI_RemapVal(-1.0f * LOAD16(data[29], data[30]), INT16_MIN, INT16_MAX, -flRollScale, flRollScale);
  382. SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, sensor_timestamp, values, 3);
  383. values[0] = -LOAD16(data[11], data[12]) * ctx->accelScale; // Acceleration along pitch axis
  384. values[1] = LOAD16(data[15], data[16]) * ctx->accelScale; // Acceleration along yaw axis
  385. values[2] = LOAD16(data[13], data[14]) * ctx->accelScale; // Acceleration along roll axis
  386. SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, sensor_timestamp, values, 3);
  387. }
  388. SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
  389. }
  390. static bool HIDAPI_DriverFlydigi_UpdateDevice(SDL_HIDAPI_Device *device)
  391. {
  392. SDL_DriverFlydigi_Context *ctx = (SDL_DriverFlydigi_Context *)device->context;
  393. SDL_Joystick *joystick = NULL;
  394. Uint8 data[USB_PACKET_LENGTH];
  395. int size = 0;
  396. if (device->num_joysticks > 0) {
  397. joystick = SDL_GetJoystickFromID(device->joysticks[0]);
  398. } else {
  399. return false;
  400. }
  401. while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
  402. #ifdef DEBUG_FLYDIGI_PROTOCOL
  403. HIDAPI_DumpPacket("Flydigi packet: size = %d", data, size);
  404. #endif
  405. if (!joystick) {
  406. continue;
  407. }
  408. HIDAPI_DriverFlydigi_HandleStatePacket(joystick, ctx, data, size);
  409. }
  410. if (size < 0) {
  411. // Read error, device is disconnected
  412. HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
  413. }
  414. return (size >= 0);
  415. }
  416. static void HIDAPI_DriverFlydigi_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
  417. {
  418. }
  419. static void HIDAPI_DriverFlydigi_FreeDevice(SDL_HIDAPI_Device *device)
  420. {
  421. }
  422. SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverFlydigi = {
  423. SDL_HINT_JOYSTICK_HIDAPI_FLYDIGI,
  424. true,
  425. HIDAPI_DriverFlydigi_RegisterHints,
  426. HIDAPI_DriverFlydigi_UnregisterHints,
  427. HIDAPI_DriverFlydigi_IsEnabled,
  428. HIDAPI_DriverFlydigi_IsSupportedDevice,
  429. HIDAPI_DriverFlydigi_InitDevice,
  430. HIDAPI_DriverFlydigi_GetDevicePlayerIndex,
  431. HIDAPI_DriverFlydigi_SetDevicePlayerIndex,
  432. HIDAPI_DriverFlydigi_UpdateDevice,
  433. HIDAPI_DriverFlydigi_OpenJoystick,
  434. HIDAPI_DriverFlydigi_RumbleJoystick,
  435. HIDAPI_DriverFlydigi_RumbleJoystickTriggers,
  436. HIDAPI_DriverFlydigi_GetJoystickCapabilities,
  437. HIDAPI_DriverFlydigi_SetJoystickLED,
  438. HIDAPI_DriverFlydigi_SendJoystickEffect,
  439. HIDAPI_DriverFlydigi_SetJoystickSensorsEnabled,
  440. HIDAPI_DriverFlydigi_CloseJoystick,
  441. HIDAPI_DriverFlydigi_FreeDevice,
  442. };
  443. #endif // SDL_JOYSTICK_HIDAPI_FLYDIGI
  444. #endif // SDL_JOYSTICK_HIDAPI