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.
master
Morph 2020-10-23 12:09:28 +07:00
parent 978ca65f59
commit 91c06dae1a
2 changed files with 74 additions and 51 deletions

@ -19,6 +19,8 @@
namespace { namespace {
constexpr std::size_t HANDHELD_INDEX = 8;
constexpr std::array<std::array<bool, 4>, 8> led_patterns{{ constexpr std::array<std::array<bool, 4>, 8> led_patterns{{
{true, false, false, false}, {true, false, false, false},
{true, true, false, false}, {true, true, false, false},
@ -260,11 +262,6 @@ int QtControllerSelectorDialog::exec() {
} }
void QtControllerSelectorDialog::ApplyConfiguration() { void QtControllerSelectorDialog::ApplyConfiguration() {
// Update the controller state once more, just to be sure they are properly applied.
for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
UpdateControllerState(index);
}
const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked()); Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked());
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue()); OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue());
@ -275,15 +272,16 @@ void QtControllerSelectorDialog::ApplyConfiguration() {
void QtControllerSelectorDialog::LoadConfiguration() { void QtControllerSelectorDialog::LoadConfiguration() {
for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
const auto connected = Settings::values.players.GetValue()[index].connected || const auto connected =
(index == 0 && Settings::values.players.GetValue()[8].connected); Settings::values.players.GetValue()[index].connected ||
(index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
player_groupboxes[index]->setChecked(connected); player_groupboxes[index]->setChecked(connected);
connected_controller_checkboxes[index]->setChecked(connected); connected_controller_checkboxes[index]->setChecked(connected);
emulated_controllers[index]->setCurrentIndex( emulated_controllers[index]->setCurrentIndex(
GetIndexFromControllerType(Settings::values.players.GetValue()[index].controller_type)); GetIndexFromControllerType(Settings::values.players.GetValue()[index].controller_type));
} }
UpdateDockedState(Settings::values.players.GetValue()[8].connected); UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
@ -468,32 +466,46 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) { void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
auto& player = Settings::values.players.GetValue()[player_index]; auto& player = Settings::values.players.GetValue()[player_index];
player.controller_type = const auto controller_type =
GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()); GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex());
player.connected = player_groupboxes[player_index]->isChecked(); const auto player_connected = player_groupboxes[player_index]->isChecked() &&
controller_type != Settings::ControllerType::Handheld;
// Player 2-8 if (player.controller_type == controller_type && player.connected == player_connected) {
if (player_index != 0) { // Set vibration devices in the event that the input device has changed.
UpdateController(player.controller_type, player_index, player.connected); ConfigureVibration::SetVibrationDevices(player_index);
return; return;
} }
// Disconnect the controller first.
UpdateController(controller_type, player_index, false);
player.controller_type = controller_type;
player.connected = player_connected;
ConfigureVibration::SetVibrationDevices(player_index); ConfigureVibration::SetVibrationDevices(player_index);
// Player 1 and Handheld // Handheld
auto& handheld = Settings::values.players.GetValue()[8]; if (player_index == 0) {
// If Handheld is selected, copy all the settings from Player 1 to Handheld. auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
if (player.controller_type == Settings::ControllerType::Handheld) { if (controller_type == Settings::ControllerType::Handheld) {
handheld = player; handheld = player;
handheld.connected = player_groupboxes[player_index]->isChecked(); }
player.connected = false; // Disconnect Player 1 handheld.connected = player_groupboxes[player_index]->isChecked() &&
} else { controller_type == Settings::ControllerType::Handheld;
player.connected = player_groupboxes[player_index]->isChecked(); UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected);
handheld.connected = false; // Disconnect Handheld
} }
UpdateController(player.controller_type, player_index, player.connected); if (!player.connected) {
UpdateController(Settings::ControllerType::Handheld, 8, handheld.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 QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) { void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {

@ -26,8 +26,6 @@
#include "yuzu/configuration/input_profiles.h" #include "yuzu/configuration/input_profiles.h"
#include "yuzu/util/limitable_input_dialog.h" #include "yuzu/util/limitable_input_dialog.h"
constexpr std::size_t HANDHELD_INDEX = 8;
const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
ConfigureInputPlayer::analog_sub_buttons{{ ConfigureInputPlayer::analog_sub_buttons{{
"up", "up",
@ -38,12 +36,10 @@ const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
namespace { namespace {
constexpr std::size_t HANDHELD_INDEX = 8;
void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
bool connected) { bool connected) {
auto& player = Settings::values.players.GetValue()[npad_index];
player.controller_type = controller_type;
player.connected = connected;
Core::System& system{Core::System::GetInstance()}; Core::System& system{Core::System::GetInstance()};
if (!system.IsPoweredOn()) { if (!system.IsPoweredOn()) {
return; return;
@ -563,35 +559,50 @@ void ConfigureInputPlayer::ApplyConfiguration() {
} }
auto& motions = player.motions; auto& motions = player.motions;
std::transform(motions_param.begin(), motions_param.end(), motions.begin(), std::transform(motions_param.begin(), motions_param.end(), motions.begin(),
[](const Common::ParamPackage& param) { return param.Serialize(); }); [](const Common::ParamPackage& param) { return param.Serialize(); });
player.controller_type = const auto controller_type =
static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex()); GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
player.connected = ui->groupConnectedController->isChecked(); const auto player_connected = ui->groupConnectedController->isChecked() &&
controller_type != Settings::ControllerType::Handheld;
ConfigureVibration::SetVibrationDevices(player_index); if (player.controller_type == controller_type && player.connected == player_connected) {
// Set vibration devices in the event that the input device has changed.
// Player 2-8 ConfigureVibration::SetVibrationDevices(player_index);
if (player_index != 0) {
UpdateController(player.controller_type, player_index, player.connected);
return; return;
} }
// Player 1 and Handheld // Disconnect the controller first.
auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; UpdateController(controller_type, player_index, false);
// If Handheld is selected, copy all the settings from Player 1 to Handheld.
if (player.controller_type == Settings::ControllerType::Handheld) { player.controller_type = controller_type;
handheld = player; player.connected = player_connected;
handheld.connected = ui->groupConnectedController->isChecked();
player.connected = false; // Disconnect Player 1 ConfigureVibration::SetVibrationDevices(player_index);
} else {
player.connected = ui->groupConnectedController->isChecked(); // Handheld
handheld.connected = false; // Disconnect 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);
} }
UpdateController(player.controller_type, player_index, player.connected); if (!player.connected) {
UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.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) { void ConfigureInputPlayer::showEvent(QShowEvent* event) {