From cc943db117646f1297889f1ca582c01a3ff4d64a Mon Sep 17 00:00:00 2001 From: raziel1000 Date: Tue, 16 Jul 2024 23:58:30 -0600 Subject: [PATCH] WIP: - Initial gamepad support. - Initial keyboard mapping support using config.toml --- CMakeLists.txt | 5 + src/common/config.cpp | 92 ++++++++++++++++ src/common/config.h | 6 ++ src/core/libraries/pad/pad.cpp | 104 ++++++++++-------- src/input/gamepad.cpp | 102 ++++++++++++++++++ src/input/gamepad.h | 32 ++++++ src/input/input_manager.h | 20 ++++ src/input/keyboard.cpp | 188 +++++++++++++++++++++++++++++++++ src/input/keyboard.h | 31 ++++++ src/sdl_window.cpp | 12 +-- 10 files changed, 543 insertions(+), 49 deletions(-) create mode 100644 src/input/gamepad.cpp create mode 100644 src/input/gamepad.h create mode 100644 src/input/input_manager.h create mode 100644 src/input/keyboard.cpp create mode 100644 src/input/keyboard.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bd38f64..c7a06540 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -466,6 +466,11 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp set(INPUT src/input/controller.cpp src/input/controller.h + src/input/gamepad.cpp + src/input/gamepad.h + src/input/keyboard.cpp + src/input/keyboard.h + src/input/input_manager.h ) set(EMULATOR src/emulator.cpp diff --git a/src/common/config.cpp b/src/common/config.cpp index a577b143..864f6a97 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -42,6 +42,33 @@ std::vector m_pkg_viewer; std::vector m_elf_viewer; std::vector m_recent_files; +// Keyboard +static std::string Up = "UP"; +static std::string Down = "DOWN"; +static std::string Left = "LEFT"; +static std::string Right = "RIGHT"; +static std::string Cross = "S"; +static std::string Triangle = "W"; +static std::string Square = "D"; +static std::string Circle = "A"; +static std::string L1 = "Q"; +static std::string R1 = "E"; +static std::string L2 = "R"; +static std::string R2 = "F"; +static std::string L3 = "LCTRL"; +static std::string R3 = "LSHIFT"; +static std::string Options = "1"; +static std::string LStickUp = "I"; +static std::string LStickDown = "K"; +static std::string LStickLeft = "J"; +static std::string LStickRight = "L"; +static std::string RStickUp = "V"; +static std::string RStickDown = "B"; +static std::string RStickLeft = "N"; +static std::string RStickRight = "M"; +std::unordered_map keyMappings; +static u32 controller = 0; + bool isLleLibc() { return isLibc; } @@ -102,6 +129,18 @@ bool vkValidationSyncEnabled() { return vkValidationSync; } +std::unordered_map getKeyMap() { + return keyMappings; +} + +void setControllerType(u32 type) { + controller = type; +} + +u32 getControllerType() { + return controller; +} + void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_x = x; main_window_geometry_y = y; @@ -224,6 +263,7 @@ void load(const std::filesystem::path& path) { logFilter = toml::find_or(general, "logFilter", ""); logType = toml::find_or(general, "logType", "sync"); isShowSplash = toml::find_or(general, "showSplash", true); + controller = toml::find_or(general, "controller", 0); } } if (data.contains("GPU")) { @@ -287,6 +327,35 @@ void load(const std::filesystem::path& path) { m_table_mode = toml::find_or(gui, "gameTableMode", 0); } } + if (data.contains("Controller")) { + auto generalResult = toml::expect(data.at("Controller")); + if (generalResult.is_ok()) { + auto general = generalResult.unwrap(); + keyMappings["Up"] = (toml::find_or(general, "Up", "")); + keyMappings["Down"] = (toml::find_or(general, "Down", "")); + keyMappings["Left"] = toml::find_or(general, "Left", ""); + keyMappings["Right"] = toml::find_or(general, "Right", ""); + keyMappings["Cross"] = toml::find_or(general, "Cross", ""); + keyMappings["Triangle"] = toml::find_or(general, "Triangle", ""); + keyMappings["Square"] = toml::find_or(general, "Square", ""); + keyMappings["Circle"] = toml::find_or(general, "Circle", ""); + keyMappings["L1"] = toml::find_or(general, "L1", ""); + keyMappings["R1"] = toml::find_or(general, "R1", ""); + keyMappings["L2"] = toml::find_or(general, "L2", ""); + keyMappings["R2"] = toml::find_or(general, "R2", ""); + keyMappings["L3"] = toml::find_or(general, "L3", ""); + keyMappings["R3"] = toml::find_or(general, "R3", ""); + keyMappings["Options"] = toml::find_or(general, "Options", ""); + keyMappings["LStickUp"] = toml::find_or(general, "LStickUp", ""); + keyMappings["LStickDown"] = toml::find_or(general, "LStickDown", ""); + keyMappings["LStickLeft"] = toml::find_or(general, "LStickLeft", ""); + keyMappings["LStickRight"] = toml::find_or(general, "LStickRight", ""); + keyMappings["RStickUp"] = toml::find_or(general, "RStickUp", ""); + keyMappings["RStickDown"] = toml::find_or(general, "RStickDown", ""); + keyMappings["RStickLeft"] = toml::find_or(general, "RStickLeft", ""); + keyMappings["RStickRight"] = toml::find_or(general, "RStickRight", ""); + } + } } void save(const std::filesystem::path& path) { toml::basic_value data; @@ -338,6 +407,29 @@ void save(const std::filesystem::path& path) { data["GUI"]["pkgDirs"] = m_pkg_viewer; data["GUI"]["elfDirs"] = m_elf_viewer; data["GUI"]["recentFiles"] = m_recent_files; + data["Controller"]["Up"] = Up; + data["Controller"]["Down"] = Down; + data["Controller"]["Left"] = Left; + data["Controller"]["Right"] = Right; + data["Controller"]["Cross"] = Cross; + data["Controller"]["Triangle"] = Triangle; + data["Controller"]["Square"] = Square; + data["Controller"]["Circle"] = Circle; + data["Controller"]["L1"] = L1; + data["Controller"]["R1"] = R1; + data["Controller"]["L2"] = L2; + data["Controller"]["R2"] = R2; + data["Controller"]["L3"] = L3; + data["Controller"]["R3"] = R3; + data["Controller"]["Options"] = Options; + data["Controller"]["LStickUp"] = LStickUp; + data["Controller"]["LStickDown"] = LStickDown; + data["Controller"]["LStickLeft"] = LStickLeft; + data["Controller"]["LStickRight"] = LStickRight; + data["Controller"]["RStickUp"] = RStickUp; + data["Controller"]["RStickDown"] = RStickDown; + data["Controller"]["RStickLeft"] = RStickLeft; + data["Controller"]["RStickRight"] = RStickRight; std::ofstream file(path, std::ios::out); file << data; diff --git a/src/common/config.h b/src/common/config.h index 0a3b4905..cc564e30 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include "types.h" @@ -30,6 +31,11 @@ bool dumpPM4(); bool vkValidationEnabled(); bool vkValidationSyncEnabled(); +// Controllers +std::unordered_map getKeyMap(); +void setControllerType(u32 type); +u32 getControllerType(); + // Gui void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h); void setGameInstallDir(const std::string& dir); diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index e318e152..7ee6ffe6 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -5,11 +5,17 @@ #include #include "common/logging/log.h" #include "core/libraries/error_codes.h" +#include "core/libraries/kernel/time_management.h" #include "core/libraries/libs.h" #include "input/controller.h" +#include "input/gamepad.h" +#include "input/input_manager.h" +#include "input/keyboard.h" #include "pad.h" namespace Libraries::Pad { +std::unique_ptr controller; +OrbisPadData oldData; int PS4_SYSV_ABI scePadClose(s32 handle) { LOG_ERROR(Lib_Pad, "(STUBBED) called"); @@ -174,6 +180,13 @@ int PS4_SYSV_ABI scePadGetVersionInfo() { } int PS4_SYSV_ABI scePadInit() { + bool is_keyboard = (Config::getControllerType() == 0); + if (is_keyboard) { + controller = std::unique_ptr(Common::Singleton::Instance()); + } else { + controller = std::unique_ptr(Common::Singleton::Instance()); + } + controller->Init(); LOG_ERROR(Lib_Pad, "(STUBBED) called"); return ORBIS_OK; } @@ -239,25 +252,18 @@ int PS4_SYSV_ABI scePadOutputReport() { } int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { + //LOG_ERROR(Lib_Pad, "(STUBBED) called"); + // Temporary implementation. will be fixed and accurate later. std::memset(pData, 0, sizeof(OrbisPadData)); - int connected_count = 0; - bool connected = false; - Input::State states[64]; - auto* controller = Common::Singleton::Instance(); - int ret_num = controller->ReadStates(states, num, &connected, &connected_count); - - if (!connected) { - ret_num = 1; - } - - for (int i = 0; i < ret_num; i++) { - pData[i].buttons = states[i].buttonsState; - pData[i].leftStick.x = states[i].axes[static_cast(Input::Axis::LeftX)]; - pData[i].leftStick.y = states[i].axes[static_cast(Input::Axis::LeftY)]; - pData[i].rightStick.x = states[i].axes[static_cast(Input::Axis::RightX)]; - pData[i].rightStick.y = states[i].axes[static_cast(Input::Axis::RightY)]; - pData[i].analogButtons.l2 = states[i].axes[static_cast(Input::Axis::TriggerLeft)]; - pData[i].analogButtons.r2 = states[i].axes[static_cast(Input::Axis::TriggerRight)]; + InputState state; + for (int i = 0; i < num; i++) { + pData[i].buttons = controller->getButtonState(&state); + pData[i].leftStick.x = state.lx; + pData[i].leftStick.y = state.ly; + pData[i].rightStick.x = state.rx; + pData[i].rightStick.y = state.ry; + pData[i].analogButtons.l2 = state.lt; + pData[i].analogButtons.r2 = state.rt; pData[i].orientation.x = 0.0f; pData[i].orientation.y = 0.0f; pData[i].orientation.z = 0.0f; @@ -275,12 +281,22 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { pData[i].touchData.touch[1].x = 0; pData[i].touchData.touch[1].y = 0; pData[i].touchData.touch[1].id = 2; - pData[i].connected = connected; - pData[i].timestamp = states[i].time; - pData[i].connectedCount = connected_count; + pData[i].connected = true; + pData[i].timestamp = Libraries::Kernel::sceKernelGetProcessTime(); + pData[i].connectedCount = 1; pData[i].deviceUniqueDataLen = 0; } + // temp ugly return. will be fixed. + int ret_num = 0; + const u8* d1 = reinterpret_cast(&pData[0]); + const u8* d2 = reinterpret_cast(&oldData); + for (int j = 0; j < sizeof(OrbisPadData); j++) { + if (d1[j] != d2[j]) { + ret_num++; + } + } + oldData = pData[0]; return ret_num; } @@ -305,24 +321,20 @@ int PS4_SYSV_ABI scePadReadHistory() { } int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { - auto* controller = Common::Singleton::Instance(); - - int connectedCount = 0; - bool isConnected = false; - Input::State state; + LOG_ERROR(Lib_Pad, "(STUBBED) called"); std::memset(pData, 0, sizeof(OrbisPadData)); - controller->ReadState(&state, &isConnected, &connectedCount); - pData->buttons = state.buttonsState; - pData->leftStick.x = state.axes[static_cast(Input::Axis::LeftX)]; - pData->leftStick.y = state.axes[static_cast(Input::Axis::LeftY)]; - pData->rightStick.x = state.axes[static_cast(Input::Axis::RightX)]; - pData->rightStick.y = state.axes[static_cast(Input::Axis::RightY)]; - pData->analogButtons.l2 = state.axes[static_cast(Input::Axis::TriggerLeft)]; - pData->analogButtons.r2 = state.axes[static_cast(Input::Axis::TriggerRight)]; - pData->orientation.x = 0; - pData->orientation.y = 0; - pData->orientation.z = 0; - pData->orientation.w = 0; + InputState state; + pData->buttons = controller->getButtonState(&state); + pData->leftStick.x = state.lx; + pData->leftStick.y = state.ly; + pData->rightStick.x = state.rx; + pData->rightStick.y = state.ry; + pData->analogButtons.l2 = state.lt; + pData->analogButtons.r2 = state.rt; + pData->orientation.x = 0.0f; + pData->orientation.y = 0.0f; + pData->orientation.z = 0.0f; + pData->orientation.w = 1.0f; pData->acceleration.x = 0.0f; pData->acceleration.y = 0.0f; pData->acceleration.z = 0.0f; @@ -336,9 +348,9 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { pData->touchData.touch[1].x = 0; pData->touchData.touch[1].y = 0; pData->touchData.touch[1].id = 2; - pData->timestamp = state.time; - pData->connected = true; // isConnected; //TODO fix me proper - pData->connectedCount = 1; // connectedCount; + pData->connected = true; + pData->timestamp = Libraries::Kernel::sceKernelGetProcessTime(); + pData->connectedCount = 1; pData->deviceUniqueDataLen = 0; return SCE_OK; @@ -470,8 +482,14 @@ int PS4_SYSV_ABI scePadSetUserColor() { } int PS4_SYSV_ABI scePadSetVibration(s32 handle, const OrbisPadVibrationParam* pParam) { - LOG_ERROR(Lib_Pad, "(STUBBED) called"); - return ORBIS_OK; + int result = 0x80920001; + if (pParam != nullptr) { + LOG_INFO(Lib_Pad, "scePadSetVibration called handle = {}", handle); + u16 smallFreq = (u16)(((float)pParam->smallMotor / 255.0f) * 65535.0f); + u16 bigFreq = (u16)(((float)pParam->largeMotor / 255.0f) * 65535.0f); + result = (Config::getControllerType() == 1) ? controller->GetRumble(smallFreq, bigFreq) : 0; + } + return result; } int PS4_SYSV_ABI scePadSetVibrationForce() { diff --git a/src/input/gamepad.cpp b/src/input/gamepad.cpp new file mode 100644 index 00000000..f01dcfa5 --- /dev/null +++ b/src/input/gamepad.cpp @@ -0,0 +1,102 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +Gamepad::Gamepad() {} + +void Gamepad::Init() { + int count; + SDL_JoystickID* joysticks = SDL_GetJoysticks(&count); + SDL_JoystickID instance_id = joysticks[0]; + gamepad = SDL_OpenGamepad(instance_id); +} + +int Gamepad::GetRumble(u16 smallFreq, u16 bigFreq) { + return gamepad ? SDL_RumbleGamepad(gamepad, smallFreq, bigFreq, -1) : 0; +} + +u32 Gamepad::getButtonState(InputState* state) { + std::unique_lock lock(m_mutex); + u32 buttons = 0; + if (gamepad) { + buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_STICK) + ? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3) + : 0; + buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_STICK) + ? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3) + : 0; + buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START) + ? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS) + : 0; + buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP) + ? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP) + : 0; + buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT) + ? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT) + : 0; + buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN) + ? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN) + : 0; + buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT) + ? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT) + : 0; + buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER) + ? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L1) + : 0; + buttons |= SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) + ? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L2) + : 0; + buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER) + ? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1) + : 0; + buttons |= SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) + ? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2) + : 0; + buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_NORTH) + ? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE) + : 0; + buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_EAST) + ? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE) + : 0; + buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH) + ? Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS + : 0; + buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST) + ? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE) + : 0; + } + u16 deadzone = 0; // leave as 0 for now. + s16 leftx = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); + s16 lefty = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); + s16 rightx = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX); + s16 righty = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY); + s16 triggerL = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER); + s16 triggerR = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER); + + state->lx = static_cast((((std::abs(leftx) > deadzone) ? leftx : 128) + 32768) / 257); + state->ly = static_cast((((std::abs(lefty) > deadzone) ? lefty : 128) + 32768) / 257); + state->rx = static_cast((((std::abs(rightx) > deadzone) ? rightx : 128) + 32768) / 257); + state->ry = static_cast((((std::abs(righty) > deadzone) ? righty : 128) + 32768) / 257); + state->lt = static_cast((triggerL + 32768) / 65535) * 255; + state->rt = static_cast((triggerR + 32768) / 65535) * 255; + return buttons; +} + +// use this later. maybe. +void Gamepad::getAxis(InputState* state) { + u16 deadzone = 2500; // Tested with my pdp Xbox One Controller. maybe this should be an option. + s16 leftx = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); + s16 lefty = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); + s16 rightx = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX); + s16 righty = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY); + s16 triggerL = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER); + s16 triggerR = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER); + + state->lx = static_cast((((std::abs(leftx) > deadzone) ? leftx : 128) + 32768) / 257); + state->ly = static_cast((((std::abs(lefty) > deadzone) ? lefty : 128) + 32768) / 257); + state->rx = static_cast((((std::abs(rightx) > deadzone) ? rightx : 128) + 32768) / 257); + state->ry = static_cast((((std::abs(righty) > deadzone) ? righty : 128) + 32768) / 257); + state->lt = static_cast((triggerL + 32768) / 65535) * 255; + state->rt = static_cast((triggerR + 32768) / 65535) * 255; +} \ No newline at end of file diff --git a/src/input/gamepad.h b/src/input/gamepad.h new file mode 100644 index 00000000..c8ceb394 --- /dev/null +++ b/src/input/gamepad.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include + +#include "common/config.h" +#include "common/io_file.h" +#include "common/logging/log.h" +#include "common/path_util.h" +#include "core/libraries/pad/pad.h" +#include "input_manager.h" + +class Gamepad : public InputManager { +public: + Gamepad(); + virtual ~Gamepad() = default; + + virtual void Init() override; + virtual u32 getButtonState(InputState* state) override; + virtual void getAxis(InputState* state) override; + virtual int GetRumble(u16 smallFreq, u16 bigFreq) override; + +private: + SDL_Gamepad* gamepad; + std::mutex m_mutex; +}; diff --git a/src/input/input_manager.h b/src/input/input_manager.h new file mode 100644 index 00000000..4f65b6b6 --- /dev/null +++ b/src/input/input_manager.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include +#include "common/types.h" + +struct InputState { + u8 lx = 128, ly = 128, rx = 128, ry = 128, lt = 0, rt = 0; +}; + +class InputManager { +public: + virtual ~InputManager() = default; + + virtual u32 getButtonState(InputState* state) = 0; + virtual void getAxis(InputState* state) = 0; + virtual void Init() = 0; + virtual int GetRumble(u16 smallFreq, u16 bigFreq) = 0; +}; \ No newline at end of file diff --git a/src/input/keyboard.cpp b/src/input/keyboard.cpp new file mode 100644 index 00000000..ac940005 --- /dev/null +++ b/src/input/keyboard.cpp @@ -0,0 +1,188 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include + +SDL_Keycode GetKeyFromString(const std::string& keyString) { + const std::unordered_map keyMap = { + {"0", SDL_SCANCODE_0}, + {"1", SDL_SCANCODE_1}, + {"2", SDL_SCANCODE_2}, + {"3", SDL_SCANCODE_3}, + {"4", SDL_SCANCODE_4}, + {"5", SDL_SCANCODE_5}, + {"6", SDL_SCANCODE_6}, + {"7", SDL_SCANCODE_7}, + {"8", SDL_SCANCODE_8}, + {"9", SDL_SCANCODE_9}, + {"KP_0", SDL_SCANCODE_KP_0}, + {"KP_1", SDL_SCANCODE_KP_1}, + {"KP_2", SDL_SCANCODE_KP_2}, + {"KP_3", SDL_SCANCODE_KP_3}, + {"KP_4", SDL_SCANCODE_KP_4}, + {"KP_5", SDL_SCANCODE_KP_5}, + {"KP_6", SDL_SCANCODE_KP_6}, + {"KP_7", SDL_SCANCODE_KP_7}, + {"KP_8", SDL_SCANCODE_KP_8}, + {"KP_9", SDL_SCANCODE_KP_9}, + {"A", SDL_SCANCODE_A}, + {"B", SDL_SCANCODE_B}, + {"C", SDL_SCANCODE_C}, + {"D", SDL_SCANCODE_D}, + {"E", SDL_SCANCODE_E}, + {"F", SDL_SCANCODE_F}, + {"G", SDL_SCANCODE_G}, + {"H", SDL_SCANCODE_H}, + {"I", SDL_SCANCODE_I}, + {"J", SDL_SCANCODE_J}, + {"K", SDL_SCANCODE_K}, + {"L", SDL_SCANCODE_L}, + {"M", SDL_SCANCODE_M}, + {"N", SDL_SCANCODE_N}, + {"O", SDL_SCANCODE_O}, + {"P", SDL_SCANCODE_P}, + {"Q", SDL_SCANCODE_Q}, + {"R", SDL_SCANCODE_R}, + {"S", SDL_SCANCODE_S}, + {"T", SDL_SCANCODE_T}, + {"U", SDL_SCANCODE_U}, + {"V", SDL_SCANCODE_V}, + {"W", SDL_SCANCODE_W}, + {"X", SDL_SCANCODE_X}, + {"Y", SDL_SCANCODE_Y}, + {"Z", SDL_SCANCODE_Z}, + {"Left Ctrl", SDL_SCANCODE_LCTRL}, + {"Left Shift", SDL_SCANCODE_LSHIFT}, + {"Right Ctrl", SDL_SCANCODE_RCTRL}, + {"Right Shift", SDL_SCANCODE_RSHIFT}, + {"Up", SDL_SCANCODE_UP}, + {"Down", SDL_SCANCODE_DOWN}, + {"Left", SDL_SCANCODE_LEFT}, + {"Right", SDL_SCANCODE_RIGHT}, + }; + + auto it = keyMap.find(keyString); + if (it != keyMap.end()) { + return SDL_GetKeyFromScancode(it->second, SDL_KMOD_NONE); + } else { + return SDL_GetKeyFromName(keyString.c_str()); + } +} + +void Keyboard::initializeSdlKeyMappings( + const std::unordered_map& mappings, + std::unordered_map& keyMappings) { + const std::array actions = { + "Up", "Down", "Left", "Right", "Cross", "Square", + "Triangle", "Circle", "L1", "R1", "L2", "R2", + "L3", "R3", "Options", "LStickUp", "LStickDown", "LStickLeft", + "LStickRight", "RStickUp", "RStickDown", "RStickLeft", "RStickRight"}; + + for (const auto& action : actions) { + auto it = mappings.find(action); + if (it != mappings.end()) { + keyMappings[action] = GetKeyFromString(it->second); + } + } +} + +void Keyboard::Init() { + const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); + Config::load(config_dir / "config.toml"); + config_key_map = Config::getKeyMap(); + initializeSdlKeyMappings(config_key_map, sdl_key_map); +} + +Keyboard::Keyboard() {} + +u32 Keyboard::getButtonState(InputState* state) { + const Uint8* keystate = SDL_GetKeyboardState(NULL); + u32 buttons = 0; + u8 lx = 128, ly = 128, rx = 128, ry = 128; + + if (keystate) { + for (const auto& key : sdl_key_map) { + if (keystate[SDL_GetScancodeFromKey(key.second, SDL_KMOD_NONE)]) { + if (key.first == "Up") + buttons |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP; + else if (key.first == "Right") + buttons |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT; + else if (key.first == "Down") + buttons |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN; + else if (key.first == "Left") + buttons |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT; + else if (key.first == "Cross") { + buttons |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS; + } else if (key.first == "Square") + buttons |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE; + else if (key.first == "Triangle") + buttons |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE; + else if (key.first == "Circle") + buttons |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE; + else if (key.first == "L1") + buttons |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L1; + else if (key.first == "R1") + buttons |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1; + else if (key.first == "L2") + buttons |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L2; + else if (key.first == "R2") + buttons |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2; + else if (key.first == "L3") + buttons |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3; + else if (key.first == "R3") + buttons |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3; + else if (key.first == "Options") + buttons |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS; + else if (key.first == "LStickUp") + state->ly = 0; + else if (key.first == "LStickDown") + state->ly = 255; + else if (key.first == "LStickLeft") + state->lx = 0; + else if (key.first == "LStickRight") + state->lx = 255; + else if (key.first == "RStickUp") + state->ry = 0; + else if (key.first == "RStickDown") + state->ry = 255; + else if (key.first == "RStickLeft") + state->rx = 0; + else if (key.first == "RStickRight") + state->rx = 255; + } + } + } + return buttons; +} + +void Keyboard::getAxis(InputState* state) { + const Uint8* keystate = SDL_GetKeyboardState(NULL); + if (keystate) { + for (const auto& key : sdl_key_map) { + if (keystate[SDL_GetScancodeFromKey(key.second, SDL_KMOD_NONE)]) { + if (key.first == "LStickUp") + state->ly = 0; + else if (key.first == "LStickDown") + state->ly = 255; + else if (key.first == "LStickLeft") + state->lx = 0; + else if (key.first == "LStickRight") + state->lx = 255; + else if (key.first == "RStickUp") + state->ry = 0; + else if (key.first == "RStickDown") + state->ry = 255; + else if (key.first == "RStickLeft") + state->rx = 0; + else if (key.first == "RStickRight") + state->rx = 255; + } + } + } +} + +int Keyboard::GetRumble(u16 smallFreq, u16 bigFreq) { + return 0; +} \ No newline at end of file diff --git a/src/input/keyboard.h b/src/input/keyboard.h new file mode 100644 index 00000000..8e94bb74 --- /dev/null +++ b/src/input/keyboard.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include "core/libraries/pad/pad.h" +#include "input_manager.h" + +#include "common/config.h" +#include "common/io_file.h" +#include "common/logging/log.h" +#include "common/path_util.h" + +class Keyboard : public InputManager { +public: + Keyboard(); + virtual ~Keyboard() = default; + + virtual u32 getButtonState(InputState* state) override; + virtual void getAxis(InputState* state) override; + virtual void Init() override; + virtual int GetRumble(u16 smallFreq, u16 bigFreq) override; + +private: + std::unordered_map config_key_map; + void initializeSdlKeyMappings(const std::unordered_map& mappings, + std::unordered_map& keyMappings); + std::unordered_map sdl_key_map; +}; diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index d4da268a..2c39147b 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -19,7 +19,7 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_ if (SDL_Init(SDL_INIT_VIDEO) < 0) { UNREACHABLE_MSG("Failed to initialize SDL video subsystem: {}", SDL_GetError()); } - SDL_InitSubSystem(SDL_INIT_AUDIO); + SDL_InitSubSystem(SDL_INIT_AUDIO | SDL_INIT_GAMEPAD | SDL_INIT_JOYSTICK); const std::string title = "shadPS4 v" + std::string(Common::VERSION); SDL_PropertiesID props = SDL_CreateProperties(); @@ -63,7 +63,7 @@ WindowSDL::~WindowSDL() = default; void WindowSDL::waitEvent() { // Called on main thread SDL_Event event; - + SDL_PumpEvents(); if (!SDL_PollEvent(&event)) { return; } @@ -79,10 +79,10 @@ void WindowSDL::waitEvent() { is_shown = event.type == SDL_EVENT_WINDOW_EXPOSED; onResize(); break; - case SDL_EVENT_KEY_DOWN: - case SDL_EVENT_KEY_UP: - onKeyPress(&event); - break; + // case SDL_EVENT_KEY_DOWN: + // case SDL_EVENT_KEY_UP: + // onKeyPress(&event); + // break; case SDL_EVENT_QUIT: is_open = false; break;