From 102630f2b29b654146a50c8075316eefadbff763 Mon Sep 17 00:00:00 2001
From: Morph <39850852+Morph1984@users.noreply.github.com>
Date: Fri, 20 Nov 2020 09:48:26 -0500
Subject: [PATCH 1/2] configure_input_player: Use the npad style set to show
 the available controllers

This will reduce the likelihood of an invalid controller type to be set within a game
---
 .../configuration/configure_input_player.cpp  | 138 +++++++++++-------
 .../configuration/configure_input_player.h    |  13 ++
 2 files changed, 96 insertions(+), 55 deletions(-)

diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 56ab32a35c..918bfb56b9 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -27,6 +27,8 @@
 #include "yuzu/configuration/input_profiles.h"
 #include "yuzu/util/limitable_input_dialog.h"
 
+using namespace Service::HID;
+
 const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
     ConfigureInputPlayer::analog_sub_buttons{{
         "up",
@@ -47,48 +49,12 @@ void UpdateController(Settings::ControllerType controller_type, std::size_t npad
     }
     Service::SM::ServiceManager& sm = system.ServiceManager();
 
-    auto& npad =
-        sm.GetService<Service::HID::Hid>("hid")
-            ->GetAppletResource()
-            ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
+    auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>(
+        HidController::NPad);
 
     npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
 }
 
-/// Maps the controller type combobox index to Controller Type enum
-constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
-    switch (index) {
-    case 0:
-    default:
-        return Settings::ControllerType::ProController;
-    case 1:
-        return Settings::ControllerType::DualJoyconDetached;
-    case 2:
-        return Settings::ControllerType::LeftJoycon;
-    case 3:
-        return Settings::ControllerType::RightJoycon;
-    case 4:
-        return Settings::ControllerType::Handheld;
-    }
-}
-
-/// Maps the Controller Type enum to controller type combobox index
-constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
-    switch (type) {
-    case Settings::ControllerType::ProController:
-    default:
-        return 0;
-    case Settings::ControllerType::DualJoyconDetached:
-        return 1;
-    case Settings::ControllerType::LeftJoycon:
-        return 2;
-    case Settings::ControllerType::RightJoycon:
-        return 3;
-    case Settings::ControllerType::Handheld:
-        return 4;
-    }
-}
-
 QString GetKeyName(int key_code) {
     switch (key_code) {
     case Qt::LeftButton:
@@ -453,18 +419,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
     connect(ui->groupConnectedController, &QGroupBox::toggled,
             [this](bool checked) { emit Connected(checked); });
 
-    // Set up controller type. Only Player 1 can choose Handheld.
-    ui->comboControllerType->clear();
-
-    QStringList controller_types = {
-        tr("Pro Controller"),
-        tr("Dual Joycons"),
-        tr("Left Joycon"),
-        tr("Right Joycon"),
-    };
-
     if (player_index == 0) {
-        controller_types.append(tr("Handheld"));
         connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
                 [this](int index) {
                     emit HandheldStateChanged(GetControllerTypeFromIndex(index) ==
@@ -480,12 +435,9 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
     if (debug) {
         ui->buttonScreenshot->setEnabled(false);
         ui->buttonHome->setEnabled(false);
-        QStringList debug_controller_types = {
-            tr("Pro Controller"),
-        };
-        ui->comboControllerType->addItems(debug_controller_types);
+        ui->comboControllerType->addItem(tr("Pro Controller"));
     } else {
-        ui->comboControllerType->addItems(controller_types);
+        SetConnectableControllers();
     }
 
     UpdateControllerIcon();
@@ -667,7 +619,7 @@ void ConfigureInputPlayer::LoadConfiguration() {
         return;
     }
 
-    ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type));
+    ui->comboControllerType->setCurrentIndex(GetIndexFromControllerType(player.controller_type));
     ui->groupConnectedController->setChecked(
         player.connected ||
         (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected));
@@ -841,6 +793,82 @@ void ConfigureInputPlayer::UpdateUI() {
     }
 }
 
+void ConfigureInputPlayer::SetConnectableControllers() {
+    const auto add_controllers = [this](bool enable_all,
+                                        Controller_NPad::NpadStyleSet npad_style_set = {}) {
+        index_controller_type_pairs.clear();
+        ui->comboControllerType->clear();
+
+        if (enable_all || npad_style_set.pro_controller == 1) {
+            index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+                                                     Settings::ControllerType::ProController);
+            ui->comboControllerType->addItem(tr("Pro Controller"));
+        }
+
+        if (enable_all || npad_style_set.joycon_dual == 1) {
+            index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+                                                     Settings::ControllerType::DualJoyconDetached);
+            ui->comboControllerType->addItem(tr("Dual Joycons"));
+        }
+
+        if (enable_all || npad_style_set.joycon_left == 1) {
+            index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+                                                     Settings::ControllerType::LeftJoycon);
+            ui->comboControllerType->addItem(tr("Left Joycon"));
+        }
+
+        if (enable_all || npad_style_set.joycon_right == 1) {
+            index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+                                                     Settings::ControllerType::RightJoycon);
+            ui->comboControllerType->addItem(tr("Right Joycon"));
+        }
+
+        if (player_index == 0 && (enable_all || npad_style_set.handheld == 1)) {
+            index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
+                                                     Settings::ControllerType::Handheld);
+            ui->comboControllerType->addItem(tr("Handheld"));
+        }
+    };
+
+    Core::System& system{Core::System::GetInstance()};
+
+    if (!system.IsPoweredOn()) {
+        add_controllers(true);
+        return;
+    }
+
+    Service::SM::ServiceManager& sm = system.ServiceManager();
+
+    auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>(
+        HidController::NPad);
+
+    add_controllers(false, npad.GetSupportedStyleSet());
+}
+
+Settings::ControllerType ConfigureInputPlayer::GetControllerTypeFromIndex(int index) const {
+    const auto it =
+        std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(),
+                     [index](const auto& pair) { return pair.first == index; });
+
+    if (it == index_controller_type_pairs.end()) {
+        return Settings::ControllerType::ProController;
+    }
+
+    return it->second;
+}
+
+int ConfigureInputPlayer::GetIndexFromControllerType(Settings::ControllerType type) const {
+    const auto it =
+        std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(),
+                     [type](const auto& pair) { return pair.second == type; });
+
+    if (it == index_controller_type_pairs.end()) {
+        return -1;
+    }
+
+    return it->first;
+}
+
 void ConfigureInputPlayer::UpdateInputDevices() {
     input_devices = input_subsystem->GetInputDevices();
     ui->comboDevices->clear();
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 23cf6f9589..9c30879a2d 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -9,6 +9,7 @@
 #include <memory>
 #include <optional>
 #include <string>
+#include <vector>
 
 #include <QWidget>
 
@@ -112,6 +113,15 @@ private:
     /// Update UI to reflect current configuration.
     void UpdateUI();
 
+    /// Sets the available controllers.
+    void SetConnectableControllers();
+
+    /// Gets the Controller Type for a given controller combobox index.
+    Settings::ControllerType GetControllerTypeFromIndex(int index) const;
+
+    /// Gets the controller combobox index for a given Controller Type.
+    int GetIndexFromControllerType(Settings::ControllerType type) const;
+
     /// Update the available input devices.
     void UpdateInputDevices();
 
@@ -151,6 +161,9 @@ private:
     std::unique_ptr<QTimer> timeout_timer;
     std::unique_ptr<QTimer> poll_timer;
 
+    /// Stores a pair of "Connected Controllers" combobox index and Controller Type enum.
+    std::vector<std::pair<int, Settings::ControllerType>> index_controller_type_pairs;
+
     static constexpr int PLAYER_COUNT = 8;
     std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox;
 

From 8758378dc40e65239db9a1212e123c86200a3bdc Mon Sep 17 00:00:00 2001
From: Morph <39850852+Morph1984@users.noreply.github.com>
Date: Fri, 20 Nov 2020 11:04:27 -0500
Subject: [PATCH 2/2] applets/controller: Use a pair of emulated controller
 index to controller type

---
 src/yuzu/applets/controller.cpp | 123 ++++++++++++++++++++------------
 src/yuzu/applets/controller.h   |  17 +++++
 2 files changed, 96 insertions(+), 44 deletions(-)

diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp
index 8ecfec7702..6944478f3c 100644
--- a/src/yuzu/applets/controller.cpp
+++ b/src/yuzu/applets/controller.cpp
@@ -72,40 +72,6 @@ bool IsControllerCompatible(Settings::ControllerType controller_type,
     }
 }
 
