Atlas - SDL_hidapi_switch.c

Home / ext / SDL / src / joystick / hidapi Lines: 1 | Size: 129342 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/* This driver supports the Nintendo Switch Pro controller. 22 Code and logic contributed by Valve Corporation under the SDL zlib license. 23*/ 24#include "SDL_internal.h" 25 26#ifdef SDL_JOYSTICK_HIDAPI 27 28#include "../../SDL_hints_c.h" 29#include "../SDL_sysjoystick.h" 30#include "SDL_hidapijoystick_c.h" 31#include "SDL_hidapi_rumble.h" 32#include "SDL_hidapi_nintendo.h" 33 34#ifdef SDL_JOYSTICK_HIDAPI_SWITCH 35 36// Define this if you want to log all packets from the controller 37#if 0 38#define DEBUG_SWITCH_PROTOCOL 39#endif 40 41// Define this to get log output for rumble logic 42// #define DEBUG_RUMBLE 43 44/* The initialization sequence doesn't appear to work correctly on Windows unless 45 the reads and writes are on the same thread. 46 47 ... and now I can't reproduce this, so I'm leaving it in, but disabled for now. 48 */ 49// #define SWITCH_SYNCHRONOUS_WRITES 50 51/* How often you can write rumble commands to the controller. 52 If you send commands more frequently than this, you can turn off the controller 53 in Bluetooth mode, or the motors can miss the command in USB mode. 54 */ 55#define RUMBLE_WRITE_FREQUENCY_MS 30 56 57// How often you have to refresh a long duration rumble to keep the motors running 58#define RUMBLE_REFRESH_FREQUENCY_MS 50 59 60#define SWITCH_GYRO_SCALE 14.2842f 61#define SWITCH_ACCEL_SCALE 4096.f 62 63#define SWITCH_GYRO_SCALE_MULT 936.0f 64#define SWITCH_ACCEL_SCALE_MULT 4.0f 65 66enum 67{ 68 SDL_GAMEPAD_BUTTON_SWITCH_SHARE = 11, 69 SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, 70 SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, 71 SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, 72 SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, 73 SDL_GAMEPAD_NUM_SWITCH_BUTTONS, 74}; 75 76typedef enum 77{ 78 k_eSwitchInputReportIDs_SubcommandReply = 0x21, 79 k_eSwitchInputReportIDs_FullControllerState = 0x30, 80 k_eSwitchInputReportIDs_FullControllerAndMcuState = 0x31, 81 k_eSwitchInputReportIDs_SimpleControllerState = 0x3F, 82 k_eSwitchInputReportIDs_CommandAck = 0x81, 83} ESwitchInputReportIDs; 84 85typedef enum 86{ 87 k_eSwitchOutputReportIDs_RumbleAndSubcommand = 0x01, 88 k_eSwitchOutputReportIDs_Rumble = 0x10, 89 k_eSwitchOutputReportIDs_Proprietary = 0x80, 90} ESwitchOutputReportIDs; 91 92typedef enum 93{ 94 k_eSwitchSubcommandIDs_BluetoothManualPair = 0x01, 95 k_eSwitchSubcommandIDs_RequestDeviceInfo = 0x02, 96 k_eSwitchSubcommandIDs_SetInputReportMode = 0x03, 97 k_eSwitchSubcommandIDs_SetHCIState = 0x06, 98 k_eSwitchSubcommandIDs_SPIFlashRead = 0x10, 99 k_eSwitchSubcommandIDs_SetPlayerLights = 0x30, 100 k_eSwitchSubcommandIDs_SetHomeLight = 0x38, 101 k_eSwitchSubcommandIDs_EnableIMU = 0x40, 102 k_eSwitchSubcommandIDs_SetIMUSensitivity = 0x41, 103 k_eSwitchSubcommandIDs_EnableVibration = 0x48, 104} ESwitchSubcommandIDs; 105 106typedef enum 107{ 108 k_eSwitchProprietaryCommandIDs_Status = 0x01, 109 k_eSwitchProprietaryCommandIDs_Handshake = 0x02, 110 k_eSwitchProprietaryCommandIDs_HighSpeed = 0x03, 111 k_eSwitchProprietaryCommandIDs_ForceUSB = 0x04, 112 k_eSwitchProprietaryCommandIDs_ClearUSB = 0x05, 113 k_eSwitchProprietaryCommandIDs_ResetMCU = 0x06, 114} ESwitchProprietaryCommandIDs; 115 116#define k_unSwitchOutputPacketDataLength 49 117#define k_unSwitchMaxOutputPacketLength 64 118#define k_unSwitchBluetoothPacketLength k_unSwitchOutputPacketDataLength 119#define k_unSwitchUSBPacketLength k_unSwitchMaxOutputPacketLength 120 121#define k_unSPIStickFactoryCalibrationStartOffset 0x603D 122#define k_unSPIStickFactoryCalibrationEndOffset 0x604E 123#define k_unSPIStickFactoryCalibrationLength (k_unSPIStickFactoryCalibrationEndOffset - k_unSPIStickFactoryCalibrationStartOffset + 1) 124 125#define k_unSPIStickUserCalibrationStartOffset 0x8010 126#define k_unSPIStickUserCalibrationEndOffset 0x8025 127#define k_unSPIStickUserCalibrationLength (k_unSPIStickUserCalibrationEndOffset - k_unSPIStickUserCalibrationStartOffset + 1) 128 129#define k_unSPIIMUScaleStartOffset 0x6020 130#define k_unSPIIMUScaleEndOffset 0x6037 131#define k_unSPIIMUScaleLength (k_unSPIIMUScaleEndOffset - k_unSPIIMUScaleStartOffset + 1) 132 133#define k_unSPIIMUUserScaleStartOffset 0x8026 134#define k_unSPIIMUUserScaleEndOffset 0x8039 135#define k_unSPIIMUUserScaleLength (k_unSPIIMUUserScaleEndOffset - k_unSPIIMUUserScaleStartOffset + 1) 136 137#pragma pack(1) 138typedef struct 139{ 140 Uint8 rgucButtons[2]; 141 Uint8 ucStickHat; 142 Uint8 rgucJoystickLeft[2]; 143 Uint8 rgucJoystickRight[2]; 144} SwitchInputOnlyControllerStatePacket_t; 145 146typedef struct 147{ 148 Uint8 rgucButtons[2]; 149 Uint8 ucStickHat; 150 Sint16 sJoystickLeft[2]; 151 Sint16 sJoystickRight[2]; 152} SwitchSimpleStatePacket_t; 153 154typedef struct 155{ 156 Uint8 ucCounter; 157 Uint8 ucBatteryAndConnection; 158 Uint8 rgucButtons[3]; 159 Uint8 rgucJoystickLeft[3]; 160 Uint8 rgucJoystickRight[3]; 161 Uint8 ucVibrationCode; 162} SwitchControllerStatePacket_t; 163 164typedef struct 165{ 166 Sint16 sAccelX; 167 Sint16 sAccelY; 168 Sint16 sAccelZ; 169 170 Sint16 sGyroX; 171 Sint16 sGyroY; 172 Sint16 sGyroZ; 173} SwitchControllerIMUState_t; 174 175typedef struct 176{ 177 SwitchControllerStatePacket_t controllerState; 178 SwitchControllerIMUState_t imuState[3]; 179} SwitchStatePacket_t; 180 181typedef struct 182{ 183 Uint32 unAddress; 184 Uint8 ucLength; 185} SwitchSPIOpData_t; 186 187typedef struct 188{ 189 SwitchControllerStatePacket_t m_controllerState; 190 191 Uint8 ucSubcommandAck; 192 Uint8 ucSubcommandID; 193 194#define k_unSubcommandDataBytes 35 195 union 196 { 197 Uint8 rgucSubcommandData[k_unSubcommandDataBytes]; 198 199 struct 200 { 201 SwitchSPIOpData_t opData; 202 Uint8 rgucReadData[k_unSubcommandDataBytes - sizeof(SwitchSPIOpData_t)]; 203 } spiReadData; 204 205 struct 206 { 207 Uint8 rgucFirmwareVersion[2]; 208 Uint8 ucDeviceType; 209 Uint8 ucFiller1; 210 Uint8 rgucMACAddress[6]; 211 Uint8 ucFiller2; 212 Uint8 ucColorLocation; 213 } deviceInfo; 214 215 struct 216 { 217 SwitchSPIOpData_t opData; 218 Uint8 rgucLeftCalibration[9]; 219 Uint8 rgucRightCalibration[9]; 220 } stickFactoryCalibration; 221 222 struct 223 { 224 SwitchSPIOpData_t opData; 225 Uint8 rgucLeftMagic[2]; 226 Uint8 rgucLeftCalibration[9]; 227 Uint8 rgucRightMagic[2]; 228 Uint8 rgucRightCalibration[9]; 229 } stickUserCalibration; 230 }; 231} SwitchSubcommandInputPacket_t; 232 233typedef struct 234{ 235 Uint8 ucPacketType; 236 Uint8 ucCommandID; 237 Uint8 ucFiller; 238 239 Uint8 ucDeviceType; 240 Uint8 rgucMACAddress[6]; 241} SwitchProprietaryStatusPacket_t; 242 243typedef struct 244{ 245 Uint8 rgucData[4]; 246} SwitchRumbleData_t; 247 248typedef struct 249{ 250 Uint8 ucPacketType; 251 Uint8 ucPacketNumber; 252 SwitchRumbleData_t rumbleData[2]; 253} SwitchCommonOutputPacket_t; 254 255typedef struct 256{ 257 SwitchCommonOutputPacket_t commonData; 258 259 Uint8 ucSubcommandID; 260 Uint8 rgucSubcommandData[k_unSwitchOutputPacketDataLength - sizeof(SwitchCommonOutputPacket_t) - 1]; 261} SwitchSubcommandOutputPacket_t; 262 263typedef struct 264{ 265 Uint8 ucPacketType; 266 Uint8 ucProprietaryID; 267 268 Uint8 rgucProprietaryData[k_unSwitchOutputPacketDataLength - 1 - 1]; 269} SwitchProprietaryOutputPacket_t; 270#pragma pack() 271 272/* Enhanced report hint mode: 273 * "0": enhanced features are never used 274 * "1": enhanced features are always used 275 * "auto": enhanced features are advertised to the application, but SDL doesn't touch the controller state unless the application explicitly requests it. 276 */ 277typedef enum 278{ 279 SWITCH_ENHANCED_REPORT_HINT_OFF, 280 SWITCH_ENHANCED_REPORT_HINT_ON, 281 SWITCH_ENHANCED_REPORT_HINT_AUTO 282} HIDAPI_Switch_EnhancedReportHint; 283 284typedef struct 285{ 286 SDL_HIDAPI_Device *device; 287 SDL_Joystick *joystick; 288 bool m_bInputOnly; 289 bool m_bUseButtonLabels; 290 bool m_bPlayerLights; 291 int m_nPlayerIndex; 292 bool m_bSyncWrite; 293 int m_nMaxWriteAttempts; 294 ESwitchDeviceInfoControllerType m_eControllerType; 295 Uint8 m_nInitialInputMode; 296 Uint8 m_nCurrentInputMode; 297 Uint8 m_rgucMACAddress[6]; 298 Uint8 m_nCommandNumber; 299 HIDAPI_Switch_EnhancedReportHint m_eEnhancedReportHint; 300 bool m_bEnhancedMode; 301 bool m_bEnhancedModeAvailable; 302 SwitchCommonOutputPacket_t m_RumblePacket; 303 Uint8 m_rgucReadBuffer[k_unSwitchMaxOutputPacketLength]; 304 bool m_bRumbleActive; 305 Uint64 m_ulRumbleSent; 306 bool m_bRumblePending; 307 bool m_bRumbleZeroPending; 308 Uint32 m_unRumblePending; 309 bool m_bSensorsSupported; 310 bool m_bReportSensors; 311 bool m_bHasSensorData; 312 Uint64 m_ulLastInput; 313 Uint64 m_ulLastIMUReset; 314 Uint64 m_ulIMUSampleTimestampNS; 315 Uint32 m_unIMUSamples; 316 Uint64 m_ulIMUUpdateIntervalNS; 317 Uint64 m_ulTimestampNS; 318 bool m_bVerticalMode; 319 SDL_PowerState m_ePowerState; 320 int m_nPowerPercent; 321 322 SwitchInputOnlyControllerStatePacket_t m_lastInputOnlyState; 323 SwitchSimpleStatePacket_t m_lastSimpleState; 324 SwitchStatePacket_t m_lastFullState; 325 326 struct StickCalibrationData 327 { 328 struct 329 { 330 Sint16 sCenter; 331 Sint16 sMin; 332 Sint16 sMax; 333 } axis[2]; 334 } m_StickCalData[2]; 335 336 struct StickExtents 337 { 338 struct 339 { 340 Sint16 sMin; 341 Sint16 sMax; 342 } axis[2]; 343 } m_StickExtents[2], m_SimpleStickExtents[2]; 344 345 struct IMUScaleData 346 { 347 float fAccelScaleX; 348 float fAccelScaleY; 349 float fAccelScaleZ; 350 351 float fGyroScaleX; 352 float fGyroScaleY; 353 float fGyroScaleZ; 354 } m_IMUScaleData; 355} SDL_DriverSwitch_Context; 356 357static int ReadInput(SDL_DriverSwitch_Context *ctx) 358{ 359 int result; 360 361 // Make sure we don't try to read at the same time a write is happening 362 if (SDL_GetAtomicInt(&ctx->device->rumble_pending) > 0) { 363 return 0; 364 } 365 366 result = SDL_hid_read_timeout(ctx->device->dev, ctx->m_rgucReadBuffer, sizeof(ctx->m_rgucReadBuffer), 0); 367 368 // See if we can guess the initial input mode 369 if (result > 0 && !ctx->m_bInputOnly && !ctx->m_nInitialInputMode) { 370 switch (ctx->m_rgucReadBuffer[0]) { 371 case k_eSwitchInputReportIDs_FullControllerState: 372 case k_eSwitchInputReportIDs_FullControllerAndMcuState: 373 case k_eSwitchInputReportIDs_SimpleControllerState: 374 ctx->m_nInitialInputMode = ctx->m_rgucReadBuffer[0]; 375 break; 376 default: 377 break; 378 } 379 } 380 return result; 381} 382 383static int WriteOutput(SDL_DriverSwitch_Context *ctx, const Uint8 *data, int size) 384{ 385#ifdef SWITCH_SYNCHRONOUS_WRITES 386 return SDL_hid_write(ctx->device->dev, data, size); 387#else 388 // Use the rumble thread for general asynchronous writes 389 if (!SDL_HIDAPI_LockRumble()) { 390 return -1; 391 } 392 return SDL_HIDAPI_SendRumbleAndUnlock(ctx->device, data, size); 393#endif // SWITCH_SYNCHRONOUS_WRITES 394} 395 396static SwitchSubcommandInputPacket_t *ReadSubcommandReply(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs expectedID, const Uint8 *pBuf, Uint8 ucLen) 397{ 398 // Average response time for messages is ~30ms 399 Uint64 endTicks = SDL_GetTicks() + 100; 400 401 int nRead = 0; 402 while ((nRead = ReadInput(ctx)) != -1) { 403 if (nRead > 0) { 404 if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) { 405 SwitchSubcommandInputPacket_t *reply = (SwitchSubcommandInputPacket_t *)&ctx->m_rgucReadBuffer[1]; 406 if (reply->ucSubcommandID != expectedID || !(reply->ucSubcommandAck & 0x80)) { 407 continue; 408 } 409 if (reply->ucSubcommandID == k_eSwitchSubcommandIDs_SPIFlashRead) { 410 SDL_assert(ucLen == sizeof(reply->spiReadData.opData)); 411 if (SDL_memcmp(&reply->spiReadData.opData, pBuf, ucLen) != 0) { 412 // This was a reply for another SPI read command 413 continue; 414 } 415 } 416 return reply; 417 } 418 } else { 419 SDL_Delay(1); 420 } 421 422 if (SDL_GetTicks() >= endTicks) { 423 break; 424 } 425 } 426 return NULL; 427} 428 429static bool ReadProprietaryReply(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs expectedID) 430{ 431 // Average response time for messages is ~30ms 432 Uint64 endTicks = SDL_GetTicks() + 100; 433 434 int nRead = 0; 435 while ((nRead = ReadInput(ctx)) != -1) { 436 if (nRead > 0) { 437 if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_CommandAck && ctx->m_rgucReadBuffer[1] == expectedID) { 438 return true; 439 } 440 } else { 441 SDL_Delay(1); 442 } 443 444 if (SDL_GetTicks() >= endTicks) { 445 break; 446 } 447 } 448 return false; 449} 450 451static void ConstructSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, const Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandOutputPacket_t *outPacket) 452{ 453 SDL_zerop(outPacket); 454 455 outPacket->commonData.ucPacketType = k_eSwitchOutputReportIDs_RumbleAndSubcommand; 456 outPacket->commonData.ucPacketNumber = ctx->m_nCommandNumber; 457 458 SDL_memcpy(outPacket->commonData.rumbleData, ctx->m_RumblePacket.rumbleData, sizeof(ctx->m_RumblePacket.rumbleData)); 459 460 outPacket->ucSubcommandID = ucCommandID; 461 if (pBuf) { 462 SDL_memcpy(outPacket->rgucSubcommandData, pBuf, ucLen); 463 } 464 465 ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF; 466} 467 468static bool WritePacket(SDL_DriverSwitch_Context *ctx, void *pBuf, Uint8 ucLen) 469{ 470 Uint8 rgucBuf[k_unSwitchMaxOutputPacketLength]; 471 const size_t unWriteSize = ctx->device->is_bluetooth ? k_unSwitchBluetoothPacketLength : k_unSwitchUSBPacketLength; 472 473 if (ucLen > k_unSwitchOutputPacketDataLength) { 474 return false; 475 } 476 477 if (ucLen < unWriteSize) { 478 SDL_memcpy(rgucBuf, pBuf, ucLen); 479 SDL_memset(rgucBuf + ucLen, 0, unWriteSize - ucLen); 480 pBuf = rgucBuf; 481 ucLen = (Uint8)unWriteSize; 482 } 483 if (ctx->m_bSyncWrite) { 484 return SDL_hid_write(ctx->device->dev, (Uint8 *)pBuf, ucLen) >= 0; 485 } else { 486 return WriteOutput(ctx, (Uint8 *)pBuf, ucLen) >= 0; 487 } 488} 489 490static bool WriteSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, const Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandInputPacket_t **ppReply) 491{ 492 SwitchSubcommandInputPacket_t *reply = NULL; 493 int nTries; 494 495 for (nTries = 1; !reply && nTries <= ctx->m_nMaxWriteAttempts; ++nTries) { 496 SwitchSubcommandOutputPacket_t commandPacket; 497 ConstructSubcommand(ctx, ucCommandID, pBuf, ucLen, &commandPacket); 498 499 if (!WritePacket(ctx, &commandPacket, sizeof(commandPacket))) { 500 continue; 501 } 502 503 reply = ReadSubcommandReply(ctx, ucCommandID, pBuf, ucLen); 504 } 505 506 if (ppReply) { 507 *ppReply = reply; 508 } 509 return reply != NULL; 510} 511 512static bool WriteProprietary(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs ucCommand, Uint8 *pBuf, Uint8 ucLen, bool waitForReply) 513{ 514 int nTries; 515 516 for (nTries = 1; nTries <= ctx->m_nMaxWriteAttempts; ++nTries) { 517 SwitchProprietaryOutputPacket_t packet; 518 519 if ((!pBuf && ucLen > 0) || ucLen > sizeof(packet.rgucProprietaryData)) { 520 return false; 521 } 522 523 SDL_zero(packet); 524 packet.ucPacketType = k_eSwitchOutputReportIDs_Proprietary; 525 packet.ucProprietaryID = ucCommand; 526 if (pBuf) { 527 SDL_memcpy(packet.rgucProprietaryData, pBuf, ucLen); 528 } 529 530 if (!WritePacket(ctx, &packet, sizeof(packet))) { 531 continue; 532 } 533 534 if (!waitForReply || ReadProprietaryReply(ctx, ucCommand)) { 535 // SDL_Log("Succeeded%s after %d tries", ctx->m_bSyncWrite ? " (sync)" : "", nTries); 536 return true; 537 } 538 } 539 // SDL_Log("Failed%s after %d tries", ctx->m_bSyncWrite ? " (sync)" : "", nTries); 540 return false; 541} 542 543static Uint8 EncodeRumbleHighAmplitude(Uint16 amplitude) 544{ 545 /* More information about these values can be found here: 546 * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md 547 */ 548 Uint16 hfa[101][2] = { { 0, 0x0 }, { 514, 0x2 }, { 775, 0x4 }, { 921, 0x6 }, { 1096, 0x8 }, { 1303, 0x0a }, { 1550, 0x0c }, { 1843, 0x0e }, { 2192, 0x10 }, { 2606, 0x12 }, { 3100, 0x14 }, { 3686, 0x16 }, { 4383, 0x18 }, { 5213, 0x1a }, { 6199, 0x1c }, { 7372, 0x1e }, { 7698, 0x20 }, { 8039, 0x22 }, { 8395, 0x24 }, { 8767, 0x26 }, { 9155, 0x28 }, { 9560, 0x2a }, { 9984, 0x2c }, { 10426, 0x2e }, { 10887, 0x30 }, { 11369, 0x32 }, { 11873, 0x34 }, { 12398, 0x36 }, { 12947, 0x38 }, { 13520, 0x3a }, { 14119, 0x3c }, { 14744, 0x3e }, { 15067, 0x40 }, { 15397, 0x42 }, { 15734, 0x44 }, { 16079, 0x46 }, { 16431, 0x48 }, { 16790, 0x4a }, { 17158, 0x4c }, { 17534, 0x4e }, { 17918, 0x50 }, { 18310, 0x52 }, { 18711, 0x54 }, { 19121, 0x56 }, { 19540, 0x58 }, { 19967, 0x5a }, { 20405, 0x5c }, { 20851, 0x5e }, { 21308, 0x60 }, { 21775, 0x62 }, { 22251, 0x64 }, { 22739, 0x66 }, { 23236, 0x68 }, { 23745, 0x6a }, { 24265, 0x6c }, { 24797, 0x6e }, { 25340, 0x70 }, { 25894, 0x72 }, { 26462, 0x74 }, { 27041, 0x76 }, { 27633, 0x78 }, { 28238, 0x7a }, { 28856, 0x7c }, { 29488, 0x7e }, { 30134, 0x80 }, { 30794, 0x82 }, { 31468, 0x84 }, { 32157, 0x86 }, { 32861, 0x88 }, { 33581, 0x8a }, { 34316, 0x8c }, { 35068, 0x8e }, { 35836, 0x90 }, { 36620, 0x92 }, { 37422, 0x94 }, { 38242, 0x96 }, { 39079, 0x98 }, { 39935, 0x9a }, { 40809, 0x9c }, { 41703, 0x9e }, { 42616, 0xa0 }, { 43549, 0xa2 }, { 44503, 0xa4 }, { 45477, 0xa6 }, { 46473, 0xa8 }, { 47491, 0xaa }, { 48531, 0xac }, { 49593, 0xae }, { 50679, 0xb0 }, { 51789, 0xb2 }, { 52923, 0xb4 }, { 54082, 0xb6 }, { 55266, 0xb8 }, { 56476, 0xba }, { 57713, 0xbc }, { 58977, 0xbe }, { 60268, 0xc0 }, { 61588, 0xc2 }, { 62936, 0xc4 }, { 64315, 0xc6 }, { 65535, 0xc8 } }; 549 int index = 0; 550 for (; index < 101; index++) { 551 if (amplitude <= hfa[index][0]) { 552 return (Uint8)hfa[index][1]; 553 } 554 } 555 return (Uint8)hfa[100][1]; 556} 557 558static Uint16 EncodeRumbleLowAmplitude(Uint16 amplitude) 559{ 560 /* More information about these values can be found here: 561 * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md 562 */ 563 Uint16 lfa[101][2] = { { 0, 0x0040 }, { 514, 0x8040 }, { 775, 0x0041 }, { 921, 0x8041 }, { 1096, 0x0042 }, { 1303, 0x8042 }, { 1550, 0x0043 }, { 1843, 0x8043 }, { 2192, 0x0044 }, { 2606, 0x8044 }, { 3100, 0x0045 }, { 3686, 0x8045 }, { 4383, 0x0046 }, { 5213, 0x8046 }, { 6199, 0x0047 }, { 7372, 0x8047 }, { 7698, 0x0048 }, { 8039, 0x8048 }, { 8395, 0x0049 }, { 8767, 0x8049 }, { 9155, 0x004a }, { 9560, 0x804a }, { 9984, 0x004b }, { 10426, 0x804b }, { 10887, 0x004c }, { 11369, 0x804c }, { 11873, 0x004d }, { 12398, 0x804d }, { 12947, 0x004e }, { 13520, 0x804e }, { 14119, 0x004f }, { 14744, 0x804f }, { 15067, 0x0050 }, { 15397, 0x8050 }, { 15734, 0x0051 }, { 16079, 0x8051 }, { 16431, 0x0052 }, { 16790, 0x8052 }, { 17158, 0x0053 }, { 17534, 0x8053 }, { 17918, 0x0054 }, { 18310, 0x8054 }, { 18711, 0x0055 }, { 19121, 0x8055 }, { 19540, 0x0056 }, { 19967, 0x8056 }, { 20405, 0x0057 }, { 20851, 0x8057 }, { 21308, 0x0058 }, { 21775, 0x8058 }, { 22251, 0x0059 }, { 22739, 0x8059 }, { 23236, 0x005a }, { 23745, 0x805a }, { 24265, 0x005b }, { 24797, 0x805b }, { 25340, 0x005c }, { 25894, 0x805c }, { 26462, 0x005d }, { 27041, 0x805d }, { 27633, 0x005e }, { 28238, 0x805e }, { 28856, 0x005f }, { 29488, 0x805f }, { 30134, 0x0060 }, { 30794, 0x8060 }, { 31468, 0x0061 }, { 32157, 0x8061 }, { 32861, 0x0062 }, { 33581, 0x8062 }, { 34316, 0x0063 }, { 35068, 0x8063 }, { 35836, 0x0064 }, { 36620, 0x8064 }, { 37422, 0x0065 }, { 38242, 0x8065 }, { 39079, 0x0066 }, { 39935, 0x8066 }, { 40809, 0x0067 }, { 41703, 0x8067 }, { 42616, 0x0068 }, { 43549, 0x8068 }, { 44503, 0x0069 }, { 45477, 0x8069 }, { 46473, 0x006a }, { 47491, 0x806a }, { 48531, 0x006b }, { 49593, 0x806b }, { 50679, 0x006c }, { 51789, 0x806c }, { 52923, 0x006d }, { 54082, 0x806d }, { 55266, 0x006e }, { 56476, 0x806e }, { 57713, 0x006f }, { 58977, 0x806f }, { 60268, 0x0070 }, { 61588, 0x8070 }, { 62936, 0x0071 }, { 64315, 0x8071 }, { 65535, 0x0072 } }; 564 int index = 0; 565 for (; index < 101; index++) { 566 if (amplitude <= lfa[index][0]) { 567 return lfa[index][1]; 568 } 569 } 570 return lfa[100][1]; 571} 572 573static void SetNeutralRumble(SDL_HIDAPI_Device *device, SwitchRumbleData_t *pRumble) 574{ 575 bool bStandardNeutralValue; 576 if (device->vendor_id == USB_VENDOR_NINTENDO && 577 device->product_id == USB_PRODUCT_NINTENDO_N64_CONTROLLER) { 578 // The 8BitDo 64 Bluetooth Controller rumbles at startup with the standard neutral value, 579 // so we'll use a 0 amplitude value instead. 580 bStandardNeutralValue = false; 581 } else { 582 // The KingKong2 PRO Controller doesn't initialize correctly with a 0 amplitude value 583 // over Bluetooth, so we'll use the standard value in all other cases. 584 bStandardNeutralValue = true; 585 } 586 if (bStandardNeutralValue) { 587 pRumble->rgucData[0] = 0x00; 588 pRumble->rgucData[1] = 0x01; 589 pRumble->rgucData[2] = 0x40; 590 pRumble->rgucData[3] = 0x40; 591 } else { 592 pRumble->rgucData[0] = 0x00; 593 pRumble->rgucData[1] = 0x00; 594 pRumble->rgucData[2] = 0x01; 595 pRumble->rgucData[3] = 0x40; 596 } 597} 598 599static void EncodeRumble(SDL_HIDAPI_Device *device, SwitchRumbleData_t *pRumble, Uint16 usHighFreq, Uint8 ucHighFreqAmp, Uint8 ucLowFreq, Uint16 usLowFreqAmp) 600{ 601 if (ucHighFreqAmp > 0 || usLowFreqAmp > 0) { 602 // High-band frequency and low-band amplitude are actually nine-bits each so they 603 // take a bit from the high-band amplitude and low-band frequency bytes respectively 604 pRumble->rgucData[0] = usHighFreq & 0xFF; 605 pRumble->rgucData[1] = ucHighFreqAmp | ((usHighFreq >> 8) & 0x01); 606 607 pRumble->rgucData[2] = ucLowFreq | ((usLowFreqAmp >> 8) & 0x80); 608 pRumble->rgucData[3] = usLowFreqAmp & 0xFF; 609 610#ifdef DEBUG_RUMBLE 611 SDL_Log("Freq: %.2X %.2X %.2X, Amp: %.2X %.2X %.2X", 612 usHighFreq & 0xFF, ((usHighFreq >> 8) & 0x01), ucLowFreq, 613 ucHighFreqAmp, ((usLowFreqAmp >> 8) & 0x80), usLowFreqAmp & 0xFF); 614#endif 615 } else { 616 SetNeutralRumble(device, pRumble); 617 } 618} 619 620static bool WriteRumble(SDL_DriverSwitch_Context *ctx) 621{ 622 /* Write into m_RumblePacket rather than a temporary buffer to allow the current rumble state 623 * to be retained for subsequent rumble or subcommand packets sent to the controller 624 */ 625 ctx->m_RumblePacket.ucPacketType = k_eSwitchOutputReportIDs_Rumble; 626 ctx->m_RumblePacket.ucPacketNumber = ctx->m_nCommandNumber; 627 ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF; 628 629 // Refresh the rumble state periodically 630 ctx->m_ulRumbleSent = SDL_GetTicks(); 631 632 return WritePacket(ctx, (Uint8 *)&ctx->m_RumblePacket, sizeof(ctx->m_RumblePacket)); 633} 634 635static ESwitchDeviceInfoControllerType CalculateControllerType(SDL_DriverSwitch_Context *ctx, ESwitchDeviceInfoControllerType eControllerType) 636{ 637 SDL_HIDAPI_Device *device = ctx->device; 638 639 // The N64 controller reports as a Pro controller over USB 640 if (eControllerType == k_eSwitchDeviceInfoControllerType_ProController && 641 device->product_id == USB_PRODUCT_NINTENDO_N64_CONTROLLER) { 642 eControllerType = k_eSwitchDeviceInfoControllerType_N64; 643 } 644 645 if (eControllerType == k_eSwitchDeviceInfoControllerType_Unknown) { 646 // This might be a Joy-Con that's missing from a charging grip slot 647 if (device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) { 648 if (device->interface_number == 1) { 649 eControllerType = k_eSwitchDeviceInfoControllerType_JoyConLeft; 650 } else { 651 eControllerType = k_eSwitchDeviceInfoControllerType_JoyConRight; 652 } 653 } 654 } 655 return eControllerType; 656} 657 658static bool BReadDeviceInfo(SDL_DriverSwitch_Context *ctx) 659{ 660 SwitchSubcommandInputPacket_t *reply = NULL; 661 662 if (ctx->device->is_bluetooth) { 663 if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_RequestDeviceInfo, NULL, 0, &reply)) { 664 // Byte 2: Controller ID (1=LJC, 2=RJC, 3=Pro) 665 ctx->m_eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)reply->deviceInfo.ucDeviceType); 666 667 // Bytes 4-9: MAC address (big-endian) 668 SDL_memcpy(ctx->m_rgucMACAddress, reply->deviceInfo.rgucMACAddress, sizeof(ctx->m_rgucMACAddress)); 669 670 return true; 671 } 672 } else { 673 if (WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Status, NULL, 0, true)) { 674 SwitchProprietaryStatusPacket_t *status = (SwitchProprietaryStatusPacket_t *)&ctx->m_rgucReadBuffer[0]; 675 size_t i; 676 677 ctx->m_eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)status->ucDeviceType); 678 679 for (i = 0; i < sizeof(ctx->m_rgucMACAddress); ++i) { 680 ctx->m_rgucMACAddress[i] = status->rgucMACAddress[sizeof(ctx->m_rgucMACAddress) - i - 1]; 681 } 682 683 return true; 684 } 685 } 686 return false; 687} 688 689static bool BTrySetupUSB(SDL_DriverSwitch_Context *ctx) 690{ 691 /* We have to send a connection handshake to the controller when communicating over USB 692 * before we're able to send it other commands. Luckily this command is not supported 693 * over Bluetooth, so we can use the controller's lack of response as a way to 694 * determine if the connection is over USB or Bluetooth 695 */ 696 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, true)) { 697 return false; 698 } 699 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_HighSpeed, NULL, 0, true)) { 700 // The 8BitDo M30 and SF30 Pro don't respond to this command, but otherwise work correctly 701 // return false; 702 } 703 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, true)) { 704 // This fails on the right Joy-Con when plugged into the charging grip 705 // return false; 706 } 707 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, false)) { 708 return false; 709 } 710 return true; 711} 712 713static bool SetVibrationEnabled(SDL_DriverSwitch_Context *ctx, Uint8 enabled) 714{ 715 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_EnableVibration, &enabled, sizeof(enabled), NULL); 716} 717static bool SetInputMode(SDL_DriverSwitch_Context *ctx, Uint8 input_mode) 718{ 719#ifdef FORCE_SIMPLE_REPORTS 720 input_mode = k_eSwitchInputReportIDs_SimpleControllerState; 721#endif 722#ifdef FORCE_FULL_REPORTS 723 input_mode = k_eSwitchInputReportIDs_FullControllerState; 724#endif 725 726 if (input_mode == ctx->m_nCurrentInputMode) { 727 return true; 728 } else { 729 ctx->m_nCurrentInputMode = input_mode; 730 731 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetInputReportMode, &input_mode, sizeof(input_mode), NULL); 732 } 733} 734 735static bool SetHomeLED(SDL_DriverSwitch_Context *ctx, Uint8 brightness) 736{ 737 Uint8 ucLedIntensity = 0; 738 Uint8 rgucBuffer[4]; 739 740 if (brightness > 0) { 741 if (brightness < 65) { 742 ucLedIntensity = (brightness + 5) / 10; 743 } else { 744 ucLedIntensity = (Uint8)SDL_ceilf(0xF * SDL_powf((float)brightness / 100.f, 2.13f)); 745 } 746 } 747 748 rgucBuffer[0] = (0x0 << 4) | 0x1; // 0 mini cycles (besides first), cycle duration 8ms 749 rgucBuffer[1] = ((ucLedIntensity & 0xF) << 4) | 0x0; // LED start intensity (0x0-0xF), 0 cycles (LED stays on at start intensity after first cycle) 750 rgucBuffer[2] = ((ucLedIntensity & 0xF) << 4) | 0x0; // First cycle LED intensity, 0x0 intensity for second cycle 751 rgucBuffer[3] = (0x0 << 4) | 0x0; // 8ms fade transition to first cycle, 8ms first cycle LED duration 752 753 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetHomeLight, rgucBuffer, sizeof(rgucBuffer), NULL); 754} 755 756static void SDLCALL SDL_HomeLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 757{ 758 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)userdata; 759 760 if (hint && *hint) { 761 int value; 762 763 if (SDL_strchr(hint, '.') != NULL) { 764 value = (int)(100.0f * SDL_atof(hint)); 765 if (value > 255) { 766 value = 255; 767 } 768 } else if (SDL_GetStringBoolean(hint, true)) { 769 value = 100; 770 } else { 771 value = 0; 772 } 773 SetHomeLED(ctx, (Uint8)value); 774 } 775} 776 777static void UpdateSlotLED(SDL_DriverSwitch_Context *ctx) 778{ 779 if (!ctx->m_bInputOnly) { 780 Uint8 led_data = 0; 781 const Uint8 player_pattern[] = { 0x1, 0x3, 0x7, 0xf, 0x9, 0x5, 0xd, 0x6 }; 782 783 if (ctx->m_bPlayerLights && ctx->m_nPlayerIndex >= 0) { 784 led_data = player_pattern[ctx->m_nPlayerIndex % 8]; 785 } 786 WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetPlayerLights, &led_data, sizeof(led_data), NULL); 787 } 788} 789 790static void SDLCALL SDL_PlayerLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 791{ 792 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)userdata; 793 bool bPlayerLights = SDL_GetStringBoolean(hint, true); 794 795 if (bPlayerLights != ctx->m_bPlayerLights) { 796 ctx->m_bPlayerLights = bPlayerLights; 797 798 UpdateSlotLED(ctx); 799 HIDAPI_UpdateDeviceProperties(ctx->device); 800 } 801} 802 803static void GetInitialInputMode(SDL_DriverSwitch_Context *ctx) 804{ 805 if (!ctx->m_nInitialInputMode) { 806 // This will set the initial input mode if it can 807 ReadInput(ctx); 808 } 809} 810 811static Uint8 GetDefaultInputMode(SDL_DriverSwitch_Context *ctx) 812{ 813 Uint8 input_mode; 814 815 // Determine the desired input mode 816 if (ctx->m_nInitialInputMode) { 817 input_mode = ctx->m_nInitialInputMode; 818 } else { 819 if (ctx->device->is_bluetooth) { 820 input_mode = k_eSwitchInputReportIDs_SimpleControllerState; 821 } else { 822 input_mode = k_eSwitchInputReportIDs_FullControllerState; 823 } 824 } 825 826 switch (ctx->m_eEnhancedReportHint) { 827 case SWITCH_ENHANCED_REPORT_HINT_OFF: 828 input_mode = k_eSwitchInputReportIDs_SimpleControllerState; 829 break; 830 case SWITCH_ENHANCED_REPORT_HINT_ON: 831 if (input_mode == k_eSwitchInputReportIDs_SimpleControllerState) { 832 input_mode = k_eSwitchInputReportIDs_FullControllerState; 833 } 834 break; 835 case SWITCH_ENHANCED_REPORT_HINT_AUTO: 836 /* Joy-Con controllers switch their thumbsticks into D-pad mode in simple mode, 837 * so let's enable full controller state for them. 838 */ 839 if (ctx->device->vendor_id == USB_VENDOR_NINTENDO && 840 (ctx->device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT || 841 ctx->device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT)) { 842 input_mode = k_eSwitchInputReportIDs_FullControllerState; 843 } 844 break; 845 } 846 847 // Wired controllers break if they are put into simple controller state 848 if (input_mode == k_eSwitchInputReportIDs_SimpleControllerState && 849 !ctx->device->is_bluetooth) { 850 input_mode = k_eSwitchInputReportIDs_FullControllerState; 851 } 852 return input_mode; 853} 854 855static Uint8 GetSensorInputMode(SDL_DriverSwitch_Context *ctx) 856{ 857 Uint8 input_mode; 858 859 // Determine the desired input mode 860 if (!ctx->m_nInitialInputMode || 861 ctx->m_nInitialInputMode == k_eSwitchInputReportIDs_SimpleControllerState) { 862 input_mode = k_eSwitchInputReportIDs_FullControllerState; 863 } else { 864 input_mode = ctx->m_nInitialInputMode; 865 } 866 return input_mode; 867} 868 869static void UpdateInputMode(SDL_DriverSwitch_Context *ctx) 870{ 871 Uint8 input_mode; 872 873 if (ctx->m_bReportSensors) { 874 input_mode = GetSensorInputMode(ctx); 875 } else { 876 input_mode = GetDefaultInputMode(ctx); 877 } 878 SetInputMode(ctx, input_mode); 879} 880 881static void SetEnhancedModeAvailable(SDL_DriverSwitch_Context *ctx) 882{ 883 if (ctx->m_bEnhancedModeAvailable) { 884 return; 885 } 886 ctx->m_bEnhancedModeAvailable = true; 887 888 if (ctx->m_bSensorsSupported) { 889 // Use the right sensor in the combined Joy-Con pair 890 if (!ctx->device->parent || 891 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) { 892 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO, 200.0f); 893 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, 200.0f); 894 } 895 if (ctx->device->parent && 896 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) { 897 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO_L, 200.0f); 898 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL_L, 200.0f); 899 } 900 if (ctx->device->parent && 901 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) { 902 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO_R, 200.0f); 903 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL_R, 200.0f); 904 } 905 } 906} 907 908static void SetEnhancedReportHint(SDL_DriverSwitch_Context *ctx, HIDAPI_Switch_EnhancedReportHint eEnhancedReportHint) 909{ 910 ctx->m_eEnhancedReportHint = eEnhancedReportHint; 911 912 switch (eEnhancedReportHint) { 913 case SWITCH_ENHANCED_REPORT_HINT_OFF: 914 ctx->m_bEnhancedMode = false; 915 break; 916 case SWITCH_ENHANCED_REPORT_HINT_ON: 917 SetEnhancedModeAvailable(ctx); 918 ctx->m_bEnhancedMode = true; 919 break; 920 case SWITCH_ENHANCED_REPORT_HINT_AUTO: 921 SetEnhancedModeAvailable(ctx); 922 break; 923 } 924 925 UpdateInputMode(ctx); 926} 927 928static void UpdateEnhancedModeOnEnhancedReport(SDL_DriverSwitch_Context *ctx) 929{ 930 if (ctx->m_eEnhancedReportHint == SWITCH_ENHANCED_REPORT_HINT_AUTO) { 931 SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_ON); 932 } 933} 934 935static void UpdateEnhancedModeOnApplicationUsage(SDL_DriverSwitch_Context *ctx) 936{ 937 if (ctx->m_eEnhancedReportHint == SWITCH_ENHANCED_REPORT_HINT_AUTO) { 938 SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_ON); 939 } 940} 941 942static void SDLCALL SDL_EnhancedReportsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 943{ 944 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)userdata; 945 946 if (hint && SDL_strcasecmp(hint, "auto") == 0) { 947 SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_AUTO); 948 } else if (SDL_GetStringBoolean(hint, true)) { 949 SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_ON); 950 } else { 951 SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_OFF); 952 } 953} 954 955static bool SetIMUEnabled(SDL_DriverSwitch_Context *ctx, bool enabled) 956{ 957 Uint8 imu_data = enabled ? 1 : 0; 958 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_EnableIMU, &imu_data, sizeof(imu_data), NULL); 959} 960 961static bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx) 962{ 963 Uint8 *pLeftStickCal = NULL; 964 Uint8 *pRightStickCal = NULL; 965 size_t stick, axis; 966 SwitchSubcommandInputPacket_t *user_reply = NULL; 967 SwitchSubcommandInputPacket_t *factory_reply = NULL; 968 SwitchSPIOpData_t readUserParams; 969 SwitchSPIOpData_t readFactoryParams; 970 Uint8 userParamsReadSuccessCount = 0; 971 972 // Read User Calibration Info 973 readUserParams.unAddress = k_unSPIStickUserCalibrationStartOffset; 974 readUserParams.ucLength = k_unSPIStickUserCalibrationLength; 975 976 // This isn't readable on all controllers, so ignore failure 977 WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readUserParams, sizeof(readUserParams), &user_reply); 978 979 // Read Factory Calibration Info 980 readFactoryParams.unAddress = k_unSPIStickFactoryCalibrationStartOffset; 981 readFactoryParams.ucLength = k_unSPIStickFactoryCalibrationLength; 982 983 // Automatically select the user calibration if magic bytes are set 984 if (user_reply && user_reply->stickUserCalibration.rgucLeftMagic[0] == 0xB2 && user_reply->stickUserCalibration.rgucLeftMagic[1] == 0xA1) { 985 userParamsReadSuccessCount += 1; 986 pLeftStickCal = user_reply->stickUserCalibration.rgucLeftCalibration; 987 } 988 989 if (user_reply && user_reply->stickUserCalibration.rgucRightMagic[0] == 0xB2 && user_reply->stickUserCalibration.rgucRightMagic[1] == 0xA1) { 990 userParamsReadSuccessCount += 1; 991 pRightStickCal = user_reply->stickUserCalibration.rgucRightCalibration; 992 } 993 994 // Only read the factory calibration info if we failed to receive the correct magic bytes 995 if (userParamsReadSuccessCount < 2) { 996 // Read Factory Calibration Info 997 readFactoryParams.unAddress = k_unSPIStickFactoryCalibrationStartOffset; 998 readFactoryParams.ucLength = k_unSPIStickFactoryCalibrationLength; 999 1000 const int MAX_ATTEMPTS = 3; 1001 for (int attempt = 0;; ++attempt) { 1002 if (!WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readFactoryParams, sizeof(readFactoryParams), &factory_reply)) { 1003 return false; 1004 } 1005 1006 if (factory_reply->stickFactoryCalibration.opData.unAddress == k_unSPIStickFactoryCalibrationStartOffset) { 1007 // We successfully read the calibration data 1008 pLeftStickCal = factory_reply->stickFactoryCalibration.rgucLeftCalibration; 1009 pRightStickCal = factory_reply->stickFactoryCalibration.rgucRightCalibration; 1010 break; 1011 } 1012 1013 if (attempt == MAX_ATTEMPTS) { 1014 return false; 1015 } 1016 } 1017 } 1018 1019 // If we still don't have calibration data, return false 1020 if (pLeftStickCal == NULL || pRightStickCal == NULL) 1021 { 1022 return false; 1023 } 1024 1025 /* Stick calibration values are 12-bits each and are packed by bit 1026 * For whatever reason the fields are in a different order for each stick 1027 * Left: X-Max, Y-Max, X-Center, Y-Center, X-Min, Y-Min 1028 * Right: X-Center, Y-Center, X-Min, Y-Min, X-Max, Y-Max 1029 */ 1030 1031 // Left stick 1032 ctx->m_StickCalData[0].axis[0].sMax = ((pLeftStickCal[1] << 8) & 0xF00) | pLeftStickCal[0]; // X Axis max above center 1033 ctx->m_StickCalData[0].axis[1].sMax = (pLeftStickCal[2] << 4) | (pLeftStickCal[1] >> 4); // Y Axis max above center 1034 ctx->m_StickCalData[0].axis[0].sCenter = ((pLeftStickCal[4] << 8) & 0xF00) | pLeftStickCal[3]; // X Axis center 1035 ctx->m_StickCalData[0].axis[1].sCenter = (pLeftStickCal[5] << 4) | (pLeftStickCal[4] >> 4); // Y Axis center 1036 ctx->m_StickCalData[0].axis[0].sMin = ((pLeftStickCal[7] << 8) & 0xF00) | pLeftStickCal[6]; // X Axis min below center 1037 ctx->m_StickCalData[0].axis[1].sMin = (pLeftStickCal[8] << 4) | (pLeftStickCal[7] >> 4); // Y Axis min below center 1038 1039 // Right stick 1040 ctx->m_StickCalData[1].axis[0].sCenter = ((pRightStickCal[1] << 8) & 0xF00) | pRightStickCal[0]; // X Axis center 1041 ctx->m_StickCalData[1].axis[1].sCenter = (pRightStickCal[2] << 4) | (pRightStickCal[1] >> 4); // Y Axis center 1042 ctx->m_StickCalData[1].axis[0].sMin = ((pRightStickCal[4] << 8) & 0xF00) | pRightStickCal[3]; // X Axis min below center 1043 ctx->m_StickCalData[1].axis[1].sMin = (pRightStickCal[5] << 4) | (pRightStickCal[4] >> 4); // Y Axis min below center 1044 ctx->m_StickCalData[1].axis[0].sMax = ((pRightStickCal[7] << 8) & 0xF00) | pRightStickCal[6]; // X Axis max above center 1045 ctx->m_StickCalData[1].axis[1].sMax = (pRightStickCal[8] << 4) | (pRightStickCal[7] >> 4); // Y Axis max above center 1046 1047 // Filter out any values that were uninitialized (0xFFF) in the SPI read 1048 for (stick = 0; stick < 2; ++stick) { 1049 for (axis = 0; axis < 2; ++axis) { 1050 if (ctx->m_StickCalData[stick].axis[axis].sCenter == 0xFFF) { 1051 ctx->m_StickCalData[stick].axis[axis].sCenter = 2048; 1052 } 1053 if (ctx->m_StickCalData[stick].axis[axis].sMax == 0xFFF) { 1054 ctx->m_StickCalData[stick].axis[axis].sMax = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sCenter * 0.7f); 1055 } 1056 if (ctx->m_StickCalData[stick].axis[axis].sMin == 0xFFF) { 1057 ctx->m_StickCalData[stick].axis[axis].sMin = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sCenter * 0.7f); 1058 } 1059 } 1060 } 1061 1062 for (stick = 0; stick < 2; ++stick) { 1063 for (axis = 0; axis < 2; ++axis) { 1064 ctx->m_StickExtents[stick].axis[axis].sMin = -(Sint16)(ctx->m_StickCalData[stick].axis[axis].sMin * 0.7f); 1065 ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sMax * 0.7f); 1066 } 1067 } 1068 1069 for (stick = 0; stick < 2; ++stick) { 1070 for (axis = 0; axis < 2; ++axis) { 1071 ctx->m_SimpleStickExtents[stick].axis[axis].sMin = (Sint16)(SDL_MIN_SINT16 * 0.5f); 1072 ctx->m_SimpleStickExtents[stick].axis[axis].sMax = (Sint16)(SDL_MAX_SINT16 * 0.5f); 1073 } 1074 } 1075 1076 return true; 1077} 1078 1079static bool LoadIMUCalibration(SDL_DriverSwitch_Context *ctx) 1080{ 1081 SwitchSubcommandInputPacket_t *reply = NULL; 1082 1083 // Read Calibration Info 1084 SwitchSPIOpData_t readParams; 1085 readParams.unAddress = k_unSPIIMUScaleStartOffset; 1086 readParams.ucLength = k_unSPIIMUScaleLength; 1087 1088 if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply)) { 1089 Uint8 *pIMUScale; 1090 Sint16 sAccelRawX, sAccelRawY, sAccelRawZ, sGyroRawX, sGyroRawY, sGyroRawZ; 1091 Sint16 sAccelSensCoeffX, sAccelSensCoeffY, sAccelSensCoeffZ; 1092 Sint16 sGyroSensCoeffX, sGyroSensCoeffY, sGyroSensCoeffZ; 1093 1094 // IMU scale gives us multipliers for converting raw values to real world values 1095 pIMUScale = reply->spiReadData.rgucReadData; 1096 1097 sAccelRawX = (pIMUScale[1] << 8) | pIMUScale[0]; 1098 sAccelRawY = (pIMUScale[3] << 8) | pIMUScale[2]; 1099 sAccelRawZ = (pIMUScale[5] << 8) | pIMUScale[4]; 1100 1101 sAccelSensCoeffX = (pIMUScale[7] << 8) | pIMUScale[6]; 1102 sAccelSensCoeffY = (pIMUScale[9] << 8) | pIMUScale[8]; 1103 sAccelSensCoeffZ = (pIMUScale[11] << 8) | pIMUScale[10]; 1104 1105 sGyroRawX = (pIMUScale[13] << 8) | pIMUScale[12]; 1106 sGyroRawY = (pIMUScale[15] << 8) | pIMUScale[14]; 1107 sGyroRawZ = (pIMUScale[17] << 8) | pIMUScale[16]; 1108 1109 sGyroSensCoeffX = (pIMUScale[19] << 8) | pIMUScale[18]; 1110 sGyroSensCoeffY = (pIMUScale[21] << 8) | pIMUScale[20]; 1111 sGyroSensCoeffZ = (pIMUScale[23] << 8) | pIMUScale[22]; 1112 1113 // Check for user calibration data. If it's present and set, it'll override the factory settings 1114 readParams.unAddress = k_unSPIIMUUserScaleStartOffset; 1115 readParams.ucLength = k_unSPIIMUUserScaleLength; 1116 if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply) && (pIMUScale[0] | pIMUScale[1] << 8) == 0xA1B2) { 1117 pIMUScale = reply->spiReadData.rgucReadData; 1118 1119 sAccelRawX = (pIMUScale[3] << 8) | pIMUScale[2]; 1120 sAccelRawY = (pIMUScale[5] << 8) | pIMUScale[4]; 1121 sAccelRawZ = (pIMUScale[7] << 8) | pIMUScale[6]; 1122 1123 sGyroRawX = (pIMUScale[15] << 8) | pIMUScale[14]; 1124 sGyroRawY = (pIMUScale[17] << 8) | pIMUScale[16]; 1125 sGyroRawZ = (pIMUScale[19] << 8) | pIMUScale[18]; 1126 } 1127 1128 // Accelerometer scale 1129 ctx->m_IMUScaleData.fAccelScaleX = SWITCH_ACCEL_SCALE_MULT / ((float)sAccelSensCoeffX - (float)sAccelRawX) * SDL_STANDARD_GRAVITY; 1130 ctx->m_IMUScaleData.fAccelScaleY = SWITCH_ACCEL_SCALE_MULT / ((float)sAccelSensCoeffY - (float)sAccelRawY) * SDL_STANDARD_GRAVITY; 1131 ctx->m_IMUScaleData.fAccelScaleZ = SWITCH_ACCEL_SCALE_MULT / ((float)sAccelSensCoeffZ - (float)sAccelRawZ) * SDL_STANDARD_GRAVITY; 1132 1133 // Gyro scale 1134 ctx->m_IMUScaleData.fGyroScaleX = SWITCH_GYRO_SCALE_MULT / ((float)sGyroSensCoeffX - (float)sGyroRawX) * SDL_PI_F / 180.0f; 1135 ctx->m_IMUScaleData.fGyroScaleY = SWITCH_GYRO_SCALE_MULT / ((float)sGyroSensCoeffY - (float)sGyroRawY) * SDL_PI_F / 180.0f; 1136 ctx->m_IMUScaleData.fGyroScaleZ = SWITCH_GYRO_SCALE_MULT / ((float)sGyroSensCoeffZ - (float)sGyroRawZ) * SDL_PI_F / 180.0f; 1137 1138 } else { 1139 // Use default values 1140 const float accelScale = SDL_STANDARD_GRAVITY / SWITCH_ACCEL_SCALE; 1141 const float gyroScale = SDL_PI_F / 180.0f / SWITCH_GYRO_SCALE; 1142 1143 ctx->m_IMUScaleData.fAccelScaleX = accelScale; 1144 ctx->m_IMUScaleData.fAccelScaleY = accelScale; 1145 ctx->m_IMUScaleData.fAccelScaleZ = accelScale; 1146 1147 ctx->m_IMUScaleData.fGyroScaleX = gyroScale; 1148 ctx->m_IMUScaleData.fGyroScaleY = gyroScale; 1149 ctx->m_IMUScaleData.fGyroScaleZ = gyroScale; 1150 } 1151 return true; 1152} 1153 1154static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue) 1155{ 1156 sRawValue -= ctx->m_StickCalData[nStick].axis[nAxis].sCenter; 1157 1158 if (sRawValue >= 0) { 1159 if (sRawValue > ctx->m_StickExtents[nStick].axis[nAxis].sMax) { 1160 ctx->m_StickExtents[nStick].axis[nAxis].sMax = sRawValue; 1161 } 1162 return (Sint16)HIDAPI_RemapVal(sRawValue, 0, ctx->m_StickExtents[nStick].axis[nAxis].sMax, 0, SDL_MAX_SINT16); 1163 } else { 1164 if (sRawValue < ctx->m_StickExtents[nStick].axis[nAxis].sMin) { 1165 ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue; 1166 } 1167 return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, 0, SDL_MIN_SINT16, 0); 1168 } 1169} 1170 1171static Sint16 ApplySimpleStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue) 1172{ 1173 // 0x8000 is the neutral value for all joystick axes 1174 const Uint16 usJoystickCenter = 0x8000; 1175 1176 sRawValue -= usJoystickCenter; 1177 1178 if (sRawValue >= 0) { 1179 if (sRawValue > ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax) { 1180 ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax = sRawValue; 1181 } 1182 return (Sint16)HIDAPI_RemapVal(sRawValue, 0, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax, 0, SDL_MAX_SINT16); 1183 } else { 1184 if (sRawValue < ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin) { 1185 ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin = sRawValue; 1186 } 1187 return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin, 0, SDL_MIN_SINT16, 0); 1188 } 1189} 1190 1191static Uint8 RemapButton(SDL_DriverSwitch_Context *ctx, Uint8 button) 1192{ 1193 if (ctx->m_bUseButtonLabels) { 1194 // Use button labels instead of positions, e.g. Nintendo Online Classic controllers 1195 switch (button) { 1196 case SDL_GAMEPAD_BUTTON_SOUTH: 1197 return SDL_GAMEPAD_BUTTON_EAST; 1198 case SDL_GAMEPAD_BUTTON_EAST: 1199 return SDL_GAMEPAD_BUTTON_SOUTH; 1200 case SDL_GAMEPAD_BUTTON_WEST: 1201 return SDL_GAMEPAD_BUTTON_NORTH; 1202 case SDL_GAMEPAD_BUTTON_NORTH: 1203 return SDL_GAMEPAD_BUTTON_WEST; 1204 default: 1205 break; 1206 } 1207 } 1208 return button; 1209} 1210 1211static int GetMaxWriteAttempts(SDL_HIDAPI_Device *device) 1212{ 1213 if (device->vendor_id == USB_VENDOR_NINTENDO && 1214 device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) { 1215 // This device is a little slow and we know we're always on USB 1216 return 20; 1217 } else { 1218 return 5; 1219 } 1220} 1221 1222static ESwitchDeviceInfoControllerType ReadJoyConControllerType(SDL_HIDAPI_Device *device) 1223{ 1224 ESwitchDeviceInfoControllerType eControllerType = k_eSwitchDeviceInfoControllerType_Unknown; 1225 const int MAX_ATTEMPTS = 1; // Don't try too long, in case this is a zombie Bluetooth controller 1226 int attempts = 0; 1227 1228 // Create enough of a context to read the controller type from the device 1229 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx)); 1230 if (ctx) { 1231 ctx->device = device; 1232 ctx->m_bSyncWrite = true; 1233 ctx->m_nMaxWriteAttempts = GetMaxWriteAttempts(device); 1234 1235 for ( ; ; ) { 1236 ++attempts; 1237 if (device->is_bluetooth) { 1238 SwitchSubcommandInputPacket_t *reply = NULL; 1239 1240 if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_RequestDeviceInfo, NULL, 0, &reply)) { 1241 eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)reply->deviceInfo.ucDeviceType); 1242 } 1243 } else { 1244 if (WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Status, NULL, 0, true)) { 1245 SwitchProprietaryStatusPacket_t *status = (SwitchProprietaryStatusPacket_t *)&ctx->m_rgucReadBuffer[0]; 1246 1247 eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)status->ucDeviceType); 1248 } 1249 } 1250 if (eControllerType == k_eSwitchDeviceInfoControllerType_Unknown && attempts < MAX_ATTEMPTS) { 1251 // Wait a bit and try again 1252 SDL_Delay(100); 1253 continue; 1254 } 1255 break; 1256 } 1257 SDL_free(ctx); 1258 } 1259 return eControllerType; 1260} 1261 1262static bool HasHomeLED(SDL_DriverSwitch_Context *ctx) 1263{ 1264 Uint16 vendor_id = ctx->device->vendor_id; 1265 Uint16 product_id = ctx->device->product_id; 1266 1267 // The Power A Nintendo Switch Pro controllers don't have a Home LED 1268 if (vendor_id == 0 && product_id == 0) { 1269 return false; 1270 } 1271 1272 // HORI Wireless Switch Pad 1273 if (vendor_id == 0x0f0d && product_id == 0x00f6) { 1274 return false; 1275 } 1276 1277 // Third party controllers don't have a home LED and will shut off if we try to set it 1278 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_Unknown || 1279 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_LicProController) { 1280 return false; 1281 } 1282 1283 // The Nintendo Online classic controllers don't have a Home LED 1284 if (vendor_id == USB_VENDOR_NINTENDO && 1285 ctx->m_eControllerType > k_eSwitchDeviceInfoControllerType_ProController) { 1286 return false; 1287 } 1288 1289 return true; 1290} 1291 1292static bool AlwaysUsesLabels(Uint16 vendor_id, Uint16 product_id, ESwitchDeviceInfoControllerType eControllerType) 1293{ 1294 // Some controllers don't have a diamond button configuration, so should always use labels 1295 if (SDL_IsJoystickGameCube(vendor_id, product_id)) { 1296 return true; 1297 } 1298 switch (eControllerType) { 1299 case k_eSwitchDeviceInfoControllerType_HVCLeft: 1300 case k_eSwitchDeviceInfoControllerType_HVCRight: 1301 case k_eSwitchDeviceInfoControllerType_NESLeft: 1302 case k_eSwitchDeviceInfoControllerType_NESRight: 1303 case k_eSwitchDeviceInfoControllerType_N64: 1304 case k_eSwitchDeviceInfoControllerType_SEGA_Genesis: 1305 return true; 1306 default: 1307 return false; 1308 } 1309} 1310 1311static void HIDAPI_DriverNintendoClassic_RegisterHints(SDL_HintCallback callback, void *userdata) 1312{ 1313 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC, callback, userdata); 1314} 1315 1316static void HIDAPI_DriverNintendoClassic_UnregisterHints(SDL_HintCallback callback, void *userdata) 1317{ 1318 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC, callback, userdata); 1319} 1320 1321static bool HIDAPI_DriverNintendoClassic_IsEnabled(void) 1322{ 1323 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT)); 1324} 1325 1326static bool HIDAPI_DriverNintendoClassic_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) 1327{ 1328 if (vendor_id == USB_VENDOR_NINTENDO) { 1329 if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT) { 1330 if (SDL_strncmp(name, "NES Controller", 14) == 0 || 1331 SDL_strncmp(name, "HVC Controller", 14) == 0) { 1332 return true; 1333 } 1334 } 1335 1336 if (product_id == USB_PRODUCT_NINTENDO_N64_CONTROLLER) { 1337 return true; 1338 } 1339 1340 if (product_id == USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER) { 1341 return true; 1342 } 1343 1344 if (product_id == USB_PRODUCT_NINTENDO_SNES_CONTROLLER) { 1345 return true; 1346 } 1347 } 1348 1349 return false; 1350} 1351 1352static void HIDAPI_DriverJoyCons_RegisterHints(SDL_HintCallback callback, void *userdata) 1353{ 1354 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, callback, userdata); 1355} 1356 1357static void HIDAPI_DriverJoyCons_UnregisterHints(SDL_HintCallback callback, void *userdata) 1358{ 1359 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, callback, userdata); 1360} 1361 1362static bool HIDAPI_DriverJoyCons_IsEnabled(void) 1363{ 1364 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT)); 1365} 1366 1367static bool HIDAPI_DriverJoyCons_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) 1368{ 1369 if (vendor_id == USB_VENDOR_NINTENDO) { 1370 if (product_id == USB_PRODUCT_NINTENDO_SWITCH_PRO && device && device->dev) { 1371 // This might be a Kinvoca Joy-Con that reports VID/PID as a Switch Pro controller 1372 ESwitchDeviceInfoControllerType eControllerType = ReadJoyConControllerType(device); 1373 if (eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft || 1374 eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) { 1375 return true; 1376 } 1377 } 1378 1379 if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT || 1380 product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT || 1381 product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) { 1382 return true; 1383 } 1384 } 1385 return false; 1386} 1387 1388static void HIDAPI_DriverSwitch_RegisterHints(SDL_HintCallback callback, void *userdata) 1389{ 1390 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, callback, userdata); 1391} 1392 1393static void HIDAPI_DriverSwitch_UnregisterHints(SDL_HintCallback callback, void *userdata) 1394{ 1395 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, callback, userdata); 1396} 1397 1398static bool HIDAPI_DriverSwitch_IsEnabled(void) 1399{ 1400 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT)); 1401} 1402 1403static bool HIDAPI_DriverSwitch_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) 1404{ 1405 /* The HORI Wireless Switch Pad enumerates as a HID device when connected via USB 1406 with the same VID/PID as when connected over Bluetooth but doesn't actually 1407 support communication over USB. The most reliable way to block this without allowing the 1408 controller to continually attempt to reconnect is to filter it out by manufacturer/product string. 1409 Note that the controller does have a different product string when connected over Bluetooth. 1410 */ 1411 if (SDL_strcmp(name, "HORI Wireless Switch Pad") == 0) { 1412 return false; 1413 } 1414 1415 // If it's handled by another driver, it's not handled here 1416 if (HIDAPI_DriverNintendoClassic_IsSupportedDevice(device, name, type, vendor_id, product_id, version, interface_number, interface_class, interface_subclass, interface_protocol) || 1417 HIDAPI_DriverJoyCons_IsSupportedDevice(device, name, type, vendor_id, product_id, version, interface_number, interface_class, interface_subclass, interface_protocol)) { 1418 return false; 1419 } 1420 1421 if (type != SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO) { 1422 return false; 1423 } 1424 1425 // The Nintendo Switch 2 Pro uses another driver 1426 if (vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH2_PRO) { 1427 return false; 1428 } 1429 return true; 1430} 1431 1432static void UpdateDeviceIdentity(SDL_HIDAPI_Device *device) 1433{ 1434 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context; 1435 1436 if (ctx->m_bInputOnly) { 1437 if (SDL_IsJoystickGameCube(device->vendor_id, device->product_id)) { 1438 device->type = SDL_GAMEPAD_TYPE_GAMECUBE; 1439 } 1440 } else { 1441 char serial[18]; 1442 1443 switch (ctx->m_eControllerType) { 1444 case k_eSwitchDeviceInfoControllerType_JoyConLeft: 1445 HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (L)"); 1446 HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT); 1447 device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT; 1448 break; 1449 case k_eSwitchDeviceInfoControllerType_JoyConRight: 1450 HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (R)"); 1451 HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT); 1452 device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT; 1453 break; 1454 case k_eSwitchDeviceInfoControllerType_ProController: 1455 case k_eSwitchDeviceInfoControllerType_LicProController: 1456 HIDAPI_SetDeviceName(device, "Nintendo Switch Pro Controller"); 1457 HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_PRO); 1458 device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO; 1459 break; 1460 case k_eSwitchDeviceInfoControllerType_HVCLeft: 1461 HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (1)"); 1462 device->type = SDL_GAMEPAD_TYPE_STANDARD; 1463 break; 1464 case k_eSwitchDeviceInfoControllerType_HVCRight: 1465 HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (2)"); 1466 device->type = SDL_GAMEPAD_TYPE_STANDARD; 1467 break; 1468 case k_eSwitchDeviceInfoControllerType_NESLeft: 1469 HIDAPI_SetDeviceName(device, "Nintendo NES Controller (L)"); 1470 device->type = SDL_GAMEPAD_TYPE_STANDARD; 1471 break; 1472 case k_eSwitchDeviceInfoControllerType_NESRight: 1473 HIDAPI_SetDeviceName(device, "Nintendo NES Controller (R)"); 1474 device->type = SDL_GAMEPAD_TYPE_STANDARD; 1475 break; 1476 case k_eSwitchDeviceInfoControllerType_SNES: 1477 HIDAPI_SetDeviceName(device, "Nintendo SNES Controller"); 1478 HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SNES_CONTROLLER); 1479 device->type = SDL_GAMEPAD_TYPE_STANDARD; 1480 break; 1481 case k_eSwitchDeviceInfoControllerType_N64: 1482 HIDAPI_SetDeviceName(device, "Nintendo N64 Controller"); 1483 HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_N64_CONTROLLER); 1484 device->type = SDL_GAMEPAD_TYPE_STANDARD; 1485 break; 1486 case k_eSwitchDeviceInfoControllerType_SEGA_Genesis: 1487 HIDAPI_SetDeviceName(device, "Nintendo SEGA Genesis Controller"); 1488 HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER); 1489 device->type = SDL_GAMEPAD_TYPE_STANDARD; 1490 break; 1491 case k_eSwitchDeviceInfoControllerType_Unknown: 1492 // We couldn't read the device info for this controller, might not be fully compliant 1493 if (device->vendor_id == USB_VENDOR_NINTENDO) { 1494 switch (device->product_id) { 1495 case USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT: 1496 ctx->m_eControllerType = k_eSwitchDeviceInfoControllerType_JoyConLeft; 1497 HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (L)"); 1498 device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT; 1499 break; 1500 case USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT: 1501 ctx->m_eControllerType = k_eSwitchDeviceInfoControllerType_JoyConRight; 1502 HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (R)"); 1503 device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT; 1504 break; 1505 case USB_PRODUCT_NINTENDO_SWITCH_PRO: 1506 ctx->m_eControllerType = k_eSwitchDeviceInfoControllerType_ProController; 1507 HIDAPI_SetDeviceName(device, "Nintendo Switch Pro Controller"); 1508 device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO; 1509 break; 1510 default: 1511 break; 1512 } 1513 } 1514 return; 1515 default: 1516 device->type = SDL_GAMEPAD_TYPE_STANDARD; 1517 break; 1518 } 1519 device->guid.data[15] = ctx->m_eControllerType; 1520 1521 (void)SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", 1522 ctx->m_rgucMACAddress[0], 1523 ctx->m_rgucMACAddress[1], 1524 ctx->m_rgucMACAddress[2], 1525 ctx->m_rgucMACAddress[3], 1526 ctx->m_rgucMACAddress[4], 1527 ctx->m_rgucMACAddress[5]); 1528 HIDAPI_SetDeviceSerial(device, serial); 1529 } 1530} 1531 1532static bool HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device) 1533{ 1534 SDL_DriverSwitch_Context *ctx; 1535 1536 ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx)); 1537 if (!ctx) { 1538 return false; 1539 } 1540 ctx->device = device; 1541 device->context = ctx; 1542 1543 ctx->m_nMaxWriteAttempts = GetMaxWriteAttempts(device); 1544 ctx->m_bSyncWrite = true; 1545 1546 // Find out whether or not we can send output reports 1547 ctx->m_bInputOnly = SDL_IsJoystickNintendoSwitchProInputOnly(device->vendor_id, device->product_id); 1548 if (!ctx->m_bInputOnly) { 1549 // Initialize rumble data, important for reading device info on the MOBAPAD M073 1550 SetNeutralRumble(device, &ctx->m_RumblePacket.rumbleData[0]); 1551 SetNeutralRumble(device, &ctx->m_RumblePacket.rumbleData[1]); 1552 1553 BReadDeviceInfo(ctx); 1554 } 1555 UpdateDeviceIdentity(device); 1556 1557 // Prefer the USB device over the Bluetooth device 1558 if (device->is_bluetooth) { 1559 if (HIDAPI_HasConnectedUSBDevice(device->serial)) { 1560 return true; 1561 } 1562 } else { 1563 HIDAPI_DisconnectBluetoothDevice(device->serial); 1564 } 1565 return HIDAPI_JoystickConnected(device, NULL); 1566} 1567 1568static int HIDAPI_DriverSwitch_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) 1569{ 1570 return -1; 1571} 1572 1573static void HIDAPI_DriverSwitch_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) 1574{ 1575 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context; 1576 1577 if (!ctx->joystick) { 1578 return; 1579 } 1580 1581 ctx->m_nPlayerIndex = player_index; 1582 1583 UpdateSlotLED(ctx); 1584} 1585 1586static bool HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 1587{ 1588 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context; 1589 1590 SDL_AssertJoysticksLocked(); 1591 1592 ctx->joystick = joystick; 1593 1594 ctx->m_bSyncWrite = true; 1595 1596 if (!ctx->m_bInputOnly) { 1597#ifdef SDL_PLATFORM_MACOS 1598 // Wait for the OS to finish its handshake with the controller 1599 SDL_Delay(250); 1600#endif 1601 GetInitialInputMode(ctx); 1602 ctx->m_nCurrentInputMode = ctx->m_nInitialInputMode; 1603 1604 // Initialize rumble data 1605 SetNeutralRumble(device, &ctx->m_RumblePacket.rumbleData[0]); 1606 SetNeutralRumble(device, &ctx->m_RumblePacket.rumbleData[1]); 1607 1608 if (!device->is_bluetooth) { 1609 if (!BTrySetupUSB(ctx)) { 1610 SDL_SetError("Couldn't setup USB mode"); 1611 return false; 1612 } 1613 } 1614 1615 if (!LoadStickCalibration(ctx)) { 1616 SDL_SetError("Couldn't load stick calibration"); 1617 return false; 1618 } 1619 1620 if (ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_HVCLeft && 1621 ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_HVCRight && 1622 ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_NESLeft && 1623 ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_NESRight && 1624 ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_SNES && 1625 ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_N64 && 1626 ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_SEGA_Genesis && 1627 !(device->vendor_id == USB_VENDOR_PDP && device->product_id == USB_PRODUCT_PDP_REALMZ_WIRELESS)) { 1628 if (LoadIMUCalibration(ctx)) { 1629 ctx->m_bSensorsSupported = true; 1630 } 1631 } 1632 1633 // Enable vibration 1634 SetVibrationEnabled(ctx, 1); 1635 1636 // Set desired input mode 1637 SDL_AddHintCallback(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, 1638 SDL_EnhancedReportsChanged, ctx); 1639 1640 // Start sending USB reports 1641 if (!device->is_bluetooth) { 1642 // ForceUSB doesn't generate an ACK, so don't wait for a reply 1643 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, false)) { 1644 SDL_SetError("Couldn't start USB reports"); 1645 return false; 1646 } 1647 } 1648 1649 // Set the LED state 1650 if (HasHomeLED(ctx)) { 1651 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft || 1652 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) { 1653 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED, 1654 SDL_HomeLEDHintChanged, ctx); 1655 } else { 1656 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, 1657 SDL_HomeLEDHintChanged, ctx); 1658 } 1659 } 1660 } 1661 1662 if (AlwaysUsesLabels(device->vendor_id, device->product_id, ctx->m_eControllerType)) { 1663 ctx->m_bUseButtonLabels = true; 1664 } 1665 1666 // Initialize player index (needed for setting LEDs) 1667 ctx->m_nPlayerIndex = SDL_GetJoystickPlayerIndex(joystick); 1668 ctx->m_bPlayerLights = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, true); 1669 UpdateSlotLED(ctx); 1670 1671 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, 1672 SDL_PlayerLEDHintChanged, ctx); 1673 1674 // Initialize the joystick capabilities 1675 joystick->nbuttons = SDL_GAMEPAD_NUM_SWITCH_BUTTONS; 1676 joystick->naxes = SDL_GAMEPAD_AXIS_COUNT; 1677 joystick->nhats = 1; 1678 1679 // Set up for input 1680 ctx->m_bSyncWrite = false; 1681 ctx->m_ulLastIMUReset = ctx->m_ulLastInput = SDL_GetTicks(); 1682 ctx->m_ulIMUUpdateIntervalNS = SDL_MS_TO_NS(5); // Start off at 5 ms update rate 1683 1684 // Set up for vertical mode 1685 ctx->m_bVerticalMode = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, false); 1686 1687 return true; 1688} 1689 1690static bool HIDAPI_DriverSwitch_ActuallyRumbleJoystick(SDL_DriverSwitch_Context *ctx, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 1691{ 1692 /* Experimentally determined rumble values. These will only matter on some controllers as tested ones 1693 * seem to disregard these and just use any non-zero rumble values as a binary flag for constant rumble 1694 * 1695 * More information about these values can be found here: 1696 * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md 1697 */ 1698 const Uint16 k_usHighFreq = 0x0074; 1699 const Uint8 k_ucHighFreqAmp = EncodeRumbleHighAmplitude(high_frequency_rumble); 1700 const Uint8 k_ucLowFreq = 0x3D; 1701 const Uint16 k_usLowFreqAmp = EncodeRumbleLowAmplitude(low_frequency_rumble); 1702 1703 if (low_frequency_rumble || high_frequency_rumble) { 1704 EncodeRumble(ctx->device, &ctx->m_RumblePacket.rumbleData[0], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp); 1705 EncodeRumble(ctx->device, &ctx->m_RumblePacket.rumbleData[1], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp); 1706 } else { 1707 SetNeutralRumble(ctx->device, &ctx->m_RumblePacket.rumbleData[0]); 1708 SetNeutralRumble(ctx->device, &ctx->m_RumblePacket.rumbleData[1]); 1709 } 1710 1711 ctx->m_bRumbleActive = (low_frequency_rumble || high_frequency_rumble); 1712 1713 if (!WriteRumble(ctx)) { 1714 return SDL_SetError("Couldn't send rumble packet"); 1715 } 1716 return true; 1717} 1718 1719static bool HIDAPI_DriverSwitch_SendPendingRumble(SDL_DriverSwitch_Context *ctx) 1720{ 1721 if (SDL_GetTicks() < (ctx->m_ulRumbleSent + RUMBLE_WRITE_FREQUENCY_MS)) { 1722 return true; 1723 } 1724 1725 if (ctx->m_bRumblePending) { 1726 Uint16 low_frequency_rumble = (Uint16)(ctx->m_unRumblePending >> 16); 1727 Uint16 high_frequency_rumble = (Uint16)ctx->m_unRumblePending; 1728 1729#ifdef DEBUG_RUMBLE 1730 SDL_Log("Sent pending rumble %d/%d, %d ms after previous rumble", low_frequency_rumble, high_frequency_rumble, SDL_GetTicks() - ctx->m_ulRumbleSent); 1731#endif 1732 ctx->m_bRumblePending = false; 1733 ctx->m_unRumblePending = 0; 1734 1735 return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, low_frequency_rumble, high_frequency_rumble); 1736 } 1737 1738 if (ctx->m_bRumbleZeroPending) { 1739 ctx->m_bRumbleZeroPending = false; 1740 1741#ifdef DEBUG_RUMBLE 1742 SDL_Log("Sent pending zero rumble, %d ms after previous rumble", SDL_GetTicks() - ctx->m_ulRumbleSent); 1743#endif 1744 return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, 0, 0); 1745 } 1746 1747 return true; 1748} 1749 1750static bool HIDAPI_DriverSwitch_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 1751{ 1752 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context; 1753 1754 if (ctx->m_bInputOnly) { 1755 return SDL_Unsupported(); 1756 } 1757 1758 if (device->parent) { 1759 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) { 1760 // Just handle low frequency rumble 1761 high_frequency_rumble = 0; 1762 } else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) { 1763 // Just handle high frequency rumble 1764 low_frequency_rumble = 0; 1765 } 1766 } 1767 1768 if (ctx->m_bRumblePending) { 1769 if (!HIDAPI_DriverSwitch_SendPendingRumble(ctx)) { 1770 return false; 1771 } 1772 } 1773 1774 if (SDL_GetTicks() < (ctx->m_ulRumbleSent + RUMBLE_WRITE_FREQUENCY_MS)) { 1775 if (low_frequency_rumble || high_frequency_rumble) { 1776 Uint32 unRumblePending = ((Uint32)low_frequency_rumble << 16) | high_frequency_rumble; 1777 1778 // Keep the highest rumble intensity in the given interval 1779 if (unRumblePending > ctx->m_unRumblePending) { 1780 ctx->m_unRumblePending = unRumblePending; 1781 } 1782 ctx->m_bRumblePending = true; 1783 ctx->m_bRumbleZeroPending = false; 1784 } else { 1785 // When rumble is complete, turn it off 1786 ctx->m_bRumbleZeroPending = true; 1787 } 1788 return true; 1789 } 1790 1791#ifdef DEBUG_RUMBLE 1792 SDL_Log("Sent rumble %d/%d", low_frequency_rumble, high_frequency_rumble); 1793#endif 1794 1795 return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, low_frequency_rumble, high_frequency_rumble); 1796} 1797 1798static bool HIDAPI_DriverSwitch_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 1799{ 1800 return SDL_Unsupported(); 1801} 1802 1803static Uint32 HIDAPI_DriverSwitch_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 1804{ 1805 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context; 1806 Uint32 result = 0; 1807 1808 if (ctx->m_bPlayerLights && !ctx->m_bInputOnly) { 1809 result |= SDL_JOYSTICK_CAP_PLAYER_LED; 1810 } 1811 1812 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_ProController && !ctx->m_bInputOnly) { 1813 // Doesn't have an RGB LED, so don't return SDL_JOYSTICK_CAP_RGB_LED here 1814 result |= SDL_JOYSTICK_CAP_RUMBLE; 1815 // But has the HOME LED, so treat it like a mono LED 1816 result |= SDL_JOYSTICK_CAP_MONO_LED; 1817 } else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft || 1818 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) { 1819 result |= SDL_JOYSTICK_CAP_RUMBLE; 1820 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) { 1821 result |= SDL_JOYSTICK_CAP_MONO_LED; // Right JoyCon also have the HOME LED 1822 } 1823 } 1824 return result; 1825} 1826 1827static bool HIDAPI_DriverSwitch_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 1828{ 1829 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context; 1830 1831 if (!(ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_ProController && !ctx->m_bInputOnly) && 1832 ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_JoyConRight) { 1833 return SDL_Unsupported(); 1834 } 1835 1836 int value = (int)((SDL_max(red, SDL_max(green, blue)) / 255.0f) * 100.0f); // The colors are received between 0-255 and we need them to be 0-100 1837 return SetHomeLED(ctx, (Uint8)value); 1838} 1839 1840static bool HIDAPI_DriverSwitch_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size) 1841{ 1842 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context; 1843 1844 if (size == sizeof(SwitchCommonOutputPacket_t)) { 1845 const SwitchCommonOutputPacket_t *packet = (SwitchCommonOutputPacket_t *)data; 1846 1847 if (packet->ucPacketType != k_eSwitchOutputReportIDs_Rumble) { 1848 return SDL_SetError("Unknown Nintendo Switch Pro effect type"); 1849 } 1850 1851 SDL_copyp(&ctx->m_RumblePacket.rumbleData[0], &packet->rumbleData[0]); 1852 SDL_copyp(&ctx->m_RumblePacket.rumbleData[1], &packet->rumbleData[1]); 1853 if (!WriteRumble(ctx)) { 1854 return false; 1855 } 1856 1857 // This overwrites any internal rumble 1858 ctx->m_bRumblePending = false; 1859 ctx->m_bRumbleZeroPending = false; 1860 return true; 1861 } else if (size >= 2 && size <= 256) { 1862 const Uint8 *payload = (const Uint8 *)data; 1863 ESwitchSubcommandIDs cmd = (ESwitchSubcommandIDs)payload[0]; 1864 1865 if (cmd == k_eSwitchSubcommandIDs_SetInputReportMode && !device->is_bluetooth) { 1866 // Going into simple mode over USB disables input reports, so don't do that 1867 return true; 1868 } 1869 if (cmd == k_eSwitchSubcommandIDs_SetHomeLight && !HasHomeLED(ctx)) { 1870 // Setting the home LED when it's not supported can cause the controller to reset 1871 return true; 1872 } 1873 1874 if (!WriteSubcommand(ctx, cmd, &payload[1], (Uint8)(size - 1), NULL)) { 1875 return false; 1876 } 1877 return true; 1878 } 1879 return SDL_Unsupported(); 1880} 1881 1882static bool HIDAPI_DriverSwitch_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled) 1883{ 1884 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context; 1885 1886 UpdateEnhancedModeOnApplicationUsage(ctx); 1887 1888 if (!ctx->m_bSensorsSupported || (enabled && !ctx->m_bEnhancedMode)) { 1889 return SDL_Unsupported(); 1890 } 1891 1892 ctx->m_bReportSensors = enabled; 1893 ctx->m_unIMUSamples = 0; 1894 ctx->m_ulIMUSampleTimestampNS = SDL_GetTicksNS(); 1895 1896 UpdateInputMode(ctx); 1897 SetIMUEnabled(ctx, enabled); 1898 1899 return true; 1900} 1901 1902static void HandleInputOnlyControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchInputOnlyControllerStatePacket_t *packet) 1903{ 1904 Sint16 axis; 1905 Uint64 timestamp = SDL_GetTicksNS(); 1906 1907 if (packet->rgucButtons[0] != ctx->m_lastInputOnlyState.rgucButtons[0]) { 1908 Uint8 data = packet->rgucButtons[0]; 1909 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x02) != 0)); 1910 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x04) != 0)); 1911 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x01) != 0)); 1912 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0)); 1913 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0)); 1914 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0)); 1915 } 1916 1917 if (packet->rgucButtons[1] != ctx->m_lastInputOnlyState.rgucButtons[1]) { 1918 Uint8 data = packet->rgucButtons[1]; 1919 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0)); 1920 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0)); 1921 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0)); 1922 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x08) != 0)); 1923 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0)); 1924 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0)); 1925 } 1926 1927 if (packet->ucStickHat != ctx->m_lastInputOnlyState.ucStickHat) { 1928 Uint8 hat; 1929 1930 switch (packet->ucStickHat) { 1931 case 0: 1932 hat = SDL_HAT_UP; 1933 break; 1934 case 1: 1935 hat = SDL_HAT_RIGHTUP; 1936 break; 1937 case 2: 1938 hat = SDL_HAT_RIGHT; 1939 break; 1940 case 3: 1941 hat = SDL_HAT_RIGHTDOWN; 1942 break; 1943 case 4: 1944 hat = SDL_HAT_DOWN; 1945 break; 1946 case 5: 1947 hat = SDL_HAT_LEFTDOWN; 1948 break; 1949 case 6: 1950 hat = SDL_HAT_LEFT; 1951 break; 1952 case 7: 1953 hat = SDL_HAT_LEFTUP; 1954 break; 1955 default: 1956 hat = SDL_HAT_CENTERED; 1957 break; 1958 } 1959 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 1960 } 1961 1962 axis = (packet->rgucButtons[0] & 0x40) ? 32767 : -32768; 1963 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis); 1964 1965 axis = (packet->rgucButtons[0] & 0x80) ? 32767 : -32768; 1966 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis); 1967 1968 if (packet->rgucJoystickLeft[0] != ctx->m_lastInputOnlyState.rgucJoystickLeft[0]) { 1969 axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickLeft[0], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16); 1970 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis); 1971 } 1972 1973 if (packet->rgucJoystickLeft[1] != ctx->m_lastInputOnlyState.rgucJoystickLeft[1]) { 1974 axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickLeft[1], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16); 1975 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis); 1976 } 1977 1978 if (packet->rgucJoystickRight[0] != ctx->m_lastInputOnlyState.rgucJoystickRight[0]) { 1979 axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickRight[0], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16); 1980 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis); 1981 } 1982 1983 if (packet->rgucJoystickRight[1] != ctx->m_lastInputOnlyState.rgucJoystickRight[1]) { 1984 axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickRight[1], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16); 1985 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis); 1986 } 1987 1988 ctx->m_lastInputOnlyState = *packet; 1989} 1990 1991static void HandleCombinedSimpleControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet) 1992{ 1993 if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) { 1994 Uint8 data = packet->rgucButtons[0]; 1995 Uint8 hat = 0; 1996 1997 if (data & 0x01) { 1998 hat |= SDL_HAT_LEFT; 1999 } 2000 if (data & 0x02) { 2001 hat |= SDL_HAT_DOWN; 2002 } 2003 if (data & 0x04) { 2004 hat |= SDL_HAT_UP; 2005 } 2006 if (data & 0x08) { 2007 hat |= SDL_HAT_RIGHT; 2008 } 2009 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 2010 2011 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x10) != 0)); 2012 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x20) != 0)); 2013 } 2014 2015 if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) { 2016 Uint8 data = packet->rgucButtons[1]; 2017 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0)); 2018 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0)); 2019 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0)); 2020 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x40) != 0)); 2021 } 2022 2023 Sint16 axis = (packet->rgucButtons[1] & 0x80) ? 32767 : -32768; 2024 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis); 2025 2026 if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) { 2027 switch (packet->ucStickHat) { 2028 case 0: 2029 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX); 2030 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0); 2031 break; 2032 case 1: 2033 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX); 2034 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX); 2035 break; 2036 case 2: 2037 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0); 2038 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX); 2039 break; 2040 case 3: 2041 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN); 2042 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX); 2043 break; 2044 case 4: 2045 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN); 2046 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0); 2047 break; 2048 case 5: 2049 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN); 2050 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN); 2051 break; 2052 case 6: 2053 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0); 2054 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN); 2055 break; 2056 case 7: 2057 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX); 2058 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN); 2059 break; 2060 default: 2061 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0); 2062 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0); 2063 break; 2064 } 2065 } 2066} 2067 2068static void HandleCombinedSimpleControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet) 2069{ 2070 if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) { 2071 Uint8 data = packet->rgucButtons[0]; 2072 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x01) != 0)); 2073 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x02) != 0)); 2074 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x04) != 0)); 2075 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x08) != 0)); 2076 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x10) != 0)); 2077 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x20) != 0)); 2078 } 2079 2080 if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) { 2081 Uint8 data = packet->rgucButtons[1]; 2082 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0)); 2083 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x08) != 0)); 2084 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0)); 2085 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x40) != 0)); 2086 } 2087 2088 Sint16 axis = (packet->rgucButtons[1] & 0x80) ? 32767 : -32768; 2089 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis); 2090 2091 if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) { 2092 switch (packet->ucStickHat) { 2093 case 0: 2094 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MIN); 2095 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, 0); 2096 break; 2097 case 1: 2098 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MIN); 2099 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MIN); 2100 break; 2101 case 2: 2102 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, 0); 2103 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MIN); 2104 break; 2105 case 3: 2106 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MAX); 2107 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MIN); 2108 break; 2109 case 4: 2110 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MAX); 2111 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, 0); 2112 break; 2113 case 5: 2114 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MAX); 2115 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MAX); 2116 break; 2117 case 6: 2118 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, 0); 2119 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MAX); 2120 break; 2121 case 7: 2122 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MIN); 2123 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MAX); 2124 break; 2125 default: 2126 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, 0); 2127 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, 0); 2128 break; 2129 } 2130 } 2131} 2132 2133static void HandleMiniSimpleControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet) 2134{ 2135 if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) { 2136 Uint8 data = packet->rgucButtons[0]; 2137 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x01) != 0)); 2138 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0)); 2139 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0)); 2140 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0)); 2141 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0)); 2142 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0)); 2143 } 2144 2145 if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) { 2146 Uint8 data = packet->rgucButtons[1]; 2147 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x01) != 0)); 2148 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0)); 2149 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x20) != 0)); 2150 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x40) != 0)); 2151 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x80) != 0)); 2152 } 2153 2154 if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) { 2155 switch (packet->ucStickHat) { 2156 case 0: 2157 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0); 2158 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN); 2159 break; 2160 case 1: 2161 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX); 2162 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN); 2163 break; 2164 case 2: 2165 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX); 2166 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0); 2167 break; 2168 case 3: 2169 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX); 2170 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX); 2171 break; 2172 case 4: 2173 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0); 2174 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX); 2175 break; 2176 case 5: 2177 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN); 2178 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX); 2179 break; 2180 case 6: 2181 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN); 2182 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0); 2183 break; 2184 case 7: 2185 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN); 2186 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN); 2187 break; 2188 default: 2189 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0); 2190 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0); 2191 break; 2192 } 2193 } 2194} 2195 2196static void HandleMiniSimpleControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet) 2197{ 2198 if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) { 2199 Uint8 data = packet->rgucButtons[0]; 2200 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x01) != 0)); 2201 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0)); 2202 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0)); 2203 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0)); 2204 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0)); 2205 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0)); 2206 } 2207 2208 if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) { 2209 Uint8 data = packet->rgucButtons[1]; 2210 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0)); 2211 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0)); 2212 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0)); 2213 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0)); 2214 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x40) != 0)); 2215 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x80) != 0)); 2216 } 2217 2218 if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) { 2219 switch (packet->ucStickHat) { 2220 case 0: 2221 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0); 2222 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN); 2223 break; 2224 case 1: 2225 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX); 2226 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN); 2227 break; 2228 case 2: 2229 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX); 2230 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0); 2231 break; 2232 case 3: 2233 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX); 2234 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX); 2235 break; 2236 case 4: 2237 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0); 2238 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX); 2239 break; 2240 case 5: 2241 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN); 2242 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX); 2243 break; 2244 case 6: 2245 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN); 2246 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0); 2247 break; 2248 case 7: 2249 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN); 2250 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN); 2251 break; 2252 default: 2253 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0); 2254 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0); 2255 break; 2256 } 2257 } 2258} 2259 2260static void HandleSimpleControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet) 2261{ 2262 Uint64 timestamp = SDL_GetTicksNS(); 2263 2264 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) { 2265 if (ctx->device->parent || ctx->m_bVerticalMode) { 2266 HandleCombinedSimpleControllerStateL(timestamp, joystick, ctx, packet); 2267 } else { 2268 HandleMiniSimpleControllerStateL(timestamp, joystick, ctx, packet); 2269 } 2270 } else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) { 2271 if (ctx->device->parent || ctx->m_bVerticalMode) { 2272 HandleCombinedSimpleControllerStateR(timestamp, joystick, ctx, packet); 2273 } else { 2274 HandleMiniSimpleControllerStateR(timestamp, joystick, ctx, packet); 2275 } 2276 } else { 2277 Sint16 axis; 2278 2279 if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) { 2280 Uint8 data = packet->rgucButtons[0]; 2281 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x01) != 0)); 2282 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0)); 2283 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0)); 2284 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0)); 2285 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0)); 2286 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0)); 2287 } 2288 2289 if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) { 2290 Uint8 data = packet->rgucButtons[1]; 2291 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0)); 2292 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0)); 2293 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0)); 2294 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x08) != 0)); 2295 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0)); 2296 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0)); 2297 } 2298 2299 if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) { 2300 Uint8 hat; 2301 2302 switch (packet->ucStickHat) { 2303 case 0: 2304 hat = SDL_HAT_UP; 2305 break; 2306 case 1: 2307 hat = SDL_HAT_RIGHTUP; 2308 break; 2309 case 2: 2310 hat = SDL_HAT_RIGHT; 2311 break; 2312 case 3: 2313 hat = SDL_HAT_RIGHTDOWN; 2314 break; 2315 case 4: 2316 hat = SDL_HAT_DOWN; 2317 break; 2318 case 5: 2319 hat = SDL_HAT_LEFTDOWN; 2320 break; 2321 case 6: 2322 hat = SDL_HAT_LEFT; 2323 break; 2324 case 7: 2325 hat = SDL_HAT_LEFTUP; 2326 break; 2327 default: 2328 hat = SDL_HAT_CENTERED; 2329 break; 2330 } 2331 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 2332 } 2333 2334 axis = (packet->rgucButtons[0] & 0x40) ? 32767 : -32768; 2335 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis); 2336 2337 axis = ((packet->rgucButtons[0] & 0x80) || (packet->rgucButtons[1] & 0x80)) ? 32767 : -32768; 2338 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis); 2339 2340 axis = ApplySimpleStickCalibration(ctx, 0, 0, packet->sJoystickLeft[0]); 2341 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis); 2342 2343 axis = ApplySimpleStickCalibration(ctx, 0, 1, packet->sJoystickLeft[1]); 2344 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis); 2345 2346 axis = ApplySimpleStickCalibration(ctx, 1, 0, packet->sJoystickRight[0]); 2347 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis); 2348 2349 axis = ApplySimpleStickCalibration(ctx, 1, 1, packet->sJoystickRight[1]); 2350 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis); 2351 } 2352 2353 ctx->m_lastSimpleState = *packet; 2354} 2355 2356static void SendSensorUpdate(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SDL_SensorType type, Uint64 sensor_timestamp, const Sint16 *values) 2357{ 2358 float data[3]; 2359 2360 /* Note the order of components has been shuffled to match PlayStation controllers, 2361 * since that's our de facto standard from already supporting those controllers, and 2362 * users will want consistent axis mappings across devices. 2363 */ 2364 if (type == SDL_SENSOR_GYRO || type == SDL_SENSOR_GYRO_L || type == SDL_SENSOR_GYRO_R) { 2365 data[0] = -(ctx->m_IMUScaleData.fGyroScaleY * (float)values[1]); 2366 data[1] = ctx->m_IMUScaleData.fGyroScaleZ * (float)values[2]; 2367 data[2] = -(ctx->m_IMUScaleData.fGyroScaleX * (float)values[0]); 2368 } else { 2369 data[0] = -(ctx->m_IMUScaleData.fAccelScaleY * (float)values[1]); 2370 data[1] = ctx->m_IMUScaleData.fAccelScaleZ * (float)values[2]; 2371 data[2] = -(ctx->m_IMUScaleData.fAccelScaleX * (float)values[0]); 2372 } 2373 2374 // Right Joy-Con flips some axes, so let's flip them back for consistency 2375 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) { 2376 data[0] = -data[0]; 2377 data[1] = -data[1]; 2378 } 2379 2380 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft && 2381 !ctx->device->parent && !ctx->m_bVerticalMode) { 2382 // Mini-gamepad mode, swap some axes around 2383 float tmp = data[2]; 2384 data[2] = -data[0]; 2385 data[0] = tmp; 2386 } 2387 2388 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight && 2389 !ctx->device->parent && !ctx->m_bVerticalMode) { 2390 // Mini-gamepad mode, swap some axes around 2391 float tmp = data[2]; 2392 data[2] = data[0]; 2393 data[0] = -tmp; 2394 } 2395 2396 SDL_SendJoystickSensor(timestamp, joystick, type, sensor_timestamp, data, 3); 2397} 2398 2399static void HandleCombinedControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet) 2400{ 2401 Sint16 axis; 2402 2403 if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) { 2404 Uint8 data = packet->controllerState.rgucButtons[1]; 2405 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0)); 2406 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0)); 2407 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0)); 2408 } 2409 2410 if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) { 2411 Uint8 data = packet->controllerState.rgucButtons[2]; 2412 Uint8 hat = 0; 2413 2414 if (data & 0x01) { 2415 hat |= SDL_HAT_DOWN; 2416 } 2417 if (data & 0x02) { 2418 hat |= SDL_HAT_UP; 2419 } 2420 if (data & 0x04) { 2421 hat |= SDL_HAT_RIGHT; 2422 } 2423 if (data & 0x08) { 2424 hat |= SDL_HAT_LEFT; 2425 } 2426 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 2427 2428 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x10) != 0)); 2429 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x20) != 0)); 2430 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x40) != 0)); 2431 axis = (data & 0x80) ? 32767 : -32768; 2432 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis); 2433 } 2434 2435 axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8); 2436 axis = ApplyStickCalibration(ctx, 0, 0, axis); 2437 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis); 2438 2439 axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4); 2440 axis = ApplyStickCalibration(ctx, 0, 1, axis); 2441 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~axis); 2442} 2443 2444static void HandleCombinedControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet) 2445{ 2446 Sint16 axis; 2447 2448 if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) { 2449 Uint8 data = packet->controllerState.rgucButtons[0]; 2450 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x04) != 0)); 2451 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x08) != 0)); 2452 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x01) != 0)); 2453 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x02) != 0)); 2454 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x10) != 0)); 2455 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x20) != 0)); 2456 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x40) != 0)); 2457 axis = (data & 0x80) ? 32767 : -32768; 2458 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis); 2459 } 2460 2461 if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) { 2462 Uint8 data = packet->controllerState.rgucButtons[1]; 2463 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0)); 2464 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x04) != 0)); 2465 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0)); 2466 } 2467 2468 axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8); 2469 axis = ApplyStickCalibration(ctx, 1, 0, axis); 2470 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis); 2471 2472 axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4); 2473 axis = ApplyStickCalibration(ctx, 1, 1, axis); 2474 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~axis); 2475} 2476 2477static void HandleMiniControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet) 2478{ 2479 Sint16 axis; 2480 2481 if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) { 2482 Uint8 data = packet->controllerState.rgucButtons[1]; 2483 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x01) != 0)); 2484 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0)); 2485 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x20) != 0)); 2486 } 2487 2488 if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) { 2489 Uint8 data = packet->controllerState.rgucButtons[2]; 2490 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x08) != 0)); 2491 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x01) != 0)); 2492 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x02) != 0)); 2493 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x04) != 0)); 2494 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x10) != 0)); 2495 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x20) != 0)); 2496 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x40) != 0)); 2497 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x80) != 0)); 2498 } 2499 2500 axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8); 2501 axis = ApplyStickCalibration(ctx, 0, 0, axis); 2502 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~axis); 2503 2504 axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4); 2505 axis = ApplyStickCalibration(ctx, 0, 1, axis); 2506 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, ~axis); 2507} 2508 2509static void HandleMiniControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet) 2510{ 2511 Sint16 axis; 2512 2513 if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) { 2514 Uint8 data = packet->controllerState.rgucButtons[0]; 2515 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x08) != 0)); 2516 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0)); 2517 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0)); 2518 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x01) != 0)); 2519 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x10) != 0)); 2520 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x20) != 0)); 2521 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x40) != 0)); 2522 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x80) != 0)); 2523 } 2524 2525 if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) { 2526 Uint8 data = packet->controllerState.rgucButtons[1]; 2527 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0)); 2528 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0)); 2529 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0)); 2530 } 2531 2532 axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8); 2533 axis = ApplyStickCalibration(ctx, 1, 0, axis); 2534 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis); 2535 2536 axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4); 2537 axis = ApplyStickCalibration(ctx, 1, 1, axis); 2538 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis); 2539} 2540 2541static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet) SDL_NO_THREAD_SAFETY_ANALYSIS // We unlock and lock the device lock to be able to change IMU state 2542{ 2543 Uint64 timestamp = SDL_GetTicksNS(); 2544 2545 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) { 2546 if (ctx->device->parent || ctx->m_bVerticalMode) { 2547 HandleCombinedControllerStateL(timestamp, joystick, ctx, packet); 2548 } else { 2549 HandleMiniControllerStateL(timestamp, joystick, ctx, packet); 2550 } 2551 } else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) { 2552 if (ctx->device->parent || ctx->m_bVerticalMode) { 2553 HandleCombinedControllerStateR(timestamp, joystick, ctx, packet); 2554 } else { 2555 HandleMiniControllerStateR(timestamp, joystick, ctx, packet); 2556 } 2557 } else { 2558 Sint16 axis; 2559 2560 if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) { 2561 Uint8 data = packet->controllerState.rgucButtons[0]; 2562 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x04) != 0)); 2563 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x08) != 0)); 2564 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x01) != 0)); 2565 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x02) != 0)); 2566 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x40) != 0)); 2567 } 2568 2569 if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) { 2570 Uint8 data = packet->controllerState.rgucButtons[1]; 2571 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0)); 2572 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0)); 2573 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x04) != 0)); 2574 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0)); 2575 2576 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0)); 2577 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0)); 2578 } 2579 2580 if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) { 2581 Uint8 data = packet->controllerState.rgucButtons[2]; 2582 Uint8 hat = 0; 2583 2584 if (data & 0x01) { 2585 hat |= SDL_HAT_DOWN; 2586 } 2587 if (data & 0x02) { 2588 hat |= SDL_HAT_UP; 2589 } 2590 if (data & 0x04) { 2591 hat |= SDL_HAT_RIGHT; 2592 } 2593 if (data & 0x08) { 2594 hat |= SDL_HAT_LEFT; 2595 } 2596 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 2597 2598 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x40) != 0)); 2599 } 2600 2601 axis = (packet->controllerState.rgucButtons[0] & 0x80) ? 32767 : -32768; 2602 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis); 2603 2604 axis = (packet->controllerState.rgucButtons[2] & 0x80) ? 32767 : -32768; 2605 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis); 2606 2607 axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8); 2608 axis = ApplyStickCalibration(ctx, 0, 0, axis); 2609 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis); 2610 2611 axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4); 2612 axis = ApplyStickCalibration(ctx, 0, 1, axis); 2613 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~axis); 2614 2615 axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8); 2616 axis = ApplyStickCalibration(ctx, 1, 0, axis); 2617 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis); 2618 2619 axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4); 2620 axis = ApplyStickCalibration(ctx, 1, 1, axis); 2621 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~axis); 2622 } 2623 2624 /* High nibble of battery/connection byte is battery level, low nibble is connection status (always 0 on 8BitDo Pro 2) 2625 * LSB of connection nibble is USB/Switch connection status 2626 * LSB of the battery nibble is used to report charging. 2627 * The battery level is reported from 0(empty)-8(full) 2628 */ 2629 int charging = (packet->controllerState.ucBatteryAndConnection & 0x10); 2630 int level = (packet->controllerState.ucBatteryAndConnection & 0xE0) >> 4; 2631 if (charging) { 2632 if (level == 8) { 2633 ctx->m_ePowerState = SDL_POWERSTATE_CHARGED; 2634 } else { 2635 ctx->m_ePowerState = SDL_POWERSTATE_CHARGING; 2636 } 2637 } else { 2638 ctx->m_ePowerState = SDL_POWERSTATE_ON_BATTERY; 2639 } 2640 ctx->m_nPowerPercent = (int)SDL_roundf((level / 8.0f) * 100.0f); 2641 2642 if (!ctx->device->parent) { 2643 SDL_PowerState state = ctx->m_ePowerState; 2644 int percent = ctx->m_nPowerPercent; 2645 SDL_SendJoystickPowerInfo(joystick, state, percent); 2646 } else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) { 2647 SDL_DriverSwitch_Context *other = (SDL_DriverSwitch_Context *)ctx->device->parent->children[0]->context; 2648 SDL_PowerState state = (SDL_PowerState)SDL_min(ctx->m_ePowerState, other->m_ePowerState); 2649 int percent = SDL_min(ctx->m_nPowerPercent, other->m_nPowerPercent); 2650 SDL_SendJoystickPowerInfo(joystick, state, percent); 2651 } 2652 2653 if (ctx->m_bReportSensors) { 2654 // Need to copy the imuState to an aligned variable 2655 SwitchControllerIMUState_t imuState[3]; 2656 SDL_COMPILE_TIME_ASSERT(imuState_size, sizeof(imuState) == sizeof(packet->imuState)); 2657 SDL_memcpy(imuState, packet->imuState, sizeof(packet->imuState)); 2658 2659 bool bHasSensorData = (imuState[0].sAccelZ != 0 || 2660 imuState[0].sAccelY != 0 || 2661 imuState[0].sAccelX != 0); 2662 if (bHasSensorData) { 2663 const Uint32 IMU_UPDATE_RATE_SAMPLE_FREQUENCY = 1000; 2664 Uint64 sensor_timestamp[3]; 2665 2666 ctx->m_bHasSensorData = true; 2667 2668 // We got three IMU samples, calculate the IMU update rate and timestamps 2669 ctx->m_unIMUSamples += 3; 2670 if (ctx->m_unIMUSamples >= IMU_UPDATE_RATE_SAMPLE_FREQUENCY) { 2671 Uint64 now = SDL_GetTicksNS(); 2672 Uint64 elapsed = (now - ctx->m_ulIMUSampleTimestampNS); 2673 2674 if (elapsed > 0) { 2675 ctx->m_ulIMUUpdateIntervalNS = elapsed / ctx->m_unIMUSamples; 2676 } 2677 ctx->m_unIMUSamples = 0; 2678 ctx->m_ulIMUSampleTimestampNS = now; 2679 } 2680 2681 ctx->m_ulTimestampNS += ctx->m_ulIMUUpdateIntervalNS; 2682 sensor_timestamp[0] = ctx->m_ulTimestampNS; 2683 ctx->m_ulTimestampNS += ctx->m_ulIMUUpdateIntervalNS; 2684 sensor_timestamp[1] = ctx->m_ulTimestampNS; 2685 ctx->m_ulTimestampNS += ctx->m_ulIMUUpdateIntervalNS; 2686 sensor_timestamp[2] = ctx->m_ulTimestampNS; 2687 2688 if (!ctx->device->parent || 2689 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) { 2690 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO, sensor_timestamp[0], &imuState[2].sGyroX); 2691 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL, sensor_timestamp[0], &imuState[2].sAccelX); 2692 2693 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO, sensor_timestamp[1], &imuState[1].sGyroX); 2694 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL, sensor_timestamp[1], &imuState[1].sAccelX); 2695 2696 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO, sensor_timestamp[2], &imuState[0].sGyroX); 2697 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL, sensor_timestamp[2], &imuState[0].sAccelX); 2698 } 2699 2700 if (ctx->device->parent && 2701 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) { 2702 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_L, sensor_timestamp[0], &imuState[2].sGyroX); 2703 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_L, sensor_timestamp[0], &imuState[2].sAccelX); 2704 2705 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_L, sensor_timestamp[1], &imuState[1].sGyroX); 2706 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_L, sensor_timestamp[1], &imuState[1].sAccelX); 2707 2708 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_L, sensor_timestamp[2], &imuState[0].sGyroX); 2709 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_L, sensor_timestamp[2], &imuState[0].sAccelX); 2710 } 2711 if (ctx->device->parent && 2712 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) { 2713 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_R, sensor_timestamp[0], &imuState[2].sGyroX); 2714 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_R, sensor_timestamp[0], &imuState[2].sAccelX); 2715 2716 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_R, sensor_timestamp[1], &imuState[1].sGyroX); 2717 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_R, sensor_timestamp[1], &imuState[1].sAccelX); 2718 2719 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_R, sensor_timestamp[2], &imuState[0].sGyroX); 2720 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_R, sensor_timestamp[2], &imuState[0].sAccelX); 2721 } 2722 2723 } else if (ctx->m_bHasSensorData) { 2724 // Uh oh, someone turned off the IMU? 2725 const int IMU_RESET_DELAY_MS = 3000; 2726 Uint64 now = SDL_GetTicks(); 2727 2728 if (now >= (ctx->m_ulLastIMUReset + IMU_RESET_DELAY_MS)) { 2729 SetIMUEnabled(ctx, true); 2730 ctx->m_ulLastIMUReset = now; 2731 } 2732 2733 } else { 2734 // We have never gotten IMU data, probably not supported on this device 2735 } 2736 } 2737 2738 ctx->m_lastFullState = *packet; 2739} 2740 2741static bool HIDAPI_DriverSwitch_UpdateDevice(SDL_HIDAPI_Device *device) 2742{ 2743 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context; 2744 SDL_Joystick *joystick = NULL; 2745 int size; 2746 int packet_count = 0; 2747 Uint64 now = SDL_GetTicks(); 2748 2749 if (device->num_joysticks > 0) { 2750 joystick = SDL_GetJoystickFromID(device->joysticks[0]); 2751 } 2752 2753 while ((size = ReadInput(ctx)) > 0) { 2754#ifdef DEBUG_SWITCH_PROTOCOL 2755 HIDAPI_DumpPacket("Nintendo Switch packet: size = %d", ctx->m_rgucReadBuffer, size); 2756#endif 2757 ++packet_count; 2758 ctx->m_ulLastInput = now; 2759 2760 if (!joystick) { 2761 continue; 2762 } 2763 2764 if (ctx->m_bInputOnly) { 2765 HandleInputOnlyControllerState(joystick, ctx, (SwitchInputOnlyControllerStatePacket_t *)&ctx->m_rgucReadBuffer[0]); 2766 } else { 2767 if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) { 2768 continue; 2769 } 2770 2771 ctx->m_nCurrentInputMode = ctx->m_rgucReadBuffer[0]; 2772 2773 switch (ctx->m_rgucReadBuffer[0]) { 2774 case k_eSwitchInputReportIDs_SimpleControllerState: 2775 HandleSimpleControllerState(joystick, ctx, (SwitchSimpleStatePacket_t *)&ctx->m_rgucReadBuffer[1]); 2776 break; 2777 case k_eSwitchInputReportIDs_FullControllerState: 2778 case k_eSwitchInputReportIDs_FullControllerAndMcuState: 2779 // This is the extended report, we can enable sensors now in auto mode 2780 UpdateEnhancedModeOnEnhancedReport(ctx); 2781 2782 HandleFullControllerState(joystick, ctx, (SwitchStatePacket_t *)&ctx->m_rgucReadBuffer[1]); 2783 break; 2784 default: 2785 break; 2786 } 2787 } 2788 } 2789 2790 if (joystick) { 2791 if (packet_count == 0) { 2792 if (!ctx->m_bInputOnly && !device->is_bluetooth && 2793 ctx->device->product_id != USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) { 2794 const int INPUT_WAIT_TIMEOUT_MS = 100; 2795 if (now >= (ctx->m_ulLastInput + INPUT_WAIT_TIMEOUT_MS)) { 2796 // Steam may have put the controller back into non-reporting mode 2797 bool wasSyncWrite = ctx->m_bSyncWrite; 2798 2799 ctx->m_bSyncWrite = true; 2800 WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, false); 2801 ctx->m_bSyncWrite = wasSyncWrite; 2802 } 2803 } else if (device->is_bluetooth && 2804 ctx->m_nCurrentInputMode != k_eSwitchInputReportIDs_SimpleControllerState) { 2805 const int INPUT_WAIT_TIMEOUT_MS = 3000; 2806 if (now >= (ctx->m_ulLastInput + INPUT_WAIT_TIMEOUT_MS)) { 2807 // Bluetooth may have disconnected, try reopening the controller 2808 size = -1; 2809 } 2810 } 2811 } 2812 2813 if (ctx->m_bRumblePending || ctx->m_bRumbleZeroPending) { 2814 HIDAPI_DriverSwitch_SendPendingRumble(ctx); 2815 } else if (ctx->m_bRumbleActive && 2816 now >= (ctx->m_ulRumbleSent + RUMBLE_REFRESH_FREQUENCY_MS)) { 2817#ifdef DEBUG_RUMBLE 2818 SDL_Log("Sent continuing rumble, %d ms after previous rumble", now - ctx->m_ulRumbleSent); 2819#endif 2820 WriteRumble(ctx); 2821 } 2822 } 2823 2824 // Reconnect the Bluetooth device once the USB device is gone 2825 if (device->num_joysticks == 0 && device->is_bluetooth && packet_count > 0 && 2826 !device->parent && 2827 !HIDAPI_HasConnectedUSBDevice(device->serial)) { 2828 HIDAPI_JoystickConnected(device, NULL); 2829 } 2830 2831 if (size < 0 && device->num_joysticks > 0) { 2832 // Read error, device is disconnected 2833 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 2834 } 2835 return (size >= 0); 2836} 2837 2838static void HIDAPI_DriverSwitch_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 2839{ 2840 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context; 2841 2842 if (!ctx->m_bInputOnly) { 2843 // Restore simple input mode for other applications 2844 if (!ctx->m_nInitialInputMode || 2845 ctx->m_nInitialInputMode == k_eSwitchInputReportIDs_SimpleControllerState) { 2846 SetInputMode(ctx, k_eSwitchInputReportIDs_SimpleControllerState); 2847 } 2848 } 2849 2850 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, 2851 SDL_EnhancedReportsChanged, ctx); 2852 2853 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft || 2854 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) { 2855 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED, 2856 SDL_HomeLEDHintChanged, ctx); 2857 } else { 2858 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, 2859 SDL_HomeLEDHintChanged, ctx); 2860 } 2861 2862 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, 2863 SDL_PlayerLEDHintChanged, ctx); 2864 2865 ctx->joystick = NULL; 2866 2867 ctx->m_bReportSensors = false; 2868 ctx->m_bEnhancedMode = false; 2869 ctx->m_bEnhancedModeAvailable = false; 2870} 2871 2872static void HIDAPI_DriverSwitch_FreeDevice(SDL_HIDAPI_Device *device) 2873{ 2874} 2875 2876SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverNintendoClassic = { 2877 SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC, 2878 true, 2879 HIDAPI_DriverNintendoClassic_RegisterHints, 2880 HIDAPI_DriverNintendoClassic_UnregisterHints, 2881 HIDAPI_DriverNintendoClassic_IsEnabled, 2882 HIDAPI_DriverNintendoClassic_IsSupportedDevice, 2883 HIDAPI_DriverSwitch_InitDevice, 2884 HIDAPI_DriverSwitch_GetDevicePlayerIndex, 2885 HIDAPI_DriverSwitch_SetDevicePlayerIndex, 2886 HIDAPI_DriverSwitch_UpdateDevice, 2887 HIDAPI_DriverSwitch_OpenJoystick, 2888 HIDAPI_DriverSwitch_RumbleJoystick, 2889 HIDAPI_DriverSwitch_RumbleJoystickTriggers, 2890 HIDAPI_DriverSwitch_GetJoystickCapabilities, 2891 HIDAPI_DriverSwitch_SetJoystickLED, 2892 HIDAPI_DriverSwitch_SendJoystickEffect, 2893 HIDAPI_DriverSwitch_SetJoystickSensorsEnabled, 2894 HIDAPI_DriverSwitch_CloseJoystick, 2895 HIDAPI_DriverSwitch_FreeDevice, 2896}; 2897 2898SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverJoyCons = { 2899 SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, 2900 true, 2901 HIDAPI_DriverJoyCons_RegisterHints, 2902 HIDAPI_DriverJoyCons_UnregisterHints, 2903 HIDAPI_DriverJoyCons_IsEnabled, 2904 HIDAPI_DriverJoyCons_IsSupportedDevice, 2905 HIDAPI_DriverSwitch_InitDevice, 2906 HIDAPI_DriverSwitch_GetDevicePlayerIndex, 2907 HIDAPI_DriverSwitch_SetDevicePlayerIndex, 2908 HIDAPI_DriverSwitch_UpdateDevice, 2909 HIDAPI_DriverSwitch_OpenJoystick, 2910 HIDAPI_DriverSwitch_RumbleJoystick, 2911 HIDAPI_DriverSwitch_RumbleJoystickTriggers, 2912 HIDAPI_DriverSwitch_GetJoystickCapabilities, 2913 HIDAPI_DriverSwitch_SetJoystickLED, 2914 HIDAPI_DriverSwitch_SendJoystickEffect, 2915 HIDAPI_DriverSwitch_SetJoystickSensorsEnabled, 2916 HIDAPI_DriverSwitch_CloseJoystick, 2917 HIDAPI_DriverSwitch_FreeDevice, 2918}; 2919 2920SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch = { 2921 SDL_HINT_JOYSTICK_HIDAPI_SWITCH, 2922 true, 2923 HIDAPI_DriverSwitch_RegisterHints, 2924 HIDAPI_DriverSwitch_UnregisterHints, 2925 HIDAPI_DriverSwitch_IsEnabled, 2926 HIDAPI_DriverSwitch_IsSupportedDevice, 2927 HIDAPI_DriverSwitch_InitDevice, 2928 HIDAPI_DriverSwitch_GetDevicePlayerIndex, 2929 HIDAPI_DriverSwitch_SetDevicePlayerIndex, 2930 HIDAPI_DriverSwitch_UpdateDevice, 2931 HIDAPI_DriverSwitch_OpenJoystick, 2932 HIDAPI_DriverSwitch_RumbleJoystick, 2933 HIDAPI_DriverSwitch_RumbleJoystickTriggers, 2934 HIDAPI_DriverSwitch_GetJoystickCapabilities, 2935 HIDAPI_DriverSwitch_SetJoystickLED, 2936 HIDAPI_DriverSwitch_SendJoystickEffect, 2937 HIDAPI_DriverSwitch_SetJoystickSensorsEnabled, 2938 HIDAPI_DriverSwitch_CloseJoystick, 2939 HIDAPI_DriverSwitch_FreeDevice, 2940}; 2941 2942#endif // SDL_JOYSTICK_HIDAPI_SWITCH 2943 2944#endif // SDL_JOYSTICK_HIDAPI 2945
[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.