diff --git a/.ci/clang-format.sh b/.ci/clang-format.sh index 0ccd4062..b9018f50 100755 --- a/.ci/clang-format.sh +++ b/.ci/clang-format.sh @@ -3,6 +3,11 @@ # SPDX-FileCopyrightText: 2023 Citra Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later +fix=false +if [ "$1" == "--fix" ]; then + fix=true +fi + if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop \ dist/*.svg dist/*.xml; then echo Trailing whitespace found, aborting @@ -25,11 +30,15 @@ fi set +x for f in $files_to_lint; do - d=$(diff -u "$f" <($CLANG_FORMAT "$f") || true) - if ! [ -z "$d" ]; then - echo "!!! $f not compliant to coding style, here is the fix:" - echo "$d" - fail=1 + if [ "$fix" = true ]; then + $CLANG_FORMAT -i "$f" + else + d=$(diff -u "$f" <($CLANG_FORMAT "$f") || true) + if ! [ -z "$d" ]; then + echo "!!! $f not compliant to coding style, here is the fix:" + echo "$d" + fail=1 + fi fi done diff --git a/.github/workflows/linux-qt.yml b/.github/workflows/linux-qt.yml index 6848f203..466554ca 100644 --- a/.github/workflows/linux-qt.yml +++ b/.github/workflows/linux-qt.yml @@ -23,7 +23,13 @@ jobs: - name: Install misc packages run: > - sudo apt-get update && sudo apt install libx11-dev libxext-dev libwayland-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev + sudo apt-get update && sudo apt install libx11-dev libxext-dev libwayland-dev libfuse2 clang build-essential + + - name: Setup Qt + uses: jurplel/install-qt-action@v4 + with: + arch: linux_gcc_64 + version: 6.7.1 - name: Cache CMake dependency source code uses: actions/cache@v4 diff --git a/.reuse/dep5 b/.reuse/dep5 index 0140c0c0..88c3e996 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -15,6 +15,7 @@ Files: CMakeSettings.json documents/Screenshots/Undertale.png documents/Screenshots/We are DOOMED.png scripts/ps4_names.txt + src/images/PS4_controller_scheme.png src/images/about_icon.png src/images/controller_icon.png src/images/exit_icon.png diff --git a/CMakeLists.txt b/CMakeLists.txt index 9101af9d..20e2143b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -607,6 +607,7 @@ set(IMGUI src/imgui/imgui_config.h set(INPUT src/input/controller.cpp src/input/controller.h + src/input/keys_constants.h ) set(EMULATOR src/emulator.cpp @@ -651,6 +652,9 @@ set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/settings_dialog.cpp src/qt_gui/settings_dialog.h src/qt_gui/settings_dialog.ui + src/qt_gui/keyboardcontrolswindow.h + src/qt_gui/keyboardcontrolswindow.cpp + src/qt_gui/keyboardcontrolswindow.ui src/qt_gui/main.cpp ${EMULATOR} ${RESOURCE_FILES} diff --git a/src/common/config.cpp b/src/common/config.cpp index fb6ee120..1063dfca 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -53,6 +53,7 @@ std::vector m_recent_files; std::string emulator_language = "en"; // Settings u32 m_language = 1; // english +std::map m_keyboard_binding_map; bool isNeoMode() { return isNeo; @@ -283,7 +284,12 @@ void setRecentFiles(const std::vector& recentFiles) { void setEmulatorLanguage(std::string language) { emulator_language = language; } - +void setKeyboardBindingMap(std::map map) { + m_keyboard_binding_map = map; +} +const std::map& getKeyboardBindingMap() { + return m_keyboard_binding_map; +} u32 getMainWindowGeometryX() { return main_window_geometry_x; } @@ -431,6 +437,34 @@ void load(const std::filesystem::path& path) { m_language = toml::find_or(settings, "consoleLanguage", 1); } + + if (data.contains("Controls")) { + auto controls = toml::find(data, "Controls"); + + toml::table keyboardBindings{}; + auto it = controls.find("keyboardBindings"); + if (it != controls.end() && it->second.is_table()) { + keyboardBindings = it->second.as_table(); + } + + // Convert TOML table to std::map + for (const auto& [key, value] : keyboardBindings) { + try { + Uint32 int_key = static_cast(std::stoll(key)); + if (value.is_integer()) { + // Convert the TOML integer value to KeysMapping (int) + int int_value = value.as_integer(); + + // Add to the map + m_keyboard_binding_map[int_key] = static_cast(int_value); + } else { + fmt::print("Unexpected type for value: expected integer, got other type\n"); + } + } catch (const std::exception& e) { + fmt::print("Error processing key-value pair: {}\n", e.what()); + } + } + } } void save(const std::filesystem::path& path) { toml::value data; @@ -492,6 +526,15 @@ void save(const std::filesystem::path& path) { data["GUI"]["recentFiles"] = m_recent_files; data["GUI"]["emulatorLanguage"] = emulator_language; + // Create a TOML table with keyboard bindings + toml::table keyboardBindingsTable; + // Serialize the map to the TOML table + for (const auto& [key, value] : m_keyboard_binding_map) { + keyboardBindingsTable[std::to_string(key)] = static_cast(value); + } + + data["Controls"]["keyboardBindings"] = keyboardBindingsTable; + data["Settings"]["consoleLanguage"] = m_language; std::ofstream file(path, std::ios::out); diff --git a/src/common/config.h b/src/common/config.h index 7e717fe7..ff76f776 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -4,7 +4,10 @@ #pragma once #include +#include #include +#include "SDL3/SDL_stdinc.h" +#include "input/keys_constants.h" #include "types.h" namespace Config { @@ -79,6 +82,8 @@ void setPkgViewer(const std::vector& pkgList); void setElfViewer(const std::vector& elfList); void setRecentFiles(const std::vector& recentFiles); void setEmulatorLanguage(std::string language); +void setKeyboardBindingMap(std::map map); +const std::map& getKeyboardBindingMap(); u32 getMainWindowGeometryX(); u32 getMainWindowGeometryY(); diff --git a/src/emulator.cpp b/src/emulator.cpp index 9c41a3db..3006de1e 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -146,7 +146,7 @@ void Emulator::Run(const std::filesystem::path& file) { } window = std::make_unique( Config::getScreenWidth(), Config::getScreenHeight(), controller, window_title); - + window->setKeysBindingsMap(Config::getKeyboardBindingMap()); g_window = window.get(); const auto& mount_data_dir = Common::FS::GetUserPath(Common::FS::PathType::GameDataDir) / id; diff --git a/src/emulator.h b/src/emulator.h index 01bce7e7..b1f77989 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -34,6 +34,6 @@ private: Input::GameController* controller; Core::Linker* linker; std::unique_ptr window; + std::map m_keysBindingsMap; }; - } // namespace Core diff --git a/src/images/PS4_controller_scheme.png b/src/images/PS4_controller_scheme.png new file mode 100644 index 00000000..9e095a06 Binary files /dev/null and b/src/images/PS4_controller_scheme.png differ diff --git a/src/input/keys_constants.h b/src/input/keys_constants.h new file mode 100644 index 00000000..a24e93ea --- /dev/null +++ b/src/input/keys_constants.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +enum KeysMapping : int { + Start_Key = 0, + Select_Key, + LAnalogDown_Key, + LAnalogLeft_Key, + LAnalogUp_Key, + LAnalogRight_Key, + PS_Key, + RAnalogDown_Key, + RAnalogLeft_Key, + RAnalogUp_Key, + RAnalogRight_Key, + DPadLeft_Key, + DPadRight_Key, + DPadUp_Key, + DPadDown_Key, + L2_Key, + L1_Key, + Cross_Key, + R2_Key, + Circle_Key, + R1_Key, + Square_Key, + Triangle_Key +}; \ No newline at end of file diff --git a/src/qt_gui/keyboardcontrolswindow.cpp b/src/qt_gui/keyboardcontrolswindow.cpp new file mode 100644 index 00000000..bd534727 --- /dev/null +++ b/src/qt_gui/keyboardcontrolswindow.cpp @@ -0,0 +1,524 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "./ui_keyboardcontrolswindow.h" +#include "common/config.h" +#include "keyboardcontrolswindow.h" + +#include +#include + +#include + +namespace { +static constexpr auto keyBindingsSettingsKey = "ShadPS4_Keyboard_Settings_KEY"; +static constexpr auto inputErrorTimerTimeout = 2000; +} // namespace + +void showError(const QString& message) { + QMessageBox::critical(nullptr, "Error", message, QMessageBox::Ok); +} + +void showWarning(const QString& message) { + QMessageBox::warning(nullptr, "Warning", message, QMessageBox::Ok); +} + +void showInfo(const QString& message) { + QMessageBox::information(nullptr, "Info", message, QMessageBox::Ok); +} + +KeyboardControlsWindow::KeyboardControlsWindow(QWidget* parent) + : QDialog(parent), ui(new Ui::KeyboardControlsWindow) { + ui->setupUi(this); + + m_keysMap = Config::getKeyboardBindingMap(); + + for (auto& pair : m_keysMap) { + m_reverseKeysMap.emplace(pair.second, pair.first); + } + + m_listOfKeySequenceEdits = {ui->StartKeySequenceEdit, ui->SelectKeySequenceEdit, + ui->LAnalogDownkeySequenceEdit, ui->LAnalogLeftkeySequenceEdit, + ui->LAnalogUpkeySequenceEdit, ui->LAnalogRightkeySequenceEdit, + ui->PSkeySequenceEdit, ui->RAnalogDownkeySequenceEdit, + ui->RAnalogLeftkeySequenceEdit, ui->RAnalogUpkeySequenceEdit, + ui->RAnalogRightkeySequenceEdit, ui->DPadLeftkeySequenceEdit, + ui->DPadRightkeySequenceEdit, ui->DPadUpkeySequenceEdit, + ui->DPadDownkeySequenceEdit, ui->L2keySequenceEdit, + ui->L1keySequenceEdit, ui->CrossKeySequenceEdit, + ui->R2KeySequenceEdit, ui->CircleKeySequenceEdit, + ui->R1KeySequenceEdit, ui->SquareKeySequenceEdit, + ui->TriangleKeySequenceEdit}; + + for (auto edit : m_listOfKeySequenceEdits) { + edit->setStyleSheet("QLineEdit { qproperty-alignment: AlignCenter; }"); + QObject::connect(edit, &QKeySequenceEdit::editingFinished, this, + &KeyboardControlsWindow::onEditingFinished); + } + + ui->StartKeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::Start_Key])); + ui->SelectKeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::Select_Key])); + ui->LAnalogDownkeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::LAnalogDown_Key])); + ui->LAnalogLeftkeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::LAnalogLeft_Key])); + ui->LAnalogUpkeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::LAnalogUp_Key])); + ui->LAnalogRightkeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::LAnalogRight_Key])); + ui->PSkeySequenceEdit->setKeySequence(convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::PS_Key])); + ui->RAnalogDownkeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::RAnalogDown_Key])); + ui->RAnalogLeftkeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::RAnalogLeft_Key])); + ui->RAnalogUpkeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::RAnalogUp_Key])); + ui->RAnalogRightkeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::RAnalogRight_Key])); + ui->DPadLeftkeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::DPadLeft_Key])); + ui->DPadRightkeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::DPadRight_Key])); + ui->DPadUpkeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::DPadUp_Key])); + ui->DPadDownkeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::DPadDown_Key])); + ui->L2keySequenceEdit->setKeySequence(convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::L2_Key])); + ui->L1keySequenceEdit->setKeySequence(convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::L1_Key])); + ui->CrossKeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::Cross_Key])); + ui->R2KeySequenceEdit->setKeySequence(convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::R2_Key])); + ui->CircleKeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::Circle_Key])); + ui->R1KeySequenceEdit->setKeySequence(convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::R1_Key])); + ui->SquareKeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::Square_Key])); + ui->TriangleKeySequenceEdit->setKeySequence( + convertSDLKeyToQt(m_reverseKeysMap[KeysMapping::Triangle_Key])); + + QObject::connect(ui->applyButton, &QPushButton::clicked, + [this]() { validateAndSaveKeyBindings(); }); + + QObject::connect(ui->cancelButton, &QPushButton::clicked, [this]() { this->close(); }); +} + +KeyboardControlsWindow::~KeyboardControlsWindow() { + delete ui; +} + +const std::map& KeyboardControlsWindow::getKeysMapping() const { + return m_keysMap; +} + +void KeyboardControlsWindow::validateAndSaveKeyBindings() { + int nOfUnconfiguredButtons = 0; + for (auto& keyEdit : m_listOfKeySequenceEdits) { + auto keySequence = keyEdit->keySequence(); + // If key sequence is empty (i.e. there is no key assigned to it) we highlight it in red + if (keySequence.isEmpty()) { + keyEdit->setStyleSheet("background-color: red; qproperty-alignment: AlignCenter;"); + QTimer::singleShot(inputErrorTimerTimeout, keyEdit, [keyEdit]() { + keyEdit->setStyleSheet("qproperty-alignment: AlignCenter;"); // Reset to default + }); + + ++nOfUnconfiguredButtons; + } + } + + if (nOfUnconfiguredButtons > 0) { + showError("Some of the buttons were not configured"); + return; + } + + m_keysMap.clear(); + m_reverseKeysMap.clear(); + + m_keysMap.emplace(convertQtKeyToSDL(ui->LAnalogDownkeySequenceEdit->keySequence()[0].key()), + KeysMapping::LAnalogDown_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->LAnalogLeftkeySequenceEdit->keySequence()[0].key()), + KeysMapping::LAnalogLeft_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->LAnalogUpkeySequenceEdit->keySequence()[0].key()), + KeysMapping::LAnalogUp_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->LAnalogRightkeySequenceEdit->keySequence()[0].key()), + KeysMapping::LAnalogRight_Key); + + m_keysMap.emplace(convertQtKeyToSDL(ui->PSkeySequenceEdit->keySequence()[0].key()), + KeysMapping::PS_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->StartKeySequenceEdit->keySequence()[0].key()), + KeysMapping::Start_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->SelectKeySequenceEdit->keySequence()[0].key()), + KeysMapping::Select_Key); + + m_keysMap.emplace(convertQtKeyToSDL(ui->RAnalogDownkeySequenceEdit->keySequence()[0].key()), + KeysMapping::RAnalogDown_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->RAnalogLeftkeySequenceEdit->keySequence()[0].key()), + KeysMapping::RAnalogLeft_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->RAnalogUpkeySequenceEdit->keySequence()[0].key()), + KeysMapping::RAnalogUp_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->RAnalogRightkeySequenceEdit->keySequence()[0].key()), + KeysMapping::RAnalogRight_Key); + + m_keysMap.emplace(convertQtKeyToSDL(ui->DPadLeftkeySequenceEdit->keySequence()[0].key()), + KeysMapping::DPadLeft_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->DPadRightkeySequenceEdit->keySequence()[0].key()), + KeysMapping::DPadRight_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->DPadUpkeySequenceEdit->keySequence()[0].key()), + KeysMapping::DPadUp_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->DPadDownkeySequenceEdit->keySequence()[0].key()), + KeysMapping::DPadDown_Key); + + m_keysMap.emplace(convertQtKeyToSDL(ui->L1keySequenceEdit->keySequence()[0].key()), + KeysMapping::L1_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->L2keySequenceEdit->keySequence()[0].key()), + KeysMapping::L2_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->R1KeySequenceEdit->keySequence()[0].key()), + KeysMapping::R1_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->R2KeySequenceEdit->keySequence()[0].key()), + KeysMapping::R2_Key); + + m_keysMap.emplace(convertQtKeyToSDL(ui->CrossKeySequenceEdit->keySequence()[0].key()), + KeysMapping::Cross_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->CircleKeySequenceEdit->keySequence()[0].key()), + KeysMapping::Circle_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->SquareKeySequenceEdit->keySequence()[0].key()), + KeysMapping::Square_Key); + m_keysMap.emplace(convertQtKeyToSDL(ui->TriangleKeySequenceEdit->keySequence()[0].key()), + KeysMapping::Triangle_Key); + + for (auto& pair : m_keysMap) { + m_reverseKeysMap.emplace(pair.second, pair.first); + } + + // Saving into settings (for permanent storage) + Config::setKeyboardBindingMap(m_keysMap); + + this->close(); +} + +Qt::Key KeyboardControlsWindow::convertSDLKeyToQt(SDL_Keycode sdlKey) { + switch (sdlKey) { + case SDLK_A: + return Qt::Key_A; + case SDLK_B: + return Qt::Key_B; + case SDLK_C: + return Qt::Key_C; + case SDLK_D: + return Qt::Key_D; + case SDLK_E: + return Qt::Key_E; + case SDLK_F: + return Qt::Key_F; + case SDLK_G: + return Qt::Key_G; + case SDLK_H: + return Qt::Key_H; + case SDLK_I: + return Qt::Key_I; + case SDLK_J: + return Qt::Key_J; + case SDLK_K: + return Qt::Key_K; + case SDLK_L: + return Qt::Key_L; + case SDLK_M: + return Qt::Key_M; + case SDLK_N: + return Qt::Key_N; + case SDLK_O: + return Qt::Key_O; + case SDLK_P: + return Qt::Key_P; + case SDLK_Q: + return Qt::Key_Q; + case SDLK_R: + return Qt::Key_R; + case SDLK_S: + return Qt::Key_S; + case SDLK_T: + return Qt::Key_T; + case SDLK_U: + return Qt::Key_U; + case SDLK_V: + return Qt::Key_V; + case SDLK_W: + return Qt::Key_W; + case SDLK_X: + return Qt::Key_X; + case SDLK_Y: + return Qt::Key_Y; + case SDLK_Z: + return Qt::Key_Z; + case SDLK_0: + return Qt::Key_0; + case SDLK_1: + return Qt::Key_1; + case SDLK_2: + return Qt::Key_2; + case SDLK_3: + return Qt::Key_3; + case SDLK_4: + return Qt::Key_4; + case SDLK_5: + return Qt::Key_5; + case SDLK_6: + return Qt::Key_6; + case SDLK_7: + return Qt::Key_7; + case SDLK_8: + return Qt::Key_8; + case SDLK_9: + return Qt::Key_9; + case SDLK_SPACE: + return Qt::Key_Space; + case SDLK_RETURN: + return Qt::Key_Return; + case SDLK_ESCAPE: + return Qt::Key_Escape; + case SDLK_TAB: + return Qt::Key_Tab; + case SDLK_BACKSPACE: + return Qt::Key_Backspace; + case SDLK_DELETE: + return Qt::Key_Delete; + case SDLK_INSERT: + return Qt::Key_Insert; + case SDLK_HOME: + return Qt::Key_Home; + case SDLK_END: + return Qt::Key_End; + case SDLK_PAGEUP: + return Qt::Key_PageUp; + case SDLK_PAGEDOWN: + return Qt::Key_PageDown; + case SDLK_LEFT: + return Qt::Key_Left; + case SDLK_RIGHT: + return Qt::Key_Right; + case SDLK_UP: + return Qt::Key_Up; + case SDLK_DOWN: + return Qt::Key_Down; + case SDLK_CAPSLOCK: + return Qt::Key_CapsLock; + case SDLK_NUMLOCKCLEAR: + return Qt::Key_NumLock; + case SDLK_SCROLLLOCK: + return Qt::Key_ScrollLock; + case SDLK_F1: + return Qt::Key_F1; + case SDLK_F2: + return Qt::Key_F2; + case SDLK_F3: + return Qt::Key_F3; + case SDLK_F4: + return Qt::Key_F4; + case SDLK_F5: + return Qt::Key_F5; + case SDLK_F6: + return Qt::Key_F6; + case SDLK_F7: + return Qt::Key_F7; + case SDLK_F8: + return Qt::Key_F8; + case SDLK_F9: + return Qt::Key_F9; + case SDLK_F10: + return Qt::Key_F10; + case SDLK_F11: + return Qt::Key_F11; + case SDLK_F12: + return Qt::Key_F12; + case SDLK_LSHIFT: + return Qt::Key_Shift; + case SDLK_LCTRL: + return Qt::Key_Control; + case SDLK_LALT: + return Qt::Key_Alt; + case SDLK_LGUI: + return Qt::Key_Meta; + default: + return Qt::Key_unknown; + } +} + +SDL_Keycode KeyboardControlsWindow::convertQtKeyToSDL(Qt::Key qtKey) { + switch (qtKey) { + case Qt::Key_A: + return SDLK_A; + case Qt::Key_B: + return SDLK_B; + case Qt::Key_C: + return SDLK_C; + case Qt::Key_D: + return SDLK_D; + case Qt::Key_E: + return SDLK_E; + case Qt::Key_F: + return SDLK_F; + case Qt::Key_G: + return SDLK_G; + case Qt::Key_H: + return SDLK_H; + case Qt::Key_I: + return SDLK_I; + case Qt::Key_J: + return SDLK_J; + case Qt::Key_K: + return SDLK_K; + case Qt::Key_L: + return SDLK_L; + case Qt::Key_M: + return SDLK_M; + case Qt::Key_N: + return SDLK_N; + case Qt::Key_O: + return SDLK_O; + case Qt::Key_P: + return SDLK_P; + case Qt::Key_Q: + return SDLK_Q; + case Qt::Key_R: + return SDLK_R; + case Qt::Key_S: + return SDLK_S; + case Qt::Key_T: + return SDLK_T; + case Qt::Key_U: + return SDLK_U; + case Qt::Key_V: + return SDLK_V; + case Qt::Key_W: + return SDLK_W; + case Qt::Key_X: + return SDLK_X; + case Qt::Key_Y: + return SDLK_Y; + case Qt::Key_Z: + return SDLK_Z; + case Qt::Key_0: + return SDLK_0; + case Qt::Key_1: + return SDLK_1; + case Qt::Key_2: + return SDLK_2; + case Qt::Key_3: + return SDLK_3; + case Qt::Key_4: + return SDLK_4; + case Qt::Key_5: + return SDLK_5; + case Qt::Key_6: + return SDLK_6; + case Qt::Key_7: + return SDLK_7; + case Qt::Key_8: + return SDLK_8; + case Qt::Key_9: + return SDLK_9; + case Qt::Key_Space: + return SDLK_SPACE; + case Qt::Key_Enter: + return SDLK_RETURN; + case Qt::Key_Return: + return SDLK_RETURN; + case Qt::Key_Escape: + return SDLK_ESCAPE; + case Qt::Key_Tab: + return SDLK_TAB; + case Qt::Key_Backspace: + return SDLK_BACKSPACE; + case Qt::Key_Delete: + return SDLK_DELETE; + case Qt::Key_Insert: + return SDLK_INSERT; + case Qt::Key_Home: + return SDLK_HOME; + case Qt::Key_End: + return SDLK_END; + case Qt::Key_PageUp: + return SDLK_PAGEUP; + case Qt::Key_PageDown: + return SDLK_PAGEDOWN; + case Qt::Key_Left: + return SDLK_LEFT; + case Qt::Key_Right: + return SDLK_RIGHT; + case Qt::Key_Up: + return SDLK_UP; + case Qt::Key_Down: + return SDLK_DOWN; + case Qt::Key_CapsLock: + return SDLK_CAPSLOCK; + case Qt::Key_NumLock: + return SDLK_NUMLOCKCLEAR; + case Qt::Key_ScrollLock: + return SDLK_SCROLLLOCK; + case Qt::Key_F1: + return SDLK_F1; + case Qt::Key_F2: + return SDLK_F2; + case Qt::Key_F3: + return SDLK_F3; + case Qt::Key_F4: + return SDLK_F4; + case Qt::Key_F5: + return SDLK_F5; + case Qt::Key_F6: + return SDLK_F6; + case Qt::Key_F7: + return SDLK_F7; + case Qt::Key_F8: + return SDLK_F8; + case Qt::Key_F9: + return SDLK_F9; + case Qt::Key_F10: + return SDLK_F10; + case Qt::Key_F11: + return SDLK_F11; + case Qt::Key_F12: + return SDLK_F12; + case Qt::Key_Shift: + return SDLK_LSHIFT; + case Qt::Key_Control: + return SDLK_LCTRL; + case Qt::Key_Alt: + return SDLK_LALT; + case Qt::Key_Meta: + return SDLK_LGUI; + default: + return SDLK_UNKNOWN; + } +} + +void KeyboardControlsWindow::onEditingFinished() { + auto sender = qobject_cast(QObject::sender()); + auto new_keySequence = sender->keySequence(); + + // If new key sequence is empty (i.e. there is no key assigned to it) - skip 'duplicate' checks + // Two checks are needed for the sake of robustness (when we click on a widget but don't type + // anything it might no longer be "empty") + if (new_keySequence.isEmpty() || new_keySequence.toString().isEmpty()) { + return; + } + + // Check if sequance is not already used (i.e. making sure there are not duplicates) + for (auto& keyEdit : m_listOfKeySequenceEdits) { + if (keyEdit != sender && new_keySequence == keyEdit->keySequence()) { + sender->clear(); + sender->setStyleSheet("background-color: red; qproperty-alignment: AlignCenter;"); + QTimer::singleShot(inputErrorTimerTimeout, sender, [sender]() { + sender->setStyleSheet( + "QLineEdit { qproperty-alignment: AlignCenter; }"); // Reset to default + }); + + keyEdit->setStyleSheet("background-color: red; qproperty-alignment: AlignCenter;"); + QTimer::singleShot(inputErrorTimerTimeout, keyEdit, [keyEdit]() { + keyEdit->setStyleSheet( + "QLineEdit { qproperty-alignment: AlignCenter; }"); // Reset to default + }); + } + } +} diff --git a/src/qt_gui/keyboardcontrolswindow.h b/src/qt_gui/keyboardcontrolswindow.h new file mode 100644 index 00000000..3649917e --- /dev/null +++ b/src/qt_gui/keyboardcontrolswindow.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#include +#include "input/keys_constants.h" + +QT_BEGIN_NAMESPACE +namespace Ui { +class KeyboardControlsWindow; +} +QT_END_NAMESPACE + +class KeyboardControlsWindow : public QDialog { + Q_OBJECT + +public: + KeyboardControlsWindow(QWidget* parent = nullptr); + ~KeyboardControlsWindow(); + + const std::map& getKeysMapping() const; + +private slots: + void onEditingFinished(); + +private: + void validateAndSaveKeyBindings(); + SDL_Keycode convertQtKeyToSDL(Qt::Key qtKey); + Qt::Key convertSDLKeyToQt(SDL_Keycode qtKey); + + Ui::KeyboardControlsWindow* ui; + QSet m_listOfKeySequenceEdits; + std::map m_keysMap; + std::map m_reverseKeysMap; +}; diff --git a/src/qt_gui/keyboardcontrolswindow.ui b/src/qt_gui/keyboardcontrolswindow.ui new file mode 100644 index 00000000..7973e0c5 --- /dev/null +++ b/src/qt_gui/keyboardcontrolswindow.ui @@ -0,0 +1,439 @@ + + + + KeyboardControlsWindow + + + + 0 + 0 + 1155 + 790 + + + + + 0 + 0 + + + + + 1148 + 731 + + + + + 1155 + 790 + + + + Configure Controller Bindings + + + true + + + + + 10 + 10 + 1131 + 711 + + + + 0 + + + + Port 1 + + + + + -10 + 20 + 1131 + 621 + + + + + + 170 + 50 + 870 + 522 + + + + false + + + QWidget { background-image: url(:images/PS4_controller_scheme.png); + background-repeat: no-repeat; + background-position: center; } + + + + + + 550 + 0 + 71 + 40 + + + + 1 + + + + + + 260 + 0 + 71 + 40 + + + + 1 + + + + + + 280 + 480 + 71 + 40 + + + + 1 + + + + + + 240 + 440 + 71 + 40 + + + + 1 + + + + + + 280 + 400 + 71 + 40 + + + + 1 + + + + + + 320 + 440 + 71 + 40 + + + + 1 + + + + + + 400 + 400 + 71 + 40 + + + + 1 + + + + + + 520 + 480 + 71 + 40 + + + + 1 + + + + + + 480 + 440 + 71 + 40 + + + + 1 + + + + + + 520 + 400 + 71 + 40 + + + + 1 + + + + + + 560 + 440 + 71 + 40 + + + + 1 + + + + + + + 10 + 230 + 71 + 40 + + + + 1 + + + + + + 90 + 230 + 71 + 40 + + + + 1 + + + + + + 50 + 190 + 71 + 40 + + + + 1 + + + + + + 50 + 270 + 71 + 40 + + + + 1 + + + + + + 90 + 40 + 71 + 40 + + + + 1 + + + + + + 90 + 110 + 71 + 40 + + + + 1 + + + + + true + + + + 1050 + 380 + 71 + 40 + + + + 1 + + + + + true + + + + 1050 + 30 + 71 + 40 + + + + 1 + + + + + true + + + + 1050 + 240 + 71 + 40 + + + + 1 + + + + + true + + + + 1050 + 100 + 71 + 40 + + + + 1 + + + + + true + + + + 1050 + 310 + 71 + 40 + + + + 1 + + + + + true + + + + 1050 + 170 + 71 + 40 + + + + 1 + + + + + + + Port 2 + + + + + + + 1030 + 740 + 100 + 32 + + + + Apply + + + + + + 890 + 740 + 100 + 32 + + + + Cancel + + + + + + diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index e5b502c5..59387a1a 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -50,6 +50,7 @@ bool MainWindow::Init() { auto end = std::chrono::steady_clock::now(); auto duration = std::chrono::duration_cast(end - start); statusBar.reset(new QStatusBar); + m_controllerControlsDialog.reset(new KeyboardControlsWindow()); this->setStatusBar(statusBar.data()); // Update status bar int numGames = m_game_info->m_games.size(); @@ -90,6 +91,9 @@ void MainWindow::AddUiWidgets() { ui->toolBar->addWidget(ui->stopButton); ui->toolBar->addWidget(ui->refreshButton); ui->toolBar->addWidget(ui->settingsButton); + auto connection = QObject::connect(ui->controllerButton, &QPushButton::clicked, this, + &MainWindow::ControllerConfigurationButtonPressed); + ui->toolBar->addWidget(ui->controllerButton); QFrame* line = new QFrame(this); line->setFrameShape(QFrame::StyledPanel); @@ -99,6 +103,10 @@ void MainWindow::AddUiWidgets() { ui->toolBar->addWidget(ui->mw_searchbar); } +void MainWindow::ControllerConfigurationButtonPressed() { + m_controllerControlsDialog->show(); +} + void MainWindow::CreateDockWindows() { // place holder widget is needed for good health they say :) QWidget* phCentralWidget = new QWidget(this); @@ -781,6 +789,10 @@ void MainWindow::InstallDirectory() { RefreshGameTable(); } +std::map MainWindow::getKeysMapping() { + return m_controllerControlsDialog->getKeysMapping(); +} + void MainWindow::SetLastUsedTheme() { Theme lastTheme = static_cast(Config::getMainWindowTheme()); m_window_themes.SetWindowTheme(lastTheme, ui->mw_searchbar); diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index d3b83e61..8ffb760b 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -17,6 +17,7 @@ #include "game_info.h" #include "game_list_frame.h" #include "game_list_utils.h" +#include "keyboardcontrolswindow.h" #include "main_window_themes.h" #include "main_window_ui.h" #include "pkg_viewer.h" @@ -37,6 +38,8 @@ public: void InstallDirectory(); void StartGame(); + std::map getKeysMapping(); + private Q_SLOTS: void ConfigureGuiFromSettings(); void SaveWindowState() const; @@ -45,6 +48,7 @@ private Q_SLOTS: void RefreshGameTable(); void HandleResize(QResizeEvent* event); void OnLanguageChanged(const std::string& locale); + void ControllerConfigurationButtonPressed(); private: Ui_MainWindow* ui; @@ -80,6 +84,7 @@ private: QScopedPointer m_elf_viewer; // Status Bar. QScopedPointer statusBar; + QScopedPointer m_controllerControlsDialog; // Available GPU devices std::vector m_physical_devices; diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index f3418c8f..123ccce7 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -118,193 +118,310 @@ void WindowSDL::waitEvent() { } } +void WindowSDL::setKeysBindingsMap(const std::map& bindingsMap) { + keysBindingsMap = bindingsMap; +} + void WindowSDL::onResize() { SDL_GetWindowSizeInPixels(window, &width, &height); ImGui::Core::OnResize(); } +using Libraries::Pad::OrbisPadButtonDataOffset; + void WindowSDL::onKeyPress(const SDL_Event* event) { - using Libraries::Pad::OrbisPadButtonDataOffset; - -#ifdef __APPLE__ - // Use keys that are more friendly for keyboards without a keypad. - // Once there are key binding options this won't be necessary. - constexpr SDL_Keycode CrossKey = SDLK_N; - constexpr SDL_Keycode CircleKey = SDLK_B; - constexpr SDL_Keycode SquareKey = SDLK_V; - constexpr SDL_Keycode TriangleKey = SDLK_C; -#else - constexpr SDL_Keycode CrossKey = SDLK_KP_2; - constexpr SDL_Keycode CircleKey = SDLK_KP_6; - constexpr SDL_Keycode SquareKey = SDLK_KP_4; - constexpr SDL_Keycode TriangleKey = SDLK_KP_8; -#endif - u32 button = 0; Input::Axis axis = Input::Axis::AxisMax; int axisvalue = 0; int ax = 0; - switch (event->key.key) { - case SDLK_UP: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP; - break; - case SDLK_DOWN: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN; - break; - case SDLK_LEFT: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT; - break; - case SDLK_RIGHT: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT; - break; - case TriangleKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE; - break; - case CircleKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE; - break; - case CrossKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS; - break; - case SquareKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE; - break; - case SDLK_RETURN: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS; - break; - case SDLK_A: - axis = Input::Axis::LeftX; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += -127; - } else { - axisvalue = 0; + + bool keyHandlingPending = true; + if (!keysBindingsMap.empty()) { + + std::optional ps4KeyOpt; + auto foundIt = keysBindingsMap.find(event->key.key); + if (foundIt != keysBindingsMap.end()) { + ps4KeyOpt = foundIt->second; } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_D: - axis = Input::Axis::LeftX; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 127; - } else { - axisvalue = 0; + + // No support for modifiers (yet) + if (ps4KeyOpt.has_value() && (event->key.mod == SDL_KMOD_NONE)) { + keyHandlingPending = false; + + auto ps4Key = ps4KeyOpt.value(); + if (ps4Key == KeysMapping::Start_Key) + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS; + if (ps4Key == KeysMapping::Triangle_Key) + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE; + if (ps4Key == KeysMapping::Circle_Key) + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE; + if (ps4Key == KeysMapping::Cross_Key) + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS; + if (ps4Key == KeysMapping::Square_Key) + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE; + if (ps4Key == KeysMapping::R1_Key) + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1; + if (ps4Key == KeysMapping::R2_Key) + handleR2Key(event, button, axis, axisvalue, ax); + if (ps4Key == KeysMapping::DPadLeft_Key) + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT; + if (ps4Key == KeysMapping::DPadRight_Key) + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT; + if (ps4Key == KeysMapping::DPadDown_Key) + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN; + if (ps4Key == KeysMapping::DPadUp_Key) + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP; + if (ps4Key == KeysMapping::LAnalogLeft_Key) + handleLAnalogLeftKey(event, button, axis, axisvalue, ax); + if (ps4Key == KeysMapping::LAnalogUp_Key) + handleLAnalogUpKey(event, button, axis, axisvalue, ax); + if (ps4Key == KeysMapping::LAnalogDown_Key) + handleLAnalogDownKey(event, button, axis, axisvalue, ax); + if (ps4Key == KeysMapping::RAnalogLeft_Key) + handleRAnalogLeftKey(event, button, axis, axisvalue, ax); + if (ps4Key == KeysMapping::RAnalogRight_Key) + handleRAnalogRightKey(event, button, axis, axisvalue, ax); + if (ps4Key == KeysMapping::RAnalogUp_Key) + handleRAnalogUpKey(event, button, axis, axisvalue, ax); + if (ps4Key == KeysMapping::RAnalogDown_Key) + handleRAnalogDownKey(event, button, axis, axisvalue, ax); } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_W: - axis = Input::Axis::LeftY; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += -127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_S: - axis = Input::Axis::LeftY; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_J: - axis = Input::Axis::RightX; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += -127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_L: - axis = Input::Axis::RightX; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_I: - axis = Input::Axis::RightY; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += -127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_K: - axis = Input::Axis::RightY; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_X: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3; - break; - case SDLK_M: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3; - break; - case SDLK_Q: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L1; - break; - case SDLK_U: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1; - break; - case SDLK_E: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L2; - axis = Input::Axis::TriggerLeft; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 255; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(0, 0x80, axisvalue); - break; - case SDLK_O: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2; - axis = Input::Axis::TriggerRight; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 255; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(0, 0x80, axisvalue); - break; - case SDLK_SPACE: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD; - break; - case SDLK_F11: - if (event->type == SDL_EVENT_KEY_DOWN) { - { + } + + if (keyHandlingPending) { + switch (event->key.key) { + case SDLK_UP: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP; + break; + case SDLK_DOWN: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN; + break; + case SDLK_LEFT: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT; + break; + case SDLK_RIGHT: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT; + break; + case Triangle_Key: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE; + break; + case Circle_Key: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE; + break; + case Cross_Key: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS; + break; + case Square_Key: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE; + break; + case SDLK_KP_8: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE; + break; + case SDLK_KP_6: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE; + break; + case SDLK_KP_2: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS; + break; + case SDLK_KP_4: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE; + break; + case SDLK_RETURN: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS; + break; + case SDLK_A: + handleLAnalogLeftKey(event, button, axis, axisvalue, ax); + break; + case SDLK_D: + handleLAnalogRightKey(event, button, axis, axisvalue, ax); + break; + case SDLK_W: + handleLAnalogUpKey(event, button, axis, axisvalue, ax); + break; + case SDLK_S: + handleLAnalogDownKey(event, button, axis, axisvalue, ax); + if (event->key.mod == SDL_KMOD_LCTRL) { + // Trigger rdoc capture + VideoCore::TriggerCapture(); + } + break; + case SDLK_J: + handleRAnalogLeftKey(event, button, axis, axisvalue, ax); + break; + case SDLK_L: + handleRAnalogRightKey(event, button, axis, axisvalue, ax); + break; + case SDLK_I: + handleRAnalogUpKey(event, button, axis, axisvalue, ax); + break; + case SDLK_K: + handleRAnalogDownKey(event, button, axis, axisvalue, ax); + break; + case SDLK_X: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3; + break; + case SDLK_M: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3; + break; + case SDLK_Q: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L1; + break; + case SDLK_U: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1; + break; + case SDLK_E: + handleL2Key(event, button, axis, axisvalue, ax); + break; + case SDLK_O: + handleR2Key(event, button, axis, axisvalue, ax); + break; + case SDLK_SPACE: + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD; + break; + case SDLK_F11: + if (event->type == SDL_EVENT_KEY_DOWN) { SDL_WindowFlags flag = SDL_GetWindowFlags(window); bool is_fullscreen = flag & SDL_WINDOW_FULLSCREEN; SDL_SetWindowFullscreen(window, !is_fullscreen); } + break; + case SDLK_F12: + if (event->type == SDL_EVENT_KEY_DOWN) { + // Trigger rdoc capture + VideoCore::TriggerCapture(); + } + break; + default: + break; } - break; - case SDLK_F12: - if (event->type == SDL_EVENT_KEY_DOWN) { - // Trigger rdoc capture - VideoCore::TriggerCapture(); - } - break; - default: - break; } + if (button != 0) { controller->CheckButton(0, button, event->type == SDL_EVENT_KEY_DOWN); } if (axis != Input::Axis::AxisMax) { - controller->Axis(0, axis, ax); + if (event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || + event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) { + controller->Axis(0, axis, Input::GetAxis(0, 0x8000, event->gaxis.value)); + + } else { + controller->Axis(0, axis, Input::GetAxis(-0x8000, 0x8000, event->gaxis.value)); + } } } +void WindowSDL::handleR2Key(const SDL_Event* event, u32& button, Input::Axis& axis, int& axisvalue, + int& ax) { + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2; + axis = Input::Axis::TriggerRight; + if (event->type == SDL_EVENT_KEY_DOWN) { + axisvalue += 255; + } else { + axisvalue = 0; + } + ax = Input::GetAxis(0, 0x80, axisvalue); +} + +void WindowSDL::handleL2Key(const SDL_Event* event, u32& button, Input::Axis& axis, int& axisvalue, + int& ax) { + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L2; + axis = Input::Axis::TriggerLeft; + if (event->type == SDL_EVENT_KEY_DOWN) { + axisvalue += 255; + } else { + axisvalue = 0; + } + ax = Input::GetAxis(0, 0x80, axisvalue); +} + +void WindowSDL::handleLAnalogRightKey(const SDL_Event* event, u32& button, Input::Axis& axis, + int& axisvalue, int& ax) { + axis = Input::Axis::LeftX; + if (event->type == SDL_EVENT_KEY_DOWN) { + axisvalue += 127; + } else { + axisvalue = 0; + } + ax = Input::GetAxis(-0x80, 0x80, axisvalue); +} + +void WindowSDL::handleLAnalogLeftKey(const SDL_Event* event, u32& button, Input::Axis& axis, + int& axisvalue, int& ax) { + axis = Input::Axis::LeftX; + if (event->type == SDL_EVENT_KEY_DOWN) { + axisvalue += -127; + } else { + axisvalue = 0; + } + ax = Input::GetAxis(-0x80, 0x80, axisvalue); +} + +void WindowSDL::handleLAnalogUpKey(const SDL_Event* event, u32& button, Input::Axis& axis, + int& axisvalue, int& ax) { + axis = Input::Axis::LeftY; + if (event->type == SDL_EVENT_KEY_DOWN) { + axisvalue += -127; + } else { + axisvalue = 0; + } + ax = Input::GetAxis(-0x80, 0x80, axisvalue); +} + +void WindowSDL::handleLAnalogDownKey(const SDL_Event* event, u32& button, Input::Axis& axis, + int& axisvalue, int& ax) { + axis = Input::Axis::LeftY; + if (event->type == SDL_EVENT_KEY_DOWN) { + axisvalue += 127; + } else { + axisvalue = 0; + } + ax = Input::GetAxis(-0x80, 0x80, axisvalue); +} + +void WindowSDL::handleRAnalogRightKey(const SDL_Event* event, u32& button, Input::Axis& axis, + int& axisvalue, int& ax) { + axis = Input::Axis::RightX; + if (event->type == SDL_EVENT_KEY_DOWN) { + axisvalue += 127; + } else { + axisvalue = 0; + } + ax = Input::GetAxis(-0x80, 0x80, axisvalue); +} + +void WindowSDL::handleRAnalogLeftKey(const SDL_Event* event, u32& button, Input::Axis& axis, + int& axisvalue, int& ax) { + axis = Input::Axis::RightX; + if (event->type == SDL_EVENT_KEY_DOWN) { + axisvalue += -127; + } else { + axisvalue = 0; + } + ax = Input::GetAxis(-0x80, 0x80, axisvalue); +} + +void WindowSDL::handleRAnalogUpKey(const SDL_Event* event, u32& button, Input::Axis& axis, + int& axisvalue, int& ax) { + axis = Input::Axis::RightY; + if (event->type == SDL_EVENT_KEY_DOWN) { + axisvalue += -127; + } else { + axisvalue = 0; + } + ax = Input::GetAxis(-0x80, 0x80, axisvalue); +} + +void WindowSDL::handleRAnalogDownKey(const SDL_Event* event, u32& button, Input::Axis& axis, + int& axisvalue, int& ax) { + axis = Input::Axis::RightY; + if (event->type == SDL_EVENT_KEY_DOWN) { + axisvalue += 127; + } else { + axisvalue = 0; + } + ax = Input::GetAxis(-0x80, 0x80, axisvalue); +} + void WindowSDL::onGamepadEvent(const SDL_Event* event) { using Libraries::Pad::OrbisPadButtonDataOffset; @@ -389,4 +506,4 @@ int WindowSDL::sdlGamepadToOrbisButton(u8 button) { } } -} // namespace Frontend +} // namespace Frontend \ No newline at end of file diff --git a/src/sdl_window.h b/src/sdl_window.h index 2a5aeb38..10ecd081 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -3,8 +3,10 @@ #pragma once +#include #include #include "common/types.h" +#include "input/keys_constants.h" struct SDL_Window; struct SDL_Gamepad; @@ -12,7 +14,8 @@ union SDL_Event; namespace Input { class GameController; -} +enum class Axis; +} // namespace Input namespace Frontend { @@ -68,6 +71,8 @@ public: void waitEvent(); + void setKeysBindingsMap(const std::map& bindingsMap); + private: void onResize(); void onKeyPress(const SDL_Event* event); @@ -75,12 +80,34 @@ private: int sdlGamepadToOrbisButton(u8 button); + void handleR2Key(const SDL_Event* event, u32& button, Input::Axis& axis, int& axisvalue, + int& ax); + void handleL2Key(const SDL_Event* event, u32& button, Input::Axis& axis, int& axisvalue, + int& ax); + void handleLAnalogRightKey(const SDL_Event* event, u32& button, Input::Axis& axis, + int& axisvalue, int& ax); + void handleLAnalogLeftKey(const SDL_Event* event, u32& button, Input::Axis& axis, + int& axisvalue, int& ax); + void handleLAnalogUpKey(const SDL_Event* event, u32& button, Input::Axis& axis, int& axisvalue, + int& ax); + void handleLAnalogDownKey(const SDL_Event* event, u32& button, Input::Axis& axis, + int& axisvalue, int& ax); + void handleRAnalogRightKey(const SDL_Event* event, u32& button, Input::Axis& axis, + int& axisvalue, int& ax); + void handleRAnalogLeftKey(const SDL_Event* event, u32& button, Input::Axis& axis, + int& axisvalue, int& ax); + void handleRAnalogUpKey(const SDL_Event* event, u32& button, Input::Axis& axis, int& axisvalue, + int& ax); + void handleRAnalogDownKey(const SDL_Event* event, u32& button, Input::Axis& axis, + int& axisvalue, int& ax); + private: s32 width; s32 height; Input::GameController* controller; WindowSystemInfo window_info{}; SDL_Window* window{}; + std::map keysBindingsMap; bool is_shown{}; bool is_open{true}; }; diff --git a/src/shadps4.qrc b/src/shadps4.qrc index c22b837b..00f51bc6 100644 --- a/src/shadps4.qrc +++ b/src/shadps4.qrc @@ -14,6 +14,7 @@ images/exit_icon.png images/settings_icon.png images/controller_icon.png + images/PS4_controller_scheme.png images/refresh_icon.png images/list_mode_icon.png images/flag_jp.png