diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c index 70f3260ca..bca6fc46e 100644 --- a/src/joystick/SDL_gamecontroller.c +++ b/src/joystick/SDL_gamecontroller.c @@ -30,6 +30,7 @@ #include "SDL_gamecontrollerdb.h" #include "controller_type.h" #include "usb_ids.h" +#include "hidapi/SDL_hidapi_nintendo.h" #if !SDL_EVENTS_DISABLED #include "../events/SDL_events_c.h" @@ -570,25 +571,39 @@ static ControllerMapping_t *SDL_CreateMappingForHIDAPIController(SDL_JoystickGUI (vendor == USB_VENDOR_SHENZHEN && product == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER)) { /* GameCube driver has 12 buttons and 6 axes */ SDL_strlcat(mapping_string, "a:b0,b:b1,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3,start:b8,x:b2,y:b3,", sizeof(mapping_string)); - } else if (vendor == USB_VENDOR_NINTENDO && guid.data[15] != 0 && guid.data[15] != 3) { + } else if (vendor == USB_VENDOR_NINTENDO && + guid.data[15] != k_eSwitchDeviceInfoControllerType_Unknown && + guid.data[15] != k_eSwitchDeviceInfoControllerType_ProController && + guid.data[15] != k_eWiiExtensionControllerType_WiiUPro) { switch (guid.data[15]) { - case 9: - case 10: - /* NES Controller */ + case k_eSwitchDeviceInfoControllerType_NESLeft: + case k_eSwitchDeviceInfoControllerType_NESRight: 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; - case 11: - /* SNES Controller */ + case k_eSwitchDeviceInfoControllerType_SNES: 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)); break; - case 12: - /* N64 Controller */ + case k_eSwitchDeviceInfoControllerType_N64: 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,righttrigger:a5,start:b6,x:b2,y:b3,misc1:b15,", sizeof(mapping_string)); break; - case 13: - /* SEGA Genesis Controller */ + case k_eSwitchDeviceInfoControllerType_SEGA_Genesis: SDL_strlcat(mapping_string, "a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,rightshoulder:b10,righttrigger:a5,start:b6,misc1:b15,", sizeof(mapping_string)); break; + case k_eWiiExtensionControllerType_None: + SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,start:b6,x:b2,y:b3,", sizeof(mapping_string)); + break; + case k_eWiiExtensionControllerType_Nunchuck: + { + /* FIXME: Should we map this to the left or right side? */ + const SDL_bool map_nunchuck_left_side = SDL_TRUE; + + if (map_nunchuck_left_side) { + SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,start:b6,x:b2,y:b3,", sizeof(mapping_string)); + } else { + SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,rightshoulder:b9,righttrigger:a4,rightx:a0,righty:a1,start:b6,x:b2,y:b3,", 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)); diff --git a/src/joystick/hidapi/SDL_hidapi_nintendo.h b/src/joystick/hidapi/SDL_hidapi_nintendo.h new file mode 100644 index 000000000..600f3227d --- /dev/null +++ b/src/joystick/hidapi/SDL_hidapi_nintendo.h @@ -0,0 +1,47 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* These are values used in the controller type byte of the controller GUID */ + +/* These values come directly out of the hardware, so don't change them */ +typedef enum { + k_eSwitchDeviceInfoControllerType_Unknown = 0, + k_eSwitchDeviceInfoControllerType_JoyConLeft = 1, + k_eSwitchDeviceInfoControllerType_JoyConRight = 2, + k_eSwitchDeviceInfoControllerType_ProController = 3, + k_eSwitchDeviceInfoControllerType_NESLeft = 9, + k_eSwitchDeviceInfoControllerType_NESRight = 10, + k_eSwitchDeviceInfoControllerType_SNES = 11, + k_eSwitchDeviceInfoControllerType_N64 = 12, + k_eSwitchDeviceInfoControllerType_SEGA_Genesis = 13, +} ESwitchDeviceInfoControllerType; + +/* These values are used internally but can be updated as needed */ +typedef enum { + k_eWiiExtensionControllerType_Unknown = 0, + k_eWiiExtensionControllerType_None = 128, + k_eWiiExtensionControllerType_Nunchuck = 129, + k_eWiiExtensionControllerType_ClassicController = 130, + k_eWiiExtensionControllerType_ClassicControllerPro = 131, + k_eWiiExtensionControllerType_WiiUPro = 132, +} EWiiExtensionControllerType; + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c index af7ef5335..799dbb1cf 100644 --- a/src/joystick/hidapi/SDL_hidapi_switch.c +++ b/src/joystick/hidapi/SDL_hidapi_switch.c @@ -33,6 +33,7 @@ #include "../SDL_sysjoystick.h" #include "SDL_hidapijoystick_c.h" #include "SDL_hidapi_rumble.h" +#include "SDL_hidapi_nintendo.h" #ifdef SDL_JOYSTICK_HIDAPI_SWITCH @@ -102,18 +103,6 @@ typedef enum { k_eSwitchProprietaryCommandIDs_ResetMCU = 0x06, } ESwitchProprietaryCommandIDs; -typedef enum { - k_eSwitchDeviceInfoControllerType_Unknown = 0, - k_eSwitchDeviceInfoControllerType_JoyConLeft = 1, - k_eSwitchDeviceInfoControllerType_JoyConRight = 2, - k_eSwitchDeviceInfoControllerType_ProController = 3, - k_eSwitchDeviceInfoControllerType_NESLeft = 9, - k_eSwitchDeviceInfoControllerType_NESRight = 10, - k_eSwitchDeviceInfoControllerType_SNES = 11, - k_eSwitchDeviceInfoControllerType_N64 = 12, - k_eSwitchDeviceInfoControllerType_SEGA_Genesis = 13, -} ESwitchDeviceInfoControllerType; - #define k_unSwitchOutputPacketDataLength 49 #define k_unSwitchMaxOutputPacketLength 64 #define k_unSwitchBluetoothPacketLength k_unSwitchOutputPacketDataLength @@ -1244,6 +1233,7 @@ UpdateDeviceIdentity(SDL_HIDAPI_Device *device) if (name && (!name || SDL_strcmp(name, device->name) != 0)) { SDL_free(device->name); device->name = SDL_strdup(name); + SDL_SetJoystickGUIDCRC(&device->guid, SDL_crc16(0, name, SDL_strlen(name))); } } diff --git a/src/joystick/hidapi/SDL_hidapi_wii.c b/src/joystick/hidapi/SDL_hidapi_wii.c index 88521f5b0..69c199b8e 100644 --- a/src/joystick/hidapi/SDL_hidapi_wii.c +++ b/src/joystick/hidapi/SDL_hidapi_wii.c @@ -31,13 +31,13 @@ #include "../SDL_sysjoystick.h" #include "SDL_hidapijoystick_c.h" #include "SDL_hidapi_rumble.h" +#include "SDL_hidapi_nintendo.h" #ifdef SDL_JOYSTICK_HIDAPI_WII /* Define this if you want to log all packets from the controller */ /*#define DEBUG_WII_PROTOCOL*/ -#define DEBUG_WII_PROTOCOL #undef clamp #define clamp(val, min, max) (((val) > (max)) ? (max) : (((val) < (min)) ? (min) : (val))) @@ -80,15 +80,6 @@ typedef enum { k_eWiiPlayerLEDs_P4 = 0x80, } EWiiPlayerLEDs; -typedef enum { - k_eWiiExtensionControllerType_None, - k_eWiiExtensionControllerType_Unknown, - k_eWiiExtensionControllerType_Nunchuck, - k_eWiiExtensionControllerType_ClassicController, - k_eWiiExtensionControllerType_ClassicControllerPro, - k_eWiiExtensionControllerType_WiiUPro, -} EWiiExtensionControllerType; - #define k_unWiiPacketDataLength 22 typedef struct { @@ -312,13 +303,13 @@ static SDL_bool ParseExtensionResponse(SDL_DriverWii_Context *ctx) static void UpdatePowerLevelWii(SDL_Joystick *joystick, Uint8 batteryLevelByte) { if (batteryLevelByte > 178) { - joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL; + SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL); } else if (batteryLevelByte > 51) { - joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM; + SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_MEDIUM); } else if (batteryLevelByte > 13) { - joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW; + SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_LOW); } else { - joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY; + SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_EMPTY); } } @@ -335,45 +326,18 @@ static void UpdatePowerLevelWiiU(SDL_Joystick *joystick, Uint8 extensionBatteryB * No value above 4 has been observed. */ if (pluggedIn && !charging) { - joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; + SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_WIRED); } else if (batteryLevel >= 4) { - joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL; + SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL); } else if (batteryLevel > 1) { - joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM; + SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_MEDIUM); } else if (batteryLevel == 1) { - joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW; + SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_LOW); } else { - joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY; + SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_EMPTY); } } -static SDL_bool IdentifyController(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick) -{ - const Uint8 statusRequest[2] = { k_eWiiOutputReportIDs_StatusRequest, 0 }; - SDL_bool hasExtension; - WriteOutput(ctx, statusRequest, sizeof(statusRequest), SDL_TRUE); - if (!ReadInputSync(ctx, k_eWiiInputReportIDs_Status, NULL)) { - return SDL_FALSE; - } - UpdatePowerLevelWii(joystick, ctx->m_rgucReadBuffer[6]); - hasExtension = ctx->m_rgucReadBuffer[3] & 2 ? SDL_TRUE : SDL_FALSE; - if (hasExtension) { - /* http://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way */ - Uint8 data_0x55 = 0x55; - Uint8 data_0x00 = 0x00; - SDL_bool ok = WriteRegister(ctx, 0xA400F0, &data_0x55, sizeof(data_0x55), SDL_TRUE) - && WriteRegister(ctx, 0xA400FB, &data_0x00, sizeof(data_0x00), SDL_TRUE) - && ReadRegister(ctx, 0xA400FA, 6, SDL_TRUE) - && ParseExtensionResponse(ctx); - if (!ok) { - return SDL_FALSE; - } - } else { - ctx->m_eExtensionControllerType = k_eWiiExtensionControllerType_None; - } - return SDL_TRUE; -} - static EWiiInputReportIDs GetButtonPacketType(SDL_DriverWii_Context *ctx) { switch (ctx->m_eExtensionControllerType) { @@ -434,24 +398,6 @@ static void InitStickCalibrationData(SDL_DriverWii_Context *ctx) } } -static const char* GetNameFromExtensionInfo(SDL_DriverWii_Context *ctx) -{ - switch (ctx->m_eExtensionControllerType) { - case k_eWiiExtensionControllerType_None: - return "Nintendo Wii Remote"; - case k_eWiiExtensionControllerType_Nunchuck: - return "Nintendo Wii Remote with Nunchuck"; - case k_eWiiExtensionControllerType_ClassicController: - return "Nintendo Wii Remote with Classic Controller"; - case k_eWiiExtensionControllerType_ClassicControllerPro: - return "Nintendo Wii Remote with Classic Controller Pro"; - case k_eWiiExtensionControllerType_WiiUPro: - return "Nintendo Wii U Pro Controller"; - default: - return "Nintendo Wii Remote with Unknown Extension"; - } -} - static void SDLCALL SDL_GameControllerButtonReportingHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) { SDL_DriverWii_Context *ctx = (SDL_DriverWii_Context *)userdata; @@ -523,9 +469,84 @@ static void SDLCALL SDL_PlayerLEDHintChanged(void *userdata, const char *name, c } } +static EWiiExtensionControllerType +ReadControllerType(SDL_HIDAPI_Device *device) +{ + EWiiExtensionControllerType eControllerType = k_eWiiExtensionControllerType_Unknown; + + /* Create enough of a context to read the controller type from the device */ + SDL_DriverWii_Context *ctx = (SDL_DriverWii_Context *)SDL_calloc(1, sizeof(*ctx)); + if (ctx) { + ctx->device = device; + + device->dev = SDL_hid_open_path(device->path, 0); + if (device->dev) { + const Uint8 statusRequest[2] = { k_eWiiOutputReportIDs_StatusRequest, 0 }; + WriteOutput(ctx, statusRequest, sizeof(statusRequest), SDL_TRUE); + if (ReadInputSync(ctx, k_eWiiInputReportIDs_Status, NULL)) { + SDL_bool hasExtension = (ctx->m_rgucReadBuffer[3] & 2) ? SDL_TRUE : SDL_FALSE; + if (hasExtension) { + /* http://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way */ + Uint8 data_0x55 = 0x55; + Uint8 data_0x00 = 0x00; + if (WriteRegister(ctx, 0xA400F0, &data_0x55, sizeof(data_0x55), SDL_TRUE) && + WriteRegister(ctx, 0xA400FB, &data_0x00, sizeof(data_0x00), SDL_TRUE) && + ReadRegister(ctx, 0xA400FA, 6, SDL_TRUE) && ParseExtensionResponse(ctx)) { + eControllerType = ctx->m_eExtensionControllerType; + } + } else { + eControllerType = k_eWiiExtensionControllerType_None; + } + } + SDL_hid_close(device->dev); + device->dev = NULL; + } + SDL_free(ctx); + } + return eControllerType; +} + +static void +UpdateDeviceIdentity(SDL_HIDAPI_Device *device) +{ + EWiiExtensionControllerType eControllerType = (EWiiExtensionControllerType)device->guid.data[15]; + const char *name = NULL; + + switch (eControllerType) { + case k_eWiiExtensionControllerType_None: + name = "Nintendo Wii Remote"; + break; + case k_eWiiExtensionControllerType_Nunchuck: + name = "Nintendo Wii Remote with Nunchuck"; + break; + case k_eWiiExtensionControllerType_ClassicController: + name = "Nintendo Wii Remote with Classic Controller"; + break; + case k_eWiiExtensionControllerType_ClassicControllerPro: + name = "Nintendo Wii Remote with Classic Controller Pro"; + break; + case k_eWiiExtensionControllerType_WiiUPro: + name = "Nintendo Wii U Pro Controller"; + break; + default: + name = "Nintendo Wii Remote with Unknown Extension"; + break; + } + if (name && (!name || SDL_strcmp(name, device->name) != 0)) { + SDL_free(device->name); + device->name = SDL_strdup(name); + SDL_SetJoystickGUIDCRC(&device->guid, SDL_crc16(0, name, SDL_strlen(name))); + } +} + static SDL_bool HIDAPI_DriverWii_InitDevice(SDL_HIDAPI_Device *device) { + if (device->vendor_id == USB_VENDOR_NINTENDO) { + EWiiExtensionControllerType eControllerType = ReadControllerType(device); + device->guid.data[15] = eControllerType; + UpdateDeviceIdentity(device); + } return HIDAPI_JoystickConnected(device, NULL); } @@ -568,19 +589,12 @@ HIDAPI_DriverWii_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) goto error; } - SDL_AddHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, - SDL_GameControllerButtonReportingHintChanged, ctx); - - if (!IdentifyController(ctx, joystick)) { - char msg[512]; - SDL_GetErrorMsg(msg, sizeof(msg) - 1); - SDL_SetError("Couldn't read device info: %s", msg); - goto error; - } + ctx->m_eExtensionControllerType = (EWiiExtensionControllerType)device->guid.data[15]; InitStickCalibrationData(ctx); - SDL_free(device->name); - device->name = SDL_strdup(GetNameFromExtensionInfo(ctx)); + + SDL_AddHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, + SDL_GameControllerButtonReportingHintChanged, ctx); /* Initialize player index (needed for setting LEDs) */ ctx->m_nPlayerIndex = SDL_JoystickGetPlayerIndex(joystick); @@ -953,7 +967,7 @@ HIDAPI_DriverWii_UpdateDevice(SDL_HIDAPI_Device *device) } /* Request a status update periodically to make sure our battery value is up to date */ - if (SDL_TICKS_PASSED(now, ctx->m_unLastStatus + FIFTEEN_MINUTES_IN_MS)) { + if (!ctx->m_unLastStatus || SDL_TICKS_PASSED(now, ctx->m_unLastStatus + FIFTEEN_MINUTES_IN_MS)) { Uint8 data[2]; data[0] = k_eWiiOutputReportIDs_StatusRequest;