InputCore Overhaul

This commit is contained in:
Anon 2016-12-16 22:50:33 -06:00
parent 51dd13b8a0
commit 06ef32f450
38 changed files with 1435 additions and 330 deletions

View File

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

View File

@ -18,7 +18,7 @@ create_directory_groups(${SRCS} ${HEADERS})
include_directories(${SDL2_INCLUDE_DIR})
add_executable(citra ${SRCS} ${HEADERS})
target_link_libraries(citra core video_core audio_core common)
target_link_libraries(citra core video_core audio_core input_core common)
target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad)
if (MSVC)
target_link_libraries(citra getopt)

View File

@ -45,17 +45,21 @@ static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = {
SDL_SCANCODE_L,
// indirectly mapped keys
SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D,
};
SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT};
void Config::ReadValues() {
// Controls
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
Settings::values.input_mappings[Settings::NativeInput::All[i]] =
sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]);
Settings::InputDeviceMapping(sdl2_config->Get(
"Controls", Settings::NativeInput::Mapping[i], std::to_string(defaults[i])));
}
Settings::values.pad_circle_modifier =
Settings::InputDeviceMapping(sdl2_config->Get("Controls", "pad_circle_modifier", ""));
Settings::values.pad_circle_modifier_scale =
(float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5);
Settings::values.pad_circle_deadzone =
(float)sdl2_config->GetReal("Controls", "pad_circle_deadzone", 0.3);
// Core
Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);

View File

@ -37,6 +37,10 @@ pad_circle_modifier =
# Must be in range of 0.0-1.0. Defaults to 0.5
pad_circle_modifier_scale =
# Deadzone applied to the circle pad.
# Must be in range of 0.0 - 1.0. Defaults to 0.3
pad_circle_deadzone =
[Core]
# Whether to use the Just-In-Time (JIT) compiler for CPU emulation
# 0: Interpreter (slow), 1 (default): JIT (fast)

View File

@ -8,13 +8,17 @@
#define SDL_MAIN_HANDLED
#include <SDL.h>
#include <glad/glad.h>
#include "citra/emu_window/emu_window_sdl2.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "common/string_util.h"
#include "core/frontend/key_map.h"
#include "core/hle/service/hid/hid.h"
#include "core/settings.h"
#include "input_core/devices/keyboard.h"
#include "input_core/input_core.h"
#include "citra/emu_window/emu_window_sdl2.h"
#include "video_core/video_core.h"
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
@ -32,11 +36,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);
}
}
@ -51,9 +58,6 @@ void EmuWindow_SDL2::OnResize() {
}
EmuWindow_SDL2::EmuWindow_SDL2() {
keyboard_id = KeyMap::NewDeviceId();
ReloadSetKeymaps();
SDL_SetMainReady();
@ -137,7 +141,7 @@ void EmuWindow_SDL2::PollEvents() {
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
OnKeyEvent(static_cast<int>(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);
@ -161,17 +165,7 @@ void EmuWindow_SDL2::DoneCurrent() {
SDL_GL_MakeCurrent(render_window, nullptr);
}
void EmuWindow_SDL2::ReloadSetKeymaps() {
KeyMap::ClearKeyMapping(keyboard_id);
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
KeyMap::SetKeyMapping(
{Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id},
KeyMap::mapping_targets[i]);
}
}
void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) {
SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
}

View File

@ -8,6 +8,7 @@
#include "core/frontend/emu_window.h"
struct SDL_Window;
struct SDL_Keysym;
class EmuWindow_SDL2 : public EmuWindow {
public:
@ -29,12 +30,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);

View File

@ -27,8 +27,10 @@ set(SRCS
configure_graphics.cpp
configure_system.cpp
configure_input.cpp
configure_system.cpp
game_list.cpp
hotkeys.cpp
keybinding_names.cpp
main.cpp
ui_settings.cpp
citra-qt.rc
@ -61,9 +63,11 @@ set(HEADERS
configure_graphics.h
configure_system.h
configure_input.h
configure_system.h
game_list.h
game_list_p.h
hotkeys.h
keybinding_names.h
main.h
ui_settings.h
)
@ -80,6 +84,7 @@ set(UIS
configure_graphics.ui
configure_system.ui
configure_input.ui
configure_system.ui
hotkeys.ui
main.ui
)
@ -100,7 +105,7 @@ if (APPLE)
else()
add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
endif()
target_link_libraries(citra-qt core video_core audio_core common qhexedit)
target_link_libraries(citra-qt core video_core audio_core input_core common qhexedit)
target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
target_link_libraries(citra-qt ${PLATFORM_LIBRARIES} Threads::Threads)

View File

@ -13,7 +13,12 @@
#include "common/scm_rev.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/frontend/key_map.h"
#include "core/settings.h"
#include "core/system.h"
#include "input_core/devices/keyboard.h"
#include "input_core/input_core.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/video_core.h"
@ -104,9 +109,6 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
std::string window_title =
Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
setWindowTitle(QString::fromStdString(window_title));
keyboard_id = KeyMap::NewDeviceId();
ReloadSetKeymaps();
}
void GRenderWindow::moveContext() {
@ -196,11 +198,17 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
}
void GRenderWindow::keyPressEvent(QKeyEvent* event) {
KeyMap::PressKey(*this, {event->key(), keyboard_id});
auto keyboard = InputCore::GetKeyboard();
KeyboardKey param =
KeyboardKey(event->key(), QKeySequence(event->key()).toString().toStdString());
keyboard->KeyPressed(param);
}
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
KeyMap::ReleaseKey(*this, {event->key(), keyboard_id});
auto keyboard = InputCore::GetKeyboard();
KeyboardKey param =
KeyboardKey(event->key(), QKeySequence(event->key()).toString().toStdString());
keyboard->KeyReleased(param);
}
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
@ -224,15 +232,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));
}

View File

@ -126,8 +126,6 @@ public:
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void ReloadSetKeymaps() override;
void OnClientAreaResized(unsigned width, unsigned height);
void InitRenderTarget();

View File

