24 #include "../../SDL_internal.h"
26 #ifdef SDL_JOYSTICK_HIDAPI
33 #include "../../SDL_hints_c.h"
34 #include "../SDL_sysjoystick.h"
39 #ifdef SDL_JOYSTICK_HIDAPI_SWITCH
50 #define RUMBLE_WRITE_FREQUENCY_MS 25
53 #define RUMBLE_REFRESH_FREQUENCY_MS 40
56 k_eSwitchInputReportIDs_SubcommandReply = 0x21,
57 k_eSwitchInputReportIDs_FullControllerState = 0x30,
58 k_eSwitchInputReportIDs_SimpleControllerState = 0x3F,
59 k_eSwitchInputReportIDs_CommandAck = 0x81,
60 } ESwitchInputReportIDs;
63 k_eSwitchOutputReportIDs_RumbleAndSubcommand = 0x01,
64 k_eSwitchOutputReportIDs_Rumble = 0x10,
65 k_eSwitchOutputReportIDs_Proprietary = 0x80,
66 } ESwitchOutputReportIDs;
69 k_eSwitchSubcommandIDs_BluetoothManualPair = 0x01,
70 k_eSwitchSubcommandIDs_RequestDeviceInfo = 0x02,
71 k_eSwitchSubcommandIDs_SetInputReportMode = 0x03,
72 k_eSwitchSubcommandIDs_SetHCIState = 0x06,
73 k_eSwitchSubcommandIDs_SPIFlashRead = 0x10,
74 k_eSwitchSubcommandIDs_SetPlayerLights = 0x30,
75 k_eSwitchSubcommandIDs_SetHomeLight = 0x38,
76 k_eSwitchSubcommandIDs_EnableIMU = 0x40,
77 k_eSwitchSubcommandIDs_SetIMUSensitivity = 0x41,
78 k_eSwitchSubcommandIDs_EnableVibration = 0x48,
79 } ESwitchSubcommandIDs;
82 k_eSwitchProprietaryCommandIDs_Handshake = 0x02,
83 k_eSwitchProprietaryCommandIDs_HighSpeed = 0x03,
84 k_eSwitchProprietaryCommandIDs_ForceUSB = 0x04,
85 k_eSwitchProprietaryCommandIDs_ClearUSB = 0x05,
86 k_eSwitchProprietaryCommandIDs_ResetMCU = 0x06,
87 } ESwitchProprietaryCommandIDs;
90 k_eSwitchDeviceInfoControllerType_JoyConLeft = 0x1,
91 k_eSwitchDeviceInfoControllerType_JoyConRight = 0x2,
92 k_eSwitchDeviceInfoControllerType_ProController = 0x3,
93 } ESwitchDeviceInfoControllerType;
95 #define k_unSwitchOutputPacketDataLength 49
96 #define k_unSwitchMaxOutputPacketLength 64
97 #define k_unSwitchBluetoothPacketLength k_unSwitchOutputPacketDataLength
98 #define k_unSwitchUSBPacketLength k_unSwitchMaxOutputPacketLength
100 #define k_unSPIStickCalibrationStartOffset 0x603D
101 #define k_unSPIStickCalibrationEndOffset 0x604E
102 #define k_unSPIStickCalibrationLength (k_unSPIStickCalibrationEndOffset - k_unSPIStickCalibrationStartOffset + 1)
107 Uint8 rgucButtons[2];
109 Uint8 rgucJoystickLeft[2];
110 Uint8 rgucJoystickRight[2];
111 } SwitchInputOnlyControllerStatePacket_t;
115 Uint8 rgucButtons[2];
119 } SwitchSimpleStatePacket_t;
124 Uint8 ucBatteryAndConnection;
125 Uint8 rgucButtons[3];
126 Uint8 rgucJoystickLeft[3];
127 Uint8 rgucJoystickRight[3];
128 Uint8 ucVibrationCode;
129 } SwitchControllerStatePacket_t;
133 SwitchControllerStatePacket_t controllerState;
144 } SwitchStatePacket_t;
154 SwitchControllerStatePacket_t m_controllerState;
156 Uint8 ucSubcommandAck;
157 Uint8 ucSubcommandID;
159 #define k_unSubcommandDataBytes 35
161 Uint8 rgucSubcommandData[k_unSubcommandDataBytes];
164 SwitchSPIOpData_t opData;
165 Uint8 rgucReadData[k_unSubcommandDataBytes -
sizeof(SwitchSPIOpData_t)];
169 Uint8 rgucFirmwareVersion[2];
172 Uint8 rgucMACAddress[6];
174 Uint8 ucColorLocation;
177 } SwitchSubcommandInputPacket_t;
182 } SwitchRumbleData_t;
187 Uint8 ucPacketNumber;
188 SwitchRumbleData_t rumbleData[2];
189 } SwitchCommonOutputPacket_t;
193 SwitchCommonOutputPacket_t commonData;
195 Uint8 ucSubcommandID;
196 Uint8 rgucSubcommandData[k_unSwitchOutputPacketDataLength -
sizeof(SwitchCommonOutputPacket_t) - 1];
197 } SwitchSubcommandOutputPacket_t;
202 Uint8 ucProprietaryID;
204 Uint8 rgucProprietaryData[k_unSwitchOutputPacketDataLength - 1 - 1];
205 } SwitchProprietaryOutputPacket_t;
215 Uint8 m_nCommandNumber;
216 SwitchCommonOutputPacket_t m_RumblePacket;
217 Uint8 m_rgucReadBuffer[k_unSwitchMaxOutputPacketLength];
224 SwitchInputOnlyControllerStatePacket_t m_lastInputOnlyState;
225 SwitchSimpleStatePacket_t m_lastSimpleState;
226 SwitchStatePacket_t m_lastFullState;
228 struct StickCalibrationData {
236 struct StickExtents {
242 } SDL_DriverSwitch_Context;
246 HasHomeLED(
int vendor_id,
int product_id)
249 if (vendor_id == 0 && product_id == 0) {
254 if (vendor_id == 0x0f0d && product_id == 0x00f6) {
262 IsGameCubeFormFactor(
int vendor_id,
int product_id)
264 static Uint32 gamecube_formfactor[] = {
272 if (
id == gamecube_formfactor[
i]) {
295 HIDAPI_DriverSwitch_GetDeviceName(
Uint16 vendor_id,
Uint16 product_id)
298 return "Nintendo Switch Pro Controller";
301 static int ReadInput(SDL_DriverSwitch_Context *
ctx)
311 static int WriteOutput(SDL_DriverSwitch_Context *
ctx,
const Uint8 *
data,
int size)
314 if (SDL_HIDAPI_LockRumble() < 0) {
317 return SDL_HIDAPI_SendRumbleAndUnlock(
ctx->device,
data,
size);
320 static SwitchSubcommandInputPacket_t *ReadSubcommandReply(SDL_DriverSwitch_Context *
ctx, ESwitchSubcommandIDs expectedID)
327 while ((nRead = ReadInput(
ctx)) != -1) {
329 if (
ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) {
330 SwitchSubcommandInputPacket_t *reply = (SwitchSubcommandInputPacket_t *)&
ctx->m_rgucReadBuffer[1];
331 if (reply->ucSubcommandID == expectedID && (reply->ucSubcommandAck & 0x80)) {
346 static SDL_bool ReadProprietaryReply(SDL_DriverSwitch_Context *
ctx, ESwitchProprietaryCommandIDs expectedID)
353 while ((nRead = ReadInput(
ctx)) != -1) {
355 if (
ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_CommandAck &&
ctx->m_rgucReadBuffer[1] == expectedID) {
369 static void ConstructSubcommand(SDL_DriverSwitch_Context *
ctx, ESwitchSubcommandIDs ucCommandID,
Uint8 *pBuf,
Uint8 ucLen, SwitchSubcommandOutputPacket_t *outPacket)
373 outPacket->commonData.ucPacketType = k_eSwitchOutputReportIDs_RumbleAndSubcommand;
374 outPacket->commonData.ucPacketNumber =
ctx->m_nCommandNumber;
376 SDL_memcpy(&outPacket->commonData.rumbleData, &
ctx->m_RumblePacket.rumbleData,
sizeof(
ctx->m_RumblePacket.rumbleData));
378 outPacket->ucSubcommandID = ucCommandID;
379 SDL_memcpy(outPacket->rgucSubcommandData, pBuf, ucLen);
381 ctx->m_nCommandNumber = (
ctx->m_nCommandNumber + 1) & 0xF;
384 static SDL_bool WritePacket(SDL_DriverSwitch_Context *
ctx,
void *pBuf,
Uint8 ucLen)
386 Uint8 rgucBuf[k_unSwitchMaxOutputPacketLength];
387 const size_t unWriteSize =
ctx->m_bUsingBluetooth ? k_unSwitchBluetoothPacketLength : k_unSwitchUSBPacketLength;
389 if (ucLen > k_unSwitchOutputPacketDataLength) {
393 if (ucLen < unWriteSize) {
395 SDL_memset(rgucBuf+ucLen, 0, unWriteSize-ucLen);
397 ucLen = (
Uint8)unWriteSize;
399 return (WriteOutput(
ctx, (
Uint8 *)pBuf, ucLen) >= 0);
402 static SDL_bool WriteSubcommand(SDL_DriverSwitch_Context *
ctx, ESwitchSubcommandIDs ucCommandID,
Uint8 *pBuf,
Uint8 ucLen, SwitchSubcommandInputPacket_t **ppReply)
405 SwitchSubcommandInputPacket_t *reply =
NULL;
407 while (!reply && nRetries--) {
408 SwitchSubcommandOutputPacket_t commandPacket;
409 ConstructSubcommand(
ctx, ucCommandID, pBuf, ucLen, &commandPacket);
411 if (!WritePacket(
ctx, &commandPacket,
sizeof(commandPacket))) {
415 reply = ReadSubcommandReply(
ctx, ucCommandID);
421 return reply !=
NULL;
424 static SDL_bool WriteProprietary(SDL_DriverSwitch_Context *
ctx, ESwitchProprietaryCommandIDs ucCommand,
Uint8 *pBuf,
Uint8 ucLen,
SDL_bool waitForReply)
429 SwitchProprietaryOutputPacket_t packet;
431 if ((!pBuf && ucLen > 0) || ucLen >
sizeof(packet.rgucProprietaryData)) {
435 packet.ucPacketType = k_eSwitchOutputReportIDs_Proprietary;
436 packet.ucProprietaryID = ucCommand;
438 SDL_memcpy(packet.rgucProprietaryData, pBuf, ucLen);
441 if (!WritePacket(
ctx, &packet,
sizeof(packet))) {
445 if (!waitForReply || ReadProprietaryReply(
ctx, ucCommand)) {
452 static void SetNeutralRumble(SwitchRumbleData_t *pRumble)
454 pRumble->rgucData[0] = 0x00;
455 pRumble->rgucData[1] = 0x01;
456 pRumble->rgucData[2] = 0x40;
457 pRumble->rgucData[3] = 0x40;
460 static void EncodeRumble(SwitchRumbleData_t *pRumble,
Uint16 usHighFreq,
Uint8 ucHighFreqAmp,
Uint8 ucLowFreq,
Uint16 usLowFreqAmp)
462 if (ucHighFreqAmp > 0 || usLowFreqAmp > 0) {
465 pRumble->rgucData[0] = usHighFreq & 0xFF;
466 pRumble->rgucData[1] = ucHighFreqAmp | ((usHighFreq >> 8) & 0x01);
468 pRumble->rgucData[2] = ucLowFreq | ((usLowFreqAmp >> 8) & 0x80);
469 pRumble->rgucData[3] = usLowFreqAmp & 0xFF;
472 SDL_Log(
"Freq: %.2X %.2X %.2X, Amp: %.2X %.2X %.2X\n",
473 usHighFreq & 0xFF, ((usHighFreq >> 8) & 0x01), ucLowFreq,
474 ucHighFreqAmp, ((usLowFreqAmp >> 8) & 0x80), usLowFreqAmp & 0xFF);
477 SetNeutralRumble(pRumble);
481 static SDL_bool WriteRumble(SDL_DriverSwitch_Context *
ctx)
486 ctx->m_RumblePacket.ucPacketType = k_eSwitchOutputReportIDs_Rumble;
487 ctx->m_RumblePacket.ucPacketNumber =
ctx->m_nCommandNumber;
488 ctx->m_nCommandNumber = (
ctx->m_nCommandNumber + 1) & 0xF;
493 return WritePacket(
ctx, (
Uint8 *)&
ctx->m_RumblePacket,
sizeof(
ctx->m_RumblePacket));
496 static SDL_bool BTrySetupUSB(SDL_DriverSwitch_Context *
ctx)
503 if (!WriteProprietary(
ctx, k_eSwitchProprietaryCommandIDs_Handshake,
NULL, 0,
SDL_TRUE)) {
506 if (!WriteProprietary(
ctx, k_eSwitchProprietaryCommandIDs_HighSpeed,
NULL, 0,
SDL_TRUE)) {
510 if (!WriteProprietary(
ctx, k_eSwitchProprietaryCommandIDs_Handshake,
NULL, 0,
SDL_TRUE)) {
518 return WriteSubcommand(
ctx, k_eSwitchSubcommandIDs_EnableVibration, &
enabled,
sizeof(
enabled),
NULL);
521 static SDL_bool SetInputMode(SDL_DriverSwitch_Context *
ctx,
Uint8 input_mode)
523 return WriteSubcommand(
ctx, k_eSwitchSubcommandIDs_SetInputReportMode, &input_mode, 1,
NULL);
528 Uint8 ucLedIntensity = 0;
531 if (brightness > 0) {
532 if (brightness < 65) {
533 ucLedIntensity = (brightness + 5) / 10;
539 rgucBuffer[0] = (0x0 << 4) | 0x1;
540 rgucBuffer[1] = ((ucLedIntensity & 0xF) << 4) | 0x0;
541 rgucBuffer[2] = ((ucLedIntensity & 0xF) << 4) | 0x0;
542 rgucBuffer[3] = (0x0 << 4) | 0x0;
544 return WriteSubcommand(
ctx, k_eSwitchSubcommandIDs_SetHomeLight, rgucBuffer,
sizeof(rgucBuffer),
NULL);
549 Uint8 led_data = (1 << slot);
550 return WriteSubcommand(
ctx, k_eSwitchSubcommandIDs_SetPlayerLights, &led_data,
sizeof(led_data),
NULL);
553 static SDL_bool LoadStickCalibration(SDL_DriverSwitch_Context *
ctx,
Uint8 input_mode)
557 SwitchSubcommandInputPacket_t *reply =
NULL;
560 SwitchSPIOpData_t readParams;
561 readParams.unAddress = k_unSPIStickCalibrationStartOffset;
562 readParams.ucLength = k_unSPIStickCalibrationLength;
564 if (!WriteSubcommand(
ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (
uint8_t *)&readParams,
sizeof(readParams), &reply)) {
573 pStickCal = reply->spiReadData.rgucReadData;
576 ctx->m_StickCalData[0].axis[0].sMax = ((pStickCal[1] << 8) & 0xF00) | pStickCal[0];
577 ctx->m_StickCalData[0].axis[1].sMax = (pStickCal[2] << 4) | (pStickCal[1] >> 4);
578 ctx->m_StickCalData[0].axis[0].sCenter = ((pStickCal[4] << 8) & 0xF00) | pStickCal[3];
579 ctx->m_StickCalData[0].axis[1].sCenter = (pStickCal[5] << 4) | (pStickCal[4] >> 4);
580 ctx->m_StickCalData[0].axis[0].sMin = ((pStickCal[7] << 8) & 0xF00) | pStickCal[6];
581 ctx->m_StickCalData[0].axis[1].sMin = (pStickCal[8] << 4) | (pStickCal[7] >> 4);
584 ctx->m_StickCalData[1].axis[0].sCenter = ((pStickCal[10] << 8) & 0xF00) | pStickCal[9];
585 ctx->m_StickCalData[1].axis[1].sCenter = (pStickCal[11] << 4) | (pStickCal[10] >> 4);
586 ctx->m_StickCalData[1].axis[0].sMin = ((pStickCal[13] << 8) & 0xF00) | pStickCal[12];
587 ctx->m_StickCalData[1].axis[1].sMin = (pStickCal[14] << 4) | (pStickCal[13] >> 4);
588 ctx->m_StickCalData[1].axis[0].sMax = ((pStickCal[16] << 8) & 0xF00) | pStickCal[15];
589 ctx->m_StickCalData[1].axis[1].sMax = (pStickCal[17] << 4) | (pStickCal[16] >> 4);
592 for (stick = 0; stick < 2; ++stick) {
594 if (
ctx->m_StickCalData[stick].axis[
axis].sCenter == 0xFFF) {
595 ctx->m_StickCalData[stick].axis[
axis].sCenter = 0;
597 if (
ctx->m_StickCalData[stick].axis[
axis].sMax == 0xFFF) {
598 ctx->m_StickCalData[stick].axis[
axis].sMax = 0;
600 if (
ctx->m_StickCalData[stick].axis[
axis].sMin == 0xFFF) {
601 ctx->m_StickCalData[stick].axis[
axis].sMin = 0;
606 if (input_mode == k_eSwitchInputReportIDs_SimpleControllerState) {
607 for (stick = 0; stick < 2; ++stick) {
614 for (stick = 0; stick < 2; ++stick) {
616 ctx->m_StickExtents[stick].axis[
axis].sMin = -(
Sint16)(
ctx->m_StickCalData[stick].axis[
axis].sMin * 0.7f);
617 ctx->m_StickExtents[stick].axis[
axis].sMax = (
Sint16)(
ctx->m_StickCalData[stick].axis[
axis].sMax * 0.7f);
624 static float fsel(
float fComparand,
float fValGE,
float fLT)
626 return fComparand >= 0 ? fValGE : fLT;
629 static float RemapVal(
float val,
float A,
float B,
float C,
float D)
632 return fsel(
val - B , D , C);
634 return C + (D - C) * (
val - A) / (B - A);
637 static Sint16 ApplyStickCalibrationCentered(SDL_DriverSwitch_Context *
ctx,
int nStick,
int nAxis,
Sint16 sRawValue,
Sint16 sCenter)
639 sRawValue -= sCenter;
641 if (sRawValue >
ctx->m_StickExtents[nStick].axis[nAxis].sMax) {
642 ctx->m_StickExtents[nStick].axis[nAxis].sMax = sRawValue;
644 if (sRawValue < ctx->m_StickExtents[nStick].
axis[nAxis].sMin) {
645 ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue;
655 static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *
ctx,
int nStick,
int nAxis,
Sint16 sRawValue)
657 return ApplyStickCalibrationCentered(
ctx, nStick, nAxis, sRawValue,
ctx->m_StickCalData[nStick].axis[nAxis].sCenter);
660 static void SDLCALL SDL_GameControllerButtonReportingHintChanged(
void *userdata,
const char *
name,
const char *oldValue,
const char *hint)
662 SDL_DriverSwitch_Context *
ctx = (SDL_DriverSwitch_Context *)userdata;
668 if (!
ctx->m_bUseButtonLabels) {
670 if (
ctx->m_bIsGameCube) {
717 SDL_DriverSwitch_Context *
ctx;
736 if (!
ctx->m_bInputOnly) {
737 ctx->m_bHasHomeLED = HasHomeLED(
device->vendor_id,
device->product_id);
740 SetNeutralRumble(&
ctx->m_RumblePacket.rumbleData[0]);
741 SetNeutralRumble(&
ctx->m_RumblePacket.rumbleData[1]);
744 if (!BTrySetupUSB(
ctx)) {
749 if (
ctx->m_bUsingBluetooth) {
750 input_mode = k_eSwitchInputReportIDs_SimpleControllerState;
752 input_mode = k_eSwitchInputReportIDs_FullControllerState;
762 input_mode = k_eSwitchInputReportIDs_FullControllerState;
765 if (!LoadStickCalibration(
ctx, input_mode)) {
770 if (!SetVibrationEnabled(
ctx, 1)) {
776 if (!SetInputMode(
ctx, input_mode)) {
782 if (!
ctx->m_bUsingBluetooth) {
784 if (!WriteProprietary(
ctx, k_eSwitchProprietaryCommandIDs_ForceUSB,
NULL, 0,
SDL_FALSE)) {
791 if (
ctx->m_bHasHomeLED) {
792 SetHomeLED(
ctx, 100);
797 if (IsGameCubeFormFactor(
device->vendor_id,
device->product_id)) {
803 SDL_GameControllerButtonReportingHintChanged,
ctx);
825 HIDAPI_DriverSwitch_ActuallyRumbleJoystick(SDL_DriverSwitch_Context *
ctx,
Uint16 low_frequency_rumble,
Uint16 high_frequency_rumble)
833 const Uint16 k_usHighFreq = 0x0074;
834 const Uint8 k_ucHighFreqAmp = 0xBE;
835 const Uint8 k_ucLowFreq = 0x3D;
836 const Uint16 k_usLowFreqAmp = 0x806F;
838 if (low_frequency_rumble) {
839 EncodeRumble(&
ctx->m_RumblePacket.rumbleData[0], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
841 SetNeutralRumble(&
ctx->m_RumblePacket.rumbleData[0]);
844 if (high_frequency_rumble) {
845 EncodeRumble(&
ctx->m_RumblePacket.rumbleData[1], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
847 SetNeutralRumble(&
ctx->m_RumblePacket.rumbleData[1]);
852 if (!WriteRumble(
ctx)) {
860 HIDAPI_DriverSwitch_SendPendingRumble(SDL_DriverSwitch_Context *
ctx)
862 if ((
SDL_GetTicks() -
ctx->m_unRumbleSent) < RUMBLE_WRITE_FREQUENCY_MS) {
866 if (
ctx->m_bRumblePending) {
871 SDL_Log(
"Sent pending rumble %d/%d\n", low_frequency_rumble, high_frequency_rumble);
874 ctx->m_unRumblePending = 0;
876 return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(
ctx, low_frequency_rumble, high_frequency_rumble);
879 if (
ctx->m_bRumbleZeroPending) {
883 SDL_Log(
"Sent pending zero rumble\n");
885 return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(
ctx, 0, 0);
894 SDL_DriverSwitch_Context *
ctx = (SDL_DriverSwitch_Context *)
device->context;
896 if (
ctx->m_bRumblePending) {
897 if (HIDAPI_DriverSwitch_SendPendingRumble(
ctx) < 0) {
902 if (
ctx->m_bUsingBluetooth && (
SDL_GetTicks() -
ctx->m_unRumbleSent) < RUMBLE_WRITE_FREQUENCY_MS) {
903 if (low_frequency_rumble || high_frequency_rumble) {
904 Uint32 unRumblePending = ((
Uint32)low_frequency_rumble << 16) | high_frequency_rumble;
907 if (unRumblePending >
ctx->m_unRumblePending) {
908 ctx->m_unRumblePending = unRumblePending;
920 SDL_Log(
"Sent rumble %d/%d\n", low_frequency_rumble, high_frequency_rumble);
923 return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(
ctx, low_frequency_rumble, high_frequency_rumble);
951 static void HandleInputOnlyControllerState(SDL_Joystick *
joystick, SDL_DriverSwitch_Context *
ctx, SwitchInputOnlyControllerStatePacket_t *packet)
955 if (packet->rgucButtons[0] !=
ctx->m_lastInputOnlyState.rgucButtons[0]) {
964 axis = (
data & 0x40) ? 32767 : -32768;
967 axis = (
data & 0x80) ? 32767 : -32768;
971 if (packet->rgucButtons[1] !=
ctx->m_lastInputOnlyState.rgucButtons[1]) {
981 if (packet->ucStickHat !=
ctx->m_lastInputOnlyState.ucStickHat) {
987 switch (packet->ucStickHat) {
1025 if (packet->rgucJoystickLeft[0] !=
ctx->m_lastInputOnlyState.rgucJoystickLeft[0]) {
1030 if (packet->rgucJoystickLeft[1] !=
ctx->m_lastInputOnlyState.rgucJoystickLeft[1]) {
1035 if (packet->rgucJoystickRight[0] !=
ctx->m_lastInputOnlyState.rgucJoystickRight[0]) {
1040 if (packet->rgucJoystickRight[1] !=
ctx->m_lastInputOnlyState.rgucJoystickRight[1]) {
1045 ctx->m_lastInputOnlyState = *packet;
1048 static void HandleSimpleControllerState(SDL_Joystick *
joystick, SDL_DriverSwitch_Context *
ctx, SwitchSimpleStatePacket_t *packet)
1051 const Uint16 usJoystickCenter = 0x8000;
1054 if (packet->rgucButtons[0] !=
ctx->m_lastSimpleState.rgucButtons[0]) {
1063 axis = (
data & 0x40) ? 32767 : -32768;
1066 axis = (
data & 0x80) ? 32767 : -32768;
1070 if (packet->rgucButtons[1] !=
ctx->m_lastSimpleState.rgucButtons[1]) {
1080 if (packet->ucStickHat !=
ctx->m_lastSimpleState.ucStickHat) {
1086 switch (packet->ucStickHat) {
1124 axis = ApplyStickCalibrationCentered(
ctx, 0, 0, packet->sJoystickLeft[0], (
Sint16)usJoystickCenter);
1127 axis = ApplyStickCalibrationCentered(
ctx, 0, 1, packet->sJoystickLeft[1], (
Sint16)usJoystickCenter);
1130 axis = ApplyStickCalibrationCentered(
ctx, 1, 0, packet->sJoystickRight[0], (
Sint16)usJoystickCenter);
1133 axis = ApplyStickCalibrationCentered(
ctx, 1, 1, packet->sJoystickRight[1], (
Sint16)usJoystickCenter);
1136 ctx->m_lastSimpleState = *packet;
1139 static void HandleFullControllerState(SDL_Joystick *
joystick, SDL_DriverSwitch_Context *
ctx, SwitchStatePacket_t *packet)
1143 if (packet->controllerState.rgucButtons[0] !=
ctx->m_lastFullState.controllerState.rgucButtons[0]) {
1144 Uint8 data = packet->controllerState.rgucButtons[0];
1150 axis = (
data & 0x80) ? 32767 : -32768;
1154 if (packet->controllerState.rgucButtons[1] !=
ctx->m_lastFullState.controllerState.rgucButtons[1]) {
1155 Uint8 data = packet->controllerState.rgucButtons[1];
1165 if (packet->controllerState.rgucButtons[2] !=
ctx->m_lastFullState.controllerState.rgucButtons[2]) {
1166 Uint8 data = packet->controllerState.rgucButtons[2];
1172 axis = (
data & 0x80) ? 32767 : -32768;
1176 axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);
1180 axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4);
1184 axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8);
1188 axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4);
1195 if (packet->controllerState.ucBatteryAndConnection & 0x1) {
1201 int level = (packet->controllerState.ucBatteryAndConnection & 0xE0) >> 4;
1204 }
else if (
level <= 2) {
1206 }
else if (
level <= 6) {
1213 ctx->m_lastFullState = *packet;
1219 SDL_DriverSwitch_Context *
ctx = (SDL_DriverSwitch_Context *)
device->context;
1230 while ((
size = ReadInput(
ctx)) > 0) {
1231 #ifdef DEBUG_SWITCH_PROTOCOL
1234 if (
ctx->m_bInputOnly) {
1235 HandleInputOnlyControllerState(
joystick,
ctx, (SwitchInputOnlyControllerStatePacket_t *)&
ctx->m_rgucReadBuffer[0]);
1237 switch (
ctx->m_rgucReadBuffer[0]) {
1238 case k_eSwitchInputReportIDs_SimpleControllerState:
1239 HandleSimpleControllerState(
joystick,
ctx, (SwitchSimpleStatePacket_t *)&
ctx->m_rgucReadBuffer[1]);
1241 case k_eSwitchInputReportIDs_FullControllerState:
1242 HandleFullControllerState(
joystick,
ctx, (SwitchStatePacket_t *)&
ctx->m_rgucReadBuffer[1]);
1250 if (
ctx->m_bRumblePending ||
ctx->m_bRumbleZeroPending) {
1251 HIDAPI_DriverSwitch_SendPendingRumble(
ctx);
1252 }
else if (
ctx->m_bRumbleActive &&
1255 SDL_Log(
"Sent continuing rumble\n");
1270 SDL_DriverSwitch_Context *
ctx = (SDL_DriverSwitch_Context *)
device->context;
1272 if (!
ctx->m_bInputOnly) {
1274 SetInputMode(
ctx, k_eSwitchInputReportIDs_SimpleControllerState);
1278 SDL_GameControllerButtonReportingHintChanged,
ctx);
1296 HIDAPI_DriverSwitch_IsSupportedDevice,
1297 HIDAPI_DriverSwitch_GetDeviceName,
1298 HIDAPI_DriverSwitch_InitDevice,
1299 HIDAPI_DriverSwitch_GetDevicePlayerIndex,
1300 HIDAPI_DriverSwitch_SetDevicePlayerIndex,
1301 HIDAPI_DriverSwitch_UpdateDevice,
1302 HIDAPI_DriverSwitch_OpenJoystick,
1303 HIDAPI_DriverSwitch_RumbleJoystick,
1304 HIDAPI_DriverSwitch_RumbleJoystickTriggers,
1305 HIDAPI_DriverSwitch_HasJoystickLED,
1306 HIDAPI_DriverSwitch_SetJoystickLED,
1307 HIDAPI_DriverSwitch_SetJoystickSensorsEnabled,
1308 HIDAPI_DriverSwitch_CloseJoystick,
1309 HIDAPI_DriverSwitch_FreeDevice,
#define SDL_DelHintCallback
#define SDL_JoystickFromInstanceID
#define SDL_AddHintCallback
#define SDL_OutOfMemory()
#define SDL_Unsupported()
@ SDL_CONTROLLER_AXIS_LEFTX
@ SDL_CONTROLLER_AXIS_TRIGGERRIGHT
@ SDL_CONTROLLER_AXIS_RIGHTY
@ SDL_CONTROLLER_AXIS_RIGHTX
@ SDL_CONTROLLER_AXIS_MAX
@ SDL_CONTROLLER_AXIS_TRIGGERLEFT
@ SDL_CONTROLLER_AXIS_LEFTY
@ SDL_CONTROLLER_BUTTON_B
@ SDL_CONTROLLER_BUTTON_BACK
@ SDL_CONTROLLER_BUTTON_LEFTSTICK
@ SDL_CONTROLLER_BUTTON_START
@ SDL_CONTROLLER_BUTTON_DPAD_LEFT
@ SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
@ SDL_CONTROLLER_BUTTON_DPAD_DOWN
@ SDL_CONTROLLER_BUTTON_DPAD_UP
@ SDL_CONTROLLER_BUTTON_LEFTSHOULDER
@ SDL_CONTROLLER_BUTTON_GUIDE
@ SDL_CONTROLLER_BUTTON_DPAD_RIGHT
@ SDL_CONTROLLER_BUTTON_MISC1
@ SDL_CONTROLLER_BUTTON_X
@ SDL_CONTROLLER_BUTTON_RIGHTSTICK
@ SDL_CONTROLLER_BUTTON_Y
@ SDL_CONTROLLER_BUTTON_A
@ SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO
void HIDAPI_DumpPacket(const char *prefix, Uint8 *data, int size)
SDL_bool HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID)
void HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID)
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch
SDL_bool SDL_GetStringBoolean(const char *value, SDL_bool default_value)
#define SDL_HINT_JOYSTICK_HIDAPI_SWITCH
A variable controlling whether the HIDAPI driver for Nintendo Switch controllers should be used.
#define SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS
If set, game controller face buttons report their values according to their labels instead of their p...
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
SDL_bool SDL_IsJoystickNintendoSwitchProInputOnly(Uint16 vendor_id, Uint16 product_id)
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
@ SDL_JOYSTICK_POWER_FULL
@ SDL_JOYSTICK_POWER_MEDIUM
@ SDL_JOYSTICK_POWER_EMPTY
@ SDL_JOYSTICK_POWER_WIRED
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
GLuint GLuint GLsizei GLenum type
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLuint const GLchar * name
#define SDL_MAX_SINT16
A signed 16-bit integer type.
#define SDL_arraysize(array)
#define SDL_MAX_UINT8
An unsigned 8-bit integer type.
#define MAKE_VIDPID(VID, PID)
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
#define SDL_TICKS_PASSED(A, B)
Compare SDL ticks values, and return true if A has passed B.
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds)
Read an Input report from a HID device with timeout.
HID_API_EXPORT hid_device *HID_API_CALL hid_open_path(const char *path, int bExclusive)
Open a HID device by its path name.
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device)
Close a HID device.
static SDL_AudioDeviceID device
set set set set set set set macro pixldst1 abits if abits op else op endif endm macro pixldst2 abits if abits op else op endif endm macro pixldst4 abits if abits op else op endif endm macro pixldst0 abits op endm macro pixldst3 mem_operand op endm macro pixldst30 mem_operand op endm macro pixldst abits if abits elseif abits elseif abits elseif abits elseif abits pixldst0 abits else pixldst0 abits pixldst0 abits pixldst0 abits pixldst0 abits endif elseif abits else pixldst0 abits pixldst0 abits endif elseif abits else error unsupported bpp *numpix else pixst endif endm macro pixld1_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl else error unsupported endif endm macro pixld2_s mem_operand if mov asr add asl add asl mov asr sub UNIT_X add asl mov asr add asl add asl mov asr add UNIT_X add asl else pixld1_s mem_operand pixld1_s mem_operand endif endm macro pixld0_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl endif endm macro pixld_s_internal mem_operand if mem_operand pixld2_s mem_operand pixdeinterleave basereg elseif mem_operand elseif mem_operand elseif mem_operand elseif mem_operand pixld0_s mem_operand else pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else error unsupported mem_operand if bpp mem_operand endif endm macro vuzp8 reg2 vuzp d d ®2 endm macro vzip8 reg2 vzip d d ®2 endm macro pixdeinterleave basereg basereg basereg basereg basereg endif endm macro pixinterleave basereg basereg basereg basereg basereg endif endm macro PF boost_increment endif if endif PF tst PF addne PF subne PF cmp ORIG_W if endif if endif if endif PF subge ORIG_W PF subges if endif if endif if endif endif endm macro cache_preload_simple endif if dst_r_bpp pld[DST_R, #(PREFETCH_DISTANCE_SIMPLE *dst_r_bpp/8)] endif if mask_bpp pld if[MASK, #(PREFETCH_DISTANCE_SIMPLE *mask_bpp/8)] endif endif endm macro fetch_mask_pixblock pixld mask_basereg pixblock_size MASK endm macro ensure_destination_ptr_alignment process_pixblock_tail_head if beq irp skip1(dst_w_bpp<=(lowbit *8)) &&((lowbit *8)<(pixblock_size *dst_w_bpp)) .if lowbit< 16 tst DST_R
static SDL_Joystick * joystick
#define USB_VENDOR_NINTENDO
#define USB_PRODUCT_NINTENDO_SWITCH_PRO