Add optional controller rumble on button press

New "Vibrate controller on button press" setting under Controls. When enabled,
pressing a gamepad button rumbles that pad for as long as it's held - something
the PSP hardware never had, but which some people want from a modern pad.

Implemented per platform behind System_ControllerRumbleStart/Stop: SDL uses
SDL_RumbleGamepad, Windows feeds it into XinputDevice's existing vibration path
so it doesn't fight with the vibration games request through sceCtrl, and
Android goes through InputDevice.getVibrator() in Java. Stubbed elsewhere.

The D-pad is excluded, since it gets tapped constantly while navigating.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GgACRqkQNpfJQ4fjwyoEup
This commit is contained in:
HalexxanderandClaude Opus 5 authored and Henrik Rydgård committed 2026-09-05 12:08:09 -06:00
1 parent 6d4bc5f261
commit e5bd8e8e58
20 files changed
+173

No files matched your search

+21
View File
@@ -2,6 +2,7 @@
#include <climits>
#include <algorithm>
#include <atomic>
#include "Common/System/NativeApp.h"
#include "Common/CommonWindows.h"
@@ -123,6 +124,15 @@ static void UnloadXInputDLL() {}
#define XUSER_MAX_COUNT 4
#endif
// Written from the input thread by XinputSetButtonRumble, read by ApplyVibration.
static std::atomic<bool> g_buttonRumble[XUSER_MAX_COUNT];
void XinputSetButtonRumble(int pad, bool on) {
if (pad >= 0 && pad < XUSER_MAX_COUNT) {
g_buttonRumble[pad] = on;
}
}
// Undocumented. Steam annoyingly grabs this button though....
#define XINPUT_GUIDE_BUTTON 0x400
@@ -307,6 +317,7 @@ void XinputDevice::ApplyVibration(int pad, XINPUT_VIBRATION &vibration) {
vibration.wLeftMotorSpeed = 0;
vibration.wRightMotorSpeed = 0;
}
ApplyButtonRumble(pad, vibration);
if (padData_[pad].prevVibration.wLeftMotorSpeed != vibration.wLeftMotorSpeed || padData_[pad].prevVibration.wRightMotorSpeed != vibration.wRightMotorSpeed) {
PPSSPP_XInputSetState(pad, &vibration);
@@ -315,9 +326,19 @@ void XinputDevice::ApplyVibration(int pad, XINPUT_VIBRATION &vibration) {
prevVibrationTime_ = newVibrationTime_;
}
} else {
ApplyButtonRumble(pad, vibration);
DWORD dwResult = PPSSPP_XInputSetState(pad, &vibration);
if (dwResult != ERROR_SUCCESS) {
padData_[pad].checkDelayUpdates = 30;
}
}
}
void XinputDevice::ApplyButtonRumble(int pad, XINPUT_VIBRATION &vibration) {
// Runs the motors flat out for as long as the button is held, overriding whatever
// the game asked for - it's an explicit user setting, so it wins.
if (g_buttonRumble[pad]) {
vibration.wLeftMotorSpeed = 0xFFFF;
vibration.wRightMotorSpeed = 0xFFFF;
}
}