mirror of https://github.com/encounter/SDL.git
Added support for third party Nintendo Switch controllers that don't support the full protocol
This commit is contained in:
parent
43c5f62d44
commit
e6ac16ef2f
|
@ -1171,6 +1171,13 @@ SDL_IsJoystickNintendoSwitchPro(Uint16 vendor, Uint16 product)
|
||||||
eType == k_eControllerType_SwitchInputOnlyController);
|
eType == k_eControllerType_SwitchInputOnlyController);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_bool
|
||||||
|
SDL_IsJoystickNintendoSwitchProInputOnly(Uint16 vendor, Uint16 product)
|
||||||
|
{
|
||||||
|
EControllerType eType = GuessControllerType(vendor, product);
|
||||||
|
return (eType == k_eControllerType_SwitchInputOnlyController);
|
||||||
|
}
|
||||||
|
|
||||||
SDL_bool
|
SDL_bool
|
||||||
SDL_IsJoystickSteamController(Uint16 vendor, Uint16 product)
|
SDL_IsJoystickSteamController(Uint16 vendor, Uint16 product)
|
||||||
{
|
{
|
||||||
|
|
|
@ -55,7 +55,8 @@ extern void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint1
|
||||||
extern SDL_bool SDL_IsJoystickPS4(Uint16 vendor_id, Uint16 product_id);
|
extern SDL_bool SDL_IsJoystickPS4(Uint16 vendor_id, Uint16 product_id);
|
||||||
|
|
||||||
/* Function to return whether a joystick is a Nintendo Switch Pro controller */
|
/* Function to return whether a joystick is a Nintendo Switch Pro controller */
|
||||||
extern SDL_bool SDL_IsJoystickNintendoSwitchPro(Uint16 vendor_id, Uint16 product_id);
|
extern SDL_bool SDL_IsJoystickNintendoSwitchPro( Uint16 vendor_id, Uint16 product_id );
|
||||||
|
extern SDL_bool SDL_IsJoystickNintendoSwitchProInputOnly(Uint16 vendor_id, Uint16 product_id);
|
||||||
|
|
||||||
/* Function to return whether a joystick is a Steam Controller */
|
/* Function to return whether a joystick is a Steam Controller */
|
||||||
extern SDL_bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id);
|
extern SDL_bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id);
|
||||||
|
|
|
@ -87,6 +87,14 @@ typedef enum {
|
||||||
#define k_unSPIStickCalibrationLength (k_unSPIStickCalibrationEndOffset - k_unSPIStickCalibrationStartOffset + 1)
|
#define k_unSPIStickCalibrationLength (k_unSPIStickCalibrationEndOffset - k_unSPIStickCalibrationStartOffset + 1)
|
||||||
|
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Uint8 rgucButtons[2];
|
||||||
|
Uint8 ucStickHat;
|
||||||
|
Uint8 rgucJoystickLeft[2];
|
||||||
|
Uint8 rgucJoystickRight[2];
|
||||||
|
} SwitchInputOnlyControllerStatePacket_t;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
Uint8 rgucButtons[2];
|
Uint8 rgucButtons[2];
|
||||||
|
@ -135,11 +143,11 @@ typedef struct
|
||||||
|
|
||||||
#define k_unSubcommandDataBytes 35
|
#define k_unSubcommandDataBytes 35
|
||||||
union {
|
union {
|
||||||
Uint8 rgucSubcommandData[ k_unSubcommandDataBytes ];
|
Uint8 rgucSubcommandData[k_unSubcommandDataBytes];
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
SwitchSPIOpData_t opData;
|
SwitchSPIOpData_t opData;
|
||||||
Uint8 rgucReadData[ k_unSubcommandDataBytes - sizeof(SwitchSPIOpData_t) ];
|
Uint8 rgucReadData[k_unSubcommandDataBytes - sizeof(SwitchSPIOpData_t)];
|
||||||
} spiReadData;
|
} spiReadData;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -170,7 +178,7 @@ typedef struct
|
||||||
SwitchCommonOutputPacket_t commonData;
|
SwitchCommonOutputPacket_t commonData;
|
||||||
|
|
||||||
Uint8 ucSubcommandID;
|
Uint8 ucSubcommandID;
|
||||||
Uint8 rgucSubcommandData[ k_unSwitchOutputPacketDataLength - sizeof(SwitchCommonOutputPacket_t) - 1 ];
|
Uint8 rgucSubcommandData[k_unSwitchOutputPacketDataLength - sizeof(SwitchCommonOutputPacket_t) - 1];
|
||||||
} SwitchSubcommandOutputPacket_t;
|
} SwitchSubcommandOutputPacket_t;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
@ -178,17 +186,20 @@ typedef struct
|
||||||
Uint8 ucPacketType;
|
Uint8 ucPacketType;
|
||||||
Uint8 ucProprietaryID;
|
Uint8 ucProprietaryID;
|
||||||
|
|
||||||
Uint8 rgucProprietaryData[ k_unSwitchOutputPacketDataLength - 1 - 1 ];
|
Uint8 rgucProprietaryData[k_unSwitchOutputPacketDataLength - 1 - 1];
|
||||||
} SwitchProprietaryOutputPacket_t;
|
} SwitchProprietaryOutputPacket_t;
|
||||||
#pragma pack()
|
#pragma pack()
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
hid_device *dev;
|
hid_device *dev;
|
||||||
|
SDL_bool m_bIsInputOnly;
|
||||||
SDL_bool m_bIsUsingBluetooth;
|
SDL_bool m_bIsUsingBluetooth;
|
||||||
Uint8 m_nCommandNumber;
|
Uint8 m_nCommandNumber;
|
||||||
SwitchCommonOutputPacket_t m_RumblePacket;
|
SwitchCommonOutputPacket_t m_RumblePacket;
|
||||||
Uint32 m_nRumbleExpiration;
|
Uint32 m_nRumbleExpiration;
|
||||||
Uint8 m_rgucReadBuffer[k_unSwitchMaxOutputPacketLength];
|
Uint8 m_rgucReadBuffer[k_unSwitchMaxOutputPacketLength];
|
||||||
|
|
||||||
|
SwitchInputOnlyControllerStatePacket_t m_lastInputOnlyState;
|
||||||
SwitchSimpleStatePacket_t m_lastSimpleState;
|
SwitchSimpleStatePacket_t m_lastSimpleState;
|
||||||
SwitchStatePacket_t m_lastFullState;
|
SwitchStatePacket_t m_lastFullState;
|
||||||
|
|
||||||
|
@ -245,7 +256,7 @@ static SwitchSubcommandInputPacket_t *ReadSubcommandReply(SDL_DriverSwitch_Conte
|
||||||
while ((nRead = ReadInput(ctx)) != -1) {
|
while ((nRead = ReadInput(ctx)) != -1) {
|
||||||
if (nRead > 0) {
|
if (nRead > 0) {
|
||||||
if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) {
|
if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) {
|
||||||
SwitchSubcommandInputPacket_t *reply = (SwitchSubcommandInputPacket_t *)&ctx->m_rgucReadBuffer[ 1 ];
|
SwitchSubcommandInputPacket_t *reply = (SwitchSubcommandInputPacket_t *)&ctx->m_rgucReadBuffer[1];
|
||||||
if (reply->ucSubcommandID == expectedID && (reply->ucSubcommandAck & 0x80)) {
|
if (reply->ucSubcommandID == expectedID && (reply->ucSubcommandAck & 0x80)) {
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
@ -270,7 +281,7 @@ static SDL_bool ReadProprietaryReply(SDL_DriverSwitch_Context *ctx, ESwitchPropr
|
||||||
int nRead = 0;
|
int nRead = 0;
|
||||||
while ((nRead = ReadInput(ctx)) != -1) {
|
while ((nRead = ReadInput(ctx)) != -1) {
|
||||||
if (nRead > 0) {
|
if (nRead > 0) {
|
||||||
if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_CommandAck && ctx->m_rgucReadBuffer[ 1 ] == expectedID) {
|
if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_CommandAck && ctx->m_rgucReadBuffer[1] == expectedID) {
|
||||||
return SDL_TRUE;
|
return SDL_TRUE;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -584,6 +595,9 @@ HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_
|
||||||
|
|
||||||
*context = ctx;
|
*context = ctx;
|
||||||
|
|
||||||
|
/* Find out whether or not we can send output reports */
|
||||||
|
ctx->m_bIsInputOnly = SDL_IsJoystickNintendoSwitchProInputOnly(vendor_id, product_id);
|
||||||
|
if (!ctx->m_bIsInputOnly) {
|
||||||
/* Initialize rumble data */
|
/* Initialize rumble data */
|
||||||
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
|
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
|
||||||
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
|
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
|
||||||
|
@ -630,6 +644,7 @@ HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_
|
||||||
/* Set the LED state */
|
/* Set the LED state */
|
||||||
SetHomeLED(ctx, 100);
|
SetHomeLED(ctx, 100);
|
||||||
SetSlotLED(ctx, (joystick->instance_id % 4));
|
SetSlotLED(ctx, (joystick->instance_id % 4));
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialize the joystick capabilities */
|
/* Initialize the joystick capabilities */
|
||||||
joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
|
joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
|
||||||
|
@ -680,6 +695,102 @@ HIDAPI_DriverSwitch_Rumble(SDL_Joystick *joystick, hid_device *dev, void *contex
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void HandleInputOnlyControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchInputOnlyControllerStatePacket_t *packet)
|
||||||
|
{
|
||||||
|
Sint16 axis;
|
||||||
|
|
||||||
|
if (packet->rgucButtons[0] != ctx->m_lastInputOnlyState.rgucButtons[0]) {
|
||||||
|
Uint8 data = packet->rgucButtons[0];
|
||||||
|
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||||
|
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||||
|
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
||||||
|
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
||||||
|
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
|
||||||
|
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
|
||||||
|
|
||||||
|
axis = (data & 0x40) ? 32767 : -32768;
|
||||||
|
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
|
||||||
|
|
||||||
|
axis = (data & 0x80) ? 32767 : -32768;
|
||||||
|
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet->rgucButtons[1] != ctx->m_lastInputOnlyState.rgucButtons[1]) {
|
||||||
|
Uint8 data = packet->rgucButtons[1];
|
||||||
|
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||||
|
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||||
|
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
||||||
|
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
||||||
|
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet->ucStickHat != ctx->m_lastInputOnlyState.ucStickHat) {
|
||||||
|
SDL_bool dpad_up = SDL_FALSE;
|
||||||
|
SDL_bool dpad_down = SDL_FALSE;
|
||||||
|
SDL_bool dpad_left = SDL_FALSE;
|
||||||
|
SDL_bool dpad_right = SDL_FALSE;
|
||||||
|
|
||||||
|
switch (packet->ucStickHat) {
|
||||||
|
case 0:
|
||||||
|
dpad_up = SDL_TRUE;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
dpad_up = SDL_TRUE;
|
||||||
|
dpad_right = SDL_TRUE;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
dpad_right = SDL_TRUE;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
dpad_right = SDL_TRUE;
|
||||||
|
dpad_down = SDL_TRUE;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
dpad_down = SDL_TRUE;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
dpad_left = SDL_TRUE;
|
||||||
|
dpad_down = SDL_TRUE;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
dpad_left = SDL_TRUE;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
dpad_up = SDL_TRUE;
|
||||||
|
dpad_left = SDL_TRUE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
|
||||||
|
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
|
||||||
|
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
|
||||||
|
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet->rgucJoystickLeft[0] != ctx->m_lastInputOnlyState.rgucJoystickLeft[0]) {
|
||||||
|
axis = (Sint16)(RemapVal(packet->rgucJoystickLeft[0], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16));
|
||||||
|
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet->rgucJoystickLeft[1] != ctx->m_lastInputOnlyState.rgucJoystickLeft[1]) {
|
||||||
|
axis = (Sint16)(RemapVal(packet->rgucJoystickLeft[1], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16));
|
||||||
|
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet->rgucJoystickRight[0] != ctx->m_lastInputOnlyState.rgucJoystickRight[0]) {
|
||||||
|
axis = (Sint16)(RemapVal(packet->rgucJoystickRight[0], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16));
|
||||||
|
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet->rgucJoystickRight[1] != ctx->m_lastInputOnlyState.rgucJoystickRight[1]) {
|
||||||
|
axis = (Sint16)(RemapVal(packet->rgucJoystickRight[1], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16));
|
||||||
|
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->m_lastInputOnlyState = *packet;
|
||||||
|
}
|
||||||
|
|
||||||
static void HandleSimpleControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
|
static void HandleSimpleControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
|
||||||
{
|
{
|
||||||
/* 0x8000 is the neutral value for all joystick axes */
|
/* 0x8000 is the neutral value for all joystick axes */
|
||||||
|
@ -853,6 +964,9 @@ HIDAPI_DriverSwitch_Update(SDL_Joystick *joystick, hid_device *dev, void *contex
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
while ((size = ReadInput(ctx)) > 0) {
|
while ((size = ReadInput(ctx)) > 0) {
|
||||||
|
if (ctx->m_bIsInputOnly) {
|
||||||
|
HandleInputOnlyControllerState(joystick, ctx, (SwitchInputOnlyControllerStatePacket_t *)&ctx->m_rgucReadBuffer[0]);
|
||||||
|
} else {
|
||||||
switch (ctx->m_rgucReadBuffer[0]) {
|
switch (ctx->m_rgucReadBuffer[0]) {
|
||||||
case k_eSwitchInputReportIDs_SimpleControllerState:
|
case k_eSwitchInputReportIDs_SimpleControllerState:
|
||||||
HandleSimpleControllerState(joystick, ctx, (SwitchSimpleStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
|
HandleSimpleControllerState(joystick, ctx, (SwitchSimpleStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
|
||||||
|
@ -864,6 +978,7 @@ HIDAPI_DriverSwitch_Update(SDL_Joystick *joystick, hid_device *dev, void *contex
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx->m_nRumbleExpiration) {
|
if (ctx->m_nRumbleExpiration) {
|
||||||
Uint32 now = SDL_GetTicks();
|
Uint32 now = SDL_GetTicks();
|
||||||
|
|
Loading…
Reference in New Issue