@ -23,18 +23,22 @@ const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults =
Qt::Key_K, Qt::Key_J, Qt::Key_L,
// indirectly mapped keys
Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D,
};
Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right};
void Config::ReadValues() {
qt_config->beginGroup("Controls");
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
Settings::values.input_mappings[Settings::NativeInput::All[i]] =
qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i])
.toInt();
Settings::InputDeviceMapping(
qt_config->value(Settings::NativeInput::Mapping[i], defaults[i])
.toString()
.toStdString());
}
Settings::values.pad_circle_modifier = Settings::InputDeviceMapping(
qt_config->value("pad_circle_modifier", 0).toString().toStdString());
Settings::values.pad_circle_modifier_scale =
qt_config->value("pad_circle_modifier_scale", 0.5).toFloat();
Settings::values.pad_circle_deadzone = qt_config->value("pad_circle_deadzone", 0.3f).toFloat();
qt_config->endGroup();
qt_config->beginGroup("Core");
@ -137,11 +141,16 @@ void Config::ReadValues() {
void Config::SaveValues() {
qt_config->beginGroup("Controls");
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]),
Settings::values.input_mappings[Settings::NativeInput::All[i]]);
qt_config->setValue(
QString::fromStdString(Settings::NativeInput::Mapping[i]),
QString::fromStdString(
Settings::values.input_mappings[Settings::NativeInput::All[i]].ToString()));
}
qt_config->setValue("pad_circle_modifier",
QString::fromStdString(Settings::values.pad_circle_modifier.ToString()));
qt_config->setValue("pad_circle_modifier_scale",
(double)Settings::values.pad_circle_modifier_scale);
qt_config->setValue("pad_circle_deadzone", (double)Settings::values.pad_circle_deadzone);
qt_config->endGroup();
qt_config->beginGroup("Core");

View File

@ -1,146 +1,165 @@
// 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 <memory>
#include <utility>
#include <QTimer>
#include "citra_qt/config.h"
#include "citra_qt/configure_input.h"
#include "citra_qt/keybinding_names.h"
#include "common/string_util.h"
static QString getKeyName(Qt::Key key_code) {
switch (key_code) {
case Qt::Key_Shift:
return QObject::tr("Shift");
case Qt::Key_Control:
return QObject::tr("Ctrl");
case Qt::Key_Alt:
return QObject::tr("Alt");
case Qt::Key_Meta:
case -1:
return "";
default:
return QKeySequence(key_code).toString();
}
}
#include "input_core/devices/keyboard.h"
#include "input_core/input_core.h"
ConfigureInput::ConfigureInput(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
timer(std::make_unique<QTimer>()) {
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
ui->setupUi(this);
setFocusPolicy(Qt::ClickFocus);
// Initialize mapping of input enum to UI button.
button_map = {
{Settings::NativeInput::Values::A, ui->buttonA},
{Settings::NativeInput::Values::B, ui->buttonB},
{Settings::NativeInput::Values::X, ui->buttonX},
{Settings::NativeInput::Values::Y, ui->buttonY},
{Settings::NativeInput::Values::L, ui->buttonL},
{Settings::NativeInput::Values::R, ui->buttonR},
{Settings::NativeInput::Values::ZL, ui->buttonZL},
{Settings::NativeInput::Values::ZR, ui->buttonZR},
{Settings::NativeInput::Values::START, ui->buttonStart},
{Settings::NativeInput::Values::SELECT, ui->buttonSelect},
{Settings::NativeInput::Values::HOME, ui->buttonHome},
{Settings::NativeInput::Values::DUP, ui->buttonDpadUp},
{Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown},
{Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft},
{Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight},
{Settings::NativeInput::Values::CUP, ui->buttonCStickUp},
{Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown},
{Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft},
{Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight},
{Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp},
{Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown},
{Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft},
{Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight},
{Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod},
};
{std::make_pair(Settings::NativeInput::Values::A, ui->buttonA)},
{std::make_pair(Settings::NativeInput::Values::B, ui->buttonB)},
{std::make_pair(Settings::NativeInput::Values::X, ui->buttonX)},
{std::make_pair(Settings::NativeInput::Values::Y, ui->buttonY)},
{std::make_pair(Settings::NativeInput::Values::L, ui->buttonL)},
{std::make_pair(Settings::NativeInput::Values::R, ui->buttonR)},
{std::make_pair(Settings::NativeInput::Values::ZL, ui->buttonZL)},
{std::make_pair(Settings::NativeInput::Values::ZR, ui->buttonZR)},
{std::make_pair(Settings::NativeInput::Values::START, ui->buttonStart)},
{std::make_pair(Settings::NativeInput::Values::SELECT, ui->buttonSelect)},
{std::make_pair(Settings::NativeInput::Values::HOME, ui->buttonHome)},
{std::make_pair(Settings::NativeInput::Values::DUP, ui->buttonDpadUp)},
{std::make_pair(Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown)},
{std::make_pair(Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft)},
{std::make_pair(Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight)},
{std::make_pair(Settings::NativeInput::Values::CUP, ui->buttonCStickUp)},
{std::make_pair(Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown)},
{std::make_pair(Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft)},
{std::make_pair(Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight)},
{std::make_pair(Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp)},
{std::make_pair(Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown)},
{std::make_pair(Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft)},
{std::make_pair(Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight)}};
// Attach handle click method to each button click.
for (const auto& entry : button_map) {
const Settings::NativeInput::Values input_id = entry.first;
connect(entry.second, &QPushButton::released,
[this, input_id]() { handleClick(input_id); });
connect(entry.second, &QPushButton::released, [=]() { handleClick(entry.second); });
}
connect(ui->buttonCircleMod, &QPushButton::released,
[=]() { handleClick(ui->buttonCircleMod); });
connect(ui->buttonRestoreDefaults, &QPushButton::released, [=]() { restoreDefaults(); });
connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
timer->setSingleShot(true);
connect(timer.get(), &QTimer::timeout, [this]() {
releaseKeyboard();
releaseMouse();
current_input_id = boost::none;
updateButtonLabels();
});
setFocusPolicy(Qt::ClickFocus);
this->loadConfiguration();
}
void ConfigureInput::handleClick(QPushButton* sender) {
if (sender == nullptr)
return;
previous_mapping = sender->text();
sender->setText(tr("[press key]"));
sender->setFocus();
grabKeyboard();
grabMouse();
changing_button = sender;
auto update = []() { QCoreApplication::processEvents(); };
auto input_device = InputCore::DetectInput(5000, update);
setKey(input_device);
}
void ConfigureInput::keyPressEvent(QKeyEvent* event) {
if (!changing_button)
return;
if (!event || event->key() == Qt::Key_unknown)
return;
auto keyboard = InputCore::GetKeyboard();
KeyboardKey param =
KeyboardKey(event->key(), QKeySequence(event->key()).toString().toStdString());
keyboard->KeyPressed(param);
}
void ConfigureInput::applyConfiguration() {
for (const auto& input_id : Settings::NativeInput::All) {
const size_t index = static_cast<size_t>(input_id);
Settings::values.input_mappings[index] = static_cast<int>(key_map[input_id]);
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
Settings::values.input_mappings[Settings::NativeInput::All[i]] =
key_map[button_map[Settings::NativeInput::Values(i)]];
}
Settings::values.pad_circle_modifier = key_map[ui->buttonCircleMod];
Settings::Apply();
}
void ConfigureInput::loadConfiguration() {
for (const auto& input_id : Settings::NativeInput::All) {
const size_t index = static_cast<size_t>(input_id);
key_map[input_id] = static_cast<Qt::Key>(Settings::values.input_mappings[index]);
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
Settings::InputDeviceMapping mapping = Settings::values.input_mappings[i];
key_map[button_map[Settings::NativeInput::Values(i)]] = mapping;
button_map[Settings::NativeInput::Values(i)]->setText(getKeyName(mapping));
}
key_map[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 == -1 || keyPressed.key == Qt::Key_Escape)
changing_button->setText(previous_mapping);
else {
changing_button->setText(getKeyName(keyPressed));
key_map[changing_button] = keyPressed;
removeDuplicates(keyPressed);
}
releaseKeyboard();
releaseMouse();
changing_button = nullptr;
previous_mapping = nullptr;
}
QString ConfigureInput::getKeyName(Settings::InputDeviceMapping mapping) {
if (mapping.key == -1)
return "";
if (mapping.device == Settings::Device::Gamepad) {
if (KeyBindingNames::sdl_gamepad_names.size() > mapping.key && mapping.key >= 0)
return KeyBindingNames::sdl_gamepad_names[mapping.key];
else
return "";
}
if (mapping.key == Qt::Key_Shift)
return tr("Shift");
if (mapping.key == Qt::Key_Control)
return tr("Ctrl");
if (mapping.key == Qt::Key_Alt)
return tr("Alt");
if (mapping.key == Qt::Key_Meta)
return "";
if (mapping.key < 0)
return "";
return QKeySequence(mapping.key).toString();
}
void ConfigureInput::removeDuplicates(const Settings::InputDeviceMapping newValue) {
for (auto& entry : key_map) {
if (changing_button != entry.first) {
if (newValue == entry.second && newValue.key == entry.second.key) {
entry.first->setText("");
entry.second = Settings::InputDeviceMapping();
}
}
}
updateButtonLabels();
}
void ConfigureInput::restoreDefaults() {
for (const auto& input_id : Settings::NativeInput::All) {
const size_t index = static_cast<size_t>(input_id);
key_map[input_id] = static_cast<Qt::Key>(Config::defaults[index].toInt());
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
Settings::InputDeviceMapping mapping =
Settings::InputDeviceMapping(Config::defaults[i].toInt());
key_map[button_map[Settings::NativeInput::Values(i)]] = mapping;
const QString keyValue =
getKeyName(Settings::InputDeviceMapping(Config::defaults[i].toInt()));
button_map[Settings::NativeInput::Values(i)]->setText(keyValue);
}
updateButtonLabels();
applyConfiguration();
}
void ConfigureInput::updateButtonLabels() {
for (const auto& input_id : Settings::NativeInput::All) {
button_map[input_id]->setText(getKeyName(key_map[input_id]));
}
}
void ConfigureInput::handleClick(Settings::NativeInput::Values input_id) {
QPushButton* button = button_map[input_id];
button->setText(tr("[press key]"));
button->setFocus();
current_input_id = input_id;
grabKeyboard();
grabMouse();
timer->start(5000); // Cancel after 5 seconds
}
void ConfigureInput::keyPressEvent(QKeyEvent* event) {
releaseKeyboard();
releaseMouse();
if (!current_input_id || !event)
return;
if (event->key() != Qt::Key_Escape)
setInput(*current_input_id, static_cast<Qt::Key>(event->key()));
updateButtonLabels();
current_input_id = boost::none;
timer->stop();
}
void ConfigureInput::setInput(Settings::NativeInput::Values input_id, Qt::Key key_pressed) {
// Remove duplicates
for (auto& pair : key_map) {
if (pair.second == key_pressed)
pair.second = Qt::Key_unknown;
}
key_map[input_id] = key_pressed;
key_map[ui->buttonCircleMod] = Settings::InputDeviceMapping(Qt::Key_F);
ui->buttonCircleMod->setText(getKeyName(Settings::InputDeviceMapping(Qt::Key_F)));
}

