mirror of
https://github.com/starr-dusT/citra.git
synced 2024-10-02 10:26:17 -07:00
![Vitor K](/assets/img/avatar_default.png)
* Implement the basics of controller auto mapping. From testing doesn't currenlty work. Opening the controller requires the device index, but it is only known and guaranteed at boot time or when a controller is connected. * Use the SDL_INIT_GAMECONTROLLER flag to initialize the controller subsystem. It automatically initializes the joystick subsystem too, so SDL_INIT_JOYSTICK is not needed. * Implement the SDLGameController class to handle open game controllers. Based on the SDLJoystick implementation. * Address review comments * Changes SDLJoystick and SDLGameController to use a custom default constructible destructor, to improve readability. The only deleters used previously were SDL_JoystickClose and SDL_GameControllerClose, respectively, plus null lambdas. Given that both SDL functions accept null pointers with just an early return, this should be functionally the same. with just an early return * warn the user when a controller mapping is not found * Get axis direction and threshold from SDL_ExtendedGameControllerBind * Reject analog bind if it's not axis, for the couple of examples present in SDL2.0.10's db. Also add SDL_CONTROLLER_BINDTYPE_NONE for the button bind switch, with a better log message. * sdl_impl.cpp: Log the error returned by SDL_GetError upon failure to open joystick * sdl: only use extended binding on SDL2.0.6 and up * sdl_impl.cpp: minor changes
618 lines
24 KiB
C++
618 lines
24 KiB
C++
// Copyright 2016 Citra Emulator Project
|
|
// Licensed under GPLv2 or any later version
|
|
// Refer to the license.txt file included.
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <utility>
|
|
#include <QInputDialog>
|
|
#include <QKeyEvent>
|
|
#include <QLabel>
|
|
#include <QMenu>
|
|
#include <QMessageBox>
|
|
#include <QSlider>
|
|
#include <QTimer>
|
|
#include "citra_qt/configuration/config.h"
|
|
#include "citra_qt/configuration/configure_input.h"
|
|
#include "citra_qt/configuration/configure_motion_touch.h"
|
|
#include "common/param_package.h"
|
|
#include "ui_configure_input.h"
|
|
|
|
const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM>
|
|
ConfigureInput::analog_sub_buttons{{
|
|
"up",
|
|
"down",
|
|
"left",
|
|
"right",
|
|
"modifier",
|
|
}};
|
|
|
|
static QString GetKeyName(int 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:
|
|
return QString{};
|
|
default:
|
|
return QKeySequence(key_code).toString();
|
|
}
|
|
}
|
|
|
|
static void SetAnalogButton(const Common::ParamPackage& input_param,
|
|
Common::ParamPackage& analog_param, const std::string& button_name) {
|
|
if (analog_param.Get("engine", "") != "analog_from_button") {
|
|
analog_param = {
|
|
{"engine", "analog_from_button"},
|
|
};
|
|
}
|
|
analog_param.Set(button_name, input_param.Serialize());
|
|
}
|
|
|
|
static QString ButtonToText(const Common::ParamPackage& param) {
|
|
if (!param.Has("engine")) {
|
|
return QObject::tr("[not set]");
|
|
}
|
|
|
|
if (param.Get("engine", "") == "keyboard") {
|
|
return GetKeyName(param.Get("code", 0));
|
|
}
|
|
|
|
if (param.Get("engine", "") == "sdl") {
|
|
if (param.Has("hat")) {
|
|
const QString hat_str = QString::fromStdString(param.Get("hat", ""));
|
|
const QString direction_str = QString::fromStdString(param.Get("direction", ""));
|
|
|
|
return QObject::tr("Hat %1 %2").arg(hat_str, direction_str);
|
|
}
|
|
|
|
if (param.Has("axis")) {
|
|
const QString axis_str = QString::fromStdString(param.Get("axis", ""));
|
|
const QString direction_str = QString::fromStdString(param.Get("direction", ""));
|
|
|
|
return QObject::tr("Axis %1%2").arg(axis_str, direction_str);
|
|
}
|
|
|
|
if (param.Has("button")) {
|
|
const QString button_str = QString::fromStdString(param.Get("button", ""));
|
|
|
|
return QObject::tr("Button %1").arg(button_str);
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
return QObject::tr("[unknown]");
|
|
}
|
|
|
|
static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
|
|
if (!param.Has("engine")) {
|
|
return QObject::tr("[not set]");
|
|
}
|
|
|
|
if (param.Get("engine", "") == "analog_from_button") {
|
|
return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
|
|
}
|
|
|
|
if (param.Get("engine", "") == "sdl") {
|
|
if (dir == "modifier") {
|
|
return QObject::tr("[unused]");
|
|
}
|
|
|
|
if (dir == "left" || dir == "right") {
|
|
const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
|
|
|
|
return QObject::tr("Axis %1").arg(axis_x_str);
|
|
}
|
|
|
|
if (dir == "up" || dir == "down") {
|
|
const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
|
|
|
|
return QObject::tr("Axis %1").arg(axis_y_str);
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
return QObject::tr("[unknown]");
|
|
}
|
|
|
|
ConfigureInput::ConfigureInput(QWidget* parent)
|
|
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
|
|
timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
|
|
ui->setupUi(this);
|
|
setFocusPolicy(Qt::ClickFocus);
|
|
|
|
for (const auto& profile : Settings::values.input_profiles) {
|
|
ui->profile->addItem(QString::fromStdString(profile.name));
|
|
}
|
|
|
|
ui->profile->setCurrentIndex(Settings::values.current_input_profile_index);
|
|
|
|
button_map = {
|
|
ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
|
|
ui->buttonDpadUp, ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight,
|
|
ui->buttonL, ui->buttonR, ui->buttonStart, ui->buttonSelect,
|
|
ui->buttonDebug, ui->buttonGpio14, ui->buttonZL, ui->buttonZR,
|
|
ui->buttonHome,
|
|
};
|
|
|
|
analog_map_buttons = {{
|
|
{
|
|
ui->buttonCircleUp,
|
|
ui->buttonCircleDown,
|
|
ui->buttonCircleLeft,
|
|
ui->buttonCircleRight,
|
|
ui->buttonCircleMod,
|
|
},
|
|
{
|
|
ui->buttonCStickUp,
|
|
ui->buttonCStickDown,
|
|
ui->buttonCStickLeft,
|
|
ui->buttonCStickRight,
|
|
nullptr,
|
|
},
|
|
}};
|
|
|
|
analog_map_stick = {ui->buttonCircleAnalog, ui->buttonCStickAnalog};
|
|
analog_map_deadzone_and_modifier_slider = {ui->sliderCirclePadDeadzoneAndModifier,
|
|
ui->sliderCStickDeadzoneAndModifier};
|
|
analog_map_deadzone_and_modifier_slider_label = {ui->labelCirclePadDeadzoneAndModifier,
|
|
ui->labelCStickDeadzoneAndModifier};
|
|
|
|
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
|
|
if (!button_map[button_id])
|
|
continue;
|
|
button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(button_map[button_id], &QPushButton::clicked, [=]() {
|
|
HandleClick(
|
|
button_map[button_id],
|
|
[=](const Common::ParamPackage& params) {
|
|
buttons_param[button_id] = params;
|
|
// If the user closes the dialog, the changes are reverted in
|
|
// `GMainWindow::OnConfigure()`
|
|
ApplyConfiguration();
|
|
Settings::SaveProfile(ui->profile->currentIndex());
|
|
},
|
|
InputCommon::Polling::DeviceType::Button);
|
|
});
|
|
connect(button_map[button_id], &QPushButton::customContextMenuRequested,
|
|
[=](const QPoint& menu_location) {
|
|
QMenu context_menu;
|
|
context_menu.addAction(tr("Clear"), [&] {
|
|
buttons_param[button_id].Clear();
|
|
button_map[button_id]->setText(tr("[not set]"));
|
|
ApplyConfiguration();
|
|
Settings::SaveProfile(ui->profile->currentIndex());
|
|
});
|
|
context_menu.addAction(tr("Restore Default"), [&] {
|
|
buttons_param[button_id] = Common::ParamPackage{
|
|
InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
|
|
button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
|
|
ApplyConfiguration();
|
|
Settings::SaveProfile(ui->profile->currentIndex());
|
|
});
|
|
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
|
|
});
|
|
}
|
|
|
|
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
|
|
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
|
|
if (!analog_map_buttons[analog_id][sub_button_id])
|
|
continue;
|
|
analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
|
|
Qt::CustomContextMenu);
|
|
connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::clicked, [=]() {
|
|
HandleClick(
|
|
analog_map_buttons[analog_id][sub_button_id],
|
|
[=](const Common::ParamPackage& params) {
|
|
SetAnalogButton(params, analogs_param[analog_id],
|
|
analog_sub_buttons[sub_button_id]);
|
|
ApplyConfiguration();
|
|
Settings::SaveProfile(ui->profile->currentIndex());
|
|
},
|
|
InputCommon::Polling::DeviceType::Button);
|
|
});
|
|
connect(analog_map_buttons[analog_id][sub_button_id],
|
|
&QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
|
|
QMenu context_menu;
|
|
context_menu.addAction(tr("Clear"), [&] {
|
|
analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
|
|
analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
|
|
ApplyConfiguration();
|
|
Settings::SaveProfile(ui->profile->currentIndex());
|
|
});
|
|
context_menu.addAction(tr("Restore Default"), [&] {
|
|
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
|
|
Config::default_analogs[analog_id][sub_button_id])};
|
|
SetAnalogButton(params, analogs_param[analog_id],
|
|
analog_sub_buttons[sub_button_id]);
|
|
analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
|
|
analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
|
|
ApplyConfiguration();
|
|
Settings::SaveProfile(ui->profile->currentIndex());
|
|
});
|
|
context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
|
|
menu_location));
|
|
});
|
|
}
|
|
connect(analog_map_stick[analog_id], &QPushButton::clicked, [=]() {
|
|
if (QMessageBox::information(
|
|
this, tr("Information"),
|
|
tr("After pressing OK, first move your joystick horizontally, "
|
|
"and then vertically."),
|
|
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
|
|
HandleClick(
|
|
analog_map_stick[analog_id],
|
|
[=](const Common::ParamPackage& params) {
|
|
analogs_param[analog_id] = params;
|
|
ApplyConfiguration();
|
|
Settings::SaveProfile(ui->profile->currentIndex());
|
|
},
|
|
InputCommon::Polling::DeviceType::Analog);
|
|
}
|
|
});
|
|
connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] {
|
|
const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value();
|
|
if (analogs_param[analog_id].Get("engine", "") == "sdl") {
|
|
analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
|
|
tr("Deadzone: %1%").arg(slider_value));
|
|
analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
|
|
} else {
|
|
analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
|
|
tr("Modifier Scale: %1%").arg(slider_value));
|
|
analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
|
|
}
|
|
});
|
|
}
|
|
|
|
connect(ui->buttonMotionTouch, &QPushButton::clicked, [this] {
|
|
QDialog* motion_touch_dialog = new ConfigureMotionTouch(this);
|
|
return motion_touch_dialog->exec();
|
|
});
|
|
|
|
ui->buttonDelete->setEnabled(ui->profile->count() > 1);
|
|
|
|
connect(ui->buttonAutoMap, &QPushButton::clicked, this, &ConfigureInput::AutoMap);
|
|
connect(ui->buttonClearAll, &QPushButton::clicked, this, &ConfigureInput::ClearAll);
|
|
connect(ui->buttonRestoreDefaults, &QPushButton::clicked, this,
|
|
&ConfigureInput::RestoreDefaults);
|
|
connect(ui->buttonNew, &QPushButton::clicked, this, &ConfigureInput::NewProfile);
|
|
connect(ui->buttonDelete, &QPushButton::clicked, this, &ConfigureInput::DeleteProfile);
|
|
connect(ui->buttonRename, &QPushButton::clicked, this, &ConfigureInput::RenameProfile);
|
|
|
|
connect(ui->profile, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
|
[this](int i) {
|
|
ApplyConfiguration();
|
|
Settings::SaveProfile(Settings::values.current_input_profile_index);
|
|
Settings::LoadProfile(i);
|
|
LoadConfiguration();
|
|
});
|
|
|
|
timeout_timer->setSingleShot(true);
|
|
connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); });
|
|
|
|
connect(poll_timer.get(), &QTimer::timeout, [this]() {
|
|
Common::ParamPackage params;
|
|
for (auto& poller : device_pollers) {
|
|
params = poller->GetNextInput();
|
|
if (params.Has("engine")) {
|
|
SetPollingResult(params, false);
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
|
|
LoadConfiguration();
|
|
|
|
// TODO(wwylele): enable this when we actually emulate it
|
|
ui->buttonHome->setEnabled(false);
|
|
}
|
|
|
|
ConfigureInput::~ConfigureInput() = default;
|
|
|
|
void ConfigureInput::ApplyConfiguration() {
|
|
std::transform(buttons_param.begin(), buttons_param.end(),
|
|
Settings::values.current_input_profile.buttons.begin(),
|
|
[](const Common::ParamPackage& param) { return param.Serialize(); });
|
|
std::transform(analogs_param.begin(), analogs_param.end(),
|
|
Settings::values.current_input_profile.analogs.begin(),
|
|
[](const Common::ParamPackage& param) { return param.Serialize(); });
|
|
}
|
|
|
|
void ConfigureInput::ApplyProfile() {
|
|
Settings::values.current_input_profile_index = ui->profile->currentIndex();
|
|
}
|
|
|
|
void ConfigureInput::EmitInputKeysChanged() {
|
|
emit InputKeysChanged(GetUsedKeyboardKeys());
|
|
}
|
|
|
|
void ConfigureInput::OnHotkeysChanged(QList<QKeySequence> new_key_list) {
|
|
hotkey_list = new_key_list;
|
|
}
|
|
|
|
QList<QKeySequence> ConfigureInput::GetUsedKeyboardKeys() {
|
|
QList<QKeySequence> list;
|
|
for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
|
|
// TODO(adityaruplaha): Add home button to list when we finally emulate it
|
|
if (button == Settings::NativeButton::Home) {
|
|
continue;
|
|
}
|
|
|
|
auto button_param = buttons_param[button];
|
|
if (button_param.Get("engine", "") == "keyboard") {
|
|
list << QKeySequence(button_param.Get("code", 0));
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
void ConfigureInput::LoadConfiguration() {
|
|
std::transform(Settings::values.current_input_profile.buttons.begin(),
|
|
Settings::values.current_input_profile.buttons.end(), buttons_param.begin(),
|
|
[](const std::string& str) { return Common::ParamPackage(str); });
|
|
std::transform(Settings::values.current_input_profile.analogs.begin(),
|
|
Settings::values.current_input_profile.analogs.end(), analogs_param.begin(),
|
|
[](const std::string& str) { return Common::ParamPackage(str); });
|
|
UpdateButtonLabels();
|
|
}
|
|
|
|
void ConfigureInput::RestoreDefaults() {
|
|
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
|
|
buttons_param[button_id] = Common::ParamPackage{
|
|
InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
|
|
}
|
|
|
|
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
|
|
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
|
|
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
|
|
Config::default_analogs[analog_id][sub_button_id])};
|
|
SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
|
|
}
|
|
}
|
|
UpdateButtonLabels();
|
|
|
|
ApplyConfiguration();
|
|
Settings::SaveProfile(Settings::values.current_input_profile_index);
|
|
}
|
|
|
|
void ConfigureInput::ClearAll() {
|
|
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
|
|
if (button_map[button_id] && button_map[button_id]->isEnabled())
|
|
buttons_param[button_id].Clear();
|
|
}
|
|
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
|
|
analogs_param[analog_id].Clear();
|
|
}
|
|
UpdateButtonLabels();
|
|
|
|
ApplyConfiguration();
|
|
Settings::SaveProfile(Settings::values.current_input_profile_index);
|
|
}
|
|
|
|
void ConfigureInput::UpdateButtonLabels() {
|
|
for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
|
|
if (button_map[button])
|
|
button_map[button]->setText(ButtonToText(buttons_param[button]));
|
|
}
|
|
|
|
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
|
|
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
|
|
if (analog_map_buttons[analog_id][sub_button_id]) {
|
|
analog_map_buttons[analog_id][sub_button_id]->setText(
|
|
AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
|
|
}
|
|
}
|
|
analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
|
|
|
|
auto& param = analogs_param[analog_id];
|
|
auto* const analog_stick_slider = analog_map_deadzone_and_modifier_slider[analog_id];
|
|
auto* const analog_stick_slider_label =
|
|
analog_map_deadzone_and_modifier_slider_label[analog_id];
|
|
|
|
if (param.Has("engine")) {
|
|
if (param.Get("engine", "") == "sdl") {
|
|
if (!param.Has("deadzone")) {
|
|
param.Set("deadzone", 0.1f);
|
|
}
|
|
|
|
analog_stick_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100));
|
|
if (analog_stick_slider->value() == 0) {
|
|
analog_stick_slider_label->setText(tr("Deadzone: 0%"));
|
|
}
|
|
} else {
|
|
if (!param.Has("modifier_scale")) {
|
|
param.Set("modifier_scale", 0.5f);
|
|
}
|
|
|
|
analog_stick_slider->setValue(
|
|
static_cast<int>(param.Get("modifier_scale", 0.5f) * 100));
|
|
if (analog_stick_slider->value() == 0) {
|
|
analog_stick_slider_label->setText(tr("Modifier Scale: 0%"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EmitInputKeysChanged();
|
|
}
|
|
|
|
void ConfigureInput::MapFromButton(const Common::ParamPackage& params) {
|
|
Common::ParamPackage aux_param;
|
|
bool mapped = false;
|
|
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
|
|
aux_param = InputCommon::GetSDLControllerButtonBindByGUID(params.Get("guid", "0"),
|
|
params.Get("port", 0), button_id);
|
|
if (aux_param.Has("engine")) {
|
|
buttons_param[button_id] = aux_param;
|
|
mapped = true;
|
|
}
|
|
}
|
|
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
|
|
aux_param = InputCommon::GetSDLControllerAnalogBindByGUID(params.Get("guid", "0"),
|
|
params.Get("port", 0), analog_id);
|
|
if (aux_param.Has("engine")) {
|
|
analogs_param[analog_id] = aux_param;
|
|
mapped = true;
|
|
}
|
|
}
|
|
if (!mapped) {
|
|
QMessageBox::warning(
|
|
this, tr("Warning"),
|
|
tr("Auto mapping failed. Your controller may not have a corresponding mapping"));
|
|
}
|
|
}
|
|
|
|
void ConfigureInput::AutoMap() {
|
|
if (QMessageBox::information(this, tr("Information"),
|
|
tr("After pressing OK, press any button on your joystick"),
|
|
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) {
|
|
return;
|
|
}
|
|
input_setter = [=](const Common::ParamPackage& params) {
|
|
MapFromButton(params);
|
|
ApplyConfiguration();
|
|
Settings::SaveProfile(ui->profile->currentIndex());
|
|
};
|
|
device_pollers = InputCommon::Polling::GetPollers(InputCommon::Polling::DeviceType::Button);
|
|
want_keyboard_keys = false;
|
|
for (auto& poller : device_pollers) {
|
|
poller->Start();
|
|
}
|
|
timeout_timer->start(5000); // Cancel after 5 seconds
|
|
poll_timer->start(200); // Check for new inputs every 200ms
|
|
}
|
|
|
|
void ConfigureInput::HandleClick(QPushButton* button,
|
|
std::function<void(const Common::ParamPackage&)> new_input_setter,
|
|
InputCommon::Polling::DeviceType type) {
|
|
previous_key_code = QKeySequence(button->text())[0];
|
|
button->setText(tr("[press key]"));
|
|
button->setFocus();
|
|
|
|
input_setter = new_input_setter;
|
|
|
|
device_pollers = InputCommon::Polling::GetPollers(type);
|
|
|
|
// Keyboard keys can only be used as button devices
|
|
want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
|
|
|
|
for (auto& poller : device_pollers) {
|
|
poller->Start();
|
|
}
|
|
|
|
grabKeyboard();
|
|
grabMouse();
|
|
timeout_timer->start(5000); // Cancel after 5 seconds
|
|
poll_timer->start(200); // Check for new inputs every 200ms
|
|
}
|
|
|
|
void ConfigureInput::SetPollingResult(const Common::ParamPackage& params, bool abort) {
|
|
releaseKeyboard();
|
|
releaseMouse();
|
|
timeout_timer->stop();
|
|
poll_timer->stop();
|
|
for (auto& poller : device_pollers) {
|
|
poller->Stop();
|
|
}
|
|
|
|
if (!abort && input_setter) {
|
|
(*input_setter)(params);
|
|
}
|
|
|
|
UpdateButtonLabels();
|
|
input_setter.reset();
|
|
}
|
|
|
|
void ConfigureInput::keyPressEvent(QKeyEvent* event) {
|
|
if (!input_setter || !event)
|
|
return;
|
|
|
|
if (event->key() != Qt::Key_Escape && event->key() != previous_key_code) {
|
|
if (want_keyboard_keys) {
|
|
// Check if key is already bound
|
|
if (hotkey_list.contains(QKeySequence(event->key())) ||
|
|
GetUsedKeyboardKeys().contains(QKeySequence(event->key()))) {
|
|
SetPollingResult({}, true);
|
|
QMessageBox::critical(this, tr("Error!"),
|
|
tr("You're using a key that's already bound."));
|
|
return;
|
|
}
|
|
SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
|
|
false);
|
|
} else {
|
|
// Escape key wasn't pressed and we don't want any keyboard keys, so don't stop
|
|
// polling
|
|
return;
|
|
}
|
|
}
|
|
SetPollingResult({}, true);
|
|
previous_key_code = 0;
|
|
}
|
|
|
|
void ConfigureInput::RetranslateUI() {
|
|
ui->retranslateUi(this);
|
|
}
|
|
|
|
void ConfigureInput::NewProfile() {
|
|
const QString name =
|
|
QInputDialog::getText(this, tr("New Profile"), tr("Enter the name for the new profile."));
|
|
if (name.isEmpty()) {
|
|
return;
|
|
}
|
|
if (IsProfileNameDuplicate(name)) {
|
|
WarnProposedProfileNameIsDuplicate();
|
|
return;
|
|
}
|
|
|
|
ApplyConfiguration();
|
|
Settings::SaveProfile(ui->profile->currentIndex());
|
|
Settings::CreateProfile(name.toStdString());
|
|
ui->profile->addItem(name);
|
|
ui->profile->setCurrentIndex(Settings::values.current_input_profile_index);
|
|
LoadConfiguration();
|
|
ui->buttonDelete->setEnabled(ui->profile->count() > 1);
|
|
}
|
|
|
|
void ConfigureInput::DeleteProfile() {
|
|
const auto answer = QMessageBox::question(
|
|
this, tr("Delete Profile"), tr("Delete profile %1?").arg(ui->profile->currentText()));
|
|
if (answer != QMessageBox::Yes) {
|
|
return;
|
|
}
|
|
const int index = ui->profile->currentIndex();
|
|
ui->profile->removeItem(index);
|
|
ui->profile->setCurrentIndex(0);
|
|
Settings::DeleteProfile(index);
|
|
LoadConfiguration();
|
|
ui->buttonDelete->setEnabled(ui->profile->count() > 1);
|
|
}
|
|
|
|
void ConfigureInput::RenameProfile() {
|
|
const QString new_name = QInputDialog::getText(this, tr("Rename Profile"), tr("New name:"));
|
|
if (new_name.isEmpty()) {
|
|
return;
|
|
}
|
|
if (IsProfileNameDuplicate(new_name)) {
|
|
WarnProposedProfileNameIsDuplicate();
|
|
return;
|
|
}
|
|
|
|
ui->profile->setItemText(ui->profile->currentIndex(), new_name);
|
|
Settings::RenameCurrentProfile(new_name.toStdString());
|
|
Settings::SaveProfile(ui->profile->currentIndex());
|
|
}
|
|
|
|
bool ConfigureInput::IsProfileNameDuplicate(const QString& name) const {
|
|
return ui->profile->findText(name, Qt::MatchFixedString | Qt::MatchCaseSensitive) != -1;
|
|
}
|
|
|
|
void ConfigureInput::WarnProposedProfileNameIsDuplicate() {
|
|
QMessageBox::warning(this, tr("Duplicate profile name"),
|
|
tr("Profile name already exists. Please choose a different name."));
|
|
}
|