From 63fc6e6c4825589f896e6dfb0678c6cb81af2457 Mon Sep 17 00:00:00 2001 From: Anon Date: Fri, 16 Dec 2016 22:50:33 -0600 Subject: [PATCH] InputCore Overhaul --- src/CMakeLists.txt | 1 + src/citra/CMakeLists.txt | 2 +- src/citra/config.cpp | 10 +- src/citra/default_ini.h | 4 + src/citra/emu_window/emu_window_sdl2.cpp | 33 ++- src/citra/emu_window/emu_window_sdl2.h | 6 +- src/citra_qt/CMakeLists.txt | 7 +- src/citra_qt/bootmanager.cpp | 28 ++- src/citra_qt/bootmanager.h | 2 - src/citra_qt/config.cpp | 23 +- src/citra_qt/config.h | 3 +- src/citra_qt/configure_input.cpp | 236 ++++++++++--------- src/citra_qt/configure_input.h | 45 ++-- src/citra_qt/keybinding_names.cpp | 31 +++ src/citra_qt/keybinding_names.h | 19 ++ src/citra_qt/main.cpp | 1 - src/common/logging/backend.cpp | 3 +- src/common/logging/log.h | 4 +- src/core/CMakeLists.txt | 6 +- src/core/core.cpp | 3 + src/core/frontend/emu_window.cpp | 48 ++-- src/core/frontend/emu_window.h | 72 +----- src/core/hle/service/hid/hid.cpp | 18 +- src/core/hle/service/hid/hid.h | 11 + src/core/hw/gpu.cpp | 6 - src/core/hw/gpu.h | 3 + src/core/settings.cpp | 4 +- src/core/settings.h | 100 +++++++-- src/input_core/CMakeLists.txt | 30 +++ src/input_core/devices/device.cpp | 7 + src/input_core/devices/device.h | 49 ++++ src/input_core/devices/gamecontrollerdb.h | 190 ++++++++++++++++ src/input_core/devices/keyboard.cpp | 60 +++++ src/input_core/devices/keyboard.h | 49 ++++ src/input_core/devices/sdl_gamepad.cpp | 122 ++++++++++ src/input_core/devices/sdl_gamepad.h | 113 ++++++++++ src/input_core/input_core.cpp | 262 ++++++++++++++++++++++ src/input_core/input_core.h | 125 +++++++++++ src/input_core/key_map.cpp | 27 +++ src/input_core/key_map.h | 15 ++ src/tests/CMakeLists.txt | 2 +- 41 files changed, 1459 insertions(+), 321 deletions(-) create mode 100644 src/citra_qt/keybinding_names.cpp create mode 100644 src/citra_qt/keybinding_names.h create mode 100644 src/input_core/CMakeLists.txt create mode 100644 src/input_core/devices/device.cpp create mode 100644 src/input_core/devices/device.h create mode 100644 src/input_core/devices/gamecontrollerdb.h create mode 100644 src/input_core/devices/keyboard.cpp create mode 100644 src/input_core/devices/keyboard.h create mode 100644 src/input_core/devices/sdl_gamepad.cpp create mode 100644 src/input_core/devices/sdl_gamepad.h create mode 100644 src/input_core/input_core.cpp create mode 100644 src/input_core/input_core.h create mode 100644 src/input_core/key_map.cpp create mode 100644 src/input_core/key_map.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1e1245160..6eddedb0b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ include_directories(.) add_subdirectory(common) add_subdirectory(core) add_subdirectory(video_core) +add_subdirectory(input_core) add_subdirectory(audio_core) add_subdirectory(tests) if (ENABLE_SDL2) diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index ecb5d2dfe..79530fc14 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt @@ -18,7 +18,7 @@ create_directory_groups(${SRCS} ${HEADERS}) include_directories(${SDL2_INCLUDE_DIR}) add_executable(citra ${SRCS} ${HEADERS}) -target_link_libraries(citra core video_core audio_core common) +target_link_libraries(citra core video_core audio_core input_core common) target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad) if (MSVC) target_link_libraries(citra getopt) diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 1d0faf193..5571f7efb 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -45,17 +45,21 @@ static const std::array defaults = { SDL_SCANCODE_L, // indirectly mapped keys - SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D, -}; + SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT}; void Config::ReadValues() { // Controls for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { Settings::values.input_mappings[Settings::NativeInput::All[i]] = - sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]); + Settings::InputDeviceMapping(sdl2_config->Get( + "Controls", Settings::NativeInput::Mapping[i], std::to_string(defaults[i]))); } + Settings::values.pad_circle_modifier = + Settings::InputDeviceMapping(sdl2_config->Get("Controls", "pad_circle_modifier", "")); Settings::values.pad_circle_modifier_scale = (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5); + Settings::values.pad_circle_deadzone = + (float)sdl2_config->GetReal("Controls", "pad_circle_deadzone", 0.3); // Core Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 7996813b4..34115f9e5 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -37,6 +37,10 @@ pad_circle_modifier = # Must be in range of 0.0-1.0. Defaults to 0.5 pad_circle_modifier_scale = +# Deadzone applied to the circle pad. +# Must be in range of 0.0 - 1.0. Defaults to 0.3 +pad_circle_deadzone = + [Core] # Whether to use the Just-In-Time (JIT) compiler for CPU emulation # 0: Interpreter (slow), 1 (default): JIT (fast) diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp index 81a3abe3f..ca46614b8 100644 --- a/src/citra/emu_window/emu_window_sdl2.cpp +++ b/src/citra/emu_window/emu_window_sdl2.cpp @@ -8,13 +8,17 @@ #define SDL_MAIN_HANDLED #include #include -#include "citra/emu_window/emu_window_sdl2.h" #include "common/logging/log.h" #include "common/scm_rev.h" #include "common/string_util.h" -#include "core/frontend/key_map.h" #include "core/hle/service/hid/hid.h" #include "core/settings.h" + +#include "input_core/devices/keyboard.h" +#include "input_core/input_core.h" + +#include "citra/emu_window/emu_window_sdl2.h" + #include "video_core/video_core.h" void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { @@ -38,11 +42,14 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { } } -void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { +void EmuWindow_SDL2::OnKeyEvent(SDL_Keysym key, u8 state) { + auto keyboard = InputCore::GetKeyboard(); + KeyboardKey param = KeyboardKey(key.sym, SDL_GetKeyName(key.scancode)); + if (state == SDL_PRESSED) { - KeyMap::PressKey(*this, {key, keyboard_id}); + keyboard->KeyPressed(param); } else if (state == SDL_RELEASED) { - KeyMap::ReleaseKey(*this, {key, keyboard_id}); + keyboard->KeyReleased(param); } } @@ -57,11 +64,7 @@ void EmuWindow_SDL2::OnResize() { } EmuWindow_SDL2::EmuWindow_SDL2() { - keyboard_id = KeyMap::NewDeviceId(); - - ReloadSetKeymaps(); motion_emu = std::make_unique(*this); - SDL_SetMainReady(); // Initialize the window @@ -145,7 +148,7 @@ void EmuWindow_SDL2::PollEvents() { break; case SDL_KEYDOWN: case SDL_KEYUP: - OnKeyEvent(static_cast(event.key.keysym.scancode), event.key.state); + OnKeyEvent(event.key.keysym, event.key.state); break; case SDL_MOUSEMOTION: OnMouseMotion(event.motion.x, event.motion.y); @@ -169,17 +172,7 @@ void EmuWindow_SDL2::DoneCurrent() { SDL_GL_MakeCurrent(render_window, nullptr); } -void EmuWindow_SDL2::ReloadSetKeymaps() { - KeyMap::ClearKeyMapping(keyboard_id); - for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { - KeyMap::SetKeyMapping( - {Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id}, - KeyMap::mapping_targets[i]); - } -} - void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest( const std::pair& minimal_size) { - SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); } diff --git a/src/citra/emu_window/emu_window_sdl2.h b/src/citra/emu_window/emu_window_sdl2.h index b1cbf16d7..c4dc58ace 100644 --- a/src/citra/emu_window/emu_window_sdl2.h +++ b/src/citra/emu_window/emu_window_sdl2.h @@ -10,6 +10,7 @@ #include "core/frontend/motion_emu.h" struct SDL_Window; +struct SDL_Keysym; class EmuWindow_SDL2 : public EmuWindow { public: @@ -31,12 +32,9 @@ public: /// Whether the window is still open, and a close request hasn't yet been sent bool IsOpen() const; - /// Load keymap from configuration - void ReloadSetKeymaps() override; - private: /// Called by PollEvents when a key is pressed or released. - void OnKeyEvent(int key, u8 state); + void OnKeyEvent(SDL_Keysym key, u8 state); /// Called by PollEvents when the mouse moves. void OnMouseMotion(s32 x, s32 y); diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 93f1c339d..e52c459b7 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -27,8 +27,10 @@ set(SRCS configure_graphics.cpp configure_system.cpp configure_input.cpp + configure_system.cpp game_list.cpp hotkeys.cpp + keybinding_names.cpp main.cpp ui_settings.cpp citra-qt.rc @@ -61,9 +63,11 @@ set(HEADERS configure_graphics.h configure_system.h configure_input.h + configure_system.h game_list.h game_list_p.h hotkeys.h + keybinding_names.h main.h ui_settings.h ) @@ -80,6 +84,7 @@ set(UIS configure_graphics.ui configure_system.ui configure_input.ui + configure_system.ui hotkeys.ui main.ui ) @@ -100,7 +105,7 @@ if (APPLE) else() add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS}) endif() -target_link_libraries(citra-qt core video_core audio_core common qhexedit) +target_link_libraries(citra-qt core video_core audio_core input_core common qhexedit) target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) target_link_libraries(citra-qt ${PLATFORM_LIBRARIES} Threads::Threads) diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 948db384d..ae0263d7a 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -13,7 +13,11 @@ #include "common/scm_rev.h" #include "common/string_util.h" #include "core/core.h" -#include "core/frontend/key_map.h" +#include "core/settings.h" + +#include "input_core/devices/keyboard.h" +#include "input_core/input_core.h" + #include "video_core/debug_utils/debug_utils.h" #include "video_core/video_core.h" @@ -104,9 +108,6 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); setWindowTitle(QString::fromStdString(window_title)); - - keyboard_id = KeyMap::NewDeviceId(); - ReloadSetKeymaps(); } void GRenderWindow::moveContext() { @@ -197,11 +198,17 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { } void GRenderWindow::keyPressEvent(QKeyEvent* event) { - KeyMap::PressKey(*this, {event->key(), keyboard_id}); + auto keyboard = InputCore::GetKeyboard(); + KeyboardKey param = + KeyboardKey(event->key(), QKeySequence(event->key()).toString().toStdString()); + keyboard->KeyPressed(param); } void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { - KeyMap::ReleaseKey(*this, {event->key(), keyboard_id}); + auto keyboard = InputCore::GetKeyboard(); + KeyboardKey param = + KeyboardKey(event->key(), QKeySequence(event->key()).toString().toStdString()); + keyboard->KeyReleased(param); } void GRenderWindow::mousePressEvent(QMouseEvent* event) { @@ -230,15 +237,6 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { motion_emu->EndTilt(); } -void GRenderWindow::ReloadSetKeymaps() { - KeyMap::ClearKeyMapping(keyboard_id); - for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { - KeyMap::SetKeyMapping( - {Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id}, - KeyMap::mapping_targets[i]); - } -} - void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) { NotifyClientAreaSizeChanged(std::make_pair(width, height)); } diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index 7dac1c480..b1ab8c4cc 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h @@ -127,8 +127,6 @@ public: void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; - void ReloadSetKeymaps() override; - void OnClientAreaResized(unsigned width, unsigned height); void InitRenderTarget(); diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index 8021667d0..61d44379d 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp @@ -23,18 +23,24 @@ const std::array Config::defaults = Qt::Key_K, Qt::Key_J, Qt::Key_L, // indirectly mapped keys - Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D, -}; + Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right}; + +const QVariant Config::default_circle_pad_modifier = Qt::Key_F; void Config::ReadValues() { qt_config->beginGroup("Controls"); for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { Settings::values.input_mappings[Settings::NativeInput::All[i]] = - qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i]) - .toInt(); + Settings::InputDeviceMapping( + qt_config->value(Settings::NativeInput::Mapping[i], defaults[i]) + .toString() + .toStdString()); } + Settings::values.pad_circle_modifier = Settings::InputDeviceMapping( + qt_config->value("pad_circle_modifier", 0).toString().toStdString()); Settings::values.pad_circle_modifier_scale = qt_config->value("pad_circle_modifier_scale", 0.5).toFloat(); + Settings::values.pad_circle_deadzone = qt_config->value("pad_circle_deadzone", 0.3f).toFloat(); qt_config->endGroup(); qt_config->beginGroup("Core"); @@ -137,11 +143,16 @@ void Config::ReadValues() { void Config::SaveValues() { qt_config->beginGroup("Controls"); for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { - qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]), - Settings::values.input_mappings[Settings::NativeInput::All[i]]); + qt_config->setValue( + QString::fromStdString(Settings::NativeInput::Mapping[i]), + QString::fromStdString( + Settings::values.input_mappings[Settings::NativeInput::All[i]].ToString())); } + qt_config->setValue("pad_circle_modifier", + QString::fromStdString(Settings::values.pad_circle_modifier.ToString())); qt_config->setValue("pad_circle_modifier_scale", (double)Settings::values.pad_circle_modifier_scale); + qt_config->setValue("pad_circle_deadzone", (double)Settings::values.pad_circle_deadzone); qt_config->endGroup(); qt_config->beginGroup("Core"); diff --git a/src/citra_qt/config.h b/src/citra_qt/config.h index 79c901804..67e096ff2 100644 --- a/src/citra_qt/config.h +++ b/src/citra_qt/config.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2014 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -24,4 +24,5 @@ public: void Reload(); void Save(); static const std::array defaults; + static const QVariant default_circle_pad_modifier; }; diff --git a/src/citra_qt/configure_input.cpp b/src/citra_qt/configure_input.cpp index 3e6803b8a..973b44f54 100644 --- a/src/citra_qt/configure_input.cpp +++ b/src/citra_qt/configure_input.cpp @@ -1,146 +1,168 @@ -// Copyright 2016 Citra Emulator Project +// Copyright 2016 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include #include -#include + #include "citra_qt/config.h" #include "citra_qt/configure_input.h" +#include "citra_qt/keybinding_names.h" +#include "common/string_util.h" -static QString getKeyName(Qt::Key key_code) { - switch (key_code) { - case Qt::Key_Shift: - return QObject::tr("Shift"); - case Qt::Key_Control: - return QObject::tr("Ctrl"); - case Qt::Key_Alt: - return QObject::tr("Alt"); - case Qt::Key_Meta: - case -1: - return ""; - default: - return QKeySequence(key_code).toString(); - } -} +#include "input_core/devices/keyboard.h" +#include "input_core/input_core.h" ConfigureInput::ConfigureInput(QWidget* parent) - : QWidget(parent), ui(std::make_unique()), - timer(std::make_unique()) { + : QWidget(parent), ui(std::make_unique()) { ui->setupUi(this); - setFocusPolicy(Qt::ClickFocus); + // Initialize mapping of input enum to UI button. button_map = { - {Settings::NativeInput::Values::A, ui->buttonA}, - {Settings::NativeInput::Values::B, ui->buttonB}, - {Settings::NativeInput::Values::X, ui->buttonX}, - {Settings::NativeInput::Values::Y, ui->buttonY}, - {Settings::NativeInput::Values::L, ui->buttonL}, - {Settings::NativeInput::Values::R, ui->buttonR}, - {Settings::NativeInput::Values::ZL, ui->buttonZL}, - {Settings::NativeInput::Values::ZR, ui->buttonZR}, - {Settings::NativeInput::Values::START, ui->buttonStart}, - {Settings::NativeInput::Values::SELECT, ui->buttonSelect}, - {Settings::NativeInput::Values::HOME, ui->buttonHome}, - {Settings::NativeInput::Values::DUP, ui->buttonDpadUp}, - {Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown}, - {Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft}, - {Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight}, - {Settings::NativeInput::Values::CUP, ui->buttonCStickUp}, - {Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown}, - {Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft}, - {Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight}, - {Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp}, - {Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown}, - {Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft}, - {Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight}, - {Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod}, - }; + {std::make_pair(Settings::NativeInput::Values::A, ui->buttonA)}, + {std::make_pair(Settings::NativeInput::Values::B, ui->buttonB)}, + {std::make_pair(Settings::NativeInput::Values::X, ui->buttonX)}, + {std::make_pair(Settings::NativeInput::Values::Y, ui->buttonY)}, + {std::make_pair(Settings::NativeInput::Values::L, ui->buttonL)}, + {std::make_pair(Settings::NativeInput::Values::R, ui->buttonR)}, + {std::make_pair(Settings::NativeInput::Values::ZL, ui->buttonZL)}, + {std::make_pair(Settings::NativeInput::Values::ZR, ui->buttonZR)}, + {std::make_pair(Settings::NativeInput::Values::START, ui->buttonStart)}, + {std::make_pair(Settings::NativeInput::Values::SELECT, ui->buttonSelect)}, + {std::make_pair(Settings::NativeInput::Values::HOME, ui->buttonHome)}, + {std::make_pair(Settings::NativeInput::Values::DUP, ui->buttonDpadUp)}, + {std::make_pair(Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown)}, + {std::make_pair(Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft)}, + {std::make_pair(Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight)}, + {std::make_pair(Settings::NativeInput::Values::CUP, ui->buttonCStickUp)}, + {std::make_pair(Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown)}, + {std::make_pair(Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft)}, + {std::make_pair(Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight)}, + {std::make_pair(Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp)}, + {std::make_pair(Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown)}, + {std::make_pair(Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft)}, + {std::make_pair(Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight)}}; + // Attach handle click method to each button click. for (const auto& entry : button_map) { - const Settings::NativeInput::Values input_id = entry.first; - connect(entry.second, &QPushButton::released, - [this, input_id]() { handleClick(input_id); }); + connect(entry.second, &QPushButton::released, [=]() { handleClick(entry.second); }); } + connect(ui->buttonCircleMod, &QPushButton::released, + [=]() { handleClick(ui->buttonCircleMod); }); + connect(ui->buttonRestoreDefaults, &QPushButton::released, [=]() { restoreDefaults(); }); - connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); - - timer->setSingleShot(true); - connect(timer.get(), &QTimer::timeout, [this]() { - releaseKeyboard(); - releaseMouse(); - current_input_id = boost::none; - updateButtonLabels(); - }); + setFocusPolicy(Qt::ClickFocus); this->loadConfiguration(); } +void ConfigureInput::handleClick(QPushButton* sender) { + if (sender == nullptr) + return; + previous_mapping = sender->text(); + sender->setText(tr("[press key]")); + sender->setFocus(); + grabKeyboard(); + grabMouse(); + changing_button = sender; + auto update = []() { QCoreApplication::processEvents(); }; + auto input_device = InputCore::DetectInput(5000, update); + + setKey(input_device); +} + +void ConfigureInput::keyPressEvent(QKeyEvent* event) { + if (!changing_button) + return; + if (!event || event->key() == Qt::Key_unknown) + return; + + auto keyboard = InputCore::GetKeyboard(); + KeyboardKey param = + KeyboardKey(event->key(), QKeySequence(event->key()).toString().toStdString()); + keyboard->KeyPressed(param); +} + void ConfigureInput::applyConfiguration() { - for (const auto& input_id : Settings::NativeInput::All) { - const size_t index = static_cast(input_id); - Settings::values.input_mappings[index] = static_cast(key_map[input_id]); + for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { + Settings::values.input_mappings[Settings::NativeInput::All[i]] = + key_map[button_map[Settings::NativeInput::Values(i)]]; } + Settings::values.pad_circle_modifier = key_map[ui->buttonCircleMod]; Settings::Apply(); } void ConfigureInput::loadConfiguration() { - for (const auto& input_id : Settings::NativeInput::All) { - const size_t index = static_cast(input_id); - key_map[input_id] = static_cast(Settings::values.input_mappings[index]); + for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { + key_map[button_map[Settings::NativeInput::Values(i)]] = Settings::values.input_mappings[i]; + } + key_map[ui->buttonCircleMod] = Settings::values.pad_circle_modifier; + updateButtonLabels(); +} + +void ConfigureInput::setKey(Settings::InputDeviceMapping keyPressed) { + if (keyPressed.key == -1 || keyPressed.key == Qt::Key_Escape) { + } else { + key_map[changing_button] = keyPressed; + removeDuplicates(keyPressed); } updateButtonLabels(); + releaseKeyboard(); + releaseMouse(); + changing_button = nullptr; + previous_mapping = nullptr; +} + +QString ConfigureInput::getKeyName(Settings::InputDeviceMapping mapping) { + if (mapping.key == -1) + return ""; + if (mapping.device == Settings::Device::Gamepad) { + if (KeyBindingNames::sdl_gamepad_names.size() > mapping.key && mapping.key >= 0) + return KeyBindingNames::sdl_gamepad_names[mapping.key]; + else + return ""; + } + if (mapping.key == Qt::Key_Shift) + return tr("Shift"); + if (mapping.key == Qt::Key_Control) + return tr("Ctrl"); + if (mapping.key == Qt::Key_Alt) + return tr("Alt"); + if (mapping.key == Qt::Key_Meta) + return ""; + if (mapping.key < 0) + return ""; + + return QKeySequence(mapping.key).toString(); +} + +void ConfigureInput::removeDuplicates(const Settings::InputDeviceMapping newValue) { + for (auto& entry : key_map) { + if (changing_button != entry.first) { + if (newValue == entry.second && newValue.key == entry.second.key) { + entry.second = Settings::InputDeviceMapping(); + } + } + } } void ConfigureInput::restoreDefaults() { - for (const auto& input_id : Settings::NativeInput::All) { - const size_t index = static_cast(input_id); - key_map[input_id] = static_cast(Config::defaults[index].toInt()); + for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { + Settings::InputDeviceMapping mapping = + Settings::InputDeviceMapping(Config::defaults[i].toInt()); + key_map[button_map[Settings::NativeInput::Values(i)]] = mapping; + const QString keyValue = + getKeyName(Settings::InputDeviceMapping(Config::defaults[i].toInt())); } + key_map[ui->buttonCircleMod] = + Settings::InputDeviceMapping(Config::default_circle_pad_modifier.toInt()); updateButtonLabels(); - applyConfiguration(); } void ConfigureInput::updateButtonLabels() { - for (const auto& input_id : Settings::NativeInput::All) { - button_map[input_id]->setText(getKeyName(key_map[input_id])); + for (const auto& mapping : button_map) { + auto button = mapping.second; + button->setText(getKeyName(key_map[button])); } -} - -void ConfigureInput::handleClick(Settings::NativeInput::Values input_id) { - QPushButton* button = button_map[input_id]; - button->setText(tr("[press key]")); - button->setFocus(); - - current_input_id = input_id; - - grabKeyboard(); - grabMouse(); - timer->start(5000); // Cancel after 5 seconds -} - -void ConfigureInput::keyPressEvent(QKeyEvent* event) { - releaseKeyboard(); - releaseMouse(); - - if (!current_input_id || !event) - return; - - if (event->key() != Qt::Key_Escape) - setInput(*current_input_id, static_cast(event->key())); - - updateButtonLabels(); - current_input_id = boost::none; - timer->stop(); -} - -void ConfigureInput::setInput(Settings::NativeInput::Values input_id, Qt::Key key_pressed) { - // Remove duplicates - for (auto& pair : key_map) { - if (pair.second == key_pressed) - pair.second = Qt::Key_unknown; - } - - key_map[input_id] = key_pressed; + ui->buttonCircleMod->setText(getKeyName(key_map[ui->buttonCircleMod])); } diff --git a/src/citra_qt/configure_input.h b/src/citra_qt/configure_input.h index bc343db83..b9830057e 100644 --- a/src/citra_qt/configure_input.h +++ b/src/citra_qt/configure_input.h @@ -1,4 +1,4 @@ -// Copyright 2016 Citra Emulator Project +// Copyright 2016 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -7,13 +7,13 @@ #include #include #include -#include + #include "core/settings.h" +#include "input_core/devices/device.h" #include "ui_configure_input.h" class QPushButton; class QString; -class QTimer; namespace Ui { class ConfigureInput; @@ -30,28 +30,33 @@ public: private: std::unique_ptr ui; - - /// This input is currently awaiting configuration. - /// (i.e.: its corresponding QPushButton has been pressed.) - boost::optional current_input_id; - std::unique_ptr timer; - - /// Each input is represented by a QPushButton. std::map button_map; - /// Each input is configured to respond to the press of a Qt::Key. - std::map key_map; + std::map key_map; + QPushButton* changing_button = nullptr; ///< button currently waiting for key press. + QString previous_mapping; - /// Load configuration settings. + /// Load configuration settings into button text void loadConfiguration(); + + /// Check all inputs for duplicate keys. Clears out any other button with the same value as this + /// button's new value. + void removeDuplicates(const Settings::InputDeviceMapping newValue); + + /// Handle keykoard key press event for input tab when a button is 'waiting'. + void keyPressEvent(QKeyEvent* event) override; + + /// Convert key ASCII value to its' letter/name + static QString getKeyName(Settings::InputDeviceMapping mapping); + + /// Set button text to name of key pressed. + void setKey(Settings::InputDeviceMapping keyPressed); + + /// Event handler for all button released() event. + void handleClick(QPushButton* sender); + /// Restore all buttons to their default values. void restoreDefaults(); + /// Update UI to reflect current configuration. void updateButtonLabels(); - - /// Called when the button corresponding to input_id was pressed. - void handleClick(Settings::NativeInput::Values input_id); - /// Handle key press events. - void keyPressEvent(QKeyEvent* event) override; - /// Configure input input_id to respond to key key_pressed. - void setInput(Settings::NativeInput::Values input_id, Qt::Key key_pressed); }; diff --git a/src/citra_qt/keybinding_names.cpp b/src/citra_qt/keybinding_names.cpp new file mode 100644 index 000000000..886e6ca74 --- /dev/null +++ b/src/citra_qt/keybinding_names.cpp @@ -0,0 +1,31 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "citra_qt/keybinding_names.h" + +const std::array(SDLGamepad::GamepadInputs::MAX)> + KeyBindingNames::sdl_gamepad_names = {{tr("Button A"), + tr("Button B"), + tr("Button X"), + tr("Button Y"), + tr("Left Shoulder"), + tr("Right Shoulder"), + tr("Start"), + tr("Back"), + tr("Dpad Up"), + tr("Dpad Down"), + tr("Dpad Left"), + tr("Dpad Right"), + tr("L3"), + tr("R3"), + tr("Left Trigger"), + tr("Right Trigger"), + tr("Left Y+"), + tr("Left Y-"), + tr("Left X+"), + tr("Left X-"), + tr("Right Y+"), + tr("Right Y-"), + tr("Right X+"), + tr("Right X-")}}; diff --git a/src/citra_qt/keybinding_names.h b/src/citra_qt/keybinding_names.h new file mode 100644 index 000000000..b750c0344 --- /dev/null +++ b/src/citra_qt/keybinding_names.h @@ -0,0 +1,19 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "input_core/devices/sdl_gamepad.h" + +/// Map translatable, user-friendly names to input device buttons for use in the Qt input +/// configuration GUI. +class KeyBindingNames { + Q_DECLARE_TR_FUNCTIONS(KeyBindingNames) +public: + static const std::array(SDLGamepad::GamepadInputs::MAX)> + sdl_gamepad_names; +}; diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 6d59cf640..a6263f723 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -565,7 +565,6 @@ void GMainWindow::OnConfigure() { auto result = configureDialog.exec(); if (result == QDialog::Accepted) { configureDialog.applyConfiguration(); - render_window->ReloadSetKeymaps(); config->Save(); } } diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 2ef3e6b05..5514e89c9 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -69,7 +69,8 @@ namespace Log { CLS(Audio) \ SUB(Audio, DSP) \ SUB(Audio, Sink) \ - CLS(Loader) + CLS(Loader) \ + CLS(Input) // GetClassName is a macro defined by Windows.h, grrr... const char* GetLogClassName(Class log_class) { diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 4330ef879..1e56a2d1c 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -87,7 +87,9 @@ enum class Class : ClassType { Audio_DSP, ///< The HLE implementation of the DSP Audio_Sink, ///< Emulator audio output backend Loader, ///< ROM loader - Count ///< Total number of logging classes + Input, ///< Input backend + + Count ///< Total number of logging classes }; /// Logs a message to the global logger. diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index faad0a561..6362e282a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -30,8 +30,7 @@ set(SRCS file_sys/path_parser.cpp file_sys/savedata_archive.cpp frontend/emu_window.cpp - frontend/key_map.cpp - frontend/motion_emu.cpp + frontend/motion_emu.cpp gdbstub/gdbstub.cpp hle/config_mem.cpp hle/applets/applet.cpp @@ -204,8 +203,7 @@ set(HEADERS file_sys/path_parser.h file_sys/savedata_archive.h frontend/emu_window.h - frontend/key_map.h - frontend/motion_emu.h + frontend/motion_emu.h gdbstub/gdbstub.h hle/config_mem.h hle/function_wrappers.h diff --git a/src/core/core.cpp b/src/core/core.cpp index 202cd332b..1a72facc0 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -19,6 +19,7 @@ #include "core/hw/hw.h" #include "core/loader/loader.h" #include "core/settings.h" +#include "input_core/input_core.h" #include "video_core/video_core.h" namespace Core { @@ -140,6 +141,7 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { Kernel::Init(system_mode); Service::Init(); AudioCore::Init(); + InputCore::Init(); GDBStub::Init(); if (!VideoCore::Init(emu_window)) { @@ -153,6 +155,7 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { void System::Shutdown() { GDBStub::Shutdown(); + InputCore::Shutdown(); AudioCore::Shutdown(); VideoCore::Shutdown(); Service::Shutdown(); diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 1541cc39d..5e475fa47 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -7,32 +7,11 @@ #include "common/assert.h" #include "common/profiler_reporting.h" #include "core/frontend/emu_window.h" -#include "core/frontend/key_map.h" #include "video_core/video_core.h" -void EmuWindow::ButtonPressed(Service::HID::PadState pad) { - pad_state.hex |= pad.hex; -} - -void EmuWindow::ButtonReleased(Service::HID::PadState pad) { - pad_state.hex &= ~pad.hex; -} - -void EmuWindow::CirclePadUpdated(float x, float y) { - constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position - - // Make sure the coordinates are in the unit circle, - // otherwise normalize it. - float r = x * x + y * y; - if (r > 1) { - r = std::sqrt(r); - x /= r; - y /= r; - } - - circle_pad_x = static_cast(x * MAX_CIRCLEPAD_POS); - circle_pad_y = static_cast(y * MAX_CIRCLEPAD_POS); -} +#include "emu_window.h" +#include "input_core/input_core.h" +#include "video_core/video_core.h" /** * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout @@ -62,22 +41,25 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) return; - touch_x = VideoCore::kScreenBottomWidth * - (framebuffer_x - framebuffer_layout.bottom_screen.left) / - (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left); - touch_y = VideoCore::kScreenBottomHeight * - (framebuffer_y - framebuffer_layout.bottom_screen.top) / - (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top); - + int touch_x = VideoCore::kScreenBottomWidth * + (framebuffer_x - framebuffer_layout.bottom_screen.left) / + (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left); + int touch_y = VideoCore::kScreenBottomHeight * + (framebuffer_y - framebuffer_layout.bottom_screen.top) / + (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top); touch_pressed = true; + InputCore::SetTouchState(std::make_tuple(touch_x, touch_y, true)); + auto pad_state = InputCore::GetPadState(); pad_state.touch.Assign(1); + InputCore::SetPadState(pad_state); } void EmuWindow::TouchReleased() { touch_pressed = false; - touch_x = 0; - touch_y = 0; + InputCore::SetTouchState(std::make_tuple(0, 0, false)); + auto pad_state = InputCore::GetPadState(); pad_state.touch.Assign(0); + InputCore::SetPadState(pad_state); } void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 1ba64c92b..56caea7fe 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -52,30 +52,6 @@ public: /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread virtual void DoneCurrent() = 0; - virtual void ReloadSetKeymaps() = 0; - - /** - * Signals a button press action to the HID module. - * @param pad_state indicates which button to press - * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad. - */ - void ButtonPressed(Service::HID::PadState pad_state); - - /** - * Signals a button release action to the HID module. - * @param pad_state indicates which button to press - * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad. - */ - void ButtonReleased(Service::HID::PadState pad_state); - - /** - * Signals a circle pad change action to the HID module. - * @param x new x-coordinate of the circle pad, in the range [-1.0, 1.0] - * @param y new y-coordinate of the circle pad, in the range [-1.0, 1.0] - * @note the coordinates will be normalized if the radius is larger than 1 - */ - void CirclePadUpdated(float x, float y); - /** * Signal that a touch pressed event has occurred (e.g. mouse click pressed) * @param framebuffer_x Framebuffer x-coordinate that was pressed @@ -114,38 +90,7 @@ public: */ void GyroscopeChanged(float x, float y, float z); - /** - * Gets the current pad state (which buttons are pressed). - * @note This should be called by the core emu thread to get a state set by the window thread. - * @note This doesn't include analog input like circle pad direction - * @todo Fix this function to be thread-safe. - * @return PadState object indicating the current pad state - */ - Service::HID::PadState GetPadState() const { - return pad_state; - } - - /** - * Gets the current circle pad state. - * @note This should be called by the core emu thread to get a state set by the window thread. - * @todo Fix this function to be thread-safe. - * @return std::tuple of (x, y), where `x` and `y` are the circle pad coordinates - */ - std::tuple GetCirclePadState() const { - return std::make_tuple(circle_pad_x, circle_pad_y); - } - - /** - * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). - * @note This should be called by the core emu thread to get a state set by the window thread. - * @todo Fix this function to be thread-safe. - * @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and - * `pressed` is true if the touch screen is currently being pressed - */ - std::tuple GetTouchState() const { - return std::make_tuple(touch_x, touch_y, touch_pressed); - } - + /** * Gets the current accelerometer state (acceleration along each three axis). * Axis explained: @@ -230,12 +175,7 @@ protected: // TODO: Find a better place to set this. config.min_client_area_size = std::make_pair(400u, 480u); active_config = config; - pad_state.hex = 0; - touch_x = 0; - touch_y = 0; - circle_pad_x = 0; - circle_pad_y = 0; - touch_pressed = false; + accel_x = 0; accel_y = -512; accel_z = 0; @@ -301,11 +241,7 @@ private: bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false - u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) - u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) - - s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156) - s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156) + std::mutex accel_mutex; s16 accel_x; ///< Accelerometer X-axis value in native 3DS units @@ -321,6 +257,4 @@ private: * Clip the provided coordinates to be inside the touchscreen area. */ std::tuple ClipToTouchScreen(unsigned new_x, unsigned new_y); - - Service::HID::PadState pad_state; }; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 676154bd4..388a1e713 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -3,9 +3,16 @@ // Refer to the license.txt file included. #include -#include "common/logging/log.h" -#include "core/core_timing.h" #include "core/frontend/emu_window.h" + +#include "core/hle/service/hid/hid.h" +#include "core/hle/service/hid/hid_spvr.h" +#include "core/hle/service/hid/hid_user.h" +#include "core/hle/service/service.h" + +#include "input_core/input_core.h" + +#include "core/core_timing.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/hid/hid.h" @@ -73,11 +80,10 @@ void Update() { return; } - PadState state = VideoCore::g_emu_window->GetPadState(); - + PadState state = InputCore::GetPadState(); // Get current circle pad position and update circle pad direction s16 circle_pad_x, circle_pad_y; - std::tie(circle_pad_x, circle_pad_y) = VideoCore::g_emu_window->GetCirclePadState(); + std::tie(circle_pad_x, circle_pad_y) = InputCore::GetCirclePad(); state.hex |= GetCirclePadDirectionState(circle_pad_x, circle_pad_y).hex; mem->pad.current_state.hex = state.hex; @@ -114,7 +120,7 @@ void Update() { TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index]; bool pressed = false; - std::tie(touch_entry.x, touch_entry.y, pressed) = VideoCore::g_emu_window->GetTouchState(); + std::tie(touch_entry.x, touch_entry.y, pressed) = InputCore::GetTouchState(); touch_entry.valid.Assign(pressed ? 1 : 0); // TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 7904e7355..4ec8cc69e 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -23,6 +23,7 @@ namespace HID { * Structure of a Pad controller state. */ struct PadState { + PadState() = default; union { u32 hex; @@ -53,6 +54,16 @@ struct PadState { BitField<30, 1, u32> circle_up; BitField<31, 1, u32> circle_down; }; + bool operator==(const PadState& other) const { + return hex == other.hex; + } + bool operator<(const PadState& other) const { + return hex < other.hex; + } + PadState& operator=(const PadState& other) { + hex = other.hex; + return *this; + } }; /** diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 1a1ee90b2..406eb903e 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -31,9 +31,6 @@ namespace GPU { Regs g_regs; - -/// 268MHz CPU clocks / 60Hz frames per second -const u64 frame_ticks = 268123480ull / 60; /// Event id for CoreTiming static int vblank_event; /// Total number of frames drawn @@ -551,9 +548,6 @@ static void VBlankCallback(u64 userdata, int cycles_late) { Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC0); Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC1); - // Check for user input updates - Service::HID::Update(); - if (!Settings::values.use_vsync && Settings::values.toggle_framelimit) { FrameLimiter(); } diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index d53381216..51b3da9d2 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -317,6 +317,9 @@ static_assert(sizeof(Regs) == 0x1000 * sizeof(u32), "Invalid total size of regis extern Regs g_regs; +/// 268MHz CPU clocks / 60Hz frames per second +static constexpr u64 frame_ticks = 268123480ull / 60; + template void Read(T& var, const u32 addr); diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 9afaf79ec..a92fa4052 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -4,7 +4,7 @@ #include "audio_core/audio_core.h" #include "core/gdbstub/gdbstub.h" -#include "settings.h" +#include "input_core/input_core.h" #include "video_core/video_core.h" #include "core/frontend/emu_window.h" @@ -29,6 +29,8 @@ void Apply() { AudioCore::SelectSink(values.sink_id); AudioCore::EnableStretching(values.enable_audio_stretching); + + InputCore::ReloadSettings(); } } // namespace diff --git a/src/core/settings.h b/src/core/settings.h index 8dbda653a..56a482992 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -5,8 +5,12 @@ #pragma once #include +#include #include +#include + #include "common/common_types.h" +#include "common/string_util.h" namespace Settings { @@ -46,35 +50,94 @@ enum Values { CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT, - CIRCLE_MODIFIER, NUM_INPUTS }; +static const std::array Mapping = { + {// directly mapped keys + "pad_a", "pad_b", "pad_x", "pad_y", "pad_l", "pad_r", "pad_zl", "pad_zr", "pad_start", + "pad_select", "pad_home", "pad_dup", "pad_ddown", "pad_dleft", "pad_dright", "pad_cup", + "pad_cdown", "pad_cleft", "pad_cright", -static const std::array Mapping = {{ - // directly mapped keys - "pad_a", "pad_b", "pad_x", "pad_y", "pad_l", "pad_r", "pad_zl", "pad_zr", "pad_start", - "pad_select", "pad_home", "pad_dup", "pad_ddown", "pad_dleft", "pad_dright", "pad_cup", - "pad_cdown", "pad_cleft", "pad_cright", - - // indirectly mapped keys - "pad_circle_up", "pad_circle_down", "pad_circle_left", "pad_circle_right", - "pad_circle_modifier", -}}; -static const std::array All = {{ - A, B, X, Y, L, R, ZL, ZR, - START, SELECT, HOME, DUP, DDOWN, DLEFT, DRIGHT, CUP, - CDOWN, CLEFT, CRIGHT, CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT, CIRCLE_MODIFIER, -}}; + // indirectly mapped keys + "pad_circle_up", "pad_circle_down", "pad_circle_left", "pad_circle_right"}}; +static const std::array All = { + {A, B, X, Y, L, R, ZL, ZR, + START, SELECT, HOME, DUP, DDOWN, DLEFT, DRIGHT, CUP, + CDOWN, CLEFT, CRIGHT, CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT}}; } +enum class DeviceFramework { SDL }; +enum class Device { Keyboard, Gamepad }; +struct InputDeviceMapping { + DeviceFramework framework = DeviceFramework::SDL; + int number = 0; + Device device = Device::Keyboard; + int key = -1; + + InputDeviceMapping() = default; + explicit InputDeviceMapping(int key_) { + key = key_; + } + explicit InputDeviceMapping(const std::string& input) { + std::vector parts; + Common::SplitString(input, '/', parts); + if (parts.size() != 4) + return; + if (parts[0] == "SDL") + framework = DeviceFramework::SDL; + + number = std::stoi(parts[1]); + + if (parts[2] == "Keyboard") + device = Device::Keyboard; + else if (parts[2] == "Gamepad") + device = Device::Gamepad; + Common::TryParse(parts[3], &key); + } + + bool operator==(const InputDeviceMapping& rhs) const { + return std::tie(device, framework, number) == + std::tie(rhs.device, rhs.framework, rhs.number); + } + bool operator==(const std::string& rhs) const { + return ToString() == rhs; + } + bool operator<(const InputDeviceMapping& rhs) const { + return ToString() < rhs.ToString(); + } + + void InitKey() {} + + std::string ToString() const { + std::string result; + if (framework == DeviceFramework::SDL) + result = "SDL"; + + result += "/"; + result += std::to_string(this->number); + result += "/"; + + if (device == Device::Keyboard) + result += "Keyboard"; + else if (device == Device::Gamepad) + result += "Gamepad"; + + result += "/"; + result += std::to_string(key); + return result; + } +}; + struct Values { // CheckNew3DS bool is_new_3ds; // Controls - std::array input_mappings; + std::array input_mappings; + InputDeviceMapping pad_circle_modifier; float pad_circle_modifier_scale; + float pad_circle_deadzone; // Core bool use_cpu_jit; @@ -108,7 +171,8 @@ struct Values { // Debugging bool use_gdbstub; u16 gdbstub_port; -} extern values; +}; +extern Values values; // a special value for Values::region_value indicating that citra will automatically select a region // value to fit the region lockout info of the game diff --git a/src/input_core/CMakeLists.txt b/src/input_core/CMakeLists.txt new file mode 100644 index 000000000..53108ab3b --- /dev/null +++ b/src/input_core/CMakeLists.txt @@ -0,0 +1,30 @@ +set(SRCS + input_core.cpp + devices/device.cpp + devices/keyboard.cpp + devices/sdl_gamepad.cpp + key_map.cpp + ) + +set(HEADERS + input_core.h + key_map.h + devices/device.h + devices/gamecontrollerdb.h + devices/keyboard.h + devices/sdl_gamepad.h + ) + + +if(SDL2_FOUND) + include_directories(${SDL2_INCLUDE_DIR}) +endif() + +create_directory_groups(${SRCS} ${HEADERS}) + +add_library(input_core STATIC ${SRCS} ${HEADERS}) + +if(SDL2_FOUND) + target_link_libraries(input_core ${SDL2_LIBRARY}) + set_property(TARGET input_core APPEND PROPERTY COMPILE_DEFINITIONS HAVE_SDL2) +endif() diff --git a/src/input_core/devices/device.cpp b/src/input_core/devices/device.cpp new file mode 100644 index 000000000..c7b4cd9a9 --- /dev/null +++ b/src/input_core/devices/device.cpp @@ -0,0 +1,7 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "input_core/devices/device.h" + +IDevice::~IDevice() = default; diff --git a/src/input_core/devices/device.h b/src/input_core/devices/device.h new file mode 100644 index 000000000..7821c20ce --- /dev/null +++ b/src/input_core/devices/device.h @@ -0,0 +1,49 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "core/hle/service/hid/hid.h" +#include "core/settings.h" +#include "input_core/key_map.h" + +class IDevice { +public: + virtual ~IDevice(); + + /** + * Initialize IDevice object with device's index and the map of keys that it will listen to. + * @param number: device number as ordered connected to computer. + * @param keymap: vector of PadStates for device to listen for + * @return true if successful + */ + virtual bool InitDevice(int number, Settings::InputDeviceMapping device_mapping) = 0; + + /** + * Process inputs that were pressed since last frame + */ + virtual std::map ProcessInput() = 0; + + /** + * Close connection to device + * @return true if successful + */ + virtual bool CloseDevice() = 0; + + /** + * Gets Input for a single frame. Used only in Qt gui for key binding. + * @return Settings::InputDeviceMapping instance that captures what button was pressed + */ + virtual Settings::InputDeviceMapping GetInput() = 0; + + /** + * Clears info from last frame. + */ + virtual void Clear() = 0; + +protected: + Settings::InputDeviceMapping input_device_mapping; +}; diff --git a/src/input_core/devices/gamecontrollerdb.h b/src/input_core/devices/gamecontrollerdb.h new file mode 100644 index 000000000..8dc3be138 --- /dev/null +++ b/src/input_core/devices/gamecontrollerdb.h @@ -0,0 +1,190 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +// Community sourced database of controllers, from +// https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt. Taken +// 1/14/2017 +#pragma once +namespace SDLGameControllerDB { + +const char* db_file1 = R"( + # Windows - DINPUT +8f0e1200000000000000504944564944,Acme,platform:Windows,x:b2,a:b0,b:b1,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2, +341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +ffff0000000000000000504944564944,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +0d0f6e00000000000000504944564944,HORIPAD 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows, +6d0419c2000000000000504944564944,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, +4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows, +25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows, +4c05c405000000000000504944564944,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows, +4c05cc09000000000000504944564944,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows, +4c05a00b000000000000504944564944,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows, +6d0418c2000000000000504944564944,Logitech RumblePad 2 USB,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3, +36280100000000000000504944564944,OUYA Controller,platform:Windows,a:b0,b:b3,y:b2,x:b1,start:b14,guide:b15,leftstick:b6,rightstick:b7,leftshoulder:b4,rightshoulder:b5,dpup:b8,dpleft:b10,dpdown:b9,dpright:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b12,righttrigger:b13, +4f0400b3000000000000504944564944,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Windows, +00f00300000000000000504944564944,RetroUSB.com RetroPad,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Windows, +00f0f100000000000000504944564944,RetroUSB.com Super RetroPort,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Windows, +28040140000000000000504944564944,GamePad Pro USB,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,lefttrigger:b6,righttrigger:b7, +ff113133000000000000504944564944,SVEN X-PAD,platform:Windows,a:b2,b:b3,y:b1,x:b0,start:b5,back:b4,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b8,righttrigger:b9, +8f0e0300000000000000504944564944,Piranha xtreme,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2, +8f0e0d31000000000000504944564944,Multilaser JS071 USB,platform:Windows,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7, +10080300000000000000504944564944,PS2 USB,platform:Windows,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a4,righty:a2,lefttrigger:b4,righttrigger:b5, +79000600000000000000504944564944,G-Shark GS-GP702,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Windows, +4b12014d000000000000504944564944,NYKO AIRFLO,a:b0,b:b1,x:b2,y:b3,back:b8,guide:b10,start:b9,leftstick:a0,rightstick:a2,leftshoulder:a3,rightshoulder:b5,dpup:h0.1,dpdown:h0.0,dpleft:h0.8,dpright:h0.2,leftx:h0.6,lefty:h0.12,rightx:h0.9,righty:h0.4,lefttrigger:b6,righttrigger:b7,platform:Windows, +d6206dca000000000000504944564944,PowerA Pro Ex,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.0,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows, +a3060cff000000000000504944564944,Saitek P2500,a:b2,b:b3,y:b1,x:b0,start:b4,guide:b10,back:b5,leftstick:b8,rightstick:b9,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Windows, +4f0415b3000000000000504944564944,Thrustmaster Dual Analog 3.2,platform:Windows,x:b1,a:b0,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3, +6f0e1e01000000000000504944564944,Rock Candy Gamepad for PS3,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,guide:b12,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2, +83056020000000000000504944564944,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,y:b2,x:b3,start:b7,back:b6,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Windows, +10080100000000000000504944564944,PS1 USB,platform:Windows,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftshoulder:b6,rightshoulder:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2, +49190204000000000000504944564944,Ipega PG-9023,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,platform:Windows, +4f0423b3000000000000504944564944,Dual Trigger 3-in-1,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows, +0d0f4900000000000000504944564944,Hatsune Miku Sho Controller,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows, +79004318000000000000504944564944,Mayflash GameCube Controller Adapter,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b0,start:b9,guide:b0,leftshoulder:b4,rightshoulder:b7,leftstick:b0,rightstick:b0,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2, +79000018000000000000504944564944,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows, +2509e803000000000000504944564944,Mayflash Wii Classic Controller,a:b1,b:b0,x:b3,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:b11,dpdown:b13,dpleft:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows, +300f1001000000000000504944564944,Saitek P480 Rumble Pad,a:b2,b:b3,x:b0,y:b1,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b5,righttrigger:b7,platform:Windows, +10280900000000000000504944564944,8Bitdo SFC30 GamePad,a:b1,b:b0,y:b3,x:b4,start:b11,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,platform:Windows, +63252305000000000000504944564944,USB Vibration Joystick (BM),platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3, +20380900000000000000504944564944,8Bitdo NES30 PRO Wireless,platform:Windows,a:b0,b:b1,x:b3,y:b4,leftshoulder:b6,rightshoulder:b7,lefttrigger:b8,righttrigger:b9,back:b10,start:b11,leftstick:b13,rightstick:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +02200090000000000000504944564944,8Bitdo NES30 PRO USB,platform:Windows,a:b0,b:b1,x:b3,y:b4,leftshoulder:b6,rightshoulder:b7,lefttrigger:b8,righttrigger:b9,back:b10,start:b11,leftstick:b13,rightstick:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +ff113133000000000000504944564944,Gembird JPD-DualForce,platform:Windows,a:b2,b:b3,x:b0,y:b1,start:b9,back:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b6,righttrigger:b7,leftstick:b10,rightstick:b11, +341a0108000000000000504944564944,EXEQ RF USB Gamepad 8206,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,leftstick:b8,rightstick:b7,back:b8,start:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Windows, +c0111352000000000000504944564944,Battalife Joystick,platform:Windows,x:b4,a:b6,b:b7,y:b5,back:b2,start:b3,leftshoulder:b0,rightshoulder:b1,leftx:a0,lefty:a1, +100801e5000000000000504944564944,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,platform:Windows, +)"; +const char* db_file2 = R"( + # OS X +0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +6d0400000000000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +6d0400000000000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +6d040000000000001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +6d0400000000000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +4c050000000000006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, +4c05000000000000c405000000000000,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Mac OS X, +4c05000000000000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Mac OS X, +5e040000000000008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +891600000000000000fd000000000000,Razer Onza Tournament,a:b0,b:b1,y:b3,x:b2,start:b8,guide:b10,back:b9,leftstick:b6,rightstick:b7,leftshoulder:b4,rightshoulder:b5,dpup:b11,dpleft:b13,dpdown:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Mac OS X, +4f0400000000000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Mac OS X, +8f0e0000000000000300000000000000,Piranha xtreme,platform:Mac OS X,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2, +0d0f0000000000004d00000000000000,HORI Gem Pad 3,platform:Mac OS X,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7, +79000000000000000600000000000000,G-Shark GP-702,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Mac OS X, +4f0400000000000015b3000000000000,Thrustmaster Dual Analog 3.2,platform:Mac OS X,x:b1,a:b0,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3, +AD1B00000000000001F9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,y:b9,x:b10,start:b6,guide:b8,back:b7,dpup:b2,dpleft:b0,dpdown:b3,dpright:b1,leftx:a0,lefty:a1,lefttrigger:b12,righttrigger:,leftshoulder:b11,platform:Mac OS X, +83050000000000006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Mac OS X, +bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Mac OS X, +79000000000000001100000000000000,Retrolink Classic Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a3,lefty:a4,platform:Mac OS X, +5e04000000000000dd02000000000000,Xbox One Wired Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b9,guide:b10,start:b8,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b6,rightstick:b7,leftx:a0,lefty:a1,rightx:a3,righty:a4, +5e04000000000000ea02000000000000,Xbox Wireless Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b9,guide:b10,start:b8,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b6,rightstick:b7,leftx:a0,lefty:a1,rightx:a3,righty:a4, +5e04000000000000e002000000000000,Xbox Wireless Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b10,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4, +050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,x:b18,y:b17,back:b7,guide:b8,start:b6,leftstick:b23,rightstick:b24,leftshoulder:b19,rightshoulder:b20,dpup:b11,dpdown:b12,dpleft:b13,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b21,righttrigger:b22,platform:Mac OS X, +79000000000000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,x:b0,y:b12,back:b32,start:b36,leftstick:b40,rightstick:b44,leftshoulder:b16,rightshoulder:b20,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a4,rightx:a8,righty:a12,lefttrigger:b24,righttrigger:b28,platform:Mac OS X, +2509000000000000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,x:b3,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:b11,dpdown:b13,dpleft:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X, +351200000000000021ab000000000000,SFC30 Joystick,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X, +b4040000000000000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,x:b3,y:b4,back:b5,guide:b2,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X, +10280000000000000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X, +d814000000000000cecf000000000000,MC Cthulhu,platform:Mac OS X,leftx:,lefty:,rightx:,righty:,lefttrigger:b6,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,righttrigger:b7, +0d0f0000000000006600000000000000,HORIPAD FPS PLUS 4,platform:Mac OS X,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:a4, +)"; +const char* db_file3 = R"( +# Linux +0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006d04000016c2000011010000,Logitech F310 Gamepad (DInput),x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Linux, +030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +030000004c050000c405000011010000,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux, +050000004c050000c405000000010000,Sony DualShock 4 BT,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004c050000cc09000011010000,Sony DualShock 4 V2,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux, +050000004c050000cc09000000010000,Sony DualShock 4 V2 BT,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux, +030000004c050000a00b000011010000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux, +030000006f0e00003001000001010000,EA Sports PS3 Controller,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7, +03000000de280000ff11000001000000,Valve Streaming Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,y:b0,x:b3,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux, +03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Linux, +030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,y:b3,x:b1,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux, +030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5, +030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2, +030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2, +030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux, +030000006d04000016c2000010010000,Logitech Logitech Dual Action,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3, +03000000260900008888000000010000,GameCube {WiseGroup USB box},a:b0,b:b2,y:b3,x:b1,start:b7,leftshoulder:,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,rightstick:,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Linux, +030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,y:b4,x:b3,start:b8,guide:b5,back:b2,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b9,righttrigger:b10,platform:Linux, +030000006d04000018c2000010010000,Logitech Logitech RumblePad 2 USB,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3, +05000000d6200000ad0d000001000000,Moga Pro,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4, +030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7, +030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7, +0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux, +0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux, +030000006f0e00001f01000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +03000000280400000140000000010000,Gravis GamePad Pro USB ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1, +030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4, +030000005e0400008502000000010000,Microsoft X-Box pad (Japan),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4, +030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,guide:b12,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2, +03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,platform:Linux,a:b2,b:b1,y:b0,x:b3,start:b8,back:b9,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5, +030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux, +030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux, +03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux, +060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux, +050000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux, +03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick ,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a3,rightx:a1,righty:a4, +03000000666600000488000000010000,Super Joy Box 5 Pro,platform:Linux,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftshoulder:b6,rightshoulder:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,dpup:b12,dpleft:b15,dpdown:b14,dpright:b13, +05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2, +05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2, +030000008916000001fd000024010000,Razer Onza Classic Edition,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:b11,dpdown:b14,dpright:b12,dpup:b13,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +030000005e040000d102000001010000,Microsoft X-Box One pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +030000005e040000dd02000003020000,Microsoft X-Box One pad v2,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux, +)"; +const char* db_file4 = R"( +03000000790000001100000010010000,RetroLink Saturn Classic Controller,platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b5,guide:b2,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1, +050000007e0500003003000001000000,Nintendo Wii U Pro Controller,platform:Linux,a:b0,b:b1,x:b3,y:b2,back:b8,start:b9,guide:b10,leftshoulder:b4,rightshoulder:b5,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:b13,dpleft:b15,dpdown:b14,dpright:b16, +030000005e0400008e02000004010000,Microsoft X-Box 360 pad,platform:Linux,a:b0,b:b1,x:b2,y:b3,back:b6,start:b7,guide:b8,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2, +030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1, +030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7 +03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3, +0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b7,back:b6,guide:b8,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftshoulder:b4,rightshoulder:b5,lefttrigger:a5,righttrigger:a4,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a2,righty:a3, +03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Linux, +030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +030000006f0e00001304000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:a0,rightstick:a3,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4, +03000000830500006020000010010000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux, +03000000bd12000015d0000010010000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux, +03000000790000001100000010010000,Retrolink Classic Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux, +03000000c9110000f055000011010000,HJC Game GAMEPAD,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a3,dpleft:h0.8,lefttrigger:b6,x:b2,dpup:h0.1,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b0,dpright:h0.2,righttrigger:b7,b:b1,platform:Linux, +03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux, +03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,y:b3,x:b0,start:b9,guide:,back:,leftstick:,rightstick:,leftshoulder:,dpleft:b15,dpdown:b14,dpright:b13,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,rightshoulder:b7,dpup:b12,platform:Linux, +030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,platform:Linux,x:b0,a:b2,b:b3,y:b1,back:b10,guide:b12,start:b11,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3, +030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,platform:Linux,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,guide:b8,leftstick:b9,rightstick:b10,lefttrigger:a2,righttrigger:a5,leftx:a0,lefty:a1,rightx:a3,righty:a4, +03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux, +030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,platform:Linux,a:b0,b:b2,x:b1,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7, +05000000102800000900000000010000,8Bitdo SFC30 GamePad,platform:Linux,x:b4,a:b1,b:b0,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1, +03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,leftx:a0,lefty:a1, +030000000d0f00000d00000000010000,hori,platform:Linux,a:b0,b:b6,y:b2,x:b1,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,start:b9,guide:b10,back:b8,leftshoulder:b3,rightshoulder:b7,leftx:b4,lefty:b5, +03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5, +03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,platform:Linux,a:b0,b:b1,y:b2,x:b3,start:b9,back:b8,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,lefttrigger:b6,righttrigger:b7, +03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),platform:Linux,a:b3,b:b4,y:b1,x:b0,start:b7,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5, +05000000010000000100000003000000,Nintendo Wiimote,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7, +030000005e0400008e02000062230000,Microsoft X-Box 360 pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,y:b1,x:b0,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b6,righttrigger:b7,platform:Linux, +030000006f0e00000103000000020000,Logic3 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +05000000380700006652000025010000,Mad Catz C.T.R.L.R ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3, +030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,lefttrigger:a2,righttrigger:a5, +05000000a00500003232000001000000,8Bitdo Zero GamePad,platform:Linux,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1, +030000001008000001e5000010010000,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,platform:Linux, +03000000100800000300000010010000,USB Gamepad,platform:Linux,a:b2,b:b1,x:b3,y:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5, +05000000ac0500003232000001000000,VR-BOX,platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5, +)"; +} diff --git a/src/input_core/devices/keyboard.cpp b/src/input_core/devices/keyboard.cpp new file mode 100644 index 000000000..79914fb30 --- /dev/null +++ b/src/input_core/devices/keyboard.cpp @@ -0,0 +1,60 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include + +#include "input_core/devices/keyboard.h" + +Keyboard::Keyboard() {} + +Keyboard::~Keyboard() {} + +bool Keyboard::InitDevice(int number, Settings::InputDeviceMapping device_mapping) { + input_device_mapping = device_mapping; + return true; +} + +std::map Keyboard::ProcessInput() { + std::map keysPressedCopy; + { + std::lock_guard lock(m); + keysPressedCopy = keys_pressed; + } + std::map button_status; + bool circlePadModPressed = keysPressedCopy[circle_pad_modifier]; + for (const auto& key : keysPressedCopy) { + input_device_mapping.key = key.first.key; + button_status.emplace(input_device_mapping, key.second ? 1.0 : 0.0); + } + return button_status; +} + +bool Keyboard::CloseDevice() { + return true; +} + +void Keyboard::KeyPressed(KeyboardKey key) { + std::lock_guard lock(m); + keys_pressed[key] = true; +} + +void Keyboard::KeyReleased(KeyboardKey key) { + std::lock_guard lock(m); + keys_pressed[key] = false; +} + +void Keyboard::Clear() { + std::lock_guard lock(m); + keys_pressed.clear(); +} + +Settings::InputDeviceMapping Keyboard::GetInput() { + auto result = ProcessInput(); + for (const auto& entry : result) { + if (entry.second > 0.5) + return entry.first; + } + return Settings::InputDeviceMapping(""); +} diff --git a/src/input_core/devices/keyboard.h b/src/input_core/devices/keyboard.h new file mode 100644 index 000000000..01a2ae1d7 --- /dev/null +++ b/src/input_core/devices/keyboard.h @@ -0,0 +1,49 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include "input_core/devices/device.h" + +struct KeyboardKey { + uint32_t key; + std::string character; + + KeyboardKey() = default; + KeyboardKey(uint32_t key_, std::string character_) + : key(key_), character(std::move(character_)) {} + bool operator==(const KeyboardKey& other) const { + return key == other.key; + } + bool operator==(uint32_t other) const { + return key == other; + } + bool operator<(const KeyboardKey& other) const { + return key < other.key; + } +}; +class Keyboard : public IDevice { +public: + Keyboard(); + ~Keyboard(); + bool InitDevice(int number, Settings::InputDeviceMapping device_mapping) override; + std::map ProcessInput() override; + bool CloseDevice() override; + void KeyPressed(KeyboardKey key); + void KeyReleased(KeyboardKey key); + void Clear() override; + Settings::InputDeviceMapping GetInput() override; + +private: + std::map keys_pressed; + std::map keys_pressed_last; + std::mutex m; ///< Keys pressed from frontend is on a separate thread. + std::map circle_pad_directions; ///< Inverts the strength of key press if it is a + /// circle pad direction that needs it. + KeyboardKey circle_pad_modifier; +}; diff --git a/src/input_core/devices/sdl_gamepad.cpp b/src/input_core/devices/sdl_gamepad.cpp new file mode 100644 index 000000000..d307a2d9e --- /dev/null +++ b/src/input_core/devices/sdl_gamepad.cpp @@ -0,0 +1,122 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/string_util.h" + +#include "input_core/devices/gamecontrollerdb.h" +#include "input_core/devices/sdl_gamepad.h" + +bool SDLGamepad::SDLInitialized = false; + +SDLGamepad::SDLGamepad() {} +SDLGamepad::SDLGamepad(int number_, _SDL_GameController* gamepad_) + : number(number_), gamepad(gamepad_) {} +SDLGamepad::~SDLGamepad() { + CloseDevice(); +} + +bool SDLGamepad::InitDevice(int number, Settings::InputDeviceMapping device_mapping) { + if (!SDLGamepad::SDLInitialized && SDL_Init(SDL_INIT_GAMECONTROLLER) < 0) { + LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_GAMECONTROLLER) failed"); + return false; + } + SDL_GameControllerEventState(SDL_IGNORE); + SDLGamepad::SDLInitialized = true; + LoadGameControllerDB(); + + if (SDL_IsGameController(number)) { + gamepad = SDL_GameControllerOpen(number); + if (gamepad == nullptr) { + return false; + } + } + input_device_mapping = device_mapping; + return true; +} + +std::map SDLGamepad::ProcessInput() { + std::map button_status; + if (gamepad == nullptr) + return button_status; + SDL_GameControllerUpdate(); + for (int i = 0; i < SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_MAX; i++) { + SDL_GameControllerButton button = static_cast(i); + Uint8 pressed = SDL_GameControllerGetButton(gamepad, button); + input_device_mapping.key = static_cast( + gamepadinput_to_sdlname_mapping2[SDL_GameControllerGetStringForButton(button)]); + button_status.emplace(input_device_mapping, pressed); + } + for (int i = 0; i < SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_MAX; i++) { + SDL_GameControllerAxis axis = static_cast(i); + float strength = fmaxf(-1, (float)SDL_GameControllerGetAxis(gamepad, axis) / 32767.0); + input_device_mapping.key = static_cast( + gamepadinput_to_sdlname_mapping2[SDL_GameControllerGetStringForAxis(axis)]); + if (strength < 0) { + button_status.emplace(input_device_mapping, 0); + input_device_mapping.key += 1; // minus axis value is always one greater + button_status.emplace(input_device_mapping, abs(strength)); + } else if (strength >= 0 && i < 4) { + button_status.emplace(input_device_mapping, abs(strength)); + input_device_mapping.key += 1; + button_status.emplace(input_device_mapping, 0); + } else { // Only trigger buttons + button_status.emplace(input_device_mapping, abs(strength)); + } + } + return button_status; +} + +bool SDLGamepad::CloseDevice() { + if (gamepad != nullptr) { + SDL_GameControllerClose(gamepad); + } + return true; +} + +std::vector> SDLGamepad::GetAllDevices() { + std::vector> devices; + for (int i = 0; i < 8; i++) { + auto gamepad = std::make_shared(); + bool success = gamepad->InitDevice( + i, Settings::InputDeviceMapping("SDL/" + std::to_string(i) + "/Gamepad/-1")); + if (success) + devices.push_back(gamepad); + } + return devices; +} + +void SDLGamepad::LoadGameControllerDB() { + std::vector lines1, lines2, lines3, lines4; + Common::SplitString(SDLGameControllerDB::db_file1, '\n', lines1); + Common::SplitString(SDLGameControllerDB::db_file2, '\n', lines2); + Common::SplitString(SDLGameControllerDB::db_file3, '\n', lines3); + Common::SplitString(SDLGameControllerDB::db_file4, '\n', lines4); + lines1.insert(lines1.end(), lines2.begin(), lines2.end()); + lines1.insert(lines1.end(), lines3.begin(), lines3.end()); + lines1.insert(lines1.end(), lines4.begin(), lines4.end()); + for (std::string s : lines1) { + SDL_GameControllerAddMapping(s.c_str()); + } +} + +Settings::InputDeviceMapping SDLGamepad::GetInput() { + if (gamepad == nullptr) + return Settings::InputDeviceMapping(""); + + auto results = ProcessInput(); + for (auto& input : results) { + if (input.second > 0.5) + return input.first; + } + return Settings::InputDeviceMapping(""); +} + +void SDLGamepad::Clear() {} diff --git a/src/input_core/devices/sdl_gamepad.h b/src/input_core/devices/sdl_gamepad.h new file mode 100644 index 000000000..17f21a1e5 --- /dev/null +++ b/src/input_core/devices/sdl_gamepad.h @@ -0,0 +1,113 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "input_core/devices/device.h" + +struct _SDL_GameController; +class SDLGamepad : public IDevice { +public: + SDLGamepad(); + SDLGamepad(int number_, _SDL_GameController* gamepad_); + ~SDLGamepad(); + + bool InitDevice(int number, Settings::InputDeviceMapping device_mapping) override; + std::map ProcessInput() override; + bool CloseDevice() override; + Settings::InputDeviceMapping GetInput() override; + void Clear() override; + + /// Returns vector of all gamepads connected to computer. Used for keybinding setup + static std::vector> GetAllDevices(); + enum class GamepadInputs { + ButtonA, + ButtonB, + ButtonX, + ButtonY, + LeftShoulder, + RightShoulder, + Start, + Back, + DPadUp, + DpadDown, + DpadLeft, + DpadRight, + L3, + R3, + LeftTrigger, + RightTrigger, + LeftYPlus, + LeftYMinus, + LeftXPlus, + LeftXMinus, + RightYPlus, + RightYMinus, + RightXPlus, + RightXMinus, + MAX + }; + +private: + /// Maps the friendly name shown on GUI with the string name for getting the SDL button + /// instance. + std::map gamepadinput_to_sdlname_mapping = { + {GamepadInputs::ButtonA, "a"}, + {GamepadInputs::ButtonB, "b"}, + {GamepadInputs::ButtonX, "x"}, + {GamepadInputs::ButtonY, "y"}, + {GamepadInputs::LeftShoulder, "leftshoulder"}, + {GamepadInputs::RightShoulder, "rightshoulder"}, + {GamepadInputs::Start, "start"}, + {GamepadInputs::Back, "back"}, + {GamepadInputs::DPadUp, "dpup"}, + {GamepadInputs::DpadDown, "dpdown"}, + {GamepadInputs::DpadLeft, "dpleft"}, + {GamepadInputs::DpadRight, "dpright"}, + {GamepadInputs::L3, "leftstick"}, + {GamepadInputs::R3, "rightstick"}, + {GamepadInputs::LeftTrigger, "lefttrigger"}, + {GamepadInputs::RightTrigger, "righttrigger"}, + {GamepadInputs::LeftYPlus, "lefty"}, + {GamepadInputs::LeftYMinus, "lefty"}, + {GamepadInputs::LeftXPlus, "leftx"}, + {GamepadInputs::LeftXMinus, "leftx"}, + {GamepadInputs::RightYPlus, "righty"}, + {GamepadInputs::RightYMinus, "righty"}, + {GamepadInputs::RightXPlus, "rightx"}, + {GamepadInputs::RightXMinus, "rightx"}, + }; + std::map gamepadinput_to_sdlname_mapping2 = { + {"a", GamepadInputs::ButtonA}, + {"b", GamepadInputs::ButtonB}, + {"x", GamepadInputs::ButtonX}, + {"y", GamepadInputs::ButtonY}, + {"leftshoulder", GamepadInputs::LeftShoulder}, + {"rightshoulder", GamepadInputs::RightShoulder}, + {"start", GamepadInputs::Start}, + {"back", GamepadInputs::Back}, + {"dpup", GamepadInputs::DPadUp}, + {"dpdown", GamepadInputs::DpadDown}, + {"dpleft", GamepadInputs::DpadLeft}, + {"dpright", GamepadInputs::DpadRight}, + {"leftstick", GamepadInputs::L3}, + {"rightstick", GamepadInputs::R3}, + {"lefttrigger", GamepadInputs::LeftTrigger}, + {"righttrigger", GamepadInputs::RightTrigger}, + {"lefty", GamepadInputs::LeftYPlus}, + {"lefty2", GamepadInputs::LeftYMinus}, + {"leftx", GamepadInputs::LeftXPlus}, + {"leftx2", GamepadInputs::LeftXMinus}, + {"righty", GamepadInputs::RightYPlus}, + {"righty2", GamepadInputs::RightYMinus}, + {"rightx", GamepadInputs::RightXPlus}, + {"rightx2", GamepadInputs::RightXMinus}}; + static bool SDLInitialized; + std::map + keys_pressed; ///< Map of keys that were pressed on previous iteration + _SDL_GameController* gamepad = nullptr; + int number; ///< Index of gamepad connection + + static void LoadGameControllerDB(); +}; diff --git a/src/input_core/input_core.cpp b/src/input_core/input_core.cpp new file mode 100644 index 000000000..90ba2f72f --- /dev/null +++ b/src/input_core/input_core.cpp @@ -0,0 +1,262 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include + +#include "core/core_timing.h" +#include "core/hw/gpu.h" + +#include "input_core/devices/keyboard.h" +#include "input_core/devices/sdl_gamepad.h" +#include "input_core/input_core.h" + +int InputCore::tick_event; +Service::HID::PadState InputCore::pad_state; +std::tuple InputCore::circle_pad; +std::shared_ptr InputCore::main_keyboard; +std::vector> + InputCore::devices; ///< Devices that are handling input for the game +std::map> InputCore::key_mappings; +std::map + InputCore::keys_pressed; ///< keys that were pressed on previous frame. +std::mutex InputCore::pad_state_mutex; +std::mutex InputCore::touch_mutex; +u16 InputCore::touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) +u16 InputCore::touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) +bool InputCore::touch_pressed; ///< True if touchpad area is currently pressed, otherwise false +const float input_detect_threshold = + 0.45; ///< Applies to analog controls being used for digital 3ds inputs. + +void InputCore::Init() { + ParseSettings(); + tick_event = CoreTiming::RegisterEvent("InputCore::tick_event", InputTickCallback); + CoreTiming::ScheduleEvent(GPU::frame_ticks, tick_event); +} + +void InputCore::Shutdown() { + CoreTiming::UnscheduleEvent(tick_event, 0); + devices.clear(); +} + +void InputCore::InputTickCallback(u64, int cycles_late) { + std::vector> inputs; + for (auto& device : devices) { + inputs.push_back(device->ProcessInput()); + } + UpdateEmulatorInputs(inputs); + + Service::HID::Update(); + + // Reschedule recurrent event + CoreTiming::ScheduleEvent(GPU::frame_ticks - cycles_late, tick_event); +} + +void InputCore::UpdateEmulatorInputs( + std::vector> inputs) { + std::lock_guard lock(pad_state_mutex); + + // Apply deadzone for circle pad + float leftx = 0, lefty = 0; + float circle_pad_modifier = 1.0; + auto circle_pad_modifier_mapping = Settings::values.pad_circle_modifier; + for (auto& input_device : inputs) { + for (auto& button_states : input_device) { // Loop through all buttons from input device + float strength = button_states.second; + auto emulator_inputs = key_mappings[button_states.first]; + for (auto& emulator_input : emulator_inputs) { + if (emulator_input == Service::HID::PAD_CIRCLE_UP && abs(strength) > 0) { + lefty = -strength; + } else if (emulator_input == Service::HID::PAD_CIRCLE_DOWN && abs(strength) > 0) { + lefty = strength; + } else if (emulator_input == Service::HID::PAD_CIRCLE_LEFT && abs(strength) > 0) { + leftx = -strength; + } else if (emulator_input == Service::HID::PAD_CIRCLE_RIGHT && abs(strength) > 0) { + leftx = strength; + } + } + if (button_states.first == circle_pad_modifier_mapping) + circle_pad_modifier = (button_states.second > input_detect_threshold) + ? Settings::values.pad_circle_modifier_scale + : 1.0; + } + } + float deadzone = Settings::values.pad_circle_deadzone; + std::tuple left_stick = ApplyDeadzone(leftx, lefty, deadzone); + + // Set emulator circlepad values + std::get<0>(circle_pad) = + std::get<0>(left_stick) * KeyMap::MAX_CIRCLEPAD_POS * circle_pad_modifier; + std::get<1>(circle_pad) = + std::get<1>(left_stick) * KeyMap::MAX_CIRCLEPAD_POS * -1 * circle_pad_modifier; + + // Set emulator digital button values + for (auto& input_device : inputs) { + for (auto& button_states : input_device) { // Loop through all buttons from input device + float strength = button_states.second; + auto emulator_inputs = key_mappings[button_states.first]; // vector of emulator inputs + // bound to physical button + for (auto& emulator_input : emulator_inputs) { + if (std::find(std::begin(KeyMap::analog_inputs), std::end(KeyMap::analog_inputs), + emulator_input) == std::end(KeyMap::analog_inputs)) { + if (abs(strength) < input_detect_threshold && // Key released + keys_pressed[emulator_input] == true) { + pad_state.hex &= ~emulator_input.hex; + keys_pressed[emulator_input] = false; + } else if (abs(strength) >= input_detect_threshold && + keys_pressed[emulator_input] == false) { // Key pressed + pad_state.hex |= emulator_input.hex; + keys_pressed[emulator_input] = true; + } + } + } + } + } +} + +Service::HID::PadState InputCore::GetPadState() { + std::lock_guard lock(pad_state_mutex); + return pad_state; +} + +void InputCore::SetPadState(const Service::HID::PadState& state) { + std::lock_guard lock(pad_state_mutex); + pad_state.hex = state.hex; +} + +std::tuple InputCore::GetCirclePad() { + return circle_pad; +} + +std::shared_ptr InputCore::GetKeyboard() { + if (main_keyboard == nullptr) { + main_keyboard = std::make_shared(); + main_keyboard->InitDevice(0, Settings::InputDeviceMapping("SDL/0/Keyboard/-1")); + } + return main_keyboard; +} + +std::tuple InputCore::GetTouchState() { + std::lock_guard lock(touch_mutex); + return std::make_tuple(touch_x, touch_y, touch_pressed); +} + +void InputCore::SetTouchState(std::tuple value) { + std::lock_guard lock(touch_mutex); + std::tie(touch_x, touch_y, touch_pressed) = value; +} + +bool InputCore::CheckIfMappingExists(const std::vector& uniqueMapping, + Settings::InputDeviceMapping mappingToCheck) { + return std::any_of(uniqueMapping.begin(), uniqueMapping.end(), + [mappingToCheck](const auto& mapping) { return mapping == mappingToCheck; }); +} + +std::vector InputCore::GatherUniqueMappings() { + std::vector uniqueMappings; + + for (const auto& mapping : Settings::values.input_mappings) { + if (!CheckIfMappingExists(uniqueMappings, mapping)) { + uniqueMappings.push_back(mapping); + } + } + if (!CheckIfMappingExists(uniqueMappings, Settings::values.pad_circle_modifier)) { + uniqueMappings.push_back(Settings::values.pad_circle_modifier); + } + return uniqueMappings; +} + +void InputCore::BuildKeyMapping() { + key_mappings.clear(); + for (size_t i = 0; i < Settings::values.input_mappings.size(); i++) { + auto key = Settings::values.input_mappings[i]; + auto val = KeyMap::mapping_targets[i]; + key_mappings.emplace(key, std::vector()); + key_mappings[key].push_back(val); + } +} + +void InputCore::GenerateUniqueDevices() { + auto uniqueMappings = GatherUniqueMappings(); + devices.clear(); + std::shared_ptr input; + for (const auto& mapping : uniqueMappings) { + switch (mapping.framework) { + case Settings::DeviceFramework::SDL: { + if (mapping.device == Settings::Device::Keyboard) { + main_keyboard = std::make_shared(); + input = main_keyboard; + break; + } else if (mapping.device == Settings::Device::Gamepad) { + input = std::make_shared(); + break; + } + } + } + devices.push_back(input); + input->InitDevice(mapping.number, mapping); + } +} + +void InputCore::ParseSettings() { + GenerateUniqueDevices(); + BuildKeyMapping(); +} + +std::tuple InputCore::ApplyDeadzone(float x, float y, float dead_zone) { + float magnitude = std::sqrt((x * x) + (y * y)); + if (magnitude < dead_zone) { + x = 0; + y = 0; + } else { + float normalized_x = x / magnitude; + float normalized_y = y / magnitude; + x = normalized_x * ((magnitude - dead_zone) / (1 - dead_zone)); + y = normalized_y * ((magnitude - dead_zone) / (1 - dead_zone)); + } + return std::tuple(x, y); +} + +void InputCore::ReloadSettings() { + if (devices.empty()) + return; + std::lock_guard lock(pad_state_mutex); + devices.clear(); + ParseSettings(); +} + +std::vector> InputCore::GetAllDevices() { + auto all_devices = SDLGamepad::GetAllDevices(); + auto keyboard = InputCore::GetKeyboard(); + all_devices.push_back(keyboard); + + return all_devices; +} + +Settings::InputDeviceMapping InputCore::DetectInput(int max_time, + std::function update_GUI) { + auto devices = GetAllDevices(); + for (auto& device : devices) { + device->Clear(); + } + Settings::InputDeviceMapping input_device; + auto start = std::chrono::high_resolution_clock::now(); + while (input_device.key == -1) { + update_GUI(); + auto duration = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - start) + .count(); + if (duration >= max_time) { + break; + } + for (auto& device : devices) { + input_device = device->GetInput(); + if (input_device.key != -1) + break; + } + }; + return input_device; +} diff --git a/src/input_core/input_core.h b/src/input_core/input_core.h new file mode 100644 index 000000000..77170e50a --- /dev/null +++ b/src/input_core/input_core.h @@ -0,0 +1,125 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include + +#include "core/hle/service/hid/hid.h" +#include "core/settings.h" +#include "input_core/devices/device.h" + +class Keyboard; + +class InputCore { +public: + static void Init(); + static void Shutdown(); + + /** + * Threadsafe getter to the current PadState + * @return Service::HID::PadState instance + */ + static Service::HID::PadState GetPadState(); + + /** + * Threadsafe setter for the current PadState + * @param state New PadState to overwrite current PadState. + */ + static void SetPadState(const Service::HID::PadState& state); + + /** + * Getter for current CirclePad + * @return std::tuple CirclePad state + */ + static std::tuple GetCirclePad(); + + /** + * Getter for Citra's main keyboard input handler + * @return std::shared_ptr Device Keyboard instance + */ + static std::shared_ptr GetKeyboard(); + + /** + * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). + * Threadsafe. + * @note This should be called by the core emu thread to get a state set by the window thread. + * @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and + * `pressed` is true if the touch screen is currently being pressed + */ + static std::tuple GetTouchState(); + + /** + * Threadsafe setter for the current touch screen state. + * @param value New Touch State + */ + static void SetTouchState(std::tuple value); + + /** + * Reload input key mapping settings during game-play + */ + static void ReloadSettings(); + /** + * Returns vector of all available devices from user's system. + */ + static std::vector> GetAllDevices(); + + /** + * Loops through all devices and detects the first device that produces an input + * @param max_time: maximum amount of time to wait until input detected, in milliseconds. + * @param update_GUI: function to run in while loop to process any gui events. + * @return Settings::InputDeviceMapping of input device + */ + static Settings::InputDeviceMapping DetectInput(int max_time, + std::function update_GUI); + +private: + static int tick_event; + static Service::HID::PadState pad_state; + static std::tuple circle_pad; + static std::shared_ptr main_keyboard; ///< Keyboard is always active for Citra + static std::vector> + devices; ///< Devices that are handling input for the game + static std::map> key_mappings; + static std::map + keys_pressed; ///< keys that were pressed on previous frame. + static std::mutex pad_state_mutex; + static std::mutex touch_mutex; + static u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) + static u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) + static bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false + + /** + * Loops through all unique input devices, and all bound inputs to update the emulator's input + * status. + */ + static void UpdateEmulatorInputs( + std::vector> inputs); + + static void InputTickCallback(u64, int cycles_late); + + /** + * Helper methodto check if device was already initialized + */ + static bool CheckIfMappingExists(const std::vector& uniqueMapping, + Settings::InputDeviceMapping mappingToCheck); + + static std::vector + GatherUniqueMappings(); /// Get unique input mappings from settings + + static void BuildKeyMapping(); /// Builds map of input keys to 3ds buttons for unique device + + static void GenerateUniqueDevices(); /// Generate a device for each unique mapping + + static void ParseSettings(); /// Read settings to initialize devices + + /** + * Takes two floats and the deadzone and applies formula to + * correct the stick position. + */ + static std::tuple ApplyDeadzone(float x, float y, float dead_zone); +}; diff --git a/src/input_core/key_map.cpp b/src/input_core/key_map.cpp new file mode 100644 index 000000000..2ff9197f1 --- /dev/null +++ b/src/input_core/key_map.cpp @@ -0,0 +1,27 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "input_core/key_map.h" + +namespace KeyMap { +const std::array mapping_targets = {{ + Service::HID::PAD_A, Service::HID::PAD_B, + Service::HID::PAD_X, Service::HID::PAD_Y, + Service::HID::PAD_L, Service::HID::PAD_R, + Service::HID::PAD_ZL, Service::HID::PAD_ZR, + Service::HID::PAD_START, Service::HID::PAD_SELECT, + Service::HID::PAD_TOUCH, Service::HID::PAD_UP, + Service::HID::PAD_DOWN, Service::HID::PAD_LEFT, + Service::HID::PAD_RIGHT, Service::HID::PAD_C_UP, + Service::HID::PAD_C_DOWN, Service::HID::PAD_C_LEFT, + Service::HID::PAD_C_RIGHT, + + Service::HID::PAD_CIRCLE_UP, Service::HID::PAD_CIRCLE_DOWN, + Service::HID::PAD_CIRCLE_LEFT, Service::HID::PAD_CIRCLE_RIGHT, +}}; +/// Array of inputs that are analog only, and require a strength when set +const std::array analog_inputs = { + Service::HID::PAD_CIRCLE_UP, Service::HID::PAD_CIRCLE_DOWN, Service::HID::PAD_CIRCLE_LEFT, + Service::HID::PAD_CIRCLE_RIGHT}; +} diff --git a/src/input_core/key_map.h b/src/input_core/key_map.h new file mode 100644 index 000000000..2447a8847 --- /dev/null +++ b/src/input_core/key_map.h @@ -0,0 +1,15 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "core/hle/service/hid/hid.h" + +namespace KeyMap { +extern const std::array mapping_targets; +extern const std::array analog_inputs; +constexpr int MAX_CIRCLEPAD_POS = 0x9C; /// Max value for a circle pad position +} diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index b47156ca4..d85ac40f7 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -12,7 +12,7 @@ create_directory_groups(${SRCS} ${HEADERS}) include_directories(../../externals/catch/single_include/) add_executable(tests ${SRCS} ${HEADERS}) -target_link_libraries(tests core video_core audio_core common) +target_link_libraries(tests core video_core audio_core input_core common) target_link_libraries(tests ${PLATFORM_LIBRARIES} Threads::Threads) add_test(NAME tests COMMAND $)