From 5f3cb54972f7fc6d5b292bcfa733046c50a516d5 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 2 Sep 2022 08:28:28 -0700 Subject: [PATCH] Updated Wii support with @tellowkrinkle's changes in https://github.com/tellowkrinkle/SDL/commit/2f288e9d5bf596756f92b6c3c983b79dc783eac6 --- src/joystick/hidapi/SDL_hidapi_wii.c | 488 +++++++++++++++++++-------- 1 file changed, 349 insertions(+), 139 deletions(-) diff --git a/src/joystick/hidapi/SDL_hidapi_wii.c b/src/joystick/hidapi/SDL_hidapi_wii.c index 33978cd6c..ee64043a2 100644 --- a/src/joystick/hidapi/SDL_hidapi_wii.c +++ b/src/joystick/hidapi/SDL_hidapi_wii.c @@ -80,6 +80,29 @@ typedef enum { k_eWiiPlayerLEDs_P4 = 0x80, } EWiiPlayerLEDs; +typedef enum { + k_eWiiCommunicationState_None, /* No special communications happening */ + k_eWiiCommunicationState_Error, /* Special communications failed, controller communications may be broken */ + k_eWiiCommunicationState_ExtensionIdentify1, /* Sent first write for requesting extension info, awaiting ack to send second */ + k_eWiiCommunicationState_ExtensionIdentify2, /* Sent second write for requesting extension info, awaiting ack to send read */ + k_eWiiCommunicationState_ExtensionIdentify3, /* Sent read request for extension info, awaiting response */ +} EWiiCommunicationState; + +typedef enum { + k_eWiiButtons_A = SDL_CONTROLLER_BUTTON_MISC1, + k_eWiiButtons_B, + k_eWiiButtons_One, + k_eWiiButtons_Two, + k_eWiiButtons_Plus, + k_eWiiButtons_Minus, + k_eWiiButtons_Home, + k_eWiiButtons_DPad_Up, + k_eWiiButtons_DPad_Down, + k_eWiiButtons_DPad_Left, + k_eWiiButtons_DPad_Right, + k_eWiiButtons_Max +} EWiiButtons; + #define k_unWiiPacketDataLength 22 typedef struct { @@ -93,6 +116,7 @@ typedef struct { typedef struct { SDL_HIDAPI_Device *device; + EWiiCommunicationState m_eCommState; EWiiExtensionControllerType m_eExtensionControllerType; SDL_bool m_bUseButtonLabels; SDL_bool m_bPlayerLights; @@ -254,14 +278,34 @@ static SDL_bool ReadRegister(SDL_DriverWii_Context *ctx, Uint32 address, int siz return SDL_TRUE; } -static SDL_bool ParseExtensionResponse(SDL_DriverWii_Context *ctx, EWiiExtensionControllerType *controller_type) +static SDL_bool SendExtensionIdentify1(SDL_DriverWii_Context *ctx, SDL_bool sync) +{ + char data = 0x55; + return WriteRegister(ctx, 0xA400F0, &data, sizeof(data), sync); +} + +static SDL_bool SendExtensionIdentify2(SDL_DriverWii_Context *ctx, SDL_bool sync) +{ + char data = 0x00; + return WriteRegister(ctx, 0xA400FB, &data, sizeof(data), sync); +} + +static SDL_bool SendExtensionIdentify3(SDL_DriverWii_Context *ctx, SDL_bool sync) +{ + return ReadRegister(ctx, 0xA400FA, 6, sync); +} + +static SDL_bool ParseExtensionResponse(SDL_DriverWii_Context *ctx, EWiiExtensionControllerType *extension_type) { Uint64 type = 0; int i; - *controller_type = k_eWiiExtensionControllerType_Unknown; + *extension_type = k_eWiiExtensionControllerType_Unknown; - SDL_assert(ctx->m_rgucReadBuffer[0] == k_eWiiInputReportIDs_ReadMemory); + if (ctx->m_rgucReadBuffer[0] != k_eWiiInputReportIDs_ReadMemory) { + SDL_SetError("Unexpected extension response type"); + return SDL_FALSE; + } if (ctx->m_rgucReadBuffer[4] != 0x00 || ctx->m_rgucReadBuffer[5] != 0xFA) { SDL_SetError("Unexpected extension response address"); return SDL_FALSE; @@ -280,19 +324,19 @@ static SDL_bool ParseExtensionResponse(SDL_DriverWii_Context *ctx, EWiiExtension } if (type == 0x0000A4200000) { - *controller_type = k_eWiiExtensionControllerType_Nunchuck; + *extension_type = k_eWiiExtensionControllerType_Nunchuck; return SDL_TRUE; } if (type == 0x0000A4200101) { - *controller_type = k_eWiiExtensionControllerType_ClassicController; + *extension_type = k_eWiiExtensionControllerType_ClassicController; return SDL_TRUE; } if (type == 0x0100A4200101) { - *controller_type = k_eWiiExtensionControllerType_ClassicControllerPro; + *extension_type = k_eWiiExtensionControllerType_ClassicControllerPro; return SDL_TRUE; } if (type == 0x0000A4200120) { - *controller_type = k_eWiiExtensionControllerType_WiiUPro; + *extension_type = k_eWiiExtensionControllerType_WiiUPro; return SDL_TRUE; } @@ -380,16 +424,16 @@ static void InitStickCalibrationData(SDL_DriverWii_Context *ctx) case k_eWiiExtensionControllerType_ClassicController: case k_eWiiExtensionControllerType_ClassicControllerPro: for (i = 0; i < 4; i++) { - ctx->m_StickCalibrationData[i].min = 0; - ctx->m_StickCalibrationData[i].max = i < 2 ? 63 : 31; + ctx->m_StickCalibrationData[i].min = i < 2 ? 9 : 5; + ctx->m_StickCalibrationData[i].max = i < 2 ? 54 : 26; ctx->m_StickCalibrationData[i].center = 0; ctx->m_StickCalibrationData[i].deadzone = i < 2 ? 4 : 2; } break; case k_eWiiExtensionControllerType_Nunchuck: for (i = 0; i < 2; i++) { - ctx->m_StickCalibrationData[i].min = 128 - 80; - ctx->m_StickCalibrationData[i].max = 128 + 80; + ctx->m_StickCalibrationData[i].min = 40; + ctx->m_StickCalibrationData[i].max = 215; ctx->m_StickCalibrationData[i].center = 0; ctx->m_StickCalibrationData[i].deadzone = 10; } @@ -399,32 +443,18 @@ static void InitStickCalibrationData(SDL_DriverWii_Context *ctx) } } +static void InitializeExtension(SDL_DriverWii_Context *ctx) +{ + InitStickCalibrationData(ctx); + RequestButtonPacketType(ctx, GetButtonPacketType(ctx)); +} + static void SDLCALL SDL_GameControllerButtonReportingHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) { SDL_DriverWii_Context *ctx = (SDL_DriverWii_Context *)userdata; ctx->m_bUseButtonLabels = SDL_GetStringBoolean(hint, SDL_TRUE); } -static Uint8 RemapButton(SDL_DriverWii_Context *ctx, Uint8 button) -{ - if (!ctx->m_bUseButtonLabels) { - /* Use button positions */ - switch (button) { - case SDL_CONTROLLER_BUTTON_A: - return SDL_CONTROLLER_BUTTON_B; - case SDL_CONTROLLER_BUTTON_B: - return SDL_CONTROLLER_BUTTON_A; - case SDL_CONTROLLER_BUTTON_X: - return SDL_CONTROLLER_BUTTON_Y; - case SDL_CONTROLLER_BUTTON_Y: - return SDL_CONTROLLER_BUTTON_X; - default: - break; - } - } - return button; -} - static void UpdateSlotLED(SDL_DriverWii_Context *ctx) { Uint8 leds; @@ -488,11 +518,9 @@ ReadControllerType(SDL_HIDAPI_Device *device) 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)) { + if (SendExtensionIdentify1(ctx, SDL_TRUE) && + SendExtensionIdentify2(ctx, SDL_TRUE) && + SendExtensionIdentify3(ctx, SDL_TRUE)) { ParseExtensionResponse(ctx, &eExtensionControllerType); } } else { @@ -592,7 +620,7 @@ HIDAPI_DriverWii_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) ctx->m_eExtensionControllerType = (EWiiExtensionControllerType)device->guid.data[15]; - InitStickCalibrationData(ctx); + InitializeExtension(ctx); SDL_AddHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, SDL_GameControllerButtonReportingHintChanged, ctx); @@ -610,12 +638,10 @@ HIDAPI_DriverWii_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) joystick->nbuttons = 15; } else { /* Maximum is Classic Controller + Wiimote */ - joystick->nbuttons = 25; + joystick->nbuttons = k_eWiiButtons_Max; } joystick->naxes = SDL_CONTROLLER_AXIS_MAX; - RequestButtonPacketType(ctx, GetButtonPacketType(ctx)); - ctx->m_unLastInput = SDL_GetTicks(); return SDL_TRUE; @@ -720,109 +746,224 @@ static void PostStickCalibrated(SDL_Joystick *joystick, struct StickCalibrationD SDL_PrivateJoystickAxis(joystick, axis, value); } -static void HandleWiiUProButtonData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, WiiButtonData data) +/* Send button data to SDL + *`defs` is a mapping for each bit to which button it represents. 0xFF indicates an unused bit + *`data` is the button data from the controller + *`size` is the number of bytes in `data` and the number of arrays of 8 mappings in `defs` + *`on` is the joystick value to be sent if a bit is on + *`off` is the joystick value to be sent if a bit is off + */ +static void PostPackedButtonData(SDL_Joystick *joystick, const Uint8 defs[][8], const Uint8* data, int size, Uint8 on, Uint8 off) { - static const Uint8 axes[] = { SDL_CONTROLLER_AXIS_LEFTX, SDL_CONTROLLER_AXIS_RIGHTX, SDL_CONTROLLER_AXIS_LEFTY, SDL_CONTROLLER_AXIS_RIGHTY }; - static const Uint8 buttons[3][8] = { - { - 0xFF /* Unused */, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, SDL_CONTROLLER_BUTTON_START, SDL_CONTROLLER_BUTTON_GUIDE, - SDL_CONTROLLER_BUTTON_BACK, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, SDL_CONTROLLER_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, - }, { - SDL_CONTROLLER_BUTTON_DPAD_UP, SDL_CONTROLLER_BUTTON_DPAD_LEFT, 0xFF /* ZR */, SDL_CONTROLLER_BUTTON_X, - SDL_CONTROLLER_BUTTON_A, SDL_CONTROLLER_BUTTON_Y, SDL_CONTROLLER_BUTTON_B, 0xFF /*ZL*/, - }, { - SDL_CONTROLLER_BUTTON_RIGHTSTICK, SDL_CONTROLLER_BUTTON_LEFTSTICK, 0xFF /* Charging */, 0xFF /* Plugged In */, - 0xFF /* Unused */, 0xFF /* Unused */, 0xFF /* Unused */, 0xFF /* Unused */, - } - }; - Uint8 zl, zr; int i, j; - if (data.ucNExtensionBytes < 11) { - return; - } - - /* Sticks */ - for (i = 0; i < SDL_arraysize(axes); i++) { - Uint16 value = data.rgucExtension[i * 2] | (data.rgucExtension[i * 2 + 1] << 8); - PostStickCalibrated(joystick, &ctx->m_StickCalibrationData[i], axes[i], value); - } - - /* Buttons */ - for (i = 0; i < SDL_arraysize(buttons); i++) { - for (j = 0; j < SDL_arraysize(buttons[i]); j++) { - Uint8 button = buttons[i][j]; + for (i = 0; i < size; i++) { + for (j = 0; j < 8; j++) { + Uint8 button = defs[i][j]; if (button != 0xFF) { - SDL_bool state = (data.rgucExtension[8 + i] >> j) & 1 ? SDL_RELEASED : SDL_PRESSED; - SDL_PrivateJoystickButton(joystick, RemapButton(ctx, button), state); - } - } - } - - /* Triggers */ - zl = data.rgucExtension[9] & 0x80; - zr = data.rgucExtension[9] & 0x04; - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, zl ? SDL_MIN_SINT16 : SDL_MAX_SINT16); - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, zr ? SDL_MIN_SINT16 : SDL_MAX_SINT16); - - /* Power */ - UpdatePowerLevelWiiU(joystick, data.rgucExtension[10]); -} - -static void HandleWiiButtonData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, WiiButtonData data) -{ - static const Uint8 buttons[2][8] = { - { - SDL_CONTROLLER_BUTTON_DPAD_LEFT, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, SDL_CONTROLLER_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_UP, - SDL_CONTROLLER_BUTTON_START, 0xFF /* Unused */, 0xFF /* Unused */, 0xFF /* Unused */, - }, { - SDL_CONTROLLER_BUTTON_X, SDL_CONTROLLER_BUTTON_Y, SDL_CONTROLLER_BUTTON_A, SDL_CONTROLLER_BUTTON_B, - SDL_CONTROLLER_BUTTON_BACK, 0xFF /* Unused */, 0xFF /* Unused */, SDL_CONTROLLER_BUTTON_GUIDE, - } - }; - int i, j; - - /* Buttons */ - for (i = 0; i < SDL_arraysize(buttons); i++) { - for (j = 0; j < SDL_arraysize(buttons[i]); j++) { - Uint8 button = buttons[i][j]; - if (button != 0xFF) { - SDL_bool state = ((data.rgucBaseButtons[i] >> j) & 1) ? SDL_PRESSED : SDL_RELEASED; + Uint8 state = (data[i] >> j) & 1 ? on : off; SDL_PrivateJoystickButton(joystick, button, state); } } } } -static void HandleWiiNunchukData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, WiiButtonData data) +/* Sends axis data to SDL based on an analog digital trigger pair + * `digital` is zero or nonzero + * `analog` is a 5-bit number (0-31) + */ +static void PostClassicControllerTrigger(SDL_Joystick *joystick, Uint8 axis, Uint8 digital, Uint8 analog) { - static const Uint8 axes[] = { SDL_CONTROLLER_AXIS_LEFTX, SDL_CONTROLLER_AXIS_LEFTY }; - int i; + SDL_PrivateJoystickAxis(joystick, axis, digital ? SDL_MAX_SINT16 : (Uint32)analog * SDL_MAX_SINT16 / 31); +} - if (data.ucNExtensionBytes < 6) { +/* Both Wii U Pro Controller and Wii Classic Controller use the same byte layout for the first two bytes + * Only Wii U Pro Controller has the third byte + */ +static const Uint8 WUP_CLASSIC_BUTTON_DEFS[3][8] = { + { + 0xFF /* Unused */, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, SDL_CONTROLLER_BUTTON_START, SDL_CONTROLLER_BUTTON_GUIDE, + SDL_CONTROLLER_BUTTON_BACK, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, SDL_CONTROLLER_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, + }, { + SDL_CONTROLLER_BUTTON_DPAD_UP, SDL_CONTROLLER_BUTTON_DPAD_LEFT, 0xFF /* ZR */, SDL_CONTROLLER_BUTTON_X, + SDL_CONTROLLER_BUTTON_A, SDL_CONTROLLER_BUTTON_Y, SDL_CONTROLLER_BUTTON_B, 0xFF /*ZL*/, + }, { + SDL_CONTROLLER_BUTTON_RIGHTSTICK, SDL_CONTROLLER_BUTTON_LEFTSTICK, 0xFF /* Charging */, 0xFF /* Plugged In */, + 0xFF /* Unused */, 0xFF /* Unused */, 0xFF /* Unused */, 0xFF /* Unused */, + } +}; + +static const Uint8 WUP_CLASSIC_BUTTON_DEFS_POSITIONAL[3][8] = { + { + 0xFF /* Unused */, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, SDL_CONTROLLER_BUTTON_START, SDL_CONTROLLER_BUTTON_GUIDE, + SDL_CONTROLLER_BUTTON_BACK, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, SDL_CONTROLLER_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, + }, { + SDL_CONTROLLER_BUTTON_DPAD_UP, SDL_CONTROLLER_BUTTON_DPAD_LEFT, 0xFF /* ZR */, SDL_CONTROLLER_BUTTON_Y, + SDL_CONTROLLER_BUTTON_B, SDL_CONTROLLER_BUTTON_X, SDL_CONTROLLER_BUTTON_A, 0xFF /*ZL*/, + }, { + SDL_CONTROLLER_BUTTON_RIGHTSTICK, SDL_CONTROLLER_BUTTON_LEFTSTICK, 0xFF /* Charging */, 0xFF /* Plugged In */, + 0xFF /* Unused */, 0xFF /* Unused */, 0xFF /* Unused */, 0xFF /* Unused */, + } +}; + +static void HandleWiiUProButtonData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, const WiiButtonData *data) +{ + static const Uint8 axes[] = { SDL_CONTROLLER_AXIS_LEFTX, SDL_CONTROLLER_AXIS_RIGHTX, SDL_CONTROLLER_AXIS_LEFTY, SDL_CONTROLLER_AXIS_RIGHTY }; + const Uint8 (*buttons)[8] = ctx->m_bUseButtonLabels ? WUP_CLASSIC_BUTTON_DEFS : WUP_CLASSIC_BUTTON_DEFS_POSITIONAL; + Uint8 zl, zr; + + if (data->ucNExtensionBytes < 11) { return; } /* Sticks */ - for (i = 0; i < SDL_arraysize(axes); i++) { - Uint16 value = data.rgucExtension[i]; + for (int i = 0; i < 4; i++) { + Uint16 value = data->rgucExtension[i * 2] | (data->rgucExtension[i * 2 + 1] << 8); PostStickCalibrated(joystick, &ctx->m_StickCalibrationData[i], axes[i], value); } - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, (data.rgucExtension[5] & 0x01) ? SDL_MIN_SINT16 : SDL_MAX_SINT16); + /* Buttons */ + PostPackedButtonData(joystick, buttons, data->rgucExtension + 8, 3, SDL_RELEASED, SDL_PRESSED); - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data.rgucExtension[5] & 0x02) ? SDL_RELEASED : SDL_PRESSED); + /* Triggers */ + zl = data->rgucExtension[9] & 0x80; + zr = data->rgucExtension[9] & 0x04; + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, zl ? 0 : SDL_MAX_SINT16); + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, zr ? 0 : SDL_MAX_SINT16); + + /* Power */ + UpdatePowerLevelWiiU(joystick, data->rgucExtension[10]); } -static void HandleButtonData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, WiiButtonData data) +static void HandleClassicControllerButtonData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, const WiiButtonData *data) +{ + const Uint8 (*buttons)[8] = ctx->m_bUseButtonLabels ? WUP_CLASSIC_BUTTON_DEFS : WUP_CLASSIC_BUTTON_DEFS_POSITIONAL; + Uint8 lx, ly, rx, ry, zl, zr; + if (data->ucNExtensionBytes < 6) { + return; + } + + PostPackedButtonData(joystick, buttons, data->rgucExtension + 4, 2, SDL_RELEASED, SDL_PRESSED); + + lx = data->rgucExtension[0] & 0x3F; + ly = data->rgucExtension[1] & 0x3F; + rx = (data->rgucExtension[2] >> 7) | ((data->rgucExtension[1] >> 5) & 0x06) | ((data->rgucExtension[0] >> 3) & 0x18); + ry = data->rgucExtension[2] & 0x1F; + zl = (data->rgucExtension[3] >> 5) | ((data->rgucExtension[2] >> 2) & 0x18); + zr = data->rgucExtension[3] & 0x1F; + PostStickCalibrated(joystick, &ctx->m_StickCalibrationData[0], SDL_CONTROLLER_AXIS_LEFTX, lx); + PostStickCalibrated(joystick, &ctx->m_StickCalibrationData[1], SDL_CONTROLLER_AXIS_LEFTY, ly); + PostStickCalibrated(joystick, &ctx->m_StickCalibrationData[2], SDL_CONTROLLER_AXIS_RIGHTX, rx); + PostStickCalibrated(joystick, &ctx->m_StickCalibrationData[3], SDL_CONTROLLER_AXIS_RIGHTY, ry); + PostClassicControllerTrigger(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, data->rgucExtension[5] & 0x80, zl); + PostClassicControllerTrigger(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, data->rgucExtension[5] & 0x04, zr); +} + +static void HandleWiiRemoteButtonData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, const WiiButtonData *data) +{ + static const Uint8 buttons[2][8] = { + { + k_eWiiButtons_DPad_Left, k_eWiiButtons_DPad_Right, k_eWiiButtons_DPad_Down, k_eWiiButtons_DPad_Up, + k_eWiiButtons_Plus, 0xFF /* Unused */, 0xFF /* Unused */, 0xFF /* Unused */, + }, { + k_eWiiButtons_Two, k_eWiiButtons_One, k_eWiiButtons_B, k_eWiiButtons_A, + k_eWiiButtons_Minus, 0xFF /* Unused */, 0xFF /* Unused */, k_eWiiButtons_Home, + } + }; + if (data->hasBaseButtons) { + PostPackedButtonData(joystick, buttons, data->rgucBaseButtons, 2, SDL_PRESSED, SDL_RELEASED); + } +} + +static void HandleWiiRemoteButtonDataAsMainController(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, const WiiButtonData *data) +{ + /* Wii remote maps really badly to a normal controller + * Mapped 1 and 2 as X and Y + * Not going to attempt positional mapping + */ + static const Uint8 buttons[2][8] = { + { + SDL_CONTROLLER_BUTTON_DPAD_LEFT, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, SDL_CONTROLLER_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_UP, + SDL_CONTROLLER_BUTTON_START, 0xFF /* Unused */, 0xFF /* Unused */, 0xFF /* Unused */, + }, { + SDL_CONTROLLER_BUTTON_Y, SDL_CONTROLLER_BUTTON_X, SDL_CONTROLLER_BUTTON_A, SDL_CONTROLLER_BUTTON_B, + SDL_CONTROLLER_BUTTON_BACK, 0xFF /* Unused */, 0xFF /* Unused */, SDL_CONTROLLER_BUTTON_GUIDE, + } + }; + if (data->hasBaseButtons) { + PostPackedButtonData(joystick, buttons, data->rgucBaseButtons, 2, SDL_PRESSED, SDL_RELEASED); + } +} + +static void HandleNunchuckButtonData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, const WiiButtonData *data) +{ + SDL_bool c = data->rgucExtension[5] & 2 ? SDL_RELEASED : SDL_PRESSED; + SDL_bool z = data->rgucExtension[5] & 1 ? SDL_RELEASED : SDL_PRESSED; + if (data->ucNExtensionBytes < 6) { + return; + } + + if (data->rgucExtension[0] != 0xFF) { + PostStickCalibrated(joystick, &ctx->m_StickCalibrationData[0], SDL_CONTROLLER_AXIS_LEFTX, data->rgucExtension[0]); + } + if (data->rgucExtension[1] != 0xFF) { + PostStickCalibrated(joystick, &ctx->m_StickCalibrationData[1], SDL_CONTROLLER_AXIS_LEFTY, data->rgucExtension[1]); + } + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, c); + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, z ? SDL_MAX_SINT16 : SDL_MIN_SINT16); +} + +/* Clear buttons that might have been set by a previously-connected controller that won't be set by the current one */ +static void ClearUnmappedButtons(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick) +{ + switch (ctx->m_eExtensionControllerType) { + case k_eWiiExtensionControllerType_None: + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, SDL_RELEASED); + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, 0); + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, 0); + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, 0); + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, 0); + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16); + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16); + SDL_FALLTHROUGH; + case k_eWiiExtensionControllerType_Nunchuck: + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, SDL_RELEASED); + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, 0); + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, 0); + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16); + break; + case k_eWiiExtensionControllerType_ClassicController: + case k_eWiiExtensionControllerType_ClassicControllerPro: + case k_eWiiExtensionControllerType_WiiUPro: + case k_eWiiExtensionControllerType_Unknown: + /* All buttons mapped */ + break; + } +} + +static void HandleButtonData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, const WiiButtonData *data) { if (ctx->m_eExtensionControllerType == k_eWiiExtensionControllerType_WiiUPro) { HandleWiiUProButtonData(ctx, joystick, data); - } else if (ctx->m_eExtensionControllerType == k_eWiiExtensionControllerType_None) { - HandleWiiButtonData(ctx, joystick, data); - } else if (ctx->m_eExtensionControllerType == k_eWiiExtensionControllerType_Nunchuck) { - HandleWiiButtonData(ctx, joystick, data); - HandleWiiNunchukData(ctx, joystick, data); + return; + } + ClearUnmappedButtons(ctx, joystick); + HandleWiiRemoteButtonData(ctx, joystick, data); + switch (ctx->m_eExtensionControllerType) { + case k_eWiiExtensionControllerType_Nunchuck: + HandleNunchuckButtonData(ctx, joystick, data); + SDL_FALLTHROUGH; + case k_eWiiExtensionControllerType_None: + HandleWiiRemoteButtonDataAsMainController(ctx, joystick, data); + break; + case k_eWiiExtensionControllerType_ClassicController: + case k_eWiiExtensionControllerType_ClassicControllerPro: + HandleClassicControllerButtonData(ctx, joystick, data); + break; + case k_eWiiExtensionControllerType_Unknown: + case k_eWiiExtensionControllerType_WiiUPro: + break; } } @@ -847,40 +988,109 @@ static void GetExtensionData(WiiButtonData *dst, const Uint8 *src, int size) static void HandleStatus(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick) { + SDL_bool hadExtension = ctx->m_eExtensionControllerType != k_eWiiExtensionControllerType_None; + SDL_bool hasExtension = ctx->m_rgucReadBuffer[3] & 2 ? SDL_TRUE : SDL_FALSE; WiiButtonData data; SDL_zero(data); GetBaseButtons(&data, ctx->m_rgucReadBuffer + 1); - HandleButtonData(ctx, joystick, data); + HandleButtonData(ctx, joystick, &data); if (ctx->m_eExtensionControllerType != k_eWiiExtensionControllerType_WiiUPro) { /* Wii U has separate battery level tracking */ UpdatePowerLevelWii(joystick, ctx->m_rgucReadBuffer[6]); } - /* Check to see if extensions have changed */ - { - EWiiExtensionControllerType eExtensionControllerType = k_eWiiExtensionControllerType_Unknown; - SDL_bool hasExtension = (ctx->m_rgucReadBuffer[3] & 2) ? SDL_TRUE : SDL_FALSE; + if (hadExtension != hasExtension || ctx->m_eCommState == k_eWiiCommunicationState_Error) { 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, &eExtensionControllerType); - } + ctx->m_eCommState = k_eWiiCommunicationState_ExtensionIdentify1; + SendExtensionIdentify1(ctx, SDL_FALSE); } else { - eExtensionControllerType = k_eWiiExtensionControllerType_None; - } - if (eExtensionControllerType != ctx->m_eExtensionControllerType) { - /* Mark this controller as disconnected so we re-connect with a new identity */ - HIDAPI_JoystickDisconnected(ctx->device, joystick->instance_id); + ctx->m_eCommState = k_eWiiCommunicationState_None; + ctx->m_eExtensionControllerType = k_eWiiExtensionControllerType_None; + InitializeExtension(ctx); } } } -static void HandleResponse(SDL_DriverWii_Context *ctx) +static void HandleResponse(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick) { + EWiiInputReportIDs type = ctx->m_rgucReadBuffer[0]; + WiiButtonData data; + SDL_assert(type == k_eWiiInputReportIDs_Acknowledge || type == k_eWiiInputReportIDs_ReadMemory); + SDL_zero(data); + GetBaseButtons(&data, ctx->m_rgucReadBuffer + 1); + HandleButtonData(ctx, joystick, &data); + + switch (ctx->m_eCommState) { + case k_eWiiCommunicationState_None: + break; + + case k_eWiiCommunicationState_Error: + /* Don't parse packets in this state */ + return; + + case k_eWiiCommunicationState_ExtensionIdentify1: + if (type == k_eWiiInputReportIDs_Acknowledge && ctx->m_rgucReadBuffer[3] == k_eWiiOutputReportIDs_WriteMemory) { + if (ctx->m_rgucReadBuffer[4]) { + ctx->m_eCommState = k_eWiiCommunicationState_Error; + SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "HIDAPI WII: Extension identify write 1 failed: %d", ctx->m_rgucReadBuffer[4]); + } else { + ctx->m_eCommState = k_eWiiCommunicationState_ExtensionIdentify2; + SendExtensionIdentify2(ctx, SDL_FALSE); + } + return; + } + break; + + case k_eWiiCommunicationState_ExtensionIdentify2: + if (type == k_eWiiInputReportIDs_Acknowledge && ctx->m_rgucReadBuffer[3] == k_eWiiOutputReportIDs_WriteMemory) { + if (ctx->m_rgucReadBuffer[4]) { + ctx->m_eCommState = k_eWiiCommunicationState_Error; + SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "HIDAPI WII: Extension identify write 2 failed: %d", ctx->m_rgucReadBuffer[4]); + } else { + ctx->m_eCommState = k_eWiiCommunicationState_ExtensionIdentify3; + SendExtensionIdentify3(ctx, SDL_FALSE); + } + return; + } + break; + + case k_eWiiCommunicationState_ExtensionIdentify3: + if (type == k_eWiiInputReportIDs_ReadMemory) { + EWiiExtensionControllerType type = k_eWiiExtensionControllerType_Unknown; + if (ParseExtensionResponse(ctx, &type)) { + ctx->m_eCommState = k_eWiiCommunicationState_None; + ctx->m_eExtensionControllerType = type; + InitializeExtension(ctx); + } else { + char msg[512]; + SDL_GetErrorMsg(msg, sizeof(msg) - 1); + SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "HIDAPI WII: Failed to parse extension response: %s", msg); + ctx->m_eCommState = k_eWiiCommunicationState_Error; + } + return; + } + break; + } + + if (type == k_eWiiInputReportIDs_Acknowledge) { + SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "HIDAPI WII: A mysterious ack has arrived! Type: %02x, Code: %d", + ctx->m_rgucReadBuffer[3], ctx->m_rgucReadBuffer[4]); + } else { + static const char hextable[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + char str[48]; + Uint32 strpos = 0; + Uint32 len = (ctx->m_rgucReadBuffer[3] >> 4) + 1; + for (Uint32 i = 0; i < len; i++) { + str[strpos++] = hextable[ctx->m_rgucReadBuffer[6 + i] >> 4]; + str[strpos++] = hextable[ctx->m_rgucReadBuffer[6 + i] & 0xF]; + str[strpos++] = ' '; + } + str[strpos] = '\0'; + SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "HIDAPI WII: A mysterious read response has arrived! Address: %02x%02x, Code: %d, Data: %s", + ctx->m_rgucReadBuffer[4], ctx->m_rgucReadBuffer[5], + ctx->m_rgucReadBuffer[3] & 0xF, + str); + } } static void HandleButtonPacket(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick) @@ -927,7 +1137,7 @@ static void HandleButtonPacket(SDL_DriverWii_Context *ctx, SDL_Joystick *joystic SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "HIDAPI WII: Unsupported button data type %02x", ctx->m_rgucReadBuffer[0]); return; } - HandleButtonData(ctx, joystick, data); + HandleButtonData(ctx, joystick, &data); } static void HandleInput(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick) @@ -936,7 +1146,7 @@ static void HandleInput(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick) if (type == k_eWiiInputReportIDs_Status) { HandleStatus(ctx, joystick); } else if (type == k_eWiiInputReportIDs_Acknowledge || type == k_eWiiInputReportIDs_ReadMemory) { - HandleResponse(ctx); + HandleResponse(ctx, joystick); } else if (type >= k_eWiiInputReportIDs_ButtonData0 && type <= k_eWiiInputReportIDs_ButtonDataF) { HandleButtonPacket(ctx, joystick); } else {