SDL_hidapi_gamesir.c 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2026 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_GAMESIR
  24. // Define this if you want to log all packets from the controller
  25. #if 0
  26. #define DEBUG_GAMESIR_PROTOCOL
  27. #endif
  28. #define GAMESIR_PACKET_HEADER_0 0xA1
  29. #define GAMESIR_PACKET_HEADER_1_GAMEPAD 0xC8
  30. #define BTN_A 0x01
  31. #define BTN_B 0x02
  32. #define BTN_C 0x04
  33. #define BTN_X 0x08
  34. #define BTN_Y 0x10
  35. #define BTN_Z 0x20
  36. #define BTN_L1 0x40
  37. #define BTN_R1 0x80
  38. #define BTN_L2 0x01
  39. #define BTN_R2 0x02
  40. #define BTN_SELECT 0x04
  41. #define BTN_START 0x08
  42. #define BTN_HOME 0x10
  43. #define BTN_L3 0x20
  44. #define BTN_R3 0x40
  45. #define BTN_CAPTURE 0x80
  46. #define BTN_UP 0x01
  47. #define BTN_UP_L 0x08
  48. #define BTN_UP_R 0x02
  49. #define BTN_DOWN 0x05
  50. #define BTN_DOWN_L 0x06
  51. #define BTN_DOWN_R 0X04
  52. #define BTN_LEFT 0x07
  53. #define BTN_RIGHT 0x03
  54. #define BTN_M 0x10
  55. #define BTN_MUTE 0x20
  56. #define BTN_L4 0x40
  57. #define BTN_R4 0x80
  58. #define BTN_L5 0x01
  59. #define BTN_R5 0x02
  60. #define BTN_L6 0x04
  61. #define BTN_R6 0x08
  62. #define BTN_L7 0x10
  63. #define BTN_R7 0x20
  64. #define BTN_L8 0x40
  65. #define BTN_R8 0x80
  66. typedef struct {
  67. Uint8 cmd;
  68. Uint8 mode;
  69. } Gamesir_CommandMode;
  70. typedef struct {
  71. bool sensors_supported;
  72. bool sensors_enabled;
  73. bool led_supported;
  74. Uint64 sensor_timestamp_ns;
  75. Uint64 sensor_timestamp_step_ns;
  76. float accelScale;
  77. float gyroScale;
  78. bool last_state_initialized;
  79. Uint8 last_state[USB_PACKET_LENGTH];
  80. SDL_hid_device *output_handle;
  81. } SDL_DriverGamesir_Context;
  82. static void HIDAPI_DriverGameSir_RegisterHints(SDL_HintCallback callback, void *userdata)
  83. {
  84. SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_GAMESIR, callback, userdata);
  85. }
  86. static void HIDAPI_DriverGameSir_UnregisterHints(SDL_HintCallback callback, void *userdata)
  87. {
  88. SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_GAMESIR, callback, userdata);
  89. }
  90. static bool HIDAPI_DriverGameSir_IsEnabled(void)
  91. {
  92. return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_GAMESIR, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));
  93. }
  94. static bool HIDAPI_DriverGameSir_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)
  95. {
  96. return SDL_IsJoystickGameSirController(vendor_id, product_id);
  97. }
  98. static SDL_hid_device *HIDAPI_DriverGameSir_GetOutputHandle(SDL_HIDAPI_Device *device)
  99. {
  100. #if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
  101. SDL_DriverGamesir_Context *ctx = (SDL_DriverGamesir_Context *)device->context;
  102. return ctx->output_handle;
  103. #else
  104. return device->dev;
  105. #endif
  106. }
  107. static SDL_hid_device *HIDAPI_DriverGameSir_GetInputHandle(SDL_HIDAPI_Device *device, SDL_DriverGamesir_Context *ctx)
  108. {
  109. #if defined(_WIN32)
  110. if (device->is_bluetooth) {
  111. return device->dev;
  112. }
  113. if (ctx->output_handle) {
  114. return ctx->output_handle;
  115. }
  116. return device->dev;
  117. #else
  118. return device->dev;
  119. #endif
  120. }
  121. static bool SendGameSirModeSwitch(SDL_HIDAPI_Device *device)
  122. {
  123. Gamesir_CommandMode cmd = { 0x01, 0x00 };
  124. Uint8 buf[64];
  125. SDL_zero(buf);
  126. buf[0] = 0xA2;
  127. SDL_memcpy(buf + 1, &cmd, sizeof(cmd));
  128. SDL_hid_device *handle = HIDAPI_DriverGameSir_GetOutputHandle(device);
  129. if (handle == NULL) {
  130. return false;
  131. }
  132. for (int attempt = 0; attempt < 3; ++attempt) {
  133. int result = SDL_hid_write(handle, buf, sizeof(buf));
  134. if (result >= 0) {
  135. return true;
  136. }
  137. SDL_Delay(1);
  138. }
  139. return false;
  140. }
  141. /* ========================================================================= */
  142. /* Win32 HID helper */
  143. /* ========================================================================= */
  144. /* This helper requires full desktop Win32 HID APIs.
  145. * These APIs are NOT available on GDK platforms.
  146. */
  147. #if defined(SDL_PLATFORM_WIN32) && !defined(SDL_PLATFORM_GDK)
  148. /* --- Win32 HID includes ------------------------------------------------- */
  149. #include <windows.h>
  150. #include <setupapi.h>
  151. #include <hidsdi.h>
  152. #if defined(_MSC_VER)
  153. #pragma comment(lib, "setupapi.lib")
  154. #pragma comment(lib, "hid.lib")
  155. #endif
  156. static char *FindHIDInterfacePath(Uint16 vid, Uint16 pid, int collection_index)
  157. {
  158. GUID hidGuid;
  159. HidD_GetHidGuid(&hidGuid);
  160. HDEVINFO deviceInfoSet = SetupDiGetClassDevs(
  161. &hidGuid, NULL, NULL,
  162. DIGCF_PRESENT | DIGCF_DEVICEINTERFACE
  163. );
  164. if (deviceInfoSet == INVALID_HANDLE_VALUE) {
  165. return NULL;
  166. }
  167. SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
  168. deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
  169. for (DWORD i = 0;
  170. SetupDiEnumDeviceInterfaces(deviceInfoSet, NULL, &hidGuid, i, &deviceInterfaceData);
  171. i++) {
  172. DWORD requiredSize = 0;
  173. SetupDiGetDeviceInterfaceDetail(
  174. deviceInfoSet, &deviceInterfaceData,
  175. NULL, 0, &requiredSize, NULL
  176. );
  177. PSP_DEVICE_INTERFACE_DETAIL_DATA deviceDetail =
  178. (PSP_DEVICE_INTERFACE_DETAIL_DATA)SDL_malloc(requiredSize);
  179. if (!deviceDetail) {
  180. continue;
  181. }
  182. deviceDetail->cbSize = sizeof(*deviceDetail);
  183. if (!SetupDiGetDeviceInterfaceDetail(
  184. deviceInfoSet, &deviceInterfaceData,
  185. deviceDetail, requiredSize, NULL, NULL)) {
  186. SDL_free(deviceDetail);
  187. continue;
  188. }
  189. HANDLE hDevice = CreateFile(
  190. deviceDetail->DevicePath,
  191. GENERIC_READ | GENERIC_WRITE,
  192. FILE_SHARE_READ | FILE_SHARE_WRITE,
  193. NULL,
  194. OPEN_EXISTING,
  195. FILE_FLAG_OVERLAPPED,
  196. NULL
  197. );
  198. if (hDevice == INVALID_HANDLE_VALUE) {
  199. SDL_free(deviceDetail);
  200. continue;
  201. }
  202. HIDD_ATTRIBUTES attributes;
  203. attributes.Size = sizeof(attributes);
  204. if (!HidD_GetAttributes(hDevice, &attributes) ||
  205. attributes.VendorID != vid ||
  206. attributes.ProductID != pid) {
  207. CloseHandle(hDevice);
  208. SDL_free(deviceDetail);
  209. continue;
  210. }
  211. PHIDP_PREPARSED_DATA preparsedData = NULL;
  212. if (!HidD_GetPreparsedData(hDevice, &preparsedData) || !preparsedData) {
  213. CloseHandle(hDevice);
  214. SDL_free(deviceDetail);
  215. continue;
  216. }
  217. HIDP_CAPS caps;
  218. if (HidP_GetCaps(preparsedData, &caps) != HIDP_STATUS_SUCCESS) {
  219. HidD_FreePreparsedData(preparsedData);
  220. CloseHandle(hDevice);
  221. SDL_free(deviceDetail);
  222. continue;
  223. }
  224. if ((caps.InputReportByteLength == 64 && caps.OutputReportByteLength == 64) ||
  225. (caps.InputReportByteLength == 37 && caps.OutputReportByteLength == 37)) {
  226. char col_str[16];
  227. SDL_snprintf(col_str, sizeof(col_str), "col%02d", collection_index);
  228. if (SDL_strcasestr(deviceDetail->DevicePath, col_str)) {
  229. char *result = SDL_strdup(deviceDetail->DevicePath);
  230. HidD_FreePreparsedData(preparsedData);
  231. CloseHandle(hDevice);
  232. SDL_free(deviceDetail);
  233. SetupDiDestroyDeviceInfoList(deviceInfoSet);
  234. return result;
  235. }
  236. }
  237. HidD_FreePreparsedData(preparsedData);
  238. CloseHandle(hDevice);
  239. SDL_free(deviceDetail);
  240. }
  241. SetupDiDestroyDeviceInfoList(deviceInfoSet);
  242. return NULL;
  243. }
  244. #endif // SDL_PLATFORM_WIN32 && !SDL_PLATFORM_GDK
  245. static SDL_hid_device *GetOutputHandle(SDL_HIDAPI_Device *device)
  246. {
  247. Uint16 vendor_id = device->vendor_id;
  248. Uint16 product_id = device->product_id;
  249. SDL_hid_device *output_handle = NULL;
  250. struct SDL_hid_device_info *devs = SDL_hid_enumerate(vendor_id, product_id);
  251. for (struct SDL_hid_device_info *info = devs; info && !output_handle; info = info->next) {
  252. if (info->interface_number == 0) {
  253. #if defined(SDL_PLATFORM_WIN32) && !defined(SDL_PLATFORM_GDK)
  254. char *col02_path = FindHIDInterfacePath(vendor_id, product_id, 2);
  255. if (col02_path) {
  256. output_handle = SDL_hid_open_path(col02_path);
  257. SDL_free(col02_path);
  258. }
  259. #endif
  260. } else if (info->interface_number == -1) {
  261. #if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
  262. if (info->usage_page == 0x0001 && info->usage == 0x0005) {
  263. output_handle = SDL_hid_open_path(info->path);
  264. }
  265. #endif
  266. } else if (info->interface_number == 1) {
  267. output_handle = SDL_hid_open_path(info->path);
  268. }
  269. }
  270. SDL_hid_free_enumeration(devs);
  271. return output_handle;
  272. }
  273. static bool HIDAPI_DriverGameSir_InitDevice(SDL_HIDAPI_Device *device)
  274. {
  275. SDL_DriverGamesir_Context *ctx = (SDL_DriverGamesir_Context *)SDL_calloc(1, sizeof(*ctx));
  276. if (!ctx) {
  277. return false;
  278. }
  279. device->context = ctx;
  280. ctx->led_supported = true;
  281. ctx->output_handle = GetOutputHandle(device);
  282. switch (device->product_id) {
  283. case USB_PRODUCT_GAMESIR_GAMEPAD_G7_PRO_8K:
  284. HIDAPI_SetDeviceName(device, "GameSir-G7 Pro 8K");
  285. ctx->sensors_supported = true;
  286. ctx->led_supported = false;
  287. SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "GameSir: Device detected - G7 Pro 8K mode (PID 0x%04X)", device->product_id);
  288. break;
  289. default:
  290. HIDAPI_SetDeviceName(device, "GameSir Controller");
  291. break;
  292. }
  293. return HIDAPI_JoystickConnected(device, NULL);
  294. }
  295. static int HIDAPI_DriverGameSir_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
  296. {
  297. return -1;
  298. }
  299. static void HIDAPI_DriverGameSir_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
  300. {
  301. }
  302. static bool HIDAPI_DriverGameSir_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
  303. {
  304. SDL_DriverGamesir_Context *ctx = (SDL_DriverGamesir_Context *)device->context;
  305. SDL_AssertJoysticksLocked();
  306. if (!ctx) {
  307. return false;
  308. }
  309. SDL_zeroa(ctx->last_state);
  310. ctx->last_state_initialized = false;
  311. if (!SendGameSirModeSwitch(device)) {
  312. SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "GameSir: failed to send SDL mode switch command (0xA2, 0x01)");
  313. }
  314. joystick->nbuttons = 35;
  315. joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
  316. joystick->nhats = 1;
  317. if (ctx->sensors_supported) {
  318. ctx->sensor_timestamp_step_ns = SDL_NS_PER_SECOND / 125;
  319. // Accelerometer scale factor: assume a range of ±2g, 16-bit signed values (-32768 to 32767)
  320. // 32768 corresponds to 2g, so the scale factor = 2 * SDL_STANDARD_GRAVITY / 32768.0f
  321. ctx->accelScale = 2.0f * SDL_STANDARD_GRAVITY / 32768.0f;
  322. // Gyro scale factor: based on the PS4 implementation
  323. // PS4 uses (gyro_numerator / gyro_denominator) * (π / 180)
  324. // The default value is (1 / 16) * (π / 180), corresponding to a range of approximately ±2048 degrees/second
  325. // This is a common range for gamepad gyroscopes
  326. const float gyro_numerator = 1.0f;
  327. const float gyro_denominator = 16.0f;
  328. ctx->gyroScale = (gyro_numerator / gyro_denominator) * (SDL_PI_F / 180.0f);
  329. const float flSensorRate = 1000.0f;
  330. SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, flSensorRate);
  331. SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, flSensorRate);
  332. }
  333. return true;
  334. }
  335. static bool HIDAPI_DriverGameSir_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
  336. {
  337. Uint8 buf[64];
  338. SDL_zero(buf);
  339. buf[0] = 0xA2;
  340. buf[1] = 0x03;
  341. buf[2] = (Uint8)(low_frequency_rumble >> 8);
  342. buf[3] = (Uint8)(high_frequency_rumble >> 8);
  343. SDL_hid_device *handle = HIDAPI_DriverGameSir_GetOutputHandle(device);
  344. if (handle == NULL) {
  345. return false;
  346. }
  347. int result = SDL_hid_write(handle, buf, sizeof(buf));
  348. if (result < 0) {
  349. return false;
  350. }
  351. return true;
  352. }
  353. static bool HIDAPI_DriverGameSir_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
  354. {
  355. return SDL_Unsupported();
  356. }
  357. static Uint32 HIDAPI_DriverGameSir_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
  358. {
  359. SDL_DriverGamesir_Context *ctx = (SDL_DriverGamesir_Context *)device->context;
  360. Uint32 caps = SDL_JOYSTICK_CAP_RUMBLE;
  361. if (ctx->led_supported) {
  362. caps |= SDL_JOYSTICK_CAP_RGB_LED;
  363. }
  364. return caps;
  365. }
  366. static bool HIDAPI_DriverGameSir_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
  367. {
  368. SDL_DriverGamesir_Context *ctx = (SDL_DriverGamesir_Context *)device->context;
  369. if (!ctx->led_supported) {
  370. return SDL_Unsupported();
  371. }
  372. Uint8 buf[64];
  373. SDL_zero(buf);
  374. buf[0] = 0xA2;
  375. buf[1] = 0x04;
  376. buf[2] = 0x01;
  377. buf[3] = 0x01;
  378. buf[4] = red;
  379. buf[5] = green;
  380. buf[6] = blue;
  381. SDL_hid_device *handle = HIDAPI_DriverGameSir_GetOutputHandle(device);
  382. if (handle == NULL) {
  383. return false;
  384. }
  385. int result = SDL_hid_write(handle, buf, sizeof(buf));
  386. if (result < 0) {
  387. return false;
  388. }
  389. return true;
  390. }
  391. static bool HIDAPI_DriverGameSir_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
  392. {
  393. return SDL_Unsupported();
  394. }
  395. static bool HIDAPI_DriverGameSir_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)
  396. {
  397. SDL_DriverGamesir_Context *ctx = (SDL_DriverGamesir_Context *)device->context;
  398. if (ctx->sensors_supported) {
  399. ctx->sensors_enabled = enabled;
  400. return true;
  401. }
  402. return SDL_Unsupported();
  403. }
  404. static bool ApplyCircularDeadzone(Sint16 x, Sint16 y, Sint16 *out_x, Sint16 *out_y)
  405. {
  406. const Sint16 MAX_AXIS = 32767;
  407. const float deadzone_percent = 5.0f;
  408. const float deadzone_radius = (float)MAX_AXIS * deadzone_percent / 100.0f;
  409. float distance = SDL_sqrtf((float)x * (float)x + (float)y * (float)y);
  410. if (distance == 0.0f) {
  411. *out_x = 0;
  412. *out_y = 0;
  413. return false;
  414. }
  415. if (distance < deadzone_radius) {
  416. *out_x = 0;
  417. *out_y = 0;
  418. return false;
  419. }
  420. float scale = (distance - deadzone_radius) / (MAX_AXIS - deadzone_radius);
  421. float normalized_x = (float)x / distance;
  422. float normalized_y = (float)y / distance;
  423. *out_x = (Sint16)(normalized_x * scale * MAX_AXIS);
  424. *out_y = (Sint16)(normalized_y * scale * MAX_AXIS);
  425. return true;
  426. }
  427. static void HIDAPI_DriverGameSir_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverGamesir_Context *ctx, Uint8 *data, int size)
  428. {
  429. Sint16 axis;
  430. Uint64 timestamp = SDL_GetTicksNS();
  431. const Uint8 *last = ctx->last_state;
  432. bool is_initial_packet = !ctx->last_state_initialized;
  433. const int min_payload_size = ctx->sensors_enabled ? 26 : 14;
  434. if (size < min_payload_size) {
  435. return;
  436. }
  437. if (last[0] != data[0]) {
  438. Uint8 buttons = data[0];
  439. // BTN1: A B C X Y Z L1 R1
  440. // Use bitwise operations to check whether each button is pressed
  441. // buttons & BTN_A returns the value of BTN_A (if pressed) or 0 (if not pressed)
  442. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, buttons & BTN_A);
  443. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, buttons & BTN_B);
  444. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, buttons & BTN_X);
  445. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, buttons & BTN_Y);
  446. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, buttons & BTN_L1);
  447. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, buttons & BTN_R1);
  448. }
  449. if (last[1] != data[1]) {
  450. Uint8 buttons = data[1];
  451. // BTN2: L2 R2 SELECT START HOME L3 R3 CAPTURE
  452. // Note: L2/R2 appear as digital buttons in data[1], but their actual analog values are in data[12]/data[13].
  453. // Only handle the other buttons here; trigger analog values are processed later in the code.
  454. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, buttons & BTN_SELECT);
  455. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, buttons & BTN_START);
  456. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, buttons & BTN_HOME);
  457. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, buttons & BTN_L3);
  458. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, buttons & BTN_R3);
  459. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_MISC1, buttons & BTN_CAPTURE);
  460. }
  461. if (last[2] != data[2]) {
  462. Uint8 buttons = data[2];
  463. // BTN3: UP DOWN LEFT RIGHT M MUTE L4 R4
  464. // Handle the directional pad (D-pad)
  465. Uint8 hat = SDL_HAT_CENTERED;
  466. if (buttons == BTN_UP_R) {
  467. hat = SDL_HAT_RIGHTUP;
  468. } else if (buttons == BTN_UP_L) {
  469. hat = SDL_HAT_LEFTUP;
  470. } else if (buttons == BTN_DOWN_R) {
  471. hat = SDL_HAT_RIGHTDOWN;
  472. } else if (buttons == BTN_DOWN_L) {
  473. hat = SDL_HAT_LEFTDOWN;
  474. } else if (buttons == BTN_UP) {
  475. hat = SDL_HAT_UP;
  476. } else if (buttons == BTN_DOWN) {
  477. hat = SDL_HAT_DOWN;
  478. } else if (buttons == BTN_LEFT) {
  479. hat = SDL_HAT_LEFT;
  480. } else if (buttons == BTN_RIGHT) {
  481. hat = SDL_HAT_RIGHT;
  482. } else {
  483. hat = SDL_HAT_CENTERED;
  484. }
  485. SDL_SendJoystickHat(timestamp, joystick, 0, hat);
  486. // Handle other buttons
  487. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1, buttons & BTN_L4);
  488. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_PADDLE1, buttons & BTN_R4);
  489. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_MISC2, buttons & BTN_MUTE);
  490. }
  491. if (last[3] != data[3]) {
  492. Uint8 buttons = data[3];
  493. // BTN4: L5 R5 L6 R6 L7 R7 L8 R8
  494. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_PADDLE2, buttons & BTN_L5);
  495. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2, buttons & BTN_R5);
  496. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_MISC3, buttons & BTN_L6);
  497. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_MISC4, buttons & BTN_R6);
  498. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_MISC5, buttons & BTN_L7);
  499. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_MISC6, buttons & BTN_R7);
  500. }
  501. if (is_initial_packet) {
  502. // Initialize all joystick axes to center positions
  503. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
  504. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
  505. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, 0);
  506. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, 0);
  507. } else {
  508. // Left stick handling
  509. // Left stick: payload bytes 4-7 (16-bit values)
  510. // Bytes 4-5: X axis (Hi/Low combined into a signed 16-bit value, e.g. 0x7df6)
  511. // Bytes 6-7: Y axis (Hi/Low combined into a signed 16-bit value)
  512. if (size >= 8) {
  513. // Combine bytes 4-5 into a 16-bit value, e.g.: data[4]=0x7d, data[5]=0xf6 -> 0x7df6
  514. Uint16 raw_x_unsigned = ((Uint16)data[4] << 8) | data[5];
  515. Uint16 raw_y_unsigned = ((Uint16)data[6] << 8) | data[7];
  516. // Interpret the unsigned 16-bit value as a signed 16-bit value
  517. Sint16 raw_x = (Sint16)raw_x_unsigned;
  518. Sint16 raw_y = (Sint16)raw_y_unsigned;
  519. Sint16 left_x, left_y;
  520. // Use signed 16-bit values directly; invert Y-axis (SDL convention: up is negative)
  521. left_x = raw_x;
  522. left_y = -raw_y;
  523. Uint16 last_raw_x_unsigned = ((Uint16)last[4] << 8) | last[5];
  524. Uint16 last_raw_y_unsigned = ((Uint16)last[6] << 8) | last[7];
  525. Sint16 last_raw_x = (Sint16)last_raw_x_unsigned;
  526. Sint16 last_raw_y = (Sint16)last_raw_y_unsigned;
  527. bool raw_changed = (raw_x != last_raw_x || raw_y != last_raw_y);
  528. if (raw_changed) {
  529. Sint16 deadzone_x, deadzone_y;
  530. ApplyCircularDeadzone(left_x, left_y, &deadzone_x, &deadzone_y);
  531. Sint16 last_left_x, last_left_y;
  532. last_left_x = last_raw_x;
  533. last_left_y = -last_raw_y; // invert Y axis
  534. Sint16 last_deadzone_x, last_deadzone_y;
  535. ApplyCircularDeadzone(last_left_x, last_left_y, &last_deadzone_x, &last_deadzone_y);
  536. if (deadzone_x != last_deadzone_x || deadzone_y != last_deadzone_y) {
  537. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, deadzone_x);
  538. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, deadzone_y);
  539. }
  540. }
  541. }
  542. // Right stick handling
  543. // Right stick: payload bytes 8-11 (16-bit values)
  544. // Bytes 8-9: X axis (Hi/Low combined into a signed 16-bit value)
  545. // Bytes 10-11: Y axis (Hi/Low combined into a signed 16-bit value)
  546. if (size >= 12) {
  547. // Combine bytes 8-9 into a 16-bit value
  548. Uint16 raw_x_unsigned = ((Uint16)data[8] << 8) | data[9];
  549. // Combine bytes 10-11 into a 16-bit value
  550. Uint16 raw_y_unsigned = ((Uint16)data[10] << 8) | data[11];
  551. // Interpret the unsigned 16-bit value as a signed 16-bit value
  552. Sint16 raw_x = (Sint16)raw_x_unsigned;
  553. Sint16 raw_y = (Sint16)raw_y_unsigned;
  554. Sint16 right_x, right_y;
  555. // Use signed 16-bit values directly; invert Y-axis (SDL convention: up is negative)
  556. right_x = raw_x;
  557. right_y = -raw_y;
  558. Uint16 last_raw_x_unsigned = ((Uint16)last[8] << 8) | last[9];
  559. Uint16 last_raw_y_unsigned = ((Uint16)last[10] << 8) | last[11];
  560. Sint16 last_raw_x = (Sint16)last_raw_x_unsigned;
  561. Sint16 last_raw_y = (Sint16)last_raw_y_unsigned;
  562. bool raw_changed = (raw_x != last_raw_x || raw_y != last_raw_y);
  563. if (raw_changed) {
  564. Sint16 deadzone_x, deadzone_y;
  565. ApplyCircularDeadzone(right_x, right_y, &deadzone_x, &deadzone_y);
  566. Sint16 last_right_x, last_right_y;
  567. last_right_x = last_raw_x;
  568. last_right_y = -last_raw_y; // invert Y axis
  569. Sint16 last_deadzone_x, last_deadzone_y;
  570. ApplyCircularDeadzone(last_right_x, last_right_y, &last_deadzone_x, &last_deadzone_y);
  571. if (deadzone_x != last_deadzone_x || deadzone_y != last_deadzone_y) {
  572. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, deadzone_x);
  573. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, deadzone_y);
  574. }
  575. }
  576. }
  577. // Handle trigger axes
  578. // Protocol: L2 (payload byte 12) - analog left trigger 0-255, 0 = released, 255 = pressed
  579. // R2 (payload byte 13) - analog right trigger 0-255, 0 = released, 255 = pressed
  580. // SDL range: 0-32767 (0 = released, 32767 = fully pressed)
  581. // Linear mapping: 0-255 -> 0-32767
  582. if (last[12] != data[12]) {
  583. axis = (Sint16)(((int)data[12] * 255) - 32767);
  584. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
  585. }
  586. if (last[13] != data[13]) {
  587. axis = (Sint16)(((int)data[13] * 255) - 32767);
  588. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
  589. }
  590. }
  591. if (ctx->sensors_enabled && !is_initial_packet && size >= 26) {
  592. Uint64 sensor_timestamp;
  593. float values[3];
  594. sensor_timestamp = ctx->sensor_timestamp_ns;
  595. ctx->sensor_timestamp_ns += ctx->sensor_timestamp_step_ns;
  596. // Accelerometer data (payload bytes 14-19)
  597. // Bytes 14-15: Acc X (Hi/Low combined into a signed 16-bit value)
  598. // Bytes 16-17: Acc Y (Hi/Low combined into a signed 16-bit value)
  599. // Bytes 18-19: Acc Z (Hi/Low combined into a signed 16-bit value)
  600. Uint16 acc_x_unsigned = ((Uint16)data[14] << 8) | data[15];
  601. Uint16 acc_y_unsigned = ((Uint16)data[16] << 8) | data[17];
  602. Uint16 acc_z_unsigned = ((Uint16)data[18] << 8) | data[19];
  603. // Convert the unsigned 16-bit values to signed 16-bit values
  604. Sint16 acc_x = (Sint16)acc_x_unsigned;
  605. Sint16 acc_y = (Sint16)acc_y_unsigned;
  606. Sint16 acc_z = (Sint16)acc_z_unsigned;
  607. // Apply scale factor and convert to floating point
  608. // Coordinate system matches PS4; use raw values directly without sign inversion
  609. values[0] = (float)acc_x * ctx->accelScale; // Acc X
  610. values[1] = (float)acc_y * ctx->accelScale; // Acc Y
  611. values[2] = (float)acc_z * ctx->accelScale; // Acc Z
  612. SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, sensor_timestamp, values, 3);
  613. // Gyroscope data (payload bytes 20-25)
  614. // Bytes 20-21: Gyro X (Hi/Low combined into a signed 16-bit value)
  615. // Bytes 22-23: Gyro Y (Hi/Low combined into a signed 16-bit value)
  616. // Bytes 24-25: Gyro Z (Hi/Low combined into a signed 16-bit value)
  617. Uint16 gyro_x_unsigned = ((Uint16)data[20] << 8) | data[21];
  618. Uint16 gyro_y_unsigned = ((Uint16)data[22] << 8) | data[23];
  619. Uint16 gyro_z_unsigned = ((Uint16)data[24] << 8) | data[25];
  620. // Convert the unsigned 16-bit values to signed 16-bit values
  621. Sint16 gyro_x = (Sint16)gyro_x_unsigned;
  622. Sint16 gyro_y = (Sint16)gyro_y_unsigned;
  623. Sint16 gyro_z = (Sint16)gyro_z_unsigned;
  624. // Apply scale factor and convert to floating point (radians/second)
  625. // Based on the PS4 implementation: use (gyro_numerator / gyro_denominator) * (π / 180)
  626. // The default configuration corresponds to a range of approximately ±2048 degrees/second,
  627. // which is a common range for gamepad gyroscopes
  628. // Coordinate system matches the PS4; use raw values directly without sign inversion
  629. values[0] = (float)gyro_x * ctx->gyroScale; // Gyro X (Pitch)
  630. values[1] = (float)gyro_y * ctx->gyroScale; // Gyro Y (Yaw)
  631. values[2] = (float)gyro_z * ctx->gyroScale; // Gyro Z (Roll)
  632. SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, sensor_timestamp, values, 3);
  633. }
  634. if (size >= 32) {
  635. Uint16 l_touchpad_x = ((Uint16)data[26] << 4) | ((data[27] >> 4) & 0x0F);
  636. Uint16 l_touchpad_y = ((Uint16)(data[27] & 0x0F) << 8) | data[28];
  637. Uint16 r_touchpad_x = ((Uint16)data[29] << 4) | ((data[30] >> 4) & 0x0F);
  638. Uint16 r_touchpad_y = ((Uint16)(data[30] & 0x0F) << 8) | data[31];
  639. (void)l_touchpad_x;
  640. (void)l_touchpad_y;
  641. (void)r_touchpad_x;
  642. (void)r_touchpad_y;
  643. }
  644. SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
  645. ctx->last_state_initialized = true;
  646. }
  647. static void HIDAPI_DriverGameSir_HandleSimpleStatePacketBluetooth(SDL_Joystick *joystick, SDL_DriverGamesir_Context *ctx, Uint8 *data, int size)
  648. {
  649. Sint16 axis;
  650. Uint64 timestamp = SDL_GetTicksNS();
  651. const Uint8 *last = ctx->last_state;
  652. if (last[5] != data[5]) {
  653. Uint8 buttons = data[5];
  654. // BTN1: A B C X Y Z L1 R1
  655. // Use bitwise operations to check whether each button is pressed
  656. // buttons & BTN_A returns the value of BTN_A (if pressed) or 0 (if not pressed)
  657. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, buttons & BTN_A);
  658. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, buttons & BTN_B);
  659. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, buttons & BTN_X);
  660. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, buttons & BTN_Y);
  661. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, buttons & BTN_L1);
  662. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, buttons & BTN_R1);
  663. }
  664. if (last[6] != data[6]) {
  665. Uint8 buttons = data[6];
  666. // BTN2: L2 R2 SELECT START HOME L3 R3 CAPTURE
  667. // Note: L2/R2 appear as digital buttons in data[6], but their actual analog values are in data[7]/data[8].
  668. // Only handle the other buttons here; trigger analog values are processed later in the code.
  669. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, buttons & BTN_SELECT);
  670. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, buttons & BTN_START);
  671. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, buttons & BTN_HOME);
  672. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, buttons & BTN_L3);
  673. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, buttons & BTN_R3);
  674. }
  675. if (last[4] != data[4]) {
  676. Uint8 hat;
  677. switch (data[4] & 0xF) {
  678. case 0:
  679. hat = SDL_HAT_UP;
  680. break;
  681. case 1:
  682. hat = SDL_HAT_RIGHTUP;
  683. break;
  684. case 2:
  685. hat = SDL_HAT_RIGHT;
  686. break;
  687. case 3:
  688. hat = SDL_HAT_RIGHTDOWN;
  689. break;
  690. case 4:
  691. hat = SDL_HAT_DOWN;
  692. break;
  693. case 5:
  694. hat = SDL_HAT_LEFTDOWN;
  695. break;
  696. case 6:
  697. hat = SDL_HAT_LEFT;
  698. break;
  699. case 7:
  700. hat = SDL_HAT_LEFTUP;
  701. break;
  702. default:
  703. hat = SDL_HAT_CENTERED;
  704. break;
  705. }
  706. SDL_SendJoystickHat(timestamp, joystick, 0, hat);
  707. }
  708. #define READ_STICK_AXIS(offset) \
  709. (data[offset] == 0x80 ? 0 : (Sint16)HIDAPI_RemapVal((float)((int)data[offset] - 0x80), -0x80, 0xff - 0x80, SDL_MIN_SINT16, SDL_MAX_SINT16))
  710. {
  711. axis = READ_STICK_AXIS(0);
  712. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
  713. axis = READ_STICK_AXIS(1);
  714. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);
  715. axis = READ_STICK_AXIS(2);
  716. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
  717. axis = READ_STICK_AXIS(3);
  718. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
  719. }
  720. #undef READ_STICK_AXIS
  721. #define READ_TRIGGER_AXIS(offset) \
  722. (Sint16)HIDAPI_RemapVal((float)data[offset], 0, 0xff, SDL_MIN_SINT16, SDL_MAX_SINT16)
  723. axis = READ_TRIGGER_AXIS(8);
  724. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
  725. axis = READ_TRIGGER_AXIS(7);
  726. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
  727. #undef READ_TRIGGER_AXIS
  728. SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
  729. }
  730. static void HIDAPI_DriverGameSir_HandleSimpleStatePacketUSB(SDL_Joystick *joystick, SDL_DriverGamesir_Context *ctx, Uint8 *data, int size)
  731. {
  732. Sint16 axis;
  733. Uint64 timestamp = SDL_GetTicksNS();
  734. const Uint8 *last = ctx->last_state;
  735. if (last[0] != data[0]) {
  736. Uint8 buttons = data[0];
  737. // BTN1: A B C X Y Z L1 R1
  738. // Use bitwise operations to check whether each button is pressed
  739. // buttons & BTN_A returns the value of BTN_A (if pressed) or 0 (if not pressed)
  740. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, buttons & BTN_A);
  741. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, buttons & BTN_B);
  742. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, buttons & BTN_X);
  743. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, buttons & BTN_Y);
  744. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, buttons & BTN_L1);
  745. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, buttons & BTN_R1);
  746. }
  747. if (last[1] != data[1]) {
  748. Uint8 buttons = data[1];
  749. // BTN2: L2 R2 SELECT START HOME L3 R3 CAPTURE
  750. // Note: L2/R2 appear as digital buttons in data[6], but their actual analog values are in data[7]/data[8].
  751. // Only handle the other buttons here; trigger analog values are processed later in the code.
  752. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, buttons & BTN_SELECT);
  753. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, buttons & BTN_START);
  754. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, buttons & BTN_HOME);
  755. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, buttons & BTN_L3);
  756. SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, buttons & BTN_R3);
  757. }
  758. if (last[2] != data[2]) {
  759. Uint8 hat;
  760. switch (data[2] & 0xF) {
  761. case 0:
  762. hat = SDL_HAT_UP;
  763. break;
  764. case 1:
  765. hat = SDL_HAT_RIGHTUP;
  766. break;
  767. case 2:
  768. hat = SDL_HAT_RIGHT;
  769. break;
  770. case 3:
  771. hat = SDL_HAT_RIGHTDOWN;
  772. break;
  773. case 4:
  774. hat = SDL_HAT_DOWN;
  775. break;
  776. case 5:
  777. hat = SDL_HAT_LEFTDOWN;
  778. break;
  779. case 6:
  780. hat = SDL_HAT_LEFT;
  781. break;
  782. case 7:
  783. hat = SDL_HAT_LEFTUP;
  784. break;
  785. default:
  786. hat = SDL_HAT_CENTERED;
  787. break;
  788. }
  789. SDL_SendJoystickHat(timestamp, joystick, 0, hat);
  790. }
  791. #define READ_STICK_AXIS(offset) \
  792. (data[offset] == 0x80 ? 0 : (Sint16)HIDAPI_RemapVal((float)((int)data[offset] - 0x80), -0x80, 0xff - 0x80, SDL_MIN_SINT16, SDL_MAX_SINT16))
  793. {
  794. axis = READ_STICK_AXIS(3);
  795. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
  796. axis = READ_STICK_AXIS(4);
  797. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);
  798. axis = READ_STICK_AXIS(5);
  799. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
  800. axis = READ_STICK_AXIS(6);
  801. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
  802. }
  803. #undef READ_STICK_AXIS
  804. #define READ_TRIGGER_AXIS(offset) \
  805. (Sint16)HIDAPI_RemapVal((float)data[offset], 0, 0xff, SDL_MIN_SINT16, SDL_MAX_SINT16)
  806. axis = READ_TRIGGER_AXIS(7);
  807. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
  808. axis = READ_TRIGGER_AXIS(8);
  809. SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
  810. #undef READ_TRIGGER_AXIS
  811. SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
  812. }
  813. static bool HIDAPI_DriverGameSir_UpdateDevice(SDL_HIDAPI_Device *device)
  814. {
  815. SDL_DriverGamesir_Context *ctx = (SDL_DriverGamesir_Context *)device->context;
  816. SDL_Joystick *joystick = NULL;
  817. Uint8 data[USB_PACKET_LENGTH];
  818. int size;
  819. if (device->num_joysticks > 0) {
  820. joystick = SDL_GetJoystickFromID(device->joysticks[0]);
  821. }
  822. SDL_hid_device *handle = HIDAPI_DriverGameSir_GetInputHandle(device, ctx);
  823. if (handle == NULL) {
  824. return false;
  825. }
  826. while ((size = SDL_hid_read_timeout(handle, data, sizeof(data), 0)) > 0) {
  827. #ifdef DEBUG_GAMESIR_PROTOCOL
  828. HIDAPI_DumpPacket("GameSir packet: size = %d", data, size);
  829. #endif
  830. if (!joystick) {
  831. continue;
  832. }
  833. // Check packet format: it may include a report ID (0x43) as the first byte
  834. // Actual packet format: 43 a1 c8 [button data...]
  835. // If the first byte is 0x43, the second is 0xA1 and the third is 0xC8, skip the report ID
  836. Uint8 *payload = NULL;
  837. int payload_size = 0;
  838. if (size >= 3 && data[0] == 0x43 && data[1] == GAMESIR_PACKET_HEADER_0 && data[2] == GAMESIR_PACKET_HEADER_1_GAMEPAD) {
  839. payload = data + 3;
  840. payload_size = size - 3;
  841. HIDAPI_DriverGameSir_HandleStatePacket(joystick, ctx, payload, payload_size);
  842. } else if (size >= 2 && data[0] == GAMESIR_PACKET_HEADER_0 && data[1] == GAMESIR_PACKET_HEADER_1_GAMEPAD) {
  843. payload = data + 2;
  844. payload_size = size - 2;
  845. HIDAPI_DriverGameSir_HandleStatePacket(joystick, ctx, payload, payload_size);
  846. } else if (size >= 10 && data[0] == 0x07) {
  847. payload = data + 1;
  848. payload_size = size - 1;
  849. HIDAPI_DriverGameSir_HandleSimpleStatePacketBluetooth(joystick, ctx, payload, payload_size);
  850. } else if (size == 9) {
  851. payload = data;
  852. payload_size = size;
  853. HIDAPI_DriverGameSir_HandleSimpleStatePacketUSB(joystick, ctx, payload, payload_size);
  854. }
  855. }
  856. if (size < 0 && device->num_joysticks > 0) {
  857. // Read error, device is disconnected
  858. HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
  859. }
  860. return (size >= 0);
  861. }
  862. static void HIDAPI_DriverGameSir_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
  863. {
  864. }
  865. static void HIDAPI_DriverGameSir_FreeDevice(SDL_HIDAPI_Device *device)
  866. {
  867. SDL_DriverGamesir_Context *ctx = (SDL_DriverGamesir_Context *)device->context;
  868. if (ctx) {
  869. if (ctx->output_handle) {
  870. SDL_hid_close(ctx->output_handle);
  871. }
  872. SDL_free(ctx);
  873. device->context = NULL;
  874. }
  875. }
  876. SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameSir = {
  877. SDL_HINT_JOYSTICK_HIDAPI_GAMESIR,
  878. true,
  879. HIDAPI_DriverGameSir_RegisterHints,
  880. HIDAPI_DriverGameSir_UnregisterHints,
  881. HIDAPI_DriverGameSir_IsEnabled,
  882. HIDAPI_DriverGameSir_IsSupportedDevice,
  883. HIDAPI_DriverGameSir_InitDevice,
  884. HIDAPI_DriverGameSir_GetDevicePlayerIndex,
  885. HIDAPI_DriverGameSir_SetDevicePlayerIndex,
  886. HIDAPI_DriverGameSir_UpdateDevice,
  887. HIDAPI_DriverGameSir_OpenJoystick,
  888. HIDAPI_DriverGameSir_RumbleJoystick,
  889. HIDAPI_DriverGameSir_RumbleJoystickTriggers,
  890. HIDAPI_DriverGameSir_GetJoystickCapabilities,
  891. HIDAPI_DriverGameSir_SetJoystickLED,
  892. HIDAPI_DriverGameSir_SendJoystickEffect,
  893. HIDAPI_DriverGameSir_SetJoystickSensorsEnabled,
  894. HIDAPI_DriverGameSir_CloseJoystick,
  895. HIDAPI_DriverGameSir_FreeDevice,
  896. };
  897. #endif // SDL_JOYSTICK_HIDAPI_GAMESIR
  898. #endif // SDL_JOYSTICK_HIDAPI