Path: blob/master/thirdparty/sdl/joystick/hidapi/SDL_hidapi_switch.c
9906 views
/*1Simple DirectMedia Layer2Copyright (C) 1997-2025 Sam Lantinga <[email protected]>34This software is provided 'as-is', without any express or implied5warranty. In no event will the authors be held liable for any damages6arising from the use of this software.78Permission is granted to anyone to use this software for any purpose,9including commercial applications, and to alter it and redistribute it10freely, subject to the following restrictions:11121. The origin of this software must not be misrepresented; you must not13claim that you wrote the original software. If you use this software14in a product, an acknowledgment in the product documentation would be15appreciated but is not required.162. Altered source versions must be plainly marked as such, and must not be17misrepresented as being the original software.183. This notice may not be removed or altered from any source distribution.19*/20/* This driver supports the Nintendo Switch Pro controller.21Code and logic contributed by Valve Corporation under the SDL zlib license.22*/23#include "SDL_internal.h"2425#ifdef SDL_JOYSTICK_HIDAPI2627#include "../../SDL_hints_c.h"28#include "../SDL_sysjoystick.h"29#include "SDL_hidapijoystick_c.h"30#include "SDL_hidapi_rumble.h"31#include "SDL_hidapi_nintendo.h"3233#ifdef SDL_JOYSTICK_HIDAPI_SWITCH3435// Define this if you want to log all packets from the controller36// #define DEBUG_SWITCH_PROTOCOL3738// Define this to get log output for rumble logic39// #define DEBUG_RUMBLE4041/* The initialization sequence doesn't appear to work correctly on Windows unless42the reads and writes are on the same thread.4344... and now I can't reproduce this, so I'm leaving it in, but disabled for now.45*/46// #define SWITCH_SYNCHRONOUS_WRITES4748/* How often you can write rumble commands to the controller.49If you send commands more frequently than this, you can turn off the controller50in Bluetooth mode, or the motors can miss the command in USB mode.51*/52#define RUMBLE_WRITE_FREQUENCY_MS 305354// How often you have to refresh a long duration rumble to keep the motors running55#define RUMBLE_REFRESH_FREQUENCY_MS 505657#define SWITCH_GYRO_SCALE 14.2842f58#define SWITCH_ACCEL_SCALE 4096.f5960#define SWITCH_GYRO_SCALE_OFFSET 13371.0f61#define SWITCH_GYRO_SCALE_MULT 936.0f62#define SWITCH_ACCEL_SCALE_OFFSET 16384.0f63#define SWITCH_ACCEL_SCALE_MULT 4.0f6465enum66{67SDL_GAMEPAD_BUTTON_SWITCH_SHARE = 11,68SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1,69SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1,70SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2,71SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2,72SDL_GAMEPAD_NUM_SWITCH_BUTTONS,73};7475typedef enum76{77k_eSwitchInputReportIDs_SubcommandReply = 0x21,78k_eSwitchInputReportIDs_FullControllerState = 0x30,79k_eSwitchInputReportIDs_FullControllerAndMcuState = 0x31,80k_eSwitchInputReportIDs_SimpleControllerState = 0x3F,81k_eSwitchInputReportIDs_CommandAck = 0x81,82} ESwitchInputReportIDs;8384typedef enum85{86k_eSwitchOutputReportIDs_RumbleAndSubcommand = 0x01,87k_eSwitchOutputReportIDs_Rumble = 0x10,88k_eSwitchOutputReportIDs_Proprietary = 0x80,89} ESwitchOutputReportIDs;9091typedef enum92{93k_eSwitchSubcommandIDs_BluetoothManualPair = 0x01,94k_eSwitchSubcommandIDs_RequestDeviceInfo = 0x02,95k_eSwitchSubcommandIDs_SetInputReportMode = 0x03,96k_eSwitchSubcommandIDs_SetHCIState = 0x06,97k_eSwitchSubcommandIDs_SPIFlashRead = 0x10,98k_eSwitchSubcommandIDs_SetPlayerLights = 0x30,99k_eSwitchSubcommandIDs_SetHomeLight = 0x38,100k_eSwitchSubcommandIDs_EnableIMU = 0x40,101k_eSwitchSubcommandIDs_SetIMUSensitivity = 0x41,102k_eSwitchSubcommandIDs_EnableVibration = 0x48,103} ESwitchSubcommandIDs;104105typedef enum106{107k_eSwitchProprietaryCommandIDs_Status = 0x01,108k_eSwitchProprietaryCommandIDs_Handshake = 0x02,109k_eSwitchProprietaryCommandIDs_HighSpeed = 0x03,110k_eSwitchProprietaryCommandIDs_ForceUSB = 0x04,111k_eSwitchProprietaryCommandIDs_ClearUSB = 0x05,112k_eSwitchProprietaryCommandIDs_ResetMCU = 0x06,113} ESwitchProprietaryCommandIDs;114115#define k_unSwitchOutputPacketDataLength 49116#define k_unSwitchMaxOutputPacketLength 64117#define k_unSwitchBluetoothPacketLength k_unSwitchOutputPacketDataLength118#define k_unSwitchUSBPacketLength k_unSwitchMaxOutputPacketLength119120#define k_unSPIStickFactoryCalibrationStartOffset 0x603D121#define k_unSPIStickFactoryCalibrationEndOffset 0x604E122#define k_unSPIStickFactoryCalibrationLength (k_unSPIStickFactoryCalibrationEndOffset - k_unSPIStickFactoryCalibrationStartOffset + 1)123124#define k_unSPIStickUserCalibrationStartOffset 0x8010125#define k_unSPIStickUserCalibrationEndOffset 0x8025126#define k_unSPIStickUserCalibrationLength (k_unSPIStickUserCalibrationEndOffset - k_unSPIStickUserCalibrationStartOffset + 1)127128#define k_unSPIIMUScaleStartOffset 0x6020129#define k_unSPIIMUScaleEndOffset 0x6037130#define k_unSPIIMUScaleLength (k_unSPIIMUScaleEndOffset - k_unSPIIMUScaleStartOffset + 1)131132#define k_unSPIIMUUserScaleStartOffset 0x8026133#define k_unSPIIMUUserScaleEndOffset 0x8039134#define k_unSPIIMUUserScaleLength (k_unSPIIMUUserScaleEndOffset - k_unSPIIMUUserScaleStartOffset + 1)135136#pragma pack(1)137typedef struct138{139Uint8 rgucButtons[2];140Uint8 ucStickHat;141Uint8 rgucJoystickLeft[2];142Uint8 rgucJoystickRight[2];143} SwitchInputOnlyControllerStatePacket_t;144145typedef struct146{147Uint8 rgucButtons[2];148Uint8 ucStickHat;149Sint16 sJoystickLeft[2];150Sint16 sJoystickRight[2];151} SwitchSimpleStatePacket_t;152153typedef struct154{155Uint8 ucCounter;156Uint8 ucBatteryAndConnection;157Uint8 rgucButtons[3];158Uint8 rgucJoystickLeft[3];159Uint8 rgucJoystickRight[3];160Uint8 ucVibrationCode;161} SwitchControllerStatePacket_t;162163typedef struct164{165SwitchControllerStatePacket_t controllerState;166167struct168{169Sint16 sAccelX;170Sint16 sAccelY;171Sint16 sAccelZ;172173Sint16 sGyroX;174Sint16 sGyroY;175Sint16 sGyroZ;176} imuState[3];177} SwitchStatePacket_t;178179typedef struct180{181Uint32 unAddress;182Uint8 ucLength;183} SwitchSPIOpData_t;184185typedef struct186{187SwitchControllerStatePacket_t m_controllerState;188189Uint8 ucSubcommandAck;190Uint8 ucSubcommandID;191192#define k_unSubcommandDataBytes 35193union194{195Uint8 rgucSubcommandData[k_unSubcommandDataBytes];196197struct198{199SwitchSPIOpData_t opData;200Uint8 rgucReadData[k_unSubcommandDataBytes - sizeof(SwitchSPIOpData_t)];201} spiReadData;202203struct204{205Uint8 rgucFirmwareVersion[2];206Uint8 ucDeviceType;207Uint8 ucFiller1;208Uint8 rgucMACAddress[6];209Uint8 ucFiller2;210Uint8 ucColorLocation;211} deviceInfo;212213struct214{215SwitchSPIOpData_t opData;216Uint8 rgucLeftCalibration[9];217Uint8 rgucRightCalibration[9];218} stickFactoryCalibration;219220struct221{222SwitchSPIOpData_t opData;223Uint8 rgucLeftMagic[2];224Uint8 rgucLeftCalibration[9];225Uint8 rgucRightMagic[2];226Uint8 rgucRightCalibration[9];227} stickUserCalibration;228};229} SwitchSubcommandInputPacket_t;230231typedef struct232{233Uint8 ucPacketType;234Uint8 ucCommandID;235Uint8 ucFiller;236237Uint8 ucDeviceType;238Uint8 rgucMACAddress[6];239} SwitchProprietaryStatusPacket_t;240241typedef struct242{243Uint8 rgucData[4];244} SwitchRumbleData_t;245246typedef struct247{248Uint8 ucPacketType;249Uint8 ucPacketNumber;250SwitchRumbleData_t rumbleData[2];251} SwitchCommonOutputPacket_t;252253typedef struct254{255SwitchCommonOutputPacket_t commonData;256257Uint8 ucSubcommandID;258Uint8 rgucSubcommandData[k_unSwitchOutputPacketDataLength - sizeof(SwitchCommonOutputPacket_t) - 1];259} SwitchSubcommandOutputPacket_t;260261typedef struct262{263Uint8 ucPacketType;264Uint8 ucProprietaryID;265266Uint8 rgucProprietaryData[k_unSwitchOutputPacketDataLength - 1 - 1];267} SwitchProprietaryOutputPacket_t;268#pragma pack()269270/* Enhanced report hint mode:271* "0": enhanced features are never used272* "1": enhanced features are always used273* "auto": enhanced features are advertised to the application, but SDL doesn't touch the controller state unless the application explicitly requests it.274*/275typedef enum276{277SWITCH_ENHANCED_REPORT_HINT_OFF,278SWITCH_ENHANCED_REPORT_HINT_ON,279SWITCH_ENHANCED_REPORT_HINT_AUTO280} HIDAPI_Switch_EnhancedReportHint;281282typedef struct283{284SDL_HIDAPI_Device *device;285SDL_Joystick *joystick;286bool m_bInputOnly;287bool m_bUseButtonLabels;288bool m_bPlayerLights;289int m_nPlayerIndex;290bool m_bSyncWrite;291int m_nMaxWriteAttempts;292ESwitchDeviceInfoControllerType m_eControllerType;293Uint8 m_nInitialInputMode;294Uint8 m_nCurrentInputMode;295Uint8 m_rgucMACAddress[6];296Uint8 m_nCommandNumber;297HIDAPI_Switch_EnhancedReportHint m_eEnhancedReportHint;298bool m_bEnhancedMode;299bool m_bEnhancedModeAvailable;300SwitchCommonOutputPacket_t m_RumblePacket;301Uint8 m_rgucReadBuffer[k_unSwitchMaxOutputPacketLength];302bool m_bRumbleActive;303Uint64 m_ulRumbleSent;304bool m_bRumblePending;305bool m_bRumbleZeroPending;306Uint32 m_unRumblePending;307bool m_bSensorsSupported;308bool m_bReportSensors;309bool m_bHasSensorData;310Uint64 m_ulLastInput;311Uint64 m_ulLastIMUReset;312Uint64 m_ulIMUSampleTimestampNS;313Uint32 m_unIMUSamples;314Uint64 m_ulIMUUpdateIntervalNS;315Uint64 m_ulTimestampNS;316bool m_bVerticalMode;317318SwitchInputOnlyControllerStatePacket_t m_lastInputOnlyState;319SwitchSimpleStatePacket_t m_lastSimpleState;320SwitchStatePacket_t m_lastFullState;321322struct StickCalibrationData323{324struct325{326Sint16 sCenter;327Sint16 sMin;328Sint16 sMax;329} axis[2];330} m_StickCalData[2];331332struct StickExtents333{334struct335{336Sint16 sMin;337Sint16 sMax;338} axis[2];339} m_StickExtents[2], m_SimpleStickExtents[2];340341struct IMUScaleData342{343float fAccelScaleX;344float fAccelScaleY;345float fAccelScaleZ;346347float fGyroScaleX;348float fGyroScaleY;349float fGyroScaleZ;350} m_IMUScaleData;351} SDL_DriverSwitch_Context;352353static int ReadInput(SDL_DriverSwitch_Context *ctx)354{355int result;356357// Make sure we don't try to read at the same time a write is happening358if (SDL_GetAtomicInt(&ctx->device->rumble_pending) > 0) {359return 0;360}361362result = SDL_hid_read_timeout(ctx->device->dev, ctx->m_rgucReadBuffer, sizeof(ctx->m_rgucReadBuffer), 0);363364// See if we can guess the initial input mode365if (result > 0 && !ctx->m_bInputOnly && !ctx->m_nInitialInputMode) {366switch (ctx->m_rgucReadBuffer[0]) {367case k_eSwitchInputReportIDs_FullControllerState:368case k_eSwitchInputReportIDs_FullControllerAndMcuState:369case k_eSwitchInputReportIDs_SimpleControllerState:370ctx->m_nInitialInputMode = ctx->m_rgucReadBuffer[0];371break;372default:373break;374}375}376return result;377}378379static int WriteOutput(SDL_DriverSwitch_Context *ctx, const Uint8 *data, int size)380{381#ifdef SWITCH_SYNCHRONOUS_WRITES382return SDL_hid_write(ctx->device->dev, data, size);383#else384// Use the rumble thread for general asynchronous writes385if (!SDL_HIDAPI_LockRumble()) {386return -1;387}388return SDL_HIDAPI_SendRumbleAndUnlock(ctx->device, data, size);389#endif // SWITCH_SYNCHRONOUS_WRITES390}391392static SwitchSubcommandInputPacket_t *ReadSubcommandReply(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs expectedID)393{394// Average response time for messages is ~30ms395Uint64 endTicks = SDL_GetTicks() + 100;396397int nRead = 0;398while ((nRead = ReadInput(ctx)) != -1) {399if (nRead > 0) {400if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) {401SwitchSubcommandInputPacket_t *reply = (SwitchSubcommandInputPacket_t *)&ctx->m_rgucReadBuffer[1];402if (reply->ucSubcommandID == expectedID && (reply->ucSubcommandAck & 0x80)) {403return reply;404}405}406} else {407SDL_Delay(1);408}409410if (SDL_GetTicks() >= endTicks) {411break;412}413}414return NULL;415}416417static bool ReadProprietaryReply(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs expectedID)418{419// Average response time for messages is ~30ms420Uint64 endTicks = SDL_GetTicks() + 100;421422int nRead = 0;423while ((nRead = ReadInput(ctx)) != -1) {424if (nRead > 0) {425if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_CommandAck && ctx->m_rgucReadBuffer[1] == expectedID) {426return true;427}428} else {429SDL_Delay(1);430}431432if (SDL_GetTicks() >= endTicks) {433break;434}435}436return false;437}438439static void ConstructSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, const Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandOutputPacket_t *outPacket)440{441SDL_memset(outPacket, 0, sizeof(*outPacket));442443outPacket->commonData.ucPacketType = k_eSwitchOutputReportIDs_RumbleAndSubcommand;444outPacket->commonData.ucPacketNumber = ctx->m_nCommandNumber;445446SDL_memcpy(outPacket->commonData.rumbleData, ctx->m_RumblePacket.rumbleData, sizeof(ctx->m_RumblePacket.rumbleData));447448outPacket->ucSubcommandID = ucCommandID;449if (pBuf) {450SDL_memcpy(outPacket->rgucSubcommandData, pBuf, ucLen);451}452453ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;454}455456static bool WritePacket(SDL_DriverSwitch_Context *ctx, void *pBuf, Uint8 ucLen)457{458Uint8 rgucBuf[k_unSwitchMaxOutputPacketLength];459const size_t unWriteSize = ctx->device->is_bluetooth ? k_unSwitchBluetoothPacketLength : k_unSwitchUSBPacketLength;460461if (ucLen > k_unSwitchOutputPacketDataLength) {462return false;463}464465if (ucLen < unWriteSize) {466SDL_memcpy(rgucBuf, pBuf, ucLen);467SDL_memset(rgucBuf + ucLen, 0, unWriteSize - ucLen);468pBuf = rgucBuf;469ucLen = (Uint8)unWriteSize;470}471if (ctx->m_bSyncWrite) {472return SDL_hid_write(ctx->device->dev, (Uint8 *)pBuf, ucLen) >= 0;473} else {474return WriteOutput(ctx, (Uint8 *)pBuf, ucLen) >= 0;475}476}477478static bool WriteSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, const Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandInputPacket_t **ppReply)479{480SwitchSubcommandInputPacket_t *reply = NULL;481int nTries;482483for (nTries = 1; !reply && nTries <= ctx->m_nMaxWriteAttempts; ++nTries) {484SwitchSubcommandOutputPacket_t commandPacket;485ConstructSubcommand(ctx, ucCommandID, pBuf, ucLen, &commandPacket);486487if (!WritePacket(ctx, &commandPacket, sizeof(commandPacket))) {488continue;489}490491reply = ReadSubcommandReply(ctx, ucCommandID);492}493494if (ppReply) {495*ppReply = reply;496}497return reply != NULL;498}499500static bool WriteProprietary(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs ucCommand, Uint8 *pBuf, Uint8 ucLen, bool waitForReply)501{502int nTries;503504for (nTries = 1; nTries <= ctx->m_nMaxWriteAttempts; ++nTries) {505SwitchProprietaryOutputPacket_t packet;506507if ((!pBuf && ucLen > 0) || ucLen > sizeof(packet.rgucProprietaryData)) {508return false;509}510511SDL_zero(packet);512packet.ucPacketType = k_eSwitchOutputReportIDs_Proprietary;513packet.ucProprietaryID = ucCommand;514if (pBuf) {515SDL_memcpy(packet.rgucProprietaryData, pBuf, ucLen);516}517518if (!WritePacket(ctx, &packet, sizeof(packet))) {519continue;520}521522if (!waitForReply || ReadProprietaryReply(ctx, ucCommand)) {523// SDL_Log("Succeeded%s after %d tries", ctx->m_bSyncWrite ? " (sync)" : "", nTries);524return true;525}526}527// SDL_Log("Failed%s after %d tries", ctx->m_bSyncWrite ? " (sync)" : "", nTries);528return false;529}530531static Uint8 EncodeRumbleHighAmplitude(Uint16 amplitude)532{533/* More information about these values can be found here:534* https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md535*/536Uint16 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 } };537int index = 0;538for (; index < 101; index++) {539if (amplitude <= hfa[index][0]) {540return (Uint8)hfa[index][1];541}542}543return (Uint8)hfa[100][1];544}545546static Uint16 EncodeRumbleLowAmplitude(Uint16 amplitude)547{548/* More information about these values can be found here:549* https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md550*/551Uint16 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 } };552int index = 0;553for (; index < 101; index++) {554if (amplitude <= lfa[index][0]) {555return lfa[index][1];556}557}558return lfa[100][1];559}560561static void SetNeutralRumble(SwitchRumbleData_t *pRumble)562{563pRumble->rgucData[0] = 0x00;564pRumble->rgucData[1] = 0x01;565pRumble->rgucData[2] = 0x40;566pRumble->rgucData[3] = 0x40;567}568569static void EncodeRumble(SwitchRumbleData_t *pRumble, Uint16 usHighFreq, Uint8 ucHighFreqAmp, Uint8 ucLowFreq, Uint16 usLowFreqAmp)570{571if (ucHighFreqAmp > 0 || usLowFreqAmp > 0) {572// High-band frequency and low-band amplitude are actually nine-bits each so they573// take a bit from the high-band amplitude and low-band frequency bytes respectively574pRumble->rgucData[0] = usHighFreq & 0xFF;575pRumble->rgucData[1] = ucHighFreqAmp | ((usHighFreq >> 8) & 0x01);576577pRumble->rgucData[2] = ucLowFreq | ((usLowFreqAmp >> 8) & 0x80);578pRumble->rgucData[3] = usLowFreqAmp & 0xFF;579580#ifdef DEBUG_RUMBLE581SDL_Log("Freq: %.2X %.2X %.2X, Amp: %.2X %.2X %.2X",582usHighFreq & 0xFF, ((usHighFreq >> 8) & 0x01), ucLowFreq,583ucHighFreqAmp, ((usLowFreqAmp >> 8) & 0x80), usLowFreqAmp & 0xFF);584#endif585} else {586SetNeutralRumble(pRumble);587}588}589590static bool WriteRumble(SDL_DriverSwitch_Context *ctx)591{592/* Write into m_RumblePacket rather than a temporary buffer to allow the current rumble state593* to be retained for subsequent rumble or subcommand packets sent to the controller594*/595ctx->m_RumblePacket.ucPacketType = k_eSwitchOutputReportIDs_Rumble;596ctx->m_RumblePacket.ucPacketNumber = ctx->m_nCommandNumber;597ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;598599// Refresh the rumble state periodically600ctx->m_ulRumbleSent = SDL_GetTicks();601602return WritePacket(ctx, (Uint8 *)&ctx->m_RumblePacket, sizeof(ctx->m_RumblePacket));603}604605static ESwitchDeviceInfoControllerType CalculateControllerType(SDL_DriverSwitch_Context *ctx, ESwitchDeviceInfoControllerType eControllerType)606{607SDL_HIDAPI_Device *device = ctx->device;608609// The N64 controller reports as a Pro controller over USB610if (eControllerType == k_eSwitchDeviceInfoControllerType_ProController &&611device->product_id == USB_PRODUCT_NINTENDO_N64_CONTROLLER) {612eControllerType = k_eSwitchDeviceInfoControllerType_N64;613}614615if (eControllerType == k_eSwitchDeviceInfoControllerType_Unknown) {616// This might be a Joy-Con that's missing from a charging grip slot617if (device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {618if (device->interface_number == 1) {619eControllerType = k_eSwitchDeviceInfoControllerType_JoyConLeft;620} else {621eControllerType = k_eSwitchDeviceInfoControllerType_JoyConRight;622}623}624}625return eControllerType;626}627628static bool BReadDeviceInfo(SDL_DriverSwitch_Context *ctx)629{630SwitchSubcommandInputPacket_t *reply = NULL;631632if (ctx->device->is_bluetooth) {633if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_RequestDeviceInfo, NULL, 0, &reply)) {634// Byte 2: Controller ID (1=LJC, 2=RJC, 3=Pro)635ctx->m_eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)reply->deviceInfo.ucDeviceType);636637// Bytes 4-9: MAC address (big-endian)638SDL_memcpy(ctx->m_rgucMACAddress, reply->deviceInfo.rgucMACAddress, sizeof(ctx->m_rgucMACAddress));639640return true;641}642} else {643if (WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Status, NULL, 0, true)) {644SwitchProprietaryStatusPacket_t *status = (SwitchProprietaryStatusPacket_t *)&ctx->m_rgucReadBuffer[0];645size_t i;646647ctx->m_eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)status->ucDeviceType);648649for (i = 0; i < sizeof(ctx->m_rgucMACAddress); ++i) {650ctx->m_rgucMACAddress[i] = status->rgucMACAddress[sizeof(ctx->m_rgucMACAddress) - i - 1];651}652653return true;654}655}656return false;657}658659static bool BTrySetupUSB(SDL_DriverSwitch_Context *ctx)660{661/* We have to send a connection handshake to the controller when communicating over USB662* before we're able to send it other commands. Luckily this command is not supported663* over Bluetooth, so we can use the controller's lack of response as a way to664* determine if the connection is over USB or Bluetooth665*/666if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, true)) {667return false;668}669if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_HighSpeed, NULL, 0, true)) {670// The 8BitDo M30 and SF30 Pro don't respond to this command, but otherwise work correctly671// return false;672}673if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, true)) {674// This fails on the right Joy-Con when plugged into the charging grip675// return false;676}677if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, false)) {678return false;679}680return true;681}682683static bool SetVibrationEnabled(SDL_DriverSwitch_Context *ctx, Uint8 enabled)684{685return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_EnableVibration, &enabled, sizeof(enabled), NULL);686}687static bool SetInputMode(SDL_DriverSwitch_Context *ctx, Uint8 input_mode)688{689#ifdef FORCE_SIMPLE_REPORTS690input_mode = k_eSwitchInputReportIDs_SimpleControllerState;691#endif692#ifdef FORCE_FULL_REPORTS693input_mode = k_eSwitchInputReportIDs_FullControllerState;694#endif695696if (input_mode == ctx->m_nCurrentInputMode) {697return true;698} else {699ctx->m_nCurrentInputMode = input_mode;700701return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetInputReportMode, &input_mode, sizeof(input_mode), NULL);702}703}704705static bool SetHomeLED(SDL_DriverSwitch_Context *ctx, Uint8 brightness)706{707Uint8 ucLedIntensity = 0;708Uint8 rgucBuffer[4];709710if (brightness > 0) {711if (brightness < 65) {712ucLedIntensity = (brightness + 5) / 10;713} else {714ucLedIntensity = (Uint8)SDL_ceilf(0xF * SDL_powf((float)brightness / 100.f, 2.13f));715}716}717718rgucBuffer[0] = (0x0 << 4) | 0x1; // 0 mini cycles (besides first), cycle duration 8ms719rgucBuffer[1] = ((ucLedIntensity & 0xF) << 4) | 0x0; // LED start intensity (0x0-0xF), 0 cycles (LED stays on at start intensity after first cycle)720rgucBuffer[2] = ((ucLedIntensity & 0xF) << 4) | 0x0; // First cycle LED intensity, 0x0 intensity for second cycle721rgucBuffer[3] = (0x0 << 4) | 0x0; // 8ms fade transition to first cycle, 8ms first cycle LED duration722723return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetHomeLight, rgucBuffer, sizeof(rgucBuffer), NULL);724}725726static void SDLCALL SDL_HomeLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)727{728SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)userdata;729730if (hint && *hint) {731int value;732733if (SDL_strchr(hint, '.') != NULL) {734value = (int)(100.0f * SDL_atof(hint));735if (value > 255) {736value = 255;737}738} else if (SDL_GetStringBoolean(hint, true)) {739value = 100;740} else {741value = 0;742}743SetHomeLED(ctx, (Uint8)value);744}745}746747static void UpdateSlotLED(SDL_DriverSwitch_Context *ctx)748{749if (!ctx->m_bInputOnly) {750Uint8 led_data = 0;751752if (ctx->m_bPlayerLights && ctx->m_nPlayerIndex >= 0) {753led_data = (1 << (ctx->m_nPlayerIndex % 4));754}755WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetPlayerLights, &led_data, sizeof(led_data), NULL);756}757}758759static void SDLCALL SDL_PlayerLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)760{761SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)userdata;762bool bPlayerLights = SDL_GetStringBoolean(hint, true);763764if (bPlayerLights != ctx->m_bPlayerLights) {765ctx->m_bPlayerLights = bPlayerLights;766767UpdateSlotLED(ctx);768HIDAPI_UpdateDeviceProperties(ctx->device);769}770}771772static void GetInitialInputMode(SDL_DriverSwitch_Context *ctx)773{774if (!ctx->m_nInitialInputMode) {775// This will set the initial input mode if it can776ReadInput(ctx);777}778}779780static Uint8 GetDefaultInputMode(SDL_DriverSwitch_Context *ctx)781{782Uint8 input_mode;783784// Determine the desired input mode785if (ctx->m_nInitialInputMode) {786input_mode = ctx->m_nInitialInputMode;787} else {788if (ctx->device->is_bluetooth) {789input_mode = k_eSwitchInputReportIDs_SimpleControllerState;790} else {791input_mode = k_eSwitchInputReportIDs_FullControllerState;792}793}794795switch (ctx->m_eEnhancedReportHint) {796case SWITCH_ENHANCED_REPORT_HINT_OFF:797input_mode = k_eSwitchInputReportIDs_SimpleControllerState;798break;799case SWITCH_ENHANCED_REPORT_HINT_ON:800if (input_mode == k_eSwitchInputReportIDs_SimpleControllerState) {801input_mode = k_eSwitchInputReportIDs_FullControllerState;802}803break;804case SWITCH_ENHANCED_REPORT_HINT_AUTO:805/* Joy-Con controllers switch their thumbsticks into D-pad mode in simple mode,806* so let's enable full controller state for them.807*/808if (ctx->device->vendor_id == USB_VENDOR_NINTENDO &&809(ctx->device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT ||810ctx->device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT)) {811input_mode = k_eSwitchInputReportIDs_FullControllerState;812}813break;814}815816// Wired controllers break if they are put into simple controller state817if (input_mode == k_eSwitchInputReportIDs_SimpleControllerState &&818!ctx->device->is_bluetooth) {819input_mode = k_eSwitchInputReportIDs_FullControllerState;820}821return input_mode;822}823824static Uint8 GetSensorInputMode(SDL_DriverSwitch_Context *ctx)825{826Uint8 input_mode;827828// Determine the desired input mode829if (!ctx->m_nInitialInputMode ||830ctx->m_nInitialInputMode == k_eSwitchInputReportIDs_SimpleControllerState) {831input_mode = k_eSwitchInputReportIDs_FullControllerState;832} else {833input_mode = ctx->m_nInitialInputMode;834}835return input_mode;836}837838static void UpdateInputMode(SDL_DriverSwitch_Context *ctx)839{840Uint8 input_mode;841842if (ctx->m_bReportSensors) {843input_mode = GetSensorInputMode(ctx);844} else {845input_mode = GetDefaultInputMode(ctx);846}847SetInputMode(ctx, input_mode);848}849850static void SetEnhancedModeAvailable(SDL_DriverSwitch_Context *ctx)851{852if (ctx->m_bEnhancedModeAvailable) {853return;854}855ctx->m_bEnhancedModeAvailable = true;856857if (ctx->m_bSensorsSupported) {858// Use the right sensor in the combined Joy-Con pair859if (!ctx->device->parent ||860ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {861SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO, 200.0f);862SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, 200.0f);863}864if (ctx->device->parent &&865ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {866SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO_L, 200.0f);867SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL_L, 200.0f);868}869if (ctx->device->parent &&870ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {871SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO_R, 200.0f);872SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL_R, 200.0f);873}874}875}876877static void SetEnhancedReportHint(SDL_DriverSwitch_Context *ctx, HIDAPI_Switch_EnhancedReportHint eEnhancedReportHint)878{879ctx->m_eEnhancedReportHint = eEnhancedReportHint;880881switch (eEnhancedReportHint) {882case SWITCH_ENHANCED_REPORT_HINT_OFF:883ctx->m_bEnhancedMode = false;884break;885case SWITCH_ENHANCED_REPORT_HINT_ON:886SetEnhancedModeAvailable(ctx);887ctx->m_bEnhancedMode = true;888break;889case SWITCH_ENHANCED_REPORT_HINT_AUTO:890SetEnhancedModeAvailable(ctx);891break;892}893894UpdateInputMode(ctx);895}896897static void UpdateEnhancedModeOnEnhancedReport(SDL_DriverSwitch_Context *ctx)898{899if (ctx->m_eEnhancedReportHint == SWITCH_ENHANCED_REPORT_HINT_AUTO) {900SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_ON);901}902}903904static void UpdateEnhancedModeOnApplicationUsage(SDL_DriverSwitch_Context *ctx)905{906if (ctx->m_eEnhancedReportHint == SWITCH_ENHANCED_REPORT_HINT_AUTO) {907SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_ON);908}909}910911static void SDLCALL SDL_EnhancedReportsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)912{913SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)userdata;914915if (hint && SDL_strcasecmp(hint, "auto") == 0) {916SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_AUTO);917} else if (SDL_GetStringBoolean(hint, true)) {918SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_ON);919} else {920SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_OFF);921}922}923924static bool SetIMUEnabled(SDL_DriverSwitch_Context *ctx, bool enabled)925{926Uint8 imu_data = enabled ? 1 : 0;927return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_EnableIMU, &imu_data, sizeof(imu_data), NULL);928}929930static bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)931{932Uint8 *pLeftStickCal;933Uint8 *pRightStickCal;934size_t stick, axis;935SwitchSubcommandInputPacket_t *user_reply = NULL;936SwitchSubcommandInputPacket_t *factory_reply = NULL;937SwitchSPIOpData_t readUserParams;938SwitchSPIOpData_t readFactoryParams;939940// Read User Calibration Info941readUserParams.unAddress = k_unSPIStickUserCalibrationStartOffset;942readUserParams.ucLength = k_unSPIStickUserCalibrationLength;943944// This isn't readable on all controllers, so ignore failure945WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readUserParams, sizeof(readUserParams), &user_reply);946947// Read Factory Calibration Info948readFactoryParams.unAddress = k_unSPIStickFactoryCalibrationStartOffset;949readFactoryParams.ucLength = k_unSPIStickFactoryCalibrationLength;950951const int MAX_ATTEMPTS = 3;952for (int attempt = 0; ; ++attempt) {953if (!WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readFactoryParams, sizeof(readFactoryParams), &factory_reply)) {954return false;955}956957if (factory_reply->stickFactoryCalibration.opData.unAddress == k_unSPIStickFactoryCalibrationStartOffset) {958// We successfully read the calibration data959break;960}961962if (attempt == MAX_ATTEMPTS) {963return false;964}965}966967// Automatically select the user calibration if magic bytes are set968if (user_reply && user_reply->stickUserCalibration.rgucLeftMagic[0] == 0xB2 && user_reply->stickUserCalibration.rgucLeftMagic[1] == 0xA1) {969pLeftStickCal = user_reply->stickUserCalibration.rgucLeftCalibration;970} else {971pLeftStickCal = factory_reply->stickFactoryCalibration.rgucLeftCalibration;972}973974if (user_reply && user_reply->stickUserCalibration.rgucRightMagic[0] == 0xB2 && user_reply->stickUserCalibration.rgucRightMagic[1] == 0xA1) {975pRightStickCal = user_reply->stickUserCalibration.rgucRightCalibration;976} else {977pRightStickCal = factory_reply->stickFactoryCalibration.rgucRightCalibration;978}979980/* Stick calibration values are 12-bits each and are packed by bit981* For whatever reason the fields are in a different order for each stick982* Left: X-Max, Y-Max, X-Center, Y-Center, X-Min, Y-Min983* Right: X-Center, Y-Center, X-Min, Y-Min, X-Max, Y-Max984*/985986// Left stick987ctx->m_StickCalData[0].axis[0].sMax = ((pLeftStickCal[1] << 8) & 0xF00) | pLeftStickCal[0]; // X Axis max above center988ctx->m_StickCalData[0].axis[1].sMax = (pLeftStickCal[2] << 4) | (pLeftStickCal[1] >> 4); // Y Axis max above center989ctx->m_StickCalData[0].axis[0].sCenter = ((pLeftStickCal[4] << 8) & 0xF00) | pLeftStickCal[3]; // X Axis center990ctx->m_StickCalData[0].axis[1].sCenter = (pLeftStickCal[5] << 4) | (pLeftStickCal[4] >> 4); // Y Axis center991ctx->m_StickCalData[0].axis[0].sMin = ((pLeftStickCal[7] << 8) & 0xF00) | pLeftStickCal[6]; // X Axis min below center992ctx->m_StickCalData[0].axis[1].sMin = (pLeftStickCal[8] << 4) | (pLeftStickCal[7] >> 4); // Y Axis min below center993994// Right stick995ctx->m_StickCalData[1].axis[0].sCenter = ((pRightStickCal[1] << 8) & 0xF00) | pRightStickCal[0]; // X Axis center996ctx->m_StickCalData[1].axis[1].sCenter = (pRightStickCal[2] << 4) | (pRightStickCal[1] >> 4); // Y Axis center997ctx->m_StickCalData[1].axis[0].sMin = ((pRightStickCal[4] << 8) & 0xF00) | pRightStickCal[3]; // X Axis min below center998ctx->m_StickCalData[1].axis[1].sMin = (pRightStickCal[5] << 4) | (pRightStickCal[4] >> 4); // Y Axis min below center999ctx->m_StickCalData[1].axis[0].sMax = ((pRightStickCal[7] << 8) & 0xF00) | pRightStickCal[6]; // X Axis max above center1000ctx->m_StickCalData[1].axis[1].sMax = (pRightStickCal[8] << 4) | (pRightStickCal[7] >> 4); // Y Axis max above center10011002// Filter out any values that were uninitialized (0xFFF) in the SPI read1003for (stick = 0; stick < 2; ++stick) {1004for (axis = 0; axis < 2; ++axis) {1005if (ctx->m_StickCalData[stick].axis[axis].sCenter == 0xFFF) {1006ctx->m_StickCalData[stick].axis[axis].sCenter = 2048;1007}1008if (ctx->m_StickCalData[stick].axis[axis].sMax == 0xFFF) {1009ctx->m_StickCalData[stick].axis[axis].sMax = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sCenter * 0.7f);1010}1011if (ctx->m_StickCalData[stick].axis[axis].sMin == 0xFFF) {1012ctx->m_StickCalData[stick].axis[axis].sMin = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sCenter * 0.7f);1013}1014}1015}10161017for (stick = 0; stick < 2; ++stick) {1018for (axis = 0; axis < 2; ++axis) {1019ctx->m_StickExtents[stick].axis[axis].sMin = -(Sint16)(ctx->m_StickCalData[stick].axis[axis].sMin * 0.7f);1020ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sMax * 0.7f);1021}1022}10231024for (stick = 0; stick < 2; ++stick) {1025for (axis = 0; axis < 2; ++axis) {1026ctx->m_SimpleStickExtents[stick].axis[axis].sMin = (Sint16)(SDL_MIN_SINT16 * 0.5f);1027ctx->m_SimpleStickExtents[stick].axis[axis].sMax = (Sint16)(SDL_MAX_SINT16 * 0.5f);1028}1029}10301031return true;1032}10331034static bool LoadIMUCalibration(SDL_DriverSwitch_Context *ctx)1035{1036SwitchSubcommandInputPacket_t *reply = NULL;10371038// Read Calibration Info1039SwitchSPIOpData_t readParams;1040readParams.unAddress = k_unSPIIMUScaleStartOffset;1041readParams.ucLength = k_unSPIIMUScaleLength;10421043if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply)) {1044Uint8 *pIMUScale;1045Sint16 sAccelRawX, sAccelRawY, sAccelRawZ, sGyroRawX, sGyroRawY, sGyroRawZ;10461047// IMU scale gives us multipliers for converting raw values to real world values1048pIMUScale = reply->spiReadData.rgucReadData;10491050sAccelRawX = (pIMUScale[1] << 8) | pIMUScale[0];1051sAccelRawY = (pIMUScale[3] << 8) | pIMUScale[2];1052sAccelRawZ = (pIMUScale[5] << 8) | pIMUScale[4];10531054sGyroRawX = (pIMUScale[13] << 8) | pIMUScale[12];1055sGyroRawY = (pIMUScale[15] << 8) | pIMUScale[14];1056sGyroRawZ = (pIMUScale[17] << 8) | pIMUScale[16];10571058// Check for user calibration data. If it's present and set, it'll override the factory settings1059readParams.unAddress = k_unSPIIMUUserScaleStartOffset;1060readParams.ucLength = k_unSPIIMUUserScaleLength;1061if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply) && (pIMUScale[0] | pIMUScale[1] << 8) == 0xA1B2) {1062pIMUScale = reply->spiReadData.rgucReadData;10631064sAccelRawX = (pIMUScale[3] << 8) | pIMUScale[2];1065sAccelRawY = (pIMUScale[5] << 8) | pIMUScale[4];1066sAccelRawZ = (pIMUScale[7] << 8) | pIMUScale[6];10671068sGyroRawX = (pIMUScale[15] << 8) | pIMUScale[14];1069sGyroRawY = (pIMUScale[17] << 8) | pIMUScale[16];1070sGyroRawZ = (pIMUScale[19] << 8) | pIMUScale[18];1071}10721073// Accelerometer scale1074ctx->m_IMUScaleData.fAccelScaleX = SWITCH_ACCEL_SCALE_MULT / (SWITCH_ACCEL_SCALE_OFFSET - (float)sAccelRawX) * SDL_STANDARD_GRAVITY;1075ctx->m_IMUScaleData.fAccelScaleY = SWITCH_ACCEL_SCALE_MULT / (SWITCH_ACCEL_SCALE_OFFSET - (float)sAccelRawY) * SDL_STANDARD_GRAVITY;1076ctx->m_IMUScaleData.fAccelScaleZ = SWITCH_ACCEL_SCALE_MULT / (SWITCH_ACCEL_SCALE_OFFSET - (float)sAccelRawZ) * SDL_STANDARD_GRAVITY;10771078// Gyro scale1079ctx->m_IMUScaleData.fGyroScaleX = SWITCH_GYRO_SCALE_MULT / (SWITCH_GYRO_SCALE_OFFSET - (float)sGyroRawX) * SDL_PI_F / 180.0f;1080ctx->m_IMUScaleData.fGyroScaleY = SWITCH_GYRO_SCALE_MULT / (SWITCH_GYRO_SCALE_OFFSET - (float)sGyroRawY) * SDL_PI_F / 180.0f;1081ctx->m_IMUScaleData.fGyroScaleZ = SWITCH_GYRO_SCALE_MULT / (SWITCH_GYRO_SCALE_OFFSET - (float)sGyroRawZ) * SDL_PI_F / 180.0f;10821083} else {1084// Use default values1085const float accelScale = SDL_STANDARD_GRAVITY / SWITCH_ACCEL_SCALE;1086const float gyroScale = SDL_PI_F / 180.0f / SWITCH_GYRO_SCALE;10871088ctx->m_IMUScaleData.fAccelScaleX = accelScale;1089ctx->m_IMUScaleData.fAccelScaleY = accelScale;1090ctx->m_IMUScaleData.fAccelScaleZ = accelScale;10911092ctx->m_IMUScaleData.fGyroScaleX = gyroScale;1093ctx->m_IMUScaleData.fGyroScaleY = gyroScale;1094ctx->m_IMUScaleData.fGyroScaleZ = gyroScale;1095}1096return true;1097}10981099static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue)1100{1101sRawValue -= ctx->m_StickCalData[nStick].axis[nAxis].sCenter;11021103if (sRawValue > ctx->m_StickExtents[nStick].axis[nAxis].sMax) {1104ctx->m_StickExtents[nStick].axis[nAxis].sMax = sRawValue;1105}1106if (sRawValue < ctx->m_StickExtents[nStick].axis[nAxis].sMin) {1107ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue;1108}11091110return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, ctx->m_StickExtents[nStick].axis[nAxis].sMax, SDL_MIN_SINT16, SDL_MAX_SINT16);1111}11121113static Sint16 ApplySimpleStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue)1114{1115// 0x8000 is the neutral value for all joystick axes1116const Uint16 usJoystickCenter = 0x8000;11171118sRawValue -= usJoystickCenter;11191120if (sRawValue > ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax) {1121ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax = sRawValue;1122}1123if (sRawValue < ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin) {1124ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin = sRawValue;1125}11261127return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax, SDL_MIN_SINT16, SDL_MAX_SINT16);1128}11291130static Uint8 RemapButton(SDL_DriverSwitch_Context *ctx, Uint8 button)1131{1132if (ctx->m_bUseButtonLabels) {1133// Use button labels instead of positions, e.g. Nintendo Online Classic controllers1134switch (button) {1135case SDL_GAMEPAD_BUTTON_SOUTH:1136return SDL_GAMEPAD_BUTTON_EAST;1137case SDL_GAMEPAD_BUTTON_EAST:1138return SDL_GAMEPAD_BUTTON_SOUTH;1139case SDL_GAMEPAD_BUTTON_WEST:1140return SDL_GAMEPAD_BUTTON_NORTH;1141case SDL_GAMEPAD_BUTTON_NORTH:1142return SDL_GAMEPAD_BUTTON_WEST;1143default:1144break;1145}1146}1147return button;1148}11491150static int GetMaxWriteAttempts(SDL_HIDAPI_Device *device)1151{1152if (device->vendor_id == USB_VENDOR_NINTENDO &&1153device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {1154// This device is a little slow and we know we're always on USB1155return 20;1156} else {1157return 5;1158}1159}11601161static ESwitchDeviceInfoControllerType ReadJoyConControllerType(SDL_HIDAPI_Device *device)1162{1163ESwitchDeviceInfoControllerType eControllerType = k_eSwitchDeviceInfoControllerType_Unknown;1164const int MAX_ATTEMPTS = 1; // Don't try too long, in case this is a zombie Bluetooth controller1165int attempts = 0;11661167// Create enough of a context to read the controller type from the device1168SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx));1169if (ctx) {1170ctx->device = device;1171ctx->m_bSyncWrite = true;1172ctx->m_nMaxWriteAttempts = GetMaxWriteAttempts(device);11731174for ( ; ; ) {1175++attempts;1176if (device->is_bluetooth) {1177SwitchSubcommandInputPacket_t *reply = NULL;11781179if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_RequestDeviceInfo, NULL, 0, &reply)) {1180eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)reply->deviceInfo.ucDeviceType);1181}1182} else {1183if (WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Status, NULL, 0, true)) {1184SwitchProprietaryStatusPacket_t *status = (SwitchProprietaryStatusPacket_t *)&ctx->m_rgucReadBuffer[0];11851186eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)status->ucDeviceType);1187}1188}1189if (eControllerType == k_eSwitchDeviceInfoControllerType_Unknown && attempts < MAX_ATTEMPTS) {1190// Wait a bit and try again1191SDL_Delay(100);1192continue;1193}1194break;1195}1196SDL_free(ctx);1197}1198return eControllerType;1199}12001201static bool HasHomeLED(SDL_DriverSwitch_Context *ctx)1202{1203Uint16 vendor_id = ctx->device->vendor_id;1204Uint16 product_id = ctx->device->product_id;12051206// The Power A Nintendo Switch Pro controllers don't have a Home LED1207if (vendor_id == 0 && product_id == 0) {1208return false;1209}12101211// HORI Wireless Switch Pad1212if (vendor_id == 0x0f0d && product_id == 0x00f6) {1213return false;1214}12151216// Third party controllers don't have a home LED and will shut off if we try to set it1217if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_Unknown ||1218ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_LicProController) {1219return false;1220}12211222// The Nintendo Online classic controllers don't have a Home LED1223if (vendor_id == USB_VENDOR_NINTENDO &&1224ctx->m_eControllerType > k_eSwitchDeviceInfoControllerType_ProController) {1225return false;1226}12271228return true;1229}12301231static bool AlwaysUsesLabels(Uint16 vendor_id, Uint16 product_id, ESwitchDeviceInfoControllerType eControllerType)1232{1233// Some controllers don't have a diamond button configuration, so should always use labels1234if (SDL_IsJoystickGameCube(vendor_id, product_id)) {1235return true;1236}1237switch (eControllerType) {1238case k_eSwitchDeviceInfoControllerType_HVCLeft:1239case k_eSwitchDeviceInfoControllerType_HVCRight:1240case k_eSwitchDeviceInfoControllerType_NESLeft:1241case k_eSwitchDeviceInfoControllerType_NESRight:1242case k_eSwitchDeviceInfoControllerType_N64:1243case k_eSwitchDeviceInfoControllerType_SEGA_Genesis:1244return true;1245default:1246return false;1247}1248}12491250static void HIDAPI_DriverNintendoClassic_RegisterHints(SDL_HintCallback callback, void *userdata)1251{1252SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC, callback, userdata);1253}12541255static void HIDAPI_DriverNintendoClassic_UnregisterHints(SDL_HintCallback callback, void *userdata)1256{1257SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC, callback, userdata);1258}12591260static bool HIDAPI_DriverNintendoClassic_IsEnabled(void)1261{1262return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));1263}12641265static 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)1266{1267if (vendor_id == USB_VENDOR_NINTENDO) {1268if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT) {1269if (SDL_strncmp(name, "NES Controller", 14) == 0 ||1270SDL_strncmp(name, "HVC Controller", 14) == 0) {1271return true;1272}1273}12741275if (product_id == USB_PRODUCT_NINTENDO_N64_CONTROLLER) {1276return true;1277}12781279if (product_id == USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER) {1280return true;1281}12821283if (product_id == USB_PRODUCT_NINTENDO_SNES_CONTROLLER) {1284return true;1285}1286}12871288return false;1289}12901291static void HIDAPI_DriverJoyCons_RegisterHints(SDL_HintCallback callback, void *userdata)1292{1293SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, callback, userdata);1294}12951296static void HIDAPI_DriverJoyCons_UnregisterHints(SDL_HintCallback callback, void *userdata)1297{1298SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, callback, userdata);1299}13001301static bool HIDAPI_DriverJoyCons_IsEnabled(void)1302{1303return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));1304}13051306static 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)1307{1308if (vendor_id == USB_VENDOR_NINTENDO) {1309if (product_id == USB_PRODUCT_NINTENDO_SWITCH_PRO && device && device->dev) {1310// This might be a Kinvoca Joy-Con that reports VID/PID as a Switch Pro controller1311ESwitchDeviceInfoControllerType eControllerType = ReadJoyConControllerType(device);1312if (eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft ||1313eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {1314return true;1315}1316}13171318if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT ||1319product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT ||1320product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {1321return true;1322}1323}1324return false;1325}13261327static void HIDAPI_DriverSwitch_RegisterHints(SDL_HintCallback callback, void *userdata)1328{1329SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, callback, userdata);1330}13311332static void HIDAPI_DriverSwitch_UnregisterHints(SDL_HintCallback callback, void *userdata)1333{1334SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, callback, userdata);1335}13361337static bool HIDAPI_DriverSwitch_IsEnabled(void)1338{1339return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));1340}13411342static 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)1343{1344/* The HORI Wireless Switch Pad enumerates as a HID device when connected via USB1345with the same VID/PID as when connected over Bluetooth but doesn't actually1346support communication over USB. The most reliable way to block this without allowing the1347controller to continually attempt to reconnect is to filter it out by manufacturer/product string.1348Note that the controller does have a different product string when connected over Bluetooth.1349*/1350if (SDL_strcmp(name, "HORI Wireless Switch Pad") == 0) {1351return false;1352}13531354// If it's handled by another driver, it's not handled here1355if (HIDAPI_DriverNintendoClassic_IsSupportedDevice(device, name, type, vendor_id, product_id, version, interface_number, interface_class, interface_subclass, interface_protocol) ||1356HIDAPI_DriverJoyCons_IsSupportedDevice(device, name, type, vendor_id, product_id, version, interface_number, interface_class, interface_subclass, interface_protocol)) {1357return false;1358}13591360return (type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO);1361}13621363static void UpdateDeviceIdentity(SDL_HIDAPI_Device *device)1364{1365SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;13661367if (ctx->m_bInputOnly) {1368if (SDL_IsJoystickGameCube(device->vendor_id, device->product_id)) {1369device->type = SDL_GAMEPAD_TYPE_STANDARD;1370}1371} else {1372char serial[18];13731374switch (ctx->m_eControllerType) {1375case k_eSwitchDeviceInfoControllerType_JoyConLeft:1376HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (L)");1377HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT);1378device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;1379break;1380case k_eSwitchDeviceInfoControllerType_JoyConRight:1381HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (R)");1382HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT);1383device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;1384break;1385case k_eSwitchDeviceInfoControllerType_ProController:1386case k_eSwitchDeviceInfoControllerType_LicProController:1387HIDAPI_SetDeviceName(device, "Nintendo Switch Pro Controller");1388HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_PRO);1389device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;1390break;1391case k_eSwitchDeviceInfoControllerType_HVCLeft:1392HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (1)");1393device->type = SDL_GAMEPAD_TYPE_STANDARD;1394break;1395case k_eSwitchDeviceInfoControllerType_HVCRight:1396HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (2)");1397device->type = SDL_GAMEPAD_TYPE_STANDARD;1398break;1399case k_eSwitchDeviceInfoControllerType_NESLeft:1400HIDAPI_SetDeviceName(device, "Nintendo NES Controller (L)");1401device->type = SDL_GAMEPAD_TYPE_STANDARD;1402break;1403case k_eSwitchDeviceInfoControllerType_NESRight:1404HIDAPI_SetDeviceName(device, "Nintendo NES Controller (R)");1405device->type = SDL_GAMEPAD_TYPE_STANDARD;1406break;1407case k_eSwitchDeviceInfoControllerType_SNES:1408HIDAPI_SetDeviceName(device, "Nintendo SNES Controller");1409HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SNES_CONTROLLER);1410device->type = SDL_GAMEPAD_TYPE_STANDARD;1411break;1412case k_eSwitchDeviceInfoControllerType_N64:1413HIDAPI_SetDeviceName(device, "Nintendo N64 Controller");1414HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_N64_CONTROLLER);1415device->type = SDL_GAMEPAD_TYPE_STANDARD;1416break;1417case k_eSwitchDeviceInfoControllerType_SEGA_Genesis:1418HIDAPI_SetDeviceName(device, "Nintendo SEGA Genesis Controller");1419HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER);1420device->type = SDL_GAMEPAD_TYPE_STANDARD;1421break;1422case k_eSwitchDeviceInfoControllerType_Unknown:1423// We couldn't read the device info for this controller, might not be fully compliant1424if (device->vendor_id == USB_VENDOR_NINTENDO) {1425switch (device->product_id) {1426case USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT:1427ctx->m_eControllerType = k_eSwitchDeviceInfoControllerType_JoyConLeft;1428HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (L)");1429device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;1430break;1431case USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT:1432ctx->m_eControllerType = k_eSwitchDeviceInfoControllerType_JoyConRight;1433HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (R)");1434device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;1435break;1436case USB_PRODUCT_NINTENDO_SWITCH_PRO:1437ctx->m_eControllerType = k_eSwitchDeviceInfoControllerType_ProController;1438HIDAPI_SetDeviceName(device, "Nintendo Switch Pro Controller");1439device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;1440break;1441default:1442break;1443}1444}1445return;1446default:1447device->type = SDL_GAMEPAD_TYPE_STANDARD;1448break;1449}1450device->guid.data[15] = ctx->m_eControllerType;14511452(void)SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",1453ctx->m_rgucMACAddress[0],1454ctx->m_rgucMACAddress[1],1455ctx->m_rgucMACAddress[2],1456ctx->m_rgucMACAddress[3],1457ctx->m_rgucMACAddress[4],1458ctx->m_rgucMACAddress[5]);1459HIDAPI_SetDeviceSerial(device, serial);1460}1461}14621463static bool HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device)1464{1465SDL_DriverSwitch_Context *ctx;14661467ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx));1468if (!ctx) {1469return false;1470}1471ctx->device = device;1472device->context = ctx;14731474ctx->m_nMaxWriteAttempts = GetMaxWriteAttempts(device);1475ctx->m_bSyncWrite = true;14761477// Find out whether or not we can send output reports1478ctx->m_bInputOnly = SDL_IsJoystickNintendoSwitchProInputOnly(device->vendor_id, device->product_id);1479if (!ctx->m_bInputOnly) {1480// Initialize rumble data, important for reading device info on the MOBAPAD M0731481SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);1482SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);14831484BReadDeviceInfo(ctx);1485}1486UpdateDeviceIdentity(device);14871488// Prefer the USB device over the Bluetooth device1489if (device->is_bluetooth) {1490if (HIDAPI_HasConnectedUSBDevice(device->serial)) {1491return true;1492}1493} else {1494HIDAPI_DisconnectBluetoothDevice(device->serial);1495}1496return HIDAPI_JoystickConnected(device, NULL);1497}14981499static int HIDAPI_DriverSwitch_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)1500{1501return -1;1502}15031504static void HIDAPI_DriverSwitch_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)1505{1506SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;15071508if (!ctx->joystick) {1509return;1510}15111512ctx->m_nPlayerIndex = player_index;15131514UpdateSlotLED(ctx);1515}15161517static bool HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)1518{1519SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;15201521SDL_AssertJoysticksLocked();15221523ctx->joystick = joystick;15241525ctx->m_bSyncWrite = true;15261527if (!ctx->m_bInputOnly) {1528#ifdef SDL_PLATFORM_MACOS1529// Wait for the OS to finish its handshake with the controller1530SDL_Delay(250);1531#endif1532GetInitialInputMode(ctx);1533ctx->m_nCurrentInputMode = ctx->m_nInitialInputMode;15341535// Initialize rumble data1536SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);1537SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);15381539if (!device->is_bluetooth) {1540if (!BTrySetupUSB(ctx)) {1541SDL_SetError("Couldn't setup USB mode");1542return false;1543}1544}15451546if (!LoadStickCalibration(ctx)) {1547SDL_SetError("Couldn't load stick calibration");1548return false;1549}15501551if (ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_HVCLeft &&1552ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_HVCRight &&1553ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_NESLeft &&1554ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_NESRight &&1555ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_SNES &&1556ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_N64 &&1557ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_SEGA_Genesis) {1558if (LoadIMUCalibration(ctx)) {1559ctx->m_bSensorsSupported = true;1560}1561}15621563// Enable vibration1564SetVibrationEnabled(ctx, 1);15651566// Set desired input mode1567SDL_AddHintCallback(SDL_HINT_JOYSTICK_ENHANCED_REPORTS,1568SDL_EnhancedReportsChanged, ctx);15691570// Start sending USB reports1571if (!device->is_bluetooth) {1572// ForceUSB doesn't generate an ACK, so don't wait for a reply1573if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, false)) {1574SDL_SetError("Couldn't start USB reports");1575return false;1576}1577}15781579// Set the LED state1580if (HasHomeLED(ctx)) {1581if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft ||1582ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {1583SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED,1584SDL_HomeLEDHintChanged, ctx);1585} else {1586SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED,1587SDL_HomeLEDHintChanged, ctx);1588}1589}1590}15911592if (AlwaysUsesLabels(device->vendor_id, device->product_id, ctx->m_eControllerType)) {1593ctx->m_bUseButtonLabels = true;1594}15951596// Initialize player index (needed for setting LEDs)1597ctx->m_nPlayerIndex = SDL_GetJoystickPlayerIndex(joystick);1598ctx->m_bPlayerLights = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, true);1599UpdateSlotLED(ctx);16001601SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED,1602SDL_PlayerLEDHintChanged, ctx);16031604// Initialize the joystick capabilities1605joystick->nbuttons = SDL_GAMEPAD_NUM_SWITCH_BUTTONS;1606joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;1607joystick->nhats = 1;16081609// Set up for input1610ctx->m_bSyncWrite = false;1611ctx->m_ulLastIMUReset = ctx->m_ulLastInput = SDL_GetTicks();1612ctx->m_ulIMUUpdateIntervalNS = SDL_MS_TO_NS(5); // Start off at 5 ms update rate16131614// Set up for vertical mode1615ctx->m_bVerticalMode = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, false);16161617return true;1618}16191620static bool HIDAPI_DriverSwitch_ActuallyRumbleJoystick(SDL_DriverSwitch_Context *ctx, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)1621{1622/* Experimentally determined rumble values. These will only matter on some controllers as tested ones1623* seem to disregard these and just use any non-zero rumble values as a binary flag for constant rumble1624*1625* More information about these values can be found here:1626* https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md1627*/1628const Uint16 k_usHighFreq = 0x0074;1629const Uint8 k_ucHighFreqAmp = EncodeRumbleHighAmplitude(high_frequency_rumble);1630const Uint8 k_ucLowFreq = 0x3D;1631const Uint16 k_usLowFreqAmp = EncodeRumbleLowAmplitude(low_frequency_rumble);16321633if (low_frequency_rumble || high_frequency_rumble) {1634EncodeRumble(&ctx->m_RumblePacket.rumbleData[0], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);1635EncodeRumble(&ctx->m_RumblePacket.rumbleData[1], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);1636} else {1637SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);1638SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);1639}16401641ctx->m_bRumbleActive = (low_frequency_rumble || high_frequency_rumble);16421643if (!WriteRumble(ctx)) {1644return SDL_SetError("Couldn't send rumble packet");1645}1646return true;1647}16481649static bool HIDAPI_DriverSwitch_SendPendingRumble(SDL_DriverSwitch_Context *ctx)1650{1651if (SDL_GetTicks() < (ctx->m_ulRumbleSent + RUMBLE_WRITE_FREQUENCY_MS)) {1652return true;1653}16541655if (ctx->m_bRumblePending) {1656Uint16 low_frequency_rumble = (Uint16)(ctx->m_unRumblePending >> 16);1657Uint16 high_frequency_rumble = (Uint16)ctx->m_unRumblePending;16581659#ifdef DEBUG_RUMBLE1660SDL_Log("Sent pending rumble %d/%d, %d ms after previous rumble", low_frequency_rumble, high_frequency_rumble, SDL_GetTicks() - ctx->m_ulRumbleSent);1661#endif1662ctx->m_bRumblePending = false;1663ctx->m_unRumblePending = 0;16641665return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, low_frequency_rumble, high_frequency_rumble);1666}16671668if (ctx->m_bRumbleZeroPending) {1669ctx->m_bRumbleZeroPending = false;16701671#ifdef DEBUG_RUMBLE1672SDL_Log("Sent pending zero rumble, %d ms after previous rumble", SDL_GetTicks() - ctx->m_ulRumbleSent);1673#endif1674return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, 0, 0);1675}16761677return true;1678}16791680static bool HIDAPI_DriverSwitch_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)1681{1682SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;16831684if (ctx->m_bInputOnly) {1685return SDL_Unsupported();1686}16871688if (device->parent) {1689if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {1690// Just handle low frequency rumble1691high_frequency_rumble = 0;1692} else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {1693// Just handle high frequency rumble1694low_frequency_rumble = 0;1695}1696}16971698if (ctx->m_bRumblePending) {1699if (!HIDAPI_DriverSwitch_SendPendingRumble(ctx)) {1700return false;1701}1702}17031704if (SDL_GetTicks() < (ctx->m_ulRumbleSent + RUMBLE_WRITE_FREQUENCY_MS)) {1705if (low_frequency_rumble || high_frequency_rumble) {1706Uint32 unRumblePending = ((Uint32)low_frequency_rumble << 16) | high_frequency_rumble;17071708// Keep the highest rumble intensity in the given interval1709if (unRumblePending > ctx->m_unRumblePending) {1710ctx->m_unRumblePending = unRumblePending;1711}1712ctx->m_bRumblePending = true;1713ctx->m_bRumbleZeroPending = false;1714} else {1715// When rumble is complete, turn it off1716ctx->m_bRumbleZeroPending = true;1717}1718return true;1719}17201721#ifdef DEBUG_RUMBLE1722SDL_Log("Sent rumble %d/%d", low_frequency_rumble, high_frequency_rumble);1723#endif17241725return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, low_frequency_rumble, high_frequency_rumble);1726}17271728static bool HIDAPI_DriverSwitch_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)1729{1730return SDL_Unsupported();1731}17321733static Uint32 HIDAPI_DriverSwitch_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)1734{1735SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;1736Uint32 result = 0;17371738if (ctx->m_bPlayerLights && !ctx->m_bInputOnly) {1739result |= SDL_JOYSTICK_CAP_PLAYER_LED;1740}17411742if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_ProController && !ctx->m_bInputOnly) {1743// Doesn't have an RGB LED, so don't return SDL_JOYSTICK_CAP_RGB_LED here1744result |= SDL_JOYSTICK_CAP_RUMBLE;1745} else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft ||1746ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {1747result |= SDL_JOYSTICK_CAP_RUMBLE;1748}1749return result;1750}17511752static bool HIDAPI_DriverSwitch_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)1753{1754return SDL_Unsupported();1755}17561757static bool HIDAPI_DriverSwitch_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)1758{1759SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;17601761if (size == sizeof(SwitchCommonOutputPacket_t)) {1762const SwitchCommonOutputPacket_t *packet = (SwitchCommonOutputPacket_t *)data;17631764if (packet->ucPacketType != k_eSwitchOutputReportIDs_Rumble) {1765return SDL_SetError("Unknown Nintendo Switch Pro effect type");1766}17671768SDL_copyp(&ctx->m_RumblePacket.rumbleData[0], &packet->rumbleData[0]);1769SDL_copyp(&ctx->m_RumblePacket.rumbleData[1], &packet->rumbleData[1]);1770if (!WriteRumble(ctx)) {1771return false;1772}17731774// This overwrites any internal rumble1775ctx->m_bRumblePending = false;1776ctx->m_bRumbleZeroPending = false;1777return true;1778} else if (size >= 2 && size <= 256) {1779const Uint8 *payload = (const Uint8 *)data;1780ESwitchSubcommandIDs cmd = (ESwitchSubcommandIDs)payload[0];17811782if (cmd == k_eSwitchSubcommandIDs_SetInputReportMode && !device->is_bluetooth) {1783// Going into simple mode over USB disables input reports, so don't do that1784return true;1785}1786if (cmd == k_eSwitchSubcommandIDs_SetHomeLight && !HasHomeLED(ctx)) {1787// Setting the home LED when it's not supported can cause the controller to reset1788return true;1789}17901791if (!WriteSubcommand(ctx, cmd, &payload[1], (Uint8)(size - 1), NULL)) {1792return false;1793}1794return true;1795}1796return SDL_Unsupported();1797}17981799static bool HIDAPI_DriverSwitch_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)1800{1801SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;18021803UpdateEnhancedModeOnApplicationUsage(ctx);18041805if (!ctx->m_bSensorsSupported || (enabled && !ctx->m_bEnhancedMode)) {1806return SDL_Unsupported();1807}18081809ctx->m_bReportSensors = enabled;1810ctx->m_unIMUSamples = 0;1811ctx->m_ulIMUSampleTimestampNS = SDL_GetTicksNS();18121813UpdateInputMode(ctx);1814SetIMUEnabled(ctx, enabled);18151816return true;1817}18181819static void HandleInputOnlyControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchInputOnlyControllerStatePacket_t *packet)1820{1821Sint16 axis;1822Uint64 timestamp = SDL_GetTicksNS();18231824if (packet->rgucButtons[0] != ctx->m_lastInputOnlyState.rgucButtons[0]) {1825Uint8 data = packet->rgucButtons[0];1826SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x02) != 0));1827SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x04) != 0));1828SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x01) != 0));1829SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0));1830SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0));1831SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0));1832}18331834if (packet->rgucButtons[1] != ctx->m_lastInputOnlyState.rgucButtons[1]) {1835Uint8 data = packet->rgucButtons[1];1836SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0));1837SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));1838SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0));1839SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x08) != 0));1840SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));1841SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));1842}18431844if (packet->ucStickHat != ctx->m_lastInputOnlyState.ucStickHat) {1845Uint8 hat;18461847switch (packet->ucStickHat) {1848case 0:1849hat = SDL_HAT_UP;1850break;1851case 1:1852hat = SDL_HAT_RIGHTUP;1853break;1854case 2:1855hat = SDL_HAT_RIGHT;1856break;1857case 3:1858hat = SDL_HAT_RIGHTDOWN;1859break;1860case 4:1861hat = SDL_HAT_DOWN;1862break;1863case 5:1864hat = SDL_HAT_LEFTDOWN;1865break;1866case 6:1867hat = SDL_HAT_LEFT;1868break;1869case 7:1870hat = SDL_HAT_LEFTUP;1871break;1872default:1873hat = SDL_HAT_CENTERED;1874break;1875}1876SDL_SendJoystickHat(timestamp, joystick, 0, hat);1877}18781879axis = (packet->rgucButtons[0] & 0x40) ? 32767 : -32768;1880SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);18811882axis = (packet->rgucButtons[0] & 0x80) ? 32767 : -32768;1883SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);18841885if (packet->rgucJoystickLeft[0] != ctx->m_lastInputOnlyState.rgucJoystickLeft[0]) {1886axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickLeft[0], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16);1887SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);1888}18891890if (packet->rgucJoystickLeft[1] != ctx->m_lastInputOnlyState.rgucJoystickLeft[1]) {1891axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickLeft[1], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16);1892SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);1893}18941895if (packet->rgucJoystickRight[0] != ctx->m_lastInputOnlyState.rgucJoystickRight[0]) {1896axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickRight[0], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16);1897SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);1898}18991900if (packet->rgucJoystickRight[1] != ctx->m_lastInputOnlyState.rgucJoystickRight[1]) {1901axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickRight[1], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16);1902SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);1903}19041905ctx->m_lastInputOnlyState = *packet;1906}19071908static void HandleCombinedSimpleControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)1909{1910if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {1911Uint8 data = packet->rgucButtons[0];1912Uint8 hat = 0;19131914if (data & 0x01) {1915hat |= SDL_HAT_LEFT;1916}1917if (data & 0x02) {1918hat |= SDL_HAT_DOWN;1919}1920if (data & 0x04) {1921hat |= SDL_HAT_UP;1922}1923if (data & 0x08) {1924hat |= SDL_HAT_RIGHT;1925}1926SDL_SendJoystickHat(timestamp, joystick, 0, hat);19271928SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x10) != 0));1929SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x20) != 0));1930}19311932if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {1933Uint8 data = packet->rgucButtons[1];1934SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0));1935SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0));1936SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));1937SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x40) != 0));1938}19391940Sint16 axis = (packet->rgucButtons[1] & 0x80) ? 32767 : -32768;1941SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);19421943if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {1944switch (packet->ucStickHat) {1945case 0:1946SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);1947SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);1948break;1949case 1:1950SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);1951SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);1952break;1953case 2:1954SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);1955SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);1956break;1957case 3:1958SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);1959SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);1960break;1961case 4:1962SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);1963SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);1964break;1965case 5:1966SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);1967SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);1968break;1969case 6:1970SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);1971SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);1972break;1973case 7:1974SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);1975SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);1976break;1977default:1978SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);1979SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);1980break;1981}1982}1983}19841985static void HandleCombinedSimpleControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)1986{1987if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {1988Uint8 data = packet->rgucButtons[0];1989SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x01) != 0));1990SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x02) != 0));1991SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x04) != 0));1992SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x08) != 0));1993SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x10) != 0));1994SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x20) != 0));1995}19961997if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {1998Uint8 data = packet->rgucButtons[1];1999SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));2000SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x08) != 0));2001SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));2002SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x40) != 0));2003}20042005Sint16 axis = (packet->rgucButtons[1] & 0x80) ? 32767 : -32768;2006SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);20072008if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {2009switch (packet->ucStickHat) {2010case 0:2011SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MIN);2012SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, 0);2013break;2014case 1:2015SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MIN);2016SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MIN);2017break;2018case 2:2019SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, 0);2020SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MIN);2021break;2022case 3:2023SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MAX);2024SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MIN);2025break;2026case 4:2027SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MAX);2028SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, 0);2029break;2030case 5:2031SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MAX);2032SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MAX);2033break;2034case 6:2035SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, 0);2036SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MAX);2037break;2038case 7:2039SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MIN);2040SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MAX);2041break;2042default:2043SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, 0);2044SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, 0);2045break;2046}2047}2048}20492050static void HandleMiniSimpleControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)2051{2052if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {2053Uint8 data = packet->rgucButtons[0];2054SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x01) != 0));2055SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0));2056SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0));2057SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0));2058SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0));2059SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0));2060}20612062if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {2063Uint8 data = packet->rgucButtons[1];2064SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x01) != 0));2065SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0));2066SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x20) != 0));2067SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x40) != 0));2068SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x80) != 0));2069}20702071if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {2072switch (packet->ucStickHat) {2073case 0:2074SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);2075SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);2076break;2077case 1:2078SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);2079SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);2080break;2081case 2:2082SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);2083SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);2084break;2085case 3:2086SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);2087SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);2088break;2089case 4:2090SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);2091SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);2092break;2093case 5:2094SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);2095SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);2096break;2097case 6:2098SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);2099SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);2100break;2101case 7:2102SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);2103SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);2104break;2105default:2106SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);2107SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);2108break;2109}2110}2111}21122113static void HandleMiniSimpleControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)2114{2115if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {2116Uint8 data = packet->rgucButtons[0];2117SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x01) != 0));2118SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0));2119SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0));2120SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0));2121SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0));2122SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0));2123}21242125if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {2126Uint8 data = packet->rgucButtons[1];2127SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));2128SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0));2129SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));2130SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));2131SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x40) != 0));2132SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x80) != 0));2133}21342135if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {2136switch (packet->ucStickHat) {2137case 0:2138SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);2139SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);2140break;2141case 1:2142SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);2143SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);2144break;2145case 2:2146SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);2147SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);2148break;2149case 3:2150SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);2151SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);2152break;2153case 4:2154SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);2155SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);2156break;2157case 5:2158SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);2159SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);2160break;2161case 6:2162SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);2163SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);2164break;2165case 7:2166SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);2167SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);2168break;2169default:2170SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);2171SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);2172break;2173}2174}2175}21762177static void HandleSimpleControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)2178{2179Uint64 timestamp = SDL_GetTicksNS();21802181if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {2182if (ctx->device->parent || ctx->m_bVerticalMode) {2183HandleCombinedSimpleControllerStateL(timestamp, joystick, ctx, packet);2184} else {2185HandleMiniSimpleControllerStateL(timestamp, joystick, ctx, packet);2186}2187} else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {2188if (ctx->device->parent || ctx->m_bVerticalMode) {2189HandleCombinedSimpleControllerStateR(timestamp, joystick, ctx, packet);2190} else {2191HandleMiniSimpleControllerStateR(timestamp, joystick, ctx, packet);2192}2193} else {2194Sint16 axis;21952196if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {2197Uint8 data = packet->rgucButtons[0];2198SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x01) != 0));2199SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0));2200SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0));2201SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0));2202SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0));2203SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0));2204}22052206if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {2207Uint8 data = packet->rgucButtons[1];2208SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0));2209SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));2210SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0));2211SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x08) != 0));2212SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));2213SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));2214}22152216if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {2217Uint8 hat;22182219switch (packet->ucStickHat) {2220case 0:2221hat = SDL_HAT_UP;2222break;2223case 1:2224hat = SDL_HAT_RIGHTUP;2225break;2226case 2:2227hat = SDL_HAT_RIGHT;2228break;2229case 3:2230hat = SDL_HAT_RIGHTDOWN;2231break;2232case 4:2233hat = SDL_HAT_DOWN;2234break;2235case 5:2236hat = SDL_HAT_LEFTDOWN;2237break;2238case 6:2239hat = SDL_HAT_LEFT;2240break;2241case 7:2242hat = SDL_HAT_LEFTUP;2243break;2244default:2245hat = SDL_HAT_CENTERED;2246break;2247}2248SDL_SendJoystickHat(timestamp, joystick, 0, hat);2249}22502251axis = (packet->rgucButtons[0] & 0x40) ? 32767 : -32768;2252SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);22532254axis = ((packet->rgucButtons[0] & 0x80) || (packet->rgucButtons[1] & 0x80)) ? 32767 : -32768;2255SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);22562257axis = ApplySimpleStickCalibration(ctx, 0, 0, packet->sJoystickLeft[0]);2258SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);22592260axis = ApplySimpleStickCalibration(ctx, 0, 1, packet->sJoystickLeft[1]);2261SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);22622263axis = ApplySimpleStickCalibration(ctx, 1, 0, packet->sJoystickRight[0]);2264SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);22652266axis = ApplySimpleStickCalibration(ctx, 1, 1, packet->sJoystickRight[1]);2267SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);2268}22692270ctx->m_lastSimpleState = *packet;2271}22722273static void SendSensorUpdate(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SDL_SensorType type, Uint64 sensor_timestamp, const Sint16 *values)2274{2275float data[3];22762277/* Note the order of components has been shuffled to match PlayStation controllers,2278* since that's our de facto standard from already supporting those controllers, and2279* users will want consistent axis mappings across devices.2280*/2281if (type == SDL_SENSOR_GYRO || type == SDL_SENSOR_GYRO_L || type == SDL_SENSOR_GYRO_R) {2282data[0] = -(ctx->m_IMUScaleData.fGyroScaleY * (float)values[1]);2283data[1] = ctx->m_IMUScaleData.fGyroScaleZ * (float)values[2];2284data[2] = -(ctx->m_IMUScaleData.fGyroScaleX * (float)values[0]);2285} else {2286data[0] = -(ctx->m_IMUScaleData.fAccelScaleY * (float)values[1]);2287data[1] = ctx->m_IMUScaleData.fAccelScaleZ * (float)values[2];2288data[2] = -(ctx->m_IMUScaleData.fAccelScaleX * (float)values[0]);2289}22902291// Right Joy-Con flips some axes, so let's flip them back for consistency2292if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {2293data[0] = -data[0];2294data[1] = -data[1];2295}22962297if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft &&2298!ctx->device->parent && !ctx->m_bVerticalMode) {2299// Mini-gamepad mode, swap some axes around2300float tmp = data[2];2301data[2] = -data[0];2302data[0] = tmp;2303}23042305if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight &&2306!ctx->device->parent && !ctx->m_bVerticalMode) {2307// Mini-gamepad mode, swap some axes around2308float tmp = data[2];2309data[2] = data[0];2310data[0] = -tmp;2311}23122313SDL_SendJoystickSensor(timestamp, joystick, type, sensor_timestamp, data, 3);2314}23152316static void HandleCombinedControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)2317{2318Sint16 axis;23192320if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {2321Uint8 data = packet->controllerState.rgucButtons[1];2322SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0));2323SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0));2324SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));2325}23262327if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {2328Uint8 data = packet->controllerState.rgucButtons[2];2329Uint8 hat = 0;23302331if (data & 0x01) {2332hat |= SDL_HAT_DOWN;2333}2334if (data & 0x02) {2335hat |= SDL_HAT_UP;2336}2337if (data & 0x04) {2338hat |= SDL_HAT_RIGHT;2339}2340if (data & 0x08) {2341hat |= SDL_HAT_LEFT;2342}2343SDL_SendJoystickHat(timestamp, joystick, 0, hat);23442345SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x10) != 0));2346SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x20) != 0));2347SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x40) != 0));2348axis = (data & 0x80) ? 32767 : -32768;2349SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);2350}23512352axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);2353axis = ApplyStickCalibration(ctx, 0, 0, axis);2354SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);23552356axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4);2357axis = ApplyStickCalibration(ctx, 0, 1, axis);2358SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~axis);2359}23602361static void HandleCombinedControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)2362{2363Sint16 axis;23642365if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) {2366Uint8 data = packet->controllerState.rgucButtons[0];2367SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x04) != 0));2368SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x08) != 0));2369SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x01) != 0));2370SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x02) != 0));2371SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x10) != 0));2372SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x20) != 0));2373SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x40) != 0));2374axis = (data & 0x80) ? 32767 : -32768;2375SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);2376}23772378if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {2379Uint8 data = packet->controllerState.rgucButtons[1];2380SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));2381SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x04) != 0));2382SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));2383}23842385axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8);2386axis = ApplyStickCalibration(ctx, 1, 0, axis);2387SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);23882389axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4);2390axis = ApplyStickCalibration(ctx, 1, 1, axis);2391SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~axis);2392}23932394static void HandleMiniControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)2395{2396Sint16 axis;23972398if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {2399Uint8 data = packet->controllerState.rgucButtons[1];2400SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x01) != 0));2401SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0));2402SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x20) != 0));2403}24042405if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {2406Uint8 data = packet->controllerState.rgucButtons[2];2407SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x08) != 0));2408SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x01) != 0));2409SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x02) != 0));2410SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x04) != 0));2411SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x10) != 0));2412SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x20) != 0));2413SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x40) != 0));2414SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x80) != 0));2415}24162417axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);2418axis = ApplyStickCalibration(ctx, 0, 0, axis);2419SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~axis);24202421axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4);2422axis = ApplyStickCalibration(ctx, 0, 1, axis);2423SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, ~axis);2424}24252426static void HandleMiniControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)2427{2428Sint16 axis;24292430if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) {2431Uint8 data = packet->controllerState.rgucButtons[0];2432SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x08) != 0));2433SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0));2434SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0));2435SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x01) != 0));2436SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x10) != 0));2437SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x20) != 0));2438SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x40) != 0));2439SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x80) != 0));2440}24412442if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {2443Uint8 data = packet->controllerState.rgucButtons[1];2444SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));2445SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0));2446SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));2447}24482449axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8);2450axis = ApplyStickCalibration(ctx, 1, 0, axis);2451SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);24522453axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4);2454axis = ApplyStickCalibration(ctx, 1, 1, axis);2455SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);2456}24572458static 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 state2459{2460Uint64 timestamp = SDL_GetTicksNS();24612462if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {2463if (ctx->device->parent || ctx->m_bVerticalMode) {2464HandleCombinedControllerStateL(timestamp, joystick, ctx, packet);2465} else {2466HandleMiniControllerStateL(timestamp, joystick, ctx, packet);2467}2468} else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {2469if (ctx->device->parent || ctx->m_bVerticalMode) {2470HandleCombinedControllerStateR(timestamp, joystick, ctx, packet);2471} else {2472HandleMiniControllerStateR(timestamp, joystick, ctx, packet);2473}2474} else {2475Sint16 axis;24762477if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) {2478Uint8 data = packet->controllerState.rgucButtons[0];2479SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x04) != 0));2480SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x08) != 0));2481SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x01) != 0));2482SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x02) != 0));2483SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x40) != 0));2484}24852486if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {2487Uint8 data = packet->controllerState.rgucButtons[1];2488SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0));2489SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));2490SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x04) != 0));2491SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0));24922493SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));2494SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));2495}24962497if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {2498Uint8 data = packet->controllerState.rgucButtons[2];2499Uint8 hat = 0;25002501if (data & 0x01) {2502hat |= SDL_HAT_DOWN;2503}2504if (data & 0x02) {2505hat |= SDL_HAT_UP;2506}2507if (data & 0x04) {2508hat |= SDL_HAT_RIGHT;2509}2510if (data & 0x08) {2511hat |= SDL_HAT_LEFT;2512}2513SDL_SendJoystickHat(timestamp, joystick, 0, hat);25142515SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x40) != 0));2516}25172518axis = (packet->controllerState.rgucButtons[0] & 0x80) ? 32767 : -32768;2519SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);25202521axis = (packet->controllerState.rgucButtons[2] & 0x80) ? 32767 : -32768;2522SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);25232524axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);2525axis = ApplyStickCalibration(ctx, 0, 0, axis);2526SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);25272528axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4);2529axis = ApplyStickCalibration(ctx, 0, 1, axis);2530SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~axis);25312532axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8);2533axis = ApplyStickCalibration(ctx, 1, 0, axis);2534SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);25352536axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4);2537axis = ApplyStickCalibration(ctx, 1, 1, axis);2538SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~axis);2539}25402541/* High nibble of battery/connection byte is battery level, low nibble is connection status (always 0 on 8BitDo Pro 2)2542* LSB of connection nibble is USB/Switch connection status2543* LSB of the battery nibble is used to report charging.2544* The battery level is reported from 0(empty)-8(full)2545*/2546SDL_PowerState state;2547int charging = (packet->controllerState.ucBatteryAndConnection & 0x10);2548int level = (packet->controllerState.ucBatteryAndConnection & 0xE0) >> 4;2549int percent = (int)SDL_roundf((level / 8.0f) * 100.0f);25502551if (charging) {2552if (level == 8) {2553state = SDL_POWERSTATE_CHARGED;2554} else {2555state = SDL_POWERSTATE_CHARGING;2556}2557} else {2558state = SDL_POWERSTATE_ON_BATTERY;2559}2560SDL_SendJoystickPowerInfo(joystick, state, percent);25612562if (ctx->m_bReportSensors) {2563bool bHasSensorData = (packet->imuState[0].sAccelZ != 0 ||2564packet->imuState[0].sAccelY != 0 ||2565packet->imuState[0].sAccelX != 0);2566if (bHasSensorData) {2567const Uint32 IMU_UPDATE_RATE_SAMPLE_FREQUENCY = 1000;2568Uint64 sensor_timestamp[3];25692570ctx->m_bHasSensorData = true;25712572// We got three IMU samples, calculate the IMU update rate and timestamps2573ctx->m_unIMUSamples += 3;2574if (ctx->m_unIMUSamples >= IMU_UPDATE_RATE_SAMPLE_FREQUENCY) {2575Uint64 now = SDL_GetTicksNS();2576Uint64 elapsed = (now - ctx->m_ulIMUSampleTimestampNS);25772578if (elapsed > 0) {2579ctx->m_ulIMUUpdateIntervalNS = elapsed / ctx->m_unIMUSamples;2580}2581ctx->m_unIMUSamples = 0;2582ctx->m_ulIMUSampleTimestampNS = now;2583}25842585ctx->m_ulTimestampNS += ctx->m_ulIMUUpdateIntervalNS;2586sensor_timestamp[0] = ctx->m_ulTimestampNS;2587ctx->m_ulTimestampNS += ctx->m_ulIMUUpdateIntervalNS;2588sensor_timestamp[1] = ctx->m_ulTimestampNS;2589ctx->m_ulTimestampNS += ctx->m_ulIMUUpdateIntervalNS;2590sensor_timestamp[2] = ctx->m_ulTimestampNS;25912592if (!ctx->device->parent ||2593ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {2594SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO, sensor_timestamp[0], &packet->imuState[2].sGyroX);2595SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL, sensor_timestamp[0], &packet->imuState[2].sAccelX);25962597SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO, sensor_timestamp[1], &packet->imuState[1].sGyroX);2598SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL, sensor_timestamp[1], &packet->imuState[1].sAccelX);25992600SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO, sensor_timestamp[2], &packet->imuState[0].sGyroX);2601SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL, sensor_timestamp[2], &packet->imuState[0].sAccelX);2602}26032604if (ctx->device->parent &&2605ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {2606SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_L, sensor_timestamp[0], &packet->imuState[2].sGyroX);2607SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_L, sensor_timestamp[0], &packet->imuState[2].sAccelX);26082609SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_L, sensor_timestamp[1], &packet->imuState[1].sGyroX);2610SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_L, sensor_timestamp[1], &packet->imuState[1].sAccelX);26112612SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_L, sensor_timestamp[2], &packet->imuState[0].sGyroX);2613SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_L, sensor_timestamp[2], &packet->imuState[0].sAccelX);2614}2615if (ctx->device->parent &&2616ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {2617SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_R, sensor_timestamp[0], &packet->imuState[2].sGyroX);2618SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_R, sensor_timestamp[0], &packet->imuState[2].sAccelX);26192620SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_R, sensor_timestamp[1], &packet->imuState[1].sGyroX);2621SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_R, sensor_timestamp[1], &packet->imuState[1].sAccelX);26222623SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_R, sensor_timestamp[2], &packet->imuState[0].sGyroX);2624SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_R, sensor_timestamp[2], &packet->imuState[0].sAccelX);2625}26262627} else if (ctx->m_bHasSensorData) {2628// Uh oh, someone turned off the IMU?2629const int IMU_RESET_DELAY_MS = 3000;2630Uint64 now = SDL_GetTicks();26312632if (now >= (ctx->m_ulLastIMUReset + IMU_RESET_DELAY_MS)) {2633SDL_HIDAPI_Device *device = ctx->device;26342635if (device->updating) {2636SDL_UnlockMutex(device->dev_lock);2637}26382639SetIMUEnabled(ctx, true);26402641if (device->updating) {2642SDL_LockMutex(device->dev_lock);2643}2644ctx->m_ulLastIMUReset = now;2645}26462647} else {2648// We have never gotten IMU data, probably not supported on this device2649}2650}26512652ctx->m_lastFullState = *packet;2653}26542655static bool HIDAPI_DriverSwitch_UpdateDevice(SDL_HIDAPI_Device *device)2656{2657SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;2658SDL_Joystick *joystick = NULL;2659int size;2660int packet_count = 0;2661Uint64 now = SDL_GetTicks();26622663if (device->num_joysticks > 0) {2664joystick = SDL_GetJoystickFromID(device->joysticks[0]);2665}26662667while ((size = ReadInput(ctx)) > 0) {2668#ifdef DEBUG_SWITCH_PROTOCOL2669HIDAPI_DumpPacket("Nintendo Switch packet: size = %d", ctx->m_rgucReadBuffer, size);2670#endif2671++packet_count;2672ctx->m_ulLastInput = now;26732674if (!joystick) {2675continue;2676}26772678if (ctx->m_bInputOnly) {2679HandleInputOnlyControllerState(joystick, ctx, (SwitchInputOnlyControllerStatePacket_t *)&ctx->m_rgucReadBuffer[0]);2680} else {2681if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) {2682continue;2683}26842685ctx->m_nCurrentInputMode = ctx->m_rgucReadBuffer[0];26862687switch (ctx->m_rgucReadBuffer[0]) {2688case k_eSwitchInputReportIDs_SimpleControllerState:2689HandleSimpleControllerState(joystick, ctx, (SwitchSimpleStatePacket_t *)&ctx->m_rgucReadBuffer[1]);2690break;2691case k_eSwitchInputReportIDs_FullControllerState:2692case k_eSwitchInputReportIDs_FullControllerAndMcuState:2693// This is the extended report, we can enable sensors now in auto mode2694UpdateEnhancedModeOnEnhancedReport(ctx);26952696HandleFullControllerState(joystick, ctx, (SwitchStatePacket_t *)&ctx->m_rgucReadBuffer[1]);2697break;2698default:2699break;2700}2701}2702}27032704if (joystick) {2705if (packet_count == 0) {2706if (!ctx->m_bInputOnly && !device->is_bluetooth &&2707ctx->device->product_id != USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {2708const int INPUT_WAIT_TIMEOUT_MS = 100;2709if (now >= (ctx->m_ulLastInput + INPUT_WAIT_TIMEOUT_MS)) {2710// Steam may have put the controller back into non-reporting mode2711bool wasSyncWrite = ctx->m_bSyncWrite;27122713ctx->m_bSyncWrite = true;2714WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, false);2715ctx->m_bSyncWrite = wasSyncWrite;2716}2717} else if (device->is_bluetooth &&2718ctx->m_nCurrentInputMode != k_eSwitchInputReportIDs_SimpleControllerState) {2719const int INPUT_WAIT_TIMEOUT_MS = 3000;2720if (now >= (ctx->m_ulLastInput + INPUT_WAIT_TIMEOUT_MS)) {2721// Bluetooth may have disconnected, try reopening the controller2722size = -1;2723}2724}2725}27262727if (ctx->m_bRumblePending || ctx->m_bRumbleZeroPending) {2728HIDAPI_DriverSwitch_SendPendingRumble(ctx);2729} else if (ctx->m_bRumbleActive &&2730now >= (ctx->m_ulRumbleSent + RUMBLE_REFRESH_FREQUENCY_MS)) {2731#ifdef DEBUG_RUMBLE2732SDL_Log("Sent continuing rumble, %d ms after previous rumble", now - ctx->m_ulRumbleSent);2733#endif2734WriteRumble(ctx);2735}2736}27372738// Reconnect the Bluetooth device once the USB device is gone2739if (device->num_joysticks == 0 && device->is_bluetooth && packet_count > 0 &&2740!device->parent &&2741!HIDAPI_HasConnectedUSBDevice(device->serial)) {2742HIDAPI_JoystickConnected(device, NULL);2743}27442745if (size < 0 && device->num_joysticks > 0) {2746// Read error, device is disconnected2747HIDAPI_JoystickDisconnected(device, device->joysticks[0]);2748}2749return (size >= 0);2750}27512752static void HIDAPI_DriverSwitch_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)2753{2754SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;27552756if (!ctx->m_bInputOnly) {2757// Restore simple input mode for other applications2758if (!ctx->m_nInitialInputMode ||2759ctx->m_nInitialInputMode == k_eSwitchInputReportIDs_SimpleControllerState) {2760SetInputMode(ctx, k_eSwitchInputReportIDs_SimpleControllerState);2761}2762}27632764SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_ENHANCED_REPORTS,2765SDL_EnhancedReportsChanged, ctx);27662767if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft ||2768ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {2769SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED,2770SDL_HomeLEDHintChanged, ctx);2771} else {2772SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED,2773SDL_HomeLEDHintChanged, ctx);2774}27752776SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED,2777SDL_PlayerLEDHintChanged, ctx);27782779ctx->joystick = NULL;27802781ctx->m_bReportSensors = false;2782ctx->m_bEnhancedMode = false;2783ctx->m_bEnhancedModeAvailable = false;2784}27852786static void HIDAPI_DriverSwitch_FreeDevice(SDL_HIDAPI_Device *device)2787{2788}27892790SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverNintendoClassic = {2791SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC,2792true,2793HIDAPI_DriverNintendoClassic_RegisterHints,2794HIDAPI_DriverNintendoClassic_UnregisterHints,2795HIDAPI_DriverNintendoClassic_IsEnabled,2796HIDAPI_DriverNintendoClassic_IsSupportedDevice,2797HIDAPI_DriverSwitch_InitDevice,2798HIDAPI_DriverSwitch_GetDevicePlayerIndex,2799HIDAPI_DriverSwitch_SetDevicePlayerIndex,2800HIDAPI_DriverSwitch_UpdateDevice,2801HIDAPI_DriverSwitch_OpenJoystick,2802HIDAPI_DriverSwitch_RumbleJoystick,2803HIDAPI_DriverSwitch_RumbleJoystickTriggers,2804HIDAPI_DriverSwitch_GetJoystickCapabilities,2805HIDAPI_DriverSwitch_SetJoystickLED,2806HIDAPI_DriverSwitch_SendJoystickEffect,2807HIDAPI_DriverSwitch_SetJoystickSensorsEnabled,2808HIDAPI_DriverSwitch_CloseJoystick,2809HIDAPI_DriverSwitch_FreeDevice,2810};28112812SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverJoyCons = {2813SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS,2814true,2815HIDAPI_DriverJoyCons_RegisterHints,2816HIDAPI_DriverJoyCons_UnregisterHints,2817HIDAPI_DriverJoyCons_IsEnabled,2818HIDAPI_DriverJoyCons_IsSupportedDevice,2819HIDAPI_DriverSwitch_InitDevice,2820HIDAPI_DriverSwitch_GetDevicePlayerIndex,2821HIDAPI_DriverSwitch_SetDevicePlayerIndex,2822HIDAPI_DriverSwitch_UpdateDevice,2823HIDAPI_DriverSwitch_OpenJoystick,2824HIDAPI_DriverSwitch_RumbleJoystick,2825HIDAPI_DriverSwitch_RumbleJoystickTriggers,2826HIDAPI_DriverSwitch_GetJoystickCapabilities,2827HIDAPI_DriverSwitch_SetJoystickLED,2828HIDAPI_DriverSwitch_SendJoystickEffect,2829HIDAPI_DriverSwitch_SetJoystickSensorsEnabled,2830HIDAPI_DriverSwitch_CloseJoystick,2831HIDAPI_DriverSwitch_FreeDevice,2832};28332834SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch = {2835SDL_HINT_JOYSTICK_HIDAPI_SWITCH,2836true,2837HIDAPI_DriverSwitch_RegisterHints,2838HIDAPI_DriverSwitch_UnregisterHints,2839HIDAPI_DriverSwitch_IsEnabled,2840HIDAPI_DriverSwitch_IsSupportedDevice,2841HIDAPI_DriverSwitch_InitDevice,2842HIDAPI_DriverSwitch_GetDevicePlayerIndex,2843HIDAPI_DriverSwitch_SetDevicePlayerIndex,2844HIDAPI_DriverSwitch_UpdateDevice,2845HIDAPI_DriverSwitch_OpenJoystick,2846HIDAPI_DriverSwitch_RumbleJoystick,2847HIDAPI_DriverSwitch_RumbleJoystickTriggers,2848HIDAPI_DriverSwitch_GetJoystickCapabilities,2849HIDAPI_DriverSwitch_SetJoystickLED,2850HIDAPI_DriverSwitch_SendJoystickEffect,2851HIDAPI_DriverSwitch_SetJoystickSensorsEnabled,2852HIDAPI_DriverSwitch_CloseJoystick,2853HIDAPI_DriverSwitch_FreeDevice,2854};28552856#endif // SDL_JOYSTICK_HIDAPI_SWITCH28572858#endif // SDL_JOYSTICK_HIDAPI285928602861