- Initial gamepad support using SDL3 (tested quickly with an xbox one controller and only supports one connected pad for now), vibration should work too

- Added a quick combobox to switch between keyboard and gamepad in Qt version.
- to enable this add controller = 1 in config.toml under [General], or delete your config.toml and let the emu create a new one
0: for keyboard
1: for gamepad
This commit is contained in:
raziel1000 2024-06-24 00:43:58 -06:00
parent cb6b21de1f
commit 808f228e54
8 changed files with 168 additions and 22 deletions

View File

@ -24,6 +24,7 @@ static bool shouldDumpShaders = false;
static bool shouldDumpPM4 = false;
static bool vkValidation = false;
static bool vkValidationSync = false;
static u32 controller = 0;
// Gui
std::string settings_install_dir = "";
u32 main_window_geometry_x = 400;
@ -102,6 +103,15 @@ bool vkValidationSyncEnabled() {
return vkValidationSync;
}
// 0: keyboard, 1: gamepad
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 +234,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")) {
@ -312,6 +323,7 @@ void save(const std::filesystem::path& path) {
data["General"]["logFilter"] = logFilter;
data["General"]["logType"] = logType;
data["General"]["showSplash"] = isShowSplash;
data["General"]["controller"] = controller;
data["GPU"]["gpuId"] = gpuId;
data["GPU"]["screenWidth"] = screenWidth;
data["GPU"]["screenHeight"] = screenHeight;

View File

@ -30,6 +30,8 @@ bool dumpPM4();
bool vkValidationEnabled();
bool vkValidationSyncEnabled();
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

@ -3,6 +3,7 @@
// Generated By moduleGenerator
#include <common/singleton.h>
#include "common/config.h"
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
@ -173,7 +174,9 @@ int PS4_SYSV_ABI scePadGetVersionInfo() {
}
int PS4_SYSV_ABI scePadInit() {
LOG_ERROR(Lib_Pad, "(STUBBED) called");
auto* controller = Common::Singleton<Input::GameController>::Instance();
controller->InitGamePad();
LOG_ERROR(Lib_Pad, "scePadInit: initialized.");
return ORBIS_OK;
}
@ -247,15 +250,27 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
if (!connected) {
ret_num = 1;
}
bool kb = (Config::getControllerType() == 0);
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)];
pData[i].buttons = kb ? states[i].buttonsState : controller->GetGamepadButtons();
pData[i].leftStick.x =
kb ? states[i].axes[static_cast<int>(Input::Axis::LeftX)]
: controller->GetGamePadAxis().axes[static_cast<int>(Input::Axis::LeftX)];
pData[i].leftStick.y =
kb ? states[i].axes[static_cast<int>(Input::Axis::LeftY)]
: controller->GetGamePadAxis().axes[static_cast<int>(Input::Axis::LeftY)];
pData[i].rightStick.x =
kb ? states[i].axes[static_cast<int>(Input::Axis::RightX)]
: controller->GetGamePadAxis().axes[static_cast<int>(Input::Axis::RightX)];
pData[i].rightStick.y =
kb ? states[i].axes[static_cast<int>(Input::Axis::RightY)]
: controller->GetGamePadAxis().axes[static_cast<int>(Input::Axis::RightY)];
pData[i].analogButtons.l2 =
kb ? states[i].axes[static_cast<int>(Input::Axis::TriggerLeft)]
: controller->GetGamePadAxis().axes[static_cast<int>(Input::Axis::TriggerLeft)];
pData[i].analogButtons.r2 =
kb ? states[i].axes[static_cast<int>(Input::Axis::TriggerRight)]
: controller->GetGamePadAxis().axes[static_cast<int>(Input::Axis::TriggerRight)];
pData[i].orientation.x = 0.0f;
pData[i].orientation.y = 0.0f;
pData[i].orientation.z = 0.0f;
@ -308,15 +323,27 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
int connectedCount = 0;
bool isConnected = false;
Input::State state;
bool kb = (Config::getControllerType() == 0);
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->buttons = kb ? state.buttonsState : controller->GetGamepadButtons();
pData->leftStick.x =
kb ? state.axes[static_cast<int>(Input::Axis::LeftX)]
: controller->GetGamePadAxis().axes[static_cast<int>(Input::Axis::LeftX)];
pData->leftStick.y =
kb ? state.axes[static_cast<int>(Input::Axis::LeftY)]
: controller->GetGamePadAxis().axes[static_cast<int>(Input::Axis::LeftY)];
pData->rightStick.x =
kb ? state.axes[static_cast<int>(Input::Axis::RightX)]
: controller->GetGamePadAxis().axes[static_cast<int>(Input::Axis::RightX)];
pData->rightStick.y =
kb ? state.axes[static_cast<int>(Input::Axis::RightY)]
: controller->GetGamePadAxis().axes[static_cast<int>(Input::Axis::RightY)];
pData->analogButtons.l2 =
kb ? state.axes[static_cast<int>(Input::Axis::TriggerLeft)]
: controller->GetGamePadAxis().axes[static_cast<int>(Input::Axis::TriggerLeft)];
pData->analogButtons.r2 =
kb ? state.axes[static_cast<int>(Input::Axis::TriggerRight)]
: controller->GetGamePadAxis().axes[static_cast<int>(Input::Axis::TriggerRight)];
pData->orientation.x = 0;
pData->orientation.y = 0;
pData->orientation.z = 0;
@ -455,8 +482,16 @@ 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 = {} enabled = {}", handle,
pParam->largeMotor);
auto* controller = Common::Singleton<Input::GameController>::Instance();
u16 smallFreq = (u16)(((float)pParam->smallMotor / 255.0f) * 65535.0f);
u16 bigFreq = (u16)(((float)pParam->largeMotor / 255.0f) * 65535.0f);
result = controller->GetRumble(smallFreq, bigFreq);
}
return result;
}
int PS4_SYSV_ABI scePadSetVibrationForce() {

View File

@ -116,4 +116,76 @@ void GameController::Axis(int id, Input::Axis axis, int value) {
AddState(state);
}
void GameController::InitGamePad() {
int count;
SDL_JoystickID* joysticks = SDL_GetJoysticks(&count);
SDL_JoystickID instance_id = joysticks[0];
gamepad = SDL_OpenGamepad(instance_id);
}
u32 GameController::GetGamepadButtons() {
u32 buttons = 0;
if (gamepad) {
buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_STICK) == SDL_PRESSED
? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3)
: 0;
buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_STICK) == SDL_PRESSED
? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3)
: 0;
buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START) == SDL_PRESSED
? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS)
: 0;
buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP) == SDL_PRESSED
? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP)
: 0;
buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT) == SDL_PRESSED
? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT)
: 0;
buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN) == SDL_PRESSED
? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN)
: 0;
buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT) == SDL_PRESSED
? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT)
: 0;
buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER) == SDL_PRESSED
? (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) == SDL_PRESSED
? (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) == SDL_PRESSED
? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE)
: 0;
buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_EAST) == SDL_PRESSED
? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE)
: 0;
buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH) == SDL_PRESSED
? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS)
: 0;
buttons |= SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST) == SDL_PRESSED
? (Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE)
: 0;
}
return buttons;
}
State GameController::GetGamePadAxis() {
u8 lx = static_cast<u8>((SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX) + 32768) / 257);
u8 ly = static_cast<u8>((SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY) + 32768) / 257);
u8 rx = static_cast<u8>((SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) + 32768) / 257);
u8 ry = static_cast<u8>((SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) + 32768) / 257);
u8 lt = static_cast<u8>(SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER));
u8 rt = static_cast<u8>(SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER));
return {0, 0, {lx, ly, rx, ry, lt, rt}};
}
int GameController::GetRumble(u16 smallFreq, u16 bigFreq) {
return SDL_RumbleGamepad(gamepad, smallFreq, bigFreq, -1);
}
} // namespace Input

