Atlas - SDL_hidapi_gamesir.c

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