-/// Maps the controller type combobox index to Controller Type enum
-constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
-    switch (index) {
-    case 0:
-    default:
-        return Settings::ControllerType::ProController;
-    case 1:
-        return Settings::ControllerType::DualJoyconDetached;
-    case 2:
-        return Settings::ControllerType::LeftJoycon;
-    case 3:
-        return Settings::ControllerType::RightJoycon;
-    case 4:
-        return Settings::ControllerType::Handheld;
-    }
-}
-
-/// Maps the Controller Type enum to controller type combobox index
-constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
-    switch (type) {
-    case Settings::ControllerType::ProController:
-    default:
-        return 0;
-    case Settings::ControllerType::DualJoyconDetached:
-        return 1;
-    case Settings::ControllerType::LeftJoycon:
-        return 2;
-    case Settings::ControllerType::RightJoycon:
-        return 3;
-    case Settings::ControllerType::Handheld:
-        return 4;
-    }
-}
-
 } // namespace
 
 QtControllerSelectorDialog::QtControllerSelectorDialog(
@@ -184,6 +150,11 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
     // This avoids unintentionally changing the states of elements while loading them in.
     SetSupportedControllers();
     DisableUnsupportedPlayers();
+
+    for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) {
+        SetEmulatedControllers(player_index);
+    }
+
     LoadConfiguration();
 
     for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
