From 3270cfe602dd0cb1ec47a777c49ac2ea239f88bf Mon Sep 17 00:00:00 2001 From: Paris Oplopoios Date: Thu, 21 Mar 2024 15:54:18 +0200 Subject: [PATCH] First step towards configurable keyboard mappings (#464) * Configurable keyboard mappings * Cleanup * Cleanup * Biggest mistake of my career * format * Fix naming convention --------- Co-authored-by: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> --- CMakeLists.txt | 6 +- include/input_mappings.hpp | 22 +++++ include/panda_qt/main_window.hpp | 4 +- include/panda_sdl/frontend_sdl.hpp | 4 + include/services/hid.hpp | 1 + src/panda_qt/main_window.cpp | 69 ++++++--------- src/panda_qt/mappings.cpp | 25 ++++++ src/panda_sdl/frontend_sdl.cpp | 132 ++++++++++++----------------- src/panda_sdl/mappings.cpp | 25 ++++++ 9 files changed, 162 insertions(+), 126 deletions(-) create mode 100644 include/input_mappings.hpp create mode 100644 src/panda_qt/mappings.cpp create mode 100644 src/panda_sdl/mappings.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ca0bcdd2..dc230bf6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -201,7 +201,7 @@ set(AUDIO_SOURCE_FILES src/core/audio/dsp_core.cpp src/core/audio/null_core.cpp ) set(RENDERER_SW_SOURCE_FILES src/core/renderer_sw/renderer_sw.cpp) -set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp +set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp include/input_mappings.hpp include/cpu.hpp include/cpu_dynarmic.hpp include/memory.hpp include/renderer.hpp include/kernel/kernel.hpp include/dynarmic_cp15.hpp include/kernel/resource_limits.hpp include/kernel/kernel_types.hpp include/kernel/config_mem.hpp include/services/service_manager.hpp include/services/apt.hpp @@ -439,7 +439,7 @@ if(NOT BUILD_HYDRA_CORE) endif() set(FRONTEND_SOURCE_FILES src/panda_qt/main.cpp src/panda_qt/screen.cpp src/panda_qt/main_window.cpp src/panda_qt/about_window.cpp - src/panda_qt/config_window.cpp src/panda_qt/zep.cpp src/panda_qt/text_editor.cpp src/panda_qt/cheats_window.cpp + src/panda_qt/config_window.cpp src/panda_qt/zep.cpp src/panda_qt/text_editor.cpp src/panda_qt/cheats_window.cpp src/panda_qt/mappings.cpp ) set(FRONTEND_HEADER_FILES include/panda_qt/screen.hpp include/panda_qt/main_window.hpp include/panda_qt/about_window.hpp include/panda_qt/config_window.hpp include/panda_qt/text_editor.hpp include/panda_qt/cheats_window.hpp @@ -478,7 +478,7 @@ if(NOT BUILD_HYDRA_CORE) docs/img/rsob_icon.png docs/img/rstarstruck_icon.png ) else() - set(FRONTEND_SOURCE_FILES src/panda_sdl/main.cpp src/panda_sdl/frontend_sdl.cpp) + set(FRONTEND_SOURCE_FILES src/panda_sdl/main.cpp src/panda_sdl/frontend_sdl.cpp src/panda_sdl/mappings.cpp) set(FRONTEND_HEADER_FILES "") endif() diff --git a/include/input_mappings.hpp b/include/input_mappings.hpp new file mode 100644 index 00000000..177f1d51 --- /dev/null +++ b/include/input_mappings.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include "helpers.hpp" +#include "services/hid.hpp" + +struct InputMappings { + using Scancode = u32; + using Container = std::unordered_map; + + u32 getMapping(Scancode scancode) const { + auto it = container.find(scancode); + return it != container.end() ? it->second : HID::Keys::Null; + } + + void setMapping(Scancode scancode, u32 key) { container[scancode] = key; } + static InputMappings defaultKeyboardMappings(); + + private: + Container container; +}; diff --git a/include/panda_qt/main_window.hpp b/include/panda_qt/main_window.hpp index c2db9ac1..c3f99c29 100644 --- a/include/panda_qt/main_window.hpp +++ b/include/panda_qt/main_window.hpp @@ -11,6 +11,7 @@ #include #include "emulator.hpp" +#include "input_mappings.hpp" #include "panda_qt/about_window.hpp" #include "panda_qt/config_window.hpp" #include "panda_qt/cheats_window.hpp" @@ -87,6 +88,7 @@ class MainWindow : public QMainWindow { std::mutex messageQueueMutex; std::vector messageQueue; + InputMappings keyboardMappings; ScreenWidget screen; AboutWindow* aboutWindow; ConfigWindow* configWindow; @@ -120,4 +122,4 @@ class MainWindow : public QMainWindow { void loadLuaScript(const std::string& code); void editCheat(u32 handle, const std::vector& cheat, const std::function& callback); -}; \ No newline at end of file +}; diff --git a/include/panda_sdl/frontend_sdl.hpp b/include/panda_sdl/frontend_sdl.hpp index 2d206175..dd6ab6c0 100644 --- a/include/panda_sdl/frontend_sdl.hpp +++ b/include/panda_sdl/frontend_sdl.hpp @@ -5,6 +5,7 @@ #include #include "emulator.hpp" +#include "input_mappings.hpp" class FrontendSDL { Emulator emu; @@ -16,9 +17,12 @@ class FrontendSDL { FrontendSDL(); bool loadROM(const std::filesystem::path& path); void run(); + u32 getMapping(InputMappings::Scancode scancode) { return keyboardMappings.getMapping(scancode); } SDL_Window* window = nullptr; SDL_GameController* gameController = nullptr; + InputMappings keyboardMappings; + int gameControllerID; bool programRunning = true; diff --git a/include/services/hid.hpp b/include/services/hid.hpp index febd7bd6..d9018a4f 100644 --- a/include/services/hid.hpp +++ b/include/services/hid.hpp @@ -10,6 +10,7 @@ namespace HID::Keys { enum : u32 { + Null = 0, A = 1 << 0, B = 1 << 1, Select = 1 << 2, diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp index dff4c171..17f9ff26 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -8,8 +8,9 @@ #include #include "cheats.hpp" +#include "input_mappings.hpp" -MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent), screen(this) { +MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent), keyboardMappings(InputMappings::defaultKeyboardMappings()), screen(this) { setWindowTitle("Alber"); // Enable drop events for loading ROMs setAcceptDrops(true); @@ -298,29 +299,21 @@ void MainWindow::keyPressEvent(QKeyEvent* event) { sendMessage(message); }; - switch (event->key()) { - case Qt::Key_L: pressKey(HID::Keys::A); break; - case Qt::Key_K: pressKey(HID::Keys::B); break; - case Qt::Key_O: pressKey(HID::Keys::X); break; - case Qt::Key_I: pressKey(HID::Keys::Y); break; + u32 key = keyboardMappings.getMapping(event->key()); + if (key != HID::Keys::Null) { + switch (key) { + case HID::Keys::CirclePadUp: setCirclePad(MessageType::SetCirclePadY, 0x9C); break; + case HID::Keys::CirclePadDown: setCirclePad(MessageType::SetCirclePadY, -0x9C); break; + case HID::Keys::CirclePadLeft: setCirclePad(MessageType::SetCirclePadX, -0x9C); break; + case HID::Keys::CirclePadRight: setCirclePad(MessageType::SetCirclePadX, 0x9C); break; - case Qt::Key_Q: pressKey(HID::Keys::L); break; - case Qt::Key_P: pressKey(HID::Keys::R); break; - - case Qt::Key_W: setCirclePad(MessageType::SetCirclePadY, 0x9C); break; - case Qt::Key_A: setCirclePad(MessageType::SetCirclePadX, -0x9C); break; - case Qt::Key_S: setCirclePad(MessageType::SetCirclePadY, -0x9C); break; - case Qt::Key_D: setCirclePad(MessageType::SetCirclePadX, 0x9C); break; - - case Qt::Key_Right: pressKey(HID::Keys::Right); break; - case Qt::Key_Left: pressKey(HID::Keys::Left); break; - case Qt::Key_Up: pressKey(HID::Keys::Up); break; - case Qt::Key_Down: pressKey(HID::Keys::Down); break; - - case Qt::Key_Return: pressKey(HID::Keys::Start); break; - case Qt::Key_Backspace: pressKey(HID::Keys::Select); break; - case Qt::Key_F4: sendMessage(EmulatorMessage{.type = MessageType::TogglePause}); break; - case Qt::Key_F5: sendMessage(EmulatorMessage{.type = MessageType::Reset}); break; + default: pressKey(key); break; + } + } else { + switch (event->key()) { + case Qt::Key_F4: sendMessage(EmulatorMessage{.type = MessageType::TogglePause}); break; + case Qt::Key_F5: sendMessage(EmulatorMessage{.type = MessageType::Reset}); break; + } } } @@ -337,28 +330,16 @@ void MainWindow::keyReleaseEvent(QKeyEvent* event) { sendMessage(message); }; - switch (event->key()) { - case Qt::Key_L: releaseKey(HID::Keys::A); break; - case Qt::Key_K: releaseKey(HID::Keys::B); break; - case Qt::Key_O: releaseKey(HID::Keys::X); break; - case Qt::Key_I: releaseKey(HID::Keys::Y); break; + u32 key = keyboardMappings.getMapping(event->key()); + if (key != HID::Keys::Null) { + switch (key) { + case HID::Keys::CirclePadUp: releaseCirclePad(MessageType::SetCirclePadY); break; + case HID::Keys::CirclePadDown: releaseCirclePad(MessageType::SetCirclePadY); break; + case HID::Keys::CirclePadLeft: releaseCirclePad(MessageType::SetCirclePadX); break; + case HID::Keys::CirclePadRight: releaseCirclePad(MessageType::SetCirclePadX); break; - case Qt::Key_Q: releaseKey(HID::Keys::L); break; - case Qt::Key_P: releaseKey(HID::Keys::R); break; - - case Qt::Key_W: - case Qt::Key_S: releaseCirclePad(MessageType::SetCirclePadY); break; - - case Qt::Key_A: - case Qt::Key_D: releaseCirclePad(MessageType::SetCirclePadX); break; - - case Qt::Key_Right: releaseKey(HID::Keys::Right); break; - case Qt::Key_Left: releaseKey(HID::Keys::Left); break; - case Qt::Key_Up: releaseKey(HID::Keys::Up); break; - case Qt::Key_Down: releaseKey(HID::Keys::Down); break; - - case Qt::Key_Return: releaseKey(HID::Keys::Start); break; - case Qt::Key_Backspace: releaseKey(HID::Keys::Select); break; + default: releaseKey(key); break; + } } } diff --git a/src/panda_qt/mappings.cpp b/src/panda_qt/mappings.cpp new file mode 100644 index 00000000..22741a73 --- /dev/null +++ b/src/panda_qt/mappings.cpp @@ -0,0 +1,25 @@ +#include "input_mappings.hpp" + +#include + +InputMappings InputMappings::defaultKeyboardMappings() { + InputMappings mappings; + mappings.setMapping(Qt::Key_L, HID::Keys::A); + mappings.setMapping(Qt::Key_K, HID::Keys::B); + mappings.setMapping(Qt::Key_O, HID::Keys::X); + mappings.setMapping(Qt::Key_I, HID::Keys::Y); + mappings.setMapping(Qt::Key_Q, HID::Keys::L); + mappings.setMapping(Qt::Key_P, HID::Keys::R); + mappings.setMapping(Qt::Key_Up, HID::Keys::Up); + mappings.setMapping(Qt::Key_Down, HID::Keys::Down); + mappings.setMapping(Qt::Key_Right, HID::Keys::Right); + mappings.setMapping(Qt::Key_Left, HID::Keys::Left); + mappings.setMapping(Qt::Key_Return, HID::Keys::Start); + mappings.setMapping(Qt::Key_Backspace, HID::Keys::Select); + mappings.setMapping(Qt::Key_W, HID::Keys::CirclePadUp); + mappings.setMapping(Qt::Key_S, HID::Keys::CirclePadDown); + mappings.setMapping(Qt::Key_D, HID::Keys::CirclePadRight); + mappings.setMapping(Qt::Key_A, HID::Keys::CirclePadLeft); + + return mappings; +} \ No newline at end of file diff --git a/src/panda_sdl/frontend_sdl.cpp b/src/panda_sdl/frontend_sdl.cpp index b6486b45..f94f98f4 100644 --- a/src/panda_sdl/frontend_sdl.cpp +++ b/src/panda_sdl/frontend_sdl.cpp @@ -2,7 +2,7 @@ #include -FrontendSDL::FrontendSDL() { +FrontendSDL::FrontendSDL() : keyboardMappings(InputMappings::defaultKeyboardMappings()) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) { Helpers::panic("Failed to initialize SDL2"); } @@ -92,95 +92,71 @@ void FrontendSDL::run() { programRunning = false; return; - case SDL_KEYDOWN: + case SDL_KEYDOWN: { if (emu.romType == ROMType::None) break; - switch (event.key.keysym.sym) { - case SDLK_l: hid.pressKey(Keys::A); break; - case SDLK_k: hid.pressKey(Keys::B); break; - case SDLK_o: hid.pressKey(Keys::X); break; - case SDLK_i: hid.pressKey(Keys::Y); break; - - case SDLK_q: hid.pressKey(Keys::L); break; - case SDLK_p: hid.pressKey(Keys::R); break; - - case SDLK_RIGHT: hid.pressKey(Keys::Right); break; - case SDLK_LEFT: hid.pressKey(Keys::Left); break; - case SDLK_UP: hid.pressKey(Keys::Up); break; - case SDLK_DOWN: hid.pressKey(Keys::Down); break; - - case SDLK_w: - hid.setCirclepadY(0x9C); - keyboardAnalogY = true; - break; - - case SDLK_a: - hid.setCirclepadX(-0x9C); - keyboardAnalogX = true; - break; - - case SDLK_s: - hid.setCirclepadY(-0x9C); - keyboardAnalogY = true; - break; - - case SDLK_d: - hid.setCirclepadX(0x9C); - keyboardAnalogX = true; - break; - - case SDLK_RETURN: hid.pressKey(Keys::Start); break; - case SDLK_BACKSPACE: hid.pressKey(Keys::Select); break; - - // Use the F4 button as a hot-key to pause or resume the emulator - // We can't use the audio play/pause buttons because it's annoying - case SDLK_F4: { - emu.togglePause(); - break; + u32 key = getMapping(event.key.keysym.scancode); + if (key != HID::Keys::Null) { + switch (key) { + case HID::Keys::CirclePadRight: + hid.setCirclepadX(0x9C); + keyboardAnalogX = true; + break; + case HID::Keys::CirclePadLeft: + hid.setCirclepadX(-0x9C); + keyboardAnalogX = true; + break; + case HID::Keys::CirclePadUp: + hid.setCirclepadY(0x9C); + keyboardAnalogY = true; + break; + case HID::Keys::CirclePadDown: + hid.setCirclepadY(-0x9C); + keyboardAnalogY = true; + break; + default: hid.pressKey(key); break; } + } else { + switch (event.key.keysym.sym) { + // Use the F4 button as a hot-key to pause or resume the emulator + // We can't use the audio play/pause buttons because it's annoying + case SDLK_F4: { + emu.togglePause(); + break; + } - // Use F5 as a reset button - case SDLK_F5: { - emu.reset(Emulator::ReloadOption::Reload); - break; + // Use F5 as a reset button + case SDLK_F5: { + emu.reset(Emulator::ReloadOption::Reload); + break; + } } } break; + } - case SDL_KEYUP: + case SDL_KEYUP: { if (emu.romType == ROMType::None) break; - switch (event.key.keysym.sym) { - case SDLK_l: hid.releaseKey(Keys::A); break; - case SDLK_k: hid.releaseKey(Keys::B); break; - case SDLK_o: hid.releaseKey(Keys::X); break; - case SDLK_i: hid.releaseKey(Keys::Y); break; - - case SDLK_q: hid.releaseKey(Keys::L); break; - case SDLK_p: hid.releaseKey(Keys::R); break; - - case SDLK_RIGHT: hid.releaseKey(Keys::Right); break; - case SDLK_LEFT: hid.releaseKey(Keys::Left); break; - case SDLK_UP: hid.releaseKey(Keys::Up); break; - case SDLK_DOWN: hid.releaseKey(Keys::Down); break; - - // Err this is probably not ideal - case SDLK_w: - case SDLK_s: - hid.setCirclepadY(0); - keyboardAnalogY = false; - break; - - case SDLK_a: - case SDLK_d: - hid.setCirclepadX(0); - keyboardAnalogX = false; - break; - - case SDLK_RETURN: hid.releaseKey(Keys::Start); break; - case SDLK_BACKSPACE: hid.releaseKey(Keys::Select); break; + u32 key = getMapping(event.key.keysym.scancode); + if (key != HID::Keys::Null) { + switch (key) { + // Err this is probably not ideal + case HID::Keys::CirclePadRight: + case HID::Keys::CirclePadLeft: + hid.setCirclepadX(0); + keyboardAnalogX = false; + break; + case HID::Keys::CirclePadUp: + case HID::Keys::CirclePadDown: + hid.setCirclepadY(0); + keyboardAnalogY = false; + break; + default: hid.releaseKey(key); break; + } } break; + } case SDL_MOUSEBUTTONDOWN: if (emu.romType == ROMType::None) break; diff --git a/src/panda_sdl/mappings.cpp b/src/panda_sdl/mappings.cpp new file mode 100644 index 00000000..0c09b852 --- /dev/null +++ b/src/panda_sdl/mappings.cpp @@ -0,0 +1,25 @@ +#include "input_mappings.hpp" + +#include + +InputMappings InputMappings::defaultKeyboardMappings() { + InputMappings mappings; + mappings.setMapping(SDLK_l, HID::Keys::A); + mappings.setMapping(SDLK_k, HID::Keys::B); + mappings.setMapping(SDLK_o, HID::Keys::X); + mappings.setMapping(SDLK_i, HID::Keys::Y); + mappings.setMapping(SDLK_q, HID::Keys::L); + mappings.setMapping(SDLK_p, HID::Keys::R); + mappings.setMapping(SDLK_UP, HID::Keys::Up); + mappings.setMapping(SDLK_DOWN, HID::Keys::Down); + mappings.setMapping(SDLK_RIGHT, HID::Keys::Right); + mappings.setMapping(SDLK_LEFT, HID::Keys::Left); + mappings.setMapping(SDLK_RETURN, HID::Keys::Start); + mappings.setMapping(SDLK_BACKSPACE, HID::Keys::Select); + mappings.setMapping(SDLK_w, HID::Keys::CirclePadUp); + mappings.setMapping(SDLK_s, HID::Keys::CirclePadDown); + mappings.setMapping(SDLK_d, HID::Keys::CirclePadRight); + mappings.setMapping(SDLK_a, HID::Keys::CirclePadLeft); + + return mappings; +} \ No newline at end of file