mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user