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