View File

@ -3,17 +3,15 @@
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <QKeyEvent>
#include <QWidget>
#include <boost/optional.hpp>
#include "core/settings.h"
#include "input_core/devices/device.h"
#include "ui_configure_input.h"
class QPushButton;
class QString;
class QTimer;
namespace Ui {
class ConfigureInput;
@ -30,28 +28,30 @@ public:
private:
std::unique_ptr<Ui::ConfigureInput> ui;
/// This input is currently awaiting configuration.
/// (i.e.: its corresponding QPushButton has been pressed.)
boost::optional<Settings::NativeInput::Values> current_input_id;
std::unique_ptr<QTimer> timer;
/// Each input is represented by a QPushButton.
std::map<Settings::NativeInput::Values, QPushButton*> button_map;
/// Each input is configured to respond to the press of a Qt::Key.
std::map<Settings::NativeInput::Values, Qt::Key> key_map;
std::map<QPushButton*, Settings::InputDeviceMapping> key_map;
QPushButton* changing_button = nullptr; ///< button currently waiting for key press.
QString previous_mapping;
/// Load configuration settings.
/// Load configuration settings into button text
void loadConfiguration();
/// Check all inputs for duplicate keys. Clears out any other button with the same value as this
/// button's new value.
void removeDuplicates(const Settings::InputDeviceMapping newValue);
/// Handle keykoard key press event for input tab when a button is 'waiting'.
void keyPressEvent(QKeyEvent* event) override;
/// Convert key ASCII value to its' letter/name
static QString getKeyName(Settings::InputDeviceMapping mapping);
/// Set button text to name of key pressed.
void setKey(Settings::InputDeviceMapping keyPressed);
/// Event handler for all button released() event.
void handleClick(QPushButton* sender);
/// Restore all buttons to their default values.
void restoreDefaults();
/// Update UI to reflect current configuration.
void updateButtonLabels();
/// Called when the button corresponding to input_id was pressed.
void handleClick(Settings::NativeInput::Values input_id);
/// Handle key press events.
void keyPressEvent(QKeyEvent* event) override;
/// Configure input input_id to respond to key key_pressed.
void setInput(Settings::NativeInput::Values input_id, Qt::Key key_pressed);
};

View File

@ -0,0 +1,31 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "citra_qt/keybinding_names.h"
const std::array<QString, static_cast<int>(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-")}};

View File

@ -0,0 +1,19 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <QCoreApplication>
#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<QString, static_cast<int>(SDLGamepad::GamepadInputs::MAX)>
sdl_gamepad_names;
};

View File

@ -565,7 +565,6 @@ void GMainWindow::OnConfigure() {
auto result = configureDialog.exec();
if (result == QDialog::Accepted) {
configureDialog.applyConfiguration();
render_window->ReloadSetKeymaps();
config->Save();
}
}

View File

@ -68,7 +68,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) {

View File

@ -86,6 +86,8 @@ 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
};

View File

