WIP: - Initial gamepad support.

- Initial keyboard mapping support using config.toml
This commit is contained in:
raziel1000 2024-07-16 23:58:30 -06:00
parent 7ad44317f8
commit cc943db117
10 changed files with 543 additions and 49 deletions

View File

@ -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

View File

@ -42,6 +42,33 @@ std::vector<std::string> m_pkg_viewer;
std::vector<std::string> m_elf_viewer;
std::vector<std::string> 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<std::string, std::string> keyMappings;
static u32 controller = 0;
bool isLleLibc() {
return isLibc;
}
@ -102,6 +129,18 @@ bool vkValidationSyncEnabled() {
return vkValidationSync;
}
std::unordered_map<std::string, std::string> 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<toml::string>(general, "logFilter", "");
logType = toml::find_or<toml::string>(general, "logType", "sync");
isShowSplash = toml::find_or<toml::boolean>(general, "showSplash", true);
controller = toml::find_or<toml::integer>(general, "controller", 0);
}
}
if (data.contains("GPU")) {
@ -287,6 +327,35 @@ void load(const std::filesystem::path& path) {
m_table_mode = toml::find_or<toml::integer>(gui, "gameTableMode", 0);
}
}
if (data.contains("Controller")) {
auto generalResult = toml::expect<toml::value>(data.at("Controller"));
if (generalResult.is_ok()) {
auto general = generalResult.unwrap();
keyMappings["Up"] = (toml::find_or<toml::string>(general, "Up", ""));
keyMappings["Down"] = (toml::find_or<toml::string>(general, "Down", ""));
keyMappings["Left"] = toml::find_or<toml::string>(general, "Left", "");
keyMappings["Right"] = toml::find_or<toml::string>(general, "Right", "");
keyMappings["Cross"] = toml::find_or<toml::string>(general, "Cross", "");
keyMappings["Triangle"] = toml::find_or<toml::string>(general, "Triangle", "");
keyMappings["Square"] = toml::find_or<toml::string>(general, "Square", "");
keyMappings["Circle"] = toml::find_or<toml::string>(general, "Circle", "");
keyMappings["L1"] = toml::find_or<toml::string>(general, "L1", "");
keyMappings["R1"] = toml::find_or<toml::string>(general, "R1", "");
keyMappings["L2"] = toml::find_or<toml::string>(general, "L2", "");
keyMappings["R2"] = toml::find_or<toml::string>(general, "R2", "");
keyMappings["L3"] = toml::find_or<toml::string>(general, "L3", "");
keyMappings["R3"] = toml::find_or<toml::string>(general, "R3", "");
keyMappings["Options"] = toml::find_or<toml::string>(general, "Options", "");
keyMappings["LStickUp"] = toml::find_or<toml::string>(general, "LStickUp", "");
keyMappings["LStickDown"] = toml::find_or<toml::string>(general, "LStickDown", "");
keyMappings["LStickLeft"] = toml::find_or<toml::string>(general, "LStickLeft", "");
keyMappings["LStickRight"] = toml::find_or<toml::string>(general, "LStickRight", "");
keyMappings["RStickUp"] = toml::find_or<toml::string>(general, "RStickUp", "");
keyMappings["RStickDown"] = toml::find_or<toml::string>(general, "RStickDown", "");
keyMappings["RStickLeft"] = toml::find_or<toml::string>(general, "RStickLeft", "");
keyMappings["RStickRight"] = toml::find_or<toml::string>(general, "RStickRight", "");
}
}
}
void save(const std::filesystem::path& path) {
toml::basic_value<toml::preserve_comments> 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;

View File

@ -4,6 +4,7 @@
#pragma once
#include <filesystem>
#include <unordered_map>
#include <vector>
#include "types.h"
@ -30,6 +31,11 @@ bool dumpPM4();
bool vkValidationEnabled();
bool vkValidationSyncEnabled();
// Controllers
std::unordered_map<std::string, std::string> getKeyMap();
void setControllerType(u32 type);
u32 getControllerType();
// Gui
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h);
void setGameInstallDir(const std::string& dir);

