diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c index 483bacd0d..626f69174 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 02afe2566..9977fac1d 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,