From ba215b24296da27dc4f9194edb16ba91be426d2b Mon Sep 17 00:00:00 2001 From: Dylan Deverill Date: Wed, 5 Aug 2026 18:13:14 -0700 Subject: [PATCH] Fix custom back button mappings being ignored in mouse click mode "Back Buttons as Mouse Click" hijacked L4 and R4 unconditionally: the virtual controller skipped their bindings entirely, so a paddle mapped to RT silently did nothing while the option was on. Only the upper paddles were affected, so the symptom read as "two of my four paddles are dead" rather than pointing at the toggle. The same gate also suppressed the Left/Right Click bindings the remap window already offered, meaning the feature disabled the very capability it existed to provide. The toggle was a strict subset of the remap window, which already offers left and right click per paddle, so retire it rather than teach it to cooperate: - Remove the backMouseEnabled gates, the tray item, and the duplicate L4/R4 handling in TrackpadMouse. Paddle mouse output now flows through the single edge-detection path in ReadLoop. - Default L4 and R4 to left click so a fresh install still clicks out of the box. - Add an explicit Off option to the picker. None was already a real "do nothing", but Reset now yields left click, so without this there would be no way to disable a paddle at all. - Migrate BackButtons=1 into real bindings, filling in only paddles with no binding of their own so an explicit mapping is never overwritten, then delete the value so it runs once. TrackpadMouse::Reset used to release a paddle held while game mode was torn down. Removing its L4/R4 handling would have stranded the mouse button down, so that cleanup moves to ReleasePaddleMouseButtons, which also covers right click and runs on the enable, disable, and destructor paths. Co-Authored-By: Claude Opus 5 --- README.md | 2 +- src/app/BackButtonConfig.h | 9 +++++---- src/app/ControllerManager.cpp | 38 ++++++++++++++++++++++++++--------- src/app/ControllerManager.h | 4 +--- src/app/RemapWindow.cpp | 19 +++++++++--------- src/app/TrackpadMouse.cpp | 21 ++----------------- src/app/TrackpadMouse.h | 5 ----- src/app/TrayApp.cpp | 35 +++++++++++++++++++++++--------- src/app/TrayApp.h | 1 - src/app/VirtualController.cpp | 10 ++++----- src/app/VirtualController.h | 2 +- 11 files changed, 78 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 895bf53..da7b175 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ When **Steamless Mode** is active, the app disables the controller's built-in ke - System tray icon shows connection and mode status - **Steamless Mode** — disables lizard mode and exposes controller as Xbox 360 gamepad - **Trackpad Mouse** — use the right (or left) trackpad as a mouse cursor -- **Back Buttons for Clicking** — map R4/R5 (or L4/L5) to left/right mouse click +- **Back Buttons for Clicking** — the upper paddles (L4/R4) left-click by default; rebind any paddle to a gamepad button, right click, or Off - **Use Left Trackpad Instead** — mirror all trackpad/back-button functionality to the left side for left-handed users - **Start with Windows** — launch automatically at login - Settings persist across restarts diff --git a/src/app/BackButtonConfig.h b/src/app/BackButtonConfig.h index 7459a79..b3f9297 100644 --- a/src/app/BackButtonConfig.h +++ b/src/app/BackButtonConfig.h @@ -12,7 +12,7 @@ enum class BackButtonAction : uint8_t { LB, RB, LT, RT, // Row 3 — d-pad DPadUp, DPadDown, DPadLeft, DPadRight, - // Row 4 — stick clicks, menu buttons, mouse buttons, and "none" (natural behavior) + // Row 4 — stick clicks, menu buttons, mouse buttons, and "none" (paddle does nothing) LeftMouseButton, RightMouseButton, None, Menu, View, @@ -21,10 +21,11 @@ enum class BackButtonAction : uint8_t { }; struct BackButtonConfig { - // Default: each back paddle uses its natural behavior (mouse click when that mode is on). - BackButtonAction l4 = BackButtonAction::None; + // Default: the upper paddles left-click the mouse (this used to be the + // "Back Buttons as Mouse Click" tray toggle), the lower paddles are unbound. + BackButtonAction l4 = BackButtonAction::LeftMouseButton; BackButtonAction l5 = BackButtonAction::None; - BackButtonAction r4 = BackButtonAction::None; + BackButtonAction r4 = BackButtonAction::LeftMouseButton; BackButtonAction r5 = BackButtonAction::None; }; diff --git a/src/app/ControllerManager.cpp b/src/app/ControllerManager.cpp index 057d8a1..3ea6516 100644 --- a/src/app/ControllerManager.cpp +++ b/src/app/ControllerManager.cpp @@ -34,6 +34,11 @@ struct ControllerManager::Slot { // thread for that timeout on every one of those attempts. std::chrono::steady_clock::time_point lastSilentAt{}; + // Mouse buttons currently held down by a back paddle, so leaving game mode + // mid-press releases them instead of stranding the button down. + bool paddleMouseLeftDown = false; + bool paddleMouseRightDown = false; + // Haptic edge-detection state for touch (movement ticks). bool hapticWasRightTouching = false; bool hapticWasLeftTouching = false; @@ -197,6 +202,7 @@ ControllerManager::~ControllerManager() { if (slot->gameModeActive) { StopReadLoop(*slot); slot->trackpad.Reset(); + ReleasePaddleMouseButtons(*slot); slot->vc.reset(); slot->gameModeActive = false; } @@ -276,10 +282,17 @@ void ControllerManager::SetBackButtonConfig(const BackButtonConfig& cfg) { // change takes effect on the very next report. } -void ControllerManager::SetBackButtonsEnabled(bool enabled) { - m_backButtonsEnabled = enabled; - for (auto& slot : m_slots) - slot->trackpad.SetBackButtonsEnabled(enabled); +void ControllerManager::ReleasePaddleMouseButtons(Slot& slot) { + auto send = [](DWORD flags) { + INPUT inp{}; + inp.type = INPUT_MOUSE; + inp.mi.dwFlags = flags; + SendInput(1, &inp, sizeof(INPUT)); + }; + if (slot.paddleMouseLeftDown) send(MOUSEEVENTF_LEFTUP); + if (slot.paddleMouseRightDown) send(MOUSEEVENTF_RIGHTUP); + slot.paddleMouseLeftDown = false; + slot.paddleMouseRightDown = false; } void ControllerManager::SetControllerPlatform(ControllerPlatform platform) { @@ -443,9 +456,9 @@ ControllerManager::EnableGameModeSlot(Slot& slot, bool& vigemMissingOut, EventLog::Write("GAMEMODE: enabled %ls", slot.path.c_str()); slot.gameModeActive = true; slot.trackpad.Reset(); + ReleasePaddleMouseButtons(slot); slot.trackpad.SetTrackpadEnabled(m_trackpadMouseEnabled); slot.trackpad.SetUseLeftTrackpad(m_useLeftTrackpad); - slot.trackpad.SetBackButtonsEnabled(m_backButtonsEnabled); StartReadLoop(slot); return GameModeOutcome::Enabled; } @@ -457,6 +470,7 @@ void ControllerManager::DisableGameModeSlot(Slot& slot) { if (slot.sc->IsOpen()) slot.sc->SetRumble(0, 0); slot.trackpad.Reset(); + ReleasePaddleMouseButtons(slot); slot.vc.reset(); slot.sc->EnableLizardMode(); // Reopen shared so Steam can obtain write access — game mode is no longer active. @@ -563,7 +577,7 @@ void ControllerManager::ReadLoop(Slot* slot) { if (!SteamController::IsStateReportId(buf[0])) continue; - if (slot->vc) slot->vc->Update(buf, n, m_backConfig, m_backButtonsEnabled); + if (slot->vc) slot->vc->Update(buf, n, m_backConfig); slot->trackpad.Update(buf, n); // Trackpad haptics. @@ -764,8 +778,7 @@ void ControllerManager::ReadLoop(Slot* slot) { // Fire mouse button events for back paddles mapped to LeftMouseButton/RightMouseButton. // Uses edge detection so we only send DOWN on press and UP on release. - // Skipped when backButtonsEnabled — TrackpadMouse already handles L4/R4 in that mode. - if (!m_backButtonsEnabled && hasPrev) { + if (hasPrev) { struct PaddleEdge { bool cur; bool prev; BackButtonAction action; }; const PaddleEdge edges[] = { { n>4 && (buf[4]&SteamController::BTN_L4)!=0, (prevBuf[4]&SteamController::BTN_L4)!=0, m_backConfig.l4 }, @@ -776,8 +789,13 @@ void ControllerManager::ReadLoop(Slot* slot) { for (const auto& e : edges) { if (e.cur == e.prev) continue; DWORD flag = 0; - if (e.action == BackButtonAction::LeftMouseButton) flag = e.cur ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; - else if (e.action == BackButtonAction::RightMouseButton) flag = e.cur ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; + if (e.action == BackButtonAction::LeftMouseButton) { + flag = e.cur ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; + slot->paddleMouseLeftDown = e.cur; + } else if (e.action == BackButtonAction::RightMouseButton) { + flag = e.cur ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; + slot->paddleMouseRightDown = e.cur; + } if (flag) { INPUT inp{}; inp.type = INPUT_MOUSE; inp.mi.dwFlags = flag; SendInput(1, &inp, sizeof(INPUT)); } } } diff --git a/src/app/ControllerManager.h b/src/app/ControllerManager.h index b953b9f..3fe2666 100644 --- a/src/app/ControllerManager.h +++ b/src/app/ControllerManager.h @@ -57,14 +57,12 @@ public: void SetTrackpadMouseEnabled(bool enabled); void SetUseLeftTrackpad(bool enabled); void SetBackButtonConfig(const BackButtonConfig& cfg); - void SetBackButtonsEnabled(bool enabled); void SetControllerPlatform(ControllerPlatform platform); bool IsConnected() const { return !m_slots.empty(); } bool IsGameModeActive() const; bool IsTrackpadMouseEnabled() const { return m_trackpadMouseEnabled; } bool IsUseLeftTrackpad() const { return m_useLeftTrackpad; } - bool IsBackButtonsEnabled() const { return m_backButtonsEnabled; } ControllerPlatform GetControllerPlatform() const { return m_controllerPlatform; } const BackButtonConfig& GetBackButtonConfig() const { return m_backConfig; } @@ -81,6 +79,7 @@ private: GameModeOutcome EnableGameModeSlot(Slot& slot, bool& vigemMissingOut, uint32_t stateWaitMs); void DisableGameModeSlot(Slot& slot); + void ReleasePaddleMouseButtons(Slot& slot); void StartReadLoop(Slot& slot); void StopReadLoop(Slot& slot); void ReadLoop(Slot* slot); @@ -91,7 +90,6 @@ private: std::vector> m_slots; bool m_trackpadMouseEnabled = false; bool m_useLeftTrackpad = false; - bool m_backButtonsEnabled = false; bool m_steamPresent = false; ControllerPlatform m_controllerPlatform = ControllerPlatform::Xbox; BackButtonConfig m_backConfig; diff --git a/src/app/RemapWindow.cpp b/src/app/RemapWindow.cpp index 6c49332..5d18b53 100644 --- a/src/app/RemapWindow.cpp +++ b/src/app/RemapWindow.cpp @@ -104,7 +104,7 @@ button{font-family:'Barlow',system-ui,sans-serif;cursor:pointer;border:none;back

Back Button Mapping

-

Click Rebind on a button, then press any gamepad button on your controller. The new binding shows up here instantly.

+

Click Rebind on a button, then press any gamepad button on your controller. The new binding shows up here instantly. Pick Off to stop a paddle doing anything at all.

LEFT GRIP
@@ -127,11 +127,12 @@ button{font-family:'Barlow',system-ui,sans-serif;cursor:pointer;border:none;back