diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 7b0c4a49b1..6622603277 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -91,7 +91,6 @@ void EmulatedController::ReloadFromSettings() {
 }
 
 void EmulatedController::ReloadInput() {
-    // LOG_ERROR(Service_HID, "reload config {}", NpadIdTypeToIndex(npad_id_type));
     // If you load any device here add the equivalent to the UnloadInput() function
     const auto left_side = button_params[Settings::NativeButton::ZL];
     const auto right_side = button_params[Settings::NativeButton::ZR];
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 3a0b20cf81..f3ee707264 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -329,7 +329,7 @@ private:
      * @param type: Input type of the event to trigger
      * @param is_service_update: indicates if this event should be sended to only services
      */
-    void TriggerOnChange(ControllerTriggerType type, bool  is_service_update);
+    void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
 
     NpadIdType npad_id_type;
     NpadType npad_type{NpadType::None};
diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp
index ee76db1106..bd17081bdf 100644
--- a/src/core/hid/hid_core.cpp
+++ b/src/core/hid/hid_core.cpp
@@ -111,6 +111,27 @@ NpadStyleTag HIDCore::GetSupportedStyleTag() const {
     return supported_style_tag;
 }
 
+s8 HIDCore::GetPlayerCount() const {
+    s8 active_players = 0;
+    for (std::size_t player_index = 0; player_index < 8; player_index++) {
+        const auto* controller = GetEmulatedControllerByIndex(player_index);
+        if (controller->IsConnected()) {
+            active_players++;
+        }
+    }
+    return active_players;
+}
+
+NpadIdType HIDCore::GetFirstNpadId() const {
+    for (std::size_t player_index = 0; player_index < 10; player_index++) {
+        const auto* controller = GetEmulatedControllerByIndex(player_index);
+        if (controller->IsConnected()) {
+            return controller->GetNpadIdType();
+        }
+    }
+    return NpadIdType::Player1;
+}
+
 void HIDCore::ReloadInputDevices() {
     player_1->ReloadFromSettings();
     player_2->ReloadFromSettings();
diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h
index f11f48b617..196466a72c 100644
--- a/src/core/hid/hid_core.h
+++ b/src/core/hid/hid_core.h
@@ -35,10 +35,16 @@ public:
     void SetSupportedStyleTag(NpadStyleTag style_tag);
     NpadStyleTag GetSupportedStyleTag() const;
 
-    // Reloads all input devices from settings
+    /// Counts the connected players from P1-P8
+    s8 GetPlayerCount() const;
+
+    /// Returns the first connected npad id
+    NpadIdType GetFirstNpadId() const;
+
+    /// Reloads all input devices from settings
     void ReloadInputDevices();
 
-    // Removes all callbacks from input common
+    /// Removes all callbacks from input common
     void UnloadInputDevices();
 
 private:
diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp
index c1b6cd1266..658265a004 100644
--- a/src/core/hle/service/am/applets/applet_controller.cpp
+++ b/src/core/hle/service/am/applets/applet_controller.cpp
@@ -243,19 +243,11 @@ void Controller::Execute() {
 void Controller::ConfigurationComplete() {
     ControllerSupportResultInfo result_info{};
 
-    const auto& players = Settings::values.players.GetValue();
-
     // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
     // Otherwise, only count connected players from P1-P8.
-    result_info.player_count =
-        is_single_mode
-            ? 1
-            : static_cast<s8>(std::count_if(players.begin(), players.end() - 2,
-                                            [](const auto& player) { return player.connected; }));
+    result_info.player_count = is_single_mode ? 1 : system.HIDCore().GetPlayerCount();
 
-    result_info.selected_id = HID::Controller_NPad::IndexToNPad(std::distance(
-        players.begin(), std::find_if(players.begin(), players.end(),
-                                      [](const auto& player) { return player.connected; })));
+    result_info.selected_id = static_cast<u32>(system.HIDCore().GetFirstNpadId());
 
     result_info.result = 0;
 
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 7807dd38ff..b048783c91 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -32,12 +32,17 @@ struct InputSubsystem::Impl {
         keyboard = std::make_shared<Keyboard>("keyboard");
         keyboard->SetMappingCallback(mapping_callback);
         keyboard_factory = std::make_shared<InputFactory>(keyboard);
+        keyboard_output_factory = std::make_shared<OutputFactory>(keyboard);
         Input::RegisterFactory<Input::InputDevice>(keyboard->GetEngineName(), keyboard_factory);
+        Input::RegisterFactory<Input::OutputDevice>(keyboard->GetEngineName(),
+                                                    keyboard_output_factory);
 
         mouse = std::make_shared<Mouse>("mouse");
         mouse->SetMappingCallback(mapping_callback);
         mouse_factory = std::make_shared<InputFactory>(mouse);
+        mouse_output_factory = std::make_shared<OutputFactory>(mouse);
         Input::RegisterFactory<Input::InputDevice>(mouse->GetEngineName(), mouse_factory);
+        Input::RegisterFactory<Input::OutputDevice>(mouse->GetEngineName(), mouse_output_factory);
 
         touch_screen = std::make_shared<TouchScreen>("touch");
         touch_screen_factory = std::make_shared<InputFactory>(touch_screen);
@@ -61,7 +66,9 @@ struct InputSubsystem::Impl {
         tas_input = std::make_shared<TasInput::Tas>("tas");
         tas_input->SetMappingCallback(mapping_callback);
         tas_input_factory = std::make_shared<InputFactory>(tas_input);
+        tas_output_factory = std::make_shared<OutputFactory>(tas_input);
         Input::RegisterFactory<Input::InputDevice>(tas_input->GetEngineName(), tas_input_factory);
+        Input::RegisterFactory<Input::OutputDevice>(tas_input->GetEngineName(), tas_output_factory);
 
 #ifdef HAVE_SDL2
         sdl = std::make_shared<SDLDriver>("sdl");
@@ -268,7 +275,10 @@ struct InputSubsystem::Impl {
     std::shared_ptr<InputFactory> udp_client_factory;
     std::shared_ptr<InputFactory> tas_input_factory;
 
+    std::shared_ptr<OutputFactory> keyboard_output_factory;
+    std::shared_ptr<OutputFactory> mouse_output_factory;
     std::shared_ptr<OutputFactory> gcadapter_output_factory;
+    std::shared_ptr<OutputFactory> tas_output_factory;
 
 #ifdef HAVE_SDL2
     std::shared_ptr<SDLDriver> sdl;
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 2cd5ed7185..32cb5b5fff 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -9,6 +9,8 @@
 #include "common/param_package.h"
 #include "common/string_util.h"
 #include "core/core.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_types.h"
 #include "core/hle/lock.h"
 #include "core/hle/service/hid/controllers/npad.h"
 #include "core/hle/service/hid/hid.h"
@@ -26,48 +28,31 @@ namespace {
 
 constexpr std::size_t HANDHELD_INDEX = 8;
 
-constexpr std::array<std::array<bool, 4>, 8> led_patterns{{
-    {true, false, false, false},
-    {true, true, false, false},
-    {true, true, true, false},
-    {true, true, true, true},
-    {true, false, false, true},
-    {true, false, true, false},
-    {true, false, true, true},
-    {false, true, true, false},
-}};
-
-void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
-                      bool connected, Core::System& system) {
-    if (!system.IsPoweredOn()) {
-        return;
+void UpdateController(Core::HID::EmulatedController* controller, Core::HID::NpadType controller_type, bool connected) {
+    if (controller->IsConnected()) {
+        controller->Disconnect();
+    }
+    controller->SetNpadType(controller_type);
+    if (connected) {
+        controller->Connect();
     }
-
-    auto& npad =
-        system.ServiceManager()
-            .GetService<Service::HID::Hid>("hid")
-            ->GetAppletResource()
-            ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
-
-    npad.UpdateControllerAt(Core::HID::EmulatedController::MapSettingsTypeToNPad(controller_type),
-                            npad_index, connected);
 }
 
 // Returns true if the given controller type is compatible with the given parameters.
-bool IsControllerCompatible(Settings::ControllerType controller_type,
+bool IsControllerCompatible(Core::HID::NpadType controller_type,
                             Core::Frontend::ControllerParameters parameters) {
     switch (controller_type) {
-    case Settings::ControllerType::ProController:
+    case Core::HID::NpadType::ProController:
         return parameters.allow_pro_controller;
-    case Settings::ControllerType::DualJoyconDetached:
+    case Core::HID::NpadType::JoyconDual:
         return parameters.allow_dual_joycons;
-    case Settings::ControllerType::LeftJoycon:
+    case Core::HID::NpadType::JoyconLeft:
         return parameters.allow_left_joycon;
-    case Settings::ControllerType::RightJoycon:
+    case Core::HID::NpadType::JoyconRight:
         return parameters.allow_right_joycon;
-    case Settings::ControllerType::Handheld:
+    case Core::HID::NpadType::Handheld:
         return parameters.enable_single_mode && parameters.allow_handheld;
-    case Settings::ControllerType::GameCube:
+    case Core::HID::NpadType::GameCube:
         return parameters.allow_gamecube_controller;
     default:
         return false;
@@ -198,7 +183,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
             connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
                     [this, i](int index) {
                         UpdateDockedState(GetControllerTypeFromIndex(index, i) ==
-                                          Settings::ControllerType::Handheld);
+                                          Core::HID::NpadType::Handheld);
                     });
         }
     }
@@ -251,17 +236,17 @@ void QtControllerSelectorDialog::ApplyConfiguration() {
 }
 
 void QtControllerSelectorDialog::LoadConfiguration() {
+    const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
     for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
-        const auto connected =
-            Settings::values.players.GetValue()[index].connected ||
-            (index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
+        const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index);
+        const auto connected = controller->IsConnected() || (index == 0 && handheld->IsConnected());
         player_groupboxes[index]->setChecked(connected);
         connected_controller_checkboxes[index]->setChecked(connected);
-        emulated_controllers[index]->setCurrentIndex(GetIndexFromControllerType(
-            Settings::values.players.GetValue()[index].controller_type, index));
+        emulated_controllers[index]->setCurrentIndex(
+            GetIndexFromControllerType(controller->GetNpadType(), index));
     }
 
-    UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
+    UpdateDockedState(handheld->IsConnected());
 
     ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
     ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
@@ -417,33 +402,32 @@ void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index
     emulated_controllers[player_index]->clear();
 
     pairs.emplace_back(emulated_controllers[player_index]->count(),
-                       Settings::ControllerType::ProController);
+                       Core::HID::NpadType::ProController);
     emulated_controllers[player_index]->addItem(tr("Pro Controller"));
 
     pairs.emplace_back(emulated_controllers[player_index]->count(),
-                       Settings::ControllerType::DualJoyconDetached);
+                       Core::HID::NpadType::JoyconDual);
     emulated_controllers[player_index]->addItem(tr("Dual Joycons"));
 
     pairs.emplace_back(emulated_controllers[player_index]->count(),
-                       Settings::ControllerType::LeftJoycon);
+                       Core::HID::NpadType::JoyconLeft);
     emulated_controllers[player_index]->addItem(tr("Left Joycon"));
 
     pairs.emplace_back(emulated_controllers[player_index]->count(),
-                       Settings::ControllerType::RightJoycon);
+                       Core::HID::NpadType::JoyconRight);
     emulated_controllers[player_index]->addItem(tr("Right Joycon"));
 
     if (player_index == 0) {
         pairs.emplace_back(emulated_controllers[player_index]->count(),
-                           Settings::ControllerType::Handheld);
+                           Core::HID::NpadType::Handheld);
         emulated_controllers[player_index]->addItem(tr("Handheld"));
     }
 
-    pairs.emplace_back(emulated_controllers[player_index]->count(),
-                       Settings::ControllerType::GameCube);
+    pairs.emplace_back(emulated_controllers[player_index]->count(), Core::HID::NpadType::GameCube);
     emulated_controllers[player_index]->addItem(tr("GameCube Controller"));
 }
 
-Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex(
+Core::HID::NpadType QtControllerSelectorDialog::GetControllerTypeFromIndex(
     int index, std::size_t player_index) const {
     const auto& pairs = index_controller_type_pairs[player_index];
 
@@ -451,13 +435,13 @@ Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex(
                                  [index](const auto& pair) { return pair.first == index; });
 
     if (it == pairs.end()) {
-        return Settings::ControllerType::ProController;
+        return Core::HID::NpadType::ProController;
     }
 
     return it->second;
 }
 
-int QtControllerSelectorDialog::GetIndexFromControllerType(Settings::ControllerType type,
+int QtControllerSelectorDialog::GetIndexFromControllerType(Core::HID::NpadType type,
                                                            std::size_t player_index) const {
     const auto& pairs = index_controller_type_pairs[player_index];
 
@@ -481,16 +465,16 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
     const QString stylesheet = [this, player_index] {
         switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
                                            player_index)) {
-        case Settings::ControllerType::ProController:
-        case Settings::ControllerType::GameCube:
+        case Core::HID::NpadType::ProController:
+        case Core::HID::NpadType::GameCube:
             return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
-        case Settings::ControllerType::DualJoyconDetached:
+        case Core::HID::NpadType::JoyconDual:
             return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ");
-        case Settings::ControllerType::LeftJoycon:
+        case Core::HID::NpadType::JoyconLeft:
             return QStringLiteral("image: url(:/controller/applet_joycon_left%0); ");
-        case Settings::ControllerType::RightJoycon:
+        case Core::HID::NpadType::JoyconRight:
             return QStringLiteral("image: url(:/controller/applet_joycon_right%0); ");
-        case Settings::ControllerType::Handheld:
+        case Core::HID::NpadType::Handheld:
             return QStringLiteral("image: url(:/controller/applet_handheld%0); ");
         default:
             return QString{};
@@ -518,54 +502,42 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
 }
 
 void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
-    auto& player = Settings::values.players.GetValue()[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 != Settings::ControllerType::Handheld;
+                                  controller_type != Core::HID::NpadType::Handheld;
 
-    if (player.controller_type == controller_type && player.connected == player_connected) {
+    if (controller->GetNpadType() == controller_type &&
+        controller->IsConnected() == 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, system);
-
-    player.controller_type = controller_type;
-    player.connected = player_connected;
+    UpdateController(controller, controller_type, false);
 
     ConfigureVibration::SetVibrationDevices(player_index);
 
     // Handheld
     if (player_index == 0) {
-        auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
-        if (controller_type == Settings::ControllerType::Handheld) {
-            handheld = player;
+        if (controller_type == Core::HID::NpadType::Handheld) {
+            auto* handheld =
+                system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
+            UpdateController(handheld, Core::HID::NpadType::Handheld,
+                             player_groupboxes[player_index]->isChecked());
         }
-        handheld.connected = player_groupboxes[player_index]->isChecked() &&
-                             controller_type == Settings::ControllerType::Handheld;
-        UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected, system);
     }
 
-    if (!player.connected) {
-        return;
-    }
-
-    // This emulates a delay between disconnecting and reconnecting controllers as some games
-    // do not respond to a change in controller type if it was instantaneous.
-    using namespace std::chrono_literals;
-    std::this_thread::sleep_for(60ms);
-
-    UpdateController(controller_type, player_index, player_connected, system);
+    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) == Settings::ControllerType::Handheld) {
+                                   player_index) == Core::HID::NpadType::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);
@@ -573,10 +545,12 @@ void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
         return;
     }
 
-    led_patterns_boxes[player_index][0]->setChecked(led_patterns[player_index][0]);
-    led_patterns_boxes[player_index][1]->setChecked(led_patterns[player_index][1]);
-    led_patterns_boxes[player_index][2]->setChecked(led_patterns[player_index][2]);
-    led_patterns_boxes[player_index][3]->setChecked(led_patterns[player_index][3]);
+    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) {
@@ -656,10 +630,9 @@ void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
     }
 
     for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) {
+        auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index);
         // Disconnect any unsupported players here and disable or hide them if applicable.
