| | |
| | |
| |
|
| | #include <algorithm> |
| | #include <thread> |
| |
|
| | #include "common/assert.h" |
| | #include "common/settings.h" |
| | #include "common/settings_enums.h" |
| | #include "common/string_util.h" |
| | #include "core/core.h" |
| | #include "core/hle/service/sm/sm.h" |
| | #include "hid_core/frontend/emulated_controller.h" |
| | #include "hid_core/hid_core.h" |
| | #include "hid_core/hid_types.h" |
| | #include "hid_core/resources/npad/npad.h" |
| | #include "ui_qt_controller.h" |
| | #include "yuzu/applets/qt_controller.h" |
| | #include "yuzu/configuration/configure_input.h" |
| | #include "yuzu/configuration/configure_input_profile_dialog.h" |
| | #include "yuzu/configuration/configure_motion_touch.h" |
| | #include "yuzu/configuration/configure_vibration.h" |
| | #include "yuzu/configuration/input_profiles.h" |
| | #include "yuzu/main.h" |
| | #include "yuzu/util/controller_navigation.h" |
| |
|
| | namespace { |
| |
|
| | void UpdateController(Core::HID::EmulatedController* controller, |
| | Core::HID::NpadStyleIndex controller_type, bool connected) { |
| | if (controller->IsConnected(true)) { |
| | controller->Disconnect(); |
| | } |
| | controller->SetNpadStyleIndex(controller_type); |
| | if (connected) { |
| | controller->Connect(true); |
| | } |
| | } |
| |
|
| | |
| | bool IsControllerCompatible(Core::HID::NpadStyleIndex controller_type, |
| | Core::Frontend::ControllerParameters parameters) { |
| | switch (controller_type) { |
| | case Core::HID::NpadStyleIndex::Fullkey: |
| | return parameters.allow_pro_controller; |
| | case Core::HID::NpadStyleIndex::JoyconDual: |
| | return parameters.allow_dual_joycons; |
| | case Core::HID::NpadStyleIndex::JoyconLeft: |
| | return parameters.allow_left_joycon; |
| | case Core::HID::NpadStyleIndex::JoyconRight: |
| | return parameters.allow_right_joycon; |
| | case Core::HID::NpadStyleIndex::Handheld: |
| | return parameters.enable_single_mode && parameters.allow_handheld; |
| | case Core::HID::NpadStyleIndex::GameCube: |
| | return parameters.allow_gamecube_controller; |
| | default: |
| | return false; |
| | } |
| | } |
| |
|
| | } |
| |
|
| | QtControllerSelectorDialog::QtControllerSelectorDialog( |
| | QWidget* parent, Core::Frontend::ControllerParameters parameters_, |
| | InputCommon::InputSubsystem* input_subsystem_, Core::System& system_) |
| | : QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()), |
| | parameters(std::move(parameters_)), input_subsystem{input_subsystem_}, |
| | input_profiles(std::make_unique<InputProfiles>()), system{system_} { |
| | ui->setupUi(this); |
| |
|
| | player_widgets = { |
| | ui->widgetPlayer1, ui->widgetPlayer2, ui->widgetPlayer3, ui->widgetPlayer4, |
| | ui->widgetPlayer5, ui->widgetPlayer6, ui->widgetPlayer7, ui->widgetPlayer8, |
| | }; |
| |
|
| | player_groupboxes = { |
| | ui->groupPlayer1Connected, ui->groupPlayer2Connected, ui->groupPlayer3Connected, |
| | ui->groupPlayer4Connected, ui->groupPlayer5Connected, ui->groupPlayer6Connected, |
| | ui->groupPlayer7Connected, ui->groupPlayer8Connected, |
| | }; |
| |
|
| | connected_controller_icons = { |
| | ui->controllerPlayer1, ui->controllerPlayer2, ui->controllerPlayer3, ui->controllerPlayer4, |
| | ui->controllerPlayer5, ui->controllerPlayer6, ui->controllerPlayer7, ui->controllerPlayer8, |
| | }; |
| |
|
| | led_patterns_boxes = {{ |
| | {ui->checkboxPlayer1LED1, ui->checkboxPlayer1LED2, ui->checkboxPlayer1LED3, |
| | ui->checkboxPlayer1LED4}, |
| | {ui->checkboxPlayer2LED1, ui->checkboxPlayer2LED2, ui->checkboxPlayer2LED3, |
| | ui->checkboxPlayer2LED4}, |
| | {ui->checkboxPlayer3LED1, ui->checkboxPlayer3LED2, ui->checkboxPlayer3LED3, |
| | ui->checkboxPlayer3LED4}, |
| | {ui->checkboxPlayer4LED1, ui->checkboxPlayer4LED2, ui->checkboxPlayer4LED3, |
| | ui->checkboxPlayer4LED4}, |
| | {ui->checkboxPlayer5LED1, ui->checkboxPlayer5LED2, ui->checkboxPlayer5LED3, |
| | ui->checkboxPlayer5LED4}, |
| | {ui->checkboxPlayer6LED1, ui->checkboxPlayer6LED2, ui->checkboxPlayer6LED3, |
| | ui->checkboxPlayer6LED4}, |
| | {ui->checkboxPlayer7LED1, ui->checkboxPlayer7LED2, ui->checkboxPlayer7LED3, |
| | ui->checkboxPlayer7LED4}, |
| | {ui->checkboxPlayer8LED1, ui->checkboxPlayer8LED2, ui->checkboxPlayer8LED3, |
| | ui->checkboxPlayer8LED4}, |
| | }}; |
| |
|
| | explain_text_labels = { |
| | ui->labelPlayer1Explain, ui->labelPlayer2Explain, ui->labelPlayer3Explain, |
| | ui->labelPlayer4Explain, ui->labelPlayer5Explain, ui->labelPlayer6Explain, |
| | ui->labelPlayer7Explain, ui->labelPlayer8Explain, |
| | }; |
| |
|
| | emulated_controllers = { |
| | ui->comboPlayer1Emulated, ui->comboPlayer2Emulated, ui->comboPlayer3Emulated, |
| | ui->comboPlayer4Emulated, ui->comboPlayer5Emulated, ui->comboPlayer6Emulated, |
| | ui->comboPlayer7Emulated, ui->comboPlayer8Emulated, |
| | }; |
| |
|
| | player_labels = { |
| | ui->labelPlayer1, ui->labelPlayer2, ui->labelPlayer3, ui->labelPlayer4, |
| | ui->labelPlayer5, ui->labelPlayer6, ui->labelPlayer7, ui->labelPlayer8, |
| | }; |
| |
|
| | connected_controller_labels = { |
| | ui->labelConnectedPlayer1, ui->labelConnectedPlayer2, ui->labelConnectedPlayer3, |
| | ui->labelConnectedPlayer4, ui->labelConnectedPlayer5, ui->labelConnectedPlayer6, |
| | ui->labelConnectedPlayer7, ui->labelConnectedPlayer8, |
| | }; |
| |
|
| | connected_controller_checkboxes = { |
| | ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected, |
| | ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected, |
| | ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected, |
| | }; |
| |
|
| | ui->labelError->setVisible(false); |
| |
|
| | |
| | |
| | SetSupportedControllers(); |
| | DisableUnsupportedPlayers(); |
| |
|
| | for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) { |
| | SetEmulatedControllers(player_index); |
| | } |
| |
|
| | LoadConfiguration(); |
| |
|
| | controller_navigation = new ControllerNavigation(system.HIDCore(), this); |
| |
|
| | for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { |
| | SetExplainText(i); |
| | UpdateControllerIcon(i); |
| | UpdateLEDPattern(i); |
| | UpdateBorderColor(i); |
| |
|
| | connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) { |
| | |
| | |
| | const bool reconnect_first = !checked && i < player_groupboxes.size() - 1 && |
| | player_groupboxes[i + 1]->isChecked(); |
| |
|
| | |
| | if (connected_controller_checkboxes[i]->isChecked() != checked) { |
| | |
| | PropagatePlayerNumberChanged(i, checked, reconnect_first); |
| | } |
| | }); |
| | connect(connected_controller_checkboxes[i], &QCheckBox::clicked, [this, i](bool checked) { |
| | |
| | |
| | const bool reconnect_first = !checked && |
| | i < connected_controller_checkboxes.size() - 1 && |
| | connected_controller_checkboxes[i + 1]->isChecked(); |
| |
|
| | |
| | PropagatePlayerNumberChanged(i, checked, reconnect_first); |
| | }); |
| |
|
| | connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged), |
| | [this, i](int) { |
| | UpdateControllerIcon(i); |
| | UpdateControllerState(i); |
| | UpdateLEDPattern(i); |
| | CheckIfParametersMet(); |
| | }); |
| |
|
| | connect(connected_controller_checkboxes[i], &QCheckBox::stateChanged, [this, i](int state) { |
| | player_groupboxes[i]->setChecked(state == Qt::Checked); |
| | UpdateControllerIcon(i); |
| | UpdateControllerState(i); |
| | UpdateLEDPattern(i); |
| | UpdateBorderColor(i); |
| | CheckIfParametersMet(); |
| | }); |
| |
|
| | if (i == 0) { |
| | connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged), |
| | [this, i](int index) { |
| | UpdateDockedState(GetControllerTypeFromIndex(index, i) == |
| | Core::HID::NpadStyleIndex::Handheld); |
| | }); |
| | } |
| | } |
| |
|
| | connect(ui->vibrationButton, &QPushButton::clicked, this, |
| | &QtControllerSelectorDialog::CallConfigureVibrationDialog); |
| |
|
| | connect(ui->motionButton, &QPushButton::clicked, this, |
| | &QtControllerSelectorDialog::CallConfigureMotionTouchDialog); |
| |
|
| | connect(ui->inputConfigButton, &QPushButton::clicked, this, |
| | &QtControllerSelectorDialog::CallConfigureInputProfileDialog); |
| |
|
| | connect(ui->buttonBox, &QDialogButtonBox::accepted, this, |
| | &QtControllerSelectorDialog::ApplyConfiguration); |
| |
|
| | connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, |
| | [this](Qt::Key key) { |
| | QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); |
| | QCoreApplication::postEvent(this, event); |
| | }); |
| |
|
| | |
| | |
| | |
| | if (CheckIfParametersMet() && parameters.enable_single_mode) { |
| | return; |
| | } |
| |
|
| | |
| | if (!parameters.keep_controllers_connected) { |
| | for (auto player : player_groupboxes) { |
| | player->setChecked(false); |
| | } |
| | } |
| |
|
| | resize(0, 0); |
| | } |
| |
|
| | QtControllerSelectorDialog::~QtControllerSelectorDialog() { |
| | controller_navigation->UnloadController(); |
| | system.HIDCore().DisableAllControllerConfiguration(); |
| | } |
| |
|
| | int QtControllerSelectorDialog::exec() { |
| | if (parameters_met && parameters.enable_single_mode) { |
| | return QDialog::Accepted; |
| | } |
| | return QDialog::exec(); |
| | } |
| |
|
| | void QtControllerSelectorDialog::ApplyConfiguration() { |
| | const bool pre_docked_mode = Settings::IsDockedMode(); |
| | const bool docked_mode_selected = ui->radioDocked->isChecked(); |
| | Settings::values.use_docked_mode.SetValue( |
| | docked_mode_selected ? Settings::ConsoleMode::Docked : Settings::ConsoleMode::Handheld); |
| | OnDockedModeChanged(pre_docked_mode, docked_mode_selected, system); |
| |
|
| | Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); |
| | Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); |
| | } |
| |
|
| | void QtControllerSelectorDialog::LoadConfiguration() { |
| | system.HIDCore().EnableAllControllerConfiguration(); |
| |
|
| | const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); |
| | for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { |
| | const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index); |
| | const auto connected = |
| | controller->IsConnected(true) || (index == 0 && handheld->IsConnected(true)); |
| | player_groupboxes[index]->setChecked(connected); |
| | connected_controller_checkboxes[index]->setChecked(connected); |
| | emulated_controllers[index]->setCurrentIndex( |
| | GetIndexFromControllerType(controller->GetNpadStyleIndex(true), index)); |
| | } |
| |
|
| | UpdateDockedState(handheld->IsConnected(true)); |
| |
|
| | ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); |
| | ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); |
| | } |
| |
|
| | void QtControllerSelectorDialog::CallConfigureVibrationDialog() { |
| | ConfigureVibration dialog(this, system.HIDCore()); |
| |
|
| | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | |
| | Qt::WindowSystemMenuHint); |
| | dialog.setWindowModality(Qt::WindowModal); |
| |
|
| | if (dialog.exec() == QDialog::Accepted) { |
| | dialog.ApplyConfiguration(); |
| | } |
| | } |
| |
|
| | void QtControllerSelectorDialog::CallConfigureMotionTouchDialog() { |
| | ConfigureMotionTouch dialog(this, input_subsystem); |
| |
|
| | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | |
| | Qt::WindowSystemMenuHint); |
| | dialog.setWindowModality(Qt::WindowModal); |
| |
|
| | if (dialog.exec() == QDialog::Accepted) { |
| | dialog.ApplyConfiguration(); |
| | } |
| | } |
| |
|
| | void QtControllerSelectorDialog::CallConfigureInputProfileDialog() { |
| | ConfigureInputProfileDialog dialog(this, input_subsystem, input_profiles.get(), system); |
| |
|
| | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | |
| | Qt::WindowSystemMenuHint); |
| | dialog.setWindowModality(Qt::WindowModal); |
| | dialog.exec(); |
| | } |
| |
|
| | void QtControllerSelectorDialog::keyPressEvent(QKeyEvent* evt) { |
| | const auto num_connected_players = static_cast<int>( |
| | std::count_if(player_groupboxes.begin(), player_groupboxes.end(), |
| | [](const QGroupBox* player) { return player->isChecked(); })); |
| |
|
| | const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players; |
| | const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players; |
| |
|
| | if ((evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) && !parameters_met) { |
| | |
| | ui->labelError->setVisible(true); |
| | return; |
| | } else if (evt->key() == Qt::Key_Left && num_connected_players > min_supported_players) { |
| | |
| | connected_controller_checkboxes[num_connected_players - 1]->setChecked(false); |
| | return; |
| | } else if (evt->key() == Qt::Key_Right && num_connected_players < max_supported_players) { |
| | |
| | ui->labelError->setVisible(false); |
| | connected_controller_checkboxes[num_connected_players]->setChecked(true); |
| | return; |
| | } |
| | QDialog::keyPressEvent(evt); |
| | } |
| |
|
| | bool QtControllerSelectorDialog::CheckIfParametersMet() { |
| | |
| | const auto num_connected_players = static_cast<int>( |
| | std::count_if(player_groupboxes.begin(), player_groupboxes.end(), |
| | [](const QGroupBox* player) { return player->isChecked(); })); |
| |
|
| | const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players; |
| | const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players; |
| |
|
| | |
| | if (num_connected_players < min_supported_players || |
| | num_connected_players > max_supported_players) { |
| | parameters_met = false; |
| | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(parameters_met); |
| | return parameters_met; |
| | } |
| |
|
| | |
| | const auto all_controllers_compatible = [this] { |
| | for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { |
| | |
| | if (!player_groupboxes[index]->isChecked() || !player_groupboxes[index]->isEnabled()) { |
| | continue; |
| | } |
| |
|
| | const auto compatible = IsControllerCompatible( |
| | GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex(), index), |
| | parameters); |
| |
|
| | |
| | if (!compatible) { |
| | return false; |
| | } |
| | } |
| |
|
| | |
| | return true; |
| | }(); |
| |
|
| | parameters_met = all_controllers_compatible; |
| | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(parameters_met); |
| | return parameters_met; |
| | } |
| |
|
| | void QtControllerSelectorDialog::SetSupportedControllers() { |
| | const QString theme = [] { |
| | if (QIcon::themeName().contains(QStringLiteral("dark"))) { |
| | return QStringLiteral("_dark"); |
| | } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) { |
| | return QStringLiteral("_midnight"); |
| | } else { |
| | return QString{}; |
| | } |
| | }(); |
| |
|
| | if (parameters.enable_single_mode && parameters.allow_handheld) { |
| | ui->controllerSupported1->setStyleSheet( |
| | QStringLiteral("image: url(:/controller/applet_handheld%0); ").arg(theme)); |
| | } else { |
| | ui->controllerSupported1->setStyleSheet( |
| | QStringLiteral("image: url(:/controller/applet_handheld%0_disabled); ").arg(theme)); |
| | } |
| |
|
| | if (parameters.allow_dual_joycons) { |
| | ui->controllerSupported2->setStyleSheet( |
| | QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ").arg(theme)); |
| | } else { |
| | ui->controllerSupported2->setStyleSheet( |
| | QStringLiteral("image: url(:/controller/applet_dual_joycon%0_disabled); ").arg(theme)); |
| | } |
| |
|
| | if (parameters.allow_left_joycon) { |
| | ui->controllerSupported3->setStyleSheet( |
| | QStringLiteral("image: url(:/controller/applet_joycon_left%0); ").arg(theme)); |
| | } else { |
| | ui->controllerSupported3->setStyleSheet( |
| | QStringLiteral("image: url(:/controller/applet_joycon_left%0_disabled); ").arg(theme)); |
| | } |
| |
|
| | if (parameters.allow_right_joycon) { |
| | ui->controllerSupported4->setStyleSheet( |
| | QStringLiteral("image: url(:/controller/applet_joycon_right%0); ").arg(theme)); |
| | } else { |
| | ui->controllerSupported4->setStyleSheet( |
| | QStringLiteral("image: url(:/controller/applet_joycon_right%0_disabled); ").arg(theme)); |
| | } |
| |
|
| | if (parameters.allow_pro_controller || parameters.allow_gamecube_controller) { |
| | ui->controllerSupported5->setStyleSheet( |
| | QStringLiteral("image: url(:/controller/applet_pro_controller%0); ").arg(theme)); |
| | } else { |
| | ui->controllerSupported5->setStyleSheet( |
| | QStringLiteral("image: url(:/controller/applet_pro_controller%0_disabled); ") |
| | .arg(theme)); |
| | } |
| |
|
| | |
| | if (parameters.enable_single_mode) { |
| | ui->numberSupportedLabel->setText(QStringLiteral("1")); |
| | return; |
| | } |
| |
|
| | if (parameters.min_players == parameters.max_players) { |
| | ui->numberSupportedLabel->setText(QStringLiteral("%1").arg(parameters.max_players)); |
| | } else { |
| | ui->numberSupportedLabel->setText( |
| | QStringLiteral("%1 - %2").arg(parameters.min_players).arg(parameters.max_players)); |
| | } |
| | } |
| |
|
| | void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index) { |
| | const auto npad_style_set = system.HIDCore().GetSupportedStyleTag(); |
| | auto& pairs = index_controller_type_pairs[player_index]; |
| |
|
| | pairs.clear(); |
| | emulated_controllers[player_index]->clear(); |
| |
|
| | const auto add_item = [&](Core::HID::NpadStyleIndex controller_type, |
| | const QString& controller_name) { |
| | pairs.emplace_back(emulated_controllers[player_index]->count(), controller_type); |
| | emulated_controllers[player_index]->addItem(controller_name); |
| | }; |
| |
|
| | if (npad_style_set.fullkey == 1) { |
| | add_item(Core::HID::NpadStyleIndex::Fullkey, tr("Pro Controller")); |
| | } |
| |
|
| | if (npad_style_set.joycon_dual == 1) { |
| | add_item(Core::HID::NpadStyleIndex::JoyconDual, tr("Dual Joycons")); |
| | } |
| |
|
| | if (npad_style_set.joycon_left == 1) { |
| | add_item(Core::HID::NpadStyleIndex::JoyconLeft, tr("Left Joycon")); |
| | } |
| |
|
| | if (npad_style_set.joycon_right == 1) { |
| | add_item(Core::HID::NpadStyleIndex::JoyconRight, tr("Right Joycon")); |
| | } |
| |
|
| | if (player_index == 0 && npad_style_set.handheld == 1) { |
| | add_item(Core::HID::NpadStyleIndex::Handheld, tr("Handheld")); |
| | } |
| |
|
| | if (npad_style_set.gamecube == 1) { |
| | add_item(Core::HID::NpadStyleIndex::GameCube, tr("GameCube Controller")); |
| | } |
| |
|
| | |
| | if (!Settings::values.enable_all_controllers) { |
| | return; |
| | } |
| |
|
| | if (npad_style_set.palma == 1) { |
| | add_item(Core::HID::NpadStyleIndex::Pokeball, tr("Poke Ball Plus")); |
| | } |
| |
|
| | if (npad_style_set.lark == 1) { |
| | add_item(Core::HID::NpadStyleIndex::NES, tr("NES Controller")); |
| | } |
| |
|
| | if (npad_style_set.lucia == 1) { |
| | add_item(Core::HID::NpadStyleIndex::SNES, tr("SNES Controller")); |
| | } |
| |
|
| | if (npad_style_set.lagoon == 1) { |
| | add_item(Core::HID::NpadStyleIndex::N64, tr("N64 Controller")); |
| | } |
| |
|
| | if (npad_style_set.lager == 1) { |
| | add_item(Core::HID::NpadStyleIndex::SegaGenesis, tr("Sega Genesis")); |
| | } |
| | } |
| |
|
| | Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex( |
| | int index, std::size_t player_index) const { |
| | const auto& pairs = index_controller_type_pairs[player_index]; |
| |
|
| | const auto it = std::find_if(pairs.begin(), pairs.end(), |
| | [index](const auto& pair) { return pair.first == index; }); |
| |
|
| | if (it == pairs.end()) { |
| | return Core::HID::NpadStyleIndex::Fullkey; |
| | } |
| |
|
| | return it->second; |
| | } |
| |
|
| | int QtControllerSelectorDialog::GetIndexFromControllerType(Core::HID::NpadStyleIndex type, |
| | std::size_t player_index) const { |
| | const auto& pairs = index_controller_type_pairs[player_index]; |
| |
|
| | const auto it = std::find_if(pairs.begin(), pairs.end(), |
| | [type](const auto& pair) { return pair.second == type; }); |
| |
|
| | if (it == pairs.end()) { |
| | return 0; |
| | } |
| |
|
| | return it->first; |
| | } |
| |
|
| | void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) { |
| | if (!player_groupboxes[player_index]->isChecked()) { |
| | connected_controller_icons[player_index]->setStyleSheet(QString{}); |
| | player_labels[player_index]->show(); |
| | return; |
| | } |
| |
|
| | const QString stylesheet = [this, player_index] { |
| | switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), |
| | player_index)) { |
| | case Core::HID::NpadStyleIndex::Fullkey: |
| | case Core::HID::NpadStyleIndex::GameCube: |
| | return QStringLiteral("image: url(:/controller/applet_pro_controller%0); "); |
| | case Core::HID::NpadStyleIndex::JoyconDual: |
| | return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); "); |
| | case Core::HID::NpadStyleIndex::JoyconLeft: |
| | return QStringLiteral("image: url(:/controller/applet_joycon_left%0); "); |
| | case Core::HID::NpadStyleIndex::JoyconRight: |
| | return QStringLiteral("image: url(:/controller/applet_joycon_right%0); "); |
| | case Core::HID::NpadStyleIndex::Handheld: |
| | return QStringLiteral("image: url(:/controller/applet_handheld%0); "); |
| | default: |
| | return QString{}; |
| | } |
| | }(); |
| |
|
| | if (stylesheet.isEmpty()) { |
| | connected_controller_icons[player_index]->setStyleSheet(QString{}); |
| | player_labels[player_index]->show(); |
| | return; |
| | } |
| |
|
| | const QString theme = [] { |
| | if (QIcon::themeName().contains(QStringLiteral("dark"))) { |
| | return QStringLiteral("_dark"); |
| | } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) { |
| | return QStringLiteral("_midnight"); |
| | } else { |
| | return QString{}; |
| | } |
| | }(); |
| |
|
| | connected_controller_icons[player_index]->setStyleSheet(stylesheet.arg(theme)); |
| | player_labels[player_index]->hide(); |
| | } |
| |
|
| | void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) { |
| | auto* controller = system.HIDCore().GetEmulatedControllerByIndex(player_index); |
| |
|
| | const auto controller_type = GetControllerTypeFromIndex( |
| | emulated_controllers[player_index]->currentIndex(), player_index); |
| | const auto player_connected = player_groupboxes[player_index]->isChecked() && |
| | controller_type != Core::HID::NpadStyleIndex::Handheld; |
| |
|
| | if (controller->GetNpadStyleIndex(true) == controller_type && |
| | controller->IsConnected(true) == player_connected) { |
| | return; |
| | } |
| |
|
| | |
| | UpdateController(controller, controller_type, false); |
| |
|
| | |
| | if (player_index == 0) { |
| | if (controller_type == Core::HID::NpadStyleIndex::Handheld) { |
| | auto* handheld = |
| | system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); |
| | UpdateController(handheld, Core::HID::NpadStyleIndex::Handheld, |
| | player_groupboxes[player_index]->isChecked()); |
| | } |
| | } |
| |
|
| | UpdateController(controller, controller_type, player_connected); |
| | } |
| |
|
| | void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) { |
| | if (!player_groupboxes[player_index]->isChecked() || |
| | GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), |
| | player_index) == Core::HID::NpadStyleIndex::Handheld) { |
| | led_patterns_boxes[player_index][0]->setChecked(false); |
| | led_patterns_boxes[player_index][1]->setChecked(false); |
| | led_patterns_boxes[player_index][2]->setChecked(false); |
| | led_patterns_boxes[player_index][3]->setChecked(false); |
| | return; |
| | } |
| |
|
| | const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(player_index); |
| | const auto led_pattern = controller->GetLedPattern(); |
| | led_patterns_boxes[player_index][0]->setChecked(led_pattern.position1); |
| | led_patterns_boxes[player_index][1]->setChecked(led_pattern.position2); |
| | led_patterns_boxes[player_index][2]->setChecked(led_pattern.position3); |
| | led_patterns_boxes[player_index][3]->setChecked(led_pattern.position4); |
| | } |
| |
|
| | void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) { |
| | if (!parameters.enable_border_color || |
| | player_index >= static_cast<std::size_t>(parameters.max_players) || |
| | player_groupboxes[player_index]->styleSheet().contains(QStringLiteral("QGroupBox"))) { |
| | return; |
| | } |
| |
|
| | player_groupboxes[player_index]->setStyleSheet( |
| | player_groupboxes[player_index]->styleSheet().append( |
| | QStringLiteral("QGroupBox#groupPlayer%1Connected:checked " |
| | "{ border: 1px solid rgba(%2, %3, %4, %5); }") |
| | .arg(player_index + 1) |
| | .arg(parameters.border_colors[player_index][0]) |
| | .arg(parameters.border_colors[player_index][1]) |
| | .arg(parameters.border_colors[player_index][2]) |
| | .arg(parameters.border_colors[player_index][3]))); |
| | } |
| |
|
| | void QtControllerSelectorDialog::SetExplainText(std::size_t player_index) { |
| | if (!parameters.enable_explain_text || |
| | player_index >= static_cast<std::size_t>(parameters.max_players)) { |
| | return; |
| | } |
| |
|
| | explain_text_labels[player_index]->setText(QString::fromStdString( |
| | Common::StringFromFixedZeroTerminatedBuffer(parameters.explain_text[player_index].data(), |
| | parameters.explain_text[player_index].size()))); |
| | } |
| |
|
| | void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) { |
| | |
| | ui->radioDocked->setEnabled(!is_handheld); |
| | ui->radioUndocked->setEnabled(!is_handheld); |
| |
|
| | ui->radioDocked->setChecked(Settings::IsDockedMode()); |
| | ui->radioUndocked->setChecked(!Settings::IsDockedMode()); |
| |
|
| | |
| | if (is_handheld) { |
| | ui->radioUndocked->setChecked(true); |
| | } |
| | } |
| |
|
| | void QtControllerSelectorDialog::PropagatePlayerNumberChanged(size_t player_index, bool checked, |
| | bool reconnect_current) { |
| | connected_controller_checkboxes[player_index]->setChecked(checked); |
| | |
| | ui->labelError->setVisible(false); |
| |
|
| | if (checked) { |
| | |
| | if (player_index > 0) { |
| | PropagatePlayerNumberChanged(player_index - 1, checked); |
| | } |
| | } else { |
| | |
| | if (player_index < connected_controller_checkboxes.size() - 1) { |
| | PropagatePlayerNumberChanged(player_index + 1, checked); |
| | } |
| | } |
| |
|
| | if (reconnect_current) { |
| | connected_controller_checkboxes[player_index]->setCheckState(Qt::Checked); |
| | } |
| | } |
| |
|
| | void QtControllerSelectorDialog::DisableUnsupportedPlayers() { |
| | const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players; |
| |
|
| | switch (max_supported_players) { |
| | case 0: |
| | default: |
| | ASSERT(false); |
| | return; |
| | case 1: |
| | ui->widgetSpacer->hide(); |
| | ui->widgetSpacer2->hide(); |
| | ui->widgetSpacer3->hide(); |
| | ui->widgetSpacer4->hide(); |
| | break; |
| | case 2: |
| | ui->widgetSpacer->hide(); |
| | ui->widgetSpacer2->hide(); |
| | ui->widgetSpacer3->hide(); |
| | break; |
| | case 3: |
| | ui->widgetSpacer->hide(); |
| | ui->widgetSpacer2->hide(); |
| | break; |
| | case 4: |
| | ui->widgetSpacer->hide(); |
| | break; |
| | case 5: |
| | case 6: |
| | case 7: |
| | case 8: |
| | break; |
| | } |
| |
|
| | for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) { |
| | auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index); |
| | |
| | UpdateController(controller, controller->GetNpadStyleIndex(true), false); |
| | |
| | if (max_supported_players <= 4) { |
| | player_widgets[index]->hide(); |
| | } |
| |
|
| | |
| | player_widgets[index]->setDisabled(true); |
| | connected_controller_checkboxes[index]->setDisabled(true); |
| | connected_controller_labels[index]->hide(); |
| | connected_controller_checkboxes[index]->hide(); |
| | } |
| | } |
| |
|
| | QtControllerSelector::QtControllerSelector(GMainWindow& parent) { |
| | connect(this, &QtControllerSelector::MainWindowReconfigureControllers, &parent, |
| | &GMainWindow::ControllerSelectorReconfigureControllers, Qt::QueuedConnection); |
| | connect(this, &QtControllerSelector::MainWindowRequestExit, &parent, |
| | &GMainWindow::ControllerSelectorRequestExit, Qt::QueuedConnection); |
| | connect(&parent, &GMainWindow::ControllerSelectorReconfigureFinished, this, |
| | &QtControllerSelector::MainWindowReconfigureFinished, Qt::QueuedConnection); |
| | } |
| |
|
| | QtControllerSelector::~QtControllerSelector() = default; |
| |
|
| | void QtControllerSelector::Close() const { |
| | callback = {}; |
| | emit MainWindowRequestExit(); |
| | } |
| |
|
| | void QtControllerSelector::ReconfigureControllers( |
| | ReconfigureCallback callback_, const Core::Frontend::ControllerParameters& parameters) const { |
| | callback = std::move(callback_); |
| | emit MainWindowReconfigureControllers(parameters); |
| | } |
| |
|
| | void QtControllerSelector::MainWindowReconfigureFinished(bool is_success) { |
| | if (callback) { |
| | callback(is_success); |
| | } |
| | } |
| |
|