Input mapping tabs UI, emulated touchpad, and rumble for non-PS5 controllers (#1541)

* Revamp input mapping dialog with tabs, gamepad remapping and gyro emulation toggle

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Add emulated touchpad (mouse/gamepad) with particle skins; widen input dialog

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Disable mouse touchpad mode when the cursor leaves the window

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Add touchpad hand-cursor toggle; share touchpad options across both tabs

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Hide touchpad trail and particles when the visual toggle is off

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Rename input mapping tabs

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Rumble non-DualSense gamepads from haptic audio

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Fix input mapping, touchpad transitions, and rumble fallback

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
Felipe
2026-10-15 23:04:37 -03:00
committed by GitHub
parent 6a0a666a41
commit 3ff6606fcb
18 changed files with 1909 additions and 210 deletions
+46 -3
View File
@@ -143,13 +143,13 @@ void DestroyAudioStream(SDL_AudioStream* stream) {
SDL_AudioDeviceID GetAudioStreamDevice(SDL_AudioStream*) {
return 999;
}
// Pads 1 and 3 are DualSenses.
// Pads 1 and 3 are DualSenses; pad 2 only supports rumble.
SDL_GamepadType GetGamepadTypeForID(SDL_JoystickID id) {
return id == 1 || id == 3 ? SDL_GAMEPAD_TYPE_PS5 : SDL_GAMEPAD_TYPE_UNKNOWN;
}
SDL_Gamepad* GetGamepadFromID(SDL_JoystickID id) {
return id == 1 || id == 3 ? reinterpret_cast<SDL_Gamepad*>(static_cast<uintptr_t>(id))
: nullptr;
return id >= 1 && id <= 3 ? reinterpret_cast<SDL_Gamepad*>(static_cast<uintptr_t>(id))
: nullptr;
}
SDL_GamepadType GetGamepadType(SDL_Gamepad* pad) {
return GetGamepadTypeForID(static_cast<SDL_JoystickID>(reinterpret_cast<uintptr_t>(pad)));
@@ -458,6 +458,47 @@ void TestDiscoveryAndHotplug() {
Check(streams.empty(), "disconnected endpoint retained its stream");
}
void TestRumbleFallbackGains() {
Fixture f;
auto port = Open();
const auto queue = [&](std::array<float, 2> gains) {
Check(Haptics::Queue(port.get(), 2, pcm.data(), 2, 2, true, unity.data(), 1.0f,
gains.data()) == 2 * 1000000 / 48000,
"rumble fallback playback time is wrong");
};
queue({0.0f, 0.25f});
Check(pad_rumble[2].large == 0 && pad_rumble[2].small == 16383,
"rumble fallback ignored per-channel gains");
queue({0.5f, 0.0f});
Check(pad_rumble[2].large == 32767 && pad_rumble[2].small == 0,
"rumble fallback swapped per-channel gains");
queue({0.0f, 0.0f});
Check(pad_rumble[2].large == 0 && pad_rumble[2].small == 0,
"muted fallback channels still rumbled");
Check(streams.empty() && hid_opens == 0, "rumble fallback opened an audio device");
}
void TestRumbleFallbackSwitch() {
for (bool bluetooth: {false, true}) {
Fixture f;
wireless = hid_available = bluetooth;
auto port = Open();
Queue(port, pcm.data());
Check(bluetooth ? Haptics::UsesBluetooth(port.get()) : streams.size() == 1,
"DualSense transport did not open before fallback");
Haptics::Queue(port.get(), 2, pcm.data(), 2, 2, true, unity.data());
Check(streams.empty() && !Haptics::UsesBluetooth(port.get()) &&
(!bluetooth || hid_closes == 1),
"switching to rumble retained the DualSense transport");
Check(pad_rumble[2].large == 65535 && pad_rumble[2].small == 65535,
"switching from DualSense did not start rumble fallback");
Queue(port, pcm.data());
Check(bluetooth ? Haptics::UsesBluetooth(port.get()) && hid_opens == 2
: streams.size() == 1 && opens == 2,
"switching back delayed reopening the DualSense transport");
}
}
void TestSpeaker() {
Fixture f;
devices = {{10, "DualSense Wireless Controller", 2}};
@@ -1279,6 +1320,8 @@ int main() {
TestAudioDefaultResumeFailure();
TestFormatsAndVolume();
TestDiscoveryAndHotplug();
TestRumbleFallbackGains();
TestRumbleFallbackSwitch();
TestSpeaker();
TestSpeakerUnplug();
TestSpeakerSwitchAndFailures();