From 71a50df221e60eecc5e91fab3147e926e63e260c Mon Sep 17 00:00:00 2001 From: Dylan Deverill Date: Sun, 6 Sep 2026 21:05:05 -0700 Subject: [PATCH] Actuate the DS4 touchpad at the same force as everything else (#96) The DS4 touchpad press was the last thing still reading the firmware's click bit, which reports fewer than half of a thumb's presses and wants more force the more heavily the pad is already being rested on. That is what "having to press pretty hard to activate the trackpad" is, and why it reproduces for one person and not another: the threshold is force based, so thumb size and resting weight decide how hard is hard enough. It now takes the same resolved press every other binding uses, so every press in the app actuates at one threshold rather than the touchpad keeping its own, much heavier one. No sensitivity setting, which is what the report asked for. The pad already actuates about as lightly as it can without a resting thumb tripping it, so there would be nothing useful to turn down. If it still reads heavy after this, that is evidence kPadPressArea itself is too high for everyone rather than a reason to add a knob most people would never find. Also fixes a second reader of the same bit, found while checking for others: TapState asked it whether a contact had become a press, so a gentle press the firmware missed left the contact looking like a tap and fired the tap binding on lift, on top of the direction the press had already sent. With a tap bound to a mouse button that is a stray click on every soft press. Co-Authored-By: Claude Opus 5 --- src/app/ControllerManager.cpp | 12 ++++++++---- src/app/VirtualController.cpp | 16 +++++++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/app/ControllerManager.cpp b/src/app/ControllerManager.cpp index 42fdb5b..c14c86a 100644 --- a/src/app/ControllerManager.cpp +++ b/src/app/ControllerManager.cpp @@ -1112,12 +1112,16 @@ void ControllerManager::ReadLoop(Slot* slot) { slot->rightPad.Directions(), slot->leftPad.ClickInCentre(), slot->rightPad.ClickInCentre(), + // A tap is a contact that never became a press, and "became a + // press" has to mean the same thing here as everywhere else. Asked + // of the firmware's click bit instead, a gentle press it failed to + // report would leave the contact looking like a tap, and firing + // the tap binding on lift on top of the direction the press had + // already sent. slot->leftTap.Update((tb3 & SteamController::BTN_TP_LT) != 0, - (tb3 & SteamController::BTN_TP_LT_CLICK) != 0, - tlx, tly), + slot->leftPress.pressed, tlx, tly), slot->rightTap.Update((tb2 & SteamController::BTN_TP_RT) != 0, - (tb2 & SteamController::BTN_TP_RT_CLICK) != 0, - trx, try_), + slot->rightPress.pressed, trx, try_), slot->leftPress.pressed, slot->rightPress.pressed, }; diff --git a/src/app/VirtualController.cpp b/src/app/VirtualController.cpp index 794c941..71dc004 100644 --- a/src/app/VirtualController.cpp +++ b/src/app/VirtualController.cpp @@ -426,10 +426,8 @@ void VirtualController::Update(const uint8_t* buf, size_t n, SetDs4DPad(r.wButtons, hat); // Touchpad — right SC pad → DS4 contact 1, left SC pad → DS4 contact 2 - const bool rawRightTouching = (b2 & SteamController::BTN_TP_RT) != 0; - const bool rawLeftTouching = (b3 & SteamController::BTN_TP_LT) != 0; - const bool rawRightClick = (b2 & SteamController::BTN_TP_RT_CLICK) != 0; - const bool rawLeftClick = (b3 & SteamController::BTN_TP_LT_CLICK) != 0; + const bool rawRightTouching = (b2 & SteamController::BTN_TP_RT) != 0; + const bool rawLeftTouching = (b3 & SteamController::BTN_TP_LT) != 0; const bool rightTouching = rawRightTouching && rightToTouchpad; const bool leftTouching = rawLeftTouching && leftToTouchpad; @@ -437,7 +435,15 @@ void VirtualController::Update(const uint8_t* buf, size_t n, // The click travels with the pad: a pad feeding the touchpad // reports its press as the touchpad press, and a pad doing // anything else has already dispatched that press as a binding. - if ((rawRightClick && rightToTouchpad) || (rawLeftClick && leftToTouchpad)) + // + // From the same resolved press every other binding uses, not the + // firmware's click bit. That bit reports fewer than half of a + // thumb's presses, and how much force it wants varies with thumb + // size and how heavily the pad is already being rested on — which + // is why a touchpad that took a firm press to activate for one + // person felt fine to another (#96). + if ((resolved.rightPressed && rightToTouchpad) + || (resolved.leftPressed && leftToTouchpad)) r.bSpecial |= DS4_SPECIAL_BUTTON_TOUCHPAD; if (rightTouching && !m_wasRightTouching)