Atlas - SDL_hidapi_sinput.c
Home / ext / SDL / src / joystick / hidapi Lines: 1 | Size: 39466 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 2025 Mitchell Cairns <[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_hints_c.h" 26#include "../SDL_sysjoystick.h" 27 28#include "SDL_hidapijoystick_c.h" 29#include "SDL_hidapi_rumble.h" 30#include "SDL_hidapi_sinput.h" 31 32#ifdef SDL_JOYSTICK_HIDAPI_SINPUT 33 34/*****************************************************************************************************/ 35// This protocol is documented at: 36// https://docs.handheldlegend.com/s/sinput 37/*****************************************************************************************************/ 38 39// Define this if you want to log all packets from the controller 40#if 0 41#define DEBUG_SINPUT_PROTOCOL 42#endif 43 44#if 0 45#define DEBUG_SINPUT_INIT 46#endif 47 48#define SINPUT_DEVICE_REPORT_SIZE 64 // Size of input reports (And CMD Input reports) 49#define SINPUT_DEVICE_REPORT_COMMAND_SIZE 48 // Size of command OUTPUT reports 50 51#define SINPUT_DEVICE_REPORT_ID_JOYSTICK_INPUT 0x01 52#define SINPUT_DEVICE_REPORT_ID_INPUT_CMDDAT 0x02 53#define SINPUT_DEVICE_REPORT_ID_OUTPUT_CMDDAT 0x03 54 55#define SINPUT_DEVICE_COMMAND_HAPTIC 0x01 56#define SINPUT_DEVICE_COMMAND_FEATURES 0x02 57#define SINPUT_DEVICE_COMMAND_PLAYERLED 0x03 58#define SINPUT_DEVICE_COMMAND_JOYSTICKRGB 0x04 59 60#define SINPUT_HAPTIC_TYPE_PRECISE 0x01 61#define SINPUT_HAPTIC_TYPE_ERMSIMULATION 0x02 62 63#define SINPUT_DEFAULT_GYRO_SENS 2000 64#define SINPUT_DEFAULT_ACCEL_SENS 8 65 66#define SINPUT_REPORT_IDX_BUTTONS_0 3 67#define SINPUT_REPORT_IDX_BUTTONS_1 4 68#define SINPUT_REPORT_IDX_BUTTONS_2 5 69#define SINPUT_REPORT_IDX_BUTTONS_3 6 70#define SINPUT_REPORT_IDX_LEFT_X 7 71#define SINPUT_REPORT_IDX_LEFT_Y 9 72#define SINPUT_REPORT_IDX_RIGHT_X 11 73#define SINPUT_REPORT_IDX_RIGHT_Y 13 74#define SINPUT_REPORT_IDX_LEFT_TRIGGER 15 75#define SINPUT_REPORT_IDX_RIGHT_TRIGGER 17 76#define SINPUT_REPORT_IDX_IMU_TIMESTAMP 19 77#define SINPUT_REPORT_IDX_IMU_ACCEL_X 23 78#define SINPUT_REPORT_IDX_IMU_ACCEL_Y 25 79#define SINPUT_REPORT_IDX_IMU_ACCEL_Z 27 80#define SINPUT_REPORT_IDX_IMU_GYRO_X 29 81#define SINPUT_REPORT_IDX_IMU_GYRO_Y 31 82#define SINPUT_REPORT_IDX_IMU_GYRO_Z 33 83#define SINPUT_REPORT_IDX_TOUCH1_X 35 84#define SINPUT_REPORT_IDX_TOUCH1_Y 37 85#define SINPUT_REPORT_IDX_TOUCH1_P 39 86#define SINPUT_REPORT_IDX_TOUCH2_X 41 87#define SINPUT_REPORT_IDX_TOUCH2_Y 43 88#define SINPUT_REPORT_IDX_TOUCH2_P 45 89 90#define SINPUT_BUTTON_IDX_EAST 0 91#define SINPUT_BUTTON_IDX_SOUTH 1 92#define SINPUT_BUTTON_IDX_NORTH 2 93#define SINPUT_BUTTON_IDX_WEST 3 94#define SINPUT_BUTTON_IDX_DPAD_UP 4 95#define SINPUT_BUTTON_IDX_DPAD_DOWN 5 96#define SINPUT_BUTTON_IDX_DPAD_LEFT 6 97#define SINPUT_BUTTON_IDX_DPAD_RIGHT 7 98#define SINPUT_BUTTON_IDX_LEFT_STICK 8 99#define SINPUT_BUTTON_IDX_RIGHT_STICK 9 100#define SINPUT_BUTTON_IDX_LEFT_BUMPER 10 101#define SINPUT_BUTTON_IDX_RIGHT_BUMPER 11 102#define SINPUT_BUTTON_IDX_LEFT_TRIGGER 12 103#define SINPUT_BUTTON_IDX_RIGHT_TRIGGER 13 104#define SINPUT_BUTTON_IDX_LEFT_PADDLE1 14 105#define SINPUT_BUTTON_IDX_RIGHT_PADDLE1 15 106#define SINPUT_BUTTON_IDX_START 16 107#define SINPUT_BUTTON_IDX_BACK 17 108#define SINPUT_BUTTON_IDX_GUIDE 18 109#define SINPUT_BUTTON_IDX_CAPTURE 19 110#define SINPUT_BUTTON_IDX_LEFT_PADDLE2 20 111#define SINPUT_BUTTON_IDX_RIGHT_PADDLE2 21 112#define SINPUT_BUTTON_IDX_TOUCHPAD1 22 113#define SINPUT_BUTTON_IDX_TOUCHPAD2 23 114#define SINPUT_BUTTON_IDX_POWER 24 115#define SINPUT_BUTTON_IDX_MISC4 25 116#define SINPUT_BUTTON_IDX_MISC5 26 117#define SINPUT_BUTTON_IDX_MISC6 27 118#define SINPUT_BUTTON_IDX_MISC7 28 119#define SINPUT_BUTTON_IDX_MISC8 29 120#define SINPUT_BUTTON_IDX_MISC9 30 121#define SINPUT_BUTTON_IDX_MISC10 31 122 123#define SINPUT_BUTTONMASK_EAST 0x01 124#define SINPUT_BUTTONMASK_SOUTH 0x02 125#define SINPUT_BUTTONMASK_NORTH 0x04 126#define SINPUT_BUTTONMASK_WEST 0x08 127#define SINPUT_BUTTONMASK_DPAD_UP 0x10 128#define SINPUT_BUTTONMASK_DPAD_DOWN 0x20 129#define SINPUT_BUTTONMASK_DPAD_LEFT 0x40 130#define SINPUT_BUTTONMASK_DPAD_RIGHT 0x80 131#define SINPUT_BUTTONMASK_LEFT_STICK 0x01 132#define SINPUT_BUTTONMASK_RIGHT_STICK 0x02 133#define SINPUT_BUTTONMASK_LEFT_BUMPER 0x04 134#define SINPUT_BUTTONMASK_RIGHT_BUMPER 0x08 135#define SINPUT_BUTTONMASK_LEFT_TRIGGER 0x10 136#define SINPUT_BUTTONMASK_RIGHT_TRIGGER 0x20 137#define SINPUT_BUTTONMASK_LEFT_PADDLE1 0x40 138#define SINPUT_BUTTONMASK_RIGHT_PADDLE1 0x80 139#define SINPUT_BUTTONMASK_START 0x01 140#define SINPUT_BUTTONMASK_BACK 0x02 141#define SINPUT_BUTTONMASK_GUIDE 0x04 142#define SINPUT_BUTTONMASK_CAPTURE 0x08 143#define SINPUT_BUTTONMASK_LEFT_PADDLE2 0x10 144#define SINPUT_BUTTONMASK_RIGHT_PADDLE2 0x20 145#define SINPUT_BUTTONMASK_TOUCHPAD1 0x40 146#define SINPUT_BUTTONMASK_TOUCHPAD2 0x80 147#define SINPUT_BUTTONMASK_POWER 0x01 148#define SINPUT_BUTTONMASK_MISC4 0x02 149#define SINPUT_BUTTONMASK_MISC5 0x04 150#define SINPUT_BUTTONMASK_MISC6 0x08 151#define SINPUT_BUTTONMASK_MISC7 0x10 152#define SINPUT_BUTTONMASK_MISC8 0x20 153#define SINPUT_BUTTONMASK_MISC9 0x40 154#define SINPUT_BUTTONMASK_MISC10 0x80 155 156#define SINPUT_REPORT_IDX_COMMAND_RESPONSE_ID 1 157#define SINPUT_REPORT_IDX_COMMAND_RESPONSE_BULK 2 158 159#define SINPUT_REPORT_IDX_PLUG_STATUS 1 160#define SINPUT_REPORT_IDX_CHARGE_LEVEL 2 161 162#define SINPUT_MAX_ALLOWED_TOUCHPADS 2 163 164#ifndef EXTRACTSINT16 165#define EXTRACTSINT16(data, idx) ((Sint16)((data)[(idx)] | ((data)[(idx) + 1] << 8))) 166#endif 167 168#ifndef EXTRACTUINT16 169#define EXTRACTUINT16(data, idx) ((Uint16)((data)[(idx)] | ((data)[(idx) + 1] << 8))) 170#endif 171 172#ifndef EXTRACTUINT32 173#define EXTRACTUINT32(data, idx) ((Uint32)((data)[(idx)] | ((data)[(idx) + 1] << 8) | ((data)[(idx) + 2] << 16) | ((data)[(idx) + 3] << 24))) 174#endif 175 176typedef struct 177{ 178 uint8_t type; 179 180 union { 181 // Frequency Amplitude pairs 182 struct { 183 struct { 184 uint16_t frequency_1; 185 uint16_t amplitude_1; 186 uint16_t frequency_2; 187 uint16_t amplitude_2; 188 } left; 189 190 struct { 191 uint16_t frequency_1; 192 uint16_t amplitude_1; 193 uint16_t frequency_2; 194 uint16_t amplitude_2; 195 } right; 196 197 } type_1; 198 199 // Basic ERM simulation model 200 struct { 201 struct { 202 uint8_t amplitude; 203 bool brake; 204 } left; 205 206 struct { 207 uint8_t amplitude; 208 bool brake; 209 } right; 210 211 } type_2; 212 }; 213} SINPUT_HAPTIC_S; 214 215typedef struct 216{ 217 SDL_HIDAPI_Device *device; 218 Uint16 protocol_version; 219 Uint16 usb_device_version; 220 bool sensors_enabled; 221 222 Uint8 player_idx; 223 224 bool player_leds_supported; 225 bool joystick_rgb_supported; 226 bool rumble_supported; 227 bool accelerometer_supported; 228 bool gyroscope_supported; 229 bool left_analog_stick_supported; 230 bool right_analog_stick_supported; 231 bool left_analog_trigger_supported; 232 bool right_analog_trigger_supported; 233 bool dpad_supported; 234 bool touchpad_supported; 235 bool is_handheld; 236 237 Uint8 touchpad_count; // 2 touchpads maximum 238 Uint8 touchpad_finger_count; // 2 fingers for one touchpad, or 1 per touchpad (2 max) 239 240 Uint16 polling_rate_us; 241 Uint8 sub_product; // Subtype of the device, 0 in most cases 242 243 Uint16 accelRange; // Example would be 2,4,8,16 +/- (g-force) 244 Uint16 gyroRange; // Example would be 1000,2000,4000 +/- (degrees per second) 245 246 float accelScale; // Scale factor for accelerometer values 247 float gyroScale; // Scale factor for gyroscope values 248 Uint8 last_state[USB_PACKET_LENGTH]; 249 250 Uint8 axes_count; 251 Uint8 buttons_count; 252 Uint8 usage_masks[4]; 253 254 Uint32 last_imu_timestamp_us; 255 256 Uint64 imu_timestamp_ns; // Nanoseconds. We accumulate with received deltas 257} SDL_DriverSInput_Context; 258 259// Converts raw int16_t gyro scale setting 260static inline float CalculateGyroScale(uint16_t dps_range) 261{ 262 return SDL_PI_F / 180.0f / (32768.0f / (float)dps_range); 263} 264 265// Converts raw int16_t accel scale setting 266static inline float CalculateAccelScale(uint16_t g_range) 267{ 268 return SDL_STANDARD_GRAVITY / (32768.0f / (float)g_range); 269} 270 271// This function uses base-n encoding to encode features into the version GUID bytes 272// that properly represents the supported device features 273// This also sets the driver context button mask correctly based on the features 274static void DeviceDynamicEncodingSetup(SDL_HIDAPI_Device *device) 275{ 276 SDL_DriverSInput_Context *ctx = device->context; 277 278 // A new button mask is generated to provide 279 // SDL with a mapping string that is sane. In case of 280 // an unconventional gamepad setup, the closest sane 281 // mapping is provided to the driver. 282 Uint8 mask[4] = { 0 }; 283 284 // For all gamepads, there is a minimum SInput expectation 285 // to have dpad, abxy, and start buttons 286 287 // ABXY + D-Pad 288 mask[0] = 0xFF; 289 ctx->dpad_supported = true; 290 291 // Start button 292 mask[2] |= SINPUT_BUTTONMASK_START; 293 294 // Bumpers 295 bool left_bumper = (ctx->usage_masks[1] & SINPUT_BUTTONMASK_LEFT_BUMPER) != 0; 296 bool right_bumper = (ctx->usage_masks[1] & SINPUT_BUTTONMASK_RIGHT_BUMPER) != 0; 297 298 int bumperStyle = SINPUT_BUMPERSTYLE_NONE; 299 if (left_bumper && right_bumper) { 300 bumperStyle = SINPUT_BUMPERSTYLE_TWO; 301 mask[1] |= (SINPUT_BUTTONMASK_LEFT_BUMPER | SINPUT_BUTTONMASK_RIGHT_BUMPER); 302 } else if (left_bumper || right_bumper) { 303 bumperStyle = SINPUT_BUMPERSTYLE_ONE; 304 305 if (left_bumper) { 306 mask[1] |= SINPUT_BUTTONMASK_LEFT_BUMPER; 307 } else if (right_bumper) { 308 mask[1] |= SINPUT_BUTTONMASK_RIGHT_BUMPER; 309 } 310 } 311 312 // Trigger bits live in mask[1] 313 bool digital_triggers = (ctx->usage_masks[1] & (SINPUT_BUTTONMASK_LEFT_TRIGGER | SINPUT_BUTTONMASK_RIGHT_TRIGGER)) != 0; 314 315 bool analog_triggers = ctx->left_analog_trigger_supported || ctx->right_analog_trigger_supported; 316 317 // Touchpads 318 bool t1 = (ctx->usage_masks[2] & SINPUT_BUTTONMASK_TOUCHPAD1) != 0; 319 bool t2 = (ctx->usage_masks[2] & SINPUT_BUTTONMASK_TOUCHPAD2) != 0; 320 321 int analogStyle = SINPUT_ANALOGSTYLE_NONE; 322 if (ctx->left_analog_stick_supported && ctx->right_analog_stick_supported) { 323 analogStyle = SINPUT_ANALOGSTYLE_LEFTRIGHT; 324 mask[1] |= (SINPUT_BUTTONMASK_LEFT_STICK | SINPUT_BUTTONMASK_RIGHT_STICK); 325 } else if (ctx->left_analog_stick_supported) { 326 analogStyle = SINPUT_ANALOGSTYLE_LEFTONLY; 327 mask[1] |= SINPUT_BUTTONMASK_LEFT_STICK; 328 } else if (ctx->right_analog_stick_supported) { 329 analogStyle = SINPUT_ANALOGSTYLE_RIGHTONLY; 330 mask[1] |= SINPUT_BUTTONMASK_RIGHT_STICK; 331 } 332 333 int triggerStyle = SINPUT_TRIGGERSTYLE_NONE; 334 335 if (analog_triggers && digital_triggers) { 336 // When we have both analog triggers and digital triggers 337 // this is interpreted as having dual-stage triggers 338 triggerStyle = SINPUT_TRIGGERSTYLE_DUALSTAGE; 339 mask[1] |= (SINPUT_BUTTONMASK_LEFT_TRIGGER | SINPUT_BUTTONMASK_RIGHT_TRIGGER); 340 } else if (analog_triggers) { 341 triggerStyle = SINPUT_TRIGGERSTYLE_ANALOG; 342 } else if (digital_triggers) { 343 triggerStyle = SINPUT_TRIGGERSTYLE_DIGITAL; 344 mask[1] |= (SINPUT_BUTTONMASK_LEFT_TRIGGER | SINPUT_BUTTONMASK_RIGHT_TRIGGER); 345 } 346 347 // Paddle bits may touch mask[1] and mask[2] 348 bool pg1 = (ctx->usage_masks[1] & (SINPUT_BUTTONMASK_LEFT_PADDLE1 | SINPUT_BUTTONMASK_RIGHT_PADDLE1)) != 0; 349 bool pg2 = (ctx->usage_masks[2] & (SINPUT_BUTTONMASK_LEFT_PADDLE2 | SINPUT_BUTTONMASK_RIGHT_PADDLE2)) != 0; 350 351 int paddleStyle = SINPUT_PADDLESTYLE_NONE; 352 if (pg1 && pg2) { 353 paddleStyle = SINPUT_PADDLESTYLE_FOUR; 354 mask[1] |= (SINPUT_BUTTONMASK_LEFT_PADDLE1 | SINPUT_BUTTONMASK_RIGHT_PADDLE1); 355 mask[2] |= (SINPUT_BUTTONMASK_LEFT_PADDLE2 | SINPUT_BUTTONMASK_RIGHT_PADDLE2); 356 } else if (pg1) { 357 paddleStyle = SINPUT_PADDLESTYLE_TWO; 358 mask[1] |= (SINPUT_BUTTONMASK_LEFT_PADDLE1 | SINPUT_BUTTONMASK_RIGHT_PADDLE1); 359 } 360 361 362 // Meta Buttons (Back, Guide, Share) 363 bool back = (ctx->usage_masks[2] & SINPUT_BUTTONMASK_BACK) != 0; 364 bool guide = (ctx->usage_masks[2] & SINPUT_BUTTONMASK_GUIDE) != 0; 365 bool share = (ctx->usage_masks[2] & SINPUT_BUTTONMASK_CAPTURE) != 0; 366 367 int metaStyle = SINPUT_METASTYLE_NONE; 368 if (share) { 369 metaStyle = SINPUT_METASTYLE_BACKGUIDESHARE; 370 mask[2] |= (SINPUT_BUTTONMASK_BACK | SINPUT_BUTTONMASK_GUIDE | SINPUT_BUTTONMASK_CAPTURE); 371 } else if (guide) { 372 metaStyle = SINPUT_METASTYLE_BACKGUIDE; 373 mask[2] |= (SINPUT_BUTTONMASK_BACK | SINPUT_BUTTONMASK_GUIDE); 374 } else if (back) { 375 metaStyle = SINPUT_METASTYLE_BACK; 376 mask[2] |= (SINPUT_BUTTONMASK_BACK); 377 } 378 379 int touchStyle = SINPUT_TOUCHSTYLE_NONE; 380 if (t1 && t2) { 381 touchStyle = SINPUT_TOUCHSTYLE_DOUBLE; 382 mask[2] |= (SINPUT_BUTTONMASK_TOUCHPAD1 | SINPUT_BUTTONMASK_TOUCHPAD2); 383 } else if (t1) { 384 touchStyle = SINPUT_TOUCHSTYLE_SINGLE; 385 mask[2] |= SINPUT_BUTTONMASK_TOUCHPAD1; 386 } 387 388 // Misc Buttons 389 int miscStyle = SINPUT_MISCSTYLE_NONE; 390 Uint8 extra_misc = ctx->usage_masks[3] & 0x0F; 391 switch (extra_misc) { 392 case 0x0F: 393 miscStyle = SINPUT_MISCSTYLE_4; 394 mask[3] = 0x0F; 395 break; 396 case 0x07: 397 miscStyle = SINPUT_MISCSTYLE_3; 398 mask[3] = 0x07; 399 break; 400 case 0x03: 401 miscStyle = SINPUT_MISCSTYLE_2; 402 mask[3] = 0x03; 403 break; 404 case 0x01: 405 miscStyle = SINPUT_MISCSTYLE_1; 406 mask[3] = 0x01; 407 break; 408 default: 409 miscStyle = SINPUT_MISCSTYLE_NONE; 410 mask[3] = 0x00; 411 break; 412 } 413 414 int version = analogStyle; 415 version = (version * (int)SINPUT_BUMPERSTYLE_MAX) + bumperStyle; 416 version = (version * (int)SINPUT_TRIGGERSTYLE_MAX) + triggerStyle; 417 version = (version * (int)SINPUT_PADDLESTYLE_MAX) + paddleStyle; 418 version = (version * (int)SINPUT_METASTYLE_MAX) + metaStyle; 419 version = (version * (int)SINPUT_TOUCHSTYLE_MAX) + touchStyle; 420 version = (version * (int)SINPUT_MISCSTYLE_MAX) + miscStyle; 421 422 // Overwrite our button usage masks 423 // with our sanitized masks 424 ctx->usage_masks[0] = mask[0]; 425 ctx->usage_masks[1] = mask[1]; 426 ctx->usage_masks[2] = mask[2]; 427 ctx->usage_masks[3] = mask[3]; 428 429 version = SDL_clamp(version, 0, UINT16_MAX); 430 431 // Overwrite 'Version' field of the GUID data 432 device->guid.data[12] = (Uint8)(version & 0xFF); 433 device->guid.data[13] = (Uint8)(version >> 8); 434} 435 436 437static void ProcessSDLFeaturesResponse(SDL_HIDAPI_Device *device, Uint8 *data) 438{ 439 SDL_DriverSInput_Context *ctx = (SDL_DriverSInput_Context *)device->context; 440 441 // Obtain protocol version 442 ctx->protocol_version = EXTRACTUINT16(data, 0); 443 444 // Bitfields are not portable, so we unpack them into a struct value 445 ctx->rumble_supported = (data[2] & 0x01) != 0; 446 ctx->player_leds_supported = (data[2] & 0x02) != 0; 447 ctx->accelerometer_supported = (data[2] & 0x04) != 0; 448 ctx->gyroscope_supported = (data[2] & 0x08) != 0; 449 450 ctx->left_analog_stick_supported = (data[2] & 0x10) != 0; 451 ctx->right_analog_stick_supported = (data[2] & 0x20) != 0; 452 ctx->left_analog_trigger_supported = (data[2] & 0x40) != 0; 453 ctx->right_analog_trigger_supported = (data[2] & 0x80) != 0; 454 455 ctx->touchpad_supported = (data[3] & 0x01) != 0; 456 ctx->joystick_rgb_supported = (data[3] & 0x02) != 0; 457 458 ctx->is_handheld = (data[3] & 0x04) != 0; 459 460 // The gamepad type represents a style of gamepad that most closely 461 // resembles the gamepad in question (Button style, button layout) 462 SDL_GamepadType type = SDL_GAMEPAD_TYPE_UNKNOWN; 463 type = (SDL_GamepadType)SDL_clamp(data[4], SDL_GAMEPAD_TYPE_UNKNOWN, SDL_GAMEPAD_TYPE_COUNT); 464 device->type = type; 465 466 // The 3 MSB represent a button layout style SDL_GamepadFaceStyle 467 // The 5 LSB represent a device sub-type 468 device->guid.data[15] = data[5]; 469 470 ctx->sub_product = (data[5] & 0x1F); 471 472#if defined(DEBUG_SINPUT_INIT) 473 SDL_Log("SInput Face Style: %d", (data[5] & 0xE0) >> 5); 474 SDL_Log("SInput Sub-product: %d", (data[5] & 0x1F)); 475#endif 476 477 ctx->polling_rate_us = EXTRACTUINT16(data, 6); 478 479#if defined(DEBUG_SINPUT_INIT) 480 SDL_Log("SInput polling interval (microseconds): %d", ctx->polling_rate_us); 481#endif 482 483 ctx->accelRange = EXTRACTUINT16(data, 8); 484 ctx->gyroRange = EXTRACTUINT16(data, 10); 485 486 ctx->usage_masks[0] = data[12]; 487 ctx->usage_masks[1] = data[13]; 488 ctx->usage_masks[2] = data[14]; 489 ctx->usage_masks[3] = data[15]; 490 491 // Get and validate touchpad parameters 492 ctx->touchpad_count = data[16]; 493 ctx->touchpad_finger_count = data[17]; 494 495 // Get device Serial - MAC address 496 char serial[18]; 497 (void)SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", 498 data[18], data[19], data[20], data[21], data[22], data[23]); 499 500#if defined(DEBUG_SINPUT_INIT) 501 SDL_Log("Serial num: %s", serial); 502#endif 503 HIDAPI_SetDeviceSerial(device, serial); 504 505#if defined(DEBUG_SINPUT_INIT) 506 SDL_Log("Accelerometer Range: %d", ctx->accelRange); 507#endif 508 509#if defined(DEBUG_SINPUT_INIT) 510 SDL_Log("Gyro Range: %d", ctx->gyroRange); 511#endif 512 513 ctx->accelScale = CalculateAccelScale(ctx->accelRange); 514 ctx->gyroScale = CalculateGyroScale(ctx->gyroRange); 515 516 Uint8 axes = 0; 517 if (ctx->left_analog_stick_supported) { 518 axes += 2; 519 } 520 521 if (ctx->right_analog_stick_supported) { 522 axes += 2; 523 } 524 525 if (ctx->left_analog_trigger_supported || ctx->right_analog_trigger_supported) { 526 // Always add both analog trigger axes if one is present 527 axes += 2; 528 } 529 530 ctx->axes_count = axes; 531 532 DeviceDynamicEncodingSetup(device); 533 534 // Derive button count from mask 535 for (Uint8 byte = 0; byte < 4; ++byte) { 536 for (Uint8 bit = 0; bit < 8; ++bit) { 537 if ((ctx->usage_masks[byte] & (1 << bit)) != 0) { 538 ++ctx->buttons_count; 539 } 540 } 541 } 542 543 // Convert DPAD to hat 544 const int DPAD_MASK = (1 << SINPUT_BUTTON_IDX_DPAD_UP) | 545 (1 << SINPUT_BUTTON_IDX_DPAD_DOWN) | 546 (1 << SINPUT_BUTTON_IDX_DPAD_LEFT) | 547 (1 << SINPUT_BUTTON_IDX_DPAD_RIGHT); 548 if ((ctx->usage_masks[0] & DPAD_MASK) == DPAD_MASK) { 549 ctx->dpad_supported = true; 550 ctx->usage_masks[0] &= ~DPAD_MASK; 551 ctx->buttons_count -= 4; 552 } 553 554#if defined(DEBUG_SINPUT_INIT) 555 SDL_Log("Buttons count: %d", ctx->buttons_count); 556#endif 557} 558 559static bool RetrieveSDLFeatures(SDL_HIDAPI_Device *device) 560{ 561 int written = 0; 562 563 // Attempt to send the SDL features get command. 564 for (int attempt = 0; attempt < 8; ++attempt) { 565 const Uint8 featuresGetCommand[SINPUT_DEVICE_REPORT_COMMAND_SIZE] = { SINPUT_DEVICE_REPORT_ID_OUTPUT_CMDDAT, SINPUT_DEVICE_COMMAND_FEATURES }; 566 // This write will occasionally return -1, so ignore failure here and try again 567 written = SDL_hid_write(device->dev, featuresGetCommand, sizeof(featuresGetCommand)); 568 569 if (written == SINPUT_DEVICE_REPORT_COMMAND_SIZE) { 570 break; 571 } 572 } 573 574 if (written < SINPUT_DEVICE_REPORT_COMMAND_SIZE) { 575 SDL_SetError("SInput device SDL Features GET command could not write"); 576 return false; 577 } 578 579 int read = 0; 580 581 // Read the reply 582 for (int i = 0; i < 100; ++i) { 583 SDL_Delay(1); 584 585 Uint8 data[USB_PACKET_LENGTH]; 586 read = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0); 587 if (read < 0) { 588 SDL_SetError("SInput device SDL Features GET command could not read"); 589 return false; 590 } 591 if (read == 0) { 592 continue; 593 } 594 595#ifdef DEBUG_SINPUT_PROTOCOL 596 HIDAPI_DumpPacket("SInput packet: size = %d", data, read); 597#endif 598 599 if ((read == USB_PACKET_LENGTH) && (data[0] == SINPUT_DEVICE_REPORT_ID_INPUT_CMDDAT) && (data[1] == SINPUT_DEVICE_COMMAND_FEATURES)) { 600 ProcessSDLFeaturesResponse(device, &(data[SINPUT_REPORT_IDX_COMMAND_RESPONSE_BULK])); 601#if defined(DEBUG_SINPUT_INIT) 602 SDL_Log("Received SInput SDL Features command response"); 603#endif 604 return true; 605 } 606 } 607 608 return false; 609} 610 611// Type 2 haptics are for more traditional rumble such as 612// ERM motors or simulated ERM motors 613static inline void HapticsType2Pack(SINPUT_HAPTIC_S *in, Uint8 *out) 614{ 615 // Type of haptics 616 out[0] = 2; 617 618 out[1] = in->type_2.left.amplitude; 619 out[2] = in->type_2.left.brake; 620 621 out[3] = in->type_2.right.amplitude; 622 out[4] = in->type_2.right.brake; 623} 624 625static void HIDAPI_DriverSInput_RegisterHints(SDL_HintCallback callback, void *userdata) 626{ 627 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SINPUT, callback, userdata); 628} 629 630static void HIDAPI_DriverSInput_UnregisterHints(SDL_HintCallback callback, void *userdata) 631{ 632 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SINPUT, callback, userdata); 633} 634 635static bool HIDAPI_DriverSInput_IsEnabled(void) 636{ 637 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_SINPUT, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT)); 638} 639 640static bool HIDAPI_DriverSInput_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) 641{ 642 return SDL_IsJoystickSInputController(vendor_id, product_id); 643} 644 645static bool HIDAPI_DriverSInput_InitDevice(SDL_HIDAPI_Device *device) 646{ 647#if defined(DEBUG_SINPUT_INIT) 648 SDL_Log("SInput device Init"); 649#endif 650 651 SDL_DriverSInput_Context *ctx = (SDL_DriverSInput_Context *)SDL_calloc(1, sizeof(*ctx)); 652 if (!ctx) { 653 return false; 654 } 655 656 ctx->device = device; 657 device->context = ctx; 658 659 if (!RetrieveSDLFeatures(device)) { 660 return false; 661 } 662 663 // Store the USB Device Version because we will overwrite this data 664 ctx->usb_device_version = device->version; 665 666 switch (device->product_id) { 667 case USB_PRODUCT_HANDHELDLEGEND_GCULTIMATE: 668 HIDAPI_SetDeviceName(device, "HHL GC Ultimate"); 669 break; 670 case USB_PRODUCT_HANDHELDLEGEND_PROGCC: 671 HIDAPI_SetDeviceName(device, "HHL ProGCC"); 672 break; 673 case USB_PRODUCT_VOIDGAMING_PS4FIREBIRD: 674 HIDAPI_SetDeviceName(device, "Void Gaming PS4 FireBird"); 675 break; 676 case USB_PRODUCT_BONZIRICHANNEL_FIREBIRD: 677 HIDAPI_SetDeviceName(device, "Bonziri FireBird"); 678 break; 679 case USB_PRODUCT_HANDHELDLEGEND_SINPUT_GENERIC: 680 default: 681 // Use the USB product name 682 break; 683 } 684 685 return HIDAPI_JoystickConnected(device, NULL); 686} 687 688static int HIDAPI_DriverSInput_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) 689{ 690 return -1; 691} 692 693static void HIDAPI_DriverSInput_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) 694{ 695 SDL_DriverSInput_Context *ctx = (SDL_DriverSInput_Context *)device->context; 696 697 if (ctx->player_leds_supported) { 698 player_index = SDL_clamp(player_index + 1, 0, 255); 699 Uint8 player_num = (Uint8)player_index; 700 701 ctx->player_idx = player_num; 702 703 // Set player number, finalizing the setup 704 Uint8 playerLedCommand[SINPUT_DEVICE_REPORT_COMMAND_SIZE] = { SINPUT_DEVICE_REPORT_ID_OUTPUT_CMDDAT, SINPUT_DEVICE_COMMAND_PLAYERLED, ctx->player_idx }; 705 int playerNumBytesWritten = SDL_hid_write(device->dev, playerLedCommand, SINPUT_DEVICE_REPORT_COMMAND_SIZE); 706 707 if (playerNumBytesWritten < 0) { 708 SDL_SetError("SInput device player led command could not write"); 709 } 710 } 711} 712 713#ifndef DEG2RAD 714#define DEG2RAD(x) ((float)(x) * (float)(SDL_PI_F / 180.f)) 715#endif 716 717 718static bool HIDAPI_DriverSInput_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 719{ 720#if defined(DEBUG_SINPUT_INIT) 721 SDL_Log("SInput device Open"); 722#endif 723 724 SDL_DriverSInput_Context *ctx = (SDL_DriverSInput_Context *)device->context; 725 726 SDL_AssertJoysticksLocked(); 727 728 joystick->nbuttons = ctx->buttons_count; 729 730 SDL_zeroa(ctx->last_state); 731 732 joystick->naxes = ctx->axes_count; 733 734 if (ctx->dpad_supported) { 735 joystick->nhats = 1; 736 } 737 738 if (ctx->gyroscope_supported) { 739 SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 1000000.0f / ctx->polling_rate_us); 740 } 741 742 if (ctx->accelerometer_supported) { 743 SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 1000000.0f / ctx->polling_rate_us); 744 } 745 746 if (ctx->touchpad_supported) { 747 // If touchpad is supported, minimum 1, max is capped 748 ctx->touchpad_count = SDL_clamp(ctx->touchpad_count, 1, SINPUT_MAX_ALLOWED_TOUCHPADS); 749 750 if (ctx->touchpad_count > 1) { 751 // Support two separate touchpads with 1 finger each 752 // or support one touchpad with 2 fingers max 753 ctx->touchpad_finger_count = 1; 754 } 755 756 if (ctx->touchpad_count > 0) { 757 SDL_PrivateJoystickAddTouchpad(joystick, ctx->touchpad_finger_count); 758 } 759 760 if (ctx->touchpad_count > 1) { 761 SDL_PrivateJoystickAddTouchpad(joystick, ctx->touchpad_finger_count); 762 } 763 } 764 765 return true; 766} 767 768static bool HIDAPI_DriverSInput_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 769{ 770 771 SDL_DriverSInput_Context *ctx = (SDL_DriverSInput_Context *)device->context; 772 773 if (ctx->rumble_supported) { 774 SINPUT_HAPTIC_S hapticData = { 0 }; 775 Uint8 hapticReport[SINPUT_DEVICE_REPORT_COMMAND_SIZE] = { SINPUT_DEVICE_REPORT_ID_OUTPUT_CMDDAT, SINPUT_DEVICE_COMMAND_HAPTIC }; 776 777 // Low Frequency = Left 778 // High Frequency = Right 779 hapticData.type_2.left.amplitude = (Uint8) (low_frequency_rumble >> 8); 780 hapticData.type_2.right.amplitude = (Uint8)(high_frequency_rumble >> 8); 781 782 HapticsType2Pack(&hapticData, &(hapticReport[2])); 783 784 SDL_HIDAPI_SendRumble(device, hapticReport, SINPUT_DEVICE_REPORT_COMMAND_SIZE); 785 786 return true; 787 } 788 789 return SDL_Unsupported(); 790} 791 792static bool HIDAPI_DriverSInput_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 793{ 794 return SDL_Unsupported(); 795} 796 797static Uint32 HIDAPI_DriverSInput_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 798{ 799 SDL_DriverSInput_Context *ctx = (SDL_DriverSInput_Context *)device->context; 800 801 Uint32 caps = 0; 802 if (ctx->rumble_supported) { 803 caps |= SDL_JOYSTICK_CAP_RUMBLE; 804 } 805 806 if (ctx->player_leds_supported) { 807 caps |= SDL_JOYSTICK_CAP_PLAYER_LED; 808 } 809 810 if (ctx->joystick_rgb_supported) { 811 caps |= SDL_JOYSTICK_CAP_RGB_LED; 812 } 813 814 return caps; 815} 816 817static bool HIDAPI_DriverSInput_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 818{ 819 SDL_DriverSInput_Context *ctx = (SDL_DriverSInput_Context *)device->context; 820 821 if (ctx->joystick_rgb_supported) { 822 Uint8 joystickRGBCommand[SINPUT_DEVICE_REPORT_COMMAND_SIZE] = { SINPUT_DEVICE_REPORT_ID_OUTPUT_CMDDAT, SINPUT_DEVICE_COMMAND_JOYSTICKRGB, red, green, blue }; 823 int joystickRGBBytesWritten = SDL_hid_write(device->dev, joystickRGBCommand, SINPUT_DEVICE_REPORT_COMMAND_SIZE); 824 825 if (joystickRGBBytesWritten < 0) { 826 SDL_SetError("SInput device joystick rgb command could not write"); 827 return false; 828 } 829 830 return true; 831 } 832 return SDL_Unsupported(); 833} 834 835static bool HIDAPI_DriverSInput_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size) 836{ 837 return SDL_Unsupported(); 838} 839 840static bool HIDAPI_DriverSInput_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled) 841{ 842 SDL_DriverSInput_Context *ctx = (SDL_DriverSInput_Context *)device->context; 843 844 if (ctx->accelerometer_supported || ctx->gyroscope_supported) { 845 ctx->sensors_enabled = enabled; 846 return true; 847 } 848 return SDL_Unsupported(); 849} 850 851static void HIDAPI_DriverSInput_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverSInput_Context *ctx, Uint8 *data, int size) 852{ 853 Sint16 axis = 0; 854 Sint16 accel = 0; 855 Sint16 gyro = 0; 856 Uint64 timestamp = SDL_GetTicksNS(); 857 float imu_values[3] = { 0 }; 858 Uint8 output_idx = 0; 859 860 // Process digital buttons according to the supplied 861 // button mask to create a contiguous button input set 862 for (Uint8 processes = 0; processes < 4; ++processes) { 863 864 Uint8 button_idx = SINPUT_REPORT_IDX_BUTTONS_0 + processes; 865 866 for (Uint8 buttons = 0; buttons < 8; ++buttons) { 867 868 // If a button is enabled by our usage mask 869 const Uint8 mask = (0x01 << buttons); 870 if ((ctx->usage_masks[processes] & mask) != 0) { 871 872 bool down = (data[button_idx] & mask) != 0; 873 874 if ( (output_idx < SDL_GAMEPAD_BUTTON_COUNT) && (ctx->last_state[button_idx] != data[button_idx]) ) { 875 SDL_SendJoystickButton(timestamp, joystick, output_idx, down); 876 } 877 878 ++output_idx; 879 } 880 } 881 } 882 883 if (ctx->dpad_supported) { 884 Uint8 hat = SDL_HAT_CENTERED; 885 886 if (data[SINPUT_REPORT_IDX_BUTTONS_0] & (1 << SINPUT_BUTTON_IDX_DPAD_UP)) { 887 hat |= SDL_HAT_UP; 888 } 889 if (data[SINPUT_REPORT_IDX_BUTTONS_0] & (1 << SINPUT_BUTTON_IDX_DPAD_DOWN)) { 890 hat |= SDL_HAT_DOWN; 891 } 892 if (data[SINPUT_REPORT_IDX_BUTTONS_0] & (1 << SINPUT_BUTTON_IDX_DPAD_LEFT)) { 893 hat |= SDL_HAT_LEFT; 894 } 895 if (data[SINPUT_REPORT_IDX_BUTTONS_0] & (1 << SINPUT_BUTTON_IDX_DPAD_RIGHT)) { 896 hat |= SDL_HAT_RIGHT; 897 } 898 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 899 } 900 901 // Analog inputs map to a signed Sint16 range of -32768 to 32767 from the device. 902 // Use an axis index because not all gamepads will have the same axis inputs. 903 Uint8 axis_idx = 0; 904 905 // Left Analog Stick 906 axis = 0; // Reset axis value for joystick 907 if (ctx->left_analog_stick_supported) { 908 axis = EXTRACTSINT16(data, SINPUT_REPORT_IDX_LEFT_X); 909 SDL_SendJoystickAxis(timestamp, joystick, axis_idx, axis); 910 ++axis_idx; 911 912 axis = EXTRACTSINT16(data, SINPUT_REPORT_IDX_LEFT_Y); 913 SDL_SendJoystickAxis(timestamp, joystick, axis_idx, axis); 914 ++axis_idx; 915 } 916 917 // Right Analog Stick 918 axis = 0; // Reset axis value for joystick 919 if (ctx->right_analog_stick_supported) { 920 axis = EXTRACTSINT16(data, SINPUT_REPORT_IDX_RIGHT_X); 921 SDL_SendJoystickAxis(timestamp, joystick, axis_idx, axis); 922 ++axis_idx; 923 924 axis = EXTRACTSINT16(data, SINPUT_REPORT_IDX_RIGHT_Y); 925 SDL_SendJoystickAxis(timestamp, joystick, axis_idx, axis); 926 ++axis_idx; 927 } 928 929 // Left Analog Trigger 930 axis = SDL_MIN_SINT16; // Reset axis value for trigger 931 if (ctx->left_analog_trigger_supported) { 932 axis = EXTRACTSINT16(data, SINPUT_REPORT_IDX_LEFT_TRIGGER); 933 SDL_SendJoystickAxis(timestamp, joystick, axis_idx, axis); 934 ++axis_idx; 935 } 936 937 // Right Analog Trigger 938 axis = SDL_MIN_SINT16; // Reset axis value for trigger 939 if (ctx->right_analog_trigger_supported) { 940 axis = EXTRACTSINT16(data, SINPUT_REPORT_IDX_RIGHT_TRIGGER); 941 SDL_SendJoystickAxis(timestamp, joystick, axis_idx, axis); 942 } 943 944 // Battery/Power state handling 945 if (ctx->last_state[SINPUT_REPORT_IDX_PLUG_STATUS] != data[SINPUT_REPORT_IDX_PLUG_STATUS] || 946 ctx->last_state[SINPUT_REPORT_IDX_CHARGE_LEVEL] != data[SINPUT_REPORT_IDX_CHARGE_LEVEL]) { 947 948 SDL_PowerState state = SDL_POWERSTATE_UNKNOWN; 949 Uint8 status = data[SINPUT_REPORT_IDX_PLUG_STATUS]; 950 int percent = data[SINPUT_REPORT_IDX_CHARGE_LEVEL]; 951 952 percent = SDL_clamp(percent, 0, 100); // Ensure percent is within valid range 953 954 switch (status) { 955 case 1: 956 state = SDL_POWERSTATE_NO_BATTERY; 957 percent = 0; 958 break; 959 case 2: 960 state = SDL_POWERSTATE_CHARGING; 961 break; 962 case 3: 963 state = SDL_POWERSTATE_CHARGED; 964 percent = 100; 965 break; 966 case 4: 967 state = SDL_POWERSTATE_ON_BATTERY; 968 break; 969 default: 970 break; 971 } 972 973 if (state != SDL_POWERSTATE_UNKNOWN) { 974 SDL_SendJoystickPowerInfo(joystick, state, percent); 975 } 976 } 977 978 // Extract the IMU timestamp delta (in microseconds) 979 Uint32 imu_timestamp_us = EXTRACTUINT32(data, SINPUT_REPORT_IDX_IMU_TIMESTAMP); 980 Uint32 imu_time_delta_us = 0; 981 982 // Check if we should process IMU data and if sensors are enabled 983 if (ctx->sensors_enabled) { 984 985 if (imu_timestamp_us >= ctx->last_imu_timestamp_us) { 986 imu_time_delta_us = (imu_timestamp_us - ctx->last_imu_timestamp_us); 987 } else { 988 // Handle rollover case 989 imu_time_delta_us = (UINT32_MAX - ctx->last_imu_timestamp_us) + imu_timestamp_us + 1; 990 } 991 992 // Convert delta to nanoseconds and update running timestamp 993 ctx->imu_timestamp_ns += (Uint64)imu_time_delta_us * 1000; 994 995 // Update last timestamp 996 ctx->last_imu_timestamp_us = imu_timestamp_us; 997 998 // Process Gyroscope 999 if (ctx->gyroscope_supported) { 1000 1001 gyro = EXTRACTSINT16(data, SINPUT_REPORT_IDX_IMU_GYRO_Y); 1002 imu_values[2] = -(float)gyro * ctx->gyroScale; // Y-axis rotation 1003 1004 gyro = EXTRACTSINT16(data, SINPUT_REPORT_IDX_IMU_GYRO_Z); 1005 imu_values[1] = (float)gyro * ctx->gyroScale; // Z-axis rotation 1006 1007 gyro = EXTRACTSINT16(data, SINPUT_REPORT_IDX_IMU_GYRO_X); 1008 imu_values[0] = -(float)gyro * ctx->gyroScale; // X-axis rotation 1009 1010 SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, ctx->imu_timestamp_ns, imu_values, 3); 1011 } 1012 1013 // Process Accelerometer 1014 if (ctx->accelerometer_supported) { 1015 1016 accel = EXTRACTSINT16(data, SINPUT_REPORT_IDX_IMU_ACCEL_Y); 1017 imu_values[2] = -(float)accel * ctx->accelScale; // Y-axis acceleration 1018 1019 accel = EXTRACTSINT16(data, SINPUT_REPORT_IDX_IMU_ACCEL_Z); 1020 imu_values[1] = (float)accel * ctx->accelScale; // Z-axis acceleration 1021 1022 accel = EXTRACTSINT16(data, SINPUT_REPORT_IDX_IMU_ACCEL_X); 1023 imu_values[0] = -(float)accel * ctx->accelScale; // X-axis acceleration 1024 1025 SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, ctx->imu_timestamp_ns, imu_values, 3); 1026 } 1027 } 1028 1029 // Check if we should process touchpad 1030 if (ctx->touchpad_supported && ctx->touchpad_count > 0) { 1031 Uint8 touchpad = 0; 1032 Uint8 finger = 0; 1033 1034 Sint16 touch1X = EXTRACTSINT16(data, SINPUT_REPORT_IDX_TOUCH1_X); 1035 Sint16 touch1Y = EXTRACTSINT16(data, SINPUT_REPORT_IDX_TOUCH1_Y); 1036 Uint16 touch1P = EXTRACTUINT16(data, SINPUT_REPORT_IDX_TOUCH1_P); 1037 1038 Sint16 touch2X = EXTRACTSINT16(data, SINPUT_REPORT_IDX_TOUCH2_X); 1039 Sint16 touch2Y = EXTRACTSINT16(data, SINPUT_REPORT_IDX_TOUCH2_Y); 1040 Uint16 touch2P = EXTRACTUINT16(data, SINPUT_REPORT_IDX_TOUCH2_P); 1041 1042 SDL_SendJoystickTouchpad(timestamp, joystick, touchpad, finger, 1043 touch1P > 0, 1044 touch1X / 65536.0f + 0.5f, 1045 touch1Y / 65536.0f + 0.5f, 1046 touch1P / 32768.0f); 1047 1048 if (ctx->touchpad_count > 1) { 1049 ++touchpad; 1050 } else if (ctx->touchpad_finger_count > 1) { 1051 ++finger; 1052 } 1053 1054 if ((touchpad > 0) || (finger > 0)) { 1055 SDL_SendJoystickTouchpad(timestamp, joystick, touchpad, finger, 1056 touch2P > 0, 1057 touch2X / 65536.0f + 0.5f, 1058 touch2Y / 65536.0f + 0.5f, 1059 touch2P / 32768.0f); 1060 } 1061 } 1062 1063 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); 1064} 1065 1066static bool HIDAPI_DriverSInput_UpdateDevice(SDL_HIDAPI_Device *device) 1067{ 1068 SDL_DriverSInput_Context *ctx = (SDL_DriverSInput_Context *)device->context; 1069 SDL_Joystick *joystick = NULL; 1070 Uint8 data[USB_PACKET_LENGTH]; 1071 int size = 0; 1072 1073 if (device->num_joysticks > 0) { 1074 joystick = SDL_GetJoystickFromID(device->joysticks[0]); 1075 } else { 1076 return false; 1077 } 1078 1079 while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { 1080#ifdef DEBUG_SINPUT_PROTOCOL 1081 HIDAPI_DumpPacket("SInput packet: size = %d", data, size); 1082#endif 1083 if (!joystick) { 1084 continue; 1085 } 1086 1087 if (data[0] == SINPUT_DEVICE_REPORT_ID_JOYSTICK_INPUT) { 1088 HIDAPI_DriverSInput_HandleStatePacket(joystick, ctx, data, size); 1089 } 1090 } 1091 1092 if (size < 0) { 1093 // Read error, device is disconnected 1094 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 1095 } 1096 return (size >= 0); 1097} 1098 1099static void HIDAPI_DriverSInput_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 1100{ 1101} 1102 1103static void HIDAPI_DriverSInput_FreeDevice(SDL_HIDAPI_Device *device) 1104{ 1105} 1106 1107SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSInput = { 1108 SDL_HINT_JOYSTICK_HIDAPI_SINPUT, 1109 true, 1110 HIDAPI_DriverSInput_RegisterHints, 1111 HIDAPI_DriverSInput_UnregisterHints, 1112 HIDAPI_DriverSInput_IsEnabled, 1113 HIDAPI_DriverSInput_IsSupportedDevice, 1114 HIDAPI_DriverSInput_InitDevice, 1115 HIDAPI_DriverSInput_GetDevicePlayerIndex, 1116 HIDAPI_DriverSInput_SetDevicePlayerIndex, 1117 HIDAPI_DriverSInput_UpdateDevice, 1118 HIDAPI_DriverSInput_OpenJoystick, 1119 HIDAPI_DriverSInput_RumbleJoystick, 1120 HIDAPI_DriverSInput_RumbleJoystickTriggers, 1121 HIDAPI_DriverSInput_GetJoystickCapabilities, 1122 HIDAPI_DriverSInput_SetJoystickLED, 1123 HIDAPI_DriverSInput_SendJoystickEffect, 1124 HIDAPI_DriverSInput_SetJoystickSensorsEnabled, 1125 HIDAPI_DriverSInput_CloseJoystick, 1126 HIDAPI_DriverSInput_FreeDevice, 1127}; 1128 1129#endif // SDL_JOYSTICK_HIDAPI_SINPUT 1130 1131#endif // SDL_JOYSTICK_HIDAPI 1132[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.