@ -5,34 +5,11 @@
#include <algorithm>
#include <cmath>
#include "common/assert.h"
#include "core/frontend/emu_window.h"
#include "core/frontend/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<s16>(x * MAX_CIRCLEPAD_POS);
circle_pad_y = static_cast<s16>(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
@ -61,22 +38,25 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
return;
touch_x = VideoCore::kScreenBottomWidth *
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 *
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) {

View File

@ -51,30 +51,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
@ -92,38 +68,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<s16, s16> 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<u16, u16, bool> GetTouchState() const {
return std::make_tuple(touch_x, touch_y, touch_pressed);
}
/**
* Gets the current accelerometer state (acceleration along each three axis).
* Axis explained:
@ -210,12 +154,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() {}
@ -274,17 +212,8 @@ private:
WindowConfig active_config; ///< Internal active configuration
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<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y);
Service::HID::PadState pad_state;
};

View File

@ -3,9 +3,16 @@
// Refer to the license.txt file included.
#include <cmath>
#include "common/logging/log.h"
#include "common/emu_window.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/hid/hid_spvr.h"
#include "core/hle/service/hid/hid_user.h"
#include "core/hle/service/service.h"
#include "input_core/input_core.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/hid/hid.h"
@ -73,11 +80,10 @@ void Update() {
return;
}
PadState state = VideoCore::g_emu_window->GetPadState();
PadState state = InputCore::GetPadState();
// Get current circle pad position and update circle pad direction
s16 circle_pad_x, circle_pad_y;
std::tie(circle_pad_x, circle_pad_y) = VideoCore::g_emu_window->GetCirclePadState();
std::tie(circle_pad_x, circle_pad_y) = InputCore::GetCirclePad();
state.hex |= GetCirclePadDirectionState(circle_pad_x, circle_pad_y).hex;
mem->pad.current_state.hex = state.hex;
@ -114,7 +120,7 @@ void Update() {
TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index];
bool pressed = false;
std::tie(touch_entry.x, touch_entry.y, pressed) = VideoCore::g_emu_window->GetTouchState();
std::tie(touch_entry.x, touch_entry.y, pressed) = InputCore::GetTouchState();
touch_entry.valid.Assign(pressed ? 1 : 0);
// TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which

View File

@ -23,6 +23,7 @@ namespace HID {
* Structure of a Pad controller state.
*/
struct PadState {
PadState() = default;
union {
u32 hex;
@ -53,6 +54,16 @@ struct PadState {
BitField<30, 1, u32> circle_up;
BitField<31, 1, u32> circle_down;
};
bool operator==(const PadState& other) const {
return hex == other.hex;
}
bool operator<(const PadState& other) const {
return hex < other.hex;
}
PadState& operator=(const PadState& other) {
hex = other.hex;
return *this;
}
};
/**

View File

@ -31,9 +31,6 @@
namespace GPU {
Regs g_regs;
/// 268MHz CPU clocks / 60Hz frames per second
const u64 frame_ticks = 268123480ull / 60;
/// Event id for CoreTiming
static int vblank_event;
/// Total number of frames drawn
@ -551,9 +548,6 @@ static void VBlankCallback(u64 userdata, int cycles_late) {
Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC0);
Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC1);
// Check for user input updates
Service::HID::Update();
if (!Settings::values.use_vsync && Settings::values.toggle_framelimit) {
FrameLimiter();
}

View File

@ -317,6 +317,9 @@ static_assert(sizeof(Regs) == 0x1000 * sizeof(u32), "Invalid total size of regis
extern Regs g_regs;
/// 268MHz CPU clocks / 60Hz frames per second
static constexpr u64 frame_ticks = 268123480ull / 60;
template <typename T>
void Read(T& var, const u32 addr);

View File

@ -4,7 +4,7 @@
#include "audio_core/audio_core.h"
#include "core/gdbstub/gdbstub.h"
#include "settings.h"
#include "input_core/input_core.h"
#include "video_core/video_core.h"
#include "core/frontend/emu_window.h"
@ -30,6 +30,8 @@ void Apply() {
AudioCore::SelectSink(values.sink_id);
AudioCore::EnableStretching(values.enable_audio_stretching);
InputCore::ReloadSettings();
}
} // namespace

View File

@ -5,8 +5,12 @@
#pragma once
#include <array>
#include <memory>
#include <string>
#include <tuple>
#include "common/common_types.h"
#include "common/string_util.h"
namespace Settings {
@ -46,35 +50,94 @@ enum Values {
CIRCLE_DOWN,
CIRCLE_LEFT,
CIRCLE_RIGHT,
CIRCLE_MODIFIER,
NUM_INPUTS
};
static const std::array<const char*, NUM_INPUTS> Mapping = {{
// directly mapped keys
static const std::array<const char*, NUM_INPUTS> 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<Values, NUM_INPUTS> All = {{
A, B, X, Y, L, R, ZL, ZR,
"pad_circle_up", "pad_circle_down", "pad_circle_left", "pad_circle_right"}};
static const std::array<Values, NUM_INPUTS> 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,
}};
CDOWN, CLEFT, CRIGHT, CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT}};
}
enum class DeviceFramework { SDL };
enum class Device { Keyboard, Gamepad };
struct InputDeviceMapping {
DeviceFramework framework = DeviceFramework::SDL;
int number = 0;
Device device = Device::Keyboard;
int key = -1;
InputDeviceMapping() = default;
explicit InputDeviceMapping(int key_) {
key = key_;
}
explicit InputDeviceMapping(const std::string& input) {
std::vector<std::string> parts;
Common::SplitString(input, '/', parts);
if (parts.size() != 4)
return;
if (parts[0] == "SDL")
framework = DeviceFramework::SDL;
number = std::stoi(parts[1]);
if (parts[2] == "Keyboard")
device = Device::Keyboard;
else if (parts[2] == "Gamepad")
device = Device::Gamepad;
Common::TryParse(parts[3], &key);
}
bool operator==(const InputDeviceMapping& rhs) const {
return std::tie(device, framework, number) ==
std::tie(rhs.device, rhs.framework, rhs.number);
}
bool operator==(const std::string& rhs) const {
return ToString() == rhs;
}
bool operator<(const InputDeviceMapping& rhs) const {
return ToString() < rhs.ToString();
}
void InitKey() {}
std::string ToString() const {
std::string result;
if (framework == DeviceFramework::SDL)
result = "SDL";
result += "/";
result += std::to_string(this->number);
result += "/";
if (device == Device::Keyboard)
result += "Keyboard";
else if (device == Device::Gamepad)
result += "Gamepad";
result += "/";
result += std::to_string(key);
return result;
}
};
struct Values {
// CheckNew3DS
bool is_new_3ds;
// Controls
std::array<int, NativeInput::NUM_INPUTS> input_mappings;
std::array<InputDeviceMapping, NativeInput::NUM_INPUTS> input_mappings;
InputDeviceMapping pad_circle_modifier;
float pad_circle_modifier_scale;
float pad_circle_deadzone;
// Core
bool use_cpu_jit;
@ -108,7 +171,8 @@ struct Values {
// Debugging
bool use_gdbstub;
u16 gdbstub_port;
} extern values;
};
extern Values values;
void Apply();
}

View File

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

View File

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

View File

@ -0,0 +1,49 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include "core/hle/service/hid/hid.h"
#include "core/settings.h"
#include "input_core/key_map.h"
class IDevice {
public:
virtual ~IDevice();
/**
* Initialize IDevice object with device's index and the map of keys that it will listen to.
* @param number: device number as ordered connected to computer.
* @param keymap: vector of PadStates for device to listen for
* @return true if successful
*/
virtual bool InitDevice(int number, Settings::InputDeviceMapping device_mapping) = 0;
/**
* Process inputs that were pressed since last frame
*/
virtual std::map<Settings::InputDeviceMapping, float> ProcessInput() = 0;
/**
* Close connection to device
* @return true if successful
*/
virtual bool CloseDevice() = 0;
/**
* Gets Input for a single frame. Used only in Qt gui for key binding.
* @return Settings::InputDeviceMapping instance that captures what button was pressed
*/
virtual Settings::InputDeviceMapping GetInput() = 0;
/**
* Clears info from last frame.
*/
virtual void Clear() = 0;
protected:
Settings::InputDeviceMapping input_device_mapping;
};

View File

@ -0,0 +1,169 @@
// 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,
)";
}

