From 23a91a37be19b596e9bbd03e513309b84a62c67a Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 26 Apr 2026 21:01:32 -0600 Subject: [PATCH] Kinda crappy initial controller support --- extern/aurora | 2 +- src/dusk/imgui/ImGuiConsole.cpp | 33 +++++++++- src/dusk/imgui/ImGuiConsole.hpp | 1 + src/dusk/imgui/ImGuiMenuGame.cpp | 100 ++++++++++++++++++++++++++----- src/dusk/imgui/ImGuiMenuGame.hpp | 3 + 5 files changed, 120 insertions(+), 19 deletions(-) diff --git a/extern/aurora b/extern/aurora index a6a3d3a65a..7784b6fc95 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit a6a3d3a65ae0de6de8b60629cf47fd0f446c21cb +Subproject commit 7784b6fc95568551499c87bd093b78d86e194eba diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index 6bca83482e..a94c7d1f5b 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -56,6 +56,21 @@ ImGuiWindow* FindDragScrollWindow(ImGuiWindow* window) { } return nullptr; } + +void FocusLastMenuBarItem() { + ImGuiContext& g = *ImGui::GetCurrentContext(); + ImGuiWindow* window = ImGui::GetCurrentWindow(); + const ImGuiID itemId = g.LastItemData.ID; + if (window == nullptr || itemId == 0) { + return; + } + + ImGui::FocusWindow(window); + ImGui::SetNavID(itemId, ImGuiNavLayer_Menu, g.CurrentFocusScopeId, + ImGui::WindowRectAbsToRel(window, g.LastItemData.NavRect)); + ImGui::SetNavCursorVisibleAfterMove(); + g.NavHighlightItemUnderNav = true; +} } // namespace namespace dusk { @@ -329,7 +344,17 @@ namespace dusk { } m_isHidden = !getSettings().backend.duskMenuOpen; - bool showMenu = !dusk::IsGameLaunched || !CheckMenuViewToggle(ImGuiKey_F1, m_isHidden); + if (dusk::IsGameLaunched) { + if (ImGui::IsKeyPressed(ImGuiKey_F1)) { + m_isHidden = !m_isHidden; + } + if (ImGui::IsKeyPressed(ImGuiKey_GamepadBack)) { + m_isHidden = !m_isHidden; + m_focusMenuBar = !m_isHidden; + } + } + + bool showMenu = !dusk::IsGameLaunched || !m_isHidden; if (dusk::IsGameLaunched) { const bool menuOpen = !m_isHidden; if (getSettings().backend.duskMenuOpen != menuOpen) { @@ -343,6 +368,10 @@ namespace dusk { ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); if (showMenu && ImGui::BeginMainMenuBar()) { m_menuGame.draw(); + if (m_focusMenuBar) { + FocusLastMenuBarItem(); + m_focusMenuBar = false; + } m_menuTools.draw(); const auto fpsLabel = @@ -367,7 +396,7 @@ namespace dusk { if (dusk::IsGameLaunched && !m_isLaunchInitialized) { m_toasts.emplace_back(ImGui::GetIO().MouseSource == ImGuiMouseSource_TouchScreen ? "Tap to toggle menu"s : - "Press F1 to toggle menu"s, + "Press F1 or Minus/Back to toggle menu"s, 2.5f); m_isLaunchInitialized = true; if (getSettings().game.liveSplitEnabled) { diff --git a/src/dusk/imgui/ImGuiConsole.hpp b/src/dusk/imgui/ImGuiConsole.hpp index a6c8b48fc7..fc7b4c51bc 100644 --- a/src/dusk/imgui/ImGuiConsole.hpp +++ b/src/dusk/imgui/ImGuiConsole.hpp @@ -41,6 +41,7 @@ private: float mouseHideTimer = 0.0f; bool m_isHidden = true; + bool m_focusMenuBar = false; bool m_isLaunchInitialized = false; bool m_touchTapActive = false; bool m_touchTapMoved = false; diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index d0da643b0c..251fabd227 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -23,6 +23,15 @@ namespace { constexpr int kInternalResolutionScaleMax = 12; + +bool is_controller_neutral(int port) { + if (port < 0) { + return true; + } + + return PADGetNativeButtonPressed(port) == -1 && + PADGetNativeAxisPulled(port).nativeAxis == -1; +} } // namespace namespace aurora::gx { @@ -196,7 +205,7 @@ namespace dusk { ImGui::SetTooltip("Restores patched glitches from Wii USA 1.0,\n" "the first released version."); } - + config::ImGuiCheckbox("Enable Rotating Link Doll", getSettings().game.enableLinkDollRotation); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Enables rotating Link in the collection menu with the C-Stick"); @@ -642,39 +651,90 @@ namespace dusk { void ImGuiMenuGame::windowControllerConfig() { if (!m_showControllerConfig) { + if (m_controllerConfig.m_isReading || + m_controllerConfig.m_suppressRemapActivationUntilRelease) + { + m_controllerConfig.m_isReading = false; + m_controllerConfig.m_pendingButtonMapping = nullptr; + m_controllerConfig.m_pendingAxisMapping = nullptr; + m_controllerConfig.m_pendingPort = -1; + m_controllerConfig.m_waitForInputRelease = false; + m_controllerConfig.m_suppressRemapActivationUntilRelease = false; + m_controllerConfig.m_suppressRemapActivationPort = -1; + PADBlockInput(false); + } return; } + bool suppressRemapActivationThisFrame = m_controllerConfig.m_suppressRemapActivationUntilRelease; + if (m_controllerConfig.m_suppressRemapActivationUntilRelease && + is_controller_neutral(m_controllerConfig.m_suppressRemapActivationPort)) + { + m_controllerConfig.m_suppressRemapActivationUntilRelease = false; + m_controllerConfig.m_suppressRemapActivationPort = -1; + PADBlockInput(false); + } + + if ((m_controllerConfig.m_pendingButtonMapping != nullptr || + m_controllerConfig.m_pendingAxisMapping != nullptr) && + m_controllerConfig.m_waitForInputRelease) + { + m_controllerConfig.m_waitForInputRelease = + !is_controller_neutral(m_controllerConfig.m_pendingPort); + } + // if pending for a button mapping, check to set new input - if (m_controllerConfig.m_pendingButtonMapping != nullptr) { + if (m_controllerConfig.m_pendingButtonMapping != nullptr && + !m_controllerConfig.m_waitForInputRelease) + { s32 nativeButton = PADGetNativeButtonPressed(m_controllerConfig.m_pendingPort); if (nativeButton != -1) { + const int suppressPort = m_controllerConfig.m_pendingPort; m_controllerConfig.m_pendingButtonMapping->nativeButton = nativeButton; m_controllerConfig.m_pendingButtonMapping = nullptr; m_controllerConfig.m_pendingPort = -1; - PADBlockInput(false); + m_controllerConfig.m_isReading = false; + m_controllerConfig.m_waitForInputRelease = false; + m_controllerConfig.m_suppressRemapActivationUntilRelease = true; + m_controllerConfig.m_suppressRemapActivationPort = suppressPort; + suppressRemapActivationThisFrame = true; + PADBlockInput(true); PADSerializeMappings(); } } // if pending for an axis mapping, check to set new input - if (m_controllerConfig.m_pendingAxisMapping != nullptr) { + if (m_controllerConfig.m_pendingAxisMapping != nullptr && + !m_controllerConfig.m_waitForInputRelease) + { auto nativeAxis = PADGetNativeAxisPulled(m_controllerConfig.m_pendingPort); if (nativeAxis.nativeAxis != -1) { + const int suppressPort = m_controllerConfig.m_pendingPort; m_controllerConfig.m_pendingAxisMapping->nativeAxis = nativeAxis; m_controllerConfig.m_pendingAxisMapping->nativeButton = -1; m_controllerConfig.m_pendingAxisMapping = nullptr; m_controllerConfig.m_pendingPort = -1; - PADBlockInput(false); + m_controllerConfig.m_isReading = false; + m_controllerConfig.m_waitForInputRelease = false; + m_controllerConfig.m_suppressRemapActivationUntilRelease = true; + m_controllerConfig.m_suppressRemapActivationPort = suppressPort; + suppressRemapActivationThisFrame = true; + PADBlockInput(true); PADSerializeMappings(); } else { auto nativeButton = PADGetNativeButtonPressed(m_controllerConfig.m_pendingPort); if (nativeButton != -1) { + const int suppressPort = m_controllerConfig.m_pendingPort; m_controllerConfig.m_pendingAxisMapping->nativeAxis = {-1, AXIS_SIGN_POSITIVE}; m_controllerConfig.m_pendingAxisMapping->nativeButton = nativeButton; m_controllerConfig.m_pendingAxisMapping = nullptr; m_controllerConfig.m_pendingPort = -1; - PADBlockInput(false); + m_controllerConfig.m_isReading = false; + m_controllerConfig.m_waitForInputRelease = false; + m_controllerConfig.m_suppressRemapActivationUntilRelease = true; + m_controllerConfig.m_suppressRemapActivationPort = suppressPort; + suppressRemapActivationThisFrame = true; + PADBlockInput(true); PADSerializeMappings(); } } @@ -710,6 +770,10 @@ namespace dusk { m_controllerConfig.m_pendingButtonMapping = nullptr; m_controllerConfig.m_pendingAxisMapping = nullptr; m_controllerConfig.m_pendingPort = -1; + m_controllerConfig.m_waitForInputRelease = false; + m_controllerConfig.m_isReading = false; + m_controllerConfig.m_suppressRemapActivationUntilRelease = false; + m_controllerConfig.m_suppressRemapActivationPort = -1; PADBlockInput(false); } @@ -786,7 +850,7 @@ namespace dusk { std::string dispName; if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingButtonMapping == &btnMappingList[i]) { - dispName = fmt::format("Press a Key...##{}", btnName); + dispName = fmt::format("{}##{}", m_controllerConfig.m_waitForInputRelease ? "Release..." : "Press a Key...", btnName); } else { const char* nativeName = GetNameForGamepadButton(gamepad, btnMappingList[i].nativeButton); if (nativeName == nullptr) { @@ -797,10 +861,11 @@ namespace dusk { bool pressed = ImGui::Button(dispName.c_str(), btnSize); - if (pressed) { + if (pressed && !m_controllerConfig.m_isReading && !suppressRemapActivationThisFrame) { m_controllerConfig.m_isReading = true; m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort; m_controllerConfig.m_pendingButtonMapping = &btnMappingList[i]; + m_controllerConfig.m_waitForInputRelease = true; PADBlockInput(true); } } @@ -830,17 +895,18 @@ namespace dusk { std::string dispName; if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingAxisMapping == &axisMappingList[trigger]) { - dispName = fmt::format("Press a Key...##{}", axisName); + dispName = fmt::format("{}##{}", m_controllerConfig.m_waitForInputRelease ? "Release..." : "Press a Key...", axisName); } else { dispName = fmt::format("{0}##-{1}", PADGetNativeAxisName(axisMappingList[trigger].nativeAxis), trigger); } bool pressed = ImGui::Button(dispName.c_str(), btnSize); - if (pressed) { + if (pressed && !m_controllerConfig.m_isReading && !suppressRemapActivationThisFrame) { m_controllerConfig.m_isReading = true; m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort; m_controllerConfig.m_pendingAxisMapping = &axisMappingList[trigger]; + m_controllerConfig.m_waitForInputRelease = true; PADBlockInput(true); } } @@ -897,7 +963,7 @@ namespace dusk { std::string dispName; if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingAxisMapping == &axisMappingList[axis]) { - dispName = fmt::format("Press a Key...##{}", label); + dispName = fmt::format("{}##{}", m_controllerConfig.m_waitForInputRelease ? "Release..." : "Press a Key...", label); } else { if (axisMappingList[axis].nativeAxis.nativeAxis != -1) { const char* signStr; @@ -916,10 +982,11 @@ namespace dusk { } bool pressed = ImGui::Button(dispName.c_str(), btnSize); - if (pressed) { + if (pressed && !m_controllerConfig.m_isReading && !suppressRemapActivationThisFrame) { m_controllerConfig.m_isReading = true; m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort; m_controllerConfig.m_pendingAxisMapping = &axisMappingList[axis]; + m_controllerConfig.m_waitForInputRelease = true; PADBlockInput(true); } } @@ -960,7 +1027,7 @@ namespace dusk { std::string dispName; if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingAxisMapping == &axisMappingList[axis]) { - dispName = fmt::format("Press a Key...##sub{}", label); + dispName = fmt::format("{}##sub{}", m_controllerConfig.m_waitForInputRelease ? "Release..." : "Press a Key...", label); } else { if (axisMappingList[axis].nativeAxis.nativeAxis != -1) { const char* signStr; @@ -979,10 +1046,11 @@ namespace dusk { } bool pressed = ImGui::Button(fmt::format("{0}##sub{1}", dispName, label).c_str(), btnSize); - if (pressed) { + if (pressed && !m_controllerConfig.m_isReading && !suppressRemapActivationThisFrame) { m_controllerConfig.m_isReading = true; m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort; m_controllerConfig.m_pendingAxisMapping = &axisMappingList[axis]; + m_controllerConfig.m_waitForInputRelease = true; PADBlockInput(true); } } @@ -1013,7 +1081,7 @@ namespace dusk { PADSerializeMappings(); } } - + if (PADSupportsRumbleIntensity(m_controllerConfig.m_selectedPort)) { ImGuiBeginGroupPanel("Rumble Intensity", ImVec2(150 * scale, -1)); u16 low; @@ -1032,7 +1100,7 @@ namespace dusk { if (ImGui::Button(fmt::format("{0}...##rumbleTest", m_controllerConfig.m_isRumbling ? "Stop": "Test").c_str(), {-1, 0})) { PADControlMotor(m_controllerConfig.m_selectedPort, !m_controllerConfig.m_isRumbling ? PAD_MOTOR_RUMBLE : PAD_MOTOR_STOP_HARD); m_controllerConfig.m_isRumbling ^= 1; - } + } ImGuiEndGroupPanel(); } ImGuiEndGroupPanel(); diff --git a/src/dusk/imgui/ImGuiMenuGame.hpp b/src/dusk/imgui/ImGuiMenuGame.hpp index e54541a010..c902c92359 100644 --- a/src/dusk/imgui/ImGuiMenuGame.hpp +++ b/src/dusk/imgui/ImGuiMenuGame.hpp @@ -68,6 +68,9 @@ namespace dusk { PADButtonMapping* m_pendingButtonMapping = nullptr; PADAxisMapping* m_pendingAxisMapping = nullptr; int m_pendingPort = -1; + bool m_waitForInputRelease = false; + bool m_suppressRemapActivationUntilRelease = false; + int m_suppressRemapActivationPort = -1; bool m_isRumbling = false; } m_controllerConfig;