|
|
|
@ -4,6 +4,7 @@
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <memory>
|
|
|
|
|
#include <thread>
|
|
|
|
|
#include <utility>
|
|
|
|
|
#include <QGridLayout>
|
|
|
|
|
#include <QInputDialog>
|
|
|
|
@ -22,8 +23,9 @@
|
|
|
|
|
#include "ui_configure_input_player.h"
|
|
|
|
|
#include "yuzu/configuration/config.h"
|
|
|
|
|
#include "yuzu/configuration/configure_input_player.h"
|
|
|
|
|
|
|
|
|
|
constexpr std::size_t HANDHELD_INDEX = 8;
|
|
|
|
|
#include "yuzu/configuration/configure_vibration.h"
|
|
|
|
|
#include "yuzu/configuration/input_profiles.h"
|
|
|
|
|
#include "yuzu/util/limitable_input_dialog.h"
|
|
|
|
|
|
|
|
|
|
const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
|
|
|
|
|
ConfigureInputPlayer::analog_sub_buttons{{
|
|
|
|
@ -35,6 +37,8 @@ const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
constexpr std::size_t HANDHELD_INDEX = 8;
|
|
|
|
|
|
|
|
|
|
void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
|
|
|
|
|
bool connected) {
|
|
|
|
|
Core::System& system{Core::System::GetInstance()};
|
|
|
|
@ -240,10 +244,11 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir)
|
|
|
|
|
ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index,
|
|
|
|
|
QWidget* bottom_row,
|
|
|
|
|
InputCommon::InputSubsystem* input_subsystem_,
|
|
|
|
|
bool debug)
|
|
|
|
|
InputProfiles* profiles_, bool debug)
|
|
|
|
|
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
|
|
|
|
|
debug(debug), input_subsystem{input_subsystem_}, timeout_timer(std::make_unique<QTimer>()),
|
|
|
|
|
poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row) {
|
|
|
|
|
debug(debug), input_subsystem{input_subsystem_}, profiles(profiles_),
|
|
|
|
|
timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()),
|
|
|
|
|
bottom_row(bottom_row) {
|
|
|
|
|
ui->setupUi(this);
|
|
|
|
|
|
|
|
|
|
setFocusPolicy(Qt::ClickFocus);
|
|
|
|
@ -366,6 +371,18 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
connect(analog_button, &QPushButton::clicked, [=, this] {
|
|
|
|
|
if (!map_analog_stick_accepted) {
|
|
|
|
|
map_analog_stick_accepted =
|
|
|
|
|
QMessageBox::information(
|
|
|
|
|
this, tr("Map Analog Stick"),
|
|
|
|
|
tr("After pressing OK, first move your joystick horizontally, and then "
|
|
|
|
|
"vertically.\nTo invert the axes, first move your joystick "
|
|
|
|
|
"vertically, and then horizontally."),
|
|
|
|
|
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok;
|
|
|
|
|
if (!map_analog_stick_accepted) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
HandleClick(
|
|
|
|
|
analog_map_buttons[analog_id][sub_button_id],
|
|
|
|
|
[=, this](const Common::ParamPackage& params) {
|
|
|
|
@ -455,11 +472,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (debug || player_index == 9) {
|
|
|
|
|
ui->groupConnectedController->setCheckable(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The Debug Controller can only choose the Pro Controller.
|
|
|
|
|
if (debug) {
|
|
|
|
|
ui->buttonScreenshot->setEnabled(false);
|
|
|
|
|
ui->buttonHome->setEnabled(false);
|
|
|
|
|
ui->groupConnectedController->setCheckable(false);
|
|
|
|
|
QStringList debug_controller_types = {
|
|
|
|
|
tr("Pro Controller"),
|
|
|
|
|
};
|
|
|
|
@ -477,11 +497,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
|
|
|
|
UpdateMotionButtons();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
|
|
|
|
connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this,
|
|
|
|
|
&ConfigureInputPlayer::UpdateMappingWithDefaults);
|
|
|
|
|
|
|
|
|
|
ui->comboDevices->setCurrentIndex(-1);
|
|
|
|
|
|
|
|
|
|
ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
|
|
|
|
|
UpdateInputDevices();
|
|
|
|
|
connect(ui->buttonRefreshDevices, &QPushButton::clicked,
|
|
|
|
|
[this] { emit RefreshInputDevices(); });
|
|
|
|
|
|
|
|
|
@ -492,14 +513,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
|
|
|
|
Common::ParamPackage params;
|
|
|
|
|
if (input_subsystem->GetGCButtons()->IsPolling()) {
|
|
|
|
|
params = input_subsystem->GetGCButtons()->GetNextInput();
|
|
|
|
|
if (params.Has("engine")) {
|
|
|
|
|
if (params.Has("engine") && IsInputAcceptable(params)) {
|
|
|
|
|
SetPollingResult(params, false);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (input_subsystem->GetGCAnalogs()->IsPolling()) {
|
|
|
|
|
params = input_subsystem->GetGCAnalogs()->GetNextInput();
|
|
|
|
|
if (params.Has("engine")) {
|
|
|
|
|
if (params.Has("engine") && IsInputAcceptable(params)) {
|
|
|
|
|
SetPollingResult(params, false);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
@ -513,13 +534,24 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
|
|
|
|
}
|
|
|
|
|
for (auto& poller : device_pollers) {
|
|
|
|
|
params = poller->GetNextInput();
|
|
|
|
|
if (params.Has("engine")) {
|
|
|
|
|
if (params.Has("engine") && IsInputAcceptable(params)) {
|
|
|
|
|
SetPollingResult(params, false);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
UpdateInputProfiles();
|
|
|
|
|
|
|
|
|
|
connect(ui->buttonProfilesNew, &QPushButton::clicked, this,
|
|
|
|
|
&ConfigureInputPlayer::CreateProfile);
|
|
|
|
|
connect(ui->buttonProfilesDelete, &QPushButton::clicked, this,
|
|
|
|
|
&ConfigureInputPlayer::DeleteProfile);
|
|
|
|
|
connect(ui->comboProfiles, qOverload<int>(&QComboBox::activated), this,
|
|
|
|
|
&ConfigureInputPlayer::LoadProfile);
|
|
|
|
|
connect(ui->buttonProfilesSave, &QPushButton::clicked, this,
|
|
|
|
|
&ConfigureInputPlayer::SaveProfile);
|
|
|
|
|
|
|
|
|
|
LoadConfiguration();
|
|
|
|
|
|
|
|
|
|
// TODO(wwylele): enable this when we actually emulate it
|
|
|
|
@ -529,7 +561,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
|
|
|
|
ConfigureInputPlayer::~ConfigureInputPlayer() = default;
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::ApplyConfiguration() {
|
|
|
|
|
auto& player = Settings::values.players[player_index];
|
|
|
|
|
auto& player = Settings::values.players.GetValue()[player_index];
|
|
|
|
|
auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons;
|
|
|
|
|
auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs;
|
|
|
|
|
|
|
|
|
@ -543,33 +575,58 @@ void ConfigureInputPlayer::ApplyConfiguration() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto& motions = player.motions;
|
|
|
|
|
|
|
|
|
|
std::transform(motions_param.begin(), motions_param.end(), motions.begin(),
|
|
|
|
|
[](const Common::ParamPackage& param) { return param.Serialize(); });
|
|
|
|
|
|
|
|
|
|
player.controller_type =
|
|
|
|
|
static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
|
|
|
|
|
player.connected = ui->groupConnectedController->isChecked();
|
|
|
|
|
const auto controller_type =
|
|
|
|
|
GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
|
|
|
|
|
const auto player_connected = ui->groupConnectedController->isChecked() &&
|
|
|
|
|
controller_type != Settings::ControllerType::Handheld;
|
|
|
|
|
|
|
|
|
|
// Player 2-8
|
|
|
|
|
if (player_index != 0) {
|
|
|
|
|
UpdateController(player.controller_type, player_index, player.connected);
|
|
|
|
|
if (player.controller_type == controller_type && player.connected == player_connected) {
|
|
|
|
|
// Set vibration devices in the event that the input device has changed.
|
|
|
|
|
ConfigureVibration::SetVibrationDevices(player_index);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Player 1 and Handheld
|
|
|
|
|
auto& handheld = Settings::values.players[HANDHELD_INDEX];
|
|
|
|
|
// If Handheld is selected, copy all the settings from Player 1 to Handheld.
|
|
|
|
|
if (player.controller_type == Settings::ControllerType::Handheld) {
|
|
|
|
|
// Disconnect the controller first.
|
|
|
|
|
UpdateController(controller_type, player_index, false);
|
|
|
|
|
|
|
|
|
|
player.controller_type = controller_type;
|
|
|
|
|
player.connected = player_connected;
|
|
|
|
|
|
|
|
|
|
ConfigureVibration::SetVibrationDevices(player_index);
|
|
|
|
|
|
|
|
|
|
// Handheld
|
|
|
|
|
if (player_index == 0) {
|
|
|
|
|
auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
|
|
|
|
|
if (controller_type == Settings::ControllerType::Handheld) {
|
|
|
|
|
handheld = player;
|
|
|
|
|
handheld.connected = ui->groupConnectedController->isChecked();
|
|
|
|
|
player.connected = false; // Disconnect Player 1
|
|
|
|
|
} else {
|
|
|
|
|
player.connected = ui->groupConnectedController->isChecked();
|
|
|
|
|
handheld.connected = false; // Disconnect Handheld
|
|
|
|
|
}
|
|
|
|
|
handheld.connected = ui->groupConnectedController->isChecked() &&
|
|
|
|
|
controller_type == Settings::ControllerType::Handheld;
|
|
|
|
|
UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateController(player.controller_type, player_index, player.connected);
|
|
|
|
|
UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected);
|
|
|
|
|
if (!player.connected) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This emulates a delay between disconnecting and reconnecting controllers as some games
|
|
|
|
|
// do not respond to a change in controller type if it was instantaneous.
|
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
|
std::this_thread::sleep_for(20ms);
|
|
|
|
|
|
|
|
|
|
UpdateController(controller_type, player_index, player_connected);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::showEvent(QShowEvent* event) {
|
|
|
|
|
if (bottom_row == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QWidget::showEvent(event);
|
|
|
|
|
ui->main->addWidget(bottom_row);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::changeEvent(QEvent* event) {
|
|
|
|
@ -586,7 +643,7 @@ void ConfigureInputPlayer::RetranslateUI() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::LoadConfiguration() {
|
|
|
|
|
auto& player = Settings::values.players[player_index];
|
|
|
|
|
auto& player = Settings::values.players.GetValue()[player_index];
|
|
|
|
|
if (debug) {
|
|
|
|
|
std::transform(Settings::values.debug_pad_buttons.begin(),
|
|
|
|
|
Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
|
|
|
|
@ -604,6 +661,7 @@ void ConfigureInputPlayer::LoadConfiguration() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateUI();
|
|
|
|
|
UpdateInputDeviceCombobox();
|
|
|
|
|
|
|
|
|
|
if (debug) {
|
|
|
|
|
return;
|
|
|
|
@ -612,44 +670,75 @@ void ConfigureInputPlayer::LoadConfiguration() {
|
|
|
|
|
ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type));
|
|
|
|
|
ui->groupConnectedController->setChecked(
|
|
|
|
|
player.connected ||
|
|
|
|
|
(player_index == 0 && Settings::values.players[HANDHELD_INDEX].connected));
|
|
|
|
|
(player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::UpdateInputDevices() {
|
|
|
|
|
input_devices = input_subsystem->GetInputDevices();
|
|
|
|
|
ui->comboDevices->clear();
|
|
|
|
|
for (auto device : input_devices) {
|
|
|
|
|
ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
|
|
|
|
|
void ConfigureInputPlayer::ConnectPlayer(bool connected) {
|
|
|
|
|
ui->groupConnectedController->setChecked(connected);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::UpdateInputDeviceCombobox() {
|
|
|
|
|
// Skip input device persistence if "Input Devices" is set to "Any".
|
|
|
|
|
if (ui->comboDevices->currentIndex() == 0) {
|
|
|
|
|
UpdateInputDevices();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the first button that isn't empty.
|
|
|
|
|
const auto button_param =
|
|
|
|
|
std::find_if(buttons_param.begin(), buttons_param.end(),
|
|
|
|
|
[](const Common::ParamPackage param) { return param.Has("engine"); });
|
|
|
|
|
const bool buttons_empty = button_param == buttons_param.end();
|
|
|
|
|
|
|
|
|
|
const auto current_engine = button_param->Get("engine", "");
|
|
|
|
|
const auto current_guid = button_param->Get("guid", "");
|
|
|
|
|
const auto current_port = button_param->Get("port", "");
|
|
|
|
|
|
|
|
|
|
const bool is_keyboard_mouse = current_engine == "keyboard" || current_engine == "mouse";
|
|
|
|
|
|
|
|
|
|
UpdateInputDevices();
|
|
|
|
|
|
|
|
|
|
if (buttons_empty) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool all_one_device =
|
|
|
|
|
std::all_of(buttons_param.begin(), buttons_param.end(),
|
|
|
|
|
[current_engine, current_guid, current_port,
|
|
|
|
|
is_keyboard_mouse](const Common::ParamPackage param) {
|
|
|
|
|
if (is_keyboard_mouse) {
|
|
|
|
|
return !param.Has("engine") || param.Get("engine", "") == "keyboard" ||
|
|
|
|
|
param.Get("engine", "") == "mouse";
|
|
|
|
|
}
|
|
|
|
|
return !param.Has("engine") || (param.Get("engine", "") == current_engine &&
|
|
|
|
|
param.Get("guid", "") == current_guid &&
|
|
|
|
|
param.Get("port", "") == current_port);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (all_one_device) {
|
|
|
|
|
if (is_keyboard_mouse) {
|
|
|
|
|
ui->comboDevices->setCurrentIndex(1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const auto devices_it = std::find_if(
|
|
|
|
|
input_devices.begin(), input_devices.end(),
|
|
|
|
|
[current_engine, current_guid, current_port](const Common::ParamPackage param) {
|
|
|
|
|
return param.Get("class", "") == current_engine &&
|
|
|
|
|
param.Get("guid", "") == current_guid &&
|
|
|
|
|
param.Get("port", "") == current_port;
|
|
|
|
|
});
|
|
|
|
|
const int device_index =
|
|
|
|
|
devices_it != input_devices.end()
|
|
|
|
|
? static_cast<int>(std::distance(input_devices.begin(), devices_it))
|
|
|
|
|
: 0;
|
|
|
|
|
ui->comboDevices->setCurrentIndex(device_index);
|
|
|
|
|
} else {
|
|
|
|
|
ui->comboDevices->setCurrentIndex(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::RestoreDefaults() {
|
|
|
|
|
// Reset Buttons
|
|
|
|
|
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])};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reset Analogs and Modifier Buttons
|
|
|
|
|
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])};
|
|
|
|
|
SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
analogs_param[analog_id].Set(
|
|
|
|
|
"modifier", InputCommon::GenerateKeyboardParam(Config::default_stick_mod[analog_id]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
|
|
|
|
|
motions_param[motion_id] = Common::ParamPackage{
|
|
|
|
|
InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateUI();
|
|
|
|
|
UpdateInputDevices();
|
|
|
|
|
ui->comboControllerType->setCurrentIndex(0);
|
|
|
|
|
UpdateMappingWithDefaults();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::ClearAll() {
|
|
|
|
@ -752,117 +841,12 @@ void ConfigureInputPlayer::UpdateUI() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::UpdateMappingWithDefaults() {
|
|
|
|
|
if (ui->comboDevices->currentIndex() < 2) {
|
|
|
|
|
return;
|
|
|
|
|
void ConfigureInputPlayer::UpdateInputDevices() {
|
|
|
|
|
input_devices = input_subsystem->GetInputDevices();
|
|
|
|
|
ui->comboDevices->clear();
|
|
|
|
|
for (auto device : input_devices) {
|
|
|
|
|
ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
|
|
|
|
|
}
|
|
|
|
|
const auto& device = input_devices[ui->comboDevices->currentIndex()];
|
|
|
|
|
auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
|
|
|
|
|
auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
|
|
|
|
|
for (std::size_t i = 0; i < buttons_param.size(); ++i) {
|
|
|
|
|
buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)];
|
|
|
|
|
}
|
|
|
|
|
for (std::size_t i = 0; i < analogs_param.size(); ++i) {
|
|
|
|
|
analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateUI();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::HandleClick(
|
|
|
|
|
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
|
|
|
|
|
InputCommon::Polling::DeviceType type) {
|
|
|
|
|
if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
|
|
|
|
|
button->setText(tr("Shake!"));
|
|
|
|
|
} else {
|
|
|
|
|
button->setText(tr("[waiting]"));
|
|
|
|
|
}
|
|
|
|
|
button->setFocus();
|
|
|
|
|
|
|
|
|
|
// The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
|
|
|
|
|
// controller, then they don't want keyboard/mouse input
|
|
|
|
|
want_keyboard_mouse = ui->comboDevices->currentIndex() < 2;
|
|
|
|
|
|
|
|
|
|
input_setter = new_input_setter;
|
|
|
|
|
|
|
|
|
|
device_pollers = input_subsystem->GetPollers(type);
|
|
|
|
|
|
|
|
|
|
for (auto& poller : device_pollers) {
|
|
|
|
|
poller->Start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QWidget::grabMouse();
|
|
|
|
|
QWidget::grabKeyboard();
|
|
|
|
|
|
|
|
|
|
if (type == InputCommon::Polling::DeviceType::Button) {
|
|
|
|
|
input_subsystem->GetGCButtons()->BeginConfiguration();
|
|
|
|
|
} else {
|
|
|
|
|
input_subsystem->GetGCAnalogs()->BeginConfiguration();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (type == InputCommon::Polling::DeviceType::Motion) {
|
|
|
|
|
input_subsystem->GetUDPMotions()->BeginConfiguration();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
timeout_timer->start(2500); // Cancel after 2.5 seconds
|
|
|
|
|
poll_timer->start(50); // Check for new inputs every 50ms
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) {
|
|
|
|
|
timeout_timer->stop();
|
|
|
|
|
poll_timer->stop();
|
|
|
|
|
for (auto& poller : device_pollers) {
|
|
|
|
|
poller->Stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QWidget::releaseMouse();
|
|
|
|
|
QWidget::releaseKeyboard();
|
|
|
|
|
|
|
|
|
|
input_subsystem->GetGCButtons()->EndConfiguration();
|
|
|
|
|
input_subsystem->GetGCAnalogs()->EndConfiguration();
|
|
|
|
|
|
|
|
|
|
input_subsystem->GetUDPMotions()->EndConfiguration();
|
|
|
|
|
|
|
|
|
|
if (!abort) {
|
|
|
|
|
(*input_setter)(params);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateUI();
|
|
|
|
|
input_setter = std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
|
|
|
|
|
if (!input_setter || !event) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (want_keyboard_mouse) {
|
|
|
|
|
SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
|
|
|
|
|
false);
|
|
|
|
|
} else {
|
|
|
|
|
// We don't want any mouse buttons, so don't stop polling
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetPollingResult({}, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
|
|
|
|
|
if (!input_setter || !event) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (event->key() != Qt::Key_Escape) {
|
|
|
|
|
if (want_keyboard_mouse) {
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::UpdateControllerIcon() {
|
|
|
|
@ -885,7 +869,7 @@ void ConfigureInputPlayer::UpdateControllerIcon() {
|
|
|
|
|
}
|
|
|
|
|
}();
|
|
|
|
|
|
|
|
|
|
const QString theme = [this] {
|
|
|
|
|
const QString theme = [] {
|
|
|
|
|
if (QIcon::themeName().contains(QStringLiteral("dark"))) {
|
|
|
|
|
return QStringLiteral("_dark");
|
|
|
|
|
} else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
|
|
|
|
@ -986,14 +970,260 @@ void ConfigureInputPlayer::UpdateMotionButtons() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::showEvent(QShowEvent* event) {
|
|
|
|
|
if (bottom_row == nullptr) {
|
|
|
|
|
void ConfigureInputPlayer::UpdateMappingWithDefaults() {
|
|
|
|
|
if (ui->comboDevices->currentIndex() == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QWidget::showEvent(event);
|
|
|
|
|
ui->main->addWidget(bottom_row);
|
|
|
|
|
|
|
|
|
|
if (ui->comboDevices->currentIndex() == 1) {
|
|
|
|
|
// Reset keyboard bindings
|
|
|
|
|
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])};
|
|
|
|
|
SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::ConnectPlayer(bool connected) {
|
|
|
|
|
ui->groupConnectedController->setChecked(connected);
|
|
|
|
|
analogs_param[analog_id].Set("modifier", InputCommon::GenerateKeyboardParam(
|
|
|
|
|
Config::default_stick_mod[analog_id]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
|
|
|
|
|
motions_param[motion_id] = Common::ParamPackage{
|
|
|
|
|
InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateUI();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reset controller bindings
|
|
|
|
|
const auto& device = input_devices[ui->comboDevices->currentIndex()];
|
|
|
|
|
auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
|
|
|
|
|
auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
|
|
|
|
|
for (std::size_t i = 0; i < buttons_param.size(); ++i) {
|
|
|
|
|
buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)];
|
|
|
|
|
}
|
|
|
|
|
for (std::size_t i = 0; i < analogs_param.size(); ++i) {
|
|
|
|
|
analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateUI();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::HandleClick(
|
|
|
|
|
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
|
|
|
|
|
InputCommon::Polling::DeviceType type) {
|
|
|
|
|
if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
|
|
|
|
|
button->setText(tr("Shake!"));
|
|
|
|
|
} else {
|
|
|
|
|
button->setText(tr("[waiting]"));
|
|
|
|
|
}
|
|
|
|
|
button->setFocus();
|
|
|
|
|
|
|
|
|
|
// The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
|
|
|
|
|
// controller, then they don't want keyboard/mouse input
|
|
|
|
|
want_keyboard_mouse = ui->comboDevices->currentIndex() < 2;
|
|
|
|
|
|
|
|
|
|
input_setter = new_input_setter;
|
|
|
|
|
|
|
|
|
|
device_pollers = input_subsystem->GetPollers(type);
|
|
|
|
|
|
|
|
|
|
for (auto& poller : device_pollers) {
|
|
|
|
|
poller->Start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QWidget::grabMouse();
|
|
|
|
|
QWidget::grabKeyboard();
|
|
|
|
|
|
|
|
|
|
if (type == InputCommon::Polling::DeviceType::Button) {
|
|
|
|
|
input_subsystem->GetGCButtons()->BeginConfiguration();
|
|
|
|
|
} else {
|
|
|
|
|
input_subsystem->GetGCAnalogs()->BeginConfiguration();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (type == InputCommon::Polling::DeviceType::Motion) {
|
|
|
|
|
input_subsystem->GetUDPMotions()->BeginConfiguration();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
timeout_timer->start(2500); // Cancel after 2.5 seconds
|
|
|
|
|
poll_timer->start(50); // Check for new inputs every 50ms
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) {
|
|
|
|
|
timeout_timer->stop();
|
|
|
|
|
poll_timer->stop();
|
|
|
|
|
for (auto& poller : device_pollers) {
|
|
|
|
|
poller->Stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QWidget::releaseMouse();
|
|
|
|
|
QWidget::releaseKeyboard();
|
|
|
|
|
|
|
|
|
|
input_subsystem->GetGCButtons()->EndConfiguration();
|
|
|
|
|
input_subsystem->GetGCAnalogs()->EndConfiguration();
|
|
|
|
|
|
|
|
|
|
input_subsystem->GetUDPMotions()->EndConfiguration();
|
|
|
|
|
|
|
|
|
|
if (!abort) {
|
|
|
|
|
(*input_setter)(params);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateUI();
|
|
|
|
|
UpdateInputDeviceCombobox();
|
|
|
|
|
|
|
|
|
|
input_setter = std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params) const {
|
|
|
|
|
if (ui->comboDevices->currentIndex() == 0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Keyboard/Mouse
|
|
|
|
|
if (ui->comboDevices->currentIndex() == 1) {
|
|
|
|
|
return params.Get("engine", "") == "keyboard" || params.Get("engine", "") == "mouse";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto current_input_device = input_devices[ui->comboDevices->currentIndex()];
|
|
|
|
|
return params.Get("engine", "") == current_input_device.Get("class", "") &&
|
|
|
|
|
params.Get("guid", "") == current_input_device.Get("guid", "") &&
|
|
|
|
|
params.Get("port", "") == current_input_device.Get("port", "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
|
|
|
|
|
if (!input_setter || !event) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (want_keyboard_mouse) {
|
|
|
|
|
SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
|
|
|
|
|
false);
|
|
|
|
|
} else {
|
|
|
|
|
// We don't want any mouse buttons, so don't stop polling
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetPollingResult({}, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
|
|
|
|
|
if (!input_setter || !event) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (event->key() != Qt::Key_Escape) {
|
|
|
|
|
if (want_keyboard_mouse) {
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::CreateProfile() {
|
|
|
|
|
const auto profile_name =
|
|
|
|
|
LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 20);
|
|
|
|
|
|
|
|
|
|
if (profile_name.isEmpty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!profiles->IsProfileNameValid(profile_name.toStdString())) {
|
|
|
|
|
QMessageBox::critical(this, tr("Create Input Profile"),
|
|
|
|
|
tr("The given profile name is not valid!"));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ApplyConfiguration();
|
|
|
|
|
|
|
|
|
|
if (!profiles->CreateProfile(profile_name.toStdString(), player_index)) {
|
|
|
|
|
QMessageBox::critical(this, tr("Create Input Profile"),
|
|
|
|
|
tr("Failed to create the input profile \"%1\"").arg(profile_name));
|
|
|
|
|
UpdateInputProfiles();
|
|
|
|
|
emit RefreshInputProfiles(player_index);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emit RefreshInputProfiles(player_index);
|
|
|
|
|
|
|
|
|
|
ui->comboProfiles->addItem(profile_name);
|
|
|
|
|
ui->comboProfiles->setCurrentIndex(ui->comboProfiles->count() - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::DeleteProfile() {
|
|
|
|
|
const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
|
|
|
|
|
|
|
|
|
|
if (profile_name.isEmpty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!profiles->DeleteProfile(profile_name.toStdString())) {
|
|
|
|
|
QMessageBox::critical(this, tr("Delete Input Profile"),
|
|
|
|
|
tr("Failed to delete the input profile \"%1\"").arg(profile_name));
|
|
|
|
|
UpdateInputProfiles();
|
|
|
|
|
emit RefreshInputProfiles(player_index);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emit RefreshInputProfiles(player_index);
|
|
|
|
|
|
|
|
|
|
ui->comboProfiles->removeItem(ui->comboProfiles->currentIndex());
|
|
|
|
|
ui->comboProfiles->setCurrentIndex(-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::LoadProfile() {
|
|
|
|
|
const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
|
|
|
|
|
|
|
|
|
|
if (profile_name.isEmpty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ApplyConfiguration();
|
|
|
|
|
|
|
|
|
|
if (!profiles->LoadProfile(profile_name.toStdString(), player_index)) {
|
|
|
|
|
QMessageBox::critical(this, tr("Load Input Profile"),
|
|
|
|
|
tr("Failed to load the input profile \"%1\"").arg(profile_name));
|
|
|
|
|
UpdateInputProfiles();
|
|
|
|
|
emit RefreshInputProfiles(player_index);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LoadConfiguration();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::SaveProfile() {
|
|
|
|
|
const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
|
|
|
|
|
|
|
|
|
|
if (profile_name.isEmpty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ApplyConfiguration();
|
|
|
|
|
|
|
|
|
|
if (!profiles->SaveProfile(profile_name.toStdString(), player_index)) {
|
|
|
|
|
QMessageBox::critical(this, tr("Save Input Profile"),
|
|
|
|
|
tr("Failed to save the input profile \"%1\"").arg(profile_name));
|
|
|
|
|
UpdateInputProfiles();
|
|
|
|
|
emit RefreshInputProfiles(player_index);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureInputPlayer::UpdateInputProfiles() {
|
|
|
|
|
ui->comboProfiles->clear();
|
|
|
|
|
|
|
|
|
|
for (const auto& profile_name : profiles->GetInputProfileNames()) {
|
|
|
|
|
ui->comboProfiles->addItem(QString::fromStdString(profile_name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ui->comboProfiles->setCurrentIndex(-1);
|
|
|
|
|
}
|
|
|
|
|