View File

@ -0,0 +1,60 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <SDL_keyboard.h>
#include "input_core/devices/keyboard.h"
Keyboard::Keyboard() {}
Keyboard::~Keyboard() {}
bool Keyboard::InitDevice(int number, Settings::InputDeviceMapping device_mapping) {
input_device_mapping = device_mapping;
return true;
}
std::map<Settings::InputDeviceMapping, float> Keyboard::ProcessInput() {
std::map<KeyboardKey, bool> keysPressedCopy;
{
std::lock_guard<std::mutex> lock(m);
keysPressedCopy = keys_pressed;
}
std::map<Settings::InputDeviceMapping, float> button_status;
bool circlePadModPressed = keysPressedCopy[circle_pad_modifier];
for (const auto& key : keysPressedCopy) {
input_device_mapping.key = key.first.key;
button_status.emplace(input_device_mapping, key.second ? 1.0 : 0.0);
}
return button_status;
}
bool Keyboard::CloseDevice() {
return true;
}
void Keyboard::KeyPressed(KeyboardKey key) {
std::lock_guard<std::mutex> lock(m);
keys_pressed[key] = true;
}
void Keyboard::KeyReleased(KeyboardKey key) {
std::lock_guard<std::mutex> lock(m);
keys_pressed[key] = false;
}
void Keyboard::Clear() {
std::lock_guard<std::mutex> lock(m);
keys_pressed.clear();
}
Settings::InputDeviceMapping Keyboard::GetInput() {
auto result = ProcessInput();
for (const auto& entry : result) {
if (entry.second > 0.5)
return entry.first;
}
return Settings::InputDeviceMapping("");
}

View File

@ -0,0 +1,49 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <memory>
#include <mutex>
#include "input_core/devices/device.h"
struct KeyboardKey {
uint32_t key;
std::string character;
KeyboardKey() = default;
KeyboardKey(uint32_t key_, std::string character_)
: key(key_), character(std::move(character_)) {}
bool operator==(const KeyboardKey& other) const {
return key == other.key;
}
bool operator==(uint32_t other) const {
return key == other;
}
bool operator<(const KeyboardKey& other) const {
return key < other.key;
}
};
class Keyboard : public IDevice {
public:
Keyboard();
~Keyboard();
bool InitDevice(int number, Settings::InputDeviceMapping device_mapping) override;
std::map<Settings::InputDeviceMapping, float> ProcessInput() override;
bool CloseDevice() override;
void KeyPressed(KeyboardKey key);
void KeyReleased(KeyboardKey key);
void Clear() override;
Settings::InputDeviceMapping GetInput() override;
private:
std::map<KeyboardKey, bool> keys_pressed;
std::map<int, bool> keys_pressed_last;
std::mutex m; ///< Keys pressed from frontend is on a separate thread.
std::map<int, float> circle_pad_directions; ///< Inverts the strength of key press if it is a
/// circle pad direction that needs it.
KeyboardKey circle_pad_modifier;
};

View File

@ -0,0 +1,122 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cmath>
#include <memory>
#include <SDL.h>
#include <SDL_gamecontroller.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "input_core/devices/gamecontrollerdb.h"
#include "input_core/devices/sdl_gamepad.h"
bool SDLGamepad::SDLInitialized = false;
SDLGamepad::SDLGamepad() {}
SDLGamepad::SDLGamepad(int number_, _SDL_GameController* gamepad_)
: number(number_), gamepad(gamepad_) {}
SDLGamepad::~SDLGamepad() {
CloseDevice();
}
bool SDLGamepad::InitDevice(int number, Settings::InputDeviceMapping device_mapping) {
if (!SDLGamepad::SDLInitialized && SDL_Init(SDL_INIT_GAMECONTROLLER) < 0) {
LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_GAMECONTROLLER) failed");
return false;
}
SDL_GameControllerEventState(SDL_IGNORE);
SDLGamepad::SDLInitialized = true;
LoadGameControllerDB();
if (SDL_IsGameController(number)) {
gamepad = SDL_GameControllerOpen(number);
if (gamepad == nullptr) {
return false;
}
}
input_device_mapping = device_mapping;
return true;
}
std::map<Settings::InputDeviceMapping, float> SDLGamepad::ProcessInput() {
std::map<Settings::InputDeviceMapping, float> button_status;
if (gamepad == nullptr)
return button_status;
SDL_GameControllerUpdate();
for (int i = 0; i < SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_MAX; i++) {
SDL_GameControllerButton button = static_cast<SDL_GameControllerButton>(i);
Uint8 pressed = SDL_GameControllerGetButton(gamepad, button);
input_device_mapping.key = static_cast<int>(
gamepadinput_to_sdlname_mapping2[SDL_GameControllerGetStringForButton(button)]);
button_status.emplace(input_device_mapping, pressed);
}
for (int i = 0; i < SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_MAX; i++) {
SDL_GameControllerAxis axis = static_cast<SDL_GameControllerAxis>(i);
float strength = fmaxf(-1, (float)SDL_GameControllerGetAxis(gamepad, axis) / 32767.0);
input_device_mapping.key = static_cast<int>(
gamepadinput_to_sdlname_mapping2[SDL_GameControllerGetStringForAxis(axis)]);
if (strength < 0) {
button_status.emplace(input_device_mapping, 0);
input_device_mapping.key += 1; // minus axis value is always one greater
button_status.emplace(input_device_mapping, abs(strength));
} else if (strength >= 0 && i < 4) {
button_status.emplace(input_device_mapping, abs(strength));
input_device_mapping.key += 1;
button_status.emplace(input_device_mapping, 0);
} else { // Only trigger buttons
button_status.emplace(input_device_mapping, abs(strength));
}
}
return button_status;
}
bool SDLGamepad::CloseDevice() {
if (gamepad != nullptr) {
SDL_GameControllerClose(gamepad);
}
return true;
}
std::vector<std::shared_ptr<IDevice>> SDLGamepad::GetAllDevices() {
std::vector<std::shared_ptr<IDevice>> devices;
for (int i = 0; i < 8; i++) {
auto gamepad = std::make_shared<SDLGamepad>();
bool success = gamepad->InitDevice(
i, Settings::InputDeviceMapping("SDL/" + std::to_string(i) + "/Gamepad/-1"));
if (success)
devices.push_back(gamepad);
}
return devices;
}
void SDLGamepad::LoadGameControllerDB() {
std::vector<std::string> lines1, lines2, lines3, lines4;
Common::SplitString(SDLGameControllerDB::db_file1, '\n', lines1);
Common::SplitString(SDLGameControllerDB::db_file2, '\n', lines2);
Common::SplitString(SDLGameControllerDB::db_file3, '\n', lines3);
Common::SplitString(SDLGameControllerDB::db_file4, '\n', lines4);
lines1.insert(lines1.end(), lines2.begin(), lines2.end());
lines1.insert(lines1.end(), lines3.begin(), lines3.end());
lines1.insert(lines1.end(), lines4.begin(), lines4.end());
for (std::string s : lines1) {
SDL_GameControllerAddMapping(s.c_str());
}
}
Settings::InputDeviceMapping SDLGamepad::GetInput() {
if (gamepad == nullptr)
return Settings::InputDeviceMapping("");
auto results = ProcessInput();
for (auto& input : results) {
if (input.second > 0.5)
return input.first;
}
return Settings::InputDeviceMapping("");
}
void SDLGamepad::Clear() {}