View File

@ -4,6 +4,8 @@
#pragma once
#include <mutex>
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_init.h>
#include "common/types.h"
namespace Input {
@ -44,10 +46,16 @@ public:
void AddState(const State& state);
void Axis(int id, Input::Axis axis, int value);
void InitGamePad();
u32 GetGamepadButtons();
State GetGamePadAxis();
int GetRumble(u16 smallFreq, u16 bigFreq);
private:
struct StateInternal {
bool obtained = false;
};
SDL_Gamepad* gamepad;
std::mutex m_mutex;
bool m_connected = true;

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QComboBox>
#include <QDir>
#include <QDockWidget>
#include <QFileDialog>
@ -93,6 +94,7 @@ void MainWindow::AddUiWidgets() {
ui->toolBar->addWidget(line);
ui->toolBar->addWidget(ui->sizeSliderContainer);
ui->toolBar->addWidget(ui->mw_searchbar);
ui->toolBar->addWidget(ui->padBox);
}
void MainWindow::CreateDockWindows() {
@ -133,7 +135,7 @@ void MainWindow::CreateDockWindows() {
m_dock_widget->setWidget(m_elf_viewer.data());
isTableList = false;
}
ui->padBox->setCurrentIndex(Config::getControllerType());
m_dock_widget->setAllowedAreas(Qt::AllDockWidgetAreas);
m_dock_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_dock_widget->resize(this->width(), this->height());
@ -205,6 +207,15 @@ void MainWindow::CreateConnects() {
}
});
connect(ui->padBox, &QComboBox::currentTextChanged, this, [&](QString item) {
if (item == "Keyboard")
Config::setControllerType(0);
else if (item == "Gamepad")
Config::setControllerType(1);
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
Config::save(config_dir / "config.toml");
});
connect(ui->setIconSizeTinyAct, &QAction::triggered, this, [this]() {
if (isTableList) {
m_game_list_frame->icon_size =

View File

@ -12,6 +12,7 @@
#ifndef MAIN_WINDOW_UI_H
#define MAIN_WINDOW_UI_H
#include <QComboBox>
#include <QtCore/QVariant>
#include <QtGui/QAction>
#include <QtWidgets/QApplication>
@ -56,6 +57,7 @@ public:
QPushButton* stopButton;
QPushButton* settingsButton;
QPushButton* controllerButton;
QComboBox* padBox;
QWidget* sizeSliderContainer;
QHBoxLayout* sizeSliderContainer_layout;
@ -168,6 +170,11 @@ public:
mw_searchbar->setFrame(false);
mw_searchbar->setClearButtonEnabled(false);
padBox = new QComboBox(centralWidget);
padBox->setObjectName("padBox");
padBox->addItem("Keyboard");
padBox->addItem("Gamepad");
playButton = new QPushButton(centralWidget);
playButton->setFlat(true);
playButton->setIcon(QIcon(":images/play_icon.png"));

View File

@ -19,8 +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();
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, title.c_str());