Add UI to configure keyboard-to-controller mapping (#308)

* Add UI to configure keyboard-to-controller mapping

* Add an optional "---fix" argument to format-checking script

* clang fix

---------

Co-authored-by: georgemoralis <giorgosmrls@gmail.com>
This commit is contained in:
Vasyl_Baran 2024-09-11 08:51:18 +03:00 committed by GitHub
parent 74c2888aaa
commit fdb13a3b90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 1436 additions and 173 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -53,6 +53,7 @@ std::vector<std::string> m_recent_files;
std::string emulator_language = "en";
// Settings
u32 m_language = 1; // english
std::map<u32, KeysMapping> m_keyboard_binding_map;
bool isNeoMode() {
return isNeo;
@ -283,7 +284,12 @@ void setRecentFiles(const std::vector<std::string>& recentFiles) {
void setEmulatorLanguage(std::string language) {
emulator_language = language;
}
void setKeyboardBindingMap(std::map<u32, KeysMapping> map) {
m_keyboard_binding_map = map;
}
const std::map<u32, KeysMapping>& 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<int>(settings, "consoleLanguage", 1);
}
if (data.contains("Controls")) {
auto controls = toml::find<toml::table>(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<u32, KeysMapping>
for (const auto& [key, value] : keyboardBindings) {
try {
Uint32 int_key = static_cast<u32>(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<KeysMapping>(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<int>(value);
}
data["Controls"]["keyboardBindings"] = keyboardBindingsTable;
data["Settings"]["consoleLanguage"] = m_language;
std::ofstream file(path, std::ios::out);

View file

@ -4,7 +4,10 @@
#pragma once
#include <filesystem>
#include <map>
#include <vector>
#include "SDL3/SDL_stdinc.h"
#include "input/keys_constants.h"
#include "types.h"
namespace Config {
@ -79,6 +82,8 @@ void setPkgViewer(const std::vector<std::string>& pkgList);
void setElfViewer(const std::vector<std::string>& elfList);
void setRecentFiles(const std::vector<std::string>& recentFiles);
void setEmulatorLanguage(std::string language);
void setKeyboardBindingMap(std::map<u32, KeysMapping> map);
const std::map<u32, KeysMapping>& getKeyboardBindingMap();
u32 getMainWindowGeometryX();
u32 getMainWindowGeometryY();

View file

@ -146,7 +146,7 @@ void Emulator::Run(const std::filesystem::path& file) {
}
window = std::make_unique<Frontend::WindowSDL>(
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;

View file

@ -34,6 +34,6 @@ private:
Input::GameController* controller;
Core::Linker* linker;
std::unique_ptr<Frontend::WindowSDL> window;
std::map<u32, KeysMapping> m_keysBindingsMap;
};
} // namespace Core

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View file

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

View file

@ -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 <QMessageBox>
#include <QTimer>
#include <iostream>
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<u32, KeysMapping>& 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<QKeySequenceEdit*>(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
});
}
}
}

View file

@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QDialog>
#include <QKeySequenceEdit>
#include <QMainWindow>
#include <SDL3/SDL_keycode.h>
#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<u32, KeysMapping>& 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<QKeySequenceEdit*> m_listOfKeySequenceEdits;
std::map<u32, KeysMapping> m_keysMap;
std::map<KeysMapping, u32> m_reverseKeysMap;
};

View file

@ -0,0 +1,439 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later
-->
<ui version="4.0">
<class>KeyboardControlsWindow</class>
<widget class="QDialog" name="KeyboardControlsWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1155</width>
<height>790</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>1148</width>
<height>731</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>1155</width>
<height>790</height>
</size>
</property>
<property name="windowTitle">
<string>Configure Controller Bindings</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<widget class="QTabWidget" name="tabWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>1131</width>
<height>711</height>
</rect>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="port_1">
<attribute name="title">
<string>Port 1</string>
</attribute>
<widget class="QWidget" name="centralwidget" native="true">
<property name="geometry">
<rect>
<x>-10</x>
<y>20</y>
<width>1131</width>
<height>621</height>
</rect>
</property>
<widget class="QWidget" name="bgWidget" native="true">
<property name="geometry">
<rect>
<x>170</x>
<y>50</y>
<width>870</width>
<height>522</height>
</rect>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true"> QWidget { background-image: url(:images/PS4_controller_scheme.png);
background-repeat: no-repeat;
background-position: center; }
</string>
</property>
<widget class="QKeySequenceEdit" name="StartKeySequenceEdit">
<property name="geometry">
<rect>
<x>550</x>
<y>0</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="SelectKeySequenceEdit">
<property name="geometry">
<rect>
<x>260</x>
<y>0</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="LAnalogDownkeySequenceEdit">
<property name="geometry">
<rect>
<x>280</x>
<y>480</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="LAnalogLeftkeySequenceEdit">
<property name="geometry">
<rect>
<x>240</x>
<y>440</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="LAnalogUpkeySequenceEdit">
<property name="geometry">
<rect>
<x>280</x>
<y>400</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="LAnalogRightkeySequenceEdit">
<property name="geometry">
<rect>
<x>320</x>
<y>440</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="PSkeySequenceEdit">
<property name="geometry">
<rect>
<x>400</x>
<y>400</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="RAnalogDownkeySequenceEdit">
<property name="geometry">
<rect>
<x>520</x>
<y>480</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="RAnalogLeftkeySequenceEdit">
<property name="geometry">
<rect>
<x>480</x>
<y>440</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="RAnalogUpkeySequenceEdit">
<property name="geometry">
<rect>
<x>520</x>
<y>400</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="RAnalogRightkeySequenceEdit">
<property name="geometry">
<rect>
<x>560</x>
<y>440</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
</widget>
<widget class="QKeySequenceEdit" name="DPadLeftkeySequenceEdit">
<property name="geometry">
<rect>
<x>10</x>
<y>230</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="DPadRightkeySequenceEdit">
<property name="geometry">
<rect>
<x>90</x>
<y>230</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="DPadUpkeySequenceEdit">
<property name="geometry">
<rect>
<x>50</x>
<y>190</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="DPadDownkeySequenceEdit">
<property name="geometry">
<rect>
<x>50</x>
<y>270</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="L2keySequenceEdit">
<property name="geometry">
<rect>
<x>90</x>
<y>40</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="L1keySequenceEdit">
<property name="geometry">
<rect>
<x>90</x>
<y>110</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="CrossKeySequenceEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>1050</x>
<y>380</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="R2KeySequenceEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>1050</x>
<y>30</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="CircleKeySequenceEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>1050</x>
<y>240</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="R1KeySequenceEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>1050</x>
<y>100</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="SquareKeySequenceEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>1050</x>
<y>310</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
<widget class="QKeySequenceEdit" name="TriangleKeySequenceEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>1050</x>
<y>170</y>
<width>71</width>
<height>40</height>
</rect>
</property>
<property name="maximumSequenceLength">
<longLong>1</longLong>
</property>
</widget>
</widget>
</widget>
<widget class="QWidget" name="port_2">
<attribute name="title">
<string>Port 2</string>
</attribute>
</widget>
</widget>
<widget class="QPushButton" name="applyButton">
<property name="geometry">
<rect>
<x>1030</x>
<y>740</y>
<width>100</width>
<height>32</height>
</rect>
</property>
<property name="text">
<string>Apply</string>
</property>
</widget>
<widget class="QPushButton" name="cancelButton">
<property name="geometry">
<rect>
<x>890</x>
<y>740</y>
<width>100</width>
<height>32</height>
</rect>
</property>
<property name="text">
<string>Cancel</string>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -50,6 +50,7 @@ bool MainWindow::Init() {
auto end = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(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<u32, KeysMapping> MainWindow::getKeysMapping() {
return m_controllerControlsDialog->getKeysMapping();
}
void MainWindow::SetLastUsedTheme() {
Theme lastTheme = static_cast<Theme>(Config::getMainWindowTheme());
m_window_themes.SetWindowTheme(lastTheme, ui->mw_searchbar);

View file

@ -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<u32, KeysMapping> 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<ElfViewer> m_elf_viewer;
// Status Bar.
QScopedPointer<QStatusBar> statusBar;
QScopedPointer<KeyboardControlsWindow> m_controllerControlsDialog;
// Available GPU devices
std::vector<QString> m_physical_devices;

View file

@ -118,193 +118,310 @@ void WindowSDL::waitEvent() {
}
}
void WindowSDL::setKeysBindingsMap(const std::map<u32, KeysMapping>& 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<KeysMapping> 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

View file

@ -3,8 +3,10 @@
#pragma once
#include <map>
#include <string>
#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<u32, KeysMapping>& 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<u32, KeysMapping> keysBindingsMap;
bool is_shown{};
bool is_open{true};
};

View file

@ -14,6 +14,7 @@
<file>images/exit_icon.png</file>
<file>images/settings_icon.png</file>
<file>images/controller_icon.png</file>
<file>images/PS4_controller_scheme.png</file>
<file>images/refresh_icon.png</file>
<file>images/list_mode_icon.png</file>
<file>images/flag_jp.png</file>