From 75eaab2e0f48eb588c1bfb85f96630e199fbc1da Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 17 Sep 2020 12:00:29 -0400 Subject: configure_input_player: Implement input exclusivity and persistence With this, the "Input Devices" combobox should accurately reflect the input device being used and disallows inputs from other input devices unless the input device is set to "Any". --- src/yuzu/configuration/configure_input_player.cpp | 318 +++++++++++++--------- 1 file changed, 191 insertions(+), 127 deletions(-) (limited to 'src/yuzu/configuration/configure_input_player.cpp') diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index f58ca29d7..0de0c6999 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -477,11 +477,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i UpdateMotionButtons(); }); - connect(ui->comboDevices, qOverload(&QComboBox::currentIndexChanged), this, + connect(ui->comboDevices, qOverload(&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 +493,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,7 +514,7 @@ 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; } @@ -572,6 +573,14 @@ void ConfigureInputPlayer::ApplyConfiguration() { UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected); } +void ConfigureInputPlayer::showEvent(QShowEvent* event) { + if (bottom_row == nullptr) { + return; + } + QWidget::showEvent(event); + ui->main->addWidget(bottom_row); +} + void ConfigureInputPlayer::changeEvent(QEvent* event) { if (event->type() == QEvent::LanguageChange) { RetranslateUI(); @@ -604,6 +613,7 @@ void ConfigureInputPlayer::LoadConfiguration() { } UpdateUI(); + UpdateInputDeviceCombobox(); if (debug) { return; @@ -615,11 +625,56 @@ void ConfigureInputPlayer::LoadConfiguration() { (player_index == 0 && Settings::values.players[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", ""); + + 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](const Common::ParamPackage param) { + return !param.Has("engine") || (param.Get("engine", "") == current_engine && + param.Get("guid", "") == current_guid && + param.Get("port", "") == current_port); + }); + + if (all_one_device) { + 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(std::distance(input_devices.begin(), devices_it)) + : 0; + ui->comboDevices->setCurrentIndex(device_index); + } else { + ui->comboDevices->setCurrentIndex(0); } } @@ -648,7 +703,7 @@ void ConfigureInputPlayer::RestoreDefaults() { } UpdateUI(); - UpdateInputDevices(); + UpdateInputDeviceCombobox(); ui->comboControllerType->setCurrentIndex(0); } @@ -752,117 +807,12 @@ void ConfigureInputPlayer::UpdateUI() { } } -void ConfigureInputPlayer::UpdateMappingWithDefaults() { - if (ui->comboDevices->currentIndex() < 2) { - return; - } - 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(i)]; - } - for (std::size_t i = 0; i < analogs_param.size(); ++i) { - analogs_param[i] = analog_mapping[static_cast(i)]; - } - - UpdateUI(); -} - -void ConfigureInputPlayer::HandleClick( - QPushButton* button, std::function 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; - } +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")), {}); } - - SetPollingResult({}, true); } void ConfigureInputPlayer::UpdateControllerIcon() { @@ -986,14 +936,128 @@ void ConfigureInputPlayer::UpdateMotionButtons() { } } -void ConfigureInputPlayer::showEvent(QShowEvent* event) { - if (bottom_row == nullptr) { +void ConfigureInputPlayer::UpdateMappingWithDefaults() { + if (ui->comboDevices->currentIndex() < 2) { return; } - QWidget::showEvent(event); - ui->main->addWidget(bottom_row); + 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(i)]; + } + for (std::size_t i = 0; i < analogs_param.size(); ++i) { + analogs_param[i] = analog_mapping[static_cast(i)]; + } + + UpdateUI(); } -void ConfigureInputPlayer::ConnectPlayer(bool connected) { - ui->groupConnectedController->setChecked(connected); +void ConfigureInputPlayer::HandleClick( + QPushButton* button, std::function 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; + } + + 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); } -- cgit v1.2.3 From 57d89e291de0eacfd368784309a0cbf89d38dcc8 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 23 Sep 2020 09:52:25 -0400 Subject: input_profiles: Implement input profiles --- src/yuzu/configuration/configure_input_player.cpp | 111 +++++++++++++++++++++- 1 file changed, 108 insertions(+), 3 deletions(-) (limited to 'src/yuzu/configuration/configure_input_player.cpp') diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 0de0c6999..b4de2f6af 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -22,6 +22,8 @@ #include "ui_configure_input_player.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input_player.h" +#include "yuzu/configuration/input_profiles.h" +#include "yuzu/util/limitable_input_dialog.h" constexpr std::size_t HANDHELD_INDEX = 8; @@ -240,10 +242,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()), player_index(player_index), - debug(debug), input_subsystem{input_subsystem_}, timeout_timer(std::make_unique()), - poll_timer(std::make_unique()), bottom_row(bottom_row) { + debug(debug), input_subsystem{input_subsystem_}, profiles(profiles_), + timeout_timer(std::make_unique()), poll_timer(std::make_unique()), + bottom_row(bottom_row) { ui->setupUi(this); setFocusPolicy(Qt::ClickFocus); @@ -521,6 +524,17 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } }); + RefreshInputProfiles(); + + connect(ui->buttonProfilesNew, &QPushButton::clicked, this, + &ConfigureInputPlayer::CreateProfile); + connect(ui->buttonProfilesDelete, &QPushButton::clicked, this, + &ConfigureInputPlayer::DeleteProfile); + connect(ui->comboProfiles, qOverload(&QComboBox::activated), this, + &ConfigureInputPlayer::LoadProfile); + connect(ui->buttonProfilesSave, &QPushButton::clicked, this, + &ConfigureInputPlayer::SaveProfile); + LoadConfiguration(); // TODO(wwylele): enable this when we actually emulate it @@ -1061,3 +1075,94 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { 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)); + RefreshInputProfiles(); + return; + } + + 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)); + RefreshInputProfiles(); + return; + } + + 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)); + RefreshInputProfiles(); + 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)); + RefreshInputProfiles(); + return; + } +} + +void ConfigureInputPlayer::RefreshInputProfiles() { + ui->comboProfiles->clear(); + + for (const auto& profile_name : profiles->GetInputProfileNames()) { + ui->comboProfiles->addItem(QString::fromStdString(profile_name)); + } + + ui->comboProfiles->setCurrentIndex(-1); +} -- cgit v1.2.3 From 8f2959f6804e0d1048ecaa6f4046622e069fe7db Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Mon, 28 Sep 2020 10:00:15 -0400 Subject: settings: Preparation for per-game input settings --- src/yuzu/configuration/configure_input_player.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/yuzu/configuration/configure_input_player.cpp') diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index b4de2f6af..213a76224 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -544,7 +544,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; @@ -572,7 +572,7 @@ void ConfigureInputPlayer::ApplyConfiguration() { } // Player 1 and Handheld - auto& handheld = Settings::values.players[HANDHELD_INDEX]; + auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; // If Handheld is selected, copy all the settings from Player 1 to Handheld. if (player.controller_type == Settings::ControllerType::Handheld) { handheld = player; @@ -609,7 +609,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(), @@ -636,7 +636,7 @@ void ConfigureInputPlayer::LoadConfiguration() { ui->comboControllerType->setCurrentIndex(static_cast(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::ConnectPlayer(bool connected) { -- cgit v1.2.3 From ceb7b11f166a4e59945a6296d364980c37ca681e Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Mon, 28 Sep 2020 10:27:29 -0400 Subject: configure_input_player: Change "Defaults" button behavior RestoreDefaults() now restores the selected devices' mappings using UpdateMappingWithDefaults(). This allows us to move the keyboard mapping from RestoreDefaults() to UpdateMappingWithDefaults(). --- src/yuzu/configuration/configure_input_player.cpp | 57 ++++++++++++----------- 1 file changed, 30 insertions(+), 27 deletions(-) (limited to 'src/yuzu/configuration/configure_input_player.cpp') diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 213a76224..460ff08a4 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -693,32 +693,7 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() { } 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(); - UpdateInputDeviceCombobox(); - ui->comboControllerType->setCurrentIndex(0); + UpdateMappingWithDefaults(); } void ConfigureInputPlayer::ClearAll() { @@ -951,9 +926,37 @@ void ConfigureInputPlayer::UpdateMotionButtons() { } void ConfigureInputPlayer::UpdateMappingWithDefaults() { - if (ui->comboDevices->currentIndex() < 2) { + if (ui->comboDevices->currentIndex() == 0) { return; } + + 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]); + } + + 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); -- cgit v1.2.3 From e9e1876e821b8bd1bb5c8254ec93e2cc479e16dd Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Tue, 20 Oct 2020 13:55:25 -0400 Subject: input_common: Add VibrationDevice and VibrationDeviceFactory A vibration device is an input device that returns an unsigned byte as status. It represents whether the vibration device supports vibration or not. If the status returns 1, it supports vibration. Otherwise, it does not support vibration. --- src/yuzu/configuration/configure_input_player.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/yuzu/configuration/configure_input_player.cpp') diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 460ff08a4..3e785c224 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -22,6 +22,7 @@ #include "ui_configure_input_player.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input_player.h" +#include "yuzu/configuration/configure_vibration.h" #include "yuzu/configuration/input_profiles.h" #include "yuzu/util/limitable_input_dialog.h" @@ -39,6 +40,10 @@ namespace { void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, bool connected) { + auto& player = Settings::values.players.GetValue()[npad_index]; + player.controller_type = controller_type; + player.connected = connected; + Core::System& system{Core::System::GetInstance()}; if (!system.IsPoweredOn()) { return; @@ -565,6 +570,8 @@ void ConfigureInputPlayer::ApplyConfiguration() { static_cast(ui->comboControllerType->currentIndex()); player.connected = ui->groupConnectedController->isChecked(); + ConfigureVibration::SetVibrationDevices(player_index); + // Player 2-8 if (player_index != 0) { UpdateController(player.controller_type, player_index, player.connected); -- cgit v1.2.3 From 91c06dae1a68efb2d6ae110d0aa28d7b3aafd573 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 23 Oct 2020 12:09:28 -0400 Subject: input: Disconnect a controller prior to connecting a new one Some games do not respond to a change in controller type if 1) The controller is not disconnected prior to being reconnected and/or 2) The controller is reconnected instantly after being disconnected. Since it is not possible to change controllers instantly on hardware and requiring a disconnect prior to connecting a new one, we should emulate this as well with a small delay, fixing the aforementioned issue. --- src/yuzu/configuration/configure_input_player.cpp | 61 +++++++++++++---------- 1 file changed, 36 insertions(+), 25 deletions(-) (limited to 'src/yuzu/configuration/configure_input_player.cpp') diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 3e785c224..1ee4725ef 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -26,8 +26,6 @@ #include "yuzu/configuration/input_profiles.h" #include "yuzu/util/limitable_input_dialog.h" -constexpr std::size_t HANDHELD_INDEX = 8; - const std::array ConfigureInputPlayer::analog_sub_buttons{{ "up", @@ -38,12 +36,10 @@ const std::array namespace { +constexpr std::size_t HANDHELD_INDEX = 8; + void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, bool connected) { - auto& player = Settings::values.players.GetValue()[npad_index]; - player.controller_type = controller_type; - player.connected = connected; - Core::System& system{Core::System::GetInstance()}; if (!system.IsPoweredOn()) { return; @@ -563,35 +559,50 @@ 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(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; + + 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; + } + + // Disconnect the controller first. + UpdateController(controller_type, player_index, false); + + player.controller_type = controller_type; + player.connected = player_connected; ConfigureVibration::SetVibrationDevices(player_index); - // Player 2-8 - if (player_index != 0) { - UpdateController(player.controller_type, player_index, player.connected); - return; + // 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() && + controller_type == Settings::ControllerType::Handheld; + UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected); } - // Player 1 and Handheld - auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; - // If Handheld is selected, copy all the settings from Player 1 to Handheld. - if (player.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 + if (!player.connected) { + return; } - UpdateController(player.controller_type, player_index, player.connected); - UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected); + // 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) { -- cgit v1.2.3 From 760a9e869322cbda51416f7001842557b90754af Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Mon, 26 Oct 2020 03:28:03 -0400 Subject: applets/controller: Change the input button to create input profiles Co-authored-by: Its-Rei --- src/yuzu/configuration/configure_input_player.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/yuzu/configuration/configure_input_player.cpp') diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 1ee4725ef..4ed704793 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -459,11 +459,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"), }; -- cgit v1.2.3 From 117bdc71e016629b9355b33a6d64655f0245f833 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Tue, 27 Oct 2020 13:15:57 -0400 Subject: sdl_impl: Revert to the "old" method of mapping sticks Not all controllers have a SDL_GameController binding. This caused controllers not present in the SDL GameController database to have buttons mapped instead of axes. Furthermore, it was not possible to invert the axes when it could be useful such as emulating a horizontal single joycon or other potential cases. This allows us to invert the axes by reversing the order of mapping (vertical, then horizontal). --- src/yuzu/configuration/configure_input_player.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/yuzu/configuration/configure_input_player.cpp') diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 4ed704793..5abf9f0bf 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -370,6 +370,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) { -- cgit v1.2.3 From 97b2220a822548eed83993fceebe0e611dbec84b Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Tue, 27 Oct 2020 13:33:25 -0400 Subject: general: Fix compiler warnings on linux and miscellaneous changes --- src/yuzu/configuration/configure_input_player.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/yuzu/configuration/configure_input_player.cpp') diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 5abf9f0bf..0d10c1360 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -857,7 +858,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"))) { -- cgit v1.2.3 From 6f5b9428971904ad8815a56d50c9aab4805a1c56 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 29 Oct 2020 12:15:35 -0400 Subject: configure_input: Update the input profiles for other player tabs --- src/yuzu/configuration/configure_input_player.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'src/yuzu/configuration/configure_input_player.cpp') diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 0d10c1360..f65a7fe73 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -541,7 +541,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } }); - RefreshInputProfiles(); + UpdateInputProfiles(); connect(ui->buttonProfilesNew, &QPushButton::clicked, this, &ConfigureInputPlayer::CreateProfile); @@ -1132,10 +1132,13 @@ void ConfigureInputPlayer::CreateProfile() { 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)); - RefreshInputProfiles(); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); return; } + emit RefreshInputProfiles(player_index); + ui->comboProfiles->addItem(profile_name); ui->comboProfiles->setCurrentIndex(ui->comboProfiles->count() - 1); } @@ -1150,10 +1153,13 @@ void ConfigureInputPlayer::DeleteProfile() { if (!profiles->DeleteProfile(profile_name.toStdString())) { QMessageBox::critical(this, tr("Delete Input Profile"), tr("Failed to delete the input profile \"%1\"").arg(profile_name)); - RefreshInputProfiles(); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); return; } + emit RefreshInputProfiles(player_index); + ui->comboProfiles->removeItem(ui->comboProfiles->currentIndex()); ui->comboProfiles->setCurrentIndex(-1); } @@ -1170,7 +1176,8 @@ void ConfigureInputPlayer::LoadProfile() { 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)); - RefreshInputProfiles(); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); return; } @@ -1189,12 +1196,13 @@ void ConfigureInputPlayer::SaveProfile() { 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)); - RefreshInputProfiles(); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); return; } } -void ConfigureInputPlayer::RefreshInputProfiles() { +void ConfigureInputPlayer::UpdateInputProfiles() { ui->comboProfiles->clear(); for (const auto& profile_name : profiles->GetInputProfileNames()) { -- cgit v1.2.3 From b254d528bcdd8d826c87ef720b2c247a08cc5049 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 11 Nov 2020 07:46:41 -0500 Subject: configure_input: Accommodate for the mouse input device engine --- src/yuzu/configuration/configure_input_player.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'src/yuzu/configuration/configure_input_player.cpp') diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index f65a7fe73..72640f5e7 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -694,6 +694,8 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() { 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) { @@ -702,13 +704,22 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() { const bool all_one_device = std::all_of(buttons_param.begin(), buttons_param.end(), - [current_engine, current_guid, current_port](const Common::ParamPackage param) { + [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) { @@ -1073,6 +1084,11 @@ bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params) 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", "") && -- cgit v1.2.3