diff --git a/src/common/config.cpp b/src/common/config.cpp index a577b143a..f15e67c7c 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -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(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")) { @@ -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; diff --git a/src/common/config.h b/src/common/config.h index 0a3b4905e..532fcbeb0 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -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); diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 949b71be3..755c351e0 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -3,6 +3,7 @@ // Generated By moduleGenerator #include +#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::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(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)]; + pData[i].buttons = kb ? states[i].buttonsState : controller->GetGamepadButtons(); + pData[i].leftStick.x = + kb ? states[i].axes[static_cast(Input::Axis::LeftX)] + : controller->GetGamePadAxis().axes[static_cast(Input::Axis::LeftX)]; + pData[i].leftStick.y = + kb ? states[i].axes[static_cast(Input::Axis::LeftY)] + : controller->GetGamePadAxis().axes[static_cast(Input::Axis::LeftY)]; + pData[i].rightStick.x = + kb ? states[i].axes[static_cast(Input::Axis::RightX)] + : controller->GetGamePadAxis().axes[static_cast(Input::Axis::RightX)]; + pData[i].rightStick.y = + kb ? states[i].axes[static_cast(Input::Axis::RightY)] + : controller->GetGamePadAxis().axes[static_cast(Input::Axis::RightY)]; + pData[i].analogButtons.l2 = + kb ? states[i].axes[static_cast(Input::Axis::TriggerLeft)] + : controller->GetGamePadAxis().axes[static_cast(Input::Axis::TriggerLeft)]; + pData[i].analogButtons.r2 = + kb ? states[i].axes[static_cast(Input::Axis::TriggerRight)] + : controller->GetGamePadAxis().axes[static_cast(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(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->buttons = kb ? state.buttonsState : controller->GetGamepadButtons(); + pData->leftStick.x = + kb ? state.axes[static_cast(Input::Axis::LeftX)] + : controller->GetGamePadAxis().axes[static_cast(Input::Axis::LeftX)]; + pData->leftStick.y = + kb ? state.axes[static_cast(Input::Axis::LeftY)] + : controller->GetGamePadAxis().axes[static_cast(Input::Axis::LeftY)]; + pData->rightStick.x = + kb ? state.axes[static_cast(Input::Axis::RightX)] + : controller->GetGamePadAxis().axes[static_cast(Input::Axis::RightX)]; + pData->rightStick.y = + kb ? state.axes[static_cast(Input::Axis::RightY)] + : controller->GetGamePadAxis().axes[static_cast(Input::Axis::RightY)]; + pData->analogButtons.l2 = + kb ? state.axes[static_cast(Input::Axis::TriggerLeft)] + : controller->GetGamePadAxis().axes[static_cast(Input::Axis::TriggerLeft)]; + pData->analogButtons.r2 = + kb ? state.axes[static_cast(Input::Axis::TriggerRight)] + : controller->GetGamePadAxis().axes[static_cast(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::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() { diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 7bfecadc8..6dbb7b285 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -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((SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX) + 32768) / 257); + u8 ly = static_cast((SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY) + 32768) / 257); + u8 rx = static_cast((SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) + 32768) / 257); + u8 ry = static_cast((SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) + 32768) / 257); + u8 lt = static_cast(SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER)); + u8 rt = static_cast(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 diff --git a/src/input/controller.h b/src/input/controller.h index a16f7dd06..8eb4bc0e0 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -4,6 +4,8 @@ #pragma once #include +#include +#include #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; diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index ee7ab650a..0d32c1484 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include #include @@ -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 = diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index 7b5bf1816..3a88b4ea1 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -12,6 +12,7 @@ #ifndef MAIN_WINDOW_UI_H #define MAIN_WINDOW_UI_H +#include #include #include #include @@ -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")); diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index ef492db67..b684dd7bb 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -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());