View File

@ -0,0 +1,113 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "input_core/devices/device.h"
struct _SDL_GameController;
class SDLGamepad : public IDevice {
public:
SDLGamepad();
SDLGamepad(int number_, _SDL_GameController* gamepad_);
~SDLGamepad();
bool InitDevice(int number, Settings::InputDeviceMapping device_mapping) override;
std::map<Settings::InputDeviceMapping, float> 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<std::shared_ptr<IDevice>> 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<GamepadInputs, std::string> gamepadinput_to_sdlname_mapping = {
{GamepadInputs::ButtonA, "a"},
{GamepadInputs::ButtonB, "b"},
{GamepadInputs::ButtonX, "x"},
{GamepadInputs::ButtonY, "y"},
{GamepadInputs::LeftShoulder, "leftshoulder"},
{GamepadInputs::RightShoulder, "rightshoulder"},
{GamepadInputs::Start, "start"},
{GamepadInputs::Back, "back"},
{GamepadInputs::DPadUp, "dpup"},
{GamepadInputs::DpadDown, "dpdown"},
{GamepadInputs::DpadLeft, "dpleft"},
{GamepadInputs::DpadRight, "dpright"},
{GamepadInputs::L3, "leftstick"},
{GamepadInputs::R3, "rightstick"},
{GamepadInputs::LeftTrigger, "lefttrigger"},
{GamepadInputs::RightTrigger, "righttrigger"},
{GamepadInputs::LeftYPlus, "lefty"},
{GamepadInputs::LeftYMinus, "lefty"},
{GamepadInputs::LeftXPlus, "leftx"},
{GamepadInputs::LeftXMinus, "leftx"},
{GamepadInputs::RightYPlus, "righty"},
{GamepadInputs::RightYMinus, "righty"},
{GamepadInputs::RightXPlus, "rightx"},
{GamepadInputs::RightXMinus, "rightx"},
};
std::map<std::string, GamepadInputs> gamepadinput_to_sdlname_mapping2 = {
{"a", GamepadInputs::ButtonA},
{"b", GamepadInputs::ButtonB},
{"x", GamepadInputs::ButtonX},
{"y", GamepadInputs::ButtonY},
{"leftshoulder", GamepadInputs::LeftShoulder},
{"rightshoulder", GamepadInputs::RightShoulder},
{"start", GamepadInputs::Start},
{"back", GamepadInputs::Back},
{"dpup", GamepadInputs::DPadUp},
{"dpdown", GamepadInputs::DpadDown},
{"dpleft", GamepadInputs::DpadLeft},
{"dpright", GamepadInputs::DpadRight},
{"leftstick", GamepadInputs::L3},
{"rightstick", GamepadInputs::R3},
{"lefttrigger", GamepadInputs::LeftTrigger},
{"righttrigger", GamepadInputs::RightTrigger},
{"lefty", GamepadInputs::LeftYPlus},
{"lefty2", GamepadInputs::LeftYMinus},
{"leftx", GamepadInputs::LeftXPlus},
{"leftx2", GamepadInputs::LeftXMinus},
{"righty", GamepadInputs::RightYPlus},
{"righty2", GamepadInputs::RightYMinus},
{"rightx", GamepadInputs::RightXPlus},
{"rightx2", GamepadInputs::RightXMinus}};
static bool SDLInitialized;
std::map<std::string, bool>
keys_pressed; ///< Map of keys that were pressed on previous iteration
_SDL_GameController* gamepad = nullptr;
int number; ///< Index of gamepad connection
static void LoadGameControllerDB();
};

View File

