controller: enable the DualSense HIDAPI driver, light bar and adaptive triggers (#437)

* controller: enable the DualSense HIDAPI driver, light bar and adaptive triggers

Three additions. SDL was opening a DualSense through the generic gamepad path, so SDL_GameControllerHasLED and SDL_GameControllerGetNumTouchpads both reported nothing and no touchpad events ever arrived - enabling the PS5 HIDAPI driver before SDL_InitSubSystem is what makes the light bar and the existing touchpad support reachable. PadSetLightBar validated its arguments and returned OK without touching the pad; it now drives the LED. PadSetTriggerEffect did the same; it now translates the mode and parameters into a DualSense trigger block and sends it.

* controller: fix DualSense effects

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
CheesyPoofs3460
2026-09-01 13:53:19 -07:00
committed by GitHub
parent 80225015c5
commit 04b404f3c9
4 changed files with 178 additions and 18 deletions
@@ -814,6 +814,7 @@ static void WindowCreate(WindowContext& context) {
int width = static_cast<int>(context.graphic_ctx.screen_width); int width = static_cast<int>(context.graphic_ctx.screen_width);
int height = static_cast<int>(context.graphic_ctx.screen_height); int height = static_cast<int>(context.graphic_ctx.screen_height);
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS #if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING, "0"); SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING, "0");
#endif #endif
+174
View File
@@ -39,11 +39,42 @@ struct PadControllerInformation {
uint8_t reserve[8]; uint8_t reserve[8];
}; };
struct PadLightBarParam {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t reserve;
};
struct PadVibrationParam { struct PadVibrationParam {
uint8_t large_motor; uint8_t large_motor;
uint8_t small_motor; uint8_t small_motor;
}; };
struct PadTriggerEffectCommand {
uint32_t mode;
uint8_t reserve[4];
uint8_t data[48];
};
struct PadTriggerEffectParam {
uint8_t trigger_mask;
uint8_t reserve[7];
PadTriggerEffectCommand command[2];
};
static_assert(sizeof(PadTriggerEffectCommand) == 56);
static_assert(sizeof(PadTriggerEffectParam) == 120);
struct DualSenseEffects {
uint8_t enable_bits;
uint8_t reserve[9];
uint8_t right_trigger[11];
uint8_t left_trigger[11];
};
static_assert(sizeof(DualSenseEffects) == 32);
struct ControllerState { struct ControllerState {
struct Touch { struct Touch {
uint8_t id = 0; uint8_t id = 0;
@@ -73,6 +104,8 @@ public:
void ResetInputState(); void ResetInputState();
void GetConnectionInfo(bool* flag, int* count); void GetConnectionInfo(bool* flag, int* count);
void SetVibration(uint8_t large_motor, uint8_t small_motor); void SetVibration(uint8_t large_motor, uint8_t small_motor);
void SetLightBar(uint8_t r, uint8_t g, uint8_t b);
bool SetTriggerEffect(const PadTriggerEffectParam& param);
void ReadState(ControllerState* state, bool* flag, int* count); void ReadState(ControllerState* state, bool* flag, int* count);
int ReadStates(ControllerState* states, int states_num, bool* flag, int* count); int ReadStates(ControllerState* states, int states_num, bool* flag, int* count);
@@ -125,6 +158,92 @@ static void pad_fill_data(PadData* data, const ControllerState& state, bool conn
data->device_unique_data_len = 0; data->device_unique_data_len = 0;
} }
static bool trigger_effect_zones(uint8_t* effect, const uint8_t* strengths, uint8_t type,
uint8_t frequency = 0) {
uint16_t active = 0;
uint32_t packed = 0;
for (int i = 0; i < 10; i++) {
if (strengths[i] > 8) {
return false;
}
if (strengths[i] != 0) {
active |= static_cast<uint16_t>(1u << i);
packed |= static_cast<uint32_t>(strengths[i] - 1u) << (3 * i);
}
}
effect[0] = active != 0 && (type != 0x26 || frequency != 0) ? type : 0x05;
effect[1] = static_cast<uint8_t>(active);
effect[2] = static_cast<uint8_t>(active >> 8u);
effect[3] = static_cast<uint8_t>(packed);
effect[4] = static_cast<uint8_t>(packed >> 8u);
effect[5] = static_cast<uint8_t>(packed >> 16u);
effect[6] = static_cast<uint8_t>(packed >> 24u);
effect[9] = frequency;
return true;
}
static bool trigger_effect_to_dualsense(const PadTriggerEffectCommand& command, uint8_t* effect) {
std::memset(effect, 0, 11);
effect[0] = 0x05;
uint8_t strengths[10] = {};
switch (command.mode) {
case 0: return true;
case 1:
if (command.data[0] > 9 || command.data[1] > 8) {
return false;
}
std::fill(strengths + command.data[0], strengths + 10, command.data[1]);
return trigger_effect_zones(effect, strengths, 0x21);
case 2: {
const auto start = command.data[0];
const auto end = command.data[1];
const auto strength = command.data[2];
if (start < 2 || start > 7 || end <= start || end > 8 || strength > 8) {
return false;
}
if (strength == 0) {
return true;
}
const uint16_t zones = static_cast<uint16_t>((1u << start) | (1u << end));
effect[0] = 0x25;
effect[1] = static_cast<uint8_t>(zones);
effect[2] = static_cast<uint8_t>(zones >> 8u);
effect[3] = strength - 1u;
return true;
}
case 3:
if (command.data[0] > 9 || command.data[1] > 8) {
return false;
}
std::fill(strengths + command.data[0], strengths + 10, command.data[1]);
return trigger_effect_zones(effect, strengths, 0x26, command.data[2]);
case 4: return trigger_effect_zones(effect, command.data, 0x21);
case 5: {
const int start = command.data[0];
const int end = command.data[1];
const int start_strength = command.data[2];
const int end_strength = command.data[3];
if (start > 8 || end <= start || end > 9 || start_strength < 1 || start_strength > 8 ||
end_strength < 1 || end_strength > 8) {
return false;
}
for (int i = start; i < 10; i++) {
const int delta = (end_strength - start_strength) * (i - start);
const int length = end - start;
strengths[i] = static_cast<uint8_t>(
i >= end ? end_strength
: start_strength +
(delta + (delta < 0 ? -length / 2 : length / 2)) / length);
}
return trigger_effect_zones(effect, strengths, 0x21);
}
case 6: return trigger_effect_zones(effect, command.data + 1, 0x26, command.data[0]);
default: return false;
}
}
void Initialize() { void Initialize() {
EXIT_IF(g_controller != nullptr); EXIT_IF(g_controller != nullptr);
@@ -304,6 +423,44 @@ void GameController::SetVibration(uint8_t large_motor, uint8_t small_motor) {
} }
} }
void GameController::SetLightBar(uint8_t r, uint8_t g, uint8_t b) {
Common::LockGuard lock(m_mutex);
if (auto* pad = SDL_GameControllerFromInstanceID(static_cast<SDL_JoystickID>(m_active_id));
pad != nullptr) {
(void)SDL_GameControllerSetLED(pad, r, g, b);
}
}
bool GameController::SetTriggerEffect(const PadTriggerEffectParam& param) {
if ((param.trigger_mask & ~0x03u) != 0) {
return false;
}
DualSenseEffects effect {};
if ((param.trigger_mask & 0x01u) != 0) {
effect.enable_bits |= 0x08;
if (!trigger_effect_to_dualsense(param.command[0], effect.left_trigger)) {
return false;
}
}
if ((param.trigger_mask & 0x02u) != 0) {
effect.enable_bits |= 0x04;
if (!trigger_effect_to_dualsense(param.command[1], effect.right_trigger)) {
return false;
}
}
if (effect.enable_bits == 0) {
return true;
}
Common::LockGuard lock(m_mutex);
auto* pad = SDL_GameControllerFromInstanceID(static_cast<SDL_JoystickID>(m_active_id));
if (pad != nullptr && SDL_GameControllerGetType(pad) == SDL_CONTROLLER_TYPE_PS5) {
(void)SDL_GameControllerSendEffect(pad, &effect, sizeof(effect));
}
return true;
}
void GameController::GetConnectionInfo(bool* flag, int* count) { void GameController::GetConnectionInfo(bool* flag, int* count) {
EXIT_IF(flag == nullptr); EXIT_IF(flag == nullptr);
EXIT_IF(count == nullptr); EXIT_IF(count == nullptr);
@@ -617,7 +774,24 @@ int KYTY_SYSV_ABI PadSetLightBar(int handle, const PadLightBarParam* param) {
return PAD_ERROR_INVALID_ARG; return PAD_ERROR_INVALID_ARG;
} }
EXIT_IF(g_controller == nullptr);
g_controller->SetLightBar(param->r, param->g, param->b);
return OK; return OK;
} }
int KYTY_SYSV_ABI PadSetTriggerEffect(int handle, const PadTriggerEffectParam* param) {
PRINT_NAME();
if (handle != 1) {
return PAD_ERROR_INVALID_HANDLE;
}
if (param == nullptr) {
return PAD_ERROR_INVALID_ARG;
}
EXIT_IF(g_controller == nullptr);
return g_controller->SetTriggerEffect(*param) ? OK : PAD_ERROR_INVALID_ARG;
}
} // namespace Libs::Controller } // namespace Libs::Controller
+2
View File
@@ -48,6 +48,7 @@ struct PadControllerInformation;
struct PadData; struct PadData;
struct PadVibrationParam; struct PadVibrationParam;
struct PadLightBarParam; struct PadLightBarParam;
struct PadTriggerEffectParam;
inline int controller_get_axis(int min, int max, int value) { inline int controller_get_axis(int min, int max, int value) {
int v = (255 * (value - min)) / (max - min); int v = (255 * (value - min)) / (max - min);
@@ -78,6 +79,7 @@ int KYTY_SYSV_ABI PadGetControllerInformation(int handle, PadControllerInformati
int KYTY_SYSV_ABI PadReadState(int handle, PadData* data); int KYTY_SYSV_ABI PadReadState(int handle, PadData* data);
int KYTY_SYSV_ABI PadRead(int handle, PadData* data, int num); int KYTY_SYSV_ABI PadRead(int handle, PadData* data, int num);
int KYTY_SYSV_ABI PadSetVibration(int handle, const PadVibrationParam* param); int KYTY_SYSV_ABI PadSetVibration(int handle, const PadVibrationParam* param);
int KYTY_SYSV_ABI PadSetTriggerEffect(int handle, const PadTriggerEffectParam* param);
int KYTY_SYSV_ABI PadResetLightBar(int handle); int KYTY_SYSV_ABI PadResetLightBar(int handle);
int KYTY_SYSV_ABI PadSetLightBar(int handle, const PadLightBarParam* param); int KYTY_SYSV_ABI PadSetLightBar(int handle, const PadLightBarParam* param);
+1 -18
View File
@@ -42,23 +42,6 @@ static int KYTY_SYSV_ABI PadGetTriggerEffectState(int
return 0; return 0;
} }
static int KYTY_SYSV_ABI PadSetTriggerEffect(int handle, const void* param) {
PRINT_NAME();
LOGF("\t handle = %d\n"
"\t param = 0x%016" PRIx64 "\n",
handle, reinterpret_cast<uint64_t>(param));
if (handle != 1) {
return -2137915389; /* 0x80920003 */
}
if (param == nullptr) {
return -2137915391; /* 0x80920001 */
}
return OK;
}
static int KYTY_SYSV_ABI PadSetVibrationTriggerEffectWeakWhileEmbeddedMicInUse(bool enabled) { static int KYTY_SYSV_ABI PadSetVibrationTriggerEffectWeakWhileEmbeddedMicInUse(bool enabled) {
PRINT_NAME(); PRINT_NAME();
@@ -198,7 +181,7 @@ LIB_DEFINE(InitPad_1) {
LIB_FUNC("rIZnR6eSpvk", Controller::PadResetOrientation); LIB_FUNC("rIZnR6eSpvk", Controller::PadResetOrientation);
LIB_FUNC("vDLMoJLde8I", PadSetTiltCorrectionState); LIB_FUNC("vDLMoJLde8I", PadSetTiltCorrectionState);
LIB_FUNC("gjP9-KQzoUk", Controller::PadGetControllerInformation); LIB_FUNC("gjP9-KQzoUk", Controller::PadGetControllerInformation);
LIB_FUNC("2JgFB2n9oUM", PadSetTriggerEffect); LIB_FUNC("2JgFB2n9oUM", Controller::PadSetTriggerEffect);
LIB_FUNC("YndgXqQVV7c", Controller::PadReadState); LIB_FUNC("YndgXqQVV7c", Controller::PadReadState);
LIB_FUNC("q1cHNfGycLI", Controller::PadRead); LIB_FUNC("q1cHNfGycLI", Controller::PadRead);
LIB_FUNC("yFVnOdGxvZY", Controller::PadSetVibration); LIB_FUNC("yFVnOdGxvZY", Controller::PadSetVibration);