Refactor pad handles (#4386)

* Set up [userid, type, index] -> pad_handle mapping and hook up currently existing GameControllers backend to it

* just use basic oop principles why are we even shoving callee responsibility in caller logic

* support special pads

* random edge case I thought of

* return error if handle is already opened, redirect scePadOpenExt to scePadOpen

* scePadClose

* error out if trying to close an unopened handle

* oof

* logging
This commit is contained in:
kalaposfos13 2026-05-10 10:04:20 +02:00 committed by GitHub
parent d3597c7f18
commit 66112bc90a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 100 additions and 76 deletions

View File

@ -18,12 +18,38 @@ using Input::GameController;
using Input::GameControllers;
using namespace Libraries::UserService;
struct HandleKey {
OrbisUserServiceUserId id;
s32 device_class;
s32 index;
bool operator==(const HandleKey&) const = default;
};
struct HandleKeyHash {
std::size_t operator()(const HandleKey& k) const {
std::size_t h1 = std::hash<OrbisUserServiceUserId>{}(k.id);
std::size_t h2 = std::hash<s32>{}(k.device_class);
std::size_t h3 = std::hash<s32>{}(k.index);
return h1 ^ (h2 << 1) ^ (h3 << 2);
}
};
static bool g_initialized = false;
static std::unordered_map<OrbisUserServiceUserId, s32> user_id_pad_handle_map{};
static constexpr s32 tv_remote_handle = 5;
static u64 pad_handle_counter = 1;
static std::unordered_map<HandleKey, s32, HandleKeyHash> pad_handle_map{};
static std::unordered_map<s32, GameController*> handle_to_controller_map{};
int PS4_SYSV_ABI scePadClose(s32 handle) {
LOG_ERROR(Lib_Pad, "(STUBBED) called");
LOG_WARNING(Lib_Pad, "called, handle: {}", handle);
if (handle_to_controller_map.erase(handle) == 0) {
return ORBIS_PAD_ERROR_INVALID_HANDLE;
}
for (auto& it : pad_handle_map) {
if (it.second == handle) {
pad_handle_map.erase(it.first);
break;
}
}
return ORBIS_OK;
}
@ -34,6 +60,10 @@ int PS4_SYSV_ABI scePadConnectPort() {
int PS4_SYSV_ABI scePadDeviceClassGetExtendedInformation(
s32 handle, OrbisPadDeviceClassExtendedInformation* pExtInfo) {
auto it = handle_to_controller_map.find(handle);
if (it == handle_to_controller_map.end()) {
return ORBIS_PAD_ERROR_INVALID_HANDLE;
}
LOG_ERROR(Lib_Pad, "(STUBBED) called");
std::memset(pExtInfo, 0, sizeof(OrbisPadDeviceClassExtendedInformation));
if (EmulatorSettings.IsUsingSpecialPad()) {
@ -100,6 +130,10 @@ int PS4_SYSV_ABI scePadGetCapability() {
int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerInformation* pInfo) {
LOG_DEBUG(Lib_Pad, "called handle = {}", handle);
auto it = handle_to_controller_map.find(handle);
if (it == handle_to_controller_map.end()) {
return ORBIS_PAD_ERROR_INVALID_HANDLE;
}
pInfo->touchPadInfo.pixelDensity = 1;
pInfo->touchPadInfo.resolution.x = 1920;
pInfo->touchPadInfo.resolution.y = 950;
@ -108,10 +142,6 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn
pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD;
pInfo->connectedCount = 1;
pInfo->deviceClass = OrbisPadDeviceClass::Standard;
if (handle < 0) {
pInfo->connected = false;
return ORBIS_OK;
}
pInfo->connected = true;
if (EmulatorSettings.IsUsingSpecialPad()) {
pInfo->connectionType = ORBIS_PAD_PORT_TYPE_SPECIAL;
@ -168,8 +198,8 @@ int PS4_SYSV_ABI scePadGetHandle(Libraries::UserService::OrbisUserServiceUserId
if (userId == -1) {
return ORBIS_PAD_ERROR_DEVICE_NO_HANDLE;
}
auto it = user_id_pad_handle_map.find(userId);
if (it == user_id_pad_handle_map.end()) {
auto it = pad_handle_map.find({userId, type, index});
if (it == pad_handle_map.end()) {
return ORBIS_PAD_ERROR_DEVICE_NO_HANDLE;
}
s32 pad_handle = it->second;
@ -281,11 +311,17 @@ int PS4_SYSV_ABI scePadOpen(Libraries::UserService::OrbisUserServiceUserId userI
if (userId < 0) {
return ORBIS_DEVICE_SERVICE_ERROR_INVALID_USER;
}
if (pad_handle_map.find({userId, type, index}) != pad_handle_map.end()) {
return ORBIS_PAD_ERROR_ALREADY_OPENED;
}
auto& controllers = *Common::Singleton<GameControllers>::Instance();
if (userId == ORBIS_USER_SERVICE_USER_ID_SYSTEM) {
if (type == ORBIS_PAD_PORT_TYPE_REMOTE_CONTROL) {
LOG_INFO(Lib_Pad, "Opened a TV remote device");
user_id_pad_handle_map[ORBIS_USER_SERVICE_USER_ID_SYSTEM] = tv_remote_handle;
return tv_remote_handle;
s32 new_handle = pad_handle_counter++;
pad_handle_map[{userId, type, index}] = new_handle;
handle_to_controller_map[new_handle] = controllers[4];
LOG_INFO(Lib_Pad, "Opened a TV remote device, out handle: {}", new_handle);
return new_handle;
}
return ORBIS_DEVICE_SERVICE_ERROR_INVALID_USER;
}
@ -297,28 +333,25 @@ int PS4_SYSV_ABI scePadOpen(Libraries::UserService::OrbisUserServiceUserId userI
return ORBIS_DEVICE_SERVICE_ERROR_USER_NOT_LOGIN;
}
s32 pad_handle = u->player_index;
LOG_INFO(Lib_Pad, "called user_id = {} type = {} index = {}, pad_handle = {}", userId, type,
LOG_INFO(Lib_Pad, "called user_id = {} type = {} index = {}, player_index = {}", userId, type,
index, pad_handle);
scePadResetLightBar(pad_handle);
scePadResetOrientation(pad_handle);
user_id_pad_handle_map[userId] = pad_handle;
return pad_handle;
s32 new_handle = pad_handle_counter++;
pad_handle_map[{userId, type, index}] = new_handle;
handle_to_controller_map[new_handle] =
controllers[type == (EmulatorSettings.IsUsingSpecialPad() ? 2 : 0)
? UserManagement.GetUserByID(userId)->player_index - 1
: 4];
LOG_INFO(Lib_Pad, "Out handle: {}", new_handle);
return new_handle;
}
int PS4_SYSV_ABI scePadOpenExt(Libraries::UserService::OrbisUserServiceUserId userId, s32 type,
s32 index, const OrbisPadOpenExtParam* pParam) {
LOG_ERROR(Lib_Pad, "(STUBBED) called");
auto u = UserManagement.GetUserByID(userId);
if (!u) {
return ORBIS_DEVICE_SERVICE_ERROR_USER_NOT_LOGIN;
}
s32 pad_handle = u->player_index;
LOG_INFO(Lib_Pad, "called user_id = {} type = {} index = {}, pad_handle = {}", userId, type,
index, pad_handle);
scePadResetLightBar(pad_handle);
scePadResetOrientation(pad_handle);
user_id_pad_handle_map[userId] = pad_handle;
return pad_handle;
LOG_WARNING(Lib_Pad, "Redirect to scePadOpen");
return scePadOpen(userId, type, index, nullptr);
}
int PS4_SYSV_ABI scePadOpenExt2() {
@ -459,12 +492,11 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
int connected_count = 0;
bool connected = false;
std::vector<Input::State> states(64);
auto controller_id = GameControllers::GetControllerIndexFromControllerID(handle);
if (!controller_id) {
auto it = handle_to_controller_map.find(handle);
if (it == handle_to_controller_map.end()) {
return ORBIS_PAD_ERROR_INVALID_HANDLE;
}
auto& controllers = *Common::Singleton<GameControllers>::Instance();
auto& controller = *controllers[*controller_id];
auto& controller = *it->second;
int ret_num = controller.ReadStates(states.data(), num, &connected, &connected_count);
return ProcessStates(handle, pData, controller, states.data(), ret_num, connected,
connected_count);
@ -492,12 +524,11 @@ int PS4_SYSV_ABI scePadReadHistory() {
int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
LOG_TRACE(Lib_Pad, "handle: {}", handle);
auto controller_id = GameControllers::GetControllerIndexFromControllerID(handle);
if (!controller_id) {
auto it = handle_to_controller_map.find(handle);
if (it == handle_to_controller_map.end()) {
return ORBIS_PAD_ERROR_INVALID_HANDLE;
}
auto& controllers = *Common::Singleton<GameControllers>::Instance();
auto& controller = *controllers[*controller_id];
auto& controller = *it->second;
int connected_count = 0;
bool connected = false;
Input::State state;
@ -513,12 +544,12 @@ int PS4_SYSV_ABI scePadReadStateExt() {
int PS4_SYSV_ABI scePadResetLightBar(s32 handle) {
LOG_DEBUG(Lib_Pad, "called, handle: {}", handle);
auto controller_id = GameControllers::GetControllerIndexFromControllerID(handle);
if (!controller_id) {
auto it = handle_to_controller_map.find(handle);
if (it == handle_to_controller_map.end()) {
return ORBIS_PAD_ERROR_INVALID_HANDLE;
}
auto& controllers = *Common::Singleton<GameControllers>::Instance();
auto u = UserManagement.GetUserByPlayerIndex(handle);
auto& controller = *it->second;
auto u = UserManagement.GetUserByPlayerIndex(controller.user_id);
s32 colour_index = u ? u->user_color - 1 : 0;
Input::Colour colour{255, 0, 0};
if (colour_index >= 0 && colour_index <= 3) {
@ -533,10 +564,7 @@ int PS4_SYSV_ABI scePadResetLightBar(s32 handle) {
LOG_ERROR(Lib_Pad, "Invalid user colour value {} for controller {}, falling back to blue",
colour_index, handle);
}
if (auto oc = GameControllers::GetControllerCustomColor(*controller_id)) {
colour = *oc;
}
controllers[*controller_id]->SetLightBarRGB(colour.r, colour.g, colour.b);
controller.SetLightBarRGB(colour.r, colour.g, colour.b);
return ORBIS_OK;
}
@ -553,15 +581,14 @@ int PS4_SYSV_ABI scePadResetLightBarAllByPortType() {
int PS4_SYSV_ABI scePadResetOrientation(s32 handle) {
LOG_INFO(Lib_Pad, "scePadResetOrientation called handle = {}", handle);
auto controller_id = GameControllers::GetControllerIndexFromControllerID(handle);
if (!controller_id) {
auto it = handle_to_controller_map.find(handle);
if (it == handle_to_controller_map.end()) {
return ORBIS_PAD_ERROR_INVALID_HANDLE;
}
auto& controllers = *Common::Singleton<GameControllers>::Instance();
auto& controller = *it->second;
Libraries::Pad::OrbisFQuaternion defaultOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
controllers[*controller_id]->SetLastOrientation(defaultOrientation);
controllers[*controller_id]->SetLastUpdate(std::chrono::steady_clock::now());
controller.SetLastOrientation(defaultOrientation);
controller.SetLastUpdate(std::chrono::steady_clock::now());
return ORBIS_OK;
}
@ -612,13 +639,11 @@ int PS4_SYSV_ABI scePadSetForceIntercepted() {
}
int PS4_SYSV_ABI scePadSetLightBar(s32 handle, const OrbisPadLightBarParam* pParam) {
auto controller_id = GameControllers::GetControllerIndexFromControllerID(handle);
if (!controller_id) {
auto it = handle_to_controller_map.find(handle);
if (it == handle_to_controller_map.end()) {
return ORBIS_PAD_ERROR_INVALID_HANDLE;
}
if (GameControllers::GetControllerCustomColor(*controller_id)) {
return ORBIS_OK;
}
auto& controller = *it->second;
if (pParam != nullptr) {
LOG_DEBUG(Lib_Pad, "called handle = {} rgb = {} {} {}", handle, pParam->r, pParam->g,
pParam->b);
@ -629,7 +654,7 @@ int PS4_SYSV_ABI scePadSetLightBar(s32 handle, const OrbisPadLightBarParam* pPar
}
auto& controllers = *Common::Singleton<GameControllers>::Instance();
controllers[*controller_id]->SetLightBarRGB(pParam->r, pParam->g, pParam->b);
controller.SetLightBarRGB(pParam->r, pParam->g, pParam->b);
return ORBIS_OK;
}
return ORBIS_PAD_ERROR_INVALID_ARG;
@ -647,12 +672,12 @@ int PS4_SYSV_ABI scePadSetLightBarBlinking() {
int PS4_SYSV_ABI scePadSetLightBarForTracker(s32 handle, const OrbisPadLightBarParam* pParam) {
LOG_INFO(Lib_Pad, "called, r: {} g: {} b: {}", pParam->r, pParam->g, pParam->b);
auto controller_id = GameControllers::GetControllerIndexFromControllerID(handle);
if (!controller_id) {
auto it = handle_to_controller_map.find(handle);
if (it == handle_to_controller_map.end()) {
return ORBIS_PAD_ERROR_INVALID_HANDLE;
}
auto& controllers = *Common::Singleton<GameControllers>::Instance();
controllers[*controller_id]->SetLightBarRGB(pParam->r, pParam->g, pParam->b);
auto& controller = *it->second;
controller.SetLightBarRGB(pParam->r, pParam->g, pParam->b);
return ORBIS_OK;
}
@ -699,15 +724,16 @@ int PS4_SYSV_ABI scePadSetUserColor() {
}
int PS4_SYSV_ABI scePadSetVibration(s32 handle, const OrbisPadVibrationParam* pParam) {
auto controller_id = GameControllers::GetControllerIndexFromControllerID(handle);
if (!controller_id) {
auto it = handle_to_controller_map.find(handle);
if (it == handle_to_controller_map.end()) {
return ORBIS_PAD_ERROR_INVALID_HANDLE;
}
auto& controller = *it->second;
if (pParam != nullptr) {
LOG_DEBUG(Lib_Pad, "scePadSetVibration called handle = {} data = {} , {}", handle,
pParam->smallMotor, pParam->largeMotor);
auto& controllers = *Common::Singleton<GameControllers>::Instance();
controllers[*controller_id]->SetVibration(pParam->smallMotor, pParam->largeMotor);
controller.SetVibration(pParam->smallMotor, pParam->largeMotor);
return ORBIS_OK;
}
return ORBIS_PAD_ERROR_INVALID_ARG;

View File

@ -151,6 +151,9 @@ void GameController::UpdateAxisSmoothing() {
}
void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) {
if (override_colour.has_value()) {
return;
}
colour = {r, g, b};
if (m_sdl_gamepad != nullptr) {
SDL_SetGamepadLED(m_sdl_gamepad, r, g, b);
@ -188,9 +191,6 @@ void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, f
}
}
std::array<std::optional<Colour>, 4> GameControllers::controller_override_colors{
std::nullopt, std::nullopt, std::nullopt, std::nullopt};
void GameControllers::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime,

View File

@ -164,6 +164,7 @@ private:
std::chrono::steady_clock::time_point m_last_update = {};
Libraries::Pad::OrbisFQuaternion m_orientation = {0.0f, 0.0f, 0.0f, 1.0f};
Colour colour;
std::optional<Colour> override_colour{};
State m_state;
@ -174,8 +175,6 @@ private:
class GameControllers {
std::array<GameController*, 5> controllers;
static std::array<std::optional<Colour>, 4> controller_override_colors;
public:
GameControllers()
: controllers({new GameController(), new GameController(), new GameController(),
@ -197,14 +196,12 @@ public:
float deltaTime,
Libraries::Pad::OrbisFQuaternion& lastOrientation,
Libraries::Pad::OrbisFQuaternion& orientation);
static void SetControllerCustomColor(s32 i, u8 r, u8 g, u8 b) {
controller_override_colors[i] = {r, g, b};
}
static std::optional<Colour> GetControllerCustomColor(s32 i) {
if (i >= controller_override_colors.size()) {
return {};
}
return controller_override_colors[i];
void SetControllerCustomColor(s32 i, u8 r, u8 g, u8 b) {
// reset to ensure the next function always runs, even if there already was a preexisting
// override colour before
controllers[i]->override_colour = std::nullopt;
controllers[i]->SetLightBarRGB(r, g, b);
controllers[i]->override_colour = {r, g, b};
}
};

View File

@ -544,7 +544,8 @@ void ParseInputConfig(const std::string game_id = "") {
}
output_gamepad_id = output_gamepad_id == -1 ? 1 : output_gamepad_id;
if (enable == "true") {
GameControllers::SetControllerCustomColor(output_gamepad_id - 1, *r, *g, *b);
ControllerOutput::controllers.SetControllerCustomColor(output_gamepad_id - 1, *r,
*g, *b);
}
LOG_DEBUG(Input, "Parsed color settings: {} {} - {} {} {}",
enable == "true" ? "override" : "no override", output_gamepad_id, *r, *b, *g);