@ -0,0 +1,277 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <chrono>
#include <cmath>
#include <map>
#include "core/core_timing.h"
#include "core/hw/gpu.h"
#include "input_core/devices/keyboard.h"
#include "input_core/devices/sdl_gamepad.h"
#include "input_core/input_core.h"
int InputCore::tick_event;
Service::HID::PadState InputCore::pad_state;
std::tuple<s16, s16> InputCore::circle_pad;
std::shared_ptr<Keyboard> InputCore::main_keyboard; ///< Keyboard is always active for Citra
std::vector<std::shared_ptr<IDevice>>
InputCore::devices; ///< Devices that are handling input for the game
std::map<Settings::InputDeviceMapping, std::vector<Service::HID::PadState>> InputCore::key_mappings;
std::map<Service::HID::PadState, bool>
InputCore::keys_pressed; ///< keys that were pressed on previous frame.
std::mutex InputCore::pad_state_mutex;
std::mutex InputCore::touch_mutex;
u16 InputCore::touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
u16 InputCore::touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
bool InputCore::touch_pressed; ///< True if touchpad area is currently pressed, otherwise false
const float default_deadzone =
0.5; ///< Applies to analog controls being used for digital 3ds inputs.
void InputCore::Init() {
ParseSettings();
tick_event = CoreTiming::RegisterEvent("InputCore::tick_event", InputTickCallback);
CoreTiming::ScheduleEvent(GPU::frame_ticks, tick_event);
}
void InputCore::Shutdown() {
CoreTiming::UnscheduleEvent(tick_event, 0);
devices.clear();
}
void InputCore::InputTickCallback(u64, int cycles_late) {
std::vector<std::map<Settings::InputDeviceMapping, float>> inputs;
for (auto& device : devices) {
inputs.push_back(device->ProcessInput());
}
UpdateEmulatorInputs(inputs);
Service::HID::Update();
// Reschedule recurrent event
CoreTiming::ScheduleEvent(GPU::frame_ticks - cycles_late, tick_event);
}
void InputCore::UpdateEmulatorInputs(
std::vector<std::map<Settings::InputDeviceMapping, float>> inputs) {
std::lock_guard<std::mutex> lock(pad_state_mutex);
// Apply deadzone
float leftx = 0, lefty = 0;
float circle_pad_modifier = 1.0;
auto circle_pad_modifier_mapping = Settings::values.pad_circle_modifier;
for (auto& input_device : inputs) {
for (auto& button_states : input_device) { // Loop through all buttons from input device
float strength = button_states.second;
auto emulator_inputs = key_mappings[button_states.first];
for (auto& emulator_input : emulator_inputs) {
if (emulator_input == Service::HID::PAD_CIRCLE_UP && abs(strength) > 0) {
lefty = -strength;
} else if (emulator_input == Service::HID::PAD_CIRCLE_DOWN && abs(strength) > 0) {
lefty = strength;
} else if (emulator_input == Service::HID::PAD_CIRCLE_LEFT && abs(strength) > 0) {
leftx = -strength;
} else if (emulator_input == Service::HID::PAD_CIRCLE_RIGHT && abs(strength) > 0) {
leftx = strength;
}
}
if (button_states.first == circle_pad_modifier_mapping)
circle_pad_modifier = (button_states.second > default_deadzone)
? Settings::values.pad_circle_modifier_scale
: 1.0;
}
}
float deadzone = Settings::values.pad_circle_deadzone;
std::tuple<float, float> left_stick = ApplyDeadzone(leftx, lefty, deadzone);
for (auto& input_device : inputs) {
for (auto& button_states : input_device) { // Loop through all buttons from input device
float strength = button_states.second;
auto emulator_inputs = key_mappings[button_states.first];
for (auto& emulator_input : emulator_inputs) {
if (std::find(std::begin(KeyMap::analog_inputs), std::end(KeyMap::analog_inputs),
emulator_input) ==
std::end(KeyMap::analog_inputs)) { // digital button
if (abs(strength) < default_deadzone && keys_pressed[emulator_input] == true) {
pad_state.hex &= ~emulator_input.hex;
keys_pressed[emulator_input] = false;
} else if (abs(strength) >= default_deadzone &&
keys_pressed[emulator_input] == false) {
pad_state.hex |= emulator_input.hex;
keys_pressed[emulator_input] = true;
}
} else { // analog stick
if (emulator_input == Service::HID::PAD_CIRCLE_UP ||
emulator_input == Service::HID::PAD_CIRCLE_DOWN) {
std::get<1>(circle_pad) = std::get<1>(left_stick) *
KeyMap::MAX_CIRCLEPAD_POS * -1 *
circle_pad_modifier;
} else if (emulator_input == Service::HID::PAD_CIRCLE_LEFT ||
emulator_input == Service::HID::PAD_CIRCLE_RIGHT) {
std::get<0>(circle_pad) = std::get<0>(left_stick) *
KeyMap::MAX_CIRCLEPAD_POS * circle_pad_modifier;
}
}
}
}
}
}
Service::HID::PadState InputCore::GetPadState() {
std::lock_guard<std::mutex> lock(pad_state_mutex);
return pad_state;
}
void InputCore::SetPadState(const Service::HID::PadState& state) {
std::lock_guard<std::mutex> lock(pad_state_mutex);
pad_state.hex = state.hex;
}
std::tuple<s16, s16> InputCore::GetCirclePad() {
return circle_pad;
}
void InputCore::SetCirclePad(std::tuple<s16, s16> pad) {
circle_pad = pad;
}
std::shared_ptr<Keyboard> InputCore::GetKeyboard() {
if (main_keyboard == nullptr)
main_keyboard = std::make_shared<Keyboard>();
return main_keyboard;
}
std::tuple<u16, u16, bool> InputCore::GetTouchState() {
std::lock_guard<std::mutex> lock(touch_mutex);
return std::make_tuple(touch_x, touch_y, touch_pressed);
}
void InputCore::SetTouchState(std::tuple<u16, u16, bool> value) {
std::lock_guard<std::mutex> lock(touch_mutex);
std::tie(touch_x, touch_y, touch_pressed) = value;
}
/// Helper method to check if device was already initialized
bool InputCore::CheckIfMappingExists(const std::vector<Settings::InputDeviceMapping>& 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
std::vector<Settings::InputDeviceMapping> InputCore::GatherUniqueMappings() {
std::vector<Settings::InputDeviceMapping> uniqueMappings;
for (const auto& mapping : Settings::values.input_mappings) {
if (!CheckIfMappingExists(uniqueMappings, mapping)) {
uniqueMappings.push_back(mapping);
}
}
if (!CheckIfMappingExists(uniqueMappings, Settings::values.pad_circle_modifier)) {
uniqueMappings.push_back(Settings::values.pad_circle_modifier);
}
return uniqueMappings;
}
/// Builds map of input keys to 3ds buttons for unique device
void InputCore::BuildKeyMapping() {
key_mappings.clear();
for (size_t i = 0; i < Settings::values.input_mappings.size(); i++) {
auto val = KeyMap::mapping_targets[i];
auto key = Settings::values.input_mappings[i];
key_mappings.emplace(key, std::vector<Service::HID::PadState>());
key_mappings[key].push_back(val);
}
}
/// Generate a device for each unique mapping
void InputCore::GenerateUniqueDevices() {
auto uniqueMappings = GatherUniqueMappings();
devices.clear();
std::shared_ptr<IDevice> input;
for (const auto& mapping : uniqueMappings) {
switch (mapping.framework) {
case Settings::DeviceFramework::SDL: {
if (mapping.device == Settings::Device::Keyboard) {
main_keyboard = std::make_shared<Keyboard>();
input = main_keyboard;
break;
} else if (mapping.device == Settings::Device::Gamepad) {
input = std::make_shared<SDLGamepad>();
break;
}
}
}
devices.push_back(input);
input->InitDevice(mapping.number, mapping);
}
if (main_keyboard == nullptr) {
main_keyboard = std::make_shared<Keyboard>();
main_keyboard->InitDevice(0, Settings::InputDeviceMapping("SDL/0/Keyboard/-1"));
devices.push_back(main_keyboard);
}
}
/// Read settings to initialize devices
void InputCore::ParseSettings() {
GenerateUniqueDevices();
BuildKeyMapping();
}
std::tuple<float, float> InputCore::ApplyDeadzone(float x, float y, float dead_zone) {
float magnitude = std::sqrt((x * x) + (y * y));
if (magnitude < dead_zone) {
x = 0;
y = 0;
} else {
float normalized_x = x / magnitude;
float normalized_y = y / magnitude;
x = normalized_x * ((magnitude - dead_zone) / (1 - dead_zone));
y = normalized_y * ((magnitude - dead_zone) / (1 - dead_zone));
}
return std::tuple<float, float>(x, y);
}
void InputCore::ReloadSettings() {
if (devices.empty())
return;
std::lock_guard<std::mutex> lock(pad_state_mutex);
devices.clear();
ParseSettings();
}
/// Returns all available input devices. Used for key binding in GUI
std::vector<std::shared_ptr<IDevice>> InputCore::GetAllDevices() {
auto all_devices = SDLGamepad::GetAllDevices();
auto keyboard = InputCore::GetKeyboard();
keyboard->InitDevice(0, Settings::InputDeviceMapping("SDL/0/Keyboard/-1"));
all_devices.push_back(keyboard);
return all_devices;
}
Settings::InputDeviceMapping InputCore::DetectInput(int max_time,
std::function<void(void)> update_GUI) {
auto devices = GetAllDevices();
for (auto& device : devices) {
device->Clear();
}
Settings::InputDeviceMapping input_device;
auto start = std::chrono::high_resolution_clock::now();
while (input_device.key == -1) {
update_GUI();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start)
.count();
if (duration >= max_time) {
break;
}
for (auto& device : devices) {
input_device = device->GetInput();
if (input_device.key != -1)
break;
}
};
return input_device;
}

