From 6980e0863de03dc13f57360f0d74de108f2cca78 Mon Sep 17 00:00:00 2001 From: Mehmet Cambaz <52370708+MehmetCambaz@users.noreply.github.com> Date: Sat, 29 Aug 2026 03:49:20 +0300 Subject: [PATCH] controller: forward guest vibration to SDL rumble (#365) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scale the SDK 8-bit motor levels to SDL’s 16-bit range and use SDL’s maximum effect duration. Keep lookup and rumble under the controller lock so hot unplug cannot invalidate the SDL pointer. --- src/libs/controller.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/libs/controller.cpp b/src/libs/controller.cpp index cd019386b..32a6c3483 100644 --- a/src/libs/controller.cpp +++ b/src/libs/controller.cpp @@ -1,5 +1,6 @@ #include "libs/controller.h" +#include "SDL.h" #include "common/assert.h" #include "common/common.h" #include "common/emulatorConfig.h" @@ -22,6 +23,9 @@ LIB_NAME("Pad", "Pad"); constexpr int PAD_ERROR_INVALID_ARG = -2137915391; /* 0x80920001 */ constexpr int PAD_ERROR_INVALID_HANDLE = -2137915389; /* 0x80920003 */ +// SDL limits rumble commands to 0xffff ms; zero strengths stop immediately. +constexpr uint32_t RUMBLE_DURATION_MS = 0xffff; + struct PadControllerInformation { float touch_pixel_density; uint16_t touch_resolution_x; @@ -59,6 +63,7 @@ public: void Axis(int id, Axis axis, int value); void ResetInputState(); void GetConnectionInfo(bool* flag, int* count); + void SetVibration(uint8_t large_motor, uint8_t small_motor); void ReadState(ControllerState* state, bool* flag, int* count); int ReadStates(ControllerState* states, int states_num, bool* flag, int* count); @@ -269,6 +274,25 @@ void GameController::ResetInputState() { AddState(state); } +void GameController::SetVibration(uint8_t large_motor, uint8_t small_motor) { + Common::LockGuard lock(m_mutex); + + if (m_active_id == HOST_INPUT_CONTROLLER_ID) { + return; + } + + auto* pad = SDL_GameControllerFromInstanceID(static_cast(m_active_id)); + if (pad == nullptr) { + return; + } + + const auto large = static_cast(large_motor * 0x101U); + const auto small = static_cast(small_motor * 0x101U); + if (SDL_GameControllerRumble(pad, large, small, RUMBLE_DURATION_MS) != 0) { + LOGF("\t rumble failed: %s\n", SDL_GetError()); + } +} + void GameController::GetConnectionInfo(bool* flag, int* count) { EXIT_IF(flag == nullptr); EXIT_IF(count == nullptr); @@ -544,6 +568,9 @@ int KYTY_SYSV_ABI PadSetVibration(int handle, const PadVibrationParam* param) { "\t small_motor = %d\n", static_cast(param->large_motor), static_cast(param->small_motor)); + EXIT_IF(g_controller == nullptr); + g_controller->SetVibration(param->large_motor, param->small_motor); + return OK; }