View File

@ -5,11 +5,17 @@
#include <common/singleton.h>
#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<InputManager> 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<InputManager>(Common::Singleton<Keyboard>::Instance());
} else {
controller = std::unique_ptr<InputManager>(Common::Singleton<Gamepad>::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<Input::GameController>::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<int>(Input::Axis::LeftX)];
pData[i].leftStick.y = states[i].axes[static_cast<int>(Input::Axis::LeftY)];
pData[i].rightStick.x = states[i].axes[static_cast<int>(Input::Axis::RightX)];
pData[i].rightStick.y = states[i].axes[static_cast<int>(Input::Axis::RightY)];
pData[i].analogButtons.l2 = states[i].axes[static_cast<int>(Input::Axis::TriggerLeft)];
pData[i].analogButtons.r2 = states[i].axes[static_cast<int>(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<u8*>(&pData[0]);
const u8* d2 = reinterpret_cast<u8*>(&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<Input::GameController>::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<int>(Input::Axis::LeftX)];
pData->leftStick.y = state.axes[static_cast<int>(Input::Axis::LeftY)];
pData->rightStick.x = state.axes[static_cast<int>(Input::Axis::RightX)];
pData->rightStick.y = state.axes[static_cast<int>(Input::Axis::RightY)];
pData->analogButtons.l2 = state.axes[static_cast<int>(Input::Axis::TriggerLeft)];
pData->analogButtons.r2 = state.axes[static_cast<int>(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() {

102
src/input/gamepad.cpp Normal file
View File

@ -0,0 +1,102 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <input/gamepad.h>
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<std::mutex> 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<u8>((((std::abs(leftx) > deadzone) ? leftx : 128) + 32768) / 257);
state->ly = static_cast<u8>((((std::abs(lefty) > deadzone) ? lefty : 128) + 32768) / 257);
state->rx = static_cast<u8>((((std::abs(rightx) > deadzone) ? rightx : 128) + 32768) / 257);
state->ry = static_cast<u8>((((std::abs(righty) > deadzone) ? righty : 128) + 32768) / 257);
state->lt = static_cast<u8>((triggerL + 32768) / 65535) * 255;
state->rt = static_cast<u8>((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<u8>((((std::abs(leftx) > deadzone) ? leftx : 128) + 32768) / 257);
state->ly = static_cast<u8>((((std::abs(lefty) > deadzone) ? lefty : 128) + 32768) / 257);
state->rx = static_cast<u8>((((std::abs(rightx) > deadzone) ? rightx : 128) + 32768) / 257);
state->ry = static_cast<u8>((((std::abs(righty) > deadzone) ? righty : 128) + 32768) / 257);
state->lt = static_cast<u8>((triggerL + 32768) / 65535) * 255;
state->rt = static_cast<u8>((triggerR + 32768) / 65535) * 255;
}

32
src/input/gamepad.h Normal file
View File

@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include <string>
#include <unordered_map>
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_init.h>
#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;
};

20
src/input/input_manager.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <memory>
#include <vector>
#include <SDL3/SDL.h>
#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;
};

188
src/input/keyboard.cpp Normal file
View File

@ -0,0 +1,188 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_init.h>
#include <input/keyboard.h>
SDL_Keycode GetKeyFromString(const std::string& keyString) {
const std::unordered_map<std::string, SDL_Scancode> 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<std::string, std::string>& mappings,
std::unordered_map<std::string, SDL_Keycode>& keyMappings) {
const std::array<std::string, 23> 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;
}

31
src/input/keyboard.h Normal file
View File

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include <unordered_map>
#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<std::string, std::string> config_key_map;
void initializeSdlKeyMappings(const std::unordered_map<std::string, std::string>& mappings,
std::unordered_map<std::string, SDL_Keycode>& keyMappings);
std::unordered_map<std::string, SDL_Keycode> sdl_key_map;
};

View File

@ -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;