mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-25 19:44:57 +00:00
- 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:
parent
cb6b21de1f
commit
808f228e54
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 =
|
||||
|
@ -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"));
|
||||
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user