@@ -223,8 +194,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
 
         if (i == 0) {
             connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
-                    [this](int index) {
-                        UpdateDockedState(GetControllerTypeFromIndex(index) ==
+                    [this, i](int index) {
+                        UpdateDockedState(GetControllerTypeFromIndex(index, i) ==
                                           Settings::ControllerType::Handheld);
                     });
         }
@@ -281,8 +252,8 @@ void QtControllerSelectorDialog::LoadConfiguration() {
             (index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
         player_groupboxes[index]->setChecked(connected);
         connected_controller_checkboxes[index]->setChecked(connected);
-        emulated_controllers[index]->setCurrentIndex(
-            GetIndexFromControllerType(Settings::values.players.GetValue()[index].controller_type));
+        emulated_controllers[index]->setCurrentIndex(GetIndexFromControllerType(
+            Settings::values.players.GetValue()[index].controller_type, index));
     }
 
     UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
@@ -338,7 +309,7 @@ bool QtControllerSelectorDialog::CheckIfParametersMet() {
             }
 
             const auto compatible = IsControllerCompatible(
-                GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex()),
+                GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex(), index),
                 parameters);
 
             // If any controller is found to be incompatible, return false early.
@@ -422,6 +393,63 @@ void QtControllerSelectorDialog::SetSupportedControllers() {
     }
 }
 
