Added support for the Nintendo Online NES Controllers to the HIDAPI driver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c
index 483bacd..626f691 100644
--- a/src/joystick/SDL_gamecontroller.c
+++ b/src/joystick/SDL_gamecontroller.c
@@ -595,8 +595,17 @@ static ControllerMapping_t *SDL_CreateMappingForHIDAPIController(SDL_JoystickGUI
SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,", sizeof(mapping_string));
} else if (SDL_IsJoystickNintendoSwitchJoyConLeft(vendor, product) ||
SDL_IsJoystickNintendoSwitchJoyConRight(vendor, product)) {
- /* Mini gamepad mode */
- SDL_strlcat(mapping_string, "a:b0,b:b1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,", sizeof(mapping_string));
+ switch (guid.data[15]) {
+ case 9:
+ case 10:
+ /* NES Controller */
+ SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,rightshoulder:b10,start:b6,", sizeof(mapping_string));
+ break;
+ default:
+ /* Mini gamepad mode */
+ SDL_strlcat(mapping_string, "a:b0,b:b1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,", sizeof(mapping_string));
+ break;
+ }
} else {
/* All other controllers have the standard set of 19 buttons and 6 axes */
SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", sizeof(mapping_string));
diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c
index 02afe25..9977fac 100644
--- a/src/joystick/hidapi/SDL_hidapi_switch.c
+++ b/src/joystick/hidapi/SDL_hidapi_switch.c
@@ -104,10 +104,12 @@ typedef enum {
} ESwitchProprietaryCommandIDs;
typedef enum {
- k_eSwitchDeviceInfoControllerType_Unknown = 0x0,
- k_eSwitchDeviceInfoControllerType_JoyConLeft = 0x1,
- k_eSwitchDeviceInfoControllerType_JoyConRight = 0x2,
- k_eSwitchDeviceInfoControllerType_ProController = 0x3,
+ k_eSwitchDeviceInfoControllerType_Unknown = 0,
+ k_eSwitchDeviceInfoControllerType_JoyConLeft = 1,
+ k_eSwitchDeviceInfoControllerType_JoyConRight = 2,
+ k_eSwitchDeviceInfoControllerType_ProController = 3,
+ k_eSwitchDeviceInfoControllerType_NESLeft = 9,
+ k_eSwitchDeviceInfoControllerType_NESRight = 10,
} ESwitchDeviceInfoControllerType;
#define k_unSwitchOutputPacketDataLength 49
@@ -318,7 +320,7 @@ HasHomeLED(int vendor_id, int product_id)
}
static SDL_bool
-AlwaysUsesLabels(int vendor_id, int product_id)
+AlwaysUsesLabels(int vendor_id, int product_id, ESwitchDeviceInfoControllerType eControllerType)
{
/* These controllers don't have a diamond button configuration, so always use labels */
if (vendor_id == USB_VENDOR_NINTENDO &&
@@ -326,6 +328,10 @@ AlwaysUsesLabels(int vendor_id, int product_id)
product_id == USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER)) {
return SDL_TRUE;
}
+ if (eControllerType == k_eSwitchDeviceInfoControllerType_NESLeft ||
+ eControllerType == k_eSwitchDeviceInfoControllerType_NESRight) {
+ return SDL_TRUE;
+ }
return SDL_FALSE;
}
@@ -510,6 +516,25 @@ static SDL_bool WritePacket(SDL_DriverSwitch_Context *ctx, void *pBuf, Uint8 ucL
return (WriteOutput(ctx, (Uint8 *)pBuf, ucLen) >= 0);
}
+static SDL_bool
+WritePacketSync(SDL_DriverSwitch_Context *ctx, void *pBuf, Uint8 ucLen)
+{
+ Uint8 rgucBuf[k_unSwitchMaxOutputPacketLength];
+ const size_t unWriteSize = ctx->m_bUsingBluetooth ? k_unSwitchBluetoothPacketLength : k_unSwitchUSBPacketLength;
+
+ if (ucLen > k_unSwitchOutputPacketDataLength) {
+ return SDL_FALSE;
+ }
+
+ if (ucLen < unWriteSize) {
+ SDL_memcpy(rgucBuf, pBuf, ucLen);
+ SDL_memset(rgucBuf + ucLen, 0, unWriteSize - ucLen);
+ pBuf = rgucBuf;
+ ucLen = (Uint8) unWriteSize;
+ }
+ return (SDL_hid_write(ctx->device->dev, (Uint8 *)pBuf, ucLen) >= 0);
+}
+
static SDL_bool WriteSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandInputPacket_t **ppReply)
{
int nRetries = 5;
@@ -532,6 +557,29 @@ static SDL_bool WriteSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommand
return reply != NULL;
}
+static SDL_bool
+WriteSubcommandSync(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandInputPacket_t **ppReply)
+{
+ int nRetries = 5;
+ SwitchSubcommandInputPacket_t *reply = NULL;
+
+ while (!reply && nRetries--) {
+ SwitchSubcommandOutputPacket_t commandPacket;
+ ConstructSubcommand(ctx, ucCommandID, pBuf, ucLen, &commandPacket);
+
+ if (!WritePacketSync(ctx, &commandPacket, sizeof(commandPacket))) {
+ continue;
+ }
+
+ reply = ReadSubcommandReply(ctx, ucCommandID);
+ }
+
+ if (ppReply) {
+ *ppReply = reply;
+ }
+ return reply != NULL;
+}
+
static SDL_bool WriteProprietary(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs ucCommand, Uint8 *pBuf, Uint8 ucLen, SDL_bool waitForReply)
{
int nRetries = 5;
@@ -975,9 +1023,55 @@ static Uint8 RemapButton(SDL_DriverSwitch_Context *ctx, Uint8 button)
return button;
}
+static ESwitchDeviceInfoControllerType
+ReadJoyConControllerType(SDL_HIDAPI_Device *device)
+{
+ ESwitchDeviceInfoControllerType eControllerType = k_eSwitchDeviceInfoControllerType_Unknown;
+
+ /* Create enough of a context to read the controller type from the device */
+ SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx));
+ if (ctx) {
+ SwitchSubcommandInputPacket_t *reply = NULL;
+
+ ctx->device = device;
+ ctx->m_bUsingBluetooth = SDL_TRUE;
+
+ device->dev = SDL_hid_open_path(device->path, 0);
+ if (device->dev) {
+ if (WriteSubcommandSync(ctx, k_eSwitchSubcommandIDs_RequestDeviceInfo, NULL, 0, &reply)) {
+ // Byte 2: Controller ID (1=LJC, 2=RJC, 3=Pro)
+ eControllerType = (ESwitchDeviceInfoControllerType) reply->deviceInfo.ucDeviceType;
+ }
+ SDL_hid_close(device->dev);
+ device->dev = NULL;
+ }
+ SDL_free(ctx);
+ }
+ return eControllerType;
+}
+
static SDL_bool
HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device)
{
+ /* The NES controllers need additional fix up, since we can't detect them without opening the device */
+ if (device->vendor_id == USB_VENDOR_NINTENDO &&
+ device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_RIGHT) {
+ ESwitchDeviceInfoControllerType eControllerType = ReadJoyConControllerType(device);
+ switch (eControllerType) {
+ case k_eSwitchDeviceInfoControllerType_NESLeft:
+ SDL_free(device->name);
+ device->name = SDL_strdup("NES Controller (L)");
+ device->guid.data[15] = eControllerType;
+ break;
+ case k_eSwitchDeviceInfoControllerType_NESRight:
+ SDL_free(device->name);
+ device->name = SDL_strdup("NES Controller (R)");
+ device->guid.data[15] = eControllerType;
+ break;
+ default:
+ break;
+ }
+ }
return HIDAPI_JoystickConnected(device, NULL);
}
@@ -1053,7 +1147,9 @@ HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joysti
input_mode = k_eSwitchInputReportIDs_FullControllerState;
}
- if (input_mode == k_eSwitchInputReportIDs_FullControllerState) {
+ if (input_mode == k_eSwitchInputReportIDs_FullControllerState &&
+ ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_NESLeft &&
+ ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_NESRight) {
/* Use the right sensor in the combined Joy-Con pair */
if (!device->parent ||
ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
@@ -1123,7 +1219,7 @@ HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joysti
ctx->m_bIsGameCube = SDL_TRUE;
}
- if (AlwaysUsesLabels(device->vendor_id, device->product_id)) {
+ if (AlwaysUsesLabels(device->vendor_id, device->product_id, ctx->m_eControllerType)) {
ctx->m_bUseButtonLabels = SDL_TRUE;
} else {
SDL_AddHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS,