From 5688b8b5b61e7587cfcb39e0c2ff8fd4a28f3d1e Mon Sep 17 00:00:00 2001 From: patchzyy <64382339+patchzyy@users.noreply.github.com> Date: Sun, 18 Oct 2026 12:00:08 +0200 Subject: [PATCH] Support Linux Wii controller mappings --- aurora-main/lib/dolphin/pad/pad.cpp | 9 +++-- runtime/src/settings_overlay.cpp | 6 ++++ runtime/src/wii_remote_input.cpp | 56 +++++++++++++++++++++++++---- 3 files changed, 62 insertions(+), 9 deletions(-) diff --git a/aurora-main/lib/dolphin/pad/pad.cpp b/aurora-main/lib/dolphin/pad/pad.cpp index 7ae292f..acdbe2a 100644 --- a/aurora-main/lib/dolphin/pad/pad.cpp +++ b/aurora-main/lib/dolphin/pad/pad.cpp @@ -195,7 +195,7 @@ std::array g_defaultButtonsJoyPair{{ }}; // Wii U Pro Controllers through SDL's HIDAPI Wii driver. No SDL_GamepadType -// singles them out, so they are picked by the name the driver gives them (see +// singles them out, so they are picked by name or USB id (see // __PADSetDefaultMapping). Wii Remotes, with or without a Nunchuk or Classic // Controller, are read by the game through KPAD instead and never get a // GameCube mapping (the runtime hides those ports from PADRead). @@ -451,11 +451,14 @@ static void reset_alt_button_mapping(aurora::input::GameController* controller) // SDL's hidapi Wii driver names the pad "Nintendo Wii U Pro Controller"; the // other names it produces are Wii Remotes, which the game reads through KPAD -// and which therefore never take a GameCube mapping. +// and which therefore never take a GameCube mapping. The Linux kernel driver's +// "Nintendo Wii Remote Pro Controller" is only recognised by its USB id. static bool wii_default_mapping(const aurora::input::GameController* controller, std::array& out) { const char* name = SDL_GetGamepadName(controller->m_controller); - if (name == nullptr || SDL_strstr(name, "Wii U Pro Controller") == nullptr) { + const bool wiiUProId = SDL_GetGamepadVendor(controller->m_controller) == 0x057e && + SDL_GetGamepadProduct(controller->m_controller) == 0x0330; + if (!wiiUProId && (name == nullptr || SDL_strstr(name, "Wii U Pro Controller") == nullptr)) { return false; } out = g_defaultButtonsWiiUPro; diff --git a/runtime/src/settings_overlay.cpp b/runtime/src/settings_overlay.cpp index 78c32bf..0907883 100644 --- a/runtime/src/settings_overlay.cpp +++ b/runtime/src/settings_overlay.cpp @@ -790,6 +790,12 @@ void DrawControllerSettings() { ImGui::EndMenu(); } + if (WiiRemoteInput::IsRemoteChannel(selectedGamePort)) { + ImGui::TextDisabled("The game reads this controller as a Wii Remote, so the button mapping does not apply."); + DrawRumbleSettings(); + return; + } + uint32_t mappingCount = 0; PADButtonMapping* mappings = PADGetButtonMappings(static_cast(g_controllerPort), &mappingCount); if (mappings == nullptr || mappingCount != PAD_BUTTON_COUNT) { diff --git a/runtime/src/wii_remote_input.cpp b/runtime/src/wii_remote_input.cpp index 8cd9753..665aa25 100644 --- a/runtime/src/wii_remote_input.cpp +++ b/runtime/src/wii_remote_input.cpp @@ -63,6 +63,8 @@ constexpr uint32_t kClUp = 0x0001, kClLeft = 0x0002, kClZR = 0x0004, kClX = 0x00 kClB = 0x0040, kClZL = 0x0080, kClR = 0x0200, kClPlus = 0x0400, kClHome = 0x0800, kClMinus = 0x1000, kClL = 0x2000, kClDown = 0x4000, kClRight = 0x8000; +constexpr Uint16 kNintendoVendor = 0x057e; + // How long a vanished remote keeps its channel alive with neutral input. SDL's // in-place reconnect after an extension change takes well under a second; a // remote that is really gone shows up as disconnected after this. @@ -86,6 +88,7 @@ bool g_scanning = false; uint64_t g_driverOffSinceMs = 0; // When the current scan started (last Wii controller seen). uint64_t g_lostAtMs = 0; +bool g_hidrawWarned = false; // Instance ids whose accelerometers have been switched on. SDL keeps sensors // off until asked and forgets that when the gamepad is closed, so a re-paired @@ -346,6 +349,40 @@ bool AnyWiiControllerConnected() { return false; } +// True for an evdev node served by the kernel hid-wiimote driver. +bool IsKernelWiiNode(SDL_JoystickID id) { + const char* path = SDL_GetGamepadPathForID(id); + return SDL_GetGamepadVendorForID(id) == kNintendoVendor && path != nullptr && + std::strncmp(path, "/dev/input/", 11) == 0; +} + +// Logs once when the kernel driver serves a Wii controller SDL's driver could have had. +void WarnIfKernelDriverServing() { + if (g_hidrawWarned || !g_wiiDriverEnabled) { + return; + } + const uint64_t now = SDL_GetTicks(); + static uint64_t s_nextCheckMs = 0; + if (now < s_nextCheckMs) { + return; + } + s_nextCheckMs = now + 2000; + int count = 0; + SDL_JoystickID* ids = SDL_GetGamepads(&count); + if (ids == nullptr) { + return; + } + for (int i = 0; i < count && !g_hidrawWarned; ++i) { + const Uint16 product = SDL_GetGamepadProductForID(ids[i]); + if ((product == 0x0306 || product == 0x0330) && IsKernelWiiNode(ids[i])) { + g_hidrawWarned = true; + RT_LOG(RT_TAG_CONFIG) << "A Wii controller is served by the kernel hid-wiimote driver. For full Wii " + << "Remote support see the Linux udev rule in the README." << std::endl; + } + } + SDL_free(ids); +} + // Route SDL's input diagnostics (HIDAPI open failures, the Wii driver's // extension/status messages) into console.log, minus the periodic chatter. // A sub-warning message is written once: SDL repeats the same line on every @@ -464,6 +501,7 @@ void Poll() { for (uint32_t port = 0; port < PAD_MAX_CONTROLLERS; ++port) { (void)EffectiveKind(port); } + WarnIfKernelDriverServing(); if (!g_wiiDriverEnabled) { return; } @@ -518,11 +556,13 @@ uint32_t ScanCount() { return g_scanCount; } -// Maps the gamepad name SDL's Wii driver reports to a Kind. +// Maps the gamepad name SDL reports to a Kind: "Nintendo Wii Remote", +// "... with Nunchuk", "... with Classic Controller" and "Nintendo Wii U Pro +// Controller" from SDL's Wii driver; the Linux kernel's "Nintendo Wii Remote +// Classic Controller" is read like the Classic Controller. Kind KindForName(const char* name) { - // Names come from SDL's hidapi Wii driver: "Nintendo Wii Remote", - // "Nintendo Wii Remote with Nunchuk", "Nintendo Wii Remote with Classic - // Controller" and "Nintendo Wii U Pro Controller". + // The kernel's Pro Controller is a plain gamepad with a built-in SDL mapping. + if (NameContains(name, "Wii Remote Pro Controller")) return Kind::NotWii; if (NameContains(name, "Wii U Pro Controller")) return Kind::WiiUPro; if (!NameContains(name, "Wii Remote")) return Kind::NotWii; if (NameContains(name, "Nunchuk")) return Kind::RemoteWithNunchuk; @@ -674,8 +714,12 @@ bool ReadKpadSample(uint32_t chan, KpadSample& sample) { // Home, the L/R clicks as shoulders, the D-pad as buttons 11-14 (never // through SDL's gamepad mapping, which expects a hat), ZL/ZR as the // trigger axes. - const auto cl = [&](int index, uint32_t bit) { - if (SDL_GetJoystickButton(joystick, index)) sample.clHold |= bit; + // The kernel's evdev node has a regular SDL mapping, so it is read as a gamepad. + const bool mapped = IsKernelWiiNode(SDL_GetGamepadID(gamepad)); + const auto cl = [&](SDL_GamepadButton button, uint32_t bit) { + if (mapped ? SDL_GetGamepadButton(gamepad, button) : SDL_GetJoystickButton(joystick, button)) { + sample.clHold |= bit; + } }; cl(SDL_GAMEPAD_BUTTON_EAST, kClA); cl(SDL_GAMEPAD_BUTTON_SOUTH, kClB);