115
src/input_core/input_core.h Normal file
View File

@ -0,0 +1,115 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include <memory>
#include <mutex>
#include <tuple>
#include "core/hle/service/hid/hid.h"
#include "core/settings.h"
#include "input_core/devices/device.h"
class Keyboard;
class InputCore {
public:
static void Init();
static void Shutdown();
/**
* Threadsafe getter to the current PadState
* @return Service::HID::PadState instance
*/
static Service::HID::PadState GetPadState();
/**
* Threadsafe setter for the current PadState
* @param state New PadState to overwrite current PadState.
*/
static void SetPadState(const Service::HID::PadState& state);
/**
* Getter for current CirclePad
* @return std::tuple<s16, s16> CirclePad state
*/
static std::tuple<s16, s16> GetCirclePad();
/**
* Setter for current CirclePad
* @param circle New CirclePad state
*/
static void SetCirclePad(std::tuple<s16, s16> circle);
/**
* Getter for Citra's main keyboard input handler
* @return std::shared_ptr<Keyboard> Device Keyboard instance
*/
static std::shared_ptr<Keyboard> GetKeyboard();
/**
* Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
* Threadsafe.
* @note This should be called by the core emu thread to get a state set by the window thread.
* @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and
* `pressed` is true if the touch screen is currently being pressed
*/
static std::tuple<u16, u16, bool> GetTouchState();
/**
* Threadsafe setter for the current touch screen state.
* @param value New Touch State
*/
static void SetTouchState(std::tuple<u16, u16, bool> value);
/**
* Reload input key mapping settings during game-play
*/
static void ReloadSettings();
static std::vector<std::shared_ptr<IDevice>> GetAllDevices();
/**
* Loops through all devices and detects the first device that produces an input
* @param max_time: maximum amount of time to wait until input detected, in milliseconds.
* @param update_GUI: function to run in while loop to process any gui events.
* @return Settings::InputDeviceMapping of input device
*/
static Settings::InputDeviceMapping DetectInput(int max_time,
std::function<void(void)> update_GUI);
private:
static int tick_event;
static Service::HID::PadState pad_state;
static std::tuple<s16, s16> circle_pad;
static std::shared_ptr<Keyboard> main_keyboard; ///< Keyboard is always active for Citra
static std::vector<std::shared_ptr<IDevice>>
devices; ///< Devices that are handling input for the game
static std::map<Settings::InputDeviceMapping, std::vector<Service::HID::PadState>> key_mappings;
static std::map<Service::HID::PadState, bool>
keys_pressed; ///< keys that were pressed on previous frame.
static std::mutex pad_state_mutex;
static std::mutex touch_mutex;
static u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
static u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
static bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false
static void UpdateEmulatorInputs(
std::vector<std::map<Settings::InputDeviceMapping, float>> inputs);
static void InputTickCallback(u64, int cycles_late);
static bool CheckIfMappingExists(const std::vector<Settings::InputDeviceMapping>& uniqueMapping,
Settings::InputDeviceMapping mappingToCheck);
static std::vector<Settings::InputDeviceMapping> GatherUniqueMappings();
static void BuildKeyMapping();
static void GenerateUniqueDevices();
static void ParseSettings();
static std::tuple<float, float> ApplyDeadzone(float x, float y, float dead_zone);
};

View File

@ -0,0 +1,27 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "input_core/key_map.h"
namespace KeyMap {
const std::array<Service::HID::PadState, Settings::NativeInput::NUM_INPUTS> 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<Service::HID::PadState, 4> analog_inputs = {
Service::HID::PAD_CIRCLE_UP, Service::HID::PAD_CIRCLE_DOWN, Service::HID::PAD_CIRCLE_LEFT,
Service::HID::PAD_CIRCLE_RIGHT};
}

15
src/input_core/key_map.h Normal file
View File

@ -0,0 +1,15 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "core/hle/service/hid/hid.h"
namespace KeyMap {
extern const std::array<Service::HID::PadState, Settings::NativeInput::NUM_INPUTS> mapping_targets;
extern const std::array<Service::HID::PadState, 4> analog_inputs;
constexpr int MAX_CIRCLEPAD_POS = 0x9C; /// Max value for a circle pad position
}

View File

@ -12,7 +12,7 @@ create_directory_groups(${SRCS} ${HEADERS})
include_directories(../../externals/catch/single_include/)
add_executable(tests ${SRCS} ${HEADERS})
target_link_libraries(tests core video_core audio_core common)
target_link_libraries(tests core video_core audio_core input_core common)
target_link_libraries(tests ${PLATFORM_LIBRARIES} Threads::Threads)
add_test(NAME tests COMMAND $<TARGET_FILE:tests>)