-        Settings::values.players.GetValue()[index].connected = false;
-        UpdateController(Settings::values.players.GetValue()[index].controller_type, index, false,
-                         system);
+        UpdateController(controller, controller->GetNpadType(), false);
         // Hide the player widgets when max_supported_controllers is less than or equal to 4.
         if (max_supported_players <= 4) {
             player_widgets[index]->hide();
diff --git a/src/yuzu/applets/qt_controller.h b/src/yuzu/applets/qt_controller.h
index ca09fde04f..dd981f4795 100644
--- a/src/yuzu/applets/qt_controller.h
+++ b/src/yuzu/applets/qt_controller.h
@@ -23,10 +23,6 @@ namespace InputCommon {
 class InputSubsystem;
 }
 
-namespace Settings {
-enum class ControllerType;
-}
-
 namespace Ui {
 class QtControllerSelectorDialog;
 }
@@ -35,6 +31,10 @@ namespace Core {
 class System;
 }
 
+namespace Core::HID {
+enum class NpadType : u8;
+}
+
 class QtControllerSelectorDialog final : public QDialog {
     Q_OBJECT
 
@@ -74,10 +74,10 @@ private:
     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;
+    Core::HID::NpadType 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;
+    int GetIndexFromControllerType(Core::HID::NpadType type, std::size_t player_index) const;
 
     // Updates the controller icons per player.
     void UpdateControllerIcon(std::size_t player_index);
@@ -139,7 +139,7 @@ private:
     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>
+    std::array<std::vector<std::pair<int, Core::HID::NpadType>>, NUM_PLAYERS>
         index_controller_type_pairs;
 
     // Labels representing the number of connected controllers
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp
index 7e87312322..3d91f80341 100644
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -10,6 +10,8 @@
 #include "common/settings.h"
 #include "common/string_util.h"
 #include "core/core.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
 #include "core/hid/hid_types.h"
 #include "core/hid/input_interpreter.h"
 #include "ui_qt_software_keyboard.h"
@@ -796,9 +798,10 @@ void QtSoftwareKeyboardDialog::SetTextDrawType() {
 }
 
 void QtSoftwareKeyboardDialog::SetControllerImage() {
-    const auto controller_type = Settings::values.players.GetValue()[8].connected
-                                     ? Settings::values.players.GetValue()[8].controller_type
-                                     : Settings::values.players.GetValue()[0].controller_type;
+    const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
+    const auto* player_1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
+    const auto controller_type =
+        handheld->IsConnected() ? handheld->GetNpadType() : player_1->GetNpadType();
 
     const QString theme = [] {
         if (QIcon::themeName().contains(QStringLiteral("dark")) ||
@@ -810,8 +813,8 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
     }();
 
     switch (controller_type) {
-    case Settings::ControllerType::ProController:
-    case Settings::ControllerType::GameCube:
+    case Core::HID::NpadType::ProController:
+    case Core::HID::NpadType::GameCube:
         ui->icon_controller->setStyleSheet(
             QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
         ui->icon_controller_shift->setStyleSheet(
@@ -819,7 +822,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
         ui->icon_controller_num->setStyleSheet(
             QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
         break;
-    case Settings::ControllerType::DualJoyconDetached:
+    case Core::HID::NpadType::JoyconDual:
         ui->icon_controller->setStyleSheet(
             QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme));
         ui->icon_controller_shift->setStyleSheet(
@@ -827,7 +830,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
         ui->icon_controller_num->setStyleSheet(
             QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme));
         break;
-    case Settings::ControllerType::LeftJoycon:
+    case Core::HID::NpadType::JoyconLeft:
         ui->icon_controller->setStyleSheet(
             QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);")
                 .arg(theme));
@@ -838,7 +841,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
             QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);")
                 .arg(theme));
         break;
-    case Settings::ControllerType::RightJoycon:
+    case Core::HID::NpadType::JoyconRight:
         ui->icon_controller->setStyleSheet(
             QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);")
                 .arg(theme));
@@ -849,7 +852,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
             QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);")
                 .arg(theme));
         break;
-    case Settings::ControllerType::Handheld:
+    case Core::HID::NpadType::Handheld:
         ui->icon_controller->setStyleSheet(
             QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme));
         ui->icon_controller_shift->setStyleSheet(
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 67faa8be8a..dece27fdeb 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -211,8 +211,10 @@ void ConfigureInput::RetranslateUI() {
 }
 
 void ConfigureInput::LoadConfiguration() {
+    const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
+
     LoadPlayerControllerIndices();
-    UpdateDockedState(Settings::values.players.GetValue()[8].connected);
+    UpdateDockedState(handheld->IsConnected());
 
     ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
     ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
@@ -220,9 +222,16 @@ void ConfigureInput::LoadConfiguration() {
 
 void ConfigureInput::LoadPlayerControllerIndices() {
     for (std::size_t i = 0; i < player_connected.size(); ++i) {
-        const auto connected = Settings::values.players.GetValue()[i].connected ||
-                               (i == 0 && Settings::values.players.GetValue()[8].connected);
-        player_connected[i]->setChecked(connected);
+        if (i == 0) {
+            auto* handheld =
+                system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
+            if (handheld->IsConnected()) {
+                player_connected[i]->setChecked(true);
+                continue;
+            }
+        }
+        const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(i);
+        player_connected[i]->setChecked(controller->IsConnected());
     }
 }
 
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index 93f7eddc92..be87204fc3 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -104,8 +104,8 @@ void PlayerControlPreview::UpdateColors() {
     colors.left = colors.primary;
     colors.right = colors.primary;
     // Possible alternative to set colors from settings
-    // colors.left = QColor(Settings::values.players.GetValue()[player_index].body_color_left);
-    // colors.right = QColor(Settings::values.players.GetValue()[player_index].body_color_right);
+    // colors.left = QColor(controller->GetColors().left.body);
+    // colors.right = QColor(controller->GetColors().right.body);
 }
 
 void PlayerControlPreview::ResetInputs() {
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index ae997ccfa3..46a5f62adf 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -832,15 +832,16 @@ void GMainWindow::InitializeWidgets() {
     dock_status_button->setFocusPolicy(Qt::NoFocus);
     connect(dock_status_button, &QPushButton::clicked, [&] {
         const bool is_docked = Settings::values.use_docked_mode.GetValue();
-        auto& controller_type = Settings::values.players.GetValue()[0].controller_type;
+        auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
+        auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
 
-        if (!is_docked && controller_type == Settings::ControllerType::Handheld) {
+        if (!is_docked && handheld->IsConnected()) {
             QMessageBox::warning(this, tr("Invalid config detected"),
                                  tr("Handheld controller can't be used on docked mode. Pro "
                                     "controller will be selected."));
-            controller_type = Settings::ControllerType::ProController;
-            ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), *system);
-            configure_dialog.ApplyConfiguration();
+            handheld->Disconnect();
+            player_1->SetNpadType(Core::HID::NpadType::ProController);
+            player_1->Connect();
         }
 
         Settings::values.use_docked_mode.SetValue(!is_docked);