Atlas - SDL_hidapi_wii.c

Home / ext / SDL / src / joystick / hidapi Lines: 1 | Size: 55027 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#ifdef SDL_JOYSTICK_HIDAPI 24 25#include "../../SDL_hints_c.h" 26#include "../SDL_sysjoystick.h" 27#include "SDL_hidapijoystick_c.h" 28#include "SDL_hidapi_rumble.h" 29#include "SDL_hidapi_nintendo.h" 30 31#ifdef SDL_JOYSTICK_HIDAPI_WII 32 33// Define this if you want to log all packets from the controller 34// #define DEBUG_WII_PROTOCOL 35 36#define ENABLE_CONTINUOUS_REPORTING true 37 38#define INPUT_WAIT_TIMEOUT_MS (3 * 1000) 39#define MOTION_PLUS_UPDATE_TIME_MS (8 * 1000) 40#define STATUS_UPDATE_TIME_MS (15 * 60 * 1000) 41 42#define WII_EXTENSION_NONE 0x2E2E 43#define WII_EXTENSION_UNINITIALIZED 0xFFFF 44#define WII_EXTENSION_NUNCHUK 0x0000 45#define WII_EXTENSION_GAMEPAD 0x0101 46#define WII_EXTENSION_WIIUPRO 0x0120 47#define WII_EXTENSION_MOTIONPLUS_MASK 0xF0FF 48#define WII_EXTENSION_MOTIONPLUS_ID 0x0005 49 50#define WII_MOTIONPLUS_MODE_NONE 0x00 51#define WII_MOTIONPLUS_MODE_STANDARD 0x04 52#define WII_MOTIONPLUS_MODE_NUNCHUK 0x05 53#define WII_MOTIONPLUS_MODE_GAMEPAD 0x07 54 55typedef enum 56{ 57 k_eWiiInputReportIDs_Status = 0x20, 58 k_eWiiInputReportIDs_ReadMemory = 0x21, 59 k_eWiiInputReportIDs_Acknowledge = 0x22, 60 k_eWiiInputReportIDs_ButtonData0 = 0x30, 61 k_eWiiInputReportIDs_ButtonData1 = 0x31, 62 k_eWiiInputReportIDs_ButtonData2 = 0x32, 63 k_eWiiInputReportIDs_ButtonData3 = 0x33, 64 k_eWiiInputReportIDs_ButtonData4 = 0x34, 65 k_eWiiInputReportIDs_ButtonData5 = 0x35, 66 k_eWiiInputReportIDs_ButtonData6 = 0x36, 67 k_eWiiInputReportIDs_ButtonData7 = 0x37, 68 k_eWiiInputReportIDs_ButtonDataD = 0x3D, 69 k_eWiiInputReportIDs_ButtonDataE = 0x3E, 70 k_eWiiInputReportIDs_ButtonDataF = 0x3F, 71} EWiiInputReportIDs; 72 73typedef enum 74{ 75 k_eWiiOutputReportIDs_Rumble = 0x10, 76 k_eWiiOutputReportIDs_LEDs = 0x11, 77 k_eWiiOutputReportIDs_DataReportingMode = 0x12, 78 k_eWiiOutputReportIDs_IRCameraEnable = 0x13, 79 k_eWiiOutputReportIDs_SpeakerEnable = 0x14, 80 k_eWiiOutputReportIDs_StatusRequest = 0x15, 81 k_eWiiOutputReportIDs_WriteMemory = 0x16, 82 k_eWiiOutputReportIDs_ReadMemory = 0x17, 83 k_eWiiOutputReportIDs_SpeakerData = 0x18, 84 k_eWiiOutputReportIDs_SpeakerMute = 0x19, 85 k_eWiiOutputReportIDs_IRCameraEnable2 = 0x1a, 86} EWiiOutputReportIDs; 87 88typedef enum 89{ 90 k_eWiiPlayerLEDs_P1 = 0x10, 91 k_eWiiPlayerLEDs_P2 = 0x20, 92 k_eWiiPlayerLEDs_P3 = 0x40, 93 k_eWiiPlayerLEDs_P4 = 0x80, 94} EWiiPlayerLEDs; 95 96typedef enum 97{ 98 k_eWiiCommunicationState_None, // No special communications happening 99 k_eWiiCommunicationState_CheckMotionPlusStage1, // Sent standard extension identify request 100 k_eWiiCommunicationState_CheckMotionPlusStage2, // Sent Motion Plus extension identify request 101} EWiiCommunicationState; 102 103typedef enum 104{ 105 k_eWiiButtons_A = SDL_GAMEPAD_BUTTON_MISC1, 106 k_eWiiButtons_B, 107 k_eWiiButtons_One, 108 k_eWiiButtons_Two, 109 k_eWiiButtons_Plus, 110 k_eWiiButtons_Minus, 111 k_eWiiButtons_Home, 112 k_eWiiButtons_DPad_Up, 113 k_eWiiButtons_DPad_Down, 114 k_eWiiButtons_DPad_Left, 115 k_eWiiButtons_DPad_Right, 116 k_eWiiButtons_Max 117} EWiiButtons; 118 119#define k_unWiiPacketDataLength 22 120 121typedef struct 122{ 123 Uint8 rgucBaseButtons[2]; 124 Uint8 rgucAccelerometer[3]; 125 Uint8 rgucExtension[21]; 126 bool hasBaseButtons; 127 bool hasAccelerometer; 128 Uint8 ucNExtensionBytes; 129} WiiButtonData; 130 131typedef struct 132{ 133 Uint16 min; 134 Uint16 max; 135 Uint16 center; 136 Uint16 deadzone; 137} StickCalibrationData; 138 139typedef struct 140{ 141 SDL_HIDAPI_Device *device; 142 SDL_Joystick *joystick; 143 Uint64 timestamp; 144 EWiiCommunicationState m_eCommState; 145 EWiiExtensionControllerType m_eExtensionControllerType; 146 bool m_bPlayerLights; 147 int m_nPlayerIndex; 148 bool m_bRumbleActive; 149 bool m_bMotionPlusPresent; 150 Uint8 m_ucMotionPlusMode; 151 bool m_bReportSensors; 152 Uint8 m_rgucReadBuffer[k_unWiiPacketDataLength]; 153 Uint64 m_ulLastInput; 154 Uint64 m_ulLastStatus; 155 Uint64 m_ulNextMotionPlusCheck; 156 bool m_bDisconnected; 157 158 StickCalibrationData m_StickCalibrationData[6]; 159} SDL_DriverWii_Context; 160 161static void HIDAPI_DriverWii_RegisterHints(SDL_HintCallback callback, void *userdata) 162{ 163 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_WII, callback, userdata); 164} 165 166static void HIDAPI_DriverWii_UnregisterHints(SDL_HintCallback callback, void *userdata) 167{ 168 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_WII, callback, userdata); 169} 170 171static bool HIDAPI_DriverWii_IsEnabled(void) 172{ 173#if 1 // This doesn't work with the dolphinbar, so don't enable by default right now 174 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_WII, false); 175#else 176 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_WII, 177 SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, 178 SDL_HIDAPI_DEFAULT)); 179#endif 180} 181 182static bool HIDAPI_DriverWii_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol) 183{ 184 if (vendor_id == USB_VENDOR_NINTENDO && 185 (product_id == USB_PRODUCT_NINTENDO_WII_REMOTE || 186 product_id == USB_PRODUCT_NINTENDO_WII_REMOTE2)) { 187 return true; 188 } 189 return false; 190} 191 192static int ReadInput(SDL_DriverWii_Context *ctx) 193{ 194 int size; 195 196 // Make sure we don't try to read at the same time a write is happening 197 if (SDL_GetAtomicInt(&ctx->device->rumble_pending) > 0) { 198 return 0; 199 } 200 201 size = SDL_hid_read_timeout(ctx->device->dev, ctx->m_rgucReadBuffer, sizeof(ctx->m_rgucReadBuffer), 0); 202#ifdef DEBUG_WII_PROTOCOL 203 if (size > 0) { 204 HIDAPI_DumpPacket("Wii packet: size = %d", ctx->m_rgucReadBuffer, size); 205 } 206#endif 207 return size; 208} 209 210static bool WriteOutput(SDL_DriverWii_Context *ctx, const Uint8 *data, int size, bool sync) 211{ 212#ifdef DEBUG_WII_PROTOCOL 213 if (size > 0) { 214 HIDAPI_DumpPacket("Wii write packet: size = %d", data, size); 215 } 216#endif 217 if (sync) { 218 return SDL_hid_write(ctx->device->dev, data, size) >= 0; 219 } else { 220 // Use the rumble thread for general asynchronous writes 221 if (!SDL_HIDAPI_LockRumble()) { 222 return false; 223 } 224 return SDL_HIDAPI_SendRumbleAndUnlock(ctx->device, data, size) >= 0; 225 } 226} 227 228static bool ReadInputSync(SDL_DriverWii_Context *ctx, EWiiInputReportIDs expectedID, bool (*isMine)(const Uint8 *)) 229{ 230 Uint64 endTicks = SDL_GetTicks() + 250; // Seeing successful reads after about 200 ms 231 232 int nRead = 0; 233 while ((nRead = ReadInput(ctx)) != -1) { 234 if (nRead > 0) { 235 if (ctx->m_rgucReadBuffer[0] == expectedID && (!isMine || isMine(ctx->m_rgucReadBuffer))) { 236 return true; 237 } 238 } else { 239 if (SDL_GetTicks() >= endTicks) { 240 break; 241 } 242 SDL_Delay(1); 243 } 244 } 245 SDL_SetError("Read timed out"); 246 return false; 247} 248 249static bool IsWriteMemoryResponse(const Uint8 *data) 250{ 251 return data[3] == k_eWiiOutputReportIDs_WriteMemory; 252} 253 254static bool WriteRegister(SDL_DriverWii_Context *ctx, Uint32 address, const Uint8 *data, int size, bool sync) 255{ 256 Uint8 writeRequest[k_unWiiPacketDataLength]; 257 258 SDL_zeroa(writeRequest); 259 writeRequest[0] = k_eWiiOutputReportIDs_WriteMemory; 260 writeRequest[1] = (Uint8)(0x04 | (Uint8)ctx->m_bRumbleActive); 261 writeRequest[2] = (address >> 16) & 0xff; 262 writeRequest[3] = (address >> 8) & 0xff; 263 writeRequest[4] = address & 0xff; 264 writeRequest[5] = (Uint8)size; 265 SDL_assert(size > 0 && size <= 16); 266 SDL_memcpy(writeRequest + 6, data, size); 267 268 if (!WriteOutput(ctx, writeRequest, sizeof(writeRequest), sync)) { 269 return false; 270 } 271 if (sync) { 272 // Wait for response 273 if (!ReadInputSync(ctx, k_eWiiInputReportIDs_Acknowledge, IsWriteMemoryResponse)) { 274 return false; 275 } 276 if (ctx->m_rgucReadBuffer[4]) { 277 SDL_SetError("Write memory failed: %u", ctx->m_rgucReadBuffer[4]); 278 return false; 279 } 280 } 281 return true; 282} 283 284static bool ReadRegister(SDL_DriverWii_Context *ctx, Uint32 address, int size, bool sync) 285{ 286 Uint8 readRequest[7]; 287 288 readRequest[0] = k_eWiiOutputReportIDs_ReadMemory; 289 readRequest[1] = (Uint8)(0x04 | (Uint8)ctx->m_bRumbleActive); 290 readRequest[2] = (address >> 16) & 0xff; 291 readRequest[3] = (address >> 8) & 0xff; 292 readRequest[4] = address & 0xff; 293 readRequest[5] = (size >> 8) & 0xff; 294 readRequest[6] = size & 0xff; 295 296 SDL_assert(size > 0 && size <= 0xffff); 297 298 if (!WriteOutput(ctx, readRequest, sizeof(readRequest), sync)) { 299 return false; 300 } 301 if (sync) { 302 SDL_assert(size <= 16); // Only waiting for one packet is supported right now 303 // Wait for response 304 if (!ReadInputSync(ctx, k_eWiiInputReportIDs_ReadMemory, NULL)) { 305 return false; 306 } 307 } 308 return true; 309} 310 311static bool SendExtensionIdentify(SDL_DriverWii_Context *ctx, bool sync) 312{ 313 return ReadRegister(ctx, 0xA400FE, 2, sync); 314} 315 316static bool ParseExtensionIdentifyResponse(SDL_DriverWii_Context *ctx, Uint16 *extension) 317{ 318 int i; 319 320 if (ctx->m_rgucReadBuffer[0] != k_eWiiInputReportIDs_ReadMemory) { 321 SDL_SetError("Unexpected extension response type"); 322 return false; 323 } 324 325 if (ctx->m_rgucReadBuffer[4] != 0x00 || ctx->m_rgucReadBuffer[5] != 0xFE) { 326 SDL_SetError("Unexpected extension response address"); 327 return false; 328 } 329 330 if (ctx->m_rgucReadBuffer[3] != 0x10) { 331 Uint8 error = (ctx->m_rgucReadBuffer[3] & 0xF); 332 333 if (error == 7) { 334 // The extension memory isn't mapped 335 *extension = WII_EXTENSION_NONE; 336 return true; 337 } 338 339 if (error) { 340 SDL_SetError("Failed to read extension type: %u", error); 341 } else { 342 SDL_SetError("Unexpected read length when reading extension type: %d", (ctx->m_rgucReadBuffer[3] >> 4) + 1); 343 } 344 return false; 345 } 346 347 *extension = 0; 348 for (i = 6; i < 8; i++) { 349 *extension = *extension << 8 | ctx->m_rgucReadBuffer[i]; 350 } 351 return true; 352} 353 354static EWiiExtensionControllerType GetExtensionType(Uint16 extension_id) 355{ 356 switch (extension_id) { 357 case WII_EXTENSION_NONE: 358 return k_eWiiExtensionControllerType_None; 359 case WII_EXTENSION_NUNCHUK: 360 return k_eWiiExtensionControllerType_Nunchuk; 361 case WII_EXTENSION_GAMEPAD: 362 return k_eWiiExtensionControllerType_Gamepad; 363 case WII_EXTENSION_WIIUPRO: 364 return k_eWiiExtensionControllerType_WiiUPro; 365 default: 366 return k_eWiiExtensionControllerType_Unknown; 367 } 368} 369 370static bool SendExtensionReset(SDL_DriverWii_Context *ctx, bool sync) 371{ 372 bool result = true; 373 { 374 Uint8 data = 0x55; 375 result = result && WriteRegister(ctx, 0xA400F0, &data, sizeof(data), sync); 376 } 377 // This write will fail if there is no extension connected, that's fine 378 { 379 Uint8 data = 0x00; 380 (void)WriteRegister(ctx, 0xA400FB, &data, sizeof(data), sync); 381 } 382 return result; 383} 384 385static bool GetMotionPlusState(SDL_DriverWii_Context *ctx, bool *connected, Uint8 *mode) 386{ 387 Uint16 extension; 388 389 if (connected) { 390 *connected = false; 391 } 392 if (mode) { 393 *mode = 0; 394 } 395 396 if (ctx->m_eExtensionControllerType == k_eWiiExtensionControllerType_WiiUPro) { 397 // The Wii U Pro controller never has the Motion Plus extension 398 return true; 399 } 400 401 if (SendExtensionIdentify(ctx, true) && 402 ParseExtensionIdentifyResponse(ctx, &extension)) { 403 if ((extension & WII_EXTENSION_MOTIONPLUS_MASK) == WII_EXTENSION_MOTIONPLUS_ID) { 404 // Motion Plus is currently active 405 if (connected) { 406 *connected = true; 407 } 408 if (mode) { 409 *mode = (extension >> 8); 410 } 411 return true; 412 } 413 } 414 415 if (ReadRegister(ctx, 0xA600FE, 2, true) && 416 ParseExtensionIdentifyResponse(ctx, &extension)) { 417 if ((extension & WII_EXTENSION_MOTIONPLUS_MASK) == WII_EXTENSION_MOTIONPLUS_ID) { 418 // Motion Plus is currently connected 419 if (connected) { 420 *connected = true; 421 } 422 } 423 return true; 424 } 425 426 // Failed to read the register or parse the response 427 return false; 428} 429 430static bool NeedsPeriodicMotionPlusCheck(SDL_DriverWii_Context *ctx, bool status_update) 431{ 432 if (ctx->m_eExtensionControllerType == k_eWiiExtensionControllerType_WiiUPro) { 433 // The Wii U Pro controller never has the Motion Plus extension 434 return false; 435 } 436 437 if (ctx->m_ucMotionPlusMode != WII_MOTIONPLUS_MODE_NONE && !status_update) { 438 // We'll get a status update when Motion Plus is disconnected 439 return false; 440 } 441 442 return true; 443} 444 445static void SchedulePeriodicMotionPlusCheck(SDL_DriverWii_Context *ctx) 446{ 447 ctx->m_ulNextMotionPlusCheck = SDL_GetTicks() + MOTION_PLUS_UPDATE_TIME_MS; 448} 449 450static void CheckMotionPlusConnection(SDL_DriverWii_Context *ctx) 451{ 452 SendExtensionIdentify(ctx, false); 453 454 ctx->m_eCommState = k_eWiiCommunicationState_CheckMotionPlusStage1; 455} 456 457static void ActivateMotionPlusWithMode(SDL_DriverWii_Context *ctx, Uint8 mode) 458{ 459#ifdef SDL_PLATFORM_LINUX 460 /* Linux drivers maintain a lot of state around the Motion Plus 461 * extension, so don't mess with it here. 462 */ 463#else 464 WriteRegister(ctx, 0xA600FE, &mode, sizeof(mode), true); 465 466 ctx->m_ucMotionPlusMode = mode; 467#endif // LINUX 468} 469 470static void ActivateMotionPlus(SDL_DriverWii_Context *ctx) 471{ 472 Uint8 mode = WII_MOTIONPLUS_MODE_STANDARD; 473 474 // Pick the pass-through mode based on the connected controller 475 if (ctx->m_eExtensionControllerType == k_eWiiExtensionControllerType_Nunchuk) { 476 mode = WII_MOTIONPLUS_MODE_NUNCHUK; 477 } else if (ctx->m_eExtensionControllerType == k_eWiiExtensionControllerType_Gamepad) { 478 mode = WII_MOTIONPLUS_MODE_GAMEPAD; 479 } 480 ActivateMotionPlusWithMode(ctx, mode); 481} 482 483static void DeactivateMotionPlus(SDL_DriverWii_Context *ctx) 484{ 485 Uint8 data = 0x55; 486 WriteRegister(ctx, 0xA400F0, &data, sizeof(data), true); 487 488 // Wait for the deactivation status message 489 ReadInputSync(ctx, k_eWiiInputReportIDs_Status, NULL); 490 491 ctx->m_ucMotionPlusMode = WII_MOTIONPLUS_MODE_NONE; 492} 493 494static void UpdatePowerLevelWii(SDL_Joystick *joystick, Uint8 batteryLevelByte) 495{ 496 int percent; 497 if (batteryLevelByte > 178) { 498 percent = 100; 499 } else if (batteryLevelByte > 51) { 500 percent = 70; 501 } else if (batteryLevelByte > 13) { 502 percent = 20; 503 } else { 504 percent = 5; 505 } 506 SDL_SendJoystickPowerInfo(joystick, SDL_POWERSTATE_ON_BATTERY, percent); 507} 508 509static void UpdatePowerLevelWiiU(SDL_Joystick *joystick, Uint8 extensionBatteryByte) 510{ 511 bool charging = !(extensionBatteryByte & 0x08); 512 bool pluggedIn = !(extensionBatteryByte & 0x04); 513 Uint8 batteryLevel = extensionBatteryByte >> 4; 514 515 SDL_AssertJoysticksLocked(); 516 517 if (pluggedIn) { 518 joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRED; 519 } else { 520 joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS; 521 } 522 523 /* Not sure if all Wii U Pro controllers act like this, but on mine 524 * 4, 3, and 2 are held for about 20 hours each 525 * 1 is held for about 6 hours 526 * 0 is held for about 2 hours 527 * No value above 4 has been observed. 528 */ 529 SDL_PowerState state; 530 int percent; 531 if (charging) { 532 state = SDL_POWERSTATE_CHARGING; 533 } else if (pluggedIn) { 534 state = SDL_POWERSTATE_CHARGED; 535 } else { 536 state = SDL_POWERSTATE_ON_BATTERY; 537 } 538 if (batteryLevel >= 4) { 539 percent = 100; 540 } else if (batteryLevel == 3) { 541 percent = 70; 542 } else if (batteryLevel == 2) { 543 percent = 40; 544 } else if (batteryLevel == 1) { 545 percent = 10; 546 } else { 547 percent = 3; 548 } 549 SDL_SendJoystickPowerInfo(joystick, state, percent); 550} 551 552static EWiiInputReportIDs GetButtonPacketType(SDL_DriverWii_Context *ctx) 553{ 554 switch (ctx->m_eExtensionControllerType) { 555 case k_eWiiExtensionControllerType_WiiUPro: 556 return k_eWiiInputReportIDs_ButtonDataD; 557 case k_eWiiExtensionControllerType_Nunchuk: 558 case k_eWiiExtensionControllerType_Gamepad: 559 if (ctx->m_bReportSensors) { 560 return k_eWiiInputReportIDs_ButtonData5; 561 } else { 562 return k_eWiiInputReportIDs_ButtonData2; 563 } 564 default: 565 if (ctx->m_bReportSensors) { 566 return k_eWiiInputReportIDs_ButtonData5; 567 } else { 568 return k_eWiiInputReportIDs_ButtonData0; 569 } 570 } 571} 572 573static bool RequestButtonPacketType(SDL_DriverWii_Context *ctx, EWiiInputReportIDs type) 574{ 575 Uint8 data[3]; 576 Uint8 tt = (Uint8)ctx->m_bRumbleActive; 577 578 // Continuous reporting off, tt & 4 == 0 579 if (ENABLE_CONTINUOUS_REPORTING) { 580 tt |= 4; 581 } 582 583 data[0] = k_eWiiOutputReportIDs_DataReportingMode; 584 data[1] = tt; 585 data[2] = type; 586 return WriteOutput(ctx, data, sizeof(data), false); 587} 588 589static void ResetButtonPacketType(SDL_DriverWii_Context *ctx) 590{ 591 RequestButtonPacketType(ctx, GetButtonPacketType(ctx)); 592} 593 594static void InitStickCalibrationData(SDL_DriverWii_Context *ctx) 595{ 596 int i; 597 switch (ctx->m_eExtensionControllerType) { 598 case k_eWiiExtensionControllerType_WiiUPro: 599 for (i = 0; i < 4; i++) { 600 ctx->m_StickCalibrationData[i].min = 1000; 601 ctx->m_StickCalibrationData[i].max = 3000; 602 ctx->m_StickCalibrationData[i].center = 0; 603 ctx->m_StickCalibrationData[i].deadzone = 100; 604 } 605 break; 606 case k_eWiiExtensionControllerType_Gamepad: 607 for (i = 0; i < 4; i++) { 608 ctx->m_StickCalibrationData[i].min = i < 2 ? 9 : 5; 609 ctx->m_StickCalibrationData[i].max = i < 2 ? 54 : 26; 610 ctx->m_StickCalibrationData[i].center = 0; 611 ctx->m_StickCalibrationData[i].deadzone = i < 2 ? 4 : 2; 612 } 613 break; 614 case k_eWiiExtensionControllerType_Nunchuk: 615 for (i = 0; i < 2; i++) { 616 ctx->m_StickCalibrationData[i].min = 40; 617 ctx->m_StickCalibrationData[i].max = 215; 618 ctx->m_StickCalibrationData[i].center = 0; 619 ctx->m_StickCalibrationData[i].deadzone = 10; 620 } 621 break; 622 default: 623 break; 624 } 625} 626 627static void InitializeExtension(SDL_DriverWii_Context *ctx) 628{ 629 SendExtensionReset(ctx, true); 630 InitStickCalibrationData(ctx); 631 ResetButtonPacketType(ctx); 632} 633 634static void UpdateSlotLED(SDL_DriverWii_Context *ctx) 635{ 636 Uint8 leds; 637 Uint8 data[2]; 638 639 // The lowest bit needs to have the rumble status 640 leds = (Uint8)ctx->m_bRumbleActive; 641 642 if (ctx->m_bPlayerLights) { 643 // Use the same LED codes as Smash 8-player for 5-7 644 if (ctx->m_nPlayerIndex == 0 || ctx->m_nPlayerIndex > 3) { 645 leds |= k_eWiiPlayerLEDs_P1; 646 } 647 if (ctx->m_nPlayerIndex == 1 || ctx->m_nPlayerIndex == 4) { 648 leds |= k_eWiiPlayerLEDs_P2; 649 } 650 if (ctx->m_nPlayerIndex == 2 || ctx->m_nPlayerIndex == 5) { 651 leds |= k_eWiiPlayerLEDs_P3; 652 } 653 if (ctx->m_nPlayerIndex == 3 || ctx->m_nPlayerIndex == 6) { 654 leds |= k_eWiiPlayerLEDs_P4; 655 } 656 // Turn on all lights for other player indexes 657 if (ctx->m_nPlayerIndex < 0 || ctx->m_nPlayerIndex > 6) { 658 leds |= k_eWiiPlayerLEDs_P1 | k_eWiiPlayerLEDs_P2 | k_eWiiPlayerLEDs_P3 | k_eWiiPlayerLEDs_P4; 659 } 660 } 661 662 data[0] = k_eWiiOutputReportIDs_LEDs; 663 data[1] = leds; 664 WriteOutput(ctx, data, sizeof(data), false); 665} 666 667static void SDLCALL SDL_PlayerLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 668{ 669 SDL_DriverWii_Context *ctx = (SDL_DriverWii_Context *)userdata; 670 bool bPlayerLights = SDL_GetStringBoolean(hint, true); 671 672 if (bPlayerLights != ctx->m_bPlayerLights) { 673 ctx->m_bPlayerLights = bPlayerLights; 674 675 UpdateSlotLED(ctx); 676 } 677} 678 679static EWiiExtensionControllerType ReadExtensionControllerType(SDL_HIDAPI_Device *device) 680{ 681 SDL_DriverWii_Context *ctx = (SDL_DriverWii_Context *)device->context; 682 EWiiExtensionControllerType eExtensionControllerType = k_eWiiExtensionControllerType_Unknown; 683 const int MAX_ATTEMPTS = 20; 684 int attempts = 0; 685 686 // Create enough of a context to read the controller type from the device 687 for (attempts = 0; attempts < MAX_ATTEMPTS; ++attempts) { 688 Uint16 extension; 689 if (SendExtensionIdentify(ctx, true) && 690 ParseExtensionIdentifyResponse(ctx, &extension)) { 691 Uint8 motion_plus_mode = 0; 692 if ((extension & WII_EXTENSION_MOTIONPLUS_MASK) == WII_EXTENSION_MOTIONPLUS_ID) { 693 motion_plus_mode = (Uint8)(extension >> 8); 694 } 695 if (motion_plus_mode || extension == WII_EXTENSION_UNINITIALIZED) { 696 SendExtensionReset(ctx, true); 697 if (SendExtensionIdentify(ctx, true)) { 698 ParseExtensionIdentifyResponse(ctx, &extension); 699 } 700 } 701 702 eExtensionControllerType = GetExtensionType(extension); 703 704 // Reset the Motion Plus controller if needed 705 if (motion_plus_mode) { 706 ActivateMotionPlusWithMode(ctx, motion_plus_mode); 707 } 708 break; 709 } 710 } 711 return eExtensionControllerType; 712} 713 714static void UpdateDeviceIdentity(SDL_HIDAPI_Device *device) 715{ 716 SDL_DriverWii_Context *ctx = (SDL_DriverWii_Context *)device->context; 717 718 switch (ctx->m_eExtensionControllerType) { 719 case k_eWiiExtensionControllerType_None: 720 HIDAPI_SetDeviceName(device, "Nintendo Wii Remote"); 721 break; 722 case k_eWiiExtensionControllerType_Nunchuk: 723 HIDAPI_SetDeviceName(device, "Nintendo Wii Remote with Nunchuk"); 724 break; 725 case k_eWiiExtensionControllerType_Gamepad: 726 HIDAPI_SetDeviceName(device, "Nintendo Wii Remote with Classic Controller"); 727 break; 728 case k_eWiiExtensionControllerType_WiiUPro: 729 HIDAPI_SetDeviceName(device, "Nintendo Wii U Pro Controller"); 730 break; 731 default: 732 HIDAPI_SetDeviceName(device, "Nintendo Wii Remote with Unknown Extension"); 733 break; 734 } 735 device->guid.data[15] = ctx->m_eExtensionControllerType; 736} 737 738static bool HIDAPI_DriverWii_InitDevice(SDL_HIDAPI_Device *device) 739{ 740 SDL_DriverWii_Context *ctx; 741 742 ctx = (SDL_DriverWii_Context *)SDL_calloc(1, sizeof(*ctx)); 743 if (!ctx) { 744 return false; 745 } 746 ctx->device = device; 747 device->context = ctx; 748 749 if (device->vendor_id == USB_VENDOR_NINTENDO) { 750 ctx->m_eExtensionControllerType = ReadExtensionControllerType(device); 751 752 UpdateDeviceIdentity(device); 753 } 754 return HIDAPI_JoystickConnected(device, NULL); 755} 756 757static int HIDAPI_DriverWii_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) 758{ 759 return -1; 760} 761 762static void HIDAPI_DriverWii_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) 763{ 764 SDL_DriverWii_Context *ctx = (SDL_DriverWii_Context *)device->context; 765 766 if (!ctx->joystick) { 767 return; 768 } 769 770 ctx->m_nPlayerIndex = player_index; 771 772 UpdateSlotLED(ctx); 773} 774 775static bool HIDAPI_DriverWii_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 776{ 777 SDL_DriverWii_Context *ctx = (SDL_DriverWii_Context *)device->context; 778 779 SDL_AssertJoysticksLocked(); 780 781 ctx->joystick = joystick; 782 783 InitializeExtension(ctx); 784 785 GetMotionPlusState(ctx, &ctx->m_bMotionPlusPresent, &ctx->m_ucMotionPlusMode); 786 787 if (NeedsPeriodicMotionPlusCheck(ctx, false)) { 788 SchedulePeriodicMotionPlusCheck(ctx); 789 } 790 791 if (ctx->m_eExtensionControllerType == k_eWiiExtensionControllerType_None || 792 ctx->m_eExtensionControllerType == k_eWiiExtensionControllerType_Nunchuk) { 793 SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 100.0f); 794 if (ctx->m_eExtensionControllerType == k_eWiiExtensionControllerType_Nunchuk) { 795 SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL_L, 100.0f); 796 } 797 798 if (ctx->m_bMotionPlusPresent) { 799 SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 100.0f); 800 } 801 } 802 803 // Initialize player index (needed for setting LEDs) 804 ctx->m_nPlayerIndex = SDL_GetJoystickPlayerIndex(joystick); 805 ctx->m_bPlayerLights = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_WII_PLAYER_LED, true); 806 UpdateSlotLED(ctx); 807 808 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_WII_PLAYER_LED, 809 SDL_PlayerLEDHintChanged, ctx); 810 811 // Initialize the joystick capabilities 812 if (ctx->m_eExtensionControllerType == k_eWiiExtensionControllerType_WiiUPro) { 813 joystick->nbuttons = 15; 814 } else { 815 // Maximum is Classic Controller + Wiimote 816 joystick->nbuttons = k_eWiiButtons_Max; 817 } 818 joystick->naxes = SDL_GAMEPAD_AXIS_COUNT; 819 820 ctx->m_ulLastInput = SDL_GetTicks(); 821 822 return true; 823} 824 825static bool HIDAPI_DriverWii_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 826{ 827 SDL_DriverWii_Context *ctx = (SDL_DriverWii_Context *)device->context; 828 bool active = (low_frequency_rumble || high_frequency_rumble); 829 830 if (active != ctx->m_bRumbleActive) { 831 Uint8 data[2]; 832 833 data[0] = k_eWiiOutputReportIDs_Rumble; 834 data[1] = (Uint8)active; 835 WriteOutput(ctx, data, sizeof(data), false); 836 837 ctx->m_bRumbleActive = active; 838 } 839 return true; 840} 841 842static bool HIDAPI_DriverWii_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 843{ 844 return SDL_Unsupported(); 845} 846 847static Uint32 HIDAPI_DriverWii_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 848{ 849 return SDL_JOYSTICK_CAP_RUMBLE; 850} 851 852static bool HIDAPI_DriverWii_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 853{ 854 return SDL_Unsupported(); 855} 856 857static bool HIDAPI_DriverWii_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size) 858{ 859 return SDL_Unsupported(); 860} 861 862static bool HIDAPI_DriverWii_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled) 863{ 864 SDL_DriverWii_Context *ctx = (SDL_DriverWii_Context *)device->context; 865 866 if (enabled != ctx->m_bReportSensors) { 867 ctx->m_bReportSensors = enabled; 868 869 if (ctx->m_bMotionPlusPresent) { 870 if (enabled) { 871 ActivateMotionPlus(ctx); 872 } else { 873 DeactivateMotionPlus(ctx); 874 } 875 } 876 877 ResetButtonPacketType(ctx); 878 } 879 return true; 880} 881 882static void PostStickCalibrated(Uint64 timestamp, SDL_Joystick *joystick, StickCalibrationData *calibration, Uint8 axis, Uint16 data) 883{ 884 Sint16 value = 0; 885 if (!calibration->center) { 886 // Center on first read 887 calibration->center = data; 888 return; 889 } 890 if (data < calibration->min) { 891 calibration->min = data; 892 } 893 if (data > calibration->max) { 894 calibration->max = data; 895 } 896 if (data < calibration->center - calibration->deadzone) { 897 Uint16 zero = calibration->center - calibration->deadzone; 898 Uint16 range = zero - calibration->min; 899 Uint16 distance = zero - data; 900 float fvalue = (float)distance / (float)range; 901 value = (Sint16)(fvalue * SDL_JOYSTICK_AXIS_MIN); 902 } else if (data > calibration->center + calibration->deadzone) { 903 Uint16 zero = calibration->center + calibration->deadzone; 904 Uint16 range = calibration->max - zero; 905 Uint16 distance = data - zero; 906 float fvalue = (float)distance / (float)range; 907 value = (Sint16)(fvalue * SDL_JOYSTICK_AXIS_MAX); 908 } 909 if (axis == SDL_GAMEPAD_AXIS_LEFTY || axis == SDL_GAMEPAD_AXIS_RIGHTY) { 910 if (value) { 911 value = ~value; 912 } 913 } 914 SDL_SendJoystickAxis(timestamp, joystick, axis, value); 915} 916 917/* Send button data to SDL 918 *`defs` is a mapping for each bit to which button it represents. 0xFF indicates an unused bit 919 *`data` is the button data from the controller 920 *`size` is the number of bytes in `data` and the number of arrays of 8 mappings in `defs` 921 *`on` is the joystick value to be sent if a bit is on 922 *`off` is the joystick value to be sent if a bit is off 923 */ 924static void PostPackedButtonData(Uint64 timestamp, SDL_Joystick *joystick, const Uint8 defs[][8], const Uint8 *data, int size, bool on, bool off) 925{ 926 int i, j; 927 928 for (i = 0; i < size; i++) { 929 for (j = 0; j < 8; j++) { 930 Uint8 button = defs[i][j]; 931 if (button != 0xFF) { 932 bool down = (data[i] >> j) & 1 ? on : off; 933 SDL_SendJoystickButton(timestamp, joystick, button, down); 934 } 935 } 936 } 937} 938 939static const Uint8 GAMEPAD_BUTTON_DEFS[3][8] = { 940 { 941 0xFF /* Unused */, 942 SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, 943 SDL_GAMEPAD_BUTTON_START, 944 SDL_GAMEPAD_BUTTON_GUIDE, 945 SDL_GAMEPAD_BUTTON_BACK, 946 SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, 947 SDL_GAMEPAD_BUTTON_DPAD_DOWN, 948 SDL_GAMEPAD_BUTTON_DPAD_RIGHT, 949 }, 950 { 951 SDL_GAMEPAD_BUTTON_DPAD_UP, 952 SDL_GAMEPAD_BUTTON_DPAD_LEFT, 953 0xFF /* ZR */, 954 SDL_GAMEPAD_BUTTON_NORTH, 955 SDL_GAMEPAD_BUTTON_EAST, 956 SDL_GAMEPAD_BUTTON_WEST, 957 SDL_GAMEPAD_BUTTON_SOUTH, 958 0xFF /*ZL*/, 959 }, 960 { 961 SDL_GAMEPAD_BUTTON_RIGHT_STICK, 962 SDL_GAMEPAD_BUTTON_LEFT_STICK, 963 0xFF /* Charging */, 964 0xFF /* Plugged In */, 965 0xFF /* Unused */, 966 0xFF /* Unused */, 967 0xFF /* Unused */, 968 0xFF /* Unused */, 969 } 970}; 971 972static const Uint8 MP_GAMEPAD_BUTTON_DEFS[3][8] = { 973 { 974 0xFF /* Unused */, 975 SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, 976 SDL_GAMEPAD_BUTTON_START, 977 SDL_GAMEPAD_BUTTON_GUIDE, 978 SDL_GAMEPAD_BUTTON_BACK, 979 SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, 980 SDL_GAMEPAD_BUTTON_DPAD_DOWN, 981 SDL_GAMEPAD_BUTTON_DPAD_RIGHT, 982 }, 983 { 984 0xFF /* Motion Plus data */, 985 0xFF /* Motion Plus data */, 986 0xFF /* ZR */, 987 SDL_GAMEPAD_BUTTON_NORTH, 988 SDL_GAMEPAD_BUTTON_EAST, 989 SDL_GAMEPAD_BUTTON_WEST, 990 SDL_GAMEPAD_BUTTON_SOUTH, 991 0xFF /*ZL*/, 992 }, 993 { 994 SDL_GAMEPAD_BUTTON_RIGHT_STICK, 995 SDL_GAMEPAD_BUTTON_LEFT_STICK, 996 0xFF /* Charging */, 997 0xFF /* Plugged In */, 998 0xFF /* Unused */, 999 0xFF /* Unused */, 1000 0xFF /* Unused */, 1001 0xFF /* Unused */, 1002 } 1003}; 1004 1005static const Uint8 MP_FIXUP_DPAD_BUTTON_DEFS[2][8] = { 1006 { 1007 SDL_GAMEPAD_BUTTON_DPAD_UP, 1008 0xFF, 1009 0xFF, 1010 0xFF, 1011 0xFF, 1012 0xFF, 1013 0xFF, 1014 0xFF, 1015 }, 1016 { 1017 SDL_GAMEPAD_BUTTON_DPAD_LEFT, 1018 0xFF, 1019 0xFF, 1020 0xFF, 1021 0xFF, 1022 0xFF, 1023 0xFF, 1024 0xFF, 1025 } 1026}; 1027 1028static void HandleWiiUProButtonData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, const WiiButtonData *data) 1029{ 1030 static const Uint8 axes[] = { SDL_GAMEPAD_AXIS_LEFTX, SDL_GAMEPAD_AXIS_RIGHTX, SDL_GAMEPAD_AXIS_LEFTY, SDL_GAMEPAD_AXIS_RIGHTY }; 1031 const Uint8(*buttons)[8] = GAMEPAD_BUTTON_DEFS; 1032 Uint8 zl, zr; 1033 int i; 1034 1035 if (data->ucNExtensionBytes < 11) { 1036 return; 1037 } 1038 1039 // Buttons 1040 PostPackedButtonData(ctx->timestamp, joystick, buttons, data->rgucExtension + 8, 3, false, true); 1041 1042 // Triggers 1043 zl = data->rgucExtension[9] & 0x80; 1044 zr = data->rgucExtension[9] & 0x04; 1045 SDL_SendJoystickAxis(ctx->timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, zl ? SDL_JOYSTICK_AXIS_MIN : SDL_JOYSTICK_AXIS_MAX); 1046 SDL_SendJoystickAxis(ctx->timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, zr ? SDL_JOYSTICK_AXIS_MIN : SDL_JOYSTICK_AXIS_MAX); 1047 1048 // Sticks 1049 for (i = 0; i < 4; i++) { 1050 Uint16 value = data->rgucExtension[i * 2] | (data->rgucExtension[i * 2 + 1] << 8); 1051 PostStickCalibrated(ctx->timestamp, joystick, &ctx->m_StickCalibrationData[i], axes[i], value); 1052 } 1053 1054 // Power 1055 UpdatePowerLevelWiiU(joystick, data->rgucExtension[10]); 1056} 1057 1058static void HandleGamepadControllerButtonData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, const WiiButtonData *data) 1059{ 1060 const Uint8(*buttons)[8] = (ctx->m_ucMotionPlusMode == WII_MOTIONPLUS_MODE_GAMEPAD) ? MP_GAMEPAD_BUTTON_DEFS : GAMEPAD_BUTTON_DEFS; 1061 Uint8 lx, ly, rx, ry, zl, zr; 1062 1063 if (data->ucNExtensionBytes < 6) { 1064 return; 1065 } 1066 1067 // Buttons 1068 PostPackedButtonData(ctx->timestamp, joystick, buttons, data->rgucExtension + 4, 2, false, true); 1069 if (ctx->m_ucMotionPlusMode == WII_MOTIONPLUS_MODE_GAMEPAD) { 1070 PostPackedButtonData(ctx->timestamp, joystick, MP_FIXUP_DPAD_BUTTON_DEFS, data->rgucExtension, 2, false, true); 1071 } 1072 1073 // Triggers 1074 zl = data->rgucExtension[5] & 0x80; 1075 zr = data->rgucExtension[5] & 0x04; 1076 SDL_SendJoystickAxis(ctx->timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, zl ? SDL_JOYSTICK_AXIS_MIN : SDL_JOYSTICK_AXIS_MAX); 1077 SDL_SendJoystickAxis(ctx->timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, zr ? SDL_JOYSTICK_AXIS_MIN : SDL_JOYSTICK_AXIS_MAX); 1078 1079 // Sticks 1080 if (ctx->m_ucMotionPlusMode == WII_MOTIONPLUS_MODE_GAMEPAD) { 1081 lx = data->rgucExtension[0] & 0x3E; 1082 ly = data->rgucExtension[1] & 0x3E; 1083 } else { 1084 lx = data->rgucExtension[0] & 0x3F; 1085 ly = data->rgucExtension[1] & 0x3F; 1086 } 1087 rx = (data->rgucExtension[2] >> 7) | ((data->rgucExtension[1] >> 5) & 0x06) | ((data->rgucExtension[0] >> 3) & 0x18); 1088 ry = data->rgucExtension[2] & 0x1F; 1089 PostStickCalibrated(ctx->timestamp, joystick, &ctx->m_StickCalibrationData[0], SDL_GAMEPAD_AXIS_LEFTX, lx); 1090 PostStickCalibrated(ctx->timestamp, joystick, &ctx->m_StickCalibrationData[1], SDL_GAMEPAD_AXIS_LEFTY, ly); 1091 PostStickCalibrated(ctx->timestamp, joystick, &ctx->m_StickCalibrationData[2], SDL_GAMEPAD_AXIS_RIGHTX, rx); 1092 PostStickCalibrated(ctx->timestamp, joystick, &ctx->m_StickCalibrationData[3], SDL_GAMEPAD_AXIS_RIGHTY, ry); 1093} 1094 1095static void HandleWiiRemoteButtonData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, const WiiButtonData *data) 1096{ 1097 static const Uint8 buttons[2][8] = { 1098 { 1099 k_eWiiButtons_DPad_Left, 1100 k_eWiiButtons_DPad_Right, 1101 k_eWiiButtons_DPad_Down, 1102 k_eWiiButtons_DPad_Up, 1103 k_eWiiButtons_Plus, 1104 0xFF /* Unused */, 1105 0xFF /* Unused */, 1106 0xFF /* Unused */, 1107 }, 1108 { 1109 k_eWiiButtons_Two, 1110 k_eWiiButtons_One, 1111 k_eWiiButtons_B, 1112 k_eWiiButtons_A, 1113 k_eWiiButtons_Minus, 1114 0xFF /* Unused */, 1115 0xFF /* Unused */, 1116 k_eWiiButtons_Home, 1117 } 1118 }; 1119 if (data->hasBaseButtons) { 1120 PostPackedButtonData(ctx->timestamp, joystick, buttons, data->rgucBaseButtons, 2, true, false); 1121 } 1122} 1123 1124static void HandleWiiRemoteButtonDataAsMainController(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, const WiiButtonData *data) 1125{ 1126 /* Wii remote maps really badly to a normal controller 1127 * Mapped 1 and 2 as X and Y 1128 * Not going to attempt positional mapping 1129 */ 1130 static const Uint8 buttons[2][8] = { 1131 { 1132 SDL_GAMEPAD_BUTTON_DPAD_LEFT, 1133 SDL_GAMEPAD_BUTTON_DPAD_RIGHT, 1134 SDL_GAMEPAD_BUTTON_DPAD_DOWN, 1135 SDL_GAMEPAD_BUTTON_DPAD_UP, 1136 SDL_GAMEPAD_BUTTON_START, 1137 0xFF /* Unused */, 1138 0xFF /* Unused */, 1139 0xFF /* Unused */, 1140 }, 1141 { 1142 SDL_GAMEPAD_BUTTON_NORTH, 1143 SDL_GAMEPAD_BUTTON_WEST, 1144 SDL_GAMEPAD_BUTTON_SOUTH, 1145 SDL_GAMEPAD_BUTTON_EAST, 1146 SDL_GAMEPAD_BUTTON_BACK, 1147 0xFF /* Unused */, 1148 0xFF /* Unused */, 1149 SDL_GAMEPAD_BUTTON_GUIDE, 1150 } 1151 }; 1152 if (data->hasBaseButtons) { 1153 PostPackedButtonData(ctx->timestamp, joystick, buttons, data->rgucBaseButtons, 2, true, false); 1154 } 1155} 1156 1157static void HandleNunchuckButtonData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, const WiiButtonData *data) 1158{ 1159 bool c_button, z_button; 1160 1161 if (data->ucNExtensionBytes < 6) { 1162 return; 1163 } 1164 1165 if (ctx->m_ucMotionPlusMode == WII_MOTIONPLUS_MODE_NUNCHUK) { 1166 c_button = (data->rgucExtension[5] & 0x08) ? false : true; 1167 z_button = (data->rgucExtension[5] & 0x04) ? false : true; 1168 } else { 1169 c_button = (data->rgucExtension[5] & 0x02) ? false : true; 1170 z_button = (data->rgucExtension[5] & 0x01) ? false : true; 1171 } 1172 SDL_SendJoystickButton(ctx->timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, c_button); 1173 SDL_SendJoystickAxis(ctx->timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, z_button ? SDL_JOYSTICK_AXIS_MAX : SDL_JOYSTICK_AXIS_MIN); 1174 PostStickCalibrated(ctx->timestamp, joystick, &ctx->m_StickCalibrationData[0], SDL_GAMEPAD_AXIS_LEFTX, data->rgucExtension[0]); 1175 PostStickCalibrated(ctx->timestamp, joystick, &ctx->m_StickCalibrationData[1], SDL_GAMEPAD_AXIS_LEFTY, data->rgucExtension[1]); 1176 1177 if (ctx->m_bReportSensors) { 1178 const float ACCEL_RES_PER_G = 200.0f; 1179 Sint16 x, y, z; 1180 float values[3]; 1181 1182 x = (data->rgucExtension[2] << 2); 1183 y = (data->rgucExtension[3] << 2); 1184 z = (data->rgucExtension[4] << 2); 1185 1186 if (ctx->m_ucMotionPlusMode == WII_MOTIONPLUS_MODE_NUNCHUK) { 1187 x |= ((data->rgucExtension[5] >> 3) & 0x02); 1188 y |= ((data->rgucExtension[5] >> 4) & 0x02); 1189 z &= ~0x04; 1190 z |= ((data->rgucExtension[5] >> 5) & 0x06); 1191 } else { 1192 x |= ((data->rgucExtension[5] >> 2) & 0x03); 1193 y |= ((data->rgucExtension[5] >> 4) & 0x03); 1194 z |= ((data->rgucExtension[5] >> 6) & 0x03); 1195 } 1196 1197 x -= 0x200; 1198 y -= 0x200; 1199 z -= 0x200; 1200 1201 values[0] = -((float)x / ACCEL_RES_PER_G) * SDL_STANDARD_GRAVITY; 1202 values[1] = ((float)z / ACCEL_RES_PER_G) * SDL_STANDARD_GRAVITY; 1203 values[2] = ((float)y / ACCEL_RES_PER_G) * SDL_STANDARD_GRAVITY; 1204 SDL_SendJoystickSensor(ctx->timestamp, joystick, SDL_SENSOR_ACCEL_L, ctx->timestamp, values, 3); 1205 } 1206} 1207 1208static void HandleMotionPlusData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, const WiiButtonData *data) 1209{ 1210 if (ctx->m_bReportSensors) { 1211 const float GYRO_RES_PER_DEGREE = 8192.0f; 1212 int x, y, z; 1213 float values[3]; 1214 1215 x = (data->rgucExtension[0] | ((data->rgucExtension[3] << 6) & 0xFF00)) - 8192; 1216 y = (data->rgucExtension[1] | ((data->rgucExtension[4] << 6) & 0xFF00)) - 8192; 1217 z = (data->rgucExtension[2] | ((data->rgucExtension[5] << 6) & 0xFF00)) - 8192; 1218 1219 if (data->rgucExtension[3] & 0x02) { 1220 // Slow rotation rate: 8192/440 units per deg/s 1221 x *= 440; 1222 } else { 1223 // Fast rotation rate: 8192/2000 units per deg/s 1224 x *= 2000; 1225 } 1226 if (data->rgucExtension[4] & 0x02) { 1227 // Slow rotation rate: 8192/440 units per deg/s 1228 y *= 440; 1229 } else { 1230 // Fast rotation rate: 8192/2000 units per deg/s 1231 y *= 2000; 1232 } 1233 if (data->rgucExtension[3] & 0x01) { 1234 // Slow rotation rate: 8192/440 units per deg/s 1235 z *= 440; 1236 } else { 1237 // Fast rotation rate: 8192/2000 units per deg/s 1238 z *= 2000; 1239 } 1240 1241 values[0] = -((float)z / GYRO_RES_PER_DEGREE) * SDL_PI_F / 180.0f; 1242 values[1] = ((float)x / GYRO_RES_PER_DEGREE) * SDL_PI_F / 180.0f; 1243 values[2] = ((float)y / GYRO_RES_PER_DEGREE) * SDL_PI_F / 180.0f; 1244 SDL_SendJoystickSensor(ctx->timestamp, joystick, SDL_SENSOR_GYRO, ctx->timestamp, values, 3); 1245 } 1246} 1247 1248static void HandleWiiRemoteAccelData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, const WiiButtonData *data) 1249{ 1250 const float ACCEL_RES_PER_G = 100.0f; 1251 Sint16 x, y, z; 1252 float values[3]; 1253 1254 if (!ctx->m_bReportSensors) { 1255 return; 1256 } 1257 1258 x = ((data->rgucAccelerometer[0] << 2) | ((data->rgucBaseButtons[0] >> 5) & 0x03)) - 0x200; 1259 y = ((data->rgucAccelerometer[1] << 2) | ((data->rgucBaseButtons[1] >> 4) & 0x02)) - 0x200; 1260 z = ((data->rgucAccelerometer[2] << 2) | ((data->rgucBaseButtons[1] >> 5) & 0x02)) - 0x200; 1261 1262 values[0] = -((float)x / ACCEL_RES_PER_G) * SDL_STANDARD_GRAVITY; 1263 values[1] = ((float)z / ACCEL_RES_PER_G) * SDL_STANDARD_GRAVITY; 1264 values[2] = ((float)y / ACCEL_RES_PER_G) * SDL_STANDARD_GRAVITY; 1265 SDL_SendJoystickSensor(ctx->timestamp, joystick, SDL_SENSOR_ACCEL, ctx->timestamp, values, 3); 1266} 1267 1268static void HandleButtonData(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick, WiiButtonData *data) 1269{ 1270 if (ctx->m_eExtensionControllerType == k_eWiiExtensionControllerType_WiiUPro) { 1271 HandleWiiUProButtonData(ctx, joystick, data); 1272 return; 1273 } 1274 1275 if (ctx->m_ucMotionPlusMode != WII_MOTIONPLUS_MODE_NONE && 1276 data->ucNExtensionBytes > 5) { 1277 if (data->rgucExtension[5] & 0x01) { 1278 // The data is invalid, possibly during a hotplug 1279 return; 1280 } 1281 1282 if (data->rgucExtension[4] & 0x01) { 1283 if (ctx->m_eExtensionControllerType == k_eWiiExtensionControllerType_None) { 1284 // Something was plugged into the extension port, reinitialize to get new state 1285 ctx->m_bDisconnected = true; 1286 } 1287 } else { 1288 if (ctx->m_eExtensionControllerType != k_eWiiExtensionControllerType_None) { 1289 // Something was removed from the extension port, reinitialize to get new state 1290 ctx->m_bDisconnected = true; 1291 } 1292 } 1293 1294 if (data->rgucExtension[5] & 0x02) { 1295 HandleMotionPlusData(ctx, joystick, data); 1296 1297 // The extension data is consumed 1298 data->ucNExtensionBytes = 0; 1299 } 1300 } 1301 1302 HandleWiiRemoteButtonData(ctx, joystick, data); 1303 switch (ctx->m_eExtensionControllerType) { 1304 case k_eWiiExtensionControllerType_Nunchuk: 1305 HandleNunchuckButtonData(ctx, joystick, data); 1306 SDL_FALLTHROUGH; 1307 case k_eWiiExtensionControllerType_None: 1308 HandleWiiRemoteButtonDataAsMainController(ctx, joystick, data); 1309 break; 1310 case k_eWiiExtensionControllerType_Gamepad: 1311 HandleGamepadControllerButtonData(ctx, joystick, data); 1312 break; 1313 default: 1314 break; 1315 } 1316 HandleWiiRemoteAccelData(ctx, joystick, data); 1317} 1318 1319static void GetBaseButtons(WiiButtonData *dst, const Uint8 *src) 1320{ 1321 SDL_memcpy(dst->rgucBaseButtons, src, 2); 1322 dst->hasBaseButtons = true; 1323} 1324 1325static void GetAccelerometer(WiiButtonData *dst, const Uint8 *src) 1326{ 1327 SDL_memcpy(dst->rgucAccelerometer, src, 3); 1328 dst->hasAccelerometer = true; 1329} 1330 1331static void GetExtensionData(WiiButtonData *dst, const Uint8 *src, int size) 1332{ 1333 bool valid_data = false; 1334 int i; 1335 1336 if (size > sizeof(dst->rgucExtension)) { 1337 size = sizeof(dst->rgucExtension); 1338 } 1339 1340 for (i = 0; i < size; ++i) { 1341 if (src[i] != 0xFF) { 1342 valid_data = true; 1343 break; 1344 } 1345 } 1346 if (valid_data) { 1347 SDL_memcpy(dst->rgucExtension, src, size); 1348 dst->ucNExtensionBytes = (Uint8)size; 1349 } 1350} 1351 1352static void HandleStatus(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick) 1353{ 1354 bool hadExtension = ctx->m_eExtensionControllerType != k_eWiiExtensionControllerType_None; 1355 bool hasExtension = (ctx->m_rgucReadBuffer[3] & 2) ? true : false; 1356 WiiButtonData data; 1357 SDL_zero(data); 1358 GetBaseButtons(&data, ctx->m_rgucReadBuffer + 1); 1359 HandleButtonData(ctx, joystick, &data); 1360 1361 if (ctx->m_eExtensionControllerType != k_eWiiExtensionControllerType_WiiUPro) { 1362 // Wii U has separate battery level tracking 1363 UpdatePowerLevelWii(joystick, ctx->m_rgucReadBuffer[6]); 1364 } 1365 1366 // The report data format has been reset, need to update it 1367 ResetButtonPacketType(ctx); 1368 1369 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "HIDAPI Wii: Status update, extension %s", hasExtension ? "CONNECTED" : "DISCONNECTED"); 1370 1371 /* When Motion Plus is active, we get extension connect/disconnect status 1372 * through the Motion Plus packets. Otherwise we can use the status here. 1373 */ 1374 if (ctx->m_ucMotionPlusMode != WII_MOTIONPLUS_MODE_NONE) { 1375 /* Check to make sure the Motion Plus extension state hasn't changed, 1376 * otherwise we'll get extension connect/disconnect status through 1377 * Motion Plus packets. 1378 */ 1379 if (NeedsPeriodicMotionPlusCheck(ctx, true)) { 1380 ctx->m_ulNextMotionPlusCheck = SDL_GetTicks(); 1381 } 1382 1383 } else if (hadExtension != hasExtension) { 1384 // Reinitialize to get new state 1385 ctx->m_bDisconnected = true; 1386 } 1387} 1388 1389static void HandleResponse(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick) 1390{ 1391 EWiiInputReportIDs type = (EWiiInputReportIDs)ctx->m_rgucReadBuffer[0]; 1392 WiiButtonData data; 1393 SDL_assert(type == k_eWiiInputReportIDs_Acknowledge || type == k_eWiiInputReportIDs_ReadMemory); 1394 SDL_zero(data); 1395 GetBaseButtons(&data, ctx->m_rgucReadBuffer + 1); 1396 HandleButtonData(ctx, joystick, &data); 1397 1398 switch (ctx->m_eCommState) { 1399 case k_eWiiCommunicationState_None: 1400 break; 1401 1402 case k_eWiiCommunicationState_CheckMotionPlusStage1: 1403 case k_eWiiCommunicationState_CheckMotionPlusStage2: 1404 { 1405 Uint16 extension = 0; 1406 if (ParseExtensionIdentifyResponse(ctx, &extension)) { 1407 if ((extension & WII_EXTENSION_MOTIONPLUS_MASK) == WII_EXTENSION_MOTIONPLUS_ID) { 1408 // Motion Plus is currently active 1409 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "HIDAPI Wii: Motion Plus CONNECTED (stage %d)", ctx->m_eCommState == k_eWiiCommunicationState_CheckMotionPlusStage1 ? 1 : 2); 1410 1411 if (!ctx->m_bMotionPlusPresent) { 1412 // Reinitialize to get new sensor availability 1413 ctx->m_bDisconnected = true; 1414 } 1415 ctx->m_eCommState = k_eWiiCommunicationState_None; 1416 1417 } else if (ctx->m_eCommState == k_eWiiCommunicationState_CheckMotionPlusStage1) { 1418 // Check to see if Motion Plus is present 1419 ReadRegister(ctx, 0xA600FE, 2, false); 1420 1421 ctx->m_eCommState = k_eWiiCommunicationState_CheckMotionPlusStage2; 1422 1423 } else { 1424 // Motion Plus is not present 1425 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "HIDAPI Wii: Motion Plus DISCONNECTED (stage %d)", ctx->m_eCommState == k_eWiiCommunicationState_CheckMotionPlusStage1 ? 1 : 2); 1426 1427 if (ctx->m_bMotionPlusPresent) { 1428 // Reinitialize to get new sensor availability 1429 ctx->m_bDisconnected = true; 1430 } 1431 ctx->m_eCommState = k_eWiiCommunicationState_None; 1432 } 1433 } 1434 } break; 1435 default: 1436 // Should never happen 1437 break; 1438 } 1439} 1440 1441static void HandleButtonPacket(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick) 1442{ 1443 EWiiInputReportIDs eExpectedReport = GetButtonPacketType(ctx); 1444 WiiButtonData data; 1445 1446 // FIXME: This should see if the data format is compatible rather than equal 1447 if (eExpectedReport != ctx->m_rgucReadBuffer[0]) { 1448 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "HIDAPI Wii: Resetting report mode to %d", eExpectedReport); 1449 RequestButtonPacketType(ctx, eExpectedReport); 1450 } 1451 1452 // IR camera data is not supported 1453 SDL_zero(data); 1454 switch (ctx->m_rgucReadBuffer[0]) { 1455 case k_eWiiInputReportIDs_ButtonData0: // 30 BB BB 1456 GetBaseButtons(&data, ctx->m_rgucReadBuffer + 1); 1457 break; 1458 case k_eWiiInputReportIDs_ButtonData1: // 31 BB BB AA AA AA 1459 case k_eWiiInputReportIDs_ButtonData3: // 33 BB BB AA AA AA II II II II II II II II II II II II 1460 GetBaseButtons(&data, ctx->m_rgucReadBuffer + 1); 1461 GetAccelerometer(&data, ctx->m_rgucReadBuffer + 3); 1462 break; 1463 case k_eWiiInputReportIDs_ButtonData2: // 32 BB BB EE EE EE EE EE EE EE EE 1464 GetBaseButtons(&data, ctx->m_rgucReadBuffer + 1); 1465 GetExtensionData(&data, ctx->m_rgucReadBuffer + 3, 8); 1466 break; 1467 case k_eWiiInputReportIDs_ButtonData4: // 34 BB BB EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE 1468 GetBaseButtons(&data, ctx->m_rgucReadBuffer + 1); 1469 GetExtensionData(&data, ctx->m_rgucReadBuffer + 3, 19); 1470 break; 1471 case k_eWiiInputReportIDs_ButtonData5: // 35 BB BB AA AA AA EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE 1472 GetBaseButtons(&data, ctx->m_rgucReadBuffer + 1); 1473 GetAccelerometer(&data, ctx->m_rgucReadBuffer + 3); 1474 GetExtensionData(&data, ctx->m_rgucReadBuffer + 6, 16); 1475 break; 1476 case k_eWiiInputReportIDs_ButtonData6: // 36 BB BB II II II II II II II II II II EE EE EE EE EE EE EE EE EE 1477 GetBaseButtons(&data, ctx->m_rgucReadBuffer + 1); 1478 GetExtensionData(&data, ctx->m_rgucReadBuffer + 13, 9); 1479 break; 1480 case k_eWiiInputReportIDs_ButtonData7: // 37 BB BB AA AA AA II II II II II II II II II II EE EE EE EE EE EE 1481 GetBaseButtons(&data, ctx->m_rgucReadBuffer + 1); 1482 GetExtensionData(&data, ctx->m_rgucReadBuffer + 16, 6); 1483 break; 1484 case k_eWiiInputReportIDs_ButtonDataD: // 3d EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE 1485 GetExtensionData(&data, ctx->m_rgucReadBuffer + 1, 21); 1486 break; 1487 case k_eWiiInputReportIDs_ButtonDataE: 1488 case k_eWiiInputReportIDs_ButtonDataF: 1489 default: 1490 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "HIDAPI Wii: Unsupported button data type %02x", ctx->m_rgucReadBuffer[0]); 1491 return; 1492 } 1493 HandleButtonData(ctx, joystick, &data); 1494} 1495 1496static void HandleInput(SDL_DriverWii_Context *ctx, SDL_Joystick *joystick) 1497{ 1498 EWiiInputReportIDs type = (EWiiInputReportIDs)ctx->m_rgucReadBuffer[0]; 1499 1500 // Set up for handling input 1501 ctx->timestamp = SDL_GetTicksNS(); 1502 1503 if (type == k_eWiiInputReportIDs_Status) { 1504 HandleStatus(ctx, joystick); 1505 } else if (type == k_eWiiInputReportIDs_Acknowledge || type == k_eWiiInputReportIDs_ReadMemory) { 1506 HandleResponse(ctx, joystick); 1507 } else if (type >= k_eWiiInputReportIDs_ButtonData0 && type <= k_eWiiInputReportIDs_ButtonDataF) { 1508 HandleButtonPacket(ctx, joystick); 1509 } else { 1510 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "HIDAPI Wii: Unexpected input packet of type %x", type); 1511 } 1512} 1513 1514static bool HIDAPI_DriverWii_UpdateDevice(SDL_HIDAPI_Device *device) 1515{ 1516 SDL_DriverWii_Context *ctx = (SDL_DriverWii_Context *)device->context; 1517 SDL_Joystick *joystick = NULL; 1518 int size; 1519 Uint64 now; 1520 1521 if (device->num_joysticks > 0) { 1522 joystick = SDL_GetJoystickFromID(device->joysticks[0]); 1523 } else { 1524 return false; 1525 } 1526 1527 now = SDL_GetTicks(); 1528 1529 while ((size = ReadInput(ctx)) > 0) { 1530 if (joystick) { 1531 HandleInput(ctx, joystick); 1532 } 1533 ctx->m_ulLastInput = now; 1534 } 1535 1536 /* Check to see if we've lost connection to the controller. 1537 * We have continuous reporting enabled, so this should be reliable now. 1538 */ 1539 { 1540 SDL_COMPILE_TIME_ASSERT(ENABLE_CONTINUOUS_REPORTING, ENABLE_CONTINUOUS_REPORTING); 1541 } 1542 if (now >= (ctx->m_ulLastInput + INPUT_WAIT_TIMEOUT_MS)) { 1543 // Bluetooth may have disconnected, try reopening the controller 1544 size = -1; 1545 } 1546 1547 if (joystick) { 1548 // These checks aren't needed on the Wii U Pro Controller 1549 if (ctx->m_eExtensionControllerType != k_eWiiExtensionControllerType_WiiUPro) { 1550 1551 // Check to see if the Motion Plus extension status has changed 1552 if (ctx->m_ulNextMotionPlusCheck && now >= ctx->m_ulNextMotionPlusCheck) { 1553 CheckMotionPlusConnection(ctx); 1554 if (NeedsPeriodicMotionPlusCheck(ctx, false)) { 1555 SchedulePeriodicMotionPlusCheck(ctx); 1556 } else { 1557 ctx->m_ulNextMotionPlusCheck = 0; 1558 } 1559 } 1560 1561 // Request a status update periodically to make sure our battery value is up to date 1562 if (!ctx->m_ulLastStatus || now >= (ctx->m_ulLastStatus + STATUS_UPDATE_TIME_MS)) { 1563 Uint8 data[2]; 1564 1565 data[0] = k_eWiiOutputReportIDs_StatusRequest; 1566 data[1] = (Uint8)ctx->m_bRumbleActive; 1567 WriteOutput(ctx, data, sizeof(data), false); 1568 1569 ctx->m_ulLastStatus = now; 1570 } 1571 } 1572 } 1573 1574 if (size < 0 || ctx->m_bDisconnected) { 1575 // Read error, device is disconnected 1576 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 1577 } 1578 return (size >= 0); 1579} 1580 1581static void HIDAPI_DriverWii_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 1582{ 1583 SDL_DriverWii_Context *ctx = (SDL_DriverWii_Context *)device->context; 1584 1585 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_WII_PLAYER_LED, 1586 SDL_PlayerLEDHintChanged, ctx); 1587 1588 ctx->joystick = NULL; 1589} 1590 1591static void HIDAPI_DriverWii_FreeDevice(SDL_HIDAPI_Device *device) 1592{ 1593} 1594 1595SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverWii = { 1596 SDL_HINT_JOYSTICK_HIDAPI_WII, 1597 true, 1598 HIDAPI_DriverWii_RegisterHints, 1599 HIDAPI_DriverWii_UnregisterHints, 1600 HIDAPI_DriverWii_IsEnabled, 1601 HIDAPI_DriverWii_IsSupportedDevice, 1602 HIDAPI_DriverWii_InitDevice, 1603 HIDAPI_DriverWii_GetDevicePlayerIndex, 1604 HIDAPI_DriverWii_SetDevicePlayerIndex, 1605 HIDAPI_DriverWii_UpdateDevice, 1606 HIDAPI_DriverWii_OpenJoystick, 1607 HIDAPI_DriverWii_RumbleJoystick, 1608 HIDAPI_DriverWii_RumbleJoystickTriggers, 1609 HIDAPI_DriverWii_GetJoystickCapabilities, 1610 HIDAPI_DriverWii_SetJoystickLED, 1611 HIDAPI_DriverWii_SendJoystickEffect, 1612 HIDAPI_DriverWii_SetJoystickSensorsEnabled, 1613 HIDAPI_DriverWii_CloseJoystick, 1614 HIDAPI_DriverWii_FreeDevice, 1615}; 1616 1617#endif // SDL_JOYSTICK_HIDAPI_WII 1618 1619#endif // SDL_JOYSTICK_HIDAPI 1620
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.