#include "libs/controller.h" #include "common/assert.h" #include "common/common.h" #include "common/logging/log.h" #include "common/stringUtils.h" #include "common/threads.h" #include "kernel/pthread.h" #include "libs/errno.h" #include "libs/libs.h" #include "libs/padData.h" #include #include #include namespace Libs::Controller { LIB_NAME("Pad", "Pad"); constexpr int PAD_ERROR_INVALID_ARG = -2137915391; /* 0x80920001 */ constexpr int PAD_ERROR_INVALID_HANDLE = -2137915389; /* 0x80920003 */ struct PadControllerInformation { float touch_pixel_density; uint16_t touch_resolution_x; uint16_t touch_resolution_y; uint8_t stick_dead_zone_left; uint8_t stick_dead_zone_right; uint8_t connection_type; uint8_t connected_count; bool connected; int device_class; uint8_t reserve[8]; }; struct PadVibrationParam { uint8_t large_motor; uint8_t small_motor; }; struct ControllerState { uint64_t time = 0; uint32_t buttons = 0; int axes[static_cast(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0}; }; class GameController { public: GameController() = default; virtual ~GameController() = default; KYTY_CLASS_NO_COPY(GameController); void Connect(int id); void Disconnect(int id); void Button(int id, uint32_t button, bool down); void Axis(int id, Axis axis, int value); void ResetInputState(); void GetConnectionInfo(bool* flag, int* count); void ReadState(ControllerState* state, bool* flag, int* count); int ReadStates(ControllerState* states, int states_num, bool* flag, int* count); private: static constexpr uint32_t STATES_MAX = 64; struct StatePrivate { bool obtained = false; }; void CheckActive(); [[nodiscard]] ControllerState GetLastState() const; void AddState(const ControllerState& state); Common::Mutex m_mutex; std::vector m_connected_ids; int m_active_id = -1; bool m_connected = false; int m_connected_count = 0; ControllerState m_states[STATES_MAX]; StatePrivate m_private[STATES_MAX]; ControllerState m_last_state; uint32_t m_states_num = 0; uint32_t m_first_state = 0; }; static GameController* g_controller = nullptr; static uint8_t pad_connected_count_to_u8(int connected_count) { return static_cast(connected_count > 255 ? 255 : connected_count); } static void pad_fill_data(PadData* data, const ControllerState& state, bool connected, int connected_count) { EXIT_IF(data == nullptr); std::memset(data, 0, sizeof(*data)); data->buttons = state.buttons; data->left_stick_x = state.axes[static_cast(Axis::LeftX)]; data->left_stick_y = state.axes[static_cast(Axis::LeftY)]; data->right_stick_x = state.axes[static_cast(Axis::RightX)]; data->right_stick_y = state.axes[static_cast(Axis::RightY)]; data->analog_buttons_l2 = state.axes[static_cast(Axis::TriggerLeft)]; data->analog_buttons_r2 = state.axes[static_cast(Axis::TriggerRight)]; data->orientation_w = 1.0f; data->touch_data_touch0_id = 1; data->touch_data_touch1_id = 2; data->connected = connected; data->timestamp = state.time; data->connected_count = pad_connected_count_to_u8(connected_count); data->device_unique_data_len = 0; } KYTY_SUBSYSTEM_INIT(Controller) { EXIT_IF(g_controller != nullptr); g_controller = new GameController; g_controller->Connect(HOST_INPUT_CONTROLLER_ID); } KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Controller) {} KYTY_SUBSYSTEM_DESTROY(Controller) {} void GameController::Connect(int id) { Common::LockGuard lock(m_mutex); if (std::find(m_connected_ids.begin(), m_connected_ids.end(), id) != m_connected_ids.end()) { return; } m_connected_ids.push_back(id); CheckActive(); } void GameController::Disconnect(int id) { Common::LockGuard lock(m_mutex); const auto it = std::find(m_connected_ids.begin(), m_connected_ids.end(), id); EXIT_IF(it == m_connected_ids.end()); m_connected_ids.erase(it); CheckActive(); } void GameController::CheckActive() { bool reset = false; int new_active_id = -1; bool new_connected = false; if (!m_connected_ids.empty()) { new_active_id = m_connected_ids[0]; for (const auto id: m_connected_ids) { if (id != HOST_INPUT_CONTROLLER_ID) { new_active_id = id; break; } } new_connected = true; } const bool was_connected = m_connected; if (m_connected != new_connected || m_active_id != new_active_id) { m_active_id = new_active_id; m_connected = new_connected; if (!was_connected && new_connected) { m_connected_count++; } reset = true; } if (reset) { m_states_num = 0; m_last_state = ControllerState(); } } ControllerState GameController::GetLastState() const { if (m_states_num == 0) { return m_last_state; } auto last = (m_first_state + m_states_num - 1) % STATES_MAX; return m_states[last]; } void GameController::AddState(const ControllerState& state) { if (m_states_num >= STATES_MAX) { m_states_num = STATES_MAX - 1; m_first_state = (m_first_state + 1) % STATES_MAX; } auto index = (m_first_state + m_states_num) % STATES_MAX; m_states[index] = state; m_last_state = state; m_private[index].obtained = false; m_states_num++; } void GameController::Button(int id, uint32_t button, bool down) { Common::LockGuard lock(m_mutex); // The keyboard shares the player-1 pad with the active gamepad. if (m_active_id == id || id == HOST_INPUT_CONTROLLER_ID) { auto state = GetLastState(); state.time = LibKernel::KernelGetProcessTime(); if (down) { state.buttons |= button; } else { state.buttons &= ~button; } AddState(state); } } void GameController::Axis(int id, Controller::Axis axis, int value) { Common::LockGuard lock(m_mutex); if (m_active_id == id || id == HOST_INPUT_CONTROLLER_ID) { auto state = GetLastState(); state.time = LibKernel::KernelGetProcessTime(); int axis_id = static_cast(axis); EXIT_IF(axis_id < 0 || axis_id >= static_cast(Controller::Axis::AxisMax)); state.axes[axis_id] = value; if (axis == Controller::Axis::TriggerLeft) { if (value > 0) { state.buttons |= PAD_BUTTON_L2; } else { state.buttons &= ~PAD_BUTTON_L2; } } if (axis == Controller::Axis::TriggerRight) { if (value > 0) { state.buttons |= PAD_BUTTON_R2; } else { state.buttons &= ~PAD_BUTTON_R2; } } AddState(state); } } void GameController::ResetInputState() { Common::LockGuard lock(m_mutex); ControllerState state {}; state.time = LibKernel::KernelGetProcessTime(); m_states_num = 0; m_first_state = 0; AddState(state); } void GameController::GetConnectionInfo(bool* flag, int* count) { EXIT_IF(flag == nullptr); EXIT_IF(count == nullptr); Common::LockGuard lock(m_mutex); *flag = m_connected; *count = m_connected_count; } void GameController::ReadState(ControllerState* state, bool* flag, int* count) { EXIT_IF(flag == nullptr); EXIT_IF(count == nullptr); EXIT_IF(state == nullptr); Common::LockGuard lock(m_mutex); *flag = m_connected; *count = m_connected_count; *state = GetLastState(); } int GameController::ReadStates(ControllerState* states, int states_num, bool* flag, int* count) { EXIT_IF(flag == nullptr); EXIT_IF(count == nullptr); EXIT_IF(states == nullptr); EXIT_IF(states_num < 1 || states_num > STATES_MAX); Common::LockGuard lock(m_mutex); *flag = m_connected; *count = m_connected_count; int ret_num = 0; if (m_connected) { if (m_states_num != 0) { for (uint32_t i = 0; i < m_states_num; i++) { if (ret_num >= states_num) { break; } auto index = (m_first_state + i) % STATES_MAX; if (!m_private[index].obtained) { m_private[index].obtained = true; states[ret_num++] = m_states[index]; } } } } return ret_num; } void ControllerConnect(int id) { EXIT_IF(g_controller == nullptr); g_controller->Connect(id); } void ControllerDisconnect(int id) { EXIT_IF(g_controller == nullptr); g_controller->Disconnect(id); } void ControllerButton(int id, uint32_t button, bool down) { EXIT_IF(g_controller == nullptr); g_controller->Button(id, button, down); } void ControllerAxis(int id, Axis axis, int value) { EXIT_IF(g_controller == nullptr); g_controller->Axis(id, axis, value); } void ControllerResetInputState() { EXIT_IF(g_controller == nullptr); g_controller->ResetInputState(); } int KYTY_SYSV_ABI PadInit() { PRINT_NAME(); return OK; } int KYTY_SYSV_ABI PadOpen(int user_id, int type, int index, const void* param) { PRINT_NAME(); LOGF("\t user_id = %d\n" "\t type = %d\n" "\t index = %d\n" "\t param = 0x%016" PRIx64 "\n", user_id, type, index, reinterpret_cast(param)); constexpr int pad_error_invalid_arg = -2137915391; /* 0x80920001 */ if (user_id != 1000 || (type != 0 && type != 2 && type != 16) || index != 0) { return pad_error_invalid_arg; } int handle = 1; return handle; } int KYTY_SYSV_ABI PadGetHandle(int user_id, int type, int index) { PRINT_NAME(); LOGF("\t user_id = %d\n" "\t type = %d\n" "\t index = %d\n", user_id, type, index); constexpr int pad_error_device_no_handle = -2137915384; /* 0x80920008 */ if (user_id != 1000 || (type != 0 && type != 2 && type != 16) || index != 0) { return pad_error_device_no_handle; } return 1; } int KYTY_SYSV_ABI PadSetMotionSensorState(int handle, bool enable) { PRINT_NAME(); if (handle != 1) { return PAD_ERROR_INVALID_HANDLE; } LOGF("\t enable = %s\n", (enable ? "true" : "false")); return OK; } int KYTY_SYSV_ABI PadSetAngularVelocityDeadbandState(int handle, bool enable) { PRINT_NAME(); if (handle != 1) { return PAD_ERROR_INVALID_HANDLE; } LOGF("\t enable = %s\n", (enable ? "true" : "false")); return OK; } int KYTY_SYSV_ABI PadResetOrientation(int handle) { PRINT_NAME(); if (handle != 1) { return PAD_ERROR_INVALID_HANDLE; } return OK; } int KYTY_SYSV_ABI PadGetControllerInformation(int handle, PadControllerInformation* info) { PRINT_NAME(); EXIT_IF(g_controller == nullptr); int connected_count = 0; bool connected = false; g_controller->GetConnectionInfo(&connected, &connected_count); if (handle != 1) { return PAD_ERROR_INVALID_HANDLE; } if (info == nullptr) { return PAD_ERROR_INVALID_ARG; } std::memset(info, 0, sizeof(*info)); info->touch_pixel_density = 44.86f; info->touch_resolution_x = 1920; info->touch_resolution_y = 943; info->stick_dead_zone_left = controller_get_axis(-32768, 32767, 8000) - 128; info->stick_dead_zone_right = controller_get_axis(-32768, 32767, 8000) - 128; info->connection_type = 0; info->connected_count = pad_connected_count_to_u8(connected_count); info->connected = connected; info->device_class = 0; return OK; } int KYTY_SYSV_ABI PadReadState(int handle, PadData* data) { PRINT_NAME(); if (handle != 1) { return PAD_ERROR_INVALID_HANDLE; } if (data == nullptr) { return PAD_ERROR_INVALID_ARG; } EXIT_IF(g_controller == nullptr); int connected_count = 0; bool connected = false; ControllerState state; g_controller->ReadState(&state, &connected, &connected_count); pad_fill_data(data, state, connected, connected_count); return OK; } int KYTY_SYSV_ABI PadRead(int handle, PadData* data, int num) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(num < 1 || num > 64); if (handle != 1) { return PAD_ERROR_INVALID_HANDLE; } if (data == nullptr) { return PAD_ERROR_INVALID_ARG; } std::memset(data, 0, sizeof(PadData) * static_cast(num)); EXIT_IF(g_controller == nullptr); int connected_count = 0; bool connected = false; ControllerState states[64] = {}; int ret_num = g_controller->ReadStates(states, num, &connected, &connected_count); if (!connected || ret_num == 0) { if (connected) { g_controller->ReadState(&states[0], &connected, &connected_count); } ret_num = 1; } for (int i = 0; i < ret_num; i++) { pad_fill_data(&data[i], states[i], connected, connected_count); } return ret_num; } int KYTY_SYSV_ABI PadSetVibration(int handle, const PadVibrationParam* param) { PRINT_NAME(); if (handle != 1) { return PAD_ERROR_INVALID_HANDLE; } if (param == nullptr) { return PAD_ERROR_INVALID_ARG; } LOGF("\t large_motor = %d\n" "\t small_motor = %d\n", static_cast(param->large_motor), static_cast(param->small_motor)); return OK; } int KYTY_SYSV_ABI PadResetLightBar(int handle) { PRINT_NAME(); if (handle != 1) { return PAD_ERROR_INVALID_HANDLE; } return OK; } int KYTY_SYSV_ABI PadSetLightBar(int handle, const PadLightBarParam* param) { PRINT_NAME(); if (handle != 1) { return PAD_ERROR_INVALID_HANDLE; } if (param == nullptr) { return PAD_ERROR_INVALID_ARG; } return OK; } } // namespace Libs::Controller