+void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index) {
+    auto& pairs = index_controller_type_pairs[player_index];
+
+    pairs.clear();
+    emulated_controllers[player_index]->clear();
+
+    pairs.emplace_back(emulated_controllers[player_index]->count(),
+                       Settings::ControllerType::ProController);
+    emulated_controllers[player_index]->addItem(tr("Pro Controller"));
+
+    pairs.emplace_back(emulated_controllers[player_index]->count(),
+                       Settings::ControllerType::DualJoyconDetached);
+    emulated_controllers[player_index]->addItem(tr("Dual Joycons"));
+
+    pairs.emplace_back(emulated_controllers[player_index]->count(),
+                       Settings::ControllerType::LeftJoycon);
+    emulated_controllers[player_index]->addItem(tr("Left Joycon"));
+
+    pairs.emplace_back(emulated_controllers[player_index]->count(),
+                       Settings::ControllerType::RightJoycon);
+    emulated_controllers[player_index]->addItem(tr("Right Joycon"));
+
+    if (player_index == 0) {
+        pairs.emplace_back(emulated_controllers[player_index]->count(),
+                           Settings::ControllerType::Handheld);
+        emulated_controllers[player_index]->addItem(tr("Handheld"));
+    }
+}
+
+Settings::ControllerType 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 Settings::ControllerType::ProController;
+    }
+
+    return it->second;
+}
+
+int QtControllerSelectorDialog::GetIndexFromControllerType(Settings::ControllerType 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{});
@@ -430,7 +458,8 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
     }
 
     const QString stylesheet = [this, player_index] {
-        switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex())) {
+        switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
+                                           player_index)) {
         case Settings::ControllerType::ProController:
             return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
         case Settings::ControllerType::DualJoyconDetached:
@@ -446,6 +475,12 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
         }
     }();
 
+    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");
@@ -463,8 +498,8 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
 void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
     auto& player = Settings::values.players.GetValue()[player_index];
 
-    const auto controller_type =
-        GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex());
+    const auto controller_type = GetControllerTypeFromIndex(
+        emulated_controllers[player_index]->currentIndex(), player_index);
     const auto player_connected = player_groupboxes[player_index]->isChecked() &&
                                   controller_type != Settings::ControllerType::Handheld;
 
@@ -507,8 +542,8 @@ void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index)
 
 void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
     if (!player_groupboxes[player_index]->isChecked() ||
-        GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()) ==
-            Settings::ControllerType::Handheld) {
+        GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
+                                   player_index) == Settings::ControllerType::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);
diff --git a/src/yuzu/applets/controller.h b/src/yuzu/applets/controller.h
index 4344e1dd0f..7a421d8567 100644
--- a/src/yuzu/applets/controller.h
+++ b/src/yuzu/applets/controller.h
@@ -22,6 +22,10 @@ namespace InputCommon {
 class InputSubsystem;
 }
 
+namespace Settings {
+enum class ControllerType;
+}
+
 namespace Ui {
 class QtControllerSelectorDialog;
 }
@@ -57,6 +61,15 @@ private:
     // Sets the controller icons for "Supported Controller Types".
     void SetSupportedControllers();
 
+    // Sets the emulated controllers per player.
+    void SetEmulatedControllers(std::size_t player_index);
+
+    // Gets the Controller Type for a given controller combobox index per player.
+    Settings::ControllerType GetControllerTypeFromIndex(int index, std::size_t player_index) const;
+
+    // Gets the controller combobox index for a given Controller Type per player.
+    int GetIndexFromControllerType(Settings::ControllerType type, std::size_t player_index) const;
+
     // Updates the controller icons per player.
     void UpdateControllerIcon(std::size_t player_index);
 
@@ -114,6 +127,10 @@ private:
     // Comboboxes with a list of emulated controllers per player.
     std::array<QComboBox*, NUM_PLAYERS> emulated_controllers;
 
+    /// Pairs of emulated controller index and Controller Type enum per player.
+    std::array<std::vector<std::pair<int, Settings::ControllerType>>, NUM_PLAYERS>
+        index_controller_type_pairs;
+
     // Labels representing the number of connected controllers
     // above the "Connected Controllers" checkboxes.
     std::array<QLabel*, NUM_PLAYERS> connected_controller_labels;