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

View File

@ -151,6 +151,9 @@ void GameController::UpdateAxisSmoothing() {
} }
void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) { void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) {
if (override_colour.has_value()) {
return;
}
colour = {r, g, b}; colour = {r, g, b};
if (m_sdl_gamepad != nullptr) { if (m_sdl_gamepad != nullptr) {
SDL_SetGamepadLED(m_sdl_gamepad, r, g, b); 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, void GameControllers::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity, Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime, float deltaTime,

View File

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

View File

@ -544,7 +544,8 @@ void ParseInputConfig(const std::string game_id = "") {
} }
output_gamepad_id = output_gamepad_id == -1 ? 1 : output_gamepad_id; output_gamepad_id = output_gamepad_id == -1 ? 1 : output_gamepad_id;
if (enable == "true") { 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: {} {} - {} {} {}", LOG_DEBUG(Input, "Parsed color settings: {} {} - {} {} {}",
enable == "true" ? "override" : "no override", output_gamepad_id, *r, *b, *g); enable == "true" ? "override" : "no override", output_gamepad_id, *r, *b, *g);