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 43fa06b4e..6298db77d 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt @@ -16,7 +16,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 110b883fb..ac35a52ed 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -52,17 +52,17 @@ static const std::array defaults = { SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, 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_scale = (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5); + 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.4); // Core Settings::values.frame_skip = sdl2_config->GetInteger("Core", "frame_skip", 0); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 2031620a5..251f10a72 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -34,7 +34,7 @@ pad_circle_right = pad_circle_modifier = # The applied modifier scale to circle pad. -# Must be in range of 0.0-1.0. Defaults to 0.5 +# Must be in range of 0.0-1.0. Defaults to 0.4 pad_circle_modifier_scale = [Core] diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp index da12307b7..2a58e7b75 100644 --- a/src/citra/emu_window/emu_window_sdl2.cpp +++ b/src/citra/emu_window/emu_window_sdl2.cpp @@ -11,7 +11,6 @@ #include -#include "common/key_map.h" #include "common/logging/log.h" #include "common/scm_rev.h" #include "common/string_util.h" @@ -19,6 +18,9 @@ #include "core/settings.h" #include "core/hle/service/hid/hid.h" +#include "input_core/input_core.h" +#include "input_core/devices/keyboard.h" + #include "citra/emu_window/emu_window_sdl2.h" #include "video_core/video_core.h" @@ -38,11 +40,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); } } @@ -59,9 +64,7 @@ void EmuWindow_SDL2::OnResize() { } EmuWindow_SDL2::EmuWindow_SDL2() { - keyboard_id = KeyMap::NewDeviceId(); - - ReloadSetKeymaps(); + keyboard_id = 0; SDL_SetMainReady(); @@ -144,7 +147,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); @@ -168,13 +171,6 @@ 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); -} +} \ No newline at end of file diff --git a/src/citra/emu_window/emu_window_sdl2.h b/src/citra/emu_window/emu_window_sdl2.h index 77279f022..1be2733f9 100644 --- a/src/citra/emu_window/emu_window_sdl2.h +++ b/src/citra/emu_window/emu_window_sdl2.h @@ -9,6 +9,7 @@ #include "common/emu_window.h" struct SDL_Window; +struct SDL_Keysym; class EmuWindow_SDL2 : public EmuWindow { public: @@ -30,12 +31,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 d25af94ed..98eec35dd 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -26,8 +26,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 @@ -60,9 +62,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 version.h @@ -81,6 +85,7 @@ set(UIS configure_graphics.ui configure_system.ui configure_input.ui + configure_system.ui hotkeys.ui main.ui ) @@ -101,7 +106,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 6dddde9ba..28f498b3c 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -10,7 +10,6 @@ #include "citra_qt/bootmanager.h" -#include "common/key_map.h" #include "common/microprofile.h" #include "common/scm_rev.h" #include "common/string_util.h" @@ -19,6 +18,9 @@ #include "core/settings.h" #include "core/system.h" +#include "input_core/input_core.h" +#include "input_core/devices/keyboard.h" + #include "video_core/debug_utils/debug_utils.h" #include "video_core/video_core.h" @@ -112,8 +114,30 @@ 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(); + keyboard_id = 0; + + // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, WA_DontShowOnScreen, WA_DeleteOnClose + QGLFormat fmt; + fmt.setVersion(3,3); + fmt.setProfile(QGLFormat::CoreProfile); + // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X + fmt.setOption(QGL::NoDeprecatedFunctions); + + child = new GGLWidgetInternal(fmt, this); + QBoxLayout* layout = new QHBoxLayout(this); + + resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); + layout->addWidget(child); + layout->setMargin(0); + setLayout(layout); + + OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); + + OnFramebufferSizeChanged(); + NotifyClientAreaSizeChanged(std::pair(child->width(), child->height())); + + BackupGeometry(); + } void GRenderWindow::moveContext() @@ -212,12 +236,16 @@ 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) @@ -245,14 +273,6 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent *event) this->TouchReleased(); } -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 c1da2bc5f..9e7868a1c 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h @@ -121,9 +121,6 @@ public: void mousePressEvent(QMouseEvent *event) override; 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 fa3fa210c..62421db06 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp @@ -27,17 +27,17 @@ const std::array Config::defaults = Qt::Key_I, 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 }; 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_scale = qt_config->value("pad_circle_modifier_scale", 0.5).toFloat(); + 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.4).toFloat(); qt_config->endGroup(); qt_config->beginGroup("Core"); @@ -128,8 +128,9 @@ 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]]); + 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->endGroup(); diff --git a/src/citra_qt/configure_input.cpp b/src/citra_qt/configure_input.cpp index 9c7a67174..f751317e7 100644 --- a/src/citra_qt/configure_input.cpp +++ b/src/citra_qt/configure_input.cpp @@ -1,18 +1,22 @@ -// 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/configure_input.h" +#include "citra_qt/keybinding_names.h" +#include "common/string_util.h" + +#include "input_core/input_core.h" +#include "input_core/devices/keyboard.h" + ConfigureInput::ConfigureInput(QWidget* parent) : QWidget(parent), ui(std::make_unique()) { ui->setupUi(this); // Initialize mapping of input enum to UI button. - input_mapping = { + qt_buttons = { { 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) }, @@ -35,19 +39,18 @@ ConfigureInput::ConfigureInput(QWidget* parent) : QWidget(parent), ui(std::make_ { 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) }, - { std::make_pair(Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod) }, + { std::make_pair(Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight) } }; // Attach handle click method to each button click. - for (const auto& entry : input_mapping) { + for (const auto& entry : qt_buttons) { connect(entry.second, SIGNAL(released()), this, SLOT(handleClick())); } + connect(ui->buttonCircleMod, SIGNAL(released()), this, SLOT(handleClick())); connect(ui->buttonRestoreDefaults, SIGNAL(released()), this, SLOT(restoreDefaults())); + setFocusPolicy(Qt::ClickFocus); - timer = new QTimer(this); - timer->setSingleShot(true); - connect(timer, &QTimer::timeout, this, [&]() { key_pressed = Qt::Key_Escape; setKey(); }); + this->setConfiguration(); } @@ -59,22 +62,12 @@ void ConfigureInput::handleClick() { grabKeyboard(); grabMouse(); changing_button = sender; - timer->start(5000); //Cancel after 5 seconds -} + auto update = []() { + QCoreApplication::processEvents(); + }; + auto input_device = InputCore::DetectInput(5000, update); -void ConfigureInput::applyConfiguration() { - for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { - int value = getKeyValue(input_mapping[Settings::NativeInput::Values(i)]->text()); - Settings::values.input_mappings[Settings::NativeInput::All[i]] = value; - } - Settings::Apply(); -} - -void ConfigureInput::setConfiguration() { - for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { - QString keyValue = getKeyName(Settings::values.input_mappings[i]); - input_mapping[Settings::NativeInput::Values(i)]->setText(keyValue); - } + setKey(input_device); } void ConfigureInput::keyPressEvent(QKeyEvent* event) { @@ -82,26 +75,57 @@ void ConfigureInput::keyPressEvent(QKeyEvent* event) { return; if (!event || event->key() == Qt::Key_unknown) return; - key_pressed = event->key(); - timer->stop(); - setKey(); + + auto keyboard = InputCore::GetKeyboard(); + KeyboardKey param = KeyboardKey(event->key(), QKeySequence(event->key()).toString().toStdString()); + keyboard->KeyPressed(param); } -void ConfigureInput::setKey() { - const QString key_value = getKeyName(key_pressed); - if (key_pressed == Qt::Key_Escape) +void ConfigureInput::applyConfiguration() { + for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { + Settings::values.input_mappings[Settings::NativeInput::All[i]] = button_mapping[qt_buttons[Settings::NativeInput::Values(i)]]; + } + Settings::values.pad_circle_modifier = button_mapping[ui->buttonCircleMod]; + Settings::Apply(); +} + +void ConfigureInput::setConfiguration() { + for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { + Settings::InputDeviceMapping mapping = Settings::values.input_mappings[i]; + button_mapping[qt_buttons[Settings::NativeInput::Values(i)]] = mapping; + + qt_buttons[Settings::NativeInput::Values(i)]->setText(getKeyName(mapping)); + } + button_mapping[ui->buttonCircleMod] = Settings::values.pad_circle_modifier; + ui->buttonCircleMod->setText(getKeyName(Settings::values.pad_circle_modifier)); +} + +void ConfigureInput::setKey(Settings::InputDeviceMapping keyPressed) { + if (keyPressed.key == "" || keyPressed.key == std::to_string(Qt::Key_Escape)) changing_button->setText(previous_mapping); - else - changing_button->setText(key_value); - removeDuplicates(key_value); - key_pressed = Qt::Key_unknown; + else { + changing_button->setText(getKeyName(keyPressed)); + button_mapping[changing_button] = keyPressed; + removeDuplicates(keyPressed); + } releaseKeyboard(); releaseMouse(); changing_button = nullptr; previous_mapping = nullptr; } -QString ConfigureInput::getKeyName(int key_code) const { +QString ConfigureInput::getKeyName(Settings::InputDeviceMapping mapping) const { + if (mapping.key.empty()) + return ""; + int key_code; + if (!Common::TryParse(mapping.key, &key_code)) + key_code = -1; + if (mapping.device == Settings::Device::Gamepad) { + if (KeyBindingNames::sdl_gamepad_names.size() > key_code && key_code >= 0) + return KeyBindingNames::sdl_gamepad_names[key_code]; + else + return ""; + } if (key_code == Qt::Key_Shift) return tr("Shift"); if (key_code == Qt::Key_Control) @@ -110,40 +134,30 @@ QString ConfigureInput::getKeyName(int key_code) const { return tr("Alt"); if (key_code == Qt::Key_Meta) return ""; - if (key_code == -1) + if (key_code < 0) return ""; return QKeySequence(key_code).toString(); } -Qt::Key ConfigureInput::getKeyValue(const QString& text) const { - if (text == "Shift") - return Qt::Key_Shift; - if (text == "Ctrl") - return Qt::Key_Control; - if (text == "Alt") - return Qt::Key_Alt; - if (text == "Meta") - return Qt::Key_unknown; - if (text == "") - return Qt::Key_unknown; - - return Qt::Key(QKeySequence(text)[0]); -} - -void ConfigureInput::removeDuplicates(const QString& newValue) { - for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { - if (changing_button != input_mapping[Settings::NativeInput::Values(i)]) { - const QString oldValue = input_mapping[Settings::NativeInput::Values(i)]->text(); - if (newValue == oldValue) - input_mapping[Settings::NativeInput::Values(i)]->setText(""); +void ConfigureInput::removeDuplicates(const Settings::InputDeviceMapping newValue) { + for (auto& entry : button_mapping) { + if (changing_button != entry.first) { + if (newValue == entry.second && newValue.key == entry.second.key) { + entry.first->setText(""); + entry.second = Settings::InputDeviceMapping(); + } } } } void ConfigureInput::restoreDefaults() { for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { - const QString keyValue = getKeyName(Config::defaults[i].toInt()); - input_mapping[Settings::NativeInput::Values(i)]->setText(keyValue); + Settings::InputDeviceMapping mapping = Settings::InputDeviceMapping(Config::defaults[i].toInt()); + button_mapping[qt_buttons[Settings::NativeInput::Values(i)]] = mapping; + const QString keyValue = getKeyName(Settings::InputDeviceMapping(Config::defaults[i].toInt())); + qt_buttons[Settings::NativeInput::Values(i)]->setText(keyValue); } + button_mapping[ui->buttonCircleMod] = Settings::InputDeviceMapping(Qt::Key_F); + ui->buttonCircleMod->setText(getKeyName(Settings::InputDeviceMapping(Qt::Key_F))); } diff --git a/src/citra_qt/configure_input.h b/src/citra_qt/configure_input.h index fe8ea5580..fe02360b5 100644 --- a/src/citra_qt/configure_input.h +++ b/src/citra_qt/configure_input.h @@ -3,17 +3,17 @@ // Refer to the license.txt file included. #pragma once - +#include #include #include #include "citra_qt/config.h" #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,29 +30,25 @@ public: private: std::unique_ptr ui; - std::map input_mapping; - int key_pressed; + std::map qt_buttons; + std::map button_mapping; QPushButton* changing_button = nullptr; ///< button currently waiting for key press. QString previous_mapping; - QTimer* timer; /// Load configuration settings into button text void setConfiguration(); /// Check all inputs for duplicate keys. Clears out any other button with the same value as this button's new value. - void removeDuplicates(const QString& newValue); + void removeDuplicates(const Settings::InputDeviceMapping newValue); - /// Handle key press event for input tab when a button is 'waiting'. + /// 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 - QString getKeyName(int key_code) const; - - /// Convert letter/name of key to its ASCII value. - Qt::Key getKeyValue(const QString& text) const; + QString getKeyName(Settings::InputDeviceMapping mapping) const; /// Set button text to name of key pressed. - void setKey(); + void setKey(Settings::InputDeviceMapping keyPressed); private slots: /// Event handler for all button released() event. diff --git a/src/citra_qt/keybinding_names.cpp b/src/citra_qt/keybinding_names.cpp new file mode 100644 index 000000000..1ee900af3 --- /dev/null +++ b/src/citra_qt/keybinding_names.cpp @@ -0,0 +1,34 @@ +// 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..027fb70a3 --- /dev/null +++ b/src/citra_qt/keybinding_names.h @@ -0,0 +1,17 @@ +// 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 5b2ad530d..feefce226 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -520,7 +520,6 @@ void GMainWindow::OnConfigure() { if (result == QDialog::Accepted) { configureDialog.applyConfiguration(); - render_window->ReloadSetKeymaps(); config->Save(); } } diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index aa6eee2a3..e17f70818 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -6,7 +6,6 @@ set(SRCS emu_window.cpp file_util.cpp hash.cpp - key_map.cpp logging/filter.cpp logging/text_formatter.cpp logging/backend.cpp @@ -36,7 +35,6 @@ set(HEADERS emu_window.h file_util.h hash.h - key_map.h linear_disk_cache.h logging/text_formatter.h logging/filter.h diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp index fd728c109..1ac32917f 100644 --- a/src/common/emu_window.cpp +++ b/src/common/emu_window.cpp @@ -6,35 +6,11 @@ #include #include "common/assert.h" -#include "common/key_map.h" #include "emu_window.h" +#include "input_core/input_core.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); -} - /** * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout * @param layout FramebufferLayout object describing the framebuffer size and screen positions @@ -64,20 +40,23 @@ 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) / + int 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) / + 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/common/emu_window.h b/src/common/emu_window.h index 57e303b6d..616761a1e 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -70,30 +70,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 @@ -111,38 +87,6 @@ public: */ void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); - /** - * 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: @@ -220,12 +164,6 @@ 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; } virtual ~EmuWindow() {} @@ -282,16 +220,8 @@ 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) - /** * 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/common/key_map.cpp b/src/common/key_map.cpp deleted file mode 100644 index ad311d66b..000000000 --- a/src/common/key_map.cpp +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include - -#include "common/emu_window.h" -#include "common/key_map.h" - -namespace KeyMap { - -// TODO (wwylele): currently we treat c-stick as four direction buttons -// and map it directly to EmuWindow::ButtonPressed. -// It should go the analog input way like circle pad does. -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_NONE, - 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, - - IndirectTarget::CirclePadUp, - IndirectTarget::CirclePadDown, - IndirectTarget::CirclePadLeft, - IndirectTarget::CirclePadRight, - IndirectTarget::CirclePadModifier, -}}; - -static std::map key_map; -static int next_device_id = 0; - -static bool circle_pad_up = false; -static bool circle_pad_down = false; -static bool circle_pad_left = false; -static bool circle_pad_right = false; -static bool circle_pad_modifier = false; - -static void UpdateCirclePad(EmuWindow& emu_window) { - constexpr float SQRT_HALF = 0.707106781; - int x = 0, y = 0; - - if (circle_pad_right) - ++x; - if (circle_pad_left) - --x; - if (circle_pad_up) - ++y; - if (circle_pad_down) - --y; - - float modifier = circle_pad_modifier ? Settings::values.pad_circle_modifier_scale : 1.0; - emu_window.CirclePadUpdated(x * modifier * (y == 0 ? 1.0 : SQRT_HALF), y * modifier * (x == 0 ? 1.0 : SQRT_HALF)); -} - -int NewDeviceId() { - return next_device_id++; -} - -void SetKeyMapping(HostDeviceKey key, KeyTarget target) { - key_map[key] = target; -} - -void ClearKeyMapping(int device_id) { - auto iter = key_map.begin(); - while (iter != key_map.end()) { - if (iter->first.device_id == device_id) - key_map.erase(iter++); - else - ++iter; - } -} - -void PressKey(EmuWindow& emu_window, HostDeviceKey key) { - auto target = key_map.find(key); - if (target == key_map.end()) - return; - - if (target->second.direct) { - emu_window.ButtonPressed({{target->second.target.direct_target_hex}}); - } else { - switch (target->second.target.indirect_target) { - case IndirectTarget::CirclePadUp: - circle_pad_up = true; - UpdateCirclePad(emu_window); - break; - case IndirectTarget::CirclePadDown: - circle_pad_down = true; - UpdateCirclePad(emu_window); - break; - case IndirectTarget::CirclePadLeft: - circle_pad_left = true; - UpdateCirclePad(emu_window); - break; - case IndirectTarget::CirclePadRight: - circle_pad_right = true; - UpdateCirclePad(emu_window); - break; - case IndirectTarget::CirclePadModifier: - circle_pad_modifier = true; - UpdateCirclePad(emu_window); - break; - } - } -} - -void ReleaseKey(EmuWindow& emu_window,HostDeviceKey key) { - auto target = key_map.find(key); - if (target == key_map.end()) - return; - - if (target->second.direct) { - emu_window.ButtonReleased({{target->second.target.direct_target_hex}}); - } else { - switch (target->second.target.indirect_target) { - case IndirectTarget::CirclePadUp: - circle_pad_up = false; - UpdateCirclePad(emu_window); - break; - case IndirectTarget::CirclePadDown: - circle_pad_down = false; - UpdateCirclePad(emu_window); - break; - case IndirectTarget::CirclePadLeft: - circle_pad_left = false; - UpdateCirclePad(emu_window); - break; - case IndirectTarget::CirclePadRight: - circle_pad_right = false; - UpdateCirclePad(emu_window); - break; - case IndirectTarget::CirclePadModifier: - circle_pad_modifier = false; - UpdateCirclePad(emu_window); - break; - } - } -} - -} diff --git a/src/common/key_map.h b/src/common/key_map.h deleted file mode 100644 index b62f017c6..000000000 --- a/src/common/key_map.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include "core/hle/service/hid/hid.h" - -class EmuWindow; - -namespace KeyMap { - -/** - * Represents key mapping targets that are not real 3DS buttons. - * They will be handled by KeyMap and translated to 3DS input. - */ -enum class IndirectTarget { - CirclePadUp, - CirclePadDown, - CirclePadLeft, - CirclePadRight, - CirclePadModifier, -}; - -/** - * Represents a key mapping target. It can be a PadState that represents real 3DS buttons, - * or an IndirectTarget. - */ -struct KeyTarget { - bool direct; - union { - u32 direct_target_hex; - IndirectTarget indirect_target; - } target; - - KeyTarget() : direct(true) { - target.direct_target_hex = 0; - } - - KeyTarget(Service::HID::PadState pad) : direct(true) { - target.direct_target_hex = pad.hex; - } - - KeyTarget(IndirectTarget i) : direct(false) { - target.indirect_target = i; - } -}; - -/** - * Represents a key for a specific host device. - */ -struct HostDeviceKey { - int key_code; - int device_id; ///< Uniquely identifies a host device - - bool operator<(const HostDeviceKey &other) const { - return std::tie(key_code, device_id) < - std::tie(other.key_code, other.device_id); - } - - bool operator==(const HostDeviceKey &other) const { - return std::tie(key_code, device_id) == - std::tie(other.key_code, other.device_id); - } -}; - -extern const std::array mapping_targets; - -/** - * Generates a new device id, which uniquely identifies a host device within KeyMap. - */ -int NewDeviceId(); - -/** - * Maps a device-specific key to a target (a PadState or an IndirectTarget). - */ -void SetKeyMapping(HostDeviceKey key, KeyTarget target); - -/** - * Clears all key mappings belonging to one device. - */ -void ClearKeyMapping(int device_id); - -/** - * Maps a key press action and call the corresponding function in EmuWindow - */ -void PressKey(EmuWindow& emu_window, HostDeviceKey key); - -/** - * Maps a key release action and call the corresponding function in EmuWindow - */ -void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key); - -} diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 0b2fabec9..40f723c54 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -66,7 +66,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 c6910b1c7..b845c0934 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -82,6 +82,7 @@ enum class Class : ClassType { Audio_DSP, ///< The HLE implementation of the DSP Audio_Sink, ///< Emulator audio output backend Loader, ///< ROM loader + Input, ///< Input backend Count ///< Total number of logging classes }; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index cdec11388..c0658eac4 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -12,6 +12,8 @@ #include "core/hle/service/hid/hid_spvr.h" #include "core/hle/service/hid/hid_user.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" @@ -74,11 +76,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; @@ -115,7 +116,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 669b1f723..2c3e4c1af 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -24,6 +24,7 @@ namespace HID { * Structure of a Pad controller state. */ struct PadState { + PadState() = default; union { u32 hex; @@ -54,6 +55,13 @@ struct PadState { BitField<30, 1, u32> circle_up; BitField<31, 1, u32> circle_down; }; + 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 a4dfb7e43..d547f7bd9 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -430,9 +430,6 @@ static void VBlankCallback(u64 userdata, int cycles_late) { GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0); GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1); - // Check for user input updates - Service::HID::Update(); - // Reschedule recurrent event CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event); } diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 1b6733a79..cc421fb01 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -7,7 +7,7 @@ #include "audio_core/audio_core.h" #include "core/gdbstub/gdbstub.h" - +#include "input_core/input_core.h" #include "video_core/video_core.h" namespace Settings { @@ -26,6 +26,7 @@ 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 fb3fbe391..55bb7442e 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -4,13 +4,15 @@ #pragma once -#include #include +#include +#include +#include #include "common/common_types.h" +#include "common/string_util.h" namespace Settings { - namespace NativeInput { enum Values { // directly mapped keys @@ -22,41 +24,101 @@ enum Values { // indirectly mapped keys CIRCLE_UP, 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 = {{ + // 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, - CIRCLE_MODIFIER, -}}; + CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT +} }; } +enum class DeviceFramework { + Qt, SDL +}; +enum class Device { + Keyboard, Gamepad +}; +struct InputDeviceMapping { + DeviceFramework framework = DeviceFramework::Qt; + int number = 0; + Device device = Device::Keyboard; + std::string key; + + InputDeviceMapping() = default; + explicit InputDeviceMapping(int keyboardKey) { + key = std::to_string(keyboardKey); + } + explicit InputDeviceMapping(const std::string& input) { + std::vector parts; + Common::SplitString(input, '/', parts); + if (parts.size() != 4) + return; + + if (parts[0] == "Qt") + framework = DeviceFramework::Qt; + else 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; + key = parts[3]; + } + + 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; + } + std::string ToString() const { + std::string result; + if (framework == DeviceFramework::Qt) + result = "Qt"; + else 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 += 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; // Core @@ -87,8 +149,8 @@ struct Values { // Debugging bool use_gdbstub; u16 gdbstub_port; -} extern values; +}; +extern Values values; void Apply(); - } diff --git a/src/core/system.cpp b/src/core/system.cpp index 59051faa5..25f9ca9cf 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -14,6 +14,7 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory.h" #include "video_core/video_core.h" +#include "input_core/input_core.h" namespace System { @@ -31,6 +32,7 @@ Result Init(EmuWindow* emu_window) { } AudioCore::Init(); CheatCore::Init(); + InputCore::Init(); GDBStub::Init(); is_powered_on = true; @@ -45,6 +47,7 @@ bool IsPoweredOn() { void Shutdown() { GDBStub::Shutdown(); CheatCore::Shutdown(); + InputCore::Shutdown(); AudioCore::Shutdown(); VideoCore::Shutdown(); HLE::Shutdown(); 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..660490b41 --- /dev/null +++ b/src/input_core/devices/device.h @@ -0,0 +1,48 @@ +// 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, const std::map>& keyMap) = 0; + + /** + * Process inputs that were pressed since last frame + */ + virtual void 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: + std::map> key_mapping; ///< Maps the string in the settings file to the HID Padstate object +}; diff --git a/src/input_core/devices/gamecontrollerdb.h b/src/input_core/devices/gamecontrollerdb.h new file mode 100644 index 000000000..78aa70360 --- /dev/null +++ b/src/input_core/devices/gamecontrollerdb.h @@ -0,0 +1,167 @@ +// 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 8/27/2016 +#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, PS4 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 : a3, leftx : a0, lefty : a1, rightshoulder : b5, rightstick : b11, righttrigger : a4, rightx : a2, righty : a5, start : b9, x : b0, y : b3, 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, +)"; + 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, PS4 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 : a3, leftx : a0, lefty : a1, rightshoulder : b5, rightstick : b11, righttrigger : a4, rightx : a2, righty : a5, start : b9, x : b0, y : b3, 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, +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, +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, +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 : 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, 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, +050000004c050000c405000000010000, PS4 Controller(Bluetooth), 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 : a3, leftx : a0, lefty : a1, rightshoulder : b5, rightstick : b11, righttrigger : a4, rightx : a2, righty : a5, start : b9, x : b0, y : b3, 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, platform:Linux, 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 : a3, 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, +)"; + const char* db_file4 = R"( +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, +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, +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, +)"; +} \ No newline at end of file diff --git a/src/input_core/devices/keyboard.cpp b/src/input_core/devices/keyboard.cpp new file mode 100644 index 000000000..c7df014e1 --- /dev/null +++ b/src/input_core/devices/keyboard.cpp @@ -0,0 +1,105 @@ +// 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, const std::map>& keyMap) { + key_mapping = keyMap; + + //Check if keyboard is mapped for circle up or left. if so, set modifier to -1 + for (const auto& entry : key_mapping) { + if (entry.first == "") + continue; + for (const auto& padstate : entry.second) { + if (padstate == Service::HID::PAD_CIRCLE_UP || padstate == Service::HID::PAD_CIRCLE_LEFT) { + circle_pad_directions[stoi(entry.first)] = -1.0; + } + else if (padstate == Service::HID::PAD_CIRCLE_DOWN || padstate == Service::HID::PAD_CIRCLE_RIGHT) { + circle_pad_directions[stoi(entry.first)] = 1.0; + } + } + } + //Check if responsible for circle pad modifier + auto mapping = Settings::values.pad_circle_modifier; + if (mapping.device == Settings::Device::Keyboard && mapping.key != "") + circle_pad_modifier = KeyboardKey(stoi(mapping.key),""); + return true; +} + +void Keyboard::ProcessInput() { + std::map keysPressedCopy; + { + std::lock_guard lock(m); + keysPressedCopy = keys_pressed; + } + bool circlePadModPressed = keysPressedCopy[circle_pad_modifier]; + for (const auto& entry : key_mapping) { + if (entry.first == "") + continue; + int keycode = std::stoi(entry.first); + KeyboardKey proxy = KeyboardKey(keycode, ""); + + //if key is pressed when prev state is unpressed, or if key pressed and is a circle pad direction + if ((keysPressedCopy[proxy] == true && keys_pressed_last[keycode] == false) || (keysPressedCopy[proxy] == true && circle_pad_directions.count(keycode))) { + for (const auto& key : entry.second) { + if (circle_pad_directions.count(keycode)) { //If is analog key press + float modifier = (circlePadModPressed) ? Settings::values.pad_circle_modifier_scale : 1; + KeyMap::PressKey(key, circle_pad_directions[keycode] * modifier); + } + else // Is digital key press + KeyMap::PressKey(key, 1.0); + } + keys_pressed_last[keycode] = true; + } + else if (keysPressedCopy[proxy] == false && keys_pressed_last[keycode] == true) { + for (const auto& key : entry.second) { + KeyMap::ReleaseKey(key); + } + keys_pressed_last[keycode] = false; + } + } +} + +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() { + std::map keysPressedCopy; + { + std::lock_guard lock(m); + keysPressedCopy = keys_pressed; + } + for (const auto& entry : keysPressedCopy) { + int keycode = entry.first.key; + if (keysPressedCopy[entry.first] == true && keys_pressed_last[keycode] == false) { + return Settings::InputDeviceMapping("QT/0/Keyboard/" + std::to_string(keycode)); + } + } + 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..5b5d7a2ca --- /dev/null +++ b/src/input_core/devices/keyboard.h @@ -0,0 +1,48 @@ +// 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, const std::map>& keyMap) override; + void 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..334c7fb42 --- /dev/null +++ b/src/input_core/devices/sdl_gamepad.cpp @@ -0,0 +1,184 @@ +// 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/sdl_gamepad.h" +#include "input_core/devices/gamecontrollerdb.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, const std::map>& keyMap) { + 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) { + LOG_INFO(Input, "Controller found but unable to open connection."); + return false; + } + } + key_mapping = keyMap; + for (const auto& entry : key_mapping) { + keys_pressed[entry.first] = false; + } + + return true; +} + +void SDLGamepad::ProcessInput() { + if (gamepad == nullptr) + return; + SDL_GameControllerUpdate(); + for (const auto& entry : key_mapping) { + SDL_GameControllerButton button = SDL_GameControllerGetButtonFromString(gamepadinput_to_sdlname_mapping[static_cast(stoi(entry.first))].c_str()); + if (button != SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_INVALID) { + Uint8 pressed = SDL_GameControllerGetButton(gamepad, button); + if (pressed == 1 && keys_pressed[entry.first] == false) { + for (const auto& padstate : entry.second) { + KeyMap::PressKey(padstate, 1.0); + keys_pressed[entry.first] = true; + } + } + else if (pressed == 0 && keys_pressed[entry.first] == true) { + for (const auto& padstate : entry.second) { + KeyMap::ReleaseKey(padstate); + keys_pressed[entry.first] = false; + } + } + } + else { + // Try axis if button isn't valid + SDL_GameControllerAxis axis = SDL_GameControllerGetAxisFromString(gamepadinput_to_sdlname_mapping[static_cast(stoi(entry.first))].c_str()); + if (axis != SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_INVALID) { + Sint16 value = SDL_GameControllerGetAxis(gamepad, axis); + for (const auto& padstate : entry.second) { + // TODO: calculate deadzone by radial field rather than axial field. (sqrt(x^2 + y^2) > deadzone) + // dont process if in deadzone. Replace later with settings for deadzone. + if (abs(value) < 0.2 * 32767.0) + KeyMap::ReleaseKey(padstate); + else + KeyMap::PressKey(padstate, (float)value / 32767.0); + } + } + } + } +} + +bool SDLGamepad::CloseDevice() { + if (gamepad != nullptr) { + SDL_GameControllerClose(gamepad); + } + return true; +} + +std::vector> SDLGamepad::GetAllDevices() { + std::vector> devices; + if (!SDLGamepad::SDLInitialized && SDL_Init(SDL_INIT_GAMECONTROLLER) < 0) { + LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_GAMECONTROLLER) failed"); + return devices; + } + LoadGameControllerDB(); + SDL_GameControllerEventState(SDL_IGNORE); + for (int i = 0; i < 8; i++) { + SDL_GameController* gamecontroller; + if (SDL_IsGameController(i)) { + gamecontroller = SDL_GameControllerOpen(i); + if (gamecontroller != nullptr) { + devices.push_back(std::make_shared(i, gamecontroller)); + } + } + } + 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(""); + SDL_GameControllerUpdate(); + for (int i = 0; i < SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_MAX; i++) + { + Uint8 pressed = SDL_GameControllerGetButton(gamepad, SDL_GameControllerButton(i)); + if (pressed == 0) + continue; + + auto buttonName = SDL_GameControllerGetStringForButton(SDL_GameControllerButton(i)); + for (const auto& mapping : gamepadinput_to_sdlname_mapping) { + if (mapping.second == buttonName) { + return Settings::InputDeviceMapping("SDL/" + std::to_string(number) + "/" + "Gamepad/" + std::to_string(static_cast(mapping.first))); + } + } + } + for (int i = 0; i < SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_MAX; i++) { + Sint16 value = SDL_GameControllerGetAxis(gamepad, SDL_GameControllerAxis(i)); + // TODO: calculate deadzone by radial field rather than axial field. (sqrt(x^2 + y^2) > deadzone) + // dont process if in deadzone. Replace later with settings for deadzone. + if (abs(value) < 0.2 * 32767.0) + continue; + std::string modifier; + if (value > 0) + modifier = "+"; + else + modifier = "-"; + std::string axisName = SDL_GameControllerGetStringForAxis(SDL_GameControllerAxis(i)); + for (const auto& mapping : gamepadinput_to_sdlname_mapping) { + if (mapping.second == axisName) { + if ((mapping.first == GamepadInputs::LeftXMinus || + mapping.first == GamepadInputs::LeftYMinus || + mapping.first == GamepadInputs::RightXMinus || + mapping.first == GamepadInputs::RightYMinus) && modifier == "+") { + continue; + } + else if ((mapping.first == GamepadInputs::LeftXPlus || + mapping.first == GamepadInputs::LeftYPlus || + mapping.first == GamepadInputs::RightXPlus || + mapping.first == GamepadInputs::RightYPlus) && modifier == "-") { + continue; + } + return Settings::InputDeviceMapping("SDL/" + std::to_string(this->number) + "/" + "Gamepad/" + std::to_string(static_cast(mapping.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..8a62e5953 --- /dev/null +++ b/src/input_core/devices/sdl_gamepad.h @@ -0,0 +1,64 @@ +// 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, const std::map>& keyMap) override; + void 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" }, + }; + 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..c6a37b6d0 --- /dev/null +++ b/src/input_core/input_core.cpp @@ -0,0 +1,192 @@ +// 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 "input_core/input_core.h" +#include "input_core/devices/keyboard.h" +#include "input_core/devices/sdl_gamepad.h" + +namespace InputCore { +constexpr u64 frame_ticks = 268123480ull / 60; +static int tick_event; +static Service::HID::PadState pad_state; +static std::tuple circle_pad = { 0,0 }; +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::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 + +static void InputTickCallback(u64, int cycles_late) { + for (auto& device : devices) + device->ProcessInput(); + + Service::HID::Update(); + + // Reschedule recurrent event + CoreTiming::ScheduleEvent(frame_ticks - cycles_late, tick_event); +} + +Service::HID::PadState GetPadState() { + std::lock_guard lock(pad_state_mutex); + return pad_state; +} + +void SetPadState(const Service::HID::PadState& state) { + std::lock_guard lock(pad_state_mutex); + pad_state.hex = state.hex; +} + +std::tuple GetCirclePad() { + return circle_pad; +} + +void SetCirclePad(std::tuple pad) { + circle_pad = pad; +} + +std::shared_ptr GetKeyboard() { + if (main_keyboard == nullptr) + main_keyboard = std::make_shared(); + return main_keyboard; +} + +std::tuple GetTouchState() { + std::lock_guard lock(touch_mutex); + return std::make_tuple(touch_x, touch_y, touch_pressed); +} + +void SetTouchState(std::tuple value) { + std::lock_guard lock(touch_mutex); + std::tie(touch_x, touch_y, touch_pressed) = value; +} + +/// Helper method to check if device was already initialized +bool CheckIfMappingExists(const std::vector& uniqueMapping, Settings::InputDeviceMapping mappingToCheck) { + return std::any_of(uniqueMapping.begin(), uniqueMapping.end(), [mappingToCheck](const auto& mapping) { + return mapping == mappingToCheck; + }); +} + +/// Get Unique input mappings from settings +static std::vector GatherUniqueMappings() { + std::vector uniqueMappings; + + for (const auto& mapping : Settings::values.input_mappings) { + if (!CheckIfMappingExists(uniqueMappings, mapping)) { + uniqueMappings.push_back(mapping); + } + } + return uniqueMappings; +} + +/// Builds map of input keys to 3ds buttons for unique device +static std::map> BuildKeyMapping(Settings::InputDeviceMapping mapping) { + std::map> keyMapping; + for (size_t i = 0; i < Settings::values.input_mappings.size(); i++) { + Service::HID::PadState val = KeyMap::mapping_targets[i]; + std::string key = Settings::values.input_mappings[i].key; + if (Settings::values.input_mappings[i] == mapping) { + keyMapping[key].push_back(val); + } + } + return keyMapping; +} + +/// Generate a device for each unique mapping +static void GenerateUniqueDevices(const std::vector& uniqueMappings) { + devices.clear(); + std::shared_ptr input; + for (const auto& mapping : uniqueMappings) { + switch (mapping.framework) { + case Settings::DeviceFramework::Qt: + { + main_keyboard = std::make_shared(); + input = main_keyboard; + break; + } + 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); + + // Build map of inputs to listen for, for this device + auto keyMapping = BuildKeyMapping(mapping); + + input->InitDevice(mapping.number, keyMapping); + } +} + +/// Read settings to initialize devices +void ParseSettings() { + auto uniqueMappings = GatherUniqueMappings(); + GenerateUniqueDevices(uniqueMappings); +} + +void ReloadSettings() { + if (devices.empty()) + return; + devices.clear(); + ParseSettings(); +} + +/// Returns all available input devices. Used for key binding in GUI +std::vector> GetAllDevices() { + auto all_devices = SDLGamepad::GetAllDevices(); + all_devices.push_back(InputCore::GetKeyboard()); + + return all_devices; +} + +Settings::InputDeviceMapping 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 == "") { + 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 != "") + break; + } + }; + return input_device; +} + +void Init() { + ParseSettings(); + tick_event = CoreTiming::RegisterEvent("InputCore::tick_event", InputTickCallback); + CoreTiming::ScheduleEvent(frame_ticks, tick_event); +} + +void Shutdown() { + CoreTiming::UnscheduleEvent(tick_event, 0); + devices.clear(); +} +} diff --git a/src/input_core/input_core.h b/src/input_core/input_core.h new file mode 100644 index 000000000..22484030d --- /dev/null +++ b/src/input_core/input_core.h @@ -0,0 +1,78 @@ +// 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 "core/hle/service/hid/hid.h" +#include "core/settings.h" +#include "input_core/devices/device.h" + +class Keyboard; + +namespace InputCore { + +void Init(); +void Shutdown(); + +/** + * Threadsafe getter to the current PadState + * @return Service::HID::PadState instance + */ +Service::HID::PadState GetPadState(); + +/** + * Threadsafe setter for the current PadState + * @param state New PadState to overwrite current PadState. + */ +void SetPadState(const Service::HID::PadState& state); + +/** + * Getter for current CirclePad + * @return std::tuple CirclePad state + */ +std::tuple GetCirclePad(); + +/** + * Setter for current CirclePad + * @param circle New CirclePad state + */ +void SetCirclePad(std::tuple circle); + +/** + * Getter for Citra's main keyboard input handler + * @return std::shared_ptr Device Keyboard instance + */ +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 + */ +std::tuple GetTouchState(); + +/** + * Threadsafe setter for the current touch screen state. + * @param value New Touch State + */ +void SetTouchState(std::tuple value); + +/** + * Reload input key mapping settings during game-play + */ +void ReloadSettings(); + +/** + * 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 + */ +Settings::InputDeviceMapping DetectInput(int max_time, std::function update_GUI); +} diff --git a/src/input_core/key_map.cpp b/src/input_core/key_map.cpp new file mode 100644 index 000000000..cee50fb3b --- /dev/null +++ b/src/input_core/key_map.cpp @@ -0,0 +1,73 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include + +#include "common/emu_window.h" + +#include "input_core/input_core.h" +#include "input_core/key_map.h" + +namespace KeyMap { +constexpr int MAX_CIRCLEPAD_POS = 0x9C; /// Max value for a circle pad position +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 +}; + +void PressKey(const Service::HID::PadState target, const float strength) { + auto pad_state = InputCore::GetPadState(); + // If is digital keytarget + if (std::find(std::begin(analog_inputs), std::end(analog_inputs), target) == std::end(analog_inputs)) { + pad_state.hex |= target.hex; + InputCore::SetPadState(pad_state); + } + else { // it is analog input + auto circle_pad = InputCore::GetCirclePad(); + if (target == Service::HID::PAD_CIRCLE_UP || target == Service::HID::PAD_CIRCLE_DOWN) { + std::get<1>(circle_pad) = MAX_CIRCLEPAD_POS * strength * -1; + } + else if (target == Service::HID::PAD_CIRCLE_LEFT || target == Service::HID::PAD_CIRCLE_RIGHT) { + std::get<0>(circle_pad) = MAX_CIRCLEPAD_POS * strength; + } + InputCore::SetCirclePad(circle_pad); + } +} + +void ReleaseKey(const Service::HID::PadState target) { + auto pad_state = InputCore::GetPadState(); + // If is digital keytarget + if (std::find(std::begin(analog_inputs), std::end(analog_inputs), target) == std::end(analog_inputs)) { + pad_state.hex &= ~target.hex; + InputCore::SetPadState(pad_state); + } + else { // it is analog input + auto circle_pad = InputCore::GetCirclePad(); + if (target == Service::HID::PAD_CIRCLE_UP || target == Service::HID::PAD_CIRCLE_DOWN) { + std::get<1>(circle_pad) = 0; + } + else if (target == Service::HID::PAD_CIRCLE_LEFT || target == Service::HID::PAD_CIRCLE_RIGHT) { + std::get<0>(circle_pad) = 0; + } + InputCore::SetCirclePad(circle_pad); + } +} +} diff --git a/src/input_core/key_map.h b/src/input_core/key_map.h new file mode 100644 index 000000000..09edd879f --- /dev/null +++ b/src/input_core/key_map.h @@ -0,0 +1,23 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "core/hle/service/hid/hid.h" + +class EmuWindow; + +namespace KeyMap { +extern const std::array mapping_targets; +extern const std::array analog_inputs; + +///Handles the pressing of a key and modifies InputCore state +void PressKey(Service::HID::PadState target, float strength); + +///Handles the releasing of a key and modifies InputCore state +void ReleaseKey(Service::HID::PadState target); +}