From 4daa435a46c6c612398645a2e0ad840c1caeda6b Mon Sep 17 00:00:00 2001
From: pineappleEA <pineaea@gmail.com>
Date: Sun, 14 Nov 2021 23:19:09 +0100
Subject: [PATCH] early-access version 2199

---
 README.md                                     |    2 +-
 src/common/CMakeLists.txt                     |    1 -
 src/common/settings.h                         |    8 +
 src/common/settings_input.h                   |   48 +-
 src/core/CMakeLists.txt                       |   19 +-
 src/core/core.cpp                             |   14 +-
 src/core/core.h                               |   10 -
 src/core/frontend/applets/controller.cpp      |   45 +-
 src/core/frontend/applets/controller.h        |    8 +-
 src/core/frontend/emu_window.cpp              |  100 +-
 src/core/frontend/emu_window.h                |   30 +-
 .../service/am/applets/applet_controller.cpp  |   17 +-
 .../service/am/applets/applet_controller.h    |    6 +-
 src/core/hle/service/am/applets/applets.cpp   |    2 +-
 .../hid/controllers/console_sixaxis.cpp       |   36 +-
 .../service/hid/controllers/console_sixaxis.h |   33 +-
 .../hid/controllers/controller_base.cpp       |    4 +-
 .../service/hid/controllers/controller_base.h |   19 +-
 .../hle/service/hid/controllers/debug_pad.cpp |   72 +-
 .../hle/service/hid/controllers/debug_pad.h   |   80 +-
 .../hle/service/hid/controllers/gesture.cpp   |  257 ++-
 .../hle/service/hid/controllers/gesture.h     |  109 +-
 .../hle/service/hid/controllers/keyboard.cpp  |   60 +-
 .../hle/service/hid/controllers/keyboard.h    |   60 +-
 .../hle/service/hid/controllers/mouse.cpp     |   60 +-
 src/core/hle/service/hid/controllers/mouse.h  |   63 +-
 src/core/hle/service/hid/controllers/npad.cpp | 1641 ++++++++---------
 src/core/hle/service/hid/controllers/npad.h   |  649 ++++---
 .../hle/service/hid/controllers/stubbed.cpp   |    6 +-
 .../hle/service/hid/controllers/stubbed.h     |   13 +-
 .../service/hid/controllers/touchscreen.cpp   |  139 +-
 .../hle/service/hid/controllers/touchscreen.h |   86 +-
 src/core/hle/service/hid/controllers/xpad.cpp |   32 +-
 src/core/hle/service/hid/controllers/xpad.h   |   59 +-
 src/core/hle/service/hid/hid.cpp              |  387 ++--
 src/core/hle/service/hid/hid.h                |   33 +-
 src/input_common/CMakeLists.txt               |   60 +-
 src/input_common/main.cpp                     |  478 +++--
 src/input_common/main.h                       |  152 +-
 src/yuzu/applets/qt_controller.cpp            |  146 +-
 src/yuzu/applets/qt_controller.h              |   18 +-
 src/yuzu/applets/qt_software_keyboard.cpp     |  110 +-
 src/yuzu/applets/qt_software_keyboard.h       |   12 +-
 src/yuzu/applets/qt_web_browser.cpp           |   66 +-
 src/yuzu/applets/qt_web_browser.h             |   12 +-
 src/yuzu/bootmanager.cpp                      |  331 +---
 src/yuzu/bootmanager.h                        |   15 +-
 src/yuzu/configuration/config.cpp             |  174 +-
 .../configure_debug_controller.cpp            |    9 +-
 .../configure_debug_controller.h              |    7 +-
 src/yuzu/configuration/configure_input.cpp    |   62 +-
 .../configuration/configure_input_player.cpp  | 1085 ++++++-----
 .../configuration/configure_input_player.h    |   57 +-
 .../configuration/configure_input_player.ui   |   50 +-
 .../configure_input_player_widget.cpp         |  709 ++++---
 .../configure_input_player_widget.h           |  173 +-
 .../configure_input_profile_dialog.cpp        |    4 +-
 .../configuration/configure_motion_touch.cpp  |   12 +-
 .../configuration/configure_motion_touch.ui   |   70 +-
 .../configure_mouse_advanced.cpp              |   57 +-
 .../configuration/configure_mouse_advanced.h  |    8 +-
 src/yuzu/configuration/configure_tas.cpp      |    2 +
 src/yuzu/configuration/configure_tas.ui       |    9 +-
 .../configure_touch_from_button.cpp           |   27 +-
 .../configure_touch_from_button.h             |    5 +
 .../configuration/configure_vibration.cpp     |    4 +-
 src/yuzu/debugger/controller.cpp              |   75 +-
 src/yuzu/debugger/controller.h                |   38 +-
 src/yuzu/main.cpp                             |   30 +-
 src/yuzu/util/overlay_dialog.cpp              |   27 +-
 src/yuzu/util/overlay_dialog.h                |   10 +-
 src/yuzu_cmd/config.cpp                       |    4 +
 src/yuzu_cmd/default_ini.h                    |   17 +-
 src/yuzu_cmd/emu_window/emu_window_sdl2.cpp   |   72 +-
 src/yuzu_cmd/emu_window/emu_window_sdl2.h     |   11 +-
 .../emu_window/emu_window_sdl2_gl.cpp         |    1 +
 76 files changed, 4516 insertions(+), 3901 deletions(-)

diff --git a/README.md b/README.md
index 1f1f1fb23..057adfcb6 100755
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 yuzu emulator early access
 =============
 
-This is the source code for early-access 2198.
+This is the source code for early-access 2199.
 
 ## Legal Notice
 
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 919da4a53..23d43a394 100755
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -73,7 +73,6 @@ add_library(common STATIC
     hex_util.h
     host_memory.cpp
     host_memory.h
-    input.h
     intrusive_red_black_tree.h
     literals.h
     logging/backend.cpp
diff --git a/src/common/settings.h b/src/common/settings.h
index ca30356bc..c7610ef1c 100755
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <array>
+#include <atomic>
 #include <map>
 #include <optional>
 #include <string>
@@ -559,15 +560,19 @@ struct Values {
     Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
 
     Setting<bool> motion_enabled{true, "motion_enabled"};
+    BasicSetting<std::string> motion_device{"engine:motion_emu,update_period:100,sensitivity:0.01",
+                                            "motion_device"};
     BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"};
 
     BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"};
     BasicSetting<bool> tas_enable{false, "tas_enable"};
     BasicSetting<bool> tas_loop{false, "tas_loop"};
+    BasicSetting<bool> tas_swap_controllers{true, "tas_swap_controllers"};
 
     BasicSetting<bool> mouse_panning{false, "mouse_panning"};
     BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
     BasicSetting<bool> mouse_enabled{false, "mouse_enabled"};
+    std::string mouse_device;
     MouseButtonsRaw mouse_buttons;
 
     BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
@@ -581,11 +586,14 @@ struct Values {
 
     TouchscreenInput touchscreen;
 
+    BasicSetting<bool> use_touch_from_button{false, "use_touch_from_button"};
     BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850",
                                            "touch_device"};
     BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"};
     std::vector<TouchFromButtonMap> touch_from_button_maps;
 
+    std::atomic_bool is_device_reload_pending{true};
+
     // Data Storage
     BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"};
     BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"};
diff --git a/src/common/settings_input.h b/src/common/settings_input.h
index a2982fca4..609600582 100755
--- a/src/common/settings_input.h
+++ b/src/common/settings_input.h
@@ -62,22 +62,11 @@ enum Values : int {
 
 constexpr int STICK_HID_BEGIN = LStick;
 constexpr int STICK_HID_END = NumAnalogs;
+constexpr int NUM_STICKS_HID = NumAnalogs;
 
 extern const std::array<const char*, NumAnalogs> mapping;
 } // namespace NativeAnalog
 
-namespace NativeTrigger {
-enum Values : int {
-    LTrigger,
-    RTrigger,
-
-    NumTriggers,
-};
-
-constexpr int TRIGGER_HID_BEGIN = LTrigger;
-constexpr int TRIGGER_HID_END = NumTriggers;
-} // namespace NativeTrigger
-
 namespace NativeVibration {
 enum Values : int {
     LeftVibrationDevice,
@@ -129,6 +118,7 @@ extern const std::array<const char*, NumMouseButtons> mapping;
 namespace NativeKeyboard {
 enum Keys {
     None,
+    Error,
 
     A = 4,
     B,
@@ -166,22 +156,22 @@ enum Keys {
     N8,
     N9,
     N0,
-    Return,
+    Enter,
     Escape,
     Backspace,
     Tab,
     Space,
     Minus,
-    Plus,
-    OpenBracket,
-    CloseBracket,
-    Pipe,
+    Equal,
+    LeftBrace,
+    RightBrace,
+    Backslash,
     Tilde,
     Semicolon,
-    Quote,
-    Backquote,
+    Apostrophe,
+    Grave,
     Comma,
-    Period,
+    Dot,
     Slash,
     CapsLockKey,
 
@@ -198,7 +188,7 @@ enum Keys {
     F11,
     F12,
 
-    PrintScreen,
+    SystemRequest,
     ScrollLockKey,
     Pause,
     Insert,
@@ -267,18 +257,8 @@ enum Keys {
     ScrollLockActive,
     KPComma,
 
-    Ro = 0x87,
-    KatakanaHiragana,
-    Yen,
-    Henkan,
-    Muhenkan,
-    NumPadCommaPc98,
-
-    HangulEnglish = 0x90,
-    Hanja,
-    KatakanaKey,
-    HiraganaKey,
-    ZenkakuHankaku,
+    KPLeftParenthesis,
+    KPRightParenthesis,
 
     LeftControlKey = 0xE0,
     LeftShiftKey,
@@ -327,8 +307,6 @@ enum Modifiers {
     CapsLock,
     ScrollLock,
     NumLock,
-    Katakana,
-    Hiragana,
 
     NumKeyboardMods,
 };
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 582c15f7e..9f0fbba2d 100755
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -132,23 +132,11 @@ add_library(core STATIC
     frontend/emu_window.h
     frontend/framebuffer_layout.cpp
     frontend/framebuffer_layout.h
+    frontend/input_interpreter.cpp
+    frontend/input_interpreter.h
+    frontend/input.h
     hardware_interrupt_manager.cpp
     hardware_interrupt_manager.h
-    hid/emulated_console.cpp
-    hid/emulated_console.h
-    hid/emulated_controller.cpp
-    hid/emulated_controller.h
-    hid/emulated_devices.cpp
-    hid/emulated_devices.h
-    hid/hid_core.cpp
-    hid/hid_core.h
-    hid/hid_types.h
-    hid/input_converter.cpp
-    hid/input_converter.h
-    hid/input_interpreter.cpp
-    hid/input_interpreter.h
-    hid/motion_input.cpp
-    hid/motion_input.h
     hle/api_version.h
     hle/ipc.h
     hle/ipc_helpers.h
@@ -414,7 +402,6 @@ add_library(core STATIC
     hle/service/hid/hid.h
     hle/service/hid/irs.cpp
     hle/service/hid/irs.h
-    hle/service/hid/ring_lifo.h
     hle/service/hid/xcd.cpp
     hle/service/hid/xcd.h
     hle/service/hid/errors.h
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 473ab9f81..07448fd29 100755
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -27,7 +27,6 @@
 #include "core/file_sys/vfs_concat.h"
 #include "core/file_sys/vfs_real.h"
 #include "core/hardware_interrupt_manager.h"
-#include "core/hid/hid_core.h"
 #include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_scheduler.h"
 #include "core/hle/kernel/kernel.h"
@@ -127,7 +126,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
 
 struct System::Impl {
     explicit Impl(System& system)
-        : kernel{system}, fs_controller{system}, memory{system}, hid_core{},
+        : kernel{system}, fs_controller{system}, memory{system},
           cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
 
     SystemResultStatus Run() {
@@ -392,7 +391,6 @@ struct System::Impl {
     std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
     std::unique_ptr<Core::DeviceMemory> device_memory;
     Core::Memory::Memory memory;
-    Core::HID::HIDCore hid_core;
     CpuManager cpu_manager;
     std::atomic_bool is_powered_on{};
     bool exit_lock = false;
@@ -617,14 +615,6 @@ const Kernel::KernelCore& System::Kernel() const {
     return impl->kernel;
 }
 
-HID::HIDCore& System::HIDCore() {
-    return impl->hid_core;
-}
-
-const HID::HIDCore& System::HIDCore() const {
-    return impl->hid_core;
-}
-
 Timing::CoreTiming& System::CoreTiming() {
     return impl->core_timing;
 }
@@ -835,6 +825,8 @@ void System::ApplySettings() {
     if (IsPoweredOn()) {
         Renderer().RefreshBaseSettings();
     }
+
+    Service::HID::ReloadInputDevices();
 }
 
 } // namespace Core
diff --git a/src/core/core.h b/src/core/core.h
index 645e5c241..01bc0a2c7 100755
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -89,10 +89,6 @@ namespace Core::Hardware {
 class InterruptManager;
 }
 
-namespace Core::HID {
-class HIDCore;
-}
-
 namespace Core {
 
 class ARM_Interface;
@@ -289,12 +285,6 @@ public:
     /// Provides a constant reference to the kernel instance.
     [[nodiscard]] const Kernel::KernelCore& Kernel() const;
 
-    /// Gets a mutable reference to the HID interface.
-    [[nodiscard]] HID::HIDCore& HIDCore();
-
-    /// Gets an immutable reference to the HID interface.
-    [[nodiscard]] const HID::HIDCore& HIDCore() const;
-
     /// Provides a reference to the internal PerfStats instance.
     [[nodiscard]] Core::PerfStats& GetPerfStats();
 
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
index 6dbd38ffa..03bbedf8b 100755
--- a/src/core/frontend/applets/controller.cpp
+++ b/src/core/frontend/applets/controller.cpp
@@ -5,15 +5,16 @@
 #include "common/assert.h"
 #include "common/logging/log.h"
 #include "core/frontend/applets/controller.h"
-#include "core/hid/emulated_controller.h"
-#include "core/hid/hid_core.h"
-#include "core/hid/hid_types.h"
+#include "core/hle/service/hid/controllers/npad.h"
+#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/sm/sm.h"
 
 namespace Core::Frontend {
 
 ControllerApplet::~ControllerApplet() = default;
 
-DefaultControllerApplet::DefaultControllerApplet(HID::HIDCore& hid_core_) : hid_core{hid_core_} {}
+DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& service_manager_)
+    : service_manager{service_manager_} {}
 
 DefaultControllerApplet::~DefaultControllerApplet() = default;
 
@@ -21,20 +22,24 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
                                                      const ControllerParameters& parameters) const {
     LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
 
+    auto& npad =
+        service_manager.GetService<Service::HID::Hid>("hid")
+            ->GetAppletResource()
+            ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
+
+    auto& players = Settings::values.players.GetValue();
+
     const std::size_t min_supported_players =
         parameters.enable_single_mode ? 1 : parameters.min_players;
 
     // Disconnect Handheld first.
-    auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
-    handheld->Disconnect();
+    npad.DisconnectNpadAtIndex(8);
 
     // Deduce the best configuration based on the input parameters.
-    for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
-        auto* controller = hid_core.GetEmulatedControllerByIndex(index);
-
+    for (std::size_t index = 0; index < players.size() - 2; ++index) {
         // First, disconnect all controllers regardless of the value of keep_controllers_connected.
         // This makes it easy to connect the desired controllers.
-        controller->Disconnect();
+        npad.DisconnectNpadAtIndex(index);
 
         // Only connect the minimum number of required players.
         if (index >= min_supported_players) {
@@ -44,27 +49,27 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
         // Connect controllers based on the following priority list from highest to lowest priority:
         // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
         if (parameters.allow_pro_controller) {
-            controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
-            controller->Connect();
+            npad.AddNewControllerAt(
+                npad.MapSettingsTypeToNPad(Settings::ControllerType::ProController), index);
         } else if (parameters.allow_dual_joycons) {
-            controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
-            controller->Connect();
+            npad.AddNewControllerAt(
+                npad.MapSettingsTypeToNPad(Settings::ControllerType::DualJoyconDetached), index);
         } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
             // Assign left joycons to even player indices and right joycons to odd player indices.
             // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
             // a right Joycon for Player 2 in 2 Player Assist mode.
             if (index % 2 == 0) {
-                controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft);
-                controller->Connect();
+                npad.AddNewControllerAt(
+                    npad.MapSettingsTypeToNPad(Settings::ControllerType::LeftJoycon), index);
             } else {
-                controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight);
-                controller->Connect();
+                npad.AddNewControllerAt(
+                    npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index);
             }
         } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
                    !Settings::values.use_docked_mode.GetValue()) {
             // We should *never* reach here under any normal circumstances.
-            controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
-            controller->Connect();
+            npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld),
+                                    index);
         } else {
             UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
         }
diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h
index 014bc8901..b0626a0f9 100755
--- a/src/core/frontend/applets/controller.h
+++ b/src/core/frontend/applets/controller.h
@@ -8,8 +8,8 @@
 
 #include "common/common_types.h"
 
-namespace Core::HID {
-class HIDCore;
+namespace Service::SM {
+class ServiceManager;
 }
 
 namespace Core::Frontend {
@@ -44,14 +44,14 @@ public:
 
 class DefaultControllerApplet final : public ControllerApplet {
 public:
-    explicit DefaultControllerApplet(HID::HIDCore& hid_core_);
+    explicit DefaultControllerApplet(Service::SM::ServiceManager& service_manager_);
     ~DefaultControllerApplet() override;
 
     void ReconfigureControllers(std::function<void()> callback,
                                 const ControllerParameters& parameters) const override;
 
 private:
-    HID::HIDCore& hid_core;
+    Service::SM::ServiceManager& service_manager;
 };
 
 } // namespace Core::Frontend
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 57c6ffc43..e1f7e5886 100755
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -3,31 +3,66 @@
 // Refer to the license.txt file included.
 
 #include <mutex>
+#include "common/settings.h"
 #include "core/frontend/emu_window.h"
+#include "core/frontend/input.h"
 
 namespace Core::Frontend {
 
 GraphicsContext::~GraphicsContext() = default;
 
+class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
+                              public std::enable_shared_from_this<TouchState> {
+public:
+    std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage&) override {
+        return std::make_unique<Device>(shared_from_this());
+    }
+
+    std::mutex mutex;
+
+    Input::TouchStatus status;
+
+private:
+    class Device : public Input::TouchDevice {
+    public:
+        explicit Device(std::weak_ptr<TouchState>&& touch_state_) : touch_state(touch_state_) {}
+        Input::TouchStatus GetStatus() const override {
+            if (auto state = touch_state.lock()) {
+                std::lock_guard guard{state->mutex};
+                return state->status;
+            }
+            return {};
+        }
+
+    private:
+        std::weak_ptr<TouchState> touch_state;
+    };
+};
+
 EmuWindow::EmuWindow() {
     // TODO: Find a better place to set this.
     config.min_client_area_size =
         std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height);
     active_config = config;
+    touch_state = std::make_shared<TouchState>();
+    Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
 }
 
-EmuWindow::~EmuWindow() {}
+EmuWindow::~EmuWindow() {
+    Input::UnregisterFactory<Input::TouchDevice>("emu_window");
+}
 
-std::pair<f32, f32> EmuWindow::MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const {
-    std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
-    const float x =
-        static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
-        static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
-    const float y =
-        static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
-        static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
-
-    return std::make_pair(x, y);
+/**
+ * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
+ * @param layout FramebufferLayout object describing the framebuffer size and screen positions
+ * @param framebuffer_x Framebuffer x-coordinate to check
+ * @param framebuffer_y Framebuffer y-coordinate to check
+ * @return True if the coordinates are within the touchpad, otherwise false
+ */
+static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, u32 framebuffer_x,
+                                u32 framebuffer_y) {
+    return (framebuffer_y >= layout.screen.top && framebuffer_y < layout.screen.bottom &&
+            framebuffer_x >= layout.screen.left && framebuffer_x < layout.screen.right);
 }
 
 std::pair<u32, u32> EmuWindow::ClipToTouchScreen(u32 new_x, u32 new_y) const {
@@ -40,6 +75,49 @@ std::pair<u32, u32> EmuWindow::ClipToTouchScreen(u32 new_x, u32 new_y) const {
     return std::make_pair(new_x, new_y);
 }
 
+void EmuWindow::TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id) {
+    if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
+        return;
+    }
+    if (id >= touch_state->status.size()) {
+        return;
+    }
+
+    std::lock_guard guard{touch_state->mutex};
+    const float x =
+        static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
+        static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
+    const float y =
+        static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
+        static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
+
+    touch_state->status[id] = std::make_tuple(x, y, true);
+}
+
+void EmuWindow::TouchReleased(size_t id) {
+    if (id >= touch_state->status.size()) {
+        return;
+    }
+    std::lock_guard guard{touch_state->mutex};
+    touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false);
+}
+
+void EmuWindow::TouchMoved(u32 framebuffer_x, u32 framebuffer_y, size_t id) {
+    if (id >= touch_state->status.size()) {
+        return;
+    }
+
+    if (!std::get<2>(touch_state->status[id])) {
+        return;
+    }
+
+    if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
+        std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
+    }
+
+    TouchPressed(framebuffer_x, framebuffer_y, id);
+}
+
 void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height) {
     NotifyFramebufferLayoutChanged(Layout::DefaultFrameLayout(width, height));
 }
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index e413a520a..8a86a1d27 100755
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -112,6 +112,28 @@ public:
     /// Returns if window is shown (not minimized)
     virtual bool IsShown() const = 0;
 
+    /**
+     * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
+     * @param framebuffer_x Framebuffer x-coordinate that was pressed
+     * @param framebuffer_y Framebuffer y-coordinate that was pressed
+     * @param id Touch event ID
+     */
+    void TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id);
+
+    /**
+     * Signal that a touch released event has occurred (e.g. mouse click released)
+     * @param id Touch event ID
+     */
+    void TouchReleased(size_t id);
+
+    /**
+     * Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
+     * @param framebuffer_x Framebuffer x-coordinate
+     * @param framebuffer_y Framebuffer y-coordinate
+     * @param id Touch event ID
+     */
+    void TouchMoved(u32 framebuffer_x, u32 framebuffer_y, size_t id);
+
     /**
      * Returns currently active configuration.
      * @note Accesses to the returned object need not be consistent because it may be modified in
@@ -190,11 +212,6 @@ protected:
         client_area_height = size.second;
     }
 
-    /**
-     * Converts a screen postion into the equivalent touchscreen position.
-     */
-    std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const;
-
     WindowSystemInfo window_info;
 
 private:
@@ -220,6 +237,9 @@ private:
     WindowConfig config;        ///< Internal configuration (changes pending for being applied in
                                 /// ProcessConfigurationChanges)
     WindowConfig active_config; ///< Internal active configuration
+
+    class TouchState;
+    std::shared_ptr<TouchState> touch_state;
 };
 
 } // namespace Core::Frontend
diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp
index d073f2210..2721679c1 100755
--- a/src/core/hle/service/am/applets/applet_controller.cpp
+++ b/src/core/hle/service/am/applets/applet_controller.cpp
@@ -10,9 +10,6 @@
 #include "common/string_util.h"
 #include "core/core.h"
 #include "core/frontend/applets/controller.h"
-#include "core/hid/emulated_controller.h"
-#include "core/hid/hid_core.h"
-#include "core/hid/hid_types.h"
 #include "core/hle/result.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/am/applets/applet_controller.h"
@@ -28,7 +25,7 @@ namespace Service::AM::Applets {
 static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
     ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
     std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
-    Core::HID::NpadStyleTag npad_style_set;
+    HID::Controller_NPad::NpadStyleSet npad_style_set;
     npad_style_set.raw = private_arg.style_set;
 
     return {
@@ -246,11 +243,19 @@ 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 : system.HIDCore().GetPlayerCount();
+    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.selected_id = static_cast<u32>(system.HIDCore().GetFirstNpadId());
+    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.result = 0;
 
diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h
index 1a832505e..0a34c4fc0 100755
--- a/src/core/hle/service/am/applets/applet_controller.h
+++ b/src/core/hle/service/am/applets/applet_controller.h
@@ -16,10 +16,6 @@ namespace Core {
 class System;
 }
 
-namespace Core::HID {
-enum class NpadStyleSet : u32;
-}
-
 namespace Service::AM::Applets {
 
 using IdentificationColor = std::array<u8, 4>;
@@ -56,7 +52,7 @@ struct ControllerSupportArgPrivate {
     bool flag_1{};
     ControllerSupportMode mode{};
     ControllerSupportCaller caller{};
-    Core::HID::NpadStyleSet style_set{};
+    u32 style_set{};
     u32 joy_hold_type{};
 };
 static_assert(sizeof(ControllerSupportArgPrivate) == 0x14,
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 134ac1ee2..7320b1c0f 100755
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -231,7 +231,7 @@ void AppletManager::SetDefaultAppletFrontendSet() {
 void AppletManager::SetDefaultAppletsIfMissing() {
     if (frontend.controller == nullptr) {
         frontend.controller =
-            std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore());
+            std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager());
     }
 
     if (frontend.error == nullptr) {
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
index ea7e8f18f..bda6e2557 100755
--- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
@@ -4,18 +4,13 @@
 
 #include "common/settings.h"
 #include "core/core_timing.h"
-#include "core/hid/emulated_console.h"
-#include "core/hid/hid_core.h"
 #include "core/hle/service/hid/controllers/console_sixaxis.h"
 
 namespace Service::HID {
 constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
 
-Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_)
-    : ControllerBase{hid_core_} {
-    console = hid_core.GetEmulatedConsole();
-}
-
+Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::System& system_)
+    : ControllerBase{system_} {}
 Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default;
 
 void Controller_ConsoleSixAxis::OnInit() {}
@@ -43,21 +38,25 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
     cur_entry.sampling_number2 = cur_entry.sampling_number;
 
     // Try to read sixaxis sensor states
-    const auto motion_status = console->GetMotion();
+    MotionDevice motion_device{};
+    const auto& device = motions[0];
+    if (device) {
+        std::tie(motion_device.accel, motion_device.gyro, motion_device.rotation,
+                 motion_device.orientation, motion_device.quaternion) = device->GetStatus();
+        console_six_axis.is_seven_six_axis_sensor_at_rest = motion_device.gyro.Length2() < 0.0001f;
+    }
 
-    console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
-
-    cur_entry.accel = motion_status.accel;
+    cur_entry.accel = motion_device.accel;
     // Zero gyro values as they just mess up with the camera
     // Note: Probably a correct sensivity setting must be set
     cur_entry.gyro = {};
     cur_entry.quaternion = {
         {
-            motion_status.quaternion.xyz.y,
-            motion_status.quaternion.xyz.x,
-            -motion_status.quaternion.w,
+            motion_device.quaternion.xyz.y,
+            motion_device.quaternion.xyz.x,
+            -motion_device.quaternion.w,
         },
-        -motion_status.quaternion.xyz.z,
+        -motion_device.quaternion.xyz.z,
     };
 
     console_six_axis.sampling_number++;
@@ -71,6 +70,13 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
     std::memcpy(transfer_memory, &seven_six_axis, sizeof(seven_six_axis));
 }
 
+void Controller_ConsoleSixAxis::OnLoadInputDevices() {
+    const auto player = Settings::values.players.GetValue()[0];
+    std::transform(player.motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
+                   player.motions.begin() + Settings::NativeMotion::MOTION_HID_END, motions.begin(),
+                   Input::CreateDevice<Input::MotionDevice>);
+}
+
 void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
     is_transfer_memory_set = true;
     transfer_memory = t_mem;
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/console_sixaxis.h
index 7c92413e8..fd8a427af 100755
--- a/src/core/hle/service/hid/controllers/console_sixaxis.h
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.h
@@ -5,20 +5,16 @@
 #pragma once
 
 #include <array>
-
+#include "common/bit_field.h"
 #include "common/common_types.h"
 #include "common/quaternion.h"
-#include "core/hid/hid_types.h"
+#include "core/frontend/input.h"
 #include "core/hle/service/hid/controllers/controller_base.h"
 
-namespace Core::HID {
-class EmulatedConsole;
-} // namespace Core::HID
-
 namespace Service::HID {
 class Controller_ConsoleSixAxis final : public ControllerBase {
 public:
-    explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_);
+    explicit Controller_ConsoleSixAxis(Core::System& system_);
     ~Controller_ConsoleSixAxis() override;
 
     // Called when the controller is initialized
@@ -30,6 +26,9 @@ public:
     // When the controller is requesting an update for the shared memory
     void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
 
+    // Called when input devices should be loaded
+    void OnLoadInputDevices() override;
+
     // Called on InitializeSevenSixAxisSensor
     void SetTransferMemoryPointer(u8* t_mem);
 
@@ -39,8 +38,8 @@ public:
 private:
     struct SevenSixAxisState {
         INSERT_PADDING_WORDS(4); // unused
-        s64 sampling_number{};
-        s64 sampling_number2{};
+        s64_le sampling_number{};
+        s64_le sampling_number2{};
         u64 unknown{};
         Common::Vec3f accel{};
         Common::Vec3f gyro{};
@@ -48,24 +47,14 @@ private:
     };
     static_assert(sizeof(SevenSixAxisState) == 0x50, "SevenSixAxisState is an invalid size");
 
-    struct CommonHeader {
-        s64 timestamp;
-        s64 total_entry_count;
-        s64 last_entry_index;
-        s64 entry_count;
-    };
-    static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
-
-    // TODO(german77): SevenSixAxisMemory doesn't follow the standard lifo. Investigate
     struct SevenSixAxisMemory {
         CommonHeader header{};
         std::array<SevenSixAxisState, 0x21> sevensixaxis_states{};
     };
     static_assert(sizeof(SevenSixAxisMemory) == 0xA70, "SevenSixAxisMemory is an invalid size");
 
-    // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
     struct ConsoleSharedMemory {
-        u64 sampling_number{};
+        u64_le sampling_number{};
         bool is_seven_six_axis_sensor_at_rest{};
         f32 verticalization_error{};
         Common::Vec3f gyro_bias{};
@@ -80,7 +69,9 @@ private:
         Common::Quaternion<f32> quaternion;
     };
 
-    Core::HID::EmulatedConsole* console;
+    using MotionArray =
+        std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>;
+    MotionArray motions;
     u8* transfer_memory = nullptr;
     bool is_transfer_memory_set = false;
     ConsoleSharedMemory console_six_axis{};
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
index 788ae9ae7..9d1e6db6a 100755
--- a/src/core/hle/service/hid/controllers/controller_base.cpp
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -6,12 +6,12 @@
 
 namespace Service::HID {
 
-ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
+ControllerBase::ControllerBase(Core::System& system_) : system(system_) {}
 ControllerBase::~ControllerBase() = default;
 
 void ControllerBase::ActivateController() {
     if (is_activated) {
-        return;
+        OnRelease();
     }
     is_activated = true;
     OnInit();
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 8125bbc84..1556fb08e 100755
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -11,14 +11,14 @@ namespace Core::Timing {
 class CoreTiming;
 }
 
-namespace Core::HID {
-class HIDCore;
+namespace Core {
+class System;
 }
 
 namespace Service::HID {
 class ControllerBase {
 public:
-    explicit ControllerBase(Core::HID::HIDCore& hid_core_);
+    explicit ControllerBase(Core::System& system_);
     virtual ~ControllerBase();
 
     // Called when the controller is initialized
@@ -35,6 +35,9 @@ public:
     virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
                                 std::size_t size) {}
 
+    // Called when input devices should be loaded
+    virtual void OnLoadInputDevices() = 0;
+
     void ActivateController();
 
     void DeactivateController();
@@ -44,6 +47,14 @@ public:
 protected:
     bool is_activated{false};
 
-    Core::HID::HIDCore& hid_core;
+    struct CommonHeader {
+        s64_le timestamp;
+        s64_le total_entry_count;
+        s64_le last_entry_index;
+        s64_le entry_count;
+    };
+    static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
+
+    Core::System& system;
 };
 } // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 6a6fb9cab..d439b8fb0 100755
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -6,19 +6,15 @@
 #include "common/common_types.h"
 #include "common/settings.h"
 #include "core/core_timing.h"
-#include "core/hid/emulated_controller.h"
-#include "core/hid/hid_core.h"
-#include "core/hid/hid_types.h"
 #include "core/hle/service/hid/controllers/debug_pad.h"
 
 namespace Service::HID {
-constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
 
-Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_)
-    : ControllerBase{hid_core_} {
-    controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
-}
+constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
+[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
+enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
 
+Controller_DebugPad::Controller_DebugPad(Core::System& system_) : ControllerBase{system_} {}
 Controller_DebugPad::~Controller_DebugPad() = default;
 
 void Controller_DebugPad::OnInit() {}
@@ -27,29 +23,63 @@ void Controller_DebugPad::OnRelease() {}
 
 void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
                                    std::size_t size) {
+    shared_memory.header.timestamp = core_timing.GetCPUTicks();
+    shared_memory.header.total_entry_count = 17;
+
     if (!IsControllerActivated()) {
-        debug_pad_lifo.buffer_count = 0;
-        debug_pad_lifo.buffer_tail = 0;
-        std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo));
+        shared_memory.header.entry_count = 0;
+        shared_memory.header.last_entry_index = 0;
         return;
     }
+    shared_memory.header.entry_count = 16;
 
-    const auto& last_entry = debug_pad_lifo.ReadCurrentEntry().state;
-    next_state.sampling_number = last_entry.sampling_number + 1;
+    const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
+    shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
+    auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
+
+    cur_entry.sampling_number = last_entry.sampling_number + 1;
+    cur_entry.sampling_number2 = cur_entry.sampling_number;
 
     if (Settings::values.debug_pad_enabled) {
-        next_state.attribute.connected.Assign(1);
+        cur_entry.attribute.connected.Assign(1);
+        auto& pad = cur_entry.pad_state;
 
-        const auto& button_state = controller->GetDebugPadButtons();
-        const auto& stick_state = controller->GetSticks();
+        using namespace Settings::NativeButton;
+        pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
+        pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
+        pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
+        pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
+        pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
+        pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
+        pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
+        pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
+        pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
+        pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
+        pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
+        pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
+        pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
+        pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
 
-        next_state.pad_state = button_state;
-        next_state.l_stick = stick_state.left;
-        next_state.r_stick = stick_state.right;
+        const auto [stick_l_x_f, stick_l_y_f] =
+            analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
+        const auto [stick_r_x_f, stick_r_y_f] =
+            analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
+        cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
+        cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
+        cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
+        cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
     }
 
-    debug_pad_lifo.WriteNextEntry(next_state);
-    std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo));
+    std::memcpy(data, &shared_memory, sizeof(SharedMemory));
 }
 
+void Controller_DebugPad::OnLoadInputDevices() {
+    std::transform(Settings::values.debug_pad_buttons.begin(),
+                   Settings::values.debug_pad_buttons.begin() +
+                       Settings::NativeButton::NUM_BUTTONS_HID,
+                   buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
+    std::transform(Settings::values.debug_pad_analogs.begin(),
+                   Settings::values.debug_pad_analogs.end(), analogs.begin(),
+                   Input::CreateDevice<Input::AnalogDevice>);
+}
 } // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 15b3afb7a..1b1645184 100755
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -8,20 +8,15 @@
 #include "common/bit_field.h"
 #include "common/common_funcs.h"
 #include "common/common_types.h"
+#include "common/settings.h"
 #include "common/swap.h"
+#include "core/frontend/input.h"
 #include "core/hle/service/hid/controllers/controller_base.h"
-#include "core/hle/service/hid/ring_lifo.h"
-
-namespace Core::HID {
-class EmulatedController;
-struct DebugPadButton;
-struct AnalogStickState;
-} // namespace Core::HID
 
 namespace Service::HID {
 class Controller_DebugPad final : public ControllerBase {
 public:
-    explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_);
+    explicit Controller_DebugPad(Core::System& system_);
     ~Controller_DebugPad() override;
 
     // Called when the controller is initialized
@@ -33,31 +28,66 @@ public:
     // When the controller is requesting an update for the shared memory
     void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
 
+    // Called when input devices should be loaded
+    void OnLoadInputDevices() override;
+
 private:
-    // This is nn::hid::DebugPadAttribute
-    struct DebugPadAttribute {
+    struct AnalogStick {
+        s32_le x;
+        s32_le y;
+    };
+    static_assert(sizeof(AnalogStick) == 0x8);
+
+    struct PadState {
         union {
-            u32 raw{};
+            u32_le raw{};
+            BitField<0, 1, u32> a;
+            BitField<1, 1, u32> b;
+            BitField<2, 1, u32> x;
+            BitField<3, 1, u32> y;
+            BitField<4, 1, u32> l;
+            BitField<5, 1, u32> r;
+            BitField<6, 1, u32> zl;
+            BitField<7, 1, u32> zr;
+            BitField<8, 1, u32> plus;
+            BitField<9, 1, u32> minus;
+            BitField<10, 1, u32> d_left;
+            BitField<11, 1, u32> d_up;
+            BitField<12, 1, u32> d_right;
+            BitField<13, 1, u32> d_down;
+        };
+    };
+    static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
+
+    struct Attributes {
+        union {
+            u32_le raw{};
             BitField<0, 1, u32> connected;
         };
     };
-    static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
+    static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
 
-    // This is nn::hid::DebugPadState
-    struct DebugPadState {
-        s64 sampling_number;
-        DebugPadAttribute attribute;
-        Core::HID::DebugPadButton pad_state;
-        Core::HID::AnalogStickState r_stick;
-        Core::HID::AnalogStickState l_stick;
+    struct PadStates {
+        s64_le sampling_number;
+        s64_le sampling_number2;
+        Attributes attribute;
+        PadState pad_state;
+        AnalogStick r_stick;
+        AnalogStick l_stick;
     };
-    static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
+    static_assert(sizeof(PadStates) == 0x28, "PadStates is an invalid state");
 
-    // This is nn::hid::detail::DebugPadLifo
-    Lifo<DebugPadState> debug_pad_lifo{};
-    static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
-    DebugPadState next_state{};
+    struct SharedMemory {
+        CommonHeader header;
+        std::array<PadStates, 17> pad_states;
+        INSERT_PADDING_BYTES(0x138);
+    };
+    static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
+    SharedMemory shared_memory{};
 
-    Core::HID::EmulatedController* controller;
+    std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
+        buttons;
+    std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
+        analogs;
 };
 } // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index fe895c4f6..764abb5b6 100755
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -7,7 +7,6 @@
 #include "common/settings.h"
 #include "core/core_timing.h"
 #include "core/frontend/emu_window.h"
-#include "core/hid/hid_core.h"
 #include "core/hle/service/hid/controllers/gesture.h"
 
 namespace Service::HID {
@@ -24,14 +23,16 @@ constexpr f32 Square(s32 num) {
     return static_cast<f32>(num * num);
 }
 
-Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) {
-    console = hid_core.GetEmulatedConsole();
-}
+Controller_Gesture::Controller_Gesture(Core::System& system_) : ControllerBase(system_) {}
 Controller_Gesture::~Controller_Gesture() = default;
 
 void Controller_Gesture::OnInit() {
-    gesture_lifo.buffer_count = 0;
-    gesture_lifo.buffer_tail = 0;
+    for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
+        mouse_finger_id[id] = MAX_POINTS;
+        keyboard_finger_id[id] = MAX_POINTS;
+        udp_finger_id[id] = MAX_POINTS;
+    }
+    shared_memory.header.entry_count = 0;
     force_update = true;
 }
 
@@ -39,38 +40,50 @@ void Controller_Gesture::OnRelease() {}
 
 void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
                                   std::size_t size) {
+    shared_memory.header.timestamp = core_timing.GetCPUTicks();
+    shared_memory.header.total_entry_count = 17;
+
     if (!IsControllerActivated()) {
-        gesture_lifo.buffer_count = 0;
-        gesture_lifo.buffer_tail = 0;
-        std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo));
+        shared_memory.header.entry_count = 0;
+        shared_memory.header.last_entry_index = 0;
         return;
     }
 
     ReadTouchInput();
 
     GestureProperties gesture = GetGestureProperties();
-    f32 time_difference =
-        static_cast<f32>(gesture_lifo.timestamp - last_update_timestamp) / (1000 * 1000 * 1000);
+    f32 time_difference = static_cast<f32>(shared_memory.header.timestamp - last_update_timestamp) /
+                          (1000 * 1000 * 1000);
 
     // Only update if necesary
     if (!ShouldUpdateGesture(gesture, time_difference)) {
         return;
     }
 
-    last_update_timestamp = gesture_lifo.timestamp;
+    last_update_timestamp = shared_memory.header.timestamp;
     UpdateGestureSharedMemory(data, size, gesture, time_difference);
 }
 
 void Controller_Gesture::ReadTouchInput() {
-    const auto touch_status = console->GetTouch();
-    for (std::size_t id = 0; id < fingers.size(); ++id) {
-        fingers[id] = touch_status[id];
+    const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus();
+    const Input::TouchStatus& udp_status = touch_udp_device->GetStatus();
+    for (std::size_t id = 0; id < mouse_status.size(); ++id) {
+        mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
+        udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
+    }
+
+    if (Settings::values.use_touch_from_button) {
+        const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
+        for (std::size_t id = 0; id < mouse_status.size(); ++id) {
+            keyboard_finger_id[id] =
+                UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
+        }
     }
 }
 
 bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
                                              f32 time_difference) {
-    const auto& last_entry = GetLastGestureEntry();
+    const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
     if (force_update) {
         force_update = false;
         return true;
@@ -84,7 +97,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
     }
 
     // Update on press and hold event after 0.5 seconds
-    if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 &&
+    if (last_entry.type == TouchType::Touch && last_entry.point_count == 1 &&
         time_difference > press_delay) {
         return enable_press_and_tap;
     }
@@ -95,19 +108,27 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
 void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
                                                    GestureProperties& gesture,
                                                    f32 time_difference) {
-    GestureType type = GestureType::Idle;
-    GestureAttribute attributes{};
+    TouchType type = TouchType::Idle;
+    Attribute attributes{};
 
-    const auto& last_entry = gesture_lifo.ReadCurrentEntry().state;
+    const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
+    shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
+    auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
 
-    // Reset next state to default
-    next_state.sampling_number = last_entry.sampling_number + 1;
-    next_state.delta = {};
-    next_state.vel_x = 0;
-    next_state.vel_y = 0;
-    next_state.direction = GestureDirection::None;
-    next_state.rotation_angle = 0;
-    next_state.scale = 0;
+    if (shared_memory.header.entry_count < 16) {
+        shared_memory.header.entry_count++;
+    }
+
+    cur_entry.sampling_number = last_entry.sampling_number + 1;
+    cur_entry.sampling_number2 = cur_entry.sampling_number;
+
+    // Reset values to default
+    cur_entry.delta = {};
+    cur_entry.vel_x = 0;
+    cur_entry.vel_y = 0;
+    cur_entry.direction = Direction::None;
+    cur_entry.rotation_angle = 0;
+    cur_entry.scale = 0;
 
     if (gesture.active_points > 0) {
         if (last_gesture.active_points == 0) {
@@ -120,47 +141,46 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
     }
 
     // Apply attributes
-    next_state.detection_count = gesture.detection_count;
-    next_state.type = type;
-    next_state.attributes = attributes;
-    next_state.pos = gesture.mid_point;
-    next_state.point_count = static_cast<s32>(gesture.active_points);
-    next_state.points = gesture.points;
+    cur_entry.detection_count = gesture.detection_count;
+    cur_entry.type = type;
+    cur_entry.attributes = attributes;
+    cur_entry.pos = gesture.mid_point;
+    cur_entry.point_count = static_cast<s32>(gesture.active_points);
+    cur_entry.points = gesture.points;
     last_gesture = gesture;
 
-    gesture_lifo.WriteNextEntry(next_state);
-    std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo));
+    std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
 }
 
-void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
-                                    GestureAttribute& attributes) {
+void Controller_Gesture::NewGesture(GestureProperties& gesture, TouchType& type,
+                                    Attribute& attributes) {
     const auto& last_entry = GetLastGestureEntry();
 
     gesture.detection_count++;
-    type = GestureType::Touch;
+    type = TouchType::Touch;
 
     // New touch after cancel is not considered new
-    if (last_entry.type != GestureType::Cancel) {
+    if (last_entry.type != TouchType::Cancel) {
         attributes.is_new_touch.Assign(1);
         enable_press_and_tap = true;
     }
 }
 
-void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
+void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, TouchType& type,
                                                f32 time_difference) {
     const auto& last_entry = GetLastGestureEntry();
 
     // Promote to pan type if touch moved
     for (size_t id = 0; id < MAX_POINTS; id++) {
         if (gesture.points[id] != last_gesture.points[id]) {
-            type = GestureType::Pan;
+            type = TouchType::Pan;
             break;
         }
     }
 
     // Number of fingers changed cancel the last event and clear data
     if (gesture.active_points != last_gesture.active_points) {
-        type = GestureType::Cancel;
+        type = TouchType::Cancel;
         enable_press_and_tap = false;
         gesture.active_points = 0;
         gesture.mid_point = {};
@@ -169,41 +189,41 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Gestu
     }
 
     // Calculate extra parameters of panning
-    if (type == GestureType::Pan) {
+    if (type == TouchType::Pan) {
         UpdatePanEvent(gesture, last_gesture, type, time_difference);
         return;
     }
 
     // Promote to press type
-    if (last_entry.type == GestureType::Touch) {
-        type = GestureType::Press;
+    if (last_entry.type == TouchType::Touch) {
+        type = TouchType::Press;
     }
 }
 
 void Controller_Gesture::EndGesture(GestureProperties& gesture,
-                                    GestureProperties& last_gesture_props, GestureType& type,
-                                    GestureAttribute& attributes, f32 time_difference) {
+                                    GestureProperties& last_gesture_props, TouchType& type,
+                                    Attribute& attributes, f32 time_difference) {
     const auto& last_entry = GetLastGestureEntry();
 
     if (last_gesture_props.active_points != 0) {
         switch (last_entry.type) {
-        case GestureType::Touch:
+        case TouchType::Touch:
             if (enable_press_and_tap) {
                 SetTapEvent(gesture, last_gesture_props, type, attributes);
                 return;
             }
-            type = GestureType::Cancel;
+            type = TouchType::Cancel;
             force_update = true;
             break;
-        case GestureType::Press:
-        case GestureType::Tap:
-        case GestureType::Swipe:
-        case GestureType::Pinch:
-        case GestureType::Rotate:
-            type = GestureType::Complete;
+        case TouchType::Press:
+        case TouchType::Tap:
+        case TouchType::Swipe:
+        case TouchType::Pinch:
+        case TouchType::Rotate:
+            type = TouchType::Complete;
             force_update = true;
             break;
-        case GestureType::Pan:
+        case TouchType::Pan:
             EndPanEvent(gesture, last_gesture_props, type, time_difference);
             break;
         default:
@@ -211,15 +231,15 @@ void Controller_Gesture::EndGesture(GestureProperties& gesture,
         }
         return;
     }
-    if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) {
+    if (last_entry.type == TouchType::Complete || last_entry.type == TouchType::Cancel) {
         gesture.detection_count++;
     }
 }
 
 void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
-                                     GestureProperties& last_gesture_props, GestureType& type,
-                                     GestureAttribute& attributes) {
-    type = GestureType::Tap;
+                                     GestureProperties& last_gesture_props, TouchType& type,
+                                     Attribute& attributes) {
+    type = TouchType::Tap;
     gesture = last_gesture_props;
     force_update = true;
     f32 tap_time_difference =
@@ -231,42 +251,44 @@ void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
 }
 
 void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
-                                        GestureProperties& last_gesture_props, GestureType& type,
+                                        GestureProperties& last_gesture_props, TouchType& type,
                                         f32 time_difference) {
+    auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
     const auto& last_entry = GetLastGestureEntry();
 
-    next_state.delta = gesture.mid_point - last_entry.pos;
-    next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
-    next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
+    cur_entry.delta = gesture.mid_point - last_entry.pos;
+    cur_entry.vel_x = static_cast<f32>(cur_entry.delta.x) / time_difference;
+    cur_entry.vel_y = static_cast<f32>(cur_entry.delta.y) / time_difference;
     last_pan_time_difference = time_difference;
 
     // Promote to pinch type
     if (std::abs(gesture.average_distance - last_gesture_props.average_distance) >
         pinch_threshold) {
-        type = GestureType::Pinch;
-        next_state.scale = gesture.average_distance / last_gesture_props.average_distance;
+        type = TouchType::Pinch;
+        cur_entry.scale = gesture.average_distance / last_gesture_props.average_distance;
     }
 
     const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) /
                                                   (1 + (gesture.angle * last_gesture_props.angle)));
     // Promote to rotate type
     if (std::abs(angle_between_two_lines) > angle_threshold) {
-        type = GestureType::Rotate;
-        next_state.scale = 0;
-        next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
+        type = TouchType::Rotate;
+        cur_entry.scale = 0;
+        cur_entry.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
     }
 }
 
 void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
-                                     GestureProperties& last_gesture_props, GestureType& type,
+                                     GestureProperties& last_gesture_props, TouchType& type,
                                      f32 time_difference) {
+    auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
     const auto& last_entry = GetLastGestureEntry();
-    next_state.vel_x =
+    cur_entry.vel_x =
         static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
-    next_state.vel_y =
+    cur_entry.vel_y =
         static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference);
     const f32 curr_vel =
-        std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
+        std::sqrt((cur_entry.vel_x * cur_entry.vel_x) + (cur_entry.vel_y * cur_entry.vel_y));
 
     // Set swipe event with parameters
     if (curr_vel > swipe_threshold) {
@@ -275,50 +297,105 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
     }
 
     // End panning without swipe
-    type = GestureType::Complete;
-    next_state.vel_x = 0;
-    next_state.vel_y = 0;
+    type = TouchType::Complete;
+    cur_entry.vel_x = 0;
+    cur_entry.vel_y = 0;
     force_update = true;
 }
 
 void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
-                                       GestureProperties& last_gesture_props, GestureType& type) {
+                                       GestureProperties& last_gesture_props, TouchType& type) {
+    auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
     const auto& last_entry = GetLastGestureEntry();
 
-    type = GestureType::Swipe;
+    type = TouchType::Swipe;
     gesture = last_gesture_props;
     force_update = true;
-    next_state.delta = last_entry.delta;
+    cur_entry.delta = last_entry.delta;
 
-    if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
-        if (next_state.delta.x > 0) {
-            next_state.direction = GestureDirection::Right;
+    if (std::abs(cur_entry.delta.x) > std::abs(cur_entry.delta.y)) {
+        if (cur_entry.delta.x > 0) {
+            cur_entry.direction = Direction::Right;
             return;
         }
-        next_state.direction = GestureDirection::Left;
+        cur_entry.direction = Direction::Left;
         return;
     }
-    if (next_state.delta.y > 0) {
-        next_state.direction = GestureDirection::Down;
+    if (cur_entry.delta.y > 0) {
+        cur_entry.direction = Direction::Down;
         return;
     }
-    next_state.direction = GestureDirection::Up;
+    cur_entry.direction = Direction::Up;
+}
+
+void Controller_Gesture::OnLoadInputDevices() {
+    touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
+    touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
+    touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
+}
+
+std::optional<std::size_t> Controller_Gesture::GetUnusedFingerID() const {
+    // Dont assign any touch input to a point if disabled
+    if (!Settings::values.touchscreen.enabled) {
+        return std::nullopt;
+    }
+    std::size_t first_free_id = 0;
+    while (first_free_id < MAX_POINTS) {
+        if (!fingers[first_free_id].pressed) {
+            return first_free_id;
+        } else {
+            first_free_id++;
+        }
+    }
+    return std::nullopt;
+}
+
+Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() {
+    return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
 }
 
 const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const {
-    return gesture_lifo.ReadCurrentEntry().state;
+    return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
+}
+
+std::size_t Controller_Gesture::UpdateTouchInputEvent(
+    const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
+    const auto& [x, y, pressed] = touch_input;
+    if (finger_id > MAX_POINTS) {
+        LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
+        return MAX_POINTS;
+    }
+    if (pressed) {
+        if (finger_id == MAX_POINTS) {
+            const auto first_free_id = GetUnusedFingerID();
+            if (!first_free_id) {
+                // Invalid finger id do nothing
+                return MAX_POINTS;
+            }
+            finger_id = first_free_id.value();
+            fingers[finger_id].pressed = true;
+        }
+        fingers[finger_id].pos = {x, y};
+        return finger_id;
+    }
+
+    if (finger_id != MAX_POINTS) {
+        fingers[finger_id].pressed = false;
+    }
+
+    return MAX_POINTS;
 }
 
 Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
     GestureProperties gesture;
-    std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
+    std::array<Finger, MAX_POINTS> active_fingers;
     const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
                                        [](const auto& finger) { return finger.pressed; });
     gesture.active_points =
         static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
 
     for (size_t id = 0; id < gesture.active_points; ++id) {
-        const auto& [active_x, active_y] = active_fingers[id].position;
+        const auto& [active_x, active_y] = active_fingers[id].pos;
         gesture.points[id] = {
             .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width),
             .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height),
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index f924464e0..7e7ae6625 100755
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -8,14 +8,13 @@
 #include "common/bit_field.h"
 #include "common/common_types.h"
 #include "common/point.h"
-#include "core/hid/emulated_console.h"
+#include "core/frontend/input.h"
 #include "core/hle/service/hid/controllers/controller_base.h"
-#include "core/hle/service/hid/ring_lifo.h"
 
 namespace Service::HID {
 class Controller_Gesture final : public ControllerBase {
 public:
-    explicit Controller_Gesture(Core::HID::HIDCore& hid_core_);
+    explicit Controller_Gesture(Core::System& system_);
     ~Controller_Gesture() override;
 
     // Called when the controller is initialized
@@ -27,12 +26,14 @@ public:
     // When the controller is requesting an update for the shared memory
     void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
 
+    // Called when input devices should be loaded
+    void OnLoadInputDevices() override;
+
 private:
     static constexpr size_t MAX_FINGERS = 16;
     static constexpr size_t MAX_POINTS = 4;
 
-    // This is nn::hid::GestureType
-    enum class GestureType : u32 {
+    enum class TouchType : u32 {
         Idle,     // Nothing touching the screen
         Complete, // Set at the end of a touch event
         Cancel,   // Set when the number of fingers change
@@ -45,8 +46,7 @@ private:
         Rotate,   // All points rotating from the midpoint
     };
 
-    // This is nn::hid::GestureDirection
-    enum class GestureDirection : u32 {
+    enum class Direction : u32 {
         None,
         Left,
         Up,
@@ -54,41 +54,51 @@ private:
         Down,
     };
 
-    // This is nn::hid::GestureAttribute
-    struct GestureAttribute {
+    struct Attribute {
         union {
-            u32 raw{};
+            u32_le raw{};
 
             BitField<4, 1, u32> is_new_touch;
             BitField<8, 1, u32> is_double_tap;
         };
     };
-    static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
+    static_assert(sizeof(Attribute) == 4, "Attribute is an invalid size");
 
-    // This is nn::hid::GestureState
     struct GestureState {
-        s64 sampling_number;
-        s64 detection_count;
-        GestureType type;
-        GestureDirection direction;
-        Common::Point<s32> pos;
-        Common::Point<s32> delta;
+        s64_le sampling_number;
+        s64_le sampling_number2;
+        s64_le detection_count;
+        TouchType type;
+        Direction direction;
+        Common::Point<s32_le> pos;
+        Common::Point<s32_le> delta;
         f32 vel_x;
         f32 vel_y;
-        GestureAttribute attributes;
+        Attribute attributes;
         f32 scale;
         f32 rotation_angle;
-        s32 point_count;
-        std::array<Common::Point<s32>, 4> points;
+        s32_le point_count;
+        std::array<Common::Point<s32_le>, 4> points;
+    };
+    static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
+
+    struct SharedMemory {
+        CommonHeader header;
+        std::array<GestureState, 17> gesture_states;
+    };
+    static_assert(sizeof(SharedMemory) == 0x708, "SharedMemory is an invalid size");
+
+    struct Finger {
+        Common::Point<f32> pos{};
+        bool pressed{};
     };
-    static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
 
     struct GestureProperties {
-        std::array<Common::Point<s32>, MAX_POINTS> points{};
+        std::array<Common::Point<s32_le>, MAX_POINTS> points{};
         std::size_t active_points{};
-        Common::Point<s32> mid_point{};
-        s64 detection_count{};
-        u64 delta_time{};
+        Common::Point<s32_le> mid_point{};
+        s64_le detection_count{};
+        u64_le delta_time{};
         f32 average_distance{};
         f32 angle{};
     };
@@ -104,48 +114,61 @@ private:
                                    f32 time_difference);
 
     // Initializes new gesture
-    void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
+    void NewGesture(GestureProperties& gesture, TouchType& type, Attribute& attributes);
 
     // Updates existing gesture state
-    void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference);
+    void UpdateExistingGesture(GestureProperties& gesture, TouchType& type, f32 time_difference);
 
     // Terminates exiting gesture
     void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
-                    GestureType& type, GestureAttribute& attributes, f32 time_difference);
+                    TouchType& type, Attribute& attributes, f32 time_difference);
 
     // Set current event to a tap event
     void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
-                     GestureType& type, GestureAttribute& attributes);
+                     TouchType& type, Attribute& attributes);
 
     // Calculates and set the extra parameters related to a pan event
     void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
-                        GestureType& type, f32 time_difference);
+                        TouchType& type, f32 time_difference);
 
     // Terminates the pan event
     void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
-                     GestureType& type, f32 time_difference);
+                     TouchType& type, f32 time_difference);
 
     // Set current event to a swipe event
     void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
-                       GestureType& type);
+                       TouchType& type);
+
+    // Returns an unused finger id, if there is no fingers available std::nullopt is returned.
+    [[nodiscard]] std::optional<size_t> GetUnusedFingerID() const;
 
     // Retrieves the last gesture entry, as indicated by shared memory indices.
+    [[nodiscard]] GestureState& GetLastGestureEntry();
     [[nodiscard]] const GestureState& GetLastGestureEntry() const;
 
+    /**
+     * If the touch is new it tries to assign a new finger id, if there is no fingers available no
+     * changes will be made. Updates the coordinates if the finger id it's already set. If the touch
+     * ends delays the output by one frame to set the end_touch flag before finally freeing the
+     * finger id
+     */
+    size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
+                                 size_t finger_id);
+
     // Returns the average distance, angle and middle point of the active fingers
     GestureProperties GetGestureProperties();
 
-    // This is nn::hid::detail::GestureLifo
-    Lifo<GestureState> gesture_lifo{};
-    static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
-    GestureState next_state{};
-
-    Core::HID::EmulatedConsole* console;
-
-    std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
+    SharedMemory shared_memory{};
+    std::unique_ptr<Input::TouchDevice> touch_mouse_device;
+    std::unique_ptr<Input::TouchDevice> touch_udp_device;
+    std::unique_ptr<Input::TouchDevice> touch_btn_device;
+    std::array<size_t, MAX_FINGERS> mouse_finger_id{};
+    std::array<size_t, MAX_FINGERS> keyboard_finger_id{};
+    std::array<size_t, MAX_FINGERS> udp_finger_id{};
+    std::array<Finger, MAX_POINTS> fingers{};
     GestureProperties last_gesture{};
-    s64 last_update_timestamp{};
-    s64 last_tap_timestamp{};
+    s64_le last_update_timestamp{};
+    s64_le last_tap_timestamp{};
     f32 last_pan_time_difference{};
     bool force_update{false};
     bool enable_press_and_tap{false};
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 0ef8af0e6..c6c620008 100755
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -6,18 +6,13 @@
 #include "common/common_types.h"
 #include "common/settings.h"
 #include "core/core_timing.h"
-#include "core/hid/emulated_devices.h"
-#include "core/hid/hid_core.h"
 #include "core/hle/service/hid/controllers/keyboard.h"
 
 namespace Service::HID {
 constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
+constexpr u8 KEYS_PER_BYTE = 8;
 
-Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_)
-    : ControllerBase{hid_core_} {
-    emulated_devices = hid_core.GetEmulatedDevices();
-}
-
+Controller_Keyboard::Controller_Keyboard(Core::System& system_) : ControllerBase{system_} {}
 Controller_Keyboard::~Controller_Keyboard() = default;
 
 void Controller_Keyboard::OnInit() {}
@@ -26,28 +21,51 @@ void Controller_Keyboard::OnRelease() {}
 
 void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
                                    std::size_t size) {
+    shared_memory.header.timestamp = core_timing.GetCPUTicks();
+    shared_memory.header.total_entry_count = 17;
+
     if (!IsControllerActivated()) {
-        keyboard_lifo.buffer_count = 0;
-        keyboard_lifo.buffer_tail = 0;
-        std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo));
+        shared_memory.header.entry_count = 0;
+        shared_memory.header.last_entry_index = 0;
         return;
     }
+    shared_memory.header.entry_count = 16;
 
-    const auto& last_entry = keyboard_lifo.ReadCurrentEntry().state;
-    next_state.sampling_number = last_entry.sampling_number + 1;
+    const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
+    shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
+    auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
 
+    cur_entry.sampling_number = last_entry.sampling_number + 1;
+    cur_entry.sampling_number2 = cur_entry.sampling_number;
+
+    cur_entry.key.fill(0);
     if (Settings::values.keyboard_enabled) {
-        const auto& keyboard_state = emulated_devices->GetKeyboard();
-        const auto& keyboard_modifier_state = emulated_devices->GetKeyboardModifier();
+        for (std::size_t i = 0; i < keyboard_keys.size(); ++i) {
+            auto& entry = cur_entry.key[i / KEYS_PER_BYTE];
+            entry = static_cast<u8>(entry | (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)));
+        }
 
-        next_state.key = keyboard_state;
-        next_state.modifier = keyboard_modifier_state;
-        // This is always enabled on HW. Check what it actually does
-        next_state.modifier.unknown.Assign(1);
+        using namespace Settings::NativeKeyboard;
+
+        // TODO: Assign the correct key to all modifiers
+        cur_entry.modifier.control.Assign(keyboard_mods[LeftControl]->GetStatus());
+        cur_entry.modifier.shift.Assign(keyboard_mods[LeftShift]->GetStatus());
+        cur_entry.modifier.left_alt.Assign(keyboard_mods[LeftAlt]->GetStatus());
+        cur_entry.modifier.right_alt.Assign(keyboard_mods[RightAlt]->GetStatus());
+        cur_entry.modifier.gui.Assign(0);
+        cur_entry.modifier.caps_lock.Assign(keyboard_mods[CapsLock]->GetStatus());
+        cur_entry.modifier.scroll_lock.Assign(keyboard_mods[ScrollLock]->GetStatus());
+        cur_entry.modifier.num_lock.Assign(keyboard_mods[NumLock]->GetStatus());
+        cur_entry.modifier.katakana.Assign(0);
+        cur_entry.modifier.hiragana.Assign(0);
     }
-
-    keyboard_lifo.WriteNextEntry(next_state);
-    std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo));
+    std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
 }
 
+void Controller_Keyboard::OnLoadInputDevices() {
+    std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(),
+                   keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>);
+    std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(),
+                   keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>);
+}
 } // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index ec5dd607c..172a80e9c 100755
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -8,20 +8,15 @@
 #include "common/bit_field.h"
 #include "common/common_funcs.h"
 #include "common/common_types.h"
+#include "common/settings.h"
 #include "common/swap.h"
+#include "core/frontend/input.h"
 #include "core/hle/service/hid/controllers/controller_base.h"
-#include "core/hle/service/hid/ring_lifo.h"
-
-namespace Core::HID {
-class EmulatedDevices;
-struct KeyboardModifier;
-struct KeyboardKey;
-} // namespace Core::HID
 
 namespace Service::HID {
 class Controller_Keyboard final : public ControllerBase {
 public:
-    explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_);
+    explicit Controller_Keyboard(Core::System& system_);
     ~Controller_Keyboard() override;
 
     // Called when the controller is initialized
@@ -33,20 +28,47 @@ public:
     // When the controller is requesting an update for the shared memory
     void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
 
+    // Called when input devices should be loaded
+    void OnLoadInputDevices() override;
+
 private:
-    // This is nn::hid::detail::KeyboardState
-    struct KeyboardState {
-        s64 sampling_number;
-        Core::HID::KeyboardModifier modifier;
-        Core::HID::KeyboardKey key;
+    struct Modifiers {
+        union {
+            u32_le raw{};
+            BitField<0, 1, u32> control;
+            BitField<1, 1, u32> shift;
+            BitField<2, 1, u32> left_alt;
+            BitField<3, 1, u32> right_alt;
+            BitField<4, 1, u32> gui;
+            BitField<8, 1, u32> caps_lock;
+            BitField<9, 1, u32> scroll_lock;
+            BitField<10, 1, u32> num_lock;
+            BitField<11, 1, u32> katakana;
+            BitField<12, 1, u32> hiragana;
+        };
     };
-    static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
+    static_assert(sizeof(Modifiers) == 0x4, "Modifiers is an invalid size");
 
-    // This is nn::hid::detail::KeyboardLifo
-    Lifo<KeyboardState> keyboard_lifo{};
-    static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
-    KeyboardState next_state{};
+    struct KeyboardState {
+        s64_le sampling_number;
+        s64_le sampling_number2;
 
-    Core::HID::EmulatedDevices* emulated_devices;
+        Modifiers modifier;
+        std::array<u8, 32> key;
+    };
+    static_assert(sizeof(KeyboardState) == 0x38, "KeyboardState is an invalid size");
+
+    struct SharedMemory {
+        CommonHeader header;
+        std::array<KeyboardState, 17> pad_states;
+        INSERT_PADDING_BYTES(0x28);
+    };
+    static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
+    SharedMemory shared_memory{};
+
+    std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys>
+        keyboard_keys;
+    std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
+        keyboard_mods;
 };
 } // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 83e69ca94..544a71948 100755
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -6,17 +6,12 @@
 #include "common/common_types.h"
 #include "core/core_timing.h"
 #include "core/frontend/emu_window.h"
-#include "core/hid/emulated_devices.h"
-#include "core/hid/hid_core.h"
 #include "core/hle/service/hid/controllers/mouse.h"
 
 namespace Service::HID {
 constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
 
-Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
-    emulated_devices = hid_core.GetEmulatedDevices();
-}
-
+Controller_Mouse::Controller_Mouse(Core::System& system_) : ControllerBase{system_} {}
 Controller_Mouse::~Controller_Mouse() = default;
 
 void Controller_Mouse::OnInit() {}
@@ -24,33 +19,50 @@ void Controller_Mouse::OnRelease() {}
 
 void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
                                 std::size_t size) {
+    shared_memory.header.timestamp = core_timing.GetCPUTicks();
+    shared_memory.header.total_entry_count = 17;
+
     if (!IsControllerActivated()) {
-        mouse_lifo.buffer_count = 0;
-        mouse_lifo.buffer_tail = 0;
-        std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo));
+        shared_memory.header.entry_count = 0;
+        shared_memory.header.last_entry_index = 0;
         return;
     }
+    shared_memory.header.entry_count = 16;
 
-    const auto& last_entry = mouse_lifo.ReadCurrentEntry().state;
-    next_state.sampling_number = last_entry.sampling_number + 1;
+    auto& last_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
+    shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
+    auto& cur_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
 
-    next_state.attribute.raw = 0;
+    cur_entry.sampling_number = last_entry.sampling_number + 1;
+    cur_entry.sampling_number2 = cur_entry.sampling_number;
+
+    cur_entry.attribute.raw = 0;
     if (Settings::values.mouse_enabled) {
-        const auto& mouse_button_state = emulated_devices->GetMouseButtons();
-        const auto& mouse_position_state = emulated_devices->GetMousePosition();
-        next_state.attribute.is_connected.Assign(1);
-        next_state.x = mouse_position_state.x;
-        next_state.y = mouse_position_state.y;
-        next_state.delta_x = next_state.x - last_entry.x;
-        next_state.delta_y = next_state.y - last_entry.y;
-        next_state.delta_wheel_x = mouse_position_state.delta_wheel_x;
-        next_state.delta_wheel_y = mouse_position_state.delta_wheel_y;
+        const auto [px, py, sx, sy] = mouse_device->GetStatus();
+        const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width);
+        const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height);
+        cur_entry.x = x;
+        cur_entry.y = y;
+        cur_entry.delta_x = x - last_entry.x;
+        cur_entry.delta_y = y - last_entry.y;
+        cur_entry.mouse_wheel_x = sx;
+        cur_entry.mouse_wheel_y = sy;
+        cur_entry.attribute.is_connected.Assign(1);
 
-        next_state.button = mouse_button_state;
+        using namespace Settings::NativeMouseButton;
+        cur_entry.button.left.Assign(mouse_button_devices[Left]->GetStatus());
+        cur_entry.button.right.Assign(mouse_button_devices[Right]->GetStatus());
+        cur_entry.button.middle.Assign(mouse_button_devices[Middle]->GetStatus());
+        cur_entry.button.forward.Assign(mouse_button_devices[Forward]->GetStatus());
+        cur_entry.button.back.Assign(mouse_button_devices[Back]->GetStatus());
     }
 
-    mouse_lifo.WriteNextEntry(next_state);
-    std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo));
+    std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
 }
 
+void Controller_Mouse::OnLoadInputDevices() {
+    mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device);
+    std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
+                   mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>);
+}
 } // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 25017f117..3d391a798 100755
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -7,19 +7,15 @@
 #include <array>
 #include "common/bit_field.h"
 #include "common/common_types.h"
+#include "common/settings.h"
 #include "common/swap.h"
+#include "core/frontend/input.h"
 #include "core/hle/service/hid/controllers/controller_base.h"
-#include "core/hle/service/hid/ring_lifo.h"
-
-namespace Core::HID {
-class EmulatedDevices;
-struct MouseState;
-} // namespace Core::HID
 
 namespace Service::HID {
 class Controller_Mouse final : public ControllerBase {
 public:
-    explicit Controller_Mouse(Core::HID::HIDCore& hid_core_);
+    explicit Controller_Mouse(Core::System& system_);
     ~Controller_Mouse() override;
 
     // Called when the controller is initialized
@@ -31,12 +27,53 @@ public:
     // When the controller is requesting an update for the shared memory
     void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
 
-private:
-    // This is nn::hid::detail::MouseLifo
-    Lifo<Core::HID::MouseState> mouse_lifo{};
-    static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
-    Core::HID::MouseState next_state{};
+    // Called when input devices should be loaded
+    void OnLoadInputDevices() override;
 
-    Core::HID::EmulatedDevices* emulated_devices;
+private:
+    struct Buttons {
+        union {
+            u32_le raw{};
+            BitField<0, 1, u32> left;
+            BitField<1, 1, u32> right;
+            BitField<2, 1, u32> middle;
+            BitField<3, 1, u32> forward;
+            BitField<4, 1, u32> back;
+        };
+    };
+    static_assert(sizeof(Buttons) == 0x4, "Buttons is an invalid size");
+
+    struct Attributes {
+        union {
+            u32_le raw{};
+            BitField<0, 1, u32> transferable;
+            BitField<1, 1, u32> is_connected;
+        };
+    };
+    static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
+
+    struct MouseState {
+        s64_le sampling_number;
+        s64_le sampling_number2;
+        s32_le x;
+        s32_le y;
+        s32_le delta_x;
+        s32_le delta_y;
+        s32_le mouse_wheel_x;
+        s32_le mouse_wheel_y;
+        Buttons button;
+        Attributes attribute;
+    };
+    static_assert(sizeof(MouseState) == 0x30, "MouseState is an invalid size");
+
+    struct SharedMemory {
+        CommonHeader header;
+        std::array<MouseState, 17> mouse_states;
+    };
+    SharedMemory shared_memory{};
+
+    std::unique_ptr<Input::MouseDevice> mouse_device;
+    std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
+        mouse_button_devices;
 };
 } // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index eaec79139..196876810 100755
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -10,9 +10,9 @@
 #include "common/common_types.h"
 #include "common/logging/log.h"
 #include "common/settings.h"
+#include "core/core.h"
 #include "core/core_timing.h"
-#include "core/hid/emulated_controller.h"
-#include "core/hid/hid_core.h"
+#include "core/frontend/input.h"
 #include "core/hle/kernel/k_event.h"
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/k_writable_event.h"
@@ -20,26 +20,120 @@
 #include "core/hle/service/kernel_helpers.h"
 
 namespace Service::HID {
+constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
+constexpr s32 HID_TRIGGER_MAX = 0x7fff;
+[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
 constexpr std::size_t NPAD_OFFSET = 0x9A00;
-constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
-    Core::HID::NpadIdType::Player1,  Core::HID::NpadIdType::Player2, Core::HID::NpadIdType::Player3,
-    Core::HID::NpadIdType::Player4,  Core::HID::NpadIdType::Player5, Core::HID::NpadIdType::Player6,
-    Core::HID::NpadIdType::Player7,  Core::HID::NpadIdType::Player8, Core::HID::NpadIdType::Other,
-    Core::HID::NpadIdType::Handheld,
+constexpr u32 BATTERY_FULL = 2;
+constexpr u32 MAX_NPAD_ID = 7;
+constexpr std::size_t HANDHELD_INDEX = 8;
+constexpr std::array<u32, 10> npad_id_list{
+    0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
 };
 
-bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
+enum class JoystickId : std::size_t {
+    Joystick_Left,
+    Joystick_Right,
+};
+
+Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
+    Settings::ControllerType type) {
+    switch (type) {
+    case Settings::ControllerType::ProController:
+        return NPadControllerType::ProController;
+    case Settings::ControllerType::DualJoyconDetached:
+        return NPadControllerType::JoyDual;
+    case Settings::ControllerType::LeftJoycon:
+        return NPadControllerType::JoyLeft;
+    case Settings::ControllerType::RightJoycon:
+        return NPadControllerType::JoyRight;
+    case Settings::ControllerType::Handheld:
+        return NPadControllerType::Handheld;
+    case Settings::ControllerType::GameCube:
+        return NPadControllerType::GameCube;
+    default:
+        UNREACHABLE();
+        return NPadControllerType::ProController;
+    }
+}
+
+Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
+    Controller_NPad::NPadControllerType type) {
+    switch (type) {
+    case NPadControllerType::ProController:
+        return Settings::ControllerType::ProController;
+    case NPadControllerType::JoyDual:
+        return Settings::ControllerType::DualJoyconDetached;
+    case NPadControllerType::JoyLeft:
+        return Settings::ControllerType::LeftJoycon;
+    case NPadControllerType::JoyRight:
+        return Settings::ControllerType::RightJoycon;
+    case NPadControllerType::Handheld:
+        return Settings::ControllerType::Handheld;
+    case NPadControllerType::GameCube:
+        return Settings::ControllerType::GameCube;
+    default:
+        UNREACHABLE();
+        return Settings::ControllerType::ProController;
+    }
+}
+
+std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
     switch (npad_id) {
-    case Core::HID::NpadIdType::Player1:
-    case Core::HID::NpadIdType::Player2:
-    case Core::HID::NpadIdType::Player3:
-    case Core::HID::NpadIdType::Player4:
-    case Core::HID::NpadIdType::Player5:
-    case Core::HID::NpadIdType::Player6:
-    case Core::HID::NpadIdType::Player7:
-    case Core::HID::NpadIdType::Player8:
-    case Core::HID::NpadIdType::Other:
-    case Core::HID::NpadIdType::Handheld:
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case 5:
+    case 6:
+    case 7:
+        return npad_id;
+    case HANDHELD_INDEX:
+    case NPAD_HANDHELD:
+        return HANDHELD_INDEX;
+    case 9:
+    case NPAD_UNKNOWN:
+        return 9;
+    default:
+        UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id);
+        return 0;
+    }
+}
+
+u32 Controller_NPad::IndexToNPad(std::size_t index) {
+    switch (index) {
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case 5:
+    case 6:
+    case 7:
+        return static_cast<u32>(index);
+    case HANDHELD_INDEX:
+        return NPAD_HANDHELD;
+    case 9:
+        return NPAD_UNKNOWN;
+    default:
+        UNIMPLEMENTED_MSG("Unknown npad index {}", index);
+        return 0;
+    }
+}
+
+bool Controller_NPad::IsNpadIdValid(u32 npad_id) {
+    switch (npad_id) {
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case 5:
+    case 6:
+    case 7:
+    case NPAD_UNKNOWN:
+    case NPAD_HANDHELD:
         return true;
     default:
         LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id);
@@ -47,213 +141,131 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
     }
 }
 
-bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
-    return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) &&
-           device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType &&
-           device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
+bool Controller_NPad::IsDeviceHandleValid(const DeviceHandle& device_handle) {
+    return IsNpadIdValid(device_handle.npad_id) &&
+           device_handle.npad_type < NpadType::MaxNpadType &&
+           device_handle.device_index < DeviceIndex::MaxDeviceIndex;
 }
 
-bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) {
-    return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) &&
-           device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType &&
-           device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
-}
-
-Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_,
+Controller_NPad::Controller_NPad(Core::System& system_,
                                  KernelHelpers::ServiceContext& service_context_)
-    : ControllerBase{hid_core_}, service_context{service_context_} {
-    for (std::size_t i = 0; i < controller_data.size(); ++i) {
-        auto& controller = controller_data[i];
-        controller.device = hid_core.GetEmulatedControllerByIndex(i);
-        controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
-            DEFAULT_VIBRATION_VALUE;
-        controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex].latest_vibration_value =
-            DEFAULT_VIBRATION_VALUE;
-        Core::HID::ControllerUpdateCallback engine_callback{
-            .on_change = [this,
-                          i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
-            .is_npad_service = true,
-        };
-        controller.callback_key = controller.device->SetCallback(engine_callback);
-    }
+    : ControllerBase{system_}, service_context{service_context_} {
+    latest_vibration_values.fill({DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE});
 }
 
 Controller_NPad::~Controller_NPad() {
-    for (std::size_t i = 0; i < controller_data.size(); ++i) {
-        auto& controller = controller_data[i];
-        controller.device->DeleteCallback(controller.callback_key);
-    }
     OnRelease();
 }
 
-void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
-                                       std::size_t controller_idx) {
-    if (type == Core::HID::ControllerTriggerType::All) {
-        ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx);
-        ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx);
+void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
+    const auto controller_type = connected_controllers[controller_idx].type;
+    auto& controller = shared_memory_entries[controller_idx];
+    if (controller_type == NPadControllerType::None) {
+        styleset_changed_events[controller_idx]->GetWritableEvent().Signal();
         return;
     }
-    if (controller_idx >= controller_data.size()) {
-        return;
-    }
-
-    auto& controller = controller_data[controller_idx];
-    const auto is_connected = controller.device->IsConnected();
-    const auto npad_type = controller.device->GetNpadStyleIndex();
-    const auto npad_id = controller.device->GetNpadIdType();
-    switch (type) {
-    case Core::HID::ControllerTriggerType::Connected:
-    case Core::HID::ControllerTriggerType::Disconnected:
-        if (is_connected == controller.is_connected) {
-            return;
-        }
-        UpdateControllerAt(npad_type, npad_id, is_connected);
-        break;
-    case Core::HID::ControllerTriggerType::Battery: {
-        if (!controller.is_connected) {
-            return;
-        }
-        auto& shared_memory = controller.shared_memory_entry;
-        const auto& battery_level = controller.device->GetBattery();
-        shared_memory.battery_level_dual = battery_level.dual.battery_level;
-        shared_memory.battery_level_left = battery_level.left.battery_level;
-        shared_memory.battery_level_right = battery_level.right.battery_level;
-        break;
-    }
-    default:
-        break;
-    }
-}
-
-void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
-    LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
-    auto& controller = GetControllerFromNpadIdType(npad_id);
-    const auto controller_type = controller.device->GetNpadStyleIndex();
-    auto& shared_memory = controller.shared_memory_entry;
-    if (controller_type == Core::HID::NpadStyleIndex::None) {
-        controller.styleset_changed_event->GetWritableEvent().Signal();
-        return;
-    }
-    shared_memory.style_tag.raw = Core::HID::NpadStyleSet::None;
-    shared_memory.device_type.raw = 0;
-    shared_memory.system_properties.raw = 0;
+    controller.style_set.raw = 0; // Zero out
+    controller.device_type.raw = 0;
+    controller.system_properties.raw = 0;
     switch (controller_type) {
-    case Core::HID::NpadStyleIndex::None:
+    case NPadControllerType::None:
         UNREACHABLE();
         break;
-    case Core::HID::NpadStyleIndex::ProController:
-        shared_memory.style_tag.fullkey.Assign(1);
-        shared_memory.device_type.fullkey.Assign(1);
-        shared_memory.system_properties.is_vertical.Assign(1);
-        shared_memory.system_properties.use_plus.Assign(1);
-        shared_memory.system_properties.use_minus.Assign(1);
-        shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
-        shared_memory.applet_footer.type = AppletFooterUiType::SwitchProController;
+    case NPadControllerType::ProController:
+        controller.style_set.fullkey.Assign(1);
+        controller.device_type.fullkey.Assign(1);
+        controller.system_properties.is_vertical.Assign(1);
+        controller.system_properties.use_plus.Assign(1);
+        controller.system_properties.use_minus.Assign(1);
+        controller.assignment_mode = NpadAssignments::Single;
+        controller.footer_type = AppletFooterUiType::SwitchProController;
         break;
-    case Core::HID::NpadStyleIndex::Handheld:
-        shared_memory.style_tag.handheld.Assign(1);
-        shared_memory.device_type.handheld_left.Assign(1);
-        shared_memory.device_type.handheld_right.Assign(1);
-        shared_memory.system_properties.is_vertical.Assign(1);
-        shared_memory.system_properties.use_plus.Assign(1);
-        shared_memory.system_properties.use_minus.Assign(1);
-        shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
-        shared_memory.applet_footer.type = AppletFooterUiType::HandheldJoyConLeftJoyConRight;
+    case NPadControllerType::Handheld:
+        controller.style_set.handheld.Assign(1);
+        controller.device_type.handheld_left.Assign(1);
+        controller.device_type.handheld_right.Assign(1);
+        controller.system_properties.is_vertical.Assign(1);
+        controller.system_properties.use_plus.Assign(1);
+        controller.system_properties.use_minus.Assign(1);
+        controller.assignment_mode = NpadAssignments::Dual;
+        controller.footer_type = AppletFooterUiType::HandheldJoyConLeftJoyConRight;
         break;
-    case Core::HID::NpadStyleIndex::JoyconDual:
-        shared_memory.style_tag.joycon_dual.Assign(1);
-        shared_memory.device_type.joycon_left.Assign(1);
-        shared_memory.device_type.joycon_right.Assign(1);
-        shared_memory.system_properties.is_vertical.Assign(1);
-        shared_memory.system_properties.use_plus.Assign(1);
-        shared_memory.system_properties.use_minus.Assign(1);
-        shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
-        shared_memory.applet_footer.type = AppletFooterUiType::JoyDual;
+    case NPadControllerType::JoyDual:
+        controller.style_set.joycon_dual.Assign(1);
+        controller.device_type.joycon_left.Assign(1);
+        controller.device_type.joycon_right.Assign(1);
+        controller.system_properties.is_vertical.Assign(1);
+        controller.system_properties.use_plus.Assign(1);
+        controller.system_properties.use_minus.Assign(1);
+        controller.assignment_mode = NpadAssignments::Dual;
+        controller.footer_type = AppletFooterUiType::JoyDual;
         break;
-    case Core::HID::NpadStyleIndex::JoyconLeft:
-        shared_memory.style_tag.joycon_left.Assign(1);
-        shared_memory.device_type.joycon_left.Assign(1);
-        shared_memory.system_properties.is_horizontal.Assign(1);
-        shared_memory.system_properties.use_minus.Assign(1);
-        shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
-        shared_memory.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
+    case NPadControllerType::JoyLeft:
+        controller.style_set.joycon_left.Assign(1);
+        controller.device_type.joycon_left.Assign(1);
+        controller.system_properties.is_horizontal.Assign(1);
+        controller.system_properties.use_minus.Assign(1);
+        controller.assignment_mode = NpadAssignments::Single;
+        controller.footer_type = AppletFooterUiType::JoyLeftHorizontal;
         break;
-    case Core::HID::NpadStyleIndex::JoyconRight:
-        shared_memory.style_tag.joycon_right.Assign(1);
-        shared_memory.device_type.joycon_right.Assign(1);
-        shared_memory.system_properties.is_horizontal.Assign(1);
-        shared_memory.system_properties.use_plus.Assign(1);
-        shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
-        shared_memory.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
+    case NPadControllerType::JoyRight:
+        controller.style_set.joycon_right.Assign(1);
+        controller.device_type.joycon_right.Assign(1);
+        controller.system_properties.is_horizontal.Assign(1);
+        controller.system_properties.use_plus.Assign(1);
+        controller.assignment_mode = NpadAssignments::Single;
+        controller.footer_type = AppletFooterUiType::JoyRightHorizontal;
         break;
-    case Core::HID::NpadStyleIndex::GameCube:
-        shared_memory.style_tag.gamecube.Assign(1);
-        shared_memory.device_type.fullkey.Assign(1);
-        shared_memory.system_properties.is_vertical.Assign(1);
-        shared_memory.system_properties.use_plus.Assign(1);
+    case NPadControllerType::GameCube:
+        controller.style_set.gamecube.Assign(1);
+        // The GC Controller behaves like a wired Pro Controller
+        controller.device_type.fullkey.Assign(1);
+        controller.system_properties.is_vertical.Assign(1);
+        controller.system_properties.use_plus.Assign(1);
         break;
-    case Core::HID::NpadStyleIndex::Pokeball:
-        shared_memory.style_tag.palma.Assign(1);
-        shared_memory.device_type.palma.Assign(1);
-        shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
-        break;
-    case Core::HID::NpadStyleIndex::NES:
-        shared_memory.style_tag.lark.Assign(1);
-        shared_memory.device_type.fullkey.Assign(1);
-        break;
-    case Core::HID::NpadStyleIndex::SNES:
-        shared_memory.style_tag.lucia.Assign(1);
-        shared_memory.device_type.fullkey.Assign(1);
-        shared_memory.applet_footer.type = AppletFooterUiType::Lucia;
-        break;
-    case Core::HID::NpadStyleIndex::N64:
-        shared_memory.style_tag.lagoon.Assign(1);
-        shared_memory.device_type.fullkey.Assign(1);
-        shared_memory.applet_footer.type = AppletFooterUiType::Lagon;
-        break;
-    case Core::HID::NpadStyleIndex::SegaGenesis:
-        shared_memory.style_tag.lager.Assign(1);
-        shared_memory.device_type.fullkey.Assign(1);
-        break;
-    default:
+    case NPadControllerType::Pokeball:
+        controller.style_set.palma.Assign(1);
+        controller.device_type.palma.Assign(1);
+        controller.assignment_mode = NpadAssignments::Single;
         break;
     }
 
-    const auto& body_colors = controller.device->GetColors();
+    controller.fullkey_color.attribute = ColorAttributes::Ok;
+    controller.fullkey_color.fullkey.body = 0;
+    controller.fullkey_color.fullkey.button = 0;
 
-    shared_memory.fullkey_color.attribute = ColorAttribute::Ok;
-    shared_memory.fullkey_color.fullkey = body_colors.fullkey;
-
-    shared_memory.joycon_color.attribute = ColorAttribute::Ok;
-    shared_memory.joycon_color.left = body_colors.left;
-    shared_memory.joycon_color.right = body_colors.right;
+    controller.joycon_color.attribute = ColorAttributes::Ok;
+    controller.joycon_color.left.body =
+        Settings::values.players.GetValue()[controller_idx].body_color_left;
+    controller.joycon_color.left.button =
+        Settings::values.players.GetValue()[controller_idx].button_color_left;
+    controller.joycon_color.right.body =
+        Settings::values.players.GetValue()[controller_idx].body_color_right;
+    controller.joycon_color.right.button =
+        Settings::values.players.GetValue()[controller_idx].button_color_right;
 
     // TODO: Investigate when we should report all batery types
-    const auto& battery_level = controller.device->GetBattery();
-    shared_memory.battery_level_dual = battery_level.dual.battery_level;
-    shared_memory.battery_level_left = battery_level.left.battery_level;
-    shared_memory.battery_level_right = battery_level.right.battery_level;
+    controller.battery_level_dual = BATTERY_FULL;
+    controller.battery_level_left = BATTERY_FULL;
+    controller.battery_level_right = BATTERY_FULL;
 
-    controller.is_connected = true;
-    controller.device->Connect();
-    SignalStyleSetChangedEvent(npad_id);
-    WriteEmptyEntry(controller.shared_memory_entry);
+    SignalStyleSetChangedEvent(IndexToNPad(controller_idx));
 }
 
 void Controller_NPad::OnInit() {
+    for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
+        styleset_changed_events[i] =
+            service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
+    }
+
     if (!IsControllerActivated()) {
         return;
     }
 
-    for (std::size_t i = 0; i < controller_data.size(); ++i) {
-        auto& controller = controller_data[i];
-        controller.styleset_changed_event =
-            service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
-    }
+    OnLoadInputDevices();
 
-    if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
+    if (style.raw == 0) {
         // We want to support all controllers
-        Core::HID::NpadStyleTag style{};
         style.handheld.Assign(1);
         style.joycon_left.Assign(1);
         style.joycon_right.Assign(1);
@@ -261,120 +273,173 @@ void Controller_NPad::OnInit() {
         style.fullkey.Assign(1);
         style.gamecube.Assign(1);
         style.palma.Assign(1);
-        hid_core.SetSupportedStyleTag(style);
+    }
+
+    std::transform(Settings::values.players.GetValue().begin(),
+                   Settings::values.players.GetValue().end(), connected_controllers.begin(),
+                   [](const Settings::PlayerInput& player) {
+                       return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
+                                               player.connected};
+                   });
+
+    // Connect the Player 1 or Handheld controller if none are connected.
+    if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
+                     [](const ControllerHolder& controller) { return controller.is_connected; })) {
+        const auto controller =
+            MapSettingsTypeToNPad(Settings::values.players.GetValue()[0].controller_type);
+        if (controller == NPadControllerType::Handheld) {
+            Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
+            connected_controllers[HANDHELD_INDEX] = {controller, true};
+        } else {
+            Settings::values.players.GetValue()[0].connected = true;
+            connected_controllers[0] = {controller, true};
+        }
+    }
+
+    // Account for handheld
+    if (connected_controllers[HANDHELD_INDEX].is_connected) {
+        connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
     }
 
     supported_npad_id_types.resize(npad_id_list.size());
     std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
-                npad_id_list.size() * sizeof(Core::HID::NpadIdType));
+                npad_id_list.size() * sizeof(u32));
 
-    // Prefill controller buffers
-    for (auto& controller : controller_data) {
-        auto& npad = controller.shared_memory_entry;
-        npad.fullkey_color = {
-            .attribute = ColorAttribute::NoController,
-            .fullkey = {},
-        };
-        npad.joycon_color = {
-            .attribute = ColorAttribute::NoController,
-            .left = {},
-            .right = {},
-        };
-        // HW seems to initialize the first 19 entries
-        for (std::size_t i = 0; i < 19; ++i) {
-            WriteEmptyEntry(npad);
-        }
-    }
-
-    // Connect controllers
-    for (auto& controller : controller_data) {
-        const auto& device = controller.device;
-        if (device->IsConnected()) {
-            AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
+    for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
+        const auto& controller = connected_controllers[i];
+        if (controller.is_connected) {
+            AddNewControllerAt(controller.type, i);
         }
     }
 }
 
-void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
-    NPadGenericState dummy_pad_state{};
-    NpadGcTriggerState dummy_gc_state{};
-    dummy_pad_state.sampling_number = npad.fullkey_lifo.ReadCurrentEntry().sampling_number + 1;
-    npad.fullkey_lifo.WriteNextEntry(dummy_pad_state);
-    dummy_pad_state.sampling_number = npad.handheld_lifo.ReadCurrentEntry().sampling_number + 1;
-    npad.handheld_lifo.WriteNextEntry(dummy_pad_state);
-    dummy_pad_state.sampling_number = npad.joy_dual_lifo.ReadCurrentEntry().sampling_number + 1;
-    npad.joy_dual_lifo.WriteNextEntry(dummy_pad_state);
-    dummy_pad_state.sampling_number = npad.joy_left_lifo.ReadCurrentEntry().sampling_number + 1;
-    npad.joy_left_lifo.WriteNextEntry(dummy_pad_state);
-    dummy_pad_state.sampling_number = npad.joy_right_lifo.ReadCurrentEntry().sampling_number + 1;
-    npad.joy_right_lifo.WriteNextEntry(dummy_pad_state);
-    dummy_pad_state.sampling_number = npad.palma_lifo.ReadCurrentEntry().sampling_number + 1;
-    npad.palma_lifo.WriteNextEntry(dummy_pad_state);
-    dummy_pad_state.sampling_number = npad.system_ext_lifo.ReadCurrentEntry().sampling_number + 1;
-    npad.system_ext_lifo.WriteNextEntry(dummy_pad_state);
-    dummy_gc_state.sampling_number = npad.gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1;
-    npad.gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
+void Controller_NPad::OnLoadInputDevices() {
+    const auto& players = Settings::values.players.GetValue();
+
+    std::lock_guard lock{mutex};
+    for (std::size_t i = 0; i < players.size(); ++i) {
+        std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
+                       players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
+                       buttons[i].begin(), Input::CreateDevice<Input::ButtonDevice>);
+        std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
+                       players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
+                       sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
+        std::transform(players[i].vibrations.begin() +
+                           Settings::NativeVibration::VIBRATION_HID_BEGIN,
+                       players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END,
+                       vibrations[i].begin(), Input::CreateDevice<Input::VibrationDevice>);
+        std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
+                       players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
+                       motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
+        for (std::size_t device_idx = 0; device_idx < vibrations[i].size(); ++device_idx) {
+            InitializeVibrationDeviceAtIndex(i, device_idx);
+        }
+    }
 }
 
 void Controller_NPad::OnRelease() {
-    for (std::size_t i = 0; i < controller_data.size(); ++i) {
-        auto& controller = controller_data[i];
-        service_context.CloseEvent(controller.styleset_changed_event);
-        for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
-            VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_idx, {});
+    for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) {
+        for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) {
+            VibrateControllerAtIndex(npad_idx, device_idx, {});
         }
     }
+
+    for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
+        service_context.CloseEvent(styleset_changed_events[i]);
+    }
 }
 
-void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
+void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
     std::lock_guard lock{mutex};
-    auto& controller = GetControllerFromNpadIdType(npad_id);
-    const auto controller_type = controller.device->GetNpadStyleIndex();
-    if (!controller.device->IsConnected()) {
+
+    const auto controller_idx = NPadIdToIndex(npad_id);
+    const auto controller_type = connected_controllers[controller_idx].type;
+    if (!connected_controllers[controller_idx].is_connected) {
         return;
     }
+    auto& pad_state = npad_pad_states[controller_idx].pad_states;
+    auto& lstick_entry = npad_pad_states[controller_idx].l_stick;
+    auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
+    auto& trigger_entry = npad_trigger_states[controller_idx];
+    const auto& button_state = buttons[controller_idx];
+    const auto& analog_state = sticks[controller_idx];
+    const auto [stick_l_x_f, stick_l_y_f] =
+        analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
+    const auto [stick_r_x_f, stick_r_y_f] =
+        analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
 
-    auto& pad_entry = controller.npad_pad_state;
-    auto& trigger_entry = controller.npad_trigger_state;
-    const auto button_state = controller.device->GetNpadButtons();
-    const auto stick_state = controller.device->GetSticks();
+    using namespace Settings::NativeButton;
+    if (controller_type != NPadControllerType::JoyLeft) {
+        pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
 
-    using btn = Core::HID::NpadButton;
-    pad_entry.npad_buttons.raw = btn::None;
-    if (controller_type != Core::HID::NpadStyleIndex::JoyconLeft) {
-        constexpr btn right_button_mask = btn::A | btn::B | btn::X | btn::Y | btn::StickR | btn::R |
-                                          btn::ZR | btn::Plus | btn::StickRLeft | btn::StickRUp |
-                                          btn::StickRRight | btn::StickRDown;
-        pad_entry.npad_buttons.raw |= button_state.raw & right_button_mask;
-        pad_entry.r_stick = stick_state.right;
+        pad_state.r_stick_right.Assign(
+            analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
+                ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
+        pad_state.r_stick_left.Assign(
+            analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
+                ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
+        pad_state.r_stick_up.Assign(
+            analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
+                ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
+        pad_state.r_stick_down.Assign(
+            analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
+                ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
+        rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
+        rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
     }
 
-    if (controller_type != Core::HID::NpadStyleIndex::JoyconRight) {
-        constexpr btn left_button_mask =
-            btn::Left | btn::Up | btn::Right | btn::Down | btn::StickL | btn::L | btn::ZL |
-            btn::Minus | btn::StickLLeft | btn::StickLUp | btn::StickLRight | btn::StickLDown;
-        pad_entry.npad_buttons.raw |= button_state.raw & left_button_mask;
-        pad_entry.l_stick = stick_state.left;
+    if (controller_type != NPadControllerType::JoyRight) {
+        pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
+
+        pad_state.l_stick_right.Assign(
+            analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
+                ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
+        pad_state.l_stick_left.Assign(
+            analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
+                ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
+        pad_state.l_stick_up.Assign(
+            analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
+                ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
+        pad_state.l_stick_down.Assign(
+            analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
+                ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
+        lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
+        lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
     }
 
-    if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft) {
-        pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl);
-        pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr);
+    if (controller_type == NPadControllerType::JoyLeft) {
+        pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
     }
 
-    if (controller_type == Core::HID::NpadStyleIndex::JoyconRight) {
-        pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl);
-        pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr);
+    if (controller_type == NPadControllerType::JoyRight) {
+        pad_state.right_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.right_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
     }
 
-    if (controller_type == Core::HID::NpadStyleIndex::GameCube) {
-        const auto& trigger_state = controller.device->GetTriggers();
-        trigger_entry.l_analog = trigger_state.left;
-        trigger_entry.r_analog = trigger_state.right;
-        pad_entry.npad_buttons.zl.Assign(false);
-        pad_entry.npad_buttons.zr.Assign(button_state.r);
-        pad_entry.npad_buttons.l.Assign(button_state.zl);
-        pad_entry.npad_buttons.r.Assign(button_state.zr);
+    if (controller_type == NPadControllerType::GameCube) {
+        trigger_entry.l_analog = static_cast<s32>(
+            button_state[ZL - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
+        trigger_entry.r_analog = static_cast<s32>(
+            button_state[ZR - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
+        pad_state.zl.Assign(false);
+        pad_state.zr.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.l.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
+        pad_state.r.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
     }
 }
 
@@ -383,132 +448,173 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
     if (!IsControllerActivated()) {
         return;
     }
+    for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
+        auto& npad = shared_memory_entries[i];
+        const std::array<NPadGeneric*, 7> controller_npads{
+            &npad.fullkey_states,   &npad.handheld_states,  &npad.joy_dual_states,
+            &npad.joy_left_states,  &npad.joy_right_states, &npad.palma_states,
+            &npad.system_ext_states};
 
-    for (std::size_t i = 0; i < controller_data.size(); ++i) {
-        auto& controller = controller_data[i];
-        auto& npad = controller.shared_memory_entry;
+        // There is the posibility to have more controllers with analog triggers
+        const std::array<TriggerGeneric*, 1> controller_triggers{
+            &npad.gc_trigger_states,
+        };
 
-        const auto& controller_type = controller.device->GetNpadStyleIndex();
+        for (auto* main_controller : controller_npads) {
+            main_controller->common.entry_count = 16;
+            main_controller->common.total_entry_count = 17;
 
-        if (controller_type == Core::HID::NpadStyleIndex::None ||
-            !controller.device->IsConnected()) {
-            // Refresh shared memory
-            std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
-                        &controller.shared_memory_entry, sizeof(NpadInternalState));
-            continue;
+            const auto& last_entry =
+                main_controller->npad[main_controller->common.last_entry_index];
+
+            main_controller->common.timestamp = core_timing.GetCPUTicks();
+            main_controller->common.last_entry_index =
+                (main_controller->common.last_entry_index + 1) % 17;
+
+            auto& cur_entry = main_controller->npad[main_controller->common.last_entry_index];
+
+            cur_entry.timestamp = last_entry.timestamp + 1;
+            cur_entry.timestamp2 = cur_entry.timestamp;
         }
 
-        RequestPadStateUpdate(controller.device->GetNpadIdType());
-        auto& pad_state = controller.npad_pad_state;
-        auto& libnx_state = controller.npad_libnx_state;
-        auto& trigger_state = controller.npad_trigger_state;
+        for (auto* analog_trigger : controller_triggers) {
+            analog_trigger->entry_count = 16;
+            analog_trigger->total_entry_count = 17;
+
+            const auto& last_entry = analog_trigger->trigger[analog_trigger->last_entry_index];
+
+            analog_trigger->timestamp = core_timing.GetCPUTicks();
+            analog_trigger->last_entry_index = (analog_trigger->last_entry_index + 1) % 17;
+
+            auto& cur_entry = analog_trigger->trigger[analog_trigger->last_entry_index];
+
+            cur_entry.timestamp = last_entry.timestamp + 1;
+            cur_entry.timestamp2 = cur_entry.timestamp;
+        }
+
+        const auto& controller_type = connected_controllers[i].type;
+
+        if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
+            continue;
+        }
+        const u32 npad_index = static_cast<u32>(i);
+
+        RequestPadStateUpdate(npad_index);
+        auto& pad_state = npad_pad_states[npad_index];
+        auto& trigger_state = npad_trigger_states[npad_index];
+
+        auto& main_controller =
+            npad.fullkey_states.npad[npad.fullkey_states.common.last_entry_index];
+        auto& handheld_entry =
+            npad.handheld_states.npad[npad.handheld_states.common.last_entry_index];
+        auto& dual_entry = npad.joy_dual_states.npad[npad.joy_dual_states.common.last_entry_index];
+        auto& left_entry = npad.joy_left_states.npad[npad.joy_left_states.common.last_entry_index];
+        auto& right_entry =
+            npad.joy_right_states.npad[npad.joy_right_states.common.last_entry_index];
+        auto& pokeball_entry = npad.palma_states.npad[npad.palma_states.common.last_entry_index];
+        auto& libnx_entry =
+            npad.system_ext_states.npad[npad.system_ext_states.common.last_entry_index];
+        auto& trigger_entry =
+            npad.gc_trigger_states.trigger[npad.gc_trigger_states.last_entry_index];
+
+        libnx_entry.connection_status.raw = 0;
+        libnx_entry.connection_status.is_connected.Assign(1);
+
+        switch (controller_type) {
+        case NPadControllerType::None:
+            UNREACHABLE();
+            break;
+        case NPadControllerType::ProController:
+            main_controller.connection_status.raw = 0;
+            main_controller.connection_status.is_connected.Assign(1);
+            main_controller.connection_status.is_wired.Assign(1);
+            main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
+            main_controller.pad.l_stick = pad_state.l_stick;
+            main_controller.pad.r_stick = pad_state.r_stick;
+
+            libnx_entry.connection_status.is_wired.Assign(1);
+            break;
+        case NPadControllerType::Handheld:
+            handheld_entry.connection_status.raw = 0;
+            handheld_entry.connection_status.is_connected.Assign(1);
+            handheld_entry.connection_status.is_wired.Assign(1);
+            handheld_entry.connection_status.is_left_connected.Assign(1);
+            handheld_entry.connection_status.is_right_connected.Assign(1);
+            handheld_entry.connection_status.is_left_wired.Assign(1);
+            handheld_entry.connection_status.is_right_wired.Assign(1);
+            handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+            handheld_entry.pad.l_stick = pad_state.l_stick;
+            handheld_entry.pad.r_stick = pad_state.r_stick;
+
+            libnx_entry.connection_status.is_wired.Assign(1);
+            libnx_entry.connection_status.is_left_connected.Assign(1);
+            libnx_entry.connection_status.is_right_connected.Assign(1);
+            libnx_entry.connection_status.is_left_wired.Assign(1);
+            libnx_entry.connection_status.is_right_wired.Assign(1);
+            break;
+        case NPadControllerType::JoyDual:
+            dual_entry.connection_status.raw = 0;
+            dual_entry.connection_status.is_connected.Assign(1);
+            dual_entry.connection_status.is_left_connected.Assign(1);
+            dual_entry.connection_status.is_right_connected.Assign(1);
+            dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+            dual_entry.pad.l_stick = pad_state.l_stick;
+            dual_entry.pad.r_stick = pad_state.r_stick;
+
+            libnx_entry.connection_status.is_left_connected.Assign(1);
+            libnx_entry.connection_status.is_right_connected.Assign(1);
+            break;
+        case NPadControllerType::JoyLeft:
+            left_entry.connection_status.raw = 0;
+            left_entry.connection_status.is_connected.Assign(1);
+            left_entry.connection_status.is_left_connected.Assign(1);
+            left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+            left_entry.pad.l_stick = pad_state.l_stick;
+            left_entry.pad.r_stick = pad_state.r_stick;
+
+            libnx_entry.connection_status.is_left_connected.Assign(1);
+            break;
+        case NPadControllerType::JoyRight:
+            right_entry.connection_status.raw = 0;
+            right_entry.connection_status.is_connected.Assign(1);
+            right_entry.connection_status.is_right_connected.Assign(1);
+            right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+            right_entry.pad.l_stick = pad_state.l_stick;
+            right_entry.pad.r_stick = pad_state.r_stick;
+
+            libnx_entry.connection_status.is_right_connected.Assign(1);
+            break;
+        case NPadControllerType::GameCube:
+            main_controller.connection_status.raw = 0;
+            main_controller.connection_status.is_connected.Assign(1);
+            main_controller.connection_status.is_wired.Assign(1);
+            main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
+            main_controller.pad.l_stick = pad_state.l_stick;
+            main_controller.pad.r_stick = pad_state.r_stick;
+            trigger_entry.l_analog = trigger_state.l_analog;
+            trigger_entry.r_analog = trigger_state.r_analog;
+
+            libnx_entry.connection_status.is_wired.Assign(1);
+            break;
+        case NPadControllerType::Pokeball:
+            pokeball_entry.connection_status.raw = 0;
+            pokeball_entry.connection_status.is_connected.Assign(1);
+            pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+            pokeball_entry.pad.l_stick = pad_state.l_stick;
+            pokeball_entry.pad.r_stick = pad_state.r_stick;
+            break;
+        }
 
         // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
         // any controllers.
-        libnx_state.connection_status.raw = 0;
-        libnx_state.connection_status.is_connected.Assign(1);
-        switch (controller_type) {
-        case Core::HID::NpadStyleIndex::None:
-            UNREACHABLE();
-            break;
-        case Core::HID::NpadStyleIndex::ProController:
-        case Core::HID::NpadStyleIndex::NES:
-        case Core::HID::NpadStyleIndex::SNES:
-        case Core::HID::NpadStyleIndex::N64:
-        case Core::HID::NpadStyleIndex::SegaGenesis:
-            pad_state.connection_status.raw = 0;
-            pad_state.connection_status.is_connected.Assign(1);
-            pad_state.connection_status.is_wired.Assign(1);
+        libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+        libnx_entry.pad.l_stick = pad_state.l_stick;
+        libnx_entry.pad.r_stick = pad_state.r_stick;
 
-            libnx_state.connection_status.is_wired.Assign(1);
-            pad_state.sampling_number =
-                npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
-            npad.fullkey_lifo.WriteNextEntry(pad_state);
-            break;
-        case Core::HID::NpadStyleIndex::Handheld:
-            pad_state.connection_status.raw = 0;
-            pad_state.connection_status.is_connected.Assign(1);
-            pad_state.connection_status.is_wired.Assign(1);
-            pad_state.connection_status.is_left_connected.Assign(1);
-            pad_state.connection_status.is_right_connected.Assign(1);
-            pad_state.connection_status.is_left_wired.Assign(1);
-            pad_state.connection_status.is_right_wired.Assign(1);
-
-            libnx_state.connection_status.is_wired.Assign(1);
-            libnx_state.connection_status.is_left_connected.Assign(1);
-            libnx_state.connection_status.is_right_connected.Assign(1);
-            libnx_state.connection_status.is_left_wired.Assign(1);
-            libnx_state.connection_status.is_right_wired.Assign(1);
-            pad_state.sampling_number =
-                npad.handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
-            npad.handheld_lifo.WriteNextEntry(pad_state);
-            break;
-        case Core::HID::NpadStyleIndex::JoyconDual:
-            pad_state.connection_status.raw = 0;
-            pad_state.connection_status.is_connected.Assign(1);
-            pad_state.connection_status.is_left_connected.Assign(1);
-            pad_state.connection_status.is_right_connected.Assign(1);
-
-            libnx_state.connection_status.is_left_connected.Assign(1);
-            libnx_state.connection_status.is_right_connected.Assign(1);
-            pad_state.sampling_number =
-                npad.joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
-            npad.joy_dual_lifo.WriteNextEntry(pad_state);
-            break;
-        case Core::HID::NpadStyleIndex::JoyconLeft:
-            pad_state.connection_status.raw = 0;
-            pad_state.connection_status.is_connected.Assign(1);
-            pad_state.connection_status.is_left_connected.Assign(1);
-
-            libnx_state.connection_status.is_left_connected.Assign(1);
-            pad_state.sampling_number =
-                npad.joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
-            npad.joy_left_lifo.WriteNextEntry(pad_state);
-            break;
-        case Core::HID::NpadStyleIndex::JoyconRight:
-            pad_state.connection_status.raw = 0;
-            pad_state.connection_status.is_connected.Assign(1);
-            pad_state.connection_status.is_right_connected.Assign(1);
-
-            libnx_state.connection_status.is_right_connected.Assign(1);
-            pad_state.sampling_number =
-                npad.joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
-            npad.joy_right_lifo.WriteNextEntry(pad_state);
-            break;
-        case Core::HID::NpadStyleIndex::GameCube:
-            pad_state.connection_status.raw = 0;
-            pad_state.connection_status.is_connected.Assign(1);
-            pad_state.connection_status.is_wired.Assign(1);
-
-            libnx_state.connection_status.is_wired.Assign(1);
-            pad_state.sampling_number =
-                npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
-            trigger_state.sampling_number =
-                npad.gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1;
-            npad.fullkey_lifo.WriteNextEntry(pad_state);
-            npad.gc_trigger_lifo.WriteNextEntry(trigger_state);
-            break;
-        case Core::HID::NpadStyleIndex::Pokeball:
-            pad_state.connection_status.raw = 0;
-            pad_state.connection_status.is_connected.Assign(1);
-            pad_state.sampling_number =
-                npad.palma_lifo.ReadCurrentEntry().state.sampling_number + 1;
-            npad.palma_lifo.WriteNextEntry(pad_state);
-            break;
-        default:
-            break;
-        }
-
-        libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw;
-        libnx_state.l_stick = pad_state.l_stick;
-        libnx_state.r_stick = pad_state.r_stick;
-        npad.system_ext_lifo.WriteNextEntry(pad_state);
-
-        press_state |= static_cast<u32>(pad_state.npad_buttons.raw);
-
-        std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
-                    &controller.shared_memory_entry, sizeof(NpadInternalState));
+        press_state |= static_cast<u32>(pad_state.pad_states.raw);
     }
+    std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
+                shared_memory_entries.size() * sizeof(NPadEntry));
 }
 
 void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
@@ -516,138 +622,145 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
     if (!IsControllerActivated()) {
         return;
     }
+    for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
+        auto& npad = shared_memory_entries[i];
 
-    for (std::size_t i = 0; i < controller_data.size(); ++i) {
-        auto& controller = controller_data[i];
+        const auto& controller_type = connected_controllers[i].type;
 
-        const auto& controller_type = controller.device->GetNpadStyleIndex();
-
-        if (controller_type == Core::HID::NpadStyleIndex::None ||
-            !controller.device->IsConnected()) {
+        if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
             continue;
         }
 
-        auto& npad = controller.shared_memory_entry;
-        const auto& motion_state = controller.device->GetMotions();
-        auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
-        auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
-        auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
-        auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
-        auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
-        auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
+        const std::array<SixAxisGeneric*, 6> controller_sixaxes{
+            &npad.sixaxis_fullkey,    &npad.sixaxis_handheld, &npad.sixaxis_dual_left,
+            &npad.sixaxis_dual_right, &npad.sixaxis_left,     &npad.sixaxis_right,
+        };
 
-        if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
-            controller.sixaxis_at_rest = true;
-            for (std::size_t e = 0; e < motion_state.size(); ++e) {
-                controller.sixaxis_at_rest =
-                    controller.sixaxis_at_rest && motion_state[e].is_at_rest;
+        for (auto* sixaxis_sensor : controller_sixaxes) {
+            sixaxis_sensor->common.entry_count = 16;
+            sixaxis_sensor->common.total_entry_count = 17;
+
+            const auto& last_entry =
+                sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
+
+            sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks();
+            sixaxis_sensor->common.last_entry_index =
+                (sixaxis_sensor->common.last_entry_index + 1) % 17;
+
+            auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
+
+            cur_entry.timestamp = last_entry.timestamp + 1;
+            cur_entry.timestamp2 = cur_entry.timestamp;
+        }
+
+        // Try to read sixaxis sensor states
+        std::array<MotionDevice, 2> motion_devices;
+
+        if (sixaxis_sensors_enabled && Settings::values.motion_enabled.GetValue()) {
+            sixaxis_at_rest = true;
+            for (std::size_t e = 0; e < motion_devices.size(); ++e) {
+                const auto& device = motions[i][e];
+                if (device) {
+                    std::tie(motion_devices[e].accel, motion_devices[e].gyro,
+                             motion_devices[e].rotation, motion_devices[e].orientation,
+                             motion_devices[e].quaternion) = device->GetStatus();
+                    sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f;
+                }
             }
         }
 
+        auto& full_sixaxis_entry =
+            npad.sixaxis_fullkey.sixaxis[npad.sixaxis_fullkey.common.last_entry_index];
+        auto& handheld_sixaxis_entry =
+            npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index];
+        auto& dual_left_sixaxis_entry =
+            npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index];
+        auto& dual_right_sixaxis_entry =
+            npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index];
+        auto& left_sixaxis_entry =
+            npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index];
+        auto& right_sixaxis_entry =
+            npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index];
+
         switch (controller_type) {
-        case Core::HID::NpadStyleIndex::None:
+        case NPadControllerType::None:
             UNREACHABLE();
             break;
-        case Core::HID::NpadStyleIndex::ProController:
-            sixaxis_fullkey_state.attribute.raw = 0;
-            if (controller.sixaxis_sensor_enabled) {
-                sixaxis_fullkey_state.attribute.is_connected.Assign(1);
-                sixaxis_fullkey_state.accel = motion_state[0].accel;
-                sixaxis_fullkey_state.gyro = motion_state[0].gyro;
-                sixaxis_fullkey_state.rotation = motion_state[0].rotation;
-                sixaxis_fullkey_state.orientation = motion_state[0].orientation;
+        case NPadControllerType::ProController:
+            full_sixaxis_entry.attribute.raw = 0;
+            if (sixaxis_sensors_enabled && motions[i][0]) {
+                full_sixaxis_entry.attribute.is_connected.Assign(1);
+                full_sixaxis_entry.accel = motion_devices[0].accel;
+                full_sixaxis_entry.gyro = motion_devices[0].gyro;
+                full_sixaxis_entry.rotation = motion_devices[0].rotation;
+                full_sixaxis_entry.orientation = motion_devices[0].orientation;
             }
             break;
-        case Core::HID::NpadStyleIndex::Handheld:
-            sixaxis_handheld_state.attribute.raw = 0;
-            if (controller.sixaxis_sensor_enabled) {
-                sixaxis_handheld_state.attribute.is_connected.Assign(1);
-                sixaxis_handheld_state.accel = motion_state[0].accel;
-                sixaxis_handheld_state.gyro = motion_state[0].gyro;
-                sixaxis_handheld_state.rotation = motion_state[0].rotation;
-                sixaxis_handheld_state.orientation = motion_state[0].orientation;
+        case NPadControllerType::Handheld:
+            handheld_sixaxis_entry.attribute.raw = 0;
+            if (sixaxis_sensors_enabled && motions[i][0]) {
+                handheld_sixaxis_entry.attribute.is_connected.Assign(1);
+                handheld_sixaxis_entry.accel = motion_devices[0].accel;
+                handheld_sixaxis_entry.gyro = motion_devices[0].gyro;
+                handheld_sixaxis_entry.rotation = motion_devices[0].rotation;
+                handheld_sixaxis_entry.orientation = motion_devices[0].orientation;
             }
             break;
-        case Core::HID::NpadStyleIndex::JoyconDual:
-            sixaxis_dual_left_state.attribute.raw = 0;
-            sixaxis_dual_right_state.attribute.raw = 0;
-            if (controller.sixaxis_sensor_enabled) {
+        case NPadControllerType::JoyDual:
+            dual_left_sixaxis_entry.attribute.raw = 0;
+            dual_right_sixaxis_entry.attribute.raw = 0;
+            if (sixaxis_sensors_enabled && motions[i][0]) {
                 // Set motion for the left joycon
-                sixaxis_dual_left_state.attribute.is_connected.Assign(1);
-                sixaxis_dual_left_state.accel = motion_state[0].accel;
-                sixaxis_dual_left_state.gyro = motion_state[0].gyro;
-                sixaxis_dual_left_state.rotation = motion_state[0].rotation;
-                sixaxis_dual_left_state.orientation = motion_state[0].orientation;
+                dual_left_sixaxis_entry.attribute.is_connected.Assign(1);
+                dual_left_sixaxis_entry.accel = motion_devices[0].accel;
+                dual_left_sixaxis_entry.gyro = motion_devices[0].gyro;
+                dual_left_sixaxis_entry.rotation = motion_devices[0].rotation;
+                dual_left_sixaxis_entry.orientation = motion_devices[0].orientation;
             }
-            if (controller.sixaxis_sensor_enabled) {
+            if (sixaxis_sensors_enabled && motions[i][1]) {
                 // Set motion for the right joycon
-                sixaxis_dual_right_state.attribute.is_connected.Assign(1);
-                sixaxis_dual_right_state.accel = motion_state[1].accel;
-                sixaxis_dual_right_state.gyro = motion_state[1].gyro;
-                sixaxis_dual_right_state.rotation = motion_state[1].rotation;
-                sixaxis_dual_right_state.orientation = motion_state[1].orientation;
+                dual_right_sixaxis_entry.attribute.is_connected.Assign(1);
+                dual_right_sixaxis_entry.accel = motion_devices[1].accel;
+                dual_right_sixaxis_entry.gyro = motion_devices[1].gyro;
+                dual_right_sixaxis_entry.rotation = motion_devices[1].rotation;
+                dual_right_sixaxis_entry.orientation = motion_devices[1].orientation;
             }
             break;
-        case Core::HID::NpadStyleIndex::JoyconLeft:
-            sixaxis_left_lifo_state.attribute.raw = 0;
-            if (controller.sixaxis_sensor_enabled) {
-                sixaxis_left_lifo_state.attribute.is_connected.Assign(1);
-                sixaxis_left_lifo_state.accel = motion_state[0].accel;
-                sixaxis_left_lifo_state.gyro = motion_state[0].gyro;
-                sixaxis_left_lifo_state.rotation = motion_state[0].rotation;
-                sixaxis_left_lifo_state.orientation = motion_state[0].orientation;
+        case NPadControllerType::JoyLeft:
+            left_sixaxis_entry.attribute.raw = 0;
+            if (sixaxis_sensors_enabled && motions[i][0]) {
+                left_sixaxis_entry.attribute.is_connected.Assign(1);
+                left_sixaxis_entry.accel = motion_devices[0].accel;
+                left_sixaxis_entry.gyro = motion_devices[0].gyro;
+                left_sixaxis_entry.rotation = motion_devices[0].rotation;
+                left_sixaxis_entry.orientation = motion_devices[0].orientation;
             }
             break;
-        case Core::HID::NpadStyleIndex::JoyconRight:
-            sixaxis_right_lifo_state.attribute.raw = 0;
-            if (controller.sixaxis_sensor_enabled) {
-                sixaxis_right_lifo_state.attribute.is_connected.Assign(1);
-                sixaxis_right_lifo_state.accel = motion_state[1].accel;
-                sixaxis_right_lifo_state.gyro = motion_state[1].gyro;
-                sixaxis_right_lifo_state.rotation = motion_state[1].rotation;
-                sixaxis_right_lifo_state.orientation = motion_state[1].orientation;
+        case NPadControllerType::JoyRight:
+            right_sixaxis_entry.attribute.raw = 0;
+            if (sixaxis_sensors_enabled && motions[i][1]) {
+                right_sixaxis_entry.attribute.is_connected.Assign(1);
+                right_sixaxis_entry.accel = motion_devices[1].accel;
+                right_sixaxis_entry.gyro = motion_devices[1].gyro;
+                right_sixaxis_entry.rotation = motion_devices[1].rotation;
+                right_sixaxis_entry.orientation = motion_devices[1].orientation;
             }
             break;
-        default:
+        case NPadControllerType::GameCube:
+        case NPadControllerType::Pokeball:
             break;
         }
-
-        sixaxis_fullkey_state.sampling_number =
-            npad.sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
-        sixaxis_handheld_state.sampling_number =
-            npad.sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
-        sixaxis_dual_left_state.sampling_number =
-            npad.sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
-        sixaxis_dual_right_state.sampling_number =
-            npad.sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
-        sixaxis_left_lifo_state.sampling_number =
-            npad.sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
-        sixaxis_right_lifo_state.sampling_number =
-            npad.sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
-
-        if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
-            // This buffer only is updated on handheld on HW
-            npad.sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
-        } else {
-            // Hanheld doesn't update this buffer on HW
-            npad.sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
-        }
-
-        npad.sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
-        npad.sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
-        npad.sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
-        npad.sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
-        std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
-                    &controller.shared_memory_entry, sizeof(NpadInternalState));
     }
+    std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
+                shared_memory_entries.size() * sizeof(NPadEntry));
 }
 
-void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
-    hid_core.SetSupportedStyleTag(style_set);
+void Controller_NPad::SetSupportedStyleSet(NpadStyleSet style_set) {
+    style.raw = style_set.raw;
 }
 
-Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
-    return hid_core.GetSupportedStyleTag();
+Controller_NPad::NpadStyleSet Controller_NPad::GetSupportedStyleSet() const {
+    return style;
 }
 
 void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) {
@@ -666,11 +779,11 @@ std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
     return supported_npad_id_types.size();
 }
 
-void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
+void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
     hold_type = joy_hold_type;
 }
 
-Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const {
+Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
     return hold_type;
 }
 
@@ -690,35 +803,29 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
     return communication_mode;
 }
 
-void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
-                                  NpadJoyAssignmentMode assignment_mode) {
-    if (!IsNpadIdValid(npad_id)) {
-        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
-        return;
-    }
-
-    auto& controller = GetControllerFromNpadIdType(npad_id);
-    if (controller.shared_memory_entry.assignment_mode != assignment_mode) {
-        controller.shared_memory_entry.assignment_mode = assignment_mode;
+void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) {
+    const std::size_t npad_index = NPadIdToIndex(npad_id);
+    ASSERT(npad_index < shared_memory_entries.size());
+    if (shared_memory_entries[npad_index].assignment_mode != assignment_mode) {
+        shared_memory_entries[npad_index].assignment_mode = assignment_mode;
     }
 }
 
-bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
-                                               std::size_t device_index,
-                                               const Core::HID::VibrationValue& vibration_value) {
-    auto& controller = GetControllerFromNpadIdType(npad_id);
-    if (!controller.device->IsConnected()) {
+bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
+                                               const VibrationValue& vibration_value) {
+    if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) {
         return false;
     }
 
-    if (!controller.device->IsVibrationEnabled()) {
-        if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f ||
-            controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) {
+    const auto& player = Settings::values.players.GetValue()[npad_index];
+
+    if (!player.vibration_enabled) {
+        if (latest_vibration_values[npad_index][device_index].amp_low != 0.0f ||
+            latest_vibration_values[npad_index][device_index].amp_high != 0.0f) {
             // Send an empty vibration to stop any vibrations.
-            Core::HID::VibrationValue vibration{0.0f, 160.0f, 0.0f, 320.0f};
-            controller.device->SetVibration(device_index, vibration);
+            vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f);
             // Then reset the vibration value to its default value.
-            controller.vibration[device_index].latest_vibration_value = DEFAULT_VIBRATION_VALUE;
+            latest_vibration_values[npad_index][device_index] = DEFAULT_VIBRATION_VALUE;
         }
 
         return false;
@@ -732,25 +839,27 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
         const auto now = steady_clock::now();
 
         // Filter out non-zero vibrations that are within 10ms of each other.
-        if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) &&
-            duration_cast<milliseconds>(
-                now - controller.vibration[device_index].last_vibration_timepoint) <
+        if ((vibration_value.amp_low != 0.0f || vibration_value.amp_high != 0.0f) &&
+            duration_cast<milliseconds>(now - last_vibration_timepoints[npad_index][device_index]) <
                 milliseconds(10)) {
             return false;
         }
 
-        controller.vibration[device_index].last_vibration_timepoint = now;
+        last_vibration_timepoints[npad_index][device_index] = now;
     }
 
-    Core::HID::VibrationValue vibration{
-        vibration_value.low_amplitude, vibration_value.low_frequency,
-        vibration_value.high_amplitude, vibration_value.high_frequency};
-    return controller.device->SetVibration(device_index, vibration);
+    auto& vibration = vibrations[npad_index][device_index];
+    const auto player_vibration_strength = static_cast<f32>(player.vibration_strength);
+    const auto amp_low =
+        std::min(vibration_value.amp_low * player_vibration_strength / 100.0f, 1.0f);
+    const auto amp_high =
+        std::min(vibration_value.amp_high * player_vibration_strength / 100.0f, 1.0f);
+    return vibration->SetRumblePlay(amp_low, vibration_value.freq_low, amp_high,
+                                    vibration_value.freq_high);
 }
 
-void Controller_NPad::VibrateController(
-    const Core::HID::VibrationDeviceHandle& vibration_device_handle,
-    const Core::HID::VibrationValue& vibration_value) {
+void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle,
+                                        const VibrationValue& vibration_value) {
     if (!IsDeviceHandleValid(vibration_device_handle)) {
         return;
     }
@@ -759,45 +868,42 @@ void Controller_NPad::VibrateController(
         return;
     }
 
-    auto& controller = GetControllerFromHandle(vibration_device_handle);
+    const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
     const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
 
-    if (!controller.vibration[device_index].device_mounted || !controller.device->IsConnected()) {
+    if (!vibration_devices_mounted[npad_index][device_index] ||
+        !connected_controllers[npad_index].is_connected) {
         return;
     }
 
-    if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) {
+    if (vibration_device_handle.device_index == DeviceIndex::None) {
         UNREACHABLE_MSG("DeviceIndex should never be None!");
         return;
     }
 
     // Some games try to send mismatched parameters in the device handle, block these.
-    if ((controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
-         (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconRight ||
-          vibration_device_handle.device_index == Core::HID::DeviceIndex::Right)) ||
-        (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight &&
-         (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconLeft ||
-          vibration_device_handle.device_index == Core::HID::DeviceIndex::Left))) {
+    if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft &&
+         (vibration_device_handle.npad_type == NpadType::JoyconRight ||
+          vibration_device_handle.device_index == DeviceIndex::Right)) ||
+        (connected_controllers[npad_index].type == NPadControllerType::JoyRight &&
+         (vibration_device_handle.npad_type == NpadType::JoyconLeft ||
+          vibration_device_handle.device_index == DeviceIndex::Left))) {
         return;
     }
 
     // Filter out vibrations with equivalent values to reduce unnecessary state changes.
-    if (vibration_value.low_amplitude ==
-            controller.vibration[device_index].latest_vibration_value.low_amplitude &&
-        vibration_value.high_amplitude ==
-            controller.vibration[device_index].latest_vibration_value.high_amplitude) {
+    if (vibration_value.amp_low == latest_vibration_values[npad_index][device_index].amp_low &&
+        vibration_value.amp_high == latest_vibration_values[npad_index][device_index].amp_high) {
         return;
     }
 
-    if (VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_index,
-                                 vibration_value)) {
-        controller.vibration[device_index].latest_vibration_value = vibration_value;
+    if (VibrateControllerAtIndex(npad_index, device_index, vibration_value)) {
+        latest_vibration_values[npad_index][device_index] = vibration_value;
     }
 }
 
-void Controller_NPad::VibrateControllers(
-    const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles,
-    const std::vector<Core::HID::VibrationValue>& vibration_values) {
+void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
+                                         const std::vector<VibrationValue>& vibration_values) {
     if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
         return;
     }
@@ -812,231 +918,167 @@ void Controller_NPad::VibrateControllers(
     }
 }
 
-Core::HID::VibrationValue Controller_NPad::GetLastVibration(
-    const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
+Controller_NPad::VibrationValue Controller_NPad::GetLastVibration(
+    const DeviceHandle& vibration_device_handle) const {
     if (!IsDeviceHandleValid(vibration_device_handle)) {
         return {};
     }
 
-    const auto& controller = GetControllerFromHandle(vibration_device_handle);
+    const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
     const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
-    return controller.vibration[device_index].latest_vibration_value;
+    return latest_vibration_values[npad_index][device_index];
 }
 
-void Controller_NPad::InitializeVibrationDevice(
-    const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
+void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_device_handle) {
     if (!IsDeviceHandleValid(vibration_device_handle)) {
         return;
     }
 
-    const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id);
+    const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
     const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
     InitializeVibrationDeviceAtIndex(npad_index, device_index);
 }
 
-void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id,
+void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index,
                                                        std::size_t device_index) {
-    auto& controller = GetControllerFromNpadIdType(npad_id);
     if (!Settings::values.vibration_enabled.GetValue()) {
-        controller.vibration[device_index].device_mounted = false;
+        vibration_devices_mounted[npad_index][device_index] = false;
         return;
     }
 
-    controller.vibration[device_index].device_mounted =
-        controller.device->TestVibration(device_index);
+    if (vibrations[npad_index][device_index]) {
+        vibration_devices_mounted[npad_index][device_index] =
+            vibrations[npad_index][device_index]->GetStatus() == 1;
+    } else {
+        vibration_devices_mounted[npad_index][device_index] = false;
+    }
 }
 
 void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
     permit_vibration_session_enabled = permit_vibration_session;
 }
 
-bool Controller_NPad::IsVibrationDeviceMounted(
-    const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
+bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const {
     if (!IsDeviceHandleValid(vibration_device_handle)) {
         return false;
     }
 
-    const auto& controller = GetControllerFromHandle(vibration_device_handle);
+    const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
     const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
-    return controller.vibration[device_index].device_mounted;
+    return vibration_devices_mounted[npad_index][device_index];
 }
 
-Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) {
-    if (!IsNpadIdValid(npad_id)) {
-        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
-        // Fallback to player 1
-        const auto& controller = GetControllerFromNpadIdType(Core::HID::NpadIdType::Player1);
-        return controller.styleset_changed_event->GetReadableEvent();
-    }
-
-    const auto& controller = GetControllerFromNpadIdType(npad_id);
-    return controller.styleset_changed_event->GetReadableEvent();
+Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) {
+    return styleset_changed_events[NPadIdToIndex(npad_id)]->GetReadableEvent();
 }
 
-void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
-    const auto& controller = GetControllerFromNpadIdType(npad_id);
-    controller.styleset_changed_event->GetWritableEvent().Signal();
+void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const {
+    styleset_changed_events[NPadIdToIndex(npad_id)]->GetWritableEvent().Signal();
 }
 
-void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller,
-                                         Core::HID::NpadIdType npad_id) {
-    UpdateControllerAt(controller, npad_id, true);
+void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
+    UpdateControllerAt(controller, npad_index, true);
 }
 
-void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
-                                         Core::HID::NpadIdType npad_id, bool connected) {
-    auto& controller = GetControllerFromNpadIdType(npad_id);
+void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
+                                         bool connected) {
     if (!connected) {
-        DisconnectNpad(npad_id);
+        DisconnectNpadAtIndex(npad_index);
         return;
     }
 
-    controller.device->SetNpadStyleIndex(type);
-    InitNewlyAddedController(npad_id);
+    if (controller == NPadControllerType::Handheld && npad_index == HANDHELD_INDEX) {
+        Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type =
+            MapNPadToSettingsType(controller);
+        Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
+        connected_controllers[HANDHELD_INDEX] = {controller, true};
+        InitNewlyAddedController(HANDHELD_INDEX);
+        return;
+    }
+
+    Settings::values.players.GetValue()[npad_index].controller_type =
+        MapNPadToSettingsType(controller);
+    Settings::values.players.GetValue()[npad_index].connected = true;
+    connected_controllers[npad_index] = {controller, true};
+    InitNewlyAddedController(npad_index);
 }
 
-void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
-    if (!IsNpadIdValid(npad_id)) {
-        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
-        return;
-    }
+void Controller_NPad::DisconnectNpad(u32 npad_id) {
+    DisconnectNpadAtIndex(NPadIdToIndex(npad_id));
+}
 
-    LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
-    auto& controller = GetControllerFromNpadIdType(npad_id);
-    for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
+void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) {
+    for (std::size_t device_idx = 0; device_idx < vibrations[npad_index].size(); ++device_idx) {
         // Send an empty vibration to stop any vibrations.
-        VibrateControllerAtIndex(npad_id, device_idx, {});
-        controller.vibration[device_idx].device_mounted = false;
+        VibrateControllerAtIndex(npad_index, device_idx, {});
+        vibration_devices_mounted[npad_index][device_idx] = false;
     }
 
-    auto& shared_memory_entry = controller.shared_memory_entry;
-    shared_memory_entry.style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out
-    shared_memory_entry.device_type.raw = 0;
-    shared_memory_entry.system_properties.raw = 0;
-    shared_memory_entry.button_properties.raw = 0;
-    shared_memory_entry.battery_level_dual = 0;
-    shared_memory_entry.battery_level_left = 0;
-    shared_memory_entry.battery_level_right = 0;
-    shared_memory_entry.fullkey_color = {
-        .attribute = ColorAttribute::NoController,
-        .fullkey = {},
+    Settings::values.players.GetValue()[npad_index].connected = false;
+    connected_controllers[npad_index].is_connected = false;
+
+    auto& controller = shared_memory_entries[npad_index];
+    controller.style_set.raw = 0; // Zero out
+    controller.device_type.raw = 0;
+    controller.system_properties.raw = 0;
+    controller.button_properties.raw = 0;
+    controller.battery_level_dual = 0;
+    controller.battery_level_left = 0;
+    controller.battery_level_right = 0;
+    controller.fullkey_color = {};
+    controller.joycon_color = {};
+    controller.assignment_mode = NpadAssignments::Dual;
+    controller.footer_type = AppletFooterUiType::None;
+
+    SignalStyleSetChangedEvent(IndexToNPad(npad_index));
+}
+
+void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) {
+    gyroscope_zero_drift_mode = drift_mode;
+}
+
+Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode() const {
+    return gyroscope_zero_drift_mode;
+}
+
+bool Controller_NPad::IsSixAxisSensorAtRest() const {
+    return sixaxis_at_rest;
+}
+
+void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) {
+    sixaxis_sensors_enabled = six_axis_status;
+}
+
+void Controller_NPad::SetSixAxisFusionParameters(f32 parameter1, f32 parameter2) {
+    sixaxis_fusion_parameter1 = parameter1;
+    sixaxis_fusion_parameter2 = parameter2;
+}
+
+std::pair<f32, f32> Controller_NPad::GetSixAxisFusionParameters() {
+    return {
+        sixaxis_fusion_parameter1,
+        sixaxis_fusion_parameter2,
     };
-    shared_memory_entry.joycon_color = {
-        .attribute = ColorAttribute::NoController,
-        .left = {},
-        .right = {},
-    };
-    shared_memory_entry.assignment_mode = NpadJoyAssignmentMode::Dual;
-    shared_memory_entry.applet_footer.type = AppletFooterUiType::None;
-
-    controller.is_connected = false;
-    controller.device->Disconnect();
-    SignalStyleSetChangedEvent(npad_id);
-    WriteEmptyEntry(controller.shared_memory_entry);
 }
 
-void Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
-                                                GyroscopeZeroDriftMode drift_mode) {
-    if (!IsDeviceHandleValid(sixaxis_handle)) {
-        LOG_ERROR(Service_HID, "Invalid handle");
-        return;
-    }
-    auto& controller = GetControllerFromHandle(sixaxis_handle);
-    controller.gyroscope_zero_drift_mode = drift_mode;
+void Controller_NPad::ResetSixAxisFusionParameters() {
+    sixaxis_fusion_parameter1 = 0.0f;
+    sixaxis_fusion_parameter2 = 0.0f;
 }
 
-Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode(
-    Core::HID::SixAxisSensorHandle sixaxis_handle) const {
-    if (!IsDeviceHandleValid(sixaxis_handle)) {
-        LOG_ERROR(Service_HID, "Invalid handle");
-        // Return the default value
-        return GyroscopeZeroDriftMode::Standard;
-    }
-    const auto& controller = GetControllerFromHandle(sixaxis_handle);
-    return controller.gyroscope_zero_drift_mode;
-}
-
-bool Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const {
-    if (!IsDeviceHandleValid(sixaxis_handle)) {
-        LOG_ERROR(Service_HID, "Invalid handle");
-        // Return the default value
-        return true;
-    }
-    const auto& controller = GetControllerFromHandle(sixaxis_handle);
-    return controller.sixaxis_at_rest;
-}
-
-void Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
-                                        bool sixaxis_status) {
-    if (!IsDeviceHandleValid(sixaxis_handle)) {
-        LOG_ERROR(Service_HID, "Invalid handle");
-        return;
-    }
-    auto& controller = GetControllerFromHandle(sixaxis_handle);
-    controller.sixaxis_sensor_enabled = sixaxis_status;
-}
-
-void Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
-                                              bool sixaxis_fusion_status) {
-    if (!IsDeviceHandleValid(sixaxis_handle)) {
-        LOG_ERROR(Service_HID, "Invalid handle");
-        return;
-    }
-    auto& controller = GetControllerFromHandle(sixaxis_handle);
-    controller.sixaxis_fusion_enabled = sixaxis_fusion_status;
-}
-
-void Controller_NPad::SetSixAxisFusionParameters(
-    Core::HID::SixAxisSensorHandle sixaxis_handle,
-    Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
-    if (!IsDeviceHandleValid(sixaxis_handle)) {
-        LOG_ERROR(Service_HID, "Invalid handle");
-        return;
-    }
-    auto& controller = GetControllerFromHandle(sixaxis_handle);
-    controller.sixaxis_fusion = sixaxis_fusion_parameters;
-}
-
-Core::HID::SixAxisSensorFusionParameters Controller_NPad::GetSixAxisFusionParameters(
-    Core::HID::SixAxisSensorHandle sixaxis_handle) {
-    if (!IsDeviceHandleValid(sixaxis_handle)) {
-        LOG_ERROR(Service_HID, "Invalid handle");
-        // Since these parameters are unknow just return zeros
-        return {};
-    }
-    auto& controller = GetControllerFromHandle(sixaxis_handle);
-    return controller.sixaxis_fusion;
-}
-
-void Controller_NPad::ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle) {
-    if (!IsDeviceHandleValid(sixaxis_handle)) {
-        LOG_ERROR(Service_HID, "Invalid handle");
-        return;
-    }
-    auto& controller = GetControllerFromHandle(sixaxis_handle);
-    // Since these parameters are unknow just fill with zeros
-    controller.sixaxis_fusion = {};
-}
-
-void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
-                                              Core::HID::NpadIdType npad_id_2) {
-    if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
-        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
-                  npad_id_2);
-        return;
-    }
-    auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
-    auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
+void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
+    const auto npad_index_1 = NPadIdToIndex(npad_id_1);
+    const auto npad_index_2 = NPadIdToIndex(npad_id_2);
 
     // If the controllers at both npad indices form a pair of left and right joycons, merge them.
     // Otherwise, do nothing.
-    if ((controller_1->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
-         controller_2->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) ||
-        (controller_2->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
-         controller_1->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight)) {
+    if ((connected_controllers[npad_index_1].type == NPadControllerType::JoyLeft &&
+         connected_controllers[npad_index_2].type == NPadControllerType::JoyRight) ||
+        (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft &&
+         connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) {
         // Disconnect the joycon at the second id and connect the dual joycon at the first index.
         DisconnectNpad(npad_id_2);
-        AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
+        AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1);
     }
 }
 
@@ -1050,61 +1092,61 @@ void Controller_NPad::StopLRAssignmentMode() {
     is_in_lr_assignment_mode = false;
 }
 
-bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
-                                         Core::HID::NpadIdType npad_id_2) {
-    if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
-        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
-                  npad_id_2);
-        return false;
-    }
-    if (npad_id_1 == Core::HID::NpadIdType::Handheld ||
-        npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other ||
-        npad_id_2 == Core::HID::NpadIdType::Other) {
+bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) {
+    if (npad_id_1 == NPAD_HANDHELD || npad_id_2 == NPAD_HANDHELD || npad_id_1 == NPAD_UNKNOWN ||
+        npad_id_2 == NPAD_UNKNOWN) {
         return true;
     }
-    const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
-    const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
-    const auto type_index_1 = controller_1->GetNpadStyleIndex();
-    const auto type_index_2 = controller_2->GetNpadStyleIndex();
+    const auto npad_index_1 = NPadIdToIndex(npad_id_1);
+    const auto npad_index_2 = NPadIdToIndex(npad_id_2);
 
-    if (!IsControllerSupported(type_index_1) || !IsControllerSupported(type_index_2)) {
+    if (!IsControllerSupported(connected_controllers[npad_index_1].type) ||
+        !IsControllerSupported(connected_controllers[npad_index_2].type)) {
         return false;
     }
 
-    AddNewControllerAt(type_index_2, npad_id_1);
-    AddNewControllerAt(type_index_1, npad_id_2);
+    std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type);
+
+    AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1);
+    AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2);
 
     return true;
 }
 
-Core::HID::LedPattern Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id) {
-    if (!IsNpadIdValid(npad_id)) {
-        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
-        return Core::HID::LedPattern{0, 0, 0, 0};
+Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
+    if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) {
+        // These are controllers without led patterns
+        return LedPattern{0, 0, 0, 0};
+    }
+    switch (npad_id) {
+    case 0:
+        return LedPattern{1, 0, 0, 0};
+    case 1:
+        return LedPattern{1, 1, 0, 0};
+    case 2:
+        return LedPattern{1, 1, 1, 0};
+    case 3:
+        return LedPattern{1, 1, 1, 1};
+    case 4:
+        return LedPattern{1, 0, 0, 1};
+    case 5:
+        return LedPattern{1, 0, 1, 0};
+    case 6:
+        return LedPattern{1, 0, 1, 1};
+    case 7:
+        return LedPattern{0, 1, 1, 0};
+    default:
+        return LedPattern{0, 0, 0, 0};
     }
-    const auto& controller = GetControllerFromNpadIdType(npad_id).device;
-    return controller->GetLedPattern();
 }
 
-bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
-    Core::HID::NpadIdType npad_id) const {
-    if (!IsNpadIdValid(npad_id)) {
-        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
-        // Return the default value
-        return false;
-    }
-    const auto& controller = GetControllerFromNpadIdType(npad_id);
-    return controller.unintended_home_button_input_protection;
+bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const {
+    return unintended_home_button_input_protection[NPadIdToIndex(npad_id)];
 }
 
 void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
-                                                                    Core::HID::NpadIdType npad_id) {
-    if (!IsNpadIdValid(npad_id)) {
-        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
-        return;
-    }
-    auto& controller = GetControllerFromNpadIdType(npad_id);
-    controller.unintended_home_button_input_protection = is_protection_enabled;
+                                                                    u32 npad_id) {
+    unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled;
 }
 
 void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
@@ -1112,34 +1154,32 @@ void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
 }
 
 void Controller_NPad::ClearAllConnectedControllers() {
-    for (auto& controller : controller_data) {
-        if (controller.device->IsConnected() &&
-            controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) {
-            controller.device->Disconnect();
-            controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
+    for (auto& controller : connected_controllers) {
+        if (controller.is_connected && controller.type != NPadControllerType::None) {
+            controller.type = NPadControllerType::None;
+            controller.is_connected = false;
         }
     }
 }
 
 void Controller_NPad::DisconnectAllConnectedControllers() {
-    for (auto& controller : controller_data) {
-        controller.device->Disconnect();
+    for (auto& controller : connected_controllers) {
+        controller.is_connected = false;
     }
 }
 
 void Controller_NPad::ConnectAllDisconnectedControllers() {
-    for (auto& controller : controller_data) {
-        if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None &&
-            !controller.device->IsConnected()) {
-            controller.device->Connect();
+    for (auto& controller : connected_controllers) {
+        if (controller.type != NPadControllerType::None && !controller.is_connected) {
+            controller.is_connected = true;
         }
     }
 }
 
 void Controller_NPad::ClearAllControllers() {
-    for (auto& controller : controller_data) {
-        controller.device->Disconnect();
-        controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
+    for (auto& controller : connected_controllers) {
+        controller.type = NPadControllerType::None;
+        controller.is_connected = false;
     }
 }
 
@@ -1147,16 +1187,16 @@ u32 Controller_NPad::GetAndResetPressState() {
     return press_state.exchange(0);
 }
 
-bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
-    if (controller == Core::HID::NpadStyleIndex::Handheld) {
+bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
+    if (controller == NPadControllerType::Handheld) {
         const bool support_handheld =
             std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
-                      Core::HID::NpadIdType::Handheld) != supported_npad_id_types.end();
+                      NPAD_HANDHELD) != supported_npad_id_types.end();
         // Handheld is not even a supported type, lets stop here
         if (!support_handheld) {
             return false;
         }
-        // Handheld shouldn't be supported in docked mode
+        // Handheld should not be supported in docked mode
         if (Settings::values.use_docked_mode.GetValue()) {
             return false;
         }
@@ -1165,31 +1205,20 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller
     }
 
     if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(),
-                    [](Core::HID::NpadIdType npad_id) {
-                        return npad_id <= Core::HID::NpadIdType::Player8;
-                    })) {
-        Core::HID::NpadStyleTag style = GetSupportedStyleSet();
+                    [](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) {
         switch (controller) {
-        case Core::HID::NpadStyleIndex::ProController:
+        case NPadControllerType::ProController:
             return style.fullkey;
-        case Core::HID::NpadStyleIndex::JoyconDual:
+        case NPadControllerType::JoyDual:
             return style.joycon_dual;
-        case Core::HID::NpadStyleIndex::JoyconLeft:
+        case NPadControllerType::JoyLeft:
             return style.joycon_left;
-        case Core::HID::NpadStyleIndex::JoyconRight:
+        case NPadControllerType::JoyRight:
             return style.joycon_right;
-        case Core::HID::NpadStyleIndex::GameCube:
+        case NPadControllerType::GameCube:
             return style.gamecube;
-        case Core::HID::NpadStyleIndex::Pokeball:
+        case NPadControllerType::Pokeball:
             return style.palma;
-        case Core::HID::NpadStyleIndex::NES:
-            return style.lark;
-        case Core::HID::NpadStyleIndex::SNES:
-            return style.lucia;
-        case Core::HID::NpadStyleIndex::N64:
-            return style.lagoon;
-        case Core::HID::NpadStyleIndex::SegaGenesis:
-            return style.lager;
         default:
             return false;
         }
@@ -1198,48 +1227,4 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller
     return false;
 }
 
-Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
-    const Core::HID::SixAxisSensorHandle& device_handle) {
-    const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
-    return GetControllerFromNpadIdType(npad_id);
-}
-
-const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
-    const Core::HID::SixAxisSensorHandle& device_handle) const {
-    const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
-    return GetControllerFromNpadIdType(npad_id);
-}
-
-Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
-    const Core::HID::VibrationDeviceHandle& device_handle) {
-    const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
-    return GetControllerFromNpadIdType(npad_id);
-}
-
-const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
-    const Core::HID::VibrationDeviceHandle& device_handle) const {
-    const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
-    return GetControllerFromNpadIdType(npad_id);
-}
-
-Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType(
-    Core::HID::NpadIdType npad_id) {
-    if (!IsNpadIdValid(npad_id)) {
-        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
-        npad_id = Core::HID::NpadIdType::Player1;
-    }
-    const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id);
-    return controller_data[npad_index];
-}
-
-const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType(
-    Core::HID::NpadIdType npad_id) const {
-    if (!IsNpadIdValid(npad_id)) {
-        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
-        npad_id = Core::HID::NpadIdType::Player1;
-    }
-    const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id);
-    return controller_data[npad_index];
-}
-
 } // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 3798c037f..9ee146caf 100755
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -11,14 +11,9 @@
 #include "common/bit_field.h"
 #include "common/common_types.h"
 #include "common/quaternion.h"
-#include "core/hid/hid_types.h"
+#include "common/settings.h"
+#include "core/frontend/input.h"
 #include "core/hle/service/hid/controllers/controller_base.h"
-#include "core/hle/service/hid/ring_lifo.h"
-
-namespace Core::HID {
-class EmulatedController;
-enum class ControllerTriggerType;
-} // namespace Core::HID
 
 namespace Kernel {
 class KEvent;
@@ -31,9 +26,12 @@ class ServiceContext;
 
 namespace Service::HID {
 
+constexpr u32 NPAD_HANDHELD = 32;
+constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
+
 class Controller_NPad final : public ControllerBase {
 public:
-    explicit Controller_NPad(Core::HID::HIDCore& hid_core_,
+    explicit Controller_NPad(Core::System& system_,
                              KernelHelpers::ServiceContext& service_context_);
     ~Controller_NPad() override;
 
@@ -50,39 +48,60 @@ public:
     void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
                         std::size_t size) override;
 
-    // This is nn::hid::GyroscopeZeroDriftMode
+    // Called when input devices should be loaded
+    void OnLoadInputDevices() override;
+
+    enum class NPadControllerType {
+        None,
+        ProController,
+        Handheld,
+        JoyDual,
+        JoyLeft,
+        JoyRight,
+        GameCube,
+        Pokeball,
+    };
+
+    enum class NpadType : u8 {
+        ProController = 3,
+        Handheld = 4,
+        JoyconDual = 5,
+        JoyconLeft = 6,
+        JoyconRight = 7,
+        GameCube = 8,
+        Pokeball = 9,
+        MaxNpadType = 10,
+    };
+
+    enum class DeviceIndex : u8 {
+        Left = 0,
+        Right = 1,
+        None = 2,
+        MaxDeviceIndex = 3,
+    };
+
     enum class GyroscopeZeroDriftMode : u32 {
         Loose = 0,
         Standard = 1,
         Tight = 2,
     };
 
-    // This is nn::hid::NpadJoyHoldType
-    enum class NpadJoyHoldType : u64 {
+    enum class NpadHoldType : u64 {
         Vertical = 0,
         Horizontal = 1,
     };
 
-    // This is nn::hid::NpadJoyAssignmentMode
-    enum class NpadJoyAssignmentMode : u32 {
+    enum class NpadAssignments : u32 {
         Dual = 0,
         Single = 1,
     };
 
-    // This is nn::hid::NpadJoyDeviceType
-    enum class NpadJoyDeviceType : s64 {
-        Left = 0,
-        Right = 1,
-    };
-
-    // This is nn::hid::NpadHandheldActivationMode
     enum class NpadHandheldActivationMode : u64 {
         Dual = 0,
         Single = 1,
         None = 2,
     };
 
-    // This is nn::hid::NpadCommunicationMode
     enum class NpadCommunicationMode : u64 {
         Mode_5ms = 0,
         Mode_10ms = 1,
@@ -90,22 +109,74 @@ public:
         Default = 3,
     };
 
-    static constexpr Core::HID::VibrationValue DEFAULT_VIBRATION_VALUE{
-        .low_amplitude = 0.0f,
-        .low_frequency = 160.0f,
-        .high_amplitude = 0.0f,
-        .high_frequency = 320.0f,
+    struct DeviceHandle {
+        NpadType npad_type;
+        u8 npad_id;
+        DeviceIndex device_index;
+        INSERT_PADDING_BYTES_NOINIT(1);
+    };
+    static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size");
+
+    struct NpadStyleSet {
+        union {
+            u32_le raw{};
+
+            BitField<0, 1, u32> fullkey;
+            BitField<1, 1, u32> handheld;
+            BitField<2, 1, u32> joycon_dual;
+            BitField<3, 1, u32> joycon_left;
+            BitField<4, 1, u32> joycon_right;
+            BitField<5, 1, u32> gamecube;
+            BitField<6, 1, u32> palma;
+            BitField<7, 1, u32> lark;
+            BitField<8, 1, u32> handheld_lark;
+            BitField<9, 1, u32> lucia;
+            BitField<29, 1, u32> system_ext;
+            BitField<30, 1, u32> system;
+        };
+    };
+    static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
+
+    struct VibrationValue {
+        f32 amp_low;
+        f32 freq_low;
+        f32 amp_high;
+        f32 freq_high;
+    };
+    static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size");
+
+    static constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
+        .amp_low = 0.0f,
+        .freq_low = 160.0f,
+        .amp_high = 0.0f,
+        .freq_high = 320.0f,
     };
 
-    void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
-    Core::HID::NpadStyleTag GetSupportedStyleSet() const;
+    struct LedPattern {
+        explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
+            position1.Assign(light1);
+            position2.Assign(light2);
+            position3.Assign(light3);
+            position4.Assign(light4);
+        }
+        union {
+            u64 raw{};
+            BitField<0, 1, u64> position1;
+            BitField<1, 1, u64> position2;
+            BitField<2, 1, u64> position3;
+            BitField<3, 1, u64> position4;
+        };
+    };
+
+    void SetSupportedStyleSet(NpadStyleSet style_set);
+    NpadStyleSet GetSupportedStyleSet() const;
 
     void SetSupportedNpadIdTypes(u8* data, std::size_t length);
     void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
     std::size_t GetSupportedNpadIdTypesSize() const;
 
-    void SetHoldType(NpadJoyHoldType joy_hold_type);
-    NpadJoyHoldType GetHoldType() const;
+    void SetHoldType(NpadHoldType joy_hold_type);
+    NpadHoldType GetHoldType() const;
 
     void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
     NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
@@ -113,106 +184,162 @@ public:
     void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
     NpadCommunicationMode GetNpadCommunicationMode() const;
 
-    void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyAssignmentMode assignment_mode);
+    void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode);
 
-    bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
-                                  const Core::HID::VibrationValue& vibration_value);
+    bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
+                                  const VibrationValue& vibration_value);
 
-    void VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle,
-                           const Core::HID::VibrationValue& vibration_value);
+    void VibrateController(const DeviceHandle& vibration_device_handle,
+                           const VibrationValue& vibration_value);
 
-    void VibrateControllers(
-        const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles,
-        const std::vector<Core::HID::VibrationValue>& vibration_values);
+    void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
+                            const std::vector<VibrationValue>& vibration_values);
 
-    Core::HID::VibrationValue GetLastVibration(
-        const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
+    VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const;
 
-    void InitializeVibrationDevice(const Core::HID::VibrationDeviceHandle& vibration_device_handle);
+    void InitializeVibrationDevice(const DeviceHandle& vibration_device_handle);
 
-    void InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index);
+    void InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index);
 
     void SetPermitVibrationSession(bool permit_vibration_session);
 
-    bool IsVibrationDeviceMounted(
-        const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
+    bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const;
 
-    Kernel::KReadableEvent& GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id);
-    void SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const;
+    Kernel::KReadableEvent& GetStyleSetChangedEvent(u32 npad_id);
+    void SignalStyleSetChangedEvent(u32 npad_id) const;
 
     // Adds a new controller at an index.
-    void AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id);
+    void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index);
     // Adds a new controller at an index with connection status.
-    void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id,
-                            bool connected);
+    void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
 
-    void DisconnectNpad(Core::HID::NpadIdType npad_id);
+    void DisconnectNpad(u32 npad_id);
+    void DisconnectNpadAtIndex(std::size_t index);
 
-    void SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
-                                   GyroscopeZeroDriftMode drift_mode);
-    GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode(
-        Core::HID::SixAxisSensorHandle sixaxis_handle) const;
-    bool IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const;
-    void SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, bool sixaxis_status);
-    void SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
-                                 bool sixaxis_fusion_status);
-    void SetSixAxisFusionParameters(
-        Core::HID::SixAxisSensorHandle sixaxis_handle,
-        Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
-    Core::HID::SixAxisSensorFusionParameters GetSixAxisFusionParameters(
-        Core::HID::SixAxisSensorHandle sixaxis_handle);
-    void ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle);
-    Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id);
-    bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const;
-    void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
-                                                       Core::HID::NpadIdType npad_id);
+    void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
+    GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
+    bool IsSixAxisSensorAtRest() const;
+    void SetSixAxisEnabled(bool six_axis_status);
+    void SetSixAxisFusionParameters(f32 parameter1, f32 parameter2);
+    std::pair<f32, f32> GetSixAxisFusionParameters();
+    void ResetSixAxisFusionParameters();
+    LedPattern GetLedPattern(u32 npad_id);
+    bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const;
+    void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id);
     void SetAnalogStickUseCenterClamp(bool use_center_clamp);
     void ClearAllConnectedControllers();
     void DisconnectAllConnectedControllers();
     void ConnectAllDisconnectedControllers();
     void ClearAllControllers();
 
-    void MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
+    void MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2);
     void StartLRAssignmentMode();
     void StopLRAssignmentMode();
-    bool SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
+    bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2);
 
     // Logical OR for all buttons presses on all controllers
     // Specifically for cheat engine and other features.
     u32 GetAndResetPressState();
 
-    static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
-    static bool IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle);
-    static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
+    static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type);
+    static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type);
+    static std::size_t NPadIdToIndex(u32 npad_id);
+    static u32 IndexToNPad(std::size_t index);
+    static bool IsNpadIdValid(u32 npad_id);
+    static bool IsDeviceHandleValid(const DeviceHandle& device_handle);
 
 private:
-    // This is nn::hid::detail::ColorAttribute
-    enum class ColorAttribute : u32 {
+    struct CommonHeader {
+        s64_le timestamp;
+        s64_le total_entry_count;
+        s64_le last_entry_index;
+        s64_le entry_count;
+    };
+    static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
+
+    enum class ColorAttributes : u32_le {
         Ok = 0,
         ReadError = 1,
         NoController = 2,
     };
-    static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");
+    static_assert(sizeof(ColorAttributes) == 4, "ColorAttributes is an invalid size");
 
-    // This is nn::hid::detail::NpadFullKeyColorState
-    struct NpadFullKeyColorState {
-        ColorAttribute attribute;
-        Core::HID::NpadControllerColor fullkey;
+    struct ControllerColor {
+        u32_le body;
+        u32_le button;
     };
-    static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
+    static_assert(sizeof(ControllerColor) == 8, "ControllerColor is an invalid size");
 
-    // This is nn::hid::detail::NpadJoyColorState
-    struct NpadJoyColorState {
-        ColorAttribute attribute;
-        Core::HID::NpadControllerColor left;
-        Core::HID::NpadControllerColor right;
+    struct FullKeyColor {
+        ColorAttributes attribute;
+        ControllerColor fullkey;
     };
-    static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
+    static_assert(sizeof(FullKeyColor) == 0xC, "FullKeyColor is an invalid size");
 
-    // This is nn::hid::NpadAttribute
-    struct NpadAttribute {
+    struct JoyconColor {
+        ColorAttributes attribute;
+        ControllerColor left;
+        ControllerColor right;
+    };
+    static_assert(sizeof(JoyconColor) == 0x14, "JoyconColor is an invalid size");
+
+    struct ControllerPadState {
         union {
-            u32 raw{};
+            u64_le raw{};
+            // Button states
+            BitField<0, 1, u64> a;
+            BitField<1, 1, u64> b;
+            BitField<2, 1, u64> x;
+            BitField<3, 1, u64> y;
+            BitField<4, 1, u64> l_stick;
+            BitField<5, 1, u64> r_stick;
+            BitField<6, 1, u64> l;
+            BitField<7, 1, u64> r;
+            BitField<8, 1, u64> zl;
+            BitField<9, 1, u64> zr;
+            BitField<10, 1, u64> plus;
+            BitField<11, 1, u64> minus;
+
+            // D-Pad
+            BitField<12, 1, u64> d_left;
+            BitField<13, 1, u64> d_up;
+            BitField<14, 1, u64> d_right;
+            BitField<15, 1, u64> d_down;
+
+            // Left JoyStick
+            BitField<16, 1, u64> l_stick_left;
+            BitField<17, 1, u64> l_stick_up;
+            BitField<18, 1, u64> l_stick_right;
+            BitField<19, 1, u64> l_stick_down;
+
+            // Right JoyStick
+            BitField<20, 1, u64> r_stick_left;
+            BitField<21, 1, u64> r_stick_up;
+            BitField<22, 1, u64> r_stick_right;
+            BitField<23, 1, u64> r_stick_down;
+
+            // Not always active?
+            BitField<24, 1, u64> left_sl;
+            BitField<25, 1, u64> left_sr;
+
+            BitField<26, 1, u64> right_sl;
+            BitField<27, 1, u64> right_sr;
+
+            BitField<28, 1, u64> palma;
+            BitField<30, 1, u64> handheld_left_b;
+        };
+    };
+    static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
+
+    struct AnalogPosition {
+        s32_le x;
+        s32_le y;
+    };
+    static_assert(sizeof(AnalogPosition) == 8, "AnalogPosition is an invalid size");
+
+    struct ConnectionState {
+        union {
+            u32_le raw{};
             BitField<0, 1, u32> is_connected;
             BitField<1, 1, u32> is_wired;
             BitField<2, 1, u32> is_left_connected;
@@ -221,60 +348,79 @@ private:
             BitField<5, 1, u32> is_right_wired;
         };
     };
-    static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size");
+    static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
 
-    // This is nn::hid::NpadFullKeyState
-    // This is nn::hid::NpadHandheldState
-    // This is nn::hid::NpadJoyDualState
-    // This is nn::hid::NpadJoyLeftState
-    // This is nn::hid::NpadJoyRightState
-    // This is nn::hid::NpadPalmaState
-    // This is nn::hid::NpadSystemExtState
-    struct NPadGenericState {
-        s64_le sampling_number;
-        Core::HID::NpadButtonState npad_buttons;
-        Core::HID::AnalogStickState l_stick;
-        Core::HID::AnalogStickState r_stick;
-        NpadAttribute connection_status;
-        INSERT_PADDING_BYTES(4); // Reserved
+    struct ControllerPad {
+        ControllerPadState pad_states;
+        AnalogPosition l_stick;
+        AnalogPosition r_stick;
     };
-    static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
+    static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size");
 
-    // This is nn::hid::SixAxisSensorAttribute
-    struct SixAxisSensorAttribute {
+    struct GenericStates {
+        s64_le timestamp;
+        s64_le timestamp2;
+        ControllerPad pad;
+        ConnectionState connection_status;
+    };
+    static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size");
+
+    struct NPadGeneric {
+        CommonHeader common;
+        std::array<GenericStates, 17> npad;
+    };
+    static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size");
+
+    struct SixAxisAttributes {
         union {
-            u32 raw{};
+            u32_le raw{};
             BitField<0, 1, u32> is_connected;
             BitField<1, 1, u32> is_interpolated;
         };
     };
-    static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
+    static_assert(sizeof(SixAxisAttributes) == 4, "SixAxisAttributes is an invalid size");
 
-    // This is nn::hid::SixAxisSensorState
-    struct SixAxisSensorState {
-        s64 delta_time{};
-        s64 sampling_number{};
+    struct SixAxisStates {
+        s64_le timestamp{};
+        INSERT_PADDING_WORDS(2);
+        s64_le timestamp2{};
         Common::Vec3f accel{};
         Common::Vec3f gyro{};
         Common::Vec3f rotation{};
         std::array<Common::Vec3f, 3> orientation{};
-        SixAxisSensorAttribute attribute;
+        SixAxisAttributes attribute;
         INSERT_PADDING_BYTES(4); // Reserved
     };
-    static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
+    static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size");
 
-    // This is nn::hid::server::NpadGcTriggerState
-    struct NpadGcTriggerState {
-        s64 sampling_number{};
-        s32 l_analog{};
-        s32 r_analog{};
+    struct SixAxisGeneric {
+        CommonHeader common{};
+        std::array<SixAxisStates, 17> sixaxis{};
     };
-    static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
+    static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size");
+
+    struct TriggerState {
+        s64_le timestamp{};
+        s64_le timestamp2{};
+        s32_le l_analog{};
+        s32_le r_analog{};
+    };
+    static_assert(sizeof(TriggerState) == 0x18, "TriggerState is an invalid size");
+
+    struct TriggerGeneric {
+        INSERT_PADDING_BYTES(0x4);
+        s64_le timestamp;
+        INSERT_PADDING_BYTES(0x4);
+        s64_le total_entry_count;
+        s64_le last_entry_index;
+        s64_le entry_count;
+        std::array<TriggerState, 17> trigger{};
+    };
+    static_assert(sizeof(TriggerGeneric) == 0x1C8, "TriggerGeneric is an invalid size");
 
-    // This is nn::hid::NpadSystemProperties
     struct NPadSystemProperties {
         union {
-            s64 raw{};
+            s64_le raw{};
             BitField<0, 1, s64> is_charging_joy_dual;
             BitField<1, 1, s64> is_charging_joy_left;
             BitField<2, 1, s64> is_charging_joy_right;
@@ -292,20 +438,17 @@ private:
     };
     static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
 
-    // This is nn::hid::NpadSystemButtonProperties
-    struct NpadSystemButtonProperties {
+    struct NPadButtonProperties {
         union {
-            s32 raw{};
+            s32_le raw{};
             BitField<0, 1, s32> is_home_button_protection_enabled;
         };
     };
-    static_assert(sizeof(NpadSystemButtonProperties) == 0x4,
-                  "NPadButtonProperties is an invalid size");
+    static_assert(sizeof(NPadButtonProperties) == 0x4, "NPadButtonProperties is an invalid size");
 
-    // This is nn::hid::system::DeviceType
-    struct DeviceType {
+    struct NPadDevice {
         union {
-            u32 raw{};
+            u32_le raw{};
             BitField<0, 1, s32> fullkey;
             BitField<1, 1, s32> debug_pad;
             BitField<2, 1, s32> handheld_left;
@@ -322,49 +465,26 @@ private:
             BitField<13, 1, s32> handheld_lark_nes_left;
             BitField<14, 1, s32> handheld_lark_nes_right;
             BitField<15, 1, s32> lucia;
-            BitField<16, 1, s32> lagon;
-            BitField<17, 1, s32> lager;
             BitField<31, 1, s32> system;
         };
     };
 
-    // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
-    struct NfcXcdDeviceHandleStateImpl {
-        u64 handle;
-        bool is_available;
-        bool is_activated;
-        INSERT_PADDING_BYTES(0x6); // Reserved
-        u64 sampling_number;
+    struct MotionDevice {
+        Common::Vec3f accel;
+        Common::Vec3f gyro;
+        Common::Vec3f rotation;
+        std::array<Common::Vec3f, 3> orientation;
+        Common::Quaternion<f32> quaternion;
     };
-    static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
-                  "NfcXcdDeviceHandleStateImpl is an invalid size");
 
-    // nn::hid::detail::NfcXcdDeviceHandleStateImplAtomicStorage
-    struct NfcXcdDeviceHandleStateImplAtomicStorage {
-        u64 sampling_number;
-        NfcXcdDeviceHandleStateImpl nfc_xcd_device_handle_state;
+    struct NfcXcdHandle {
+        INSERT_PADDING_BYTES(0x60);
     };
-    static_assert(sizeof(NfcXcdDeviceHandleStateImplAtomicStorage) == 0x20,
-                  "NfcXcdDeviceHandleStateImplAtomicStorage is an invalid size");
 
-    // This is nn::hid::detail::NfcXcdDeviceHandleState
-    struct NfcXcdDeviceHandleState {
-        // TODO(german77): Make this struct a ring lifo object
-        INSERT_PADDING_BYTES(0x8); // Unused
-        s64 total_buffer_count = max_buffer_size;
-        s64 buffer_tail{};
-        s64 buffer_count{};
-        std::array<NfcXcdDeviceHandleStateImplAtomicStorage, 2> nfc_xcd_device_handle_storage;
-    };
-    static_assert(sizeof(NfcXcdDeviceHandleState) == 0x60,
-                  "NfcXcdDeviceHandleState is an invalid size");
-
-    // This is nn::hid::system::AppletFooterUiAttributesSet
     struct AppletFooterUiAttributes {
         INSERT_PADDING_BYTES(0x4);
     };
 
-    // This is nn::hid::system::AppletFooterUiType
     enum class AppletFooterUiType : u8 {
         None = 0,
         HandheldNone = 1,
@@ -390,150 +510,95 @@ private:
         Lagon = 21,
     };
 
-    struct AppletFooterUi {
-        AppletFooterUiAttributes attributes;
-        AppletFooterUiType type;
-        INSERT_PADDING_BYTES(0x5B); // Reserved
-    };
-    static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
+    struct NPadEntry {
+        NpadStyleSet style_set;
+        NpadAssignments assignment_mode;
+        FullKeyColor fullkey_color;
+        JoyconColor joycon_color;
 
-    // This is nn::hid::NpadLarkType
-    enum class NpadLarkType : u32 {
-        Invalid,
-        H1,
-        H2,
-        NL,
-        NR,
-    };
-
-    // This is nn::hid::NpadLuciaType
-    enum class NpadLuciaType : u32 {
-        Invalid,
-        J,
-        E,
-        U,
-    };
-
-    // This is nn::hid::NpadLagonType
-    enum class NpadLagonType : u32 {
-        Invalid,
-    };
-
-    // This is nn::hid::NpadLagerType
-    enum class NpadLagerType : u32 {
-        Invalid,
-        J,
-        E,
-        U,
-    };
-
-    // This is nn::hid::detail::NpadInternalState
-    struct NpadInternalState {
-        Core::HID::NpadStyleTag style_tag;
-        NpadJoyAssignmentMode assignment_mode;
-        NpadFullKeyColorState fullkey_color;
-        NpadJoyColorState joycon_color;
-        Lifo<NPadGenericState> fullkey_lifo;
-        Lifo<NPadGenericState> handheld_lifo;
-        Lifo<NPadGenericState> joy_dual_lifo;
-        Lifo<NPadGenericState> joy_left_lifo;
-        Lifo<NPadGenericState> joy_right_lifo;
-        Lifo<NPadGenericState> palma_lifo;
-        Lifo<NPadGenericState> system_ext_lifo;
-        Lifo<SixAxisSensorState> sixaxis_fullkey_lifo;
-        Lifo<SixAxisSensorState> sixaxis_handheld_lifo;
-        Lifo<SixAxisSensorState> sixaxis_dual_left_lifo;
-        Lifo<SixAxisSensorState> sixaxis_dual_right_lifo;
-        Lifo<SixAxisSensorState> sixaxis_left_lifo;
-        Lifo<SixAxisSensorState> sixaxis_right_lifo;
-        DeviceType device_type;
-        INSERT_PADDING_BYTES(0x4); // Reserved
+        NPadGeneric fullkey_states;
+        NPadGeneric handheld_states;
+        NPadGeneric joy_dual_states;
+        NPadGeneric joy_left_states;
+        NPadGeneric joy_right_states;
+        NPadGeneric palma_states;
+        NPadGeneric system_ext_states;
+        SixAxisGeneric sixaxis_fullkey;
+        SixAxisGeneric sixaxis_handheld;
+        SixAxisGeneric sixaxis_dual_left;
+        SixAxisGeneric sixaxis_dual_right;
+        SixAxisGeneric sixaxis_left;
+        SixAxisGeneric sixaxis_right;
+        NPadDevice device_type;
+        INSERT_PADDING_BYTES(0x4); // reserved
         NPadSystemProperties system_properties;
-        NpadSystemButtonProperties button_properties;
-        Core::HID::BatteryLevel battery_level_dual;
-        Core::HID::BatteryLevel battery_level_left;
-        Core::HID::BatteryLevel battery_level_right;
-        union {
-            NfcXcdDeviceHandleState nfc_xcd_device_handle;
-            AppletFooterUi applet_footer;
-        };
-        INSERT_PADDING_BYTES(0x20); // Unknown
-        Lifo<NpadGcTriggerState> gc_trigger_lifo;
-        NpadLarkType lark_type_l_and_main;
-        NpadLarkType lark_type_r;
-        NpadLuciaType lucia_type;
-        NpadLagonType lagon_type;
-        NpadLagerType lager_type;
-        // FW 13.x Investigate there is some sort of bitflag related to joycons
-        INSERT_PADDING_BYTES(0x4);
-        INSERT_PADDING_BYTES(0xc08); // Unknown
+        NPadButtonProperties button_properties;
+        u32 battery_level_dual;
+        u32 battery_level_left;
+        u32 battery_level_right;
+        AppletFooterUiAttributes footer_attributes;
+        AppletFooterUiType footer_type;
+        // nfc_states needs to be checked switchbrew does not match with HW
+        NfcXcdHandle nfc_states;
+        INSERT_PADDING_BYTES(0x8); // Mutex
+        TriggerGeneric gc_trigger_states;
+        INSERT_PADDING_BYTES(0xc1f);
     };
-    static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size");
+    static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
 
-    struct VibrationData {
-        bool device_mounted{};
-        Core::HID::VibrationValue latest_vibration_value{};
-        std::chrono::steady_clock::time_point last_vibration_timepoint{};
+    struct ControllerHolder {
+        NPadControllerType type;
+        bool is_connected;
     };
 
-    struct NpadControllerData {
-        Core::HID::EmulatedController* device;
-        Kernel::KEvent* styleset_changed_event{};
-        NpadInternalState shared_memory_entry{};
-
-        std::array<VibrationData, 2> vibration{};
-        bool unintended_home_button_input_protection{};
-        bool is_connected{};
-        Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
-
-        // Motion parameters
-        bool sixaxis_at_rest{true};
-        bool sixaxis_sensor_enabled{true};
-        bool sixaxis_fusion_enabled{false};
-        Core::HID::SixAxisSensorFusionParameters sixaxis_fusion{};
-        GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
-
-        // Current pad state
-        NPadGenericState npad_pad_state{};
-        NPadGenericState npad_libnx_state{};
-        NpadGcTriggerState npad_trigger_state{};
-        SixAxisSensorState sixaxis_fullkey_state{};
-        SixAxisSensorState sixaxis_handheld_state{};
-        SixAxisSensorState sixaxis_dual_left_state{};
-        SixAxisSensorState sixaxis_dual_right_state{};
-        SixAxisSensorState sixaxis_left_lifo_state{};
-        SixAxisSensorState sixaxis_right_lifo_state{};
-        int callback_key;
-    };
-
-    void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx);
-    void InitNewlyAddedController(Core::HID::NpadIdType npad_id);
-    bool IsControllerSupported(Core::HID::NpadStyleIndex controller) const;
-    void RequestPadStateUpdate(Core::HID::NpadIdType npad_id);
-    void WriteEmptyEntry(NpadInternalState& npad);
-
-    NpadControllerData& GetControllerFromHandle(
-        const Core::HID::SixAxisSensorHandle& device_handle);
-    const NpadControllerData& GetControllerFromHandle(
-        const Core::HID::SixAxisSensorHandle& device_handle) const;
-    NpadControllerData& GetControllerFromHandle(
-        const Core::HID::VibrationDeviceHandle& device_handle);
-    const NpadControllerData& GetControllerFromHandle(
-        const Core::HID::VibrationDeviceHandle& device_handle) const;
-    NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
-    const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
+    void InitNewlyAddedController(std::size_t controller_idx);
+    bool IsControllerSupported(NPadControllerType controller) const;
+    void RequestPadStateUpdate(u32 npad_id);
 
     std::atomic<u32> press_state{};
 
-    std::array<NpadControllerData, 10> controller_data{};
+    NpadStyleSet style{};
+    std::array<NPadEntry, 10> shared_memory_entries{};
+    using ButtonArray = std::array<
+        std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
+        10>;
+    using StickArray = std::array<
+        std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
+        10>;
+    using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>,
+                                                 Settings::NativeVibration::NUM_VIBRATIONS_HID>,
+                                      10>;
+    using MotionArray = std::array<
+        std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
+        10>;
+
     KernelHelpers::ServiceContext& service_context;
     std::mutex mutex;
-    std::vector<Core::HID::NpadIdType> supported_npad_id_types{};
-    NpadJoyHoldType hold_type{NpadJoyHoldType::Vertical};
+    ButtonArray buttons;
+    StickArray sticks;
+    VibrationArray vibrations;
+    MotionArray motions;
+    std::vector<u32> supported_npad_id_types{};
+    NpadHoldType hold_type{NpadHoldType::Vertical};
     NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
     NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
+    // Each controller should have their own styleset changed event
+    std::array<Kernel::KEvent*, 10> styleset_changed_events{};
+    std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10>
+        last_vibration_timepoints{};
+    std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{};
     bool permit_vibration_session_enabled{false};
+    std::array<std::array<bool, 2>, 10> vibration_devices_mounted{};
+    std::array<ControllerHolder, 10> connected_controllers{};
+    std::array<bool, 10> unintended_home_button_input_protection{};
     bool analog_stick_use_center_clamp{};
+    GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
+    bool sixaxis_sensors_enabled{true};
+    f32 sixaxis_fusion_parameter1{};
+    f32 sixaxis_fusion_parameter2{};
+    bool sixaxis_at_rest{true};
+    std::array<ControllerPad, 10> npad_pad_states{};
+    std::array<TriggerState, 10> npad_trigger_states{};
     bool is_in_lr_assignment_mode{false};
 };
 } // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index b7d7a5756..772c20453 100755
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -5,12 +5,11 @@
 #include <cstring>
 #include "common/common_types.h"
 #include "core/core_timing.h"
-#include "core/hid/hid_core.h"
 #include "core/hle/service/hid/controllers/stubbed.h"
 
 namespace Service::HID {
 
-Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
+Controller_Stubbed::Controller_Stubbed(Core::System& system_) : ControllerBase{system_} {}
 Controller_Stubbed::~Controller_Stubbed() = default;
 
 void Controller_Stubbed::OnInit() {}
@@ -32,9 +31,10 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
     std::memcpy(data + common_offset, &header, sizeof(CommonHeader));
 }
 
+void Controller_Stubbed::OnLoadInputDevices() {}
+
 void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
     common_offset = off;
     smart_update = true;
 }
-
 } // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
index 0044a4efa..21092af0d 100755
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -10,7 +10,7 @@
 namespace Service::HID {
 class Controller_Stubbed final : public ControllerBase {
 public:
-    explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_);
+    explicit Controller_Stubbed(Core::System& system_);
     ~Controller_Stubbed() override;
 
     // Called when the controller is initialized
@@ -22,17 +22,12 @@ public:
     // When the controller is requesting an update for the shared memory
     void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
 
+    // Called when input devices should be loaded
+    void OnLoadInputDevices() override;
+
     void SetCommonHeaderOffset(std::size_t off);
 
 private:
-    struct CommonHeader {
-        s64 timestamp;
-        s64 total_entry_count;
-        s64 last_entry_index;
-        s64 entry_count;
-    };
-    static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
-
     bool smart_update{};
     std::size_t common_offset{};
 };
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 48978e5c6..6ef17acc5 100755
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -7,82 +7,72 @@
 #include "common/common_types.h"
 #include "common/logging/log.h"
 #include "common/settings.h"
-#include "core/core.h"
 #include "core/core_timing.h"
 #include "core/frontend/emu_window.h"
-#include "core/hid/emulated_console.h"
-#include "core/hid/hid_core.h"
+#include "core/frontend/input.h"
 #include "core/hle/service/hid/controllers/touchscreen.h"
 
 namespace Service::HID {
 constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
 
-Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_)
-    : ControllerBase{hid_core_} {
-    console = hid_core.GetEmulatedConsole();
-}
-
+Controller_Touchscreen::Controller_Touchscreen(Core::System& system_) : ControllerBase{system_} {}
 Controller_Touchscreen::~Controller_Touchscreen() = default;
 
-void Controller_Touchscreen::OnInit() {}
+void Controller_Touchscreen::OnInit() {
+    for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
+        mouse_finger_id[id] = MAX_FINGERS;
+        keyboard_finger_id[id] = MAX_FINGERS;
+        udp_finger_id[id] = MAX_FINGERS;
+    }
+}
 
 void Controller_Touchscreen::OnRelease() {}
 
 void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
                                       std::size_t size) {
-    touch_screen_lifo.timestamp = core_timing.GetCPUTicks();
+    shared_memory.header.timestamp = core_timing.GetCPUTicks();
+    shared_memory.header.total_entry_count = 17;
 
     if (!IsControllerActivated()) {
-        touch_screen_lifo.buffer_count = 0;
-        touch_screen_lifo.buffer_tail = 0;
-        std::memcpy(data, &touch_screen_lifo, sizeof(touch_screen_lifo));
+        shared_memory.header.entry_count = 0;
+        shared_memory.header.last_entry_index = 0;
         return;
     }
+    shared_memory.header.entry_count = 16;
 
-    const auto touch_status = console->GetTouch();
-    for (std::size_t id = 0; id < MAX_FINGERS; id++) {
-        const auto& current_touch = touch_status[id];
-        auto& finger = fingers[id];
-        finger.position = current_touch.position;
-        finger.id = current_touch.id;
+    const auto& last_entry =
+        shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
+    shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
+    auto& cur_entry = shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
 
-        if (finger.attribute.start_touch) {
-            finger.attribute.raw = 0;
-            continue;
-        }
+    cur_entry.sampling_number = last_entry.sampling_number + 1;
+    cur_entry.sampling_number2 = cur_entry.sampling_number;
 
-        if (finger.attribute.end_touch) {
-            finger.attribute.raw = 0;
-            finger.pressed = false;
-            continue;
-        }
+    const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus();
+    const Input::TouchStatus& udp_status = touch_udp_device->GetStatus();
+    for (std::size_t id = 0; id < mouse_status.size(); ++id) {
+        mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
+        udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
+    }
 
-        if (!finger.pressed && current_touch.pressed) {
-            finger.attribute.start_touch.Assign(1);
-            finger.pressed = true;
-            continue;
-        }
-
-        if (finger.pressed && !current_touch.pressed) {
-            finger.attribute.raw = 0;
-            finger.attribute.end_touch.Assign(1);
+    if (Settings::values.use_touch_from_button) {
+        const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
+        for (std::size_t id = 0; id < mouse_status.size(); ++id) {
+            keyboard_finger_id[id] =
+                UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
         }
     }
 
-    std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
+    std::array<Finger, 16> active_fingers;
     const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
                                        [](const auto& finger) { return finger.pressed; });
     const auto active_fingers_count =
         static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
 
     const u64 tick = core_timing.GetCPUTicks();
-    const auto& last_entry = touch_screen_lifo.ReadCurrentEntry().state;
-
-    next_state.sampling_number = last_entry.sampling_number + 1;
-    next_state.entry_count = static_cast<s32>(active_fingers_count);
-
+    cur_entry.entry_count = static_cast<s32_le>(active_fingers_count);
     for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
-        auto& touch_entry = next_state.states[id];
+        auto& touch_entry = cur_entry.states[id];
         if (id < active_fingers_count) {
             const auto& [active_x, active_y] = active_fingers[id].position;
             touch_entry.position = {
@@ -107,9 +97,66 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
             touch_entry.finger = 0;
         }
     }
+    std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
+}
 
-    touch_screen_lifo.WriteNextEntry(next_state);
-    std::memcpy(data + SHARED_MEMORY_OFFSET, &touch_screen_lifo, sizeof(touch_screen_lifo));
+void Controller_Touchscreen::OnLoadInputDevices() {
+    touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
+    touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
+    touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
+}
+
+std::optional<std::size_t> Controller_Touchscreen::GetUnusedFingerID() const {
+    // Dont assign any touch input to a finger if disabled
+    if (!Settings::values.touchscreen.enabled) {
+        return std::nullopt;
+    }
+    std::size_t first_free_id = 0;
+    while (first_free_id < MAX_FINGERS) {
+        if (!fingers[first_free_id].pressed) {
+            return first_free_id;
+        } else {
+            first_free_id++;
+        }
+    }
+    return std::nullopt;
+}
+
+std::size_t Controller_Touchscreen::UpdateTouchInputEvent(
+    const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
+    const auto& [x, y, pressed] = touch_input;
+    if (finger_id > MAX_FINGERS) {
+        LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
+        return MAX_FINGERS;
+    }
+    if (pressed) {
+        Attributes attribute{};
+        if (finger_id == MAX_FINGERS) {
+            const auto first_free_id = GetUnusedFingerID();
+            if (!first_free_id) {
+                // Invalid finger id do nothing
+                return MAX_FINGERS;
+            }
+            finger_id = first_free_id.value();
+            fingers[finger_id].pressed = true;
+            fingers[finger_id].id = static_cast<u32_le>(finger_id);
+            attribute.start_touch.Assign(1);
+        }
+        fingers[finger_id].position = {x, y};
+        fingers[finger_id].attribute = attribute;
+        return finger_id;
+    }
+
+    if (finger_id != MAX_FINGERS) {
+        if (!fingers[finger_id].attribute.end_touch) {
+            fingers[finger_id].attribute.end_touch.Assign(1);
+            fingers[finger_id].attribute.start_touch.Assign(0);
+            return finger_id;
+        }
+        fingers[finger_id].pressed = false;
+    }
+
+    return MAX_FINGERS;
 }
 
 } // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 135c2bf13..8e9b40c0a 100755
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -9,25 +9,18 @@
 #include "common/common_types.h"
 #include "common/point.h"
 #include "common/swap.h"
-#include "core/hid/hid_types.h"
+#include "core/frontend/input.h"
 #include "core/hle/service/hid/controllers/controller_base.h"
-#include "core/hle/service/hid/ring_lifo.h"
-
-namespace Core::HID {
-class EmulatedConsole;
-} // namespace Core::HID
 
 namespace Service::HID {
 class Controller_Touchscreen final : public ControllerBase {
 public:
-    // This is nn::hid::TouchScreenModeForNx
     enum class TouchScreenModeForNx : u8 {
         UseSystemSetting,
         Finger,
         Heat2,
     };
 
-    // This is nn::hid::TouchScreenConfigurationForNx
     struct TouchScreenConfigurationForNx {
         TouchScreenModeForNx mode;
         INSERT_PADDING_BYTES_NOINIT(0x7);
@@ -36,7 +29,7 @@ public:
     static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17,
                   "TouchScreenConfigurationForNx is an invalid size");
 
-    explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_);
+    explicit Controller_Touchscreen(Core::System& system_);
     ~Controller_Touchscreen() override;
 
     // Called when the controller is initialized
@@ -48,24 +41,73 @@ public:
     // When the controller is requesting an update for the shared memory
     void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
 
+    // Called when input devices should be loaded
+    void OnLoadInputDevices() override;
+
 private:
     static constexpr std::size_t MAX_FINGERS = 16;
 
-    // This is nn::hid::TouchScreenState
-    struct TouchScreenState {
-        s64 sampling_number;
-        s32 entry_count;
-        INSERT_PADDING_BYTES(4); // Reserved
-        std::array<Core::HID::TouchState, MAX_FINGERS> states;
+    // Returns an unused finger id, if there is no fingers available std::nullopt will be returned
+    std::optional<std::size_t> GetUnusedFingerID() const;
+
+    // If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no
+    // changes will be made. Updates the coordinates if the finger id it's already set. If the touch
+    // ends delays the output by one frame to set the end_touch flag before finally freeing the
+    // finger id
+    std::size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
+                                      std::size_t finger_id);
+
+    struct Attributes {
+        union {
+            u32 raw{};
+            BitField<0, 1, u32> start_touch;
+            BitField<1, 1, u32> end_touch;
+        };
     };
-    static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
+    static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
 
-    // This is nn::hid::detail::TouchScreenLifo
-    Lifo<TouchScreenState> touch_screen_lifo{};
-    static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
-    TouchScreenState next_state{};
+    struct TouchState {
+        u64_le delta_time;
+        Attributes attribute;
+        u32_le finger;
+        Common::Point<u32_le> position;
+        u32_le diameter_x;
+        u32_le diameter_y;
+        u32_le rotation_angle;
+    };
+    static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
 
-    std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers;
-    Core::HID::EmulatedConsole* console;
+    struct TouchScreenEntry {
+        s64_le sampling_number;
+        s64_le sampling_number2;
+        s32_le entry_count;
+        std::array<TouchState, MAX_FINGERS> states;
+    };
+    static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size");
+
+    struct TouchScreenSharedMemory {
+        CommonHeader header;
+        std::array<TouchScreenEntry, 17> shared_memory_entries{};
+        INSERT_PADDING_BYTES(0x3c8);
+    };
+    static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
+                  "TouchScreenSharedMemory is an invalid size");
+
+    struct Finger {
+        u64_le last_touch{};
+        Common::Point<float> position;
+        u32_le id{};
+        bool pressed{};
+        Attributes attribute;
+    };
+
+    TouchScreenSharedMemory shared_memory{};
+    std::unique_ptr<Input::TouchDevice> touch_mouse_device;
+    std::unique_ptr<Input::TouchDevice> touch_udp_device;
+    std::unique_ptr<Input::TouchDevice> touch_btn_device;
+    std::array<std::size_t, MAX_FINGERS> mouse_finger_id;
+    std::array<std::size_t, MAX_FINGERS> keyboard_finger_id;
+    std::array<std::size_t, MAX_FINGERS> udp_finger_id;
+    std::array<Finger, MAX_FINGERS> fingers;
 };
 } // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index e4da16466..41dc22cf9 100755
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -5,13 +5,12 @@
 #include <cstring>
 #include "common/common_types.h"
 #include "core/core_timing.h"
-#include "core/hid/hid_core.h"
 #include "core/hle/service/hid/controllers/xpad.h"
 
 namespace Service::HID {
 constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
 
-Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
+Controller_XPad::Controller_XPad(Core::System& system_) : ControllerBase{system_} {}
 Controller_XPad::~Controller_XPad() = default;
 
 void Controller_XPad::OnInit() {}
@@ -20,19 +19,28 @@ void Controller_XPad::OnRelease() {}
 
 void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
                                std::size_t size) {
-    if (!IsControllerActivated()) {
-        basic_xpad_lifo.buffer_count = 0;
-        basic_xpad_lifo.buffer_tail = 0;
-        std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo));
-        return;
-    }
+    for (auto& xpad_entry : shared_memory.shared_memory_entries) {
+        xpad_entry.header.timestamp = core_timing.GetCPUTicks();
+        xpad_entry.header.total_entry_count = 17;
 
-    const auto& last_entry = basic_xpad_lifo.ReadCurrentEntry().state;
-    next_state.sampling_number = last_entry.sampling_number + 1;
+        if (!IsControllerActivated()) {
+            xpad_entry.header.entry_count = 0;
+            xpad_entry.header.last_entry_index = 0;
+            return;
+        }
+        xpad_entry.header.entry_count = 16;
+
+        const auto& last_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
+        xpad_entry.header.last_entry_index = (xpad_entry.header.last_entry_index + 1) % 17;
+        auto& cur_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
+
+        cur_entry.sampling_number = last_entry.sampling_number + 1;
+        cur_entry.sampling_number2 = cur_entry.sampling_number;
+    }
     // TODO(ogniK): Update xpad states
 
-    basic_xpad_lifo.WriteNextEntry(next_state);
-    std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo));
+    std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
 }
 
+void Controller_XPad::OnLoadInputDevices() {}
 } // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index 54dae0be1..f9ab5facf 100755
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -8,14 +8,12 @@
 #include "common/common_funcs.h"
 #include "common/common_types.h"
 #include "common/swap.h"
-#include "core/hid/hid_types.h"
 #include "core/hle/service/hid/controllers/controller_base.h"
-#include "core/hle/service/hid/ring_lifo.h"
 
 namespace Service::HID {
 class Controller_XPad final : public ControllerBase {
 public:
-    explicit Controller_XPad(Core::HID::HIDCore& hid_core_);
+    explicit Controller_XPad(Core::System& system_);
     ~Controller_XPad() override;
 
     // Called when the controller is initialized
@@ -27,11 +25,13 @@ public:
     // When the controller is requesting an update for the shared memory
     void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
 
+    // Called when input devices should be loaded
+    void OnLoadInputDevices() override;
+
 private:
-    // This is nn::hid::BasicXpadAttributeSet
-    struct BasicXpadAttributeSet {
+    struct Attributes {
         union {
-            u32 raw{};
+            u32_le raw{};
             BitField<0, 1, u32> is_connected;
             BitField<1, 1, u32> is_wired;
             BitField<2, 1, u32> is_left_connected;
@@ -40,12 +40,11 @@ private:
             BitField<5, 1, u32> is_right_wired;
         };
     };
-    static_assert(sizeof(BasicXpadAttributeSet) == 4, "BasicXpadAttributeSet is an invalid size");
+    static_assert(sizeof(Attributes) == 4, "Attributes is an invalid size");
 
-    // This is nn::hid::BasicXpadButtonSet
-    struct BasicXpadButtonSet {
+    struct Buttons {
         union {
-            u32 raw{};
+            u32_le raw{};
             // Button states
             BitField<0, 1, u32> a;
             BitField<1, 1, u32> b;
@@ -89,21 +88,35 @@ private:
             BitField<30, 1, u32> handheld_left_b;
         };
     };
-    static_assert(sizeof(BasicXpadButtonSet) == 4, "BasicXpadButtonSet is an invalid size");
+    static_assert(sizeof(Buttons) == 4, "Buttons is an invalid size");
 
-    // This is nn::hid::detail::BasicXpadState
-    struct BasicXpadState {
-        s64 sampling_number;
-        BasicXpadAttributeSet attributes;
-        BasicXpadButtonSet pad_states;
-        Core::HID::AnalogStickState l_stick;
-        Core::HID::AnalogStickState r_stick;
+    struct AnalogStick {
+        s32_le x;
+        s32_le y;
     };
-    static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size");
+    static_assert(sizeof(AnalogStick) == 0x8, "AnalogStick is an invalid size");
 
-    // This is nn::hid::detail::BasicXpadLifo
-    Lifo<BasicXpadState> basic_xpad_lifo{};
-    static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size");
-    BasicXpadState next_state{};
+    struct XPadState {
+        s64_le sampling_number;
+        s64_le sampling_number2;
+        Attributes attributes;
+        Buttons pad_states;
+        AnalogStick l_stick;
+        AnalogStick r_stick;
+    };
+    static_assert(sizeof(XPadState) == 0x28, "XPadState is an invalid size");
+
+    struct XPadEntry {
+        CommonHeader header;
+        std::array<XPadState, 17> pad_states{};
+        INSERT_PADDING_BYTES(0x138);
+    };
+    static_assert(sizeof(XPadEntry) == 0x400, "XPadEntry is an invalid size");
+
+    struct SharedMemory {
+        std::array<XPadEntry, 4> shared_memory_entries{};
+    };
+    static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
+    SharedMemory shared_memory{};
 };
 } // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index e740b4331..10c64d41a 100755
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -8,7 +8,7 @@
 #include "common/settings.h"
 #include "core/core.h"
 #include "core/core_timing.h"
-#include "core/hid/hid_core.h"
+#include "core/frontend/input.h"
 #include "core/hle/ipc_helpers.h"
 #include "core/hle/kernel/k_readable_event.h"
 #include "core/hle/kernel/k_shared_memory.h"
@@ -34,10 +34,10 @@
 namespace Service::HID {
 
 // Updating period for each HID device.
-// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
-constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000};      // (4ms, 250Hz)
-constexpr auto keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
-constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000};   // (5ms, 200Hz)
+// HID is polled every 15ms, this value was derived from
+// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet
+constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000};         // (1ms, 1000Hz)
+constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz)
 constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
 
 IAppletResource::IAppletResource(Core::System& system_,
@@ -79,24 +79,17 @@ IAppletResource::IAppletResource(Core::System& system_,
             const auto guard = LockService();
             UpdateControllers(user_data, ns_late);
         });
-    keyboard_update_event = Core::Timing::CreateEvent(
-        "HID::UpdatekeyboardCallback",
-        [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
-            const auto guard = LockService();
-            UpdateKeyboard(user_data, ns_late);
-        });
     motion_update_event = Core::Timing::CreateEvent(
-        "HID::UpdateMotionCallback",
+        "HID::MotionPadCallback",
         [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
             const auto guard = LockService();
             UpdateMotion(user_data, ns_late);
         });
 
     system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event);
-    system.CoreTiming().ScheduleEvent(keyboard_update_ns, keyboard_update_event);
     system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event);
 
-    system.HIDCore().ReloadInputDevices();
+    ReloadInputDevices();
 }
 
 void IAppletResource::ActivateController(HidController controller) {
@@ -109,7 +102,6 @@ void IAppletResource::DeactivateController(HidController controller) {
 
 IAppletResource::~IAppletResource() {
     system.CoreTiming().UnscheduleEvent(pad_update_event, 0);
-    system.CoreTiming().UnscheduleEvent(keyboard_update_event, 0);
     system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
 }
 
@@ -125,37 +117,23 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
                                         std::chrono::nanoseconds ns_late) {
     auto& core_timing = system.CoreTiming();
 
+    const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
     for (const auto& controller : controllers) {
-        // Keyboard has it's own update event
-        if (controller == controllers[static_cast<size_t>(HidController::Keyboard)]) {
-            continue;
+        if (should_reload) {
+            controller->OnLoadInputDevices();
         }
         controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(),
                              SHARED_MEMORY_SIZE);
     }
 
     // If ns_late is higher than the update rate ignore the delay
-    if (ns_late > pad_update_ns) {
+    if (ns_late > motion_update_ns) {
         ns_late = {};
     }
 
     core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
 }
 
-void IAppletResource::UpdateKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
-    auto& core_timing = system.CoreTiming();
-
-    controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(
-        core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE);
-
-    // If ns_late is higher than the update rate ignore the delay
-    if (ns_late > keyboard_update_ns) {
-        ns_late = {};
-    }
-
-    core_timing.ScheduleEvent(keyboard_update_ns - ns_late, keyboard_update_event);
-}
-
 void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
     auto& core_timing = system.CoreTiming();
 
@@ -188,7 +166,7 @@ public:
 private:
     void InitializeVibrationDevice(Kernel::HLERequestContext& ctx) {
         IPC::RequestParser rp{ctx};
-        const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
+        const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
 
         if (applet_resource != nullptr) {
             applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -444,7 +422,6 @@ void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) {
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
@@ -471,18 +448,19 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
 void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        u32 basic_xpad_id;
+        Controller_NPad::DeviceHandle sixaxis_handle;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
-    // This function does nothing on 10.0.0+
+    applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true);
 
-    LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
-                parameters.basic_xpad_id, parameters.applet_resource_user_id);
+    LOG_DEBUG(Service_HID,
+              "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+              parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+              parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -491,18 +469,19 @@ void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
 void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        u32 basic_xpad_id;
+        Controller_NPad::DeviceHandle sixaxis_handle;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
-    // This function does nothing on 10.0.0+
+    applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false);
 
-    LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
-                parameters.basic_xpad_id, parameters.applet_resource_user_id);
+    LOG_DEBUG(Service_HID,
+              "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+              parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+              parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -511,16 +490,14 @@ void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
 void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::SixAxisSensorHandle sixaxis_handle;
+        Controller_NPad::DeviceHandle sixaxis_handle;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
-    applet_resource->GetController<Controller_NPad>(HidController::NPad)
-        .SetSixAxisEnabled(parameters.sixaxis_handle, true);
+    applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true);
 
     LOG_DEBUG(Service_HID,
               "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -534,16 +511,14 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
 void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::SixAxisSensorHandle sixaxis_handle;
+        Controller_NPad::DeviceHandle sixaxis_handle;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
-    applet_resource->GetController<Controller_NPad>(HidController::NPad)
-        .SetSixAxisEnabled(parameters.sixaxis_handle, false);
+    applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false);
 
     LOG_DEBUG(Service_HID,
               "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -559,23 +534,19 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
     struct Parameters {
         bool enable_sixaxis_sensor_fusion;
         INSERT_PADDING_BYTES_NOINIT(3);
-        Core::HID::SixAxisSensorHandle sixaxis_handle;
+        Controller_NPad::DeviceHandle sixaxis_handle;
         u64 applet_resource_user_id;
     };
     static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
-    applet_resource->GetController<Controller_NPad>(HidController::NPad)
-        .SetSixAxisFusionEnabled(parameters.sixaxis_handle,
-                                 parameters.enable_sixaxis_sensor_fusion);
-
-    LOG_DEBUG(Service_HID,
-              "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
-              "device_index={}, applet_resource_user_id={}",
-              parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
-              parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
-              parameters.applet_resource_user_id);
+    LOG_WARNING(Service_HID,
+                "(STUBBED) called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
+                "device_index={}, applet_resource_user_id={}",
+                parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
+                parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
+                parameters.applet_resource_user_id);
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -584,9 +555,9 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
 void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::SixAxisSensorHandle sixaxis_handle;
-        Core::HID::SixAxisSensorFusionParameters sixaxis_fusion;
-        INSERT_PADDING_WORDS_NOINIT(1);
+        Controller_NPad::DeviceHandle sixaxis_handle;
+        f32 parameter1;
+        f32 parameter2;
         u64 applet_resource_user_id;
     };
     static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
@@ -594,14 +565,14 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
     const auto parameters{rp.PopRaw<Parameters>()};
 
     applet_resource->GetController<Controller_NPad>(HidController::NPad)
-        .SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
+        .SetSixAxisFusionParameters(parameters.parameter1, parameters.parameter2);
 
-    LOG_DEBUG(Service_HID,
-              "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
-              "parameter2={}, applet_resource_user_id={}",
-              parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
-              parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1,
-              parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
+    LOG_WARNING(Service_HID,
+                "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
+                "parameter2={}, applet_resource_user_id={}",
+                parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+                parameters.sixaxis_handle.device_index, parameters.parameter1,
+                parameters.parameter2, parameters.applet_resource_user_id);
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -610,33 +581,35 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
 void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::SixAxisSensorHandle sixaxis_handle;
-        INSERT_PADDING_WORDS_NOINIT(1);
+        Controller_NPad::DeviceHandle sixaxis_handle;
         u64 applet_resource_user_id;
     };
     static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
+    f32 parameter1 = 0;
+    f32 parameter2 = 0;
     const auto parameters{rp.PopRaw<Parameters>()};
 
-    const auto sixaxis_fusion_parameters =
+    std::tie(parameter1, parameter2) =
         applet_resource->GetController<Controller_NPad>(HidController::NPad)
-            .GetSixAxisFusionParameters(parameters.sixaxis_handle);
+            .GetSixAxisFusionParameters();
 
-    LOG_DEBUG(Service_HID,
-              "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
-              parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
-              parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+    LOG_WARNING(
+        Service_HID,
+        "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+        parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+        parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
 
     IPC::ResponseBuilder rb{ctx, 4};
     rb.Push(ResultSuccess);
-    rb.PushRaw(sixaxis_fusion_parameters);
+    rb.Push(parameter1);
+    rb.Push(parameter2);
 }
 
 void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::SixAxisSensorHandle sixaxis_handle;
-        INSERT_PADDING_WORDS_NOINIT(1);
+        Controller_NPad::DeviceHandle sixaxis_handle;
         u64 applet_resource_user_id;
     };
     static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
@@ -644,12 +617,13 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
     const auto parameters{rp.PopRaw<Parameters>()};
 
     applet_resource->GetController<Controller_NPad>(HidController::NPad)
-        .ResetSixAxisFusionParameters(parameters.sixaxis_handle);
+        .ResetSixAxisFusionParameters();
 
-    LOG_DEBUG(Service_HID,
-              "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
-              parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
-              parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+    LOG_WARNING(
+        Service_HID,
+        "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+        parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+        parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -657,12 +631,12 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
 
 void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()};
+    const auto sixaxis_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
     const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()};
     const auto applet_resource_user_id{rp.Pop<u64>()};
 
     applet_resource->GetController<Controller_NPad>(HidController::NPad)
-        .SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
+        .SetGyroscopeZeroDriftMode(drift_mode);
 
     LOG_DEBUG(Service_HID,
               "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
@@ -677,11 +651,10 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
 void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::SixAxisSensorHandle sixaxis_handle;
+        Controller_NPad::DeviceHandle sixaxis_handle;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
@@ -693,23 +666,21 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(ResultSuccess);
     rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
-                    .GetGyroscopeZeroDriftMode(parameters.sixaxis_handle));
+                    .GetGyroscopeZeroDriftMode());
 }
 
 void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::SixAxisSensorHandle sixaxis_handle;
+        Controller_NPad::DeviceHandle sixaxis_handle;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
-    const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
 
     applet_resource->GetController<Controller_NPad>(HidController::NPad)
-        .SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
+        .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard);
 
     LOG_DEBUG(Service_HID,
               "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -723,11 +694,10 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
 void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::SixAxisSensorHandle sixaxis_handle;
+        Controller_NPad::DeviceHandle sixaxis_handle;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
@@ -739,17 +709,16 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(ResultSuccess);
     rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
-                .IsSixAxisSensorAtRest(parameters.sixaxis_handle));
+                .IsSixAxisSensorAtRest());
 }
 
 void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::SixAxisSensorHandle sixaxis_handle;
+        Controller_NPad::DeviceHandle sixaxis_handle;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
@@ -771,14 +740,13 @@ void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
     applet_resource->ActivateController(HidController::Gesture);
 
-    LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
-                parameters.unknown, parameters.applet_resource_user_id);
+    LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown,
+              parameters.applet_resource_user_id);
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -786,20 +754,12 @@ void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
 
 void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    struct Parameters {
-        Core::HID::NpadStyleSet supported_styleset;
-        INSERT_PADDING_WORDS_NOINIT(1);
-        u64 applet_resource_user_id;
-    };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
-
-    const auto parameters{rp.PopRaw<Parameters>()};
+    const auto supported_styleset{rp.Pop<u32>()};
 
     applet_resource->GetController<Controller_NPad>(HidController::NPad)
-        .SetSupportedStyleSet({parameters.supported_styleset});
+        .SetSupportedStyleSet({supported_styleset});
 
-    LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}",
-              parameters.supported_styleset, parameters.applet_resource_user_id);
+    LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset);
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -813,9 +773,9 @@ void Hid::GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(ResultSuccess);
-    rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
-                    .GetSupportedStyleSet()
-                    .raw);
+    rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+                .GetSupportedStyleSet()
+                .raw);
 }
 
 void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
@@ -858,12 +818,11 @@ void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) {
 void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::NpadIdType npad_id;
+        u32 npad_id;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
         u64 unknown;
     };
-    static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
@@ -879,11 +838,10 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
 void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::NpadIdType npad_id;
+        u32 npad_id;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
@@ -899,7 +857,7 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
 
 void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
+    const auto npad_id{rp.Pop<u32>()};
 
     LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
 
@@ -914,17 +872,16 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
     // Should have no effect with how our npad sets up the data
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        s32 revision;
+        u32 unknown;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
     applet_resource->ActivateController(HidController::NPad);
 
-    LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision,
+    LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown,
               parameters.applet_resource_user_id);
 
     IPC::ResponseBuilder rb{ctx, 2};
@@ -934,7 +891,7 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
 void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     const auto applet_resource_user_id{rp.Pop<u64>()};
-    const auto hold_type{rp.PopEnum<Controller_NPad::NpadJoyHoldType>()};
+    const auto hold_type{rp.PopEnum<Controller_NPad::NpadHoldType>()};
 
     applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type);
 
@@ -959,16 +916,15 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
 void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::NpadIdType npad_id;
+        u32 npad_id;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
     applet_resource->GetController<Controller_NPad>(HidController::NPad)
-        .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Single);
+        .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single);
 
     LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
                 parameters.npad_id, parameters.applet_resource_user_id);
@@ -981,17 +937,16 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
     // TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::NpadIdType npad_id;
+        u32 npad_id;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
         u64 npad_joy_device_type;
     };
-    static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
     applet_resource->GetController<Controller_NPad>(HidController::NPad)
-        .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Single);
+        .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single);
 
     LOG_WARNING(Service_HID,
                 "(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
@@ -1005,16 +960,15 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
 void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::NpadIdType npad_id;
+        u32 npad_id;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
     applet_resource->GetController<Controller_NPad>(HidController::NPad)
-        .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Dual);
+        .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Dual);
 
     LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
                 parameters.npad_id, parameters.applet_resource_user_id);
@@ -1025,8 +979,8 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
 
 void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
-    const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
+    const auto npad_id_1{rp.Pop<u32>()};
+    const auto npad_id_2{rp.Pop<u32>()};
     const auto applet_resource_user_id{rp.Pop<u64>()};
 
     applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -1092,8 +1046,8 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
 
 void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
-    const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
+    const auto npad_id_1{rp.Pop<u32>()};
+    const auto npad_id_2{rp.Pop<u32>()};
     const auto applet_resource_user_id{rp.Pop<u64>()};
 
     const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -1114,11 +1068,10 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
 void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::NpadIdType npad_id;
+        u32 npad_id;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
@@ -1136,10 +1089,9 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
     struct Parameters {
         bool unintended_home_button_input_protection;
         INSERT_PADDING_BYTES_NOINIT(3);
-        Core::HID::NpadIdType npad_id;
+        u32 npad_id;
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
@@ -1161,7 +1113,6 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
         bool analog_stick_use_center_clamp;
-        INSERT_PADDING_BYTES_NOINIT(7);
         u64 applet_resource_user_id;
     };
     static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
@@ -1181,38 +1132,38 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
 
 void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
+    const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
 
-    Core::HID::VibrationDeviceInfo vibration_device_info;
+    VibrationDeviceInfo vibration_device_info;
 
     switch (vibration_device_handle.npad_type) {
-    case Core::HID::NpadStyleIndex::ProController:
-    case Core::HID::NpadStyleIndex::Handheld:
-    case Core::HID::NpadStyleIndex::JoyconDual:
-    case Core::HID::NpadStyleIndex::JoyconLeft:
-    case Core::HID::NpadStyleIndex::JoyconRight:
+    case Controller_NPad::NpadType::ProController:
+    case Controller_NPad::NpadType::Handheld:
+    case Controller_NPad::NpadType::JoyconDual:
+    case Controller_NPad::NpadType::JoyconLeft:
+    case Controller_NPad::NpadType::JoyconRight:
     default:
-        vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
+        vibration_device_info.type = VibrationDeviceType::LinearResonantActuator;
         break;
-    case Core::HID::NpadStyleIndex::GameCube:
-        vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
+    case Controller_NPad::NpadType::GameCube:
+        vibration_device_info.type = VibrationDeviceType::GcErm;
         break;
-    case Core::HID::NpadStyleIndex::Pokeball:
-        vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
+    case Controller_NPad::NpadType::Pokeball:
+        vibration_device_info.type = VibrationDeviceType::Unknown;
         break;
     }
 
     switch (vibration_device_handle.device_index) {
-    case Core::HID::DeviceIndex::Left:
-        vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
+    case Controller_NPad::DeviceIndex::Left:
+        vibration_device_info.position = VibrationDevicePosition::Left;
         break;
-    case Core::HID::DeviceIndex::Right:
-        vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
+    case Controller_NPad::DeviceIndex::Right:
+        vibration_device_info.position = VibrationDevicePosition::Right;
         break;
-    case Core::HID::DeviceIndex::None:
+    case Controller_NPad::DeviceIndex::None:
     default:
         UNREACHABLE_MSG("DeviceIndex should never be None!");
-        vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
+        vibration_device_info.position = VibrationDevicePosition::None;
         break;
     }
 
@@ -1227,12 +1178,11 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
 void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::VibrationDeviceHandle vibration_device_handle;
-        Core::HID::VibrationValue vibration_value;
+        Controller_NPad::DeviceHandle vibration_device_handle;
+        Controller_NPad::VibrationValue vibration_value;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
@@ -1252,11 +1202,10 @@ void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
 void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::VibrationDeviceHandle vibration_device_handle;
+        Controller_NPad::DeviceHandle vibration_device_handle;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
@@ -1307,10 +1256,10 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
     const auto handles = ctx.ReadBuffer(0);
     const auto vibrations = ctx.ReadBuffer(1);
 
-    std::vector<Core::HID::VibrationDeviceHandle> vibration_device_handles(
-        handles.size() / sizeof(Core::HID::VibrationDeviceHandle));
-    std::vector<Core::HID::VibrationValue> vibration_values(vibrations.size() /
-                                                            sizeof(Core::HID::VibrationValue));
+    std::vector<Controller_NPad::DeviceHandle> vibration_device_handles(
+        handles.size() / sizeof(Controller_NPad::DeviceHandle));
+    std::vector<Controller_NPad::VibrationValue> vibration_values(
+        vibrations.size() / sizeof(Controller_NPad::VibrationValue));
 
     std::memcpy(vibration_device_handles.data(), handles.data(), handles.size());
     std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size());
@@ -1327,10 +1276,9 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
 void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::VibrationDeviceHandle vibration_device_handle;
-        INSERT_PADDING_WORDS_NOINIT(1);
+        Controller_NPad::DeviceHandle vibration_device_handle;
         u64 applet_resource_user_id;
-        Core::HID::VibrationGcErmCommand gc_erm_command;
+        VibrationGcErmCommand gc_erm_command;
     };
     static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
 
@@ -1344,26 +1292,26 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
      */
     const auto vibration_value = [parameters] {
         switch (parameters.gc_erm_command) {
-        case Core::HID::VibrationGcErmCommand::Stop:
-            return Core::HID::VibrationValue{
-                .low_amplitude = 0.0f,
-                .low_frequency = 160.0f,
-                .high_amplitude = 0.0f,
-                .high_frequency = 320.0f,
+        case VibrationGcErmCommand::Stop:
+            return Controller_NPad::VibrationValue{
+                .amp_low = 0.0f,
+                .freq_low = 160.0f,
+                .amp_high = 0.0f,
+                .freq_high = 320.0f,
             };
-        case Core::HID::VibrationGcErmCommand::Start:
-            return Core::HID::VibrationValue{
-                .low_amplitude = 1.0f,
-                .low_frequency = 160.0f,
-                .high_amplitude = 1.0f,
-                .high_frequency = 320.0f,
+        case VibrationGcErmCommand::Start:
+            return Controller_NPad::VibrationValue{
+                .amp_low = 1.0f,
+                .freq_low = 160.0f,
+                .amp_high = 1.0f,
+                .freq_high = 320.0f,
             };
-        case Core::HID::VibrationGcErmCommand::StopHard:
-            return Core::HID::VibrationValue{
-                .low_amplitude = 0.0f,
-                .low_frequency = 0.0f,
-                .high_amplitude = 0.0f,
-                .high_frequency = 0.0f,
+        case VibrationGcErmCommand::StopHard:
+            return Controller_NPad::VibrationValue{
+                .amp_low = 0.0f,
+                .freq_low = 0.0f,
+                .amp_high = 0.0f,
+                .freq_high = 0.0f,
             };
         default:
             return Controller_NPad::DEFAULT_VIBRATION_VALUE;
@@ -1388,7 +1336,7 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
 void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::VibrationDeviceHandle vibration_device_handle;
+        Controller_NPad::DeviceHandle vibration_device_handle;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
@@ -1399,8 +1347,8 @@ void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
                                     .GetLastVibration(parameters.vibration_device_handle);
 
     const auto gc_erm_command = [last_vibration] {
-        if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
-            return Core::HID::VibrationGcErmCommand::Start;
+        if (last_vibration.amp_low != 0.0f || last_vibration.amp_high != 0.0f) {
+            return VibrationGcErmCommand::Start;
         }
 
         /**
@@ -1409,11 +1357,11 @@ void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
          * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
          * This is done to reuse the controller vibration functions made for regular controllers.
          */
-        if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) {
-            return Core::HID::VibrationGcErmCommand::StopHard;
+        if (last_vibration.freq_low == 0.0f && last_vibration.freq_high == 0.0f) {
+            return VibrationGcErmCommand::StopHard;
         }
 
-        return Core::HID::VibrationGcErmCommand::Stop;
+        return VibrationGcErmCommand::Stop;
     }();
 
     LOG_DEBUG(Service_HID,
@@ -1453,11 +1401,10 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
 void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::VibrationDeviceHandle vibration_device_handle;
+        Controller_NPad::DeviceHandle vibration_device_handle;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
@@ -1488,18 +1435,18 @@ void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
 void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
+        Controller_NPad::DeviceHandle sixaxis_handle;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
-    LOG_WARNING(Service_HID,
-                "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
-                parameters.console_sixaxis_handle.unknown_1,
-                parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
+    LOG_WARNING(
+        Service_HID,
+        "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+        parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+        parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -1508,18 +1455,18 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
 void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     struct Parameters {
-        Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
+        Controller_NPad::DeviceHandle sixaxis_handle;
         INSERT_PADDING_WORDS_NOINIT(1);
         u64 applet_resource_user_id;
     };
-    static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
 
     const auto parameters{rp.PopRaw<Parameters>()};
 
-    LOG_WARNING(Service_HID,
-                "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
-                parameters.console_sixaxis_handle.unknown_1,
-                parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
+    LOG_WARNING(
+        Service_HID,
+        "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+        parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+        parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -1673,8 +1620,10 @@ void Hid::SetNpadCommunicationMode(Kernel::HLERequestContext& ctx) {
 
 void Hid::GetNpadCommunicationMode(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
+    const auto applet_resource_user_id{rp.Pop<u64>()};
 
-    LOG_WARNING(Service_HID, "(STUBBED) called");
+    LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
+                applet_resource_user_id);
 
     IPC::ResponseBuilder rb{ctx, 4};
     rb.Push(ResultSuccess);
@@ -2088,6 +2037,10 @@ public:
     }
 };
 
+void ReloadInputDevices() {
+    Settings::values.is_device_reload_pending.store(true);
+}
+
 void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
     std::make_shared<Hid>(system)->InstallAsService(service_manager);
     std::make_shared<HidBus>(system)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index bbad165f8..b1fe75e94 100755
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -60,23 +60,21 @@ public:
 private:
     template <typename T>
     void MakeController(HidController controller) {
-        controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system.HIDCore());
+        controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system);
     }
     template <typename T>
     void MakeControllerWithServiceContext(HidController controller) {
         controllers[static_cast<std::size_t>(controller)] =
-            std::make_unique<T>(system.HIDCore(), service_context);
+            std::make_unique<T>(system, service_context);
     }
 
     void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
     void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
-    void UpdateKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
     void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
 
     KernelHelpers::ServiceContext& service_context;
 
     std::shared_ptr<Core::Timing::EventType> pad_update_event;
-    std::shared_ptr<Core::Timing::EventType> keyboard_update_event;
     std::shared_ptr<Core::Timing::EventType> motion_update_event;
 
     std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
@@ -163,11 +161,38 @@ private:
     void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
     void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx);
 
+    enum class VibrationDeviceType : u32 {
+        Unknown = 0,
+        LinearResonantActuator = 1,
+        GcErm = 2,
+    };
+
+    enum class VibrationDevicePosition : u32 {
+        None = 0,
+        Left = 1,
+        Right = 2,
+    };
+
+    enum class VibrationGcErmCommand : u64 {
+        Stop = 0,
+        Start = 1,
+        StopHard = 2,
+    };
+
+    struct VibrationDeviceInfo {
+        VibrationDeviceType type{};
+        VibrationDevicePosition position{};
+    };
+    static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
+
     std::shared_ptr<IAppletResource> applet_resource;
 
     KernelHelpers::ServiceContext service_context;
 };
 
+/// Reload input devices. Used when input configuration changed
+void ReloadInputDevices();
+
 /// Registers all HID services with the specified service manager.
 void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
 
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index d4fa69a77..dd13d948f 100755
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -1,32 +1,36 @@
 add_library(input_common STATIC
-    drivers/gc_adapter.cpp
-    drivers/gc_adapter.h
-    drivers/keyboard.cpp
-    drivers/keyboard.h
-    drivers/mouse.cpp
-    drivers/mouse.h
-    drivers/sdl_driver.cpp
-    drivers/sdl_driver.h
-    drivers/tas_input.cpp
-    drivers/tas_input.h
-    drivers/touch_screen.cpp
-    drivers/touch_screen.h
-    drivers/udp_client.cpp
-    drivers/udp_client.h
-    helpers/stick_from_buttons.cpp
-    helpers/stick_from_buttons.h
-    helpers/touch_from_buttons.cpp
-    helpers/touch_from_buttons.h
-    helpers/udp_protocol.cpp
-    helpers/udp_protocol.h
-    input_engine.cpp
-    input_engine.h
-    input_mapping.cpp
-    input_mapping.h
-    input_poller.cpp
-    input_poller.h
+    analog_from_button.cpp
+    analog_from_button.h
+    keyboard.cpp
+    keyboard.h
     main.cpp
     main.h
+    motion_from_button.cpp
+    motion_from_button.h
+    motion_input.cpp
+    motion_input.h
+    touch_from_button.cpp
+    touch_from_button.h
+    gcadapter/gc_adapter.cpp
+    gcadapter/gc_adapter.h
+    gcadapter/gc_poller.cpp
+    gcadapter/gc_poller.h
+    mouse/mouse_input.cpp
+    mouse/mouse_input.h
+    mouse/mouse_poller.cpp
+    mouse/mouse_poller.h
+    sdl/sdl.cpp
+    sdl/sdl.h
+    tas/tas_input.cpp
+    tas/tas_input.h
+    tas/tas_poller.cpp
+    tas/tas_poller.h
+    udp/client.cpp
+    udp/client.h
+    udp/protocol.cpp
+    udp/protocol.h
+    udp/udp.cpp
+    udp/udp.h
 )
 
 if (MSVC)
@@ -53,8 +57,8 @@ endif()
 
 if (ENABLE_SDL2)
     target_sources(input_common PRIVATE
-        drivers/sdl_driver.cpp
-        drivers/sdl_driver.h
+        sdl/sdl_impl.cpp
+        sdl/sdl_impl.h
     )
     target_link_libraries(input_common PRIVATE SDL2)
     target_compile_definitions(input_common PRIVATE HAVE_SDL2)
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index ae2518f53..f3907c65a 100755
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -4,164 +4,146 @@
 
 #include <memory>
 #include <thread>
-#include "common/input.h"
 #include "common/param_package.h"
-#include "input_common/drivers/gc_adapter.h"
-#include "input_common/drivers/keyboard.h"
-#include "input_common/drivers/mouse.h"
-#include "input_common/drivers/tas_input.h"
-#include "input_common/drivers/touch_screen.h"
-#include "input_common/drivers/udp_client.h"
-#include "input_common/helpers/stick_from_buttons.h"
-#include "input_common/helpers/touch_from_buttons.h"
-#include "input_common/input_engine.h"
-#include "input_common/input_mapping.h"
-#include "input_common/input_poller.h"
+#include "common/settings.h"
+#include "input_common/analog_from_button.h"
+#include "input_common/gcadapter/gc_adapter.h"
+#include "input_common/gcadapter/gc_poller.h"
+#include "input_common/keyboard.h"
 #include "input_common/main.h"
+#include "input_common/motion_from_button.h"
+#include "input_common/mouse/mouse_input.h"
+#include "input_common/mouse/mouse_poller.h"
+#include "input_common/tas/tas_input.h"
+#include "input_common/tas/tas_poller.h"
+#include "input_common/touch_from_button.h"
+#include "input_common/udp/client.h"
+#include "input_common/udp/udp.h"
 #ifdef HAVE_SDL2
-#include "input_common/drivers/sdl_driver.h"
+#include "input_common/sdl/sdl.h"
 #endif
 
 namespace InputCommon {
 
 struct InputSubsystem::Impl {
     void Initialize() {
-        mapping_factory = std::make_shared<MappingFactory>();
-        MappingCallback mapping_callback{[this](MappingData data) { RegisterInput(data); }};
+        gcadapter = std::make_shared<GCAdapter::Adapter>();
+        gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
+        Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
+        gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
+        Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
+        gcvibration = std::make_shared<GCVibrationFactory>(gcadapter);
+        Input::RegisterFactory<Input::VibrationDevice>("gcpad", gcvibration);
 
-        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);
-        Common::Input::RegisterFactory<Common::Input::InputDevice>(keyboard->GetEngineName(),
-                                                                   keyboard_factory);
-        Common::Input::RegisterFactory<Common::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);
-        Common::Input::RegisterFactory<Common::Input::InputDevice>(mouse->GetEngineName(),
-                                                                   mouse_factory);
-        Common::Input::RegisterFactory<Common::Input::OutputDevice>(mouse->GetEngineName(),
-                                                                    mouse_output_factory);
-
-        touch_screen = std::make_shared<TouchScreen>("touch");
-        touch_screen_factory = std::make_shared<InputFactory>(touch_screen);
-        Common::Input::RegisterFactory<Common::Input::InputDevice>(touch_screen->GetEngineName(),
-                                                                   touch_screen_factory);
-
-        gcadapter = std::make_shared<GCAdapter>("gcpad");
-        gcadapter->SetMappingCallback(mapping_callback);
-        gcadapter_input_factory = std::make_shared<InputFactory>(gcadapter);
-        gcadapter_output_factory = std::make_shared<OutputFactory>(gcadapter);
-        Common::Input::RegisterFactory<Common::Input::InputDevice>(gcadapter->GetEngineName(),
-                                                                   gcadapter_input_factory);
-        Common::Input::RegisterFactory<Common::Input::OutputDevice>(gcadapter->GetEngineName(),
-                                                                    gcadapter_output_factory);
-
-        udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp");
-        udp_client->SetMappingCallback(mapping_callback);
-        udp_client_factory = std::make_shared<InputFactory>(udp_client);
-        Common::Input::RegisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName(),
-                                                                   udp_client_factory);
-
-        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);
-        Common::Input::RegisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName(),
-                                                                   tas_input_factory);
-        Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(),
-                                                                    tas_output_factory);
+        keyboard = std::make_shared<Keyboard>();
+        Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
+        Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
+                                                    std::make_shared<AnalogFromButton>());
+        Input::RegisterFactory<Input::MotionDevice>("keyboard",
+                                                    std::make_shared<MotionFromButton>());
+        Input::RegisterFactory<Input::TouchDevice>("touch_from_button",
+                                                   std::make_shared<TouchFromButtonFactory>());
 
 #ifdef HAVE_SDL2
-        sdl = std::make_shared<SDLDriver>("sdl");
-        sdl->SetMappingCallback(mapping_callback);
-        sdl_input_factory = std::make_shared<InputFactory>(sdl);
-        sdl_output_factory = std::make_shared<OutputFactory>(sdl);
-        Common::Input::RegisterFactory<Common::Input::InputDevice>(sdl->GetEngineName(),
-                                                                   sdl_input_factory);
-        Common::Input::RegisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName(),
-                                                                    sdl_output_factory);
+        sdl = SDL::Init();
 #endif
 
-        Common::Input::RegisterFactory<Common::Input::InputDevice>(
-            "touch_from_button", std::make_shared<TouchFromButton>());
-        Common::Input::RegisterFactory<Common::Input::InputDevice>(
-            "analog_from_button", std::make_shared<StickFromButton>());
+        udp = std::make_shared<InputCommon::CemuhookUDP::Client>();
+        udpmotion = std::make_shared<UDPMotionFactory>(udp);
+        Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion);
+        udptouch = std::make_shared<UDPTouchFactory>(udp);
+        Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch);
+
+        mouse = std::make_shared<MouseInput::Mouse>();
+        mousebuttons = std::make_shared<MouseButtonFactory>(mouse);
+        Input::RegisterFactory<Input::ButtonDevice>("mouse", mousebuttons);
+        mouseanalog = std::make_shared<MouseAnalogFactory>(mouse);
+        Input::RegisterFactory<Input::AnalogDevice>("mouse", mouseanalog);
+        mousemotion = std::make_shared<MouseMotionFactory>(mouse);
+        Input::RegisterFactory<Input::MotionDevice>("mouse", mousemotion);
+        mousetouch = std::make_shared<MouseTouchFactory>(mouse);
+        Input::RegisterFactory<Input::TouchDevice>("mouse", mousetouch);
+
+        tas = std::make_shared<TasInput::Tas>();
+        tasbuttons = std::make_shared<TasButtonFactory>(tas);
+        Input::RegisterFactory<Input::ButtonDevice>("tas", tasbuttons);
+        tasanalog = std::make_shared<TasAnalogFactory>(tas);
+        Input::RegisterFactory<Input::AnalogDevice>("tas", tasanalog);
     }
 
     void Shutdown() {
-        Common::Input::UnregisterFactory<Common::Input::InputDevice>(keyboard->GetEngineName());
-        Common::Input::UnregisterFactory<Common::Input::OutputDevice>(keyboard->GetEngineName());
+        Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
+        Input::UnregisterFactory<Input::MotionDevice>("keyboard");
         keyboard.reset();
-
-        Common::Input::UnregisterFactory<Common::Input::InputDevice>(mouse->GetEngineName());
-        Common::Input::UnregisterFactory<Common::Input::OutputDevice>(mouse->GetEngineName());
-        mouse.reset();
-
-        Common::Input::UnregisterFactory<Common::Input::InputDevice>(touch_screen->GetEngineName());
-        touch_screen.reset();
-
-        Common::Input::UnregisterFactory<Common::Input::InputDevice>(gcadapter->GetEngineName());
-        Common::Input::UnregisterFactory<Common::Input::OutputDevice>(gcadapter->GetEngineName());
-        gcadapter.reset();
-
-        Common::Input::UnregisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName());
-        udp_client.reset();
-
-        Common::Input::UnregisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName());
-        Common::Input::UnregisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName());
-        tas_input.reset();
-
+        Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
+        Input::UnregisterFactory<Input::TouchDevice>("touch_from_button");
 #ifdef HAVE_SDL2
-        Common::Input::UnregisterFactory<Common::Input::InputDevice>(sdl->GetEngineName());
-        Common::Input::UnregisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName());
         sdl.reset();
 #endif
+        Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
+        Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
+        Input::UnregisterFactory<Input::VibrationDevice>("gcpad");
 
-        Common::Input::UnregisterFactory<Common::Input::InputDevice>("touch_from_button");
-        Common::Input::UnregisterFactory<Common::Input::InputDevice>("analog_from_button");
+        gcbuttons.reset();
+        gcanalog.reset();
+        gcvibration.reset();
+
+        Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
+        Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
+
+        udpmotion.reset();
+        udptouch.reset();
+
+        Input::UnregisterFactory<Input::ButtonDevice>("mouse");
+        Input::UnregisterFactory<Input::AnalogDevice>("mouse");
+        Input::UnregisterFactory<Input::MotionDevice>("mouse");
+        Input::UnregisterFactory<Input::TouchDevice>("mouse");
+
+        mousebuttons.reset();
+        mouseanalog.reset();
+        mousemotion.reset();
+        mousetouch.reset();
+
+        Input::UnregisterFactory<Input::ButtonDevice>("tas");
+        Input::UnregisterFactory<Input::AnalogDevice>("tas");
+
+        tasbuttons.reset();
+        tasanalog.reset();
     }
 
     [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
         std::vector<Common::ParamPackage> devices = {
-            Common::ParamPackage{{"display", "Any"}, {"engine", "any"}},
+            Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
+            Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}},
         };
-
-        auto keyboard_devices = keyboard->GetInputDevices();
-        devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end());
-        auto mouse_devices = mouse->GetInputDevices();
-        devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end());
-        auto gcadapter_devices = gcadapter->GetInputDevices();
-        devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
+        if (Settings::values.tas_enable) {
+            devices.emplace_back(
+                Common::ParamPackage{{"display", "TAS Controller"}, {"class", "tas"}});
+        }
 #ifdef HAVE_SDL2
         auto sdl_devices = sdl->GetInputDevices();
         devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
 #endif
-
+        auto udp_devices = udp->GetInputDevices();
+        devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
+        auto gcpad_devices = gcadapter->GetInputDevices();
+        devices.insert(devices.end(), gcpad_devices.begin(), gcpad_devices.end());
         return devices;
     }
 
     [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
         const Common::ParamPackage& params) const {
-        if (!params.Has("engine") || params.Get("engine", "") == "any") {
+        if (!params.Has("class") || params.Get("class", "") == "any") {
             return {};
         }
-        const std::string engine = params.Get("engine", "");
-        if (engine == mouse->GetEngineName()) {
-            return mouse->GetAnalogMappingForDevice(params);
-        }
-        if (engine == gcadapter->GetEngineName()) {
+        if (params.Get("class", "") == "gcpad") {
             return gcadapter->GetAnalogMappingForDevice(params);
         }
-        if (engine == tas_input->GetEngineName()) {
-            return tas_input->GetAnalogMappingForDevice(params);
+        if (params.Get("class", "") == "tas") {
+            return tas->GetAnalogMappingForDevice(params);
         }
 #ifdef HAVE_SDL2
-        if (engine == sdl->GetEngineName()) {
+        if (params.Get("class", "") == "sdl") {
             return sdl->GetAnalogMappingForDevice(params);
         }
 #endif
@@ -170,18 +152,17 @@ struct InputSubsystem::Impl {
 
     [[nodiscard]] ButtonMapping GetButtonMappingForDevice(
         const Common::ParamPackage& params) const {
-        if (!params.Has("engine") || params.Get("engine", "") == "any") {
+        if (!params.Has("class") || params.Get("class", "") == "any") {
             return {};
         }
-        const std::string engine = params.Get("engine", "");
-        if (engine == gcadapter->GetEngineName()) {
+        if (params.Get("class", "") == "gcpad") {
             return gcadapter->GetButtonMappingForDevice(params);
         }
-        if (engine == tas_input->GetEngineName()) {
-            return tas_input->GetButtonMappingForDevice(params);
+        if (params.Get("class", "") == "tas") {
+            return tas->GetButtonMappingForDevice(params);
         }
 #ifdef HAVE_SDL2
-        if (engine == sdl->GetEngineName()) {
+        if (params.Get("class", "") == "sdl") {
             return sdl->GetButtonMappingForDevice(params);
         }
 #endif
@@ -190,115 +171,40 @@ struct InputSubsystem::Impl {
 
     [[nodiscard]] MotionMapping GetMotionMappingForDevice(
         const Common::ParamPackage& params) const {
-        if (!params.Has("engine") || params.Get("engine", "") == "any") {
+        if (!params.Has("class") || params.Get("class", "") == "any") {
             return {};
         }
-        const std::string engine = params.Get("engine", "");
-        if (engine == gcadapter->GetEngineName()) {
-            return gcadapter->GetMotionMappingForDevice(params);
+        if (params.Get("class", "") == "cemuhookudp") {
+            // TODO return the correct motion device
+            return {};
         }
 #ifdef HAVE_SDL2
-        if (engine == sdl->GetEngineName()) {
+        if (params.Get("class", "") == "sdl") {
             return sdl->GetMotionMappingForDevice(params);
         }
 #endif
         return {};
     }
 
-    std::string GetButtonName(const Common::ParamPackage& params) const {
-        if (!params.Has("engine") || params.Get("engine", "") == "any") {
-            return "Unknown";
-        }
-        const std::string engine = params.Get("engine", "");
-        if (engine == mouse->GetEngineName()) {
-            return mouse->GetUIName(params);
-        }
-        if (engine == gcadapter->GetEngineName()) {
-            return gcadapter->GetUIName(params);
-        }
-        if (engine == udp_client->GetEngineName()) {
-            return udp_client->GetUIName(params);
-        }
-        if (engine == tas_input->GetEngineName()) {
-            return tas_input->GetUIName(params);
-        }
-#ifdef HAVE_SDL2
-        if (engine == sdl->GetEngineName()) {
-            return sdl->GetUIName(params);
-        }
-#endif
-        return "Bad engine";
-    }
-
-    bool IsController(const Common::ParamPackage& params) {
-        const std::string engine = params.Get("engine", "");
-        if (engine == mouse->GetEngineName()) {
-            return true;
-        }
-        if (engine == gcadapter->GetEngineName()) {
-            return true;
-        }
-        if (engine == tas_input->GetEngineName()) {
-            return true;
-        }
-#ifdef HAVE_SDL2
-        if (engine == sdl->GetEngineName()) {
-            return true;
-        }
-#endif
-        return false;
-    }
-
-    void BeginConfiguration() {
-        keyboard->BeginConfiguration();
-        mouse->BeginConfiguration();
-        gcadapter->BeginConfiguration();
-        udp_client->BeginConfiguration();
-#ifdef HAVE_SDL2
-        sdl->BeginConfiguration();
-#endif
-    }
-
-    void EndConfiguration() {
-        keyboard->EndConfiguration();
-        mouse->EndConfiguration();
-        gcadapter->EndConfiguration();
-        udp_client->EndConfiguration();
-#ifdef HAVE_SDL2
-        sdl->EndConfiguration();
-#endif
-    }
-
-    void RegisterInput(MappingData data) {
-        mapping_factory->RegisterInput(data);
-    }
-
-    std::shared_ptr<MappingFactory> mapping_factory;
-
     std::shared_ptr<Keyboard> keyboard;
-    std::shared_ptr<Mouse> mouse;
-    std::shared_ptr<GCAdapter> gcadapter;
-    std::shared_ptr<TouchScreen> touch_screen;
-    std::shared_ptr<TasInput::Tas> tas_input;
-    std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
-
-    std::shared_ptr<InputFactory> keyboard_factory;
-    std::shared_ptr<InputFactory> mouse_factory;
-    std::shared_ptr<InputFactory> gcadapter_input_factory;
-    std::shared_ptr<InputFactory> touch_screen_factory;
-    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;
-    std::shared_ptr<InputFactory> sdl_input_factory;
-    std::shared_ptr<OutputFactory> sdl_output_factory;
+    std::unique_ptr<SDL::State> sdl;
 #endif
+    std::shared_ptr<GCButtonFactory> gcbuttons;
+    std::shared_ptr<GCAnalogFactory> gcanalog;
+    std::shared_ptr<GCVibrationFactory> gcvibration;
+    std::shared_ptr<UDPMotionFactory> udpmotion;
+    std::shared_ptr<UDPTouchFactory> udptouch;
+    std::shared_ptr<MouseButtonFactory> mousebuttons;
+    std::shared_ptr<MouseAnalogFactory> mouseanalog;
+    std::shared_ptr<MouseMotionFactory> mousemotion;
+    std::shared_ptr<MouseTouchFactory> mousetouch;
+    std::shared_ptr<TasButtonFactory> tasbuttons;
+    std::shared_ptr<TasAnalogFactory> tasanalog;
+    std::shared_ptr<CemuhookUDP::Client> udp;
+    std::shared_ptr<GCAdapter::Adapter> gcadapter;
+    std::shared_ptr<MouseInput::Mouse> mouse;
+    std::shared_ptr<TasInput::Tas> tas;
 };
 
 InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
@@ -321,28 +227,20 @@ const Keyboard* InputSubsystem::GetKeyboard() const {
     return impl->keyboard.get();
 }
 
-Mouse* InputSubsystem::GetMouse() {
+MouseInput::Mouse* InputSubsystem::GetMouse() {
     return impl->mouse.get();
 }
 
-const Mouse* InputSubsystem::GetMouse() const {
+const MouseInput::Mouse* InputSubsystem::GetMouse() const {
     return impl->mouse.get();
 }
 
-TouchScreen* InputSubsystem::GetTouchScreen() {
-    return impl->touch_screen.get();
-}
-
-const TouchScreen* InputSubsystem::GetTouchScreen() const {
-    return impl->touch_screen.get();
-}
-
 TasInput::Tas* InputSubsystem::GetTas() {
-    return impl->tas_input.get();
+    return impl->tas.get();
 }
 
 const TasInput::Tas* InputSubsystem::GetTas() const {
-    return impl->tas_input.get();
+    return impl->tas.get();
 }
 
 std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
@@ -361,37 +259,100 @@ MotionMapping InputSubsystem::GetMotionMappingForDevice(const Common::ParamPacka
     return impl->GetMotionMappingForDevice(device);
 }
 
-std::string InputSubsystem::GetButtonName(const Common::ParamPackage& params) const {
-    const std::string toggle = params.Get("toggle", false) ? "~" : "";
-    const std::string inverted = params.Get("inverted", false) ? "!" : "";
-    const std::string button_name = impl->GetButtonName(params);
-    std::string axis_direction = "";
-    if (params.Has("axis")) {
-        axis_direction = params.Get("invert", "+");
-    }
-    return fmt::format("{}{}{}{}", toggle, inverted, button_name, axis_direction);
+GCAnalogFactory* InputSubsystem::GetGCAnalogs() {
+    return impl->gcanalog.get();
 }
 
-bool InputSubsystem::IsController(const Common::ParamPackage& params) const {
-    return impl->IsController(params);
+const GCAnalogFactory* InputSubsystem::GetGCAnalogs() const {
+    return impl->gcanalog.get();
+}
+
+GCButtonFactory* InputSubsystem::GetGCButtons() {
+    return impl->gcbuttons.get();
+}
+
+const GCButtonFactory* InputSubsystem::GetGCButtons() const {
+    return impl->gcbuttons.get();
+}
+
+UDPMotionFactory* InputSubsystem::GetUDPMotions() {
+    return impl->udpmotion.get();
+}
+
+const UDPMotionFactory* InputSubsystem::GetUDPMotions() const {
+    return impl->udpmotion.get();
+}
+
+UDPTouchFactory* InputSubsystem::GetUDPTouch() {
+    return impl->udptouch.get();
+}
+
+const UDPTouchFactory* InputSubsystem::GetUDPTouch() const {
+    return impl->udptouch.get();
+}
+
+MouseButtonFactory* InputSubsystem::GetMouseButtons() {
+    return impl->mousebuttons.get();
+}
+
+const MouseButtonFactory* InputSubsystem::GetMouseButtons() const {
+    return impl->mousebuttons.get();
+}
+
+MouseAnalogFactory* InputSubsystem::GetMouseAnalogs() {
+    return impl->mouseanalog.get();
+}
+
+const MouseAnalogFactory* InputSubsystem::GetMouseAnalogs() const {
+    return impl->mouseanalog.get();
+}
+
+MouseMotionFactory* InputSubsystem::GetMouseMotions() {
+    return impl->mousemotion.get();
+}
+
+const MouseMotionFactory* InputSubsystem::GetMouseMotions() const {
+    return impl->mousemotion.get();
+}
+
+MouseTouchFactory* InputSubsystem::GetMouseTouch() {
+    return impl->mousetouch.get();
+}
+
+const MouseTouchFactory* InputSubsystem::GetMouseTouch() const {
+    return impl->mousetouch.get();
+}
+
+TasButtonFactory* InputSubsystem::GetTasButtons() {
+    return impl->tasbuttons.get();
+}
+
+const TasButtonFactory* InputSubsystem::GetTasButtons() const {
+    return impl->tasbuttons.get();
+}
+
+TasAnalogFactory* InputSubsystem::GetTasAnalogs() {
+    return impl->tasanalog.get();
+}
+
+const TasAnalogFactory* InputSubsystem::GetTasAnalogs() const {
+    return impl->tasanalog.get();
 }
 
 void InputSubsystem::ReloadInputDevices() {
-    impl->udp_client.get()->ReloadSockets();
+    if (!impl->udp) {
+        return;
+    }
+    impl->udp->ReloadSockets();
 }
 
-void InputSubsystem::BeginMapping(Polling::InputType type) {
-    impl->BeginConfiguration();
-    impl->mapping_factory->BeginMapping(type);
-}
-
-const Common::ParamPackage InputSubsystem::GetNextInput() const {
-    return impl->mapping_factory->GetNextInput();
-}
-
-void InputSubsystem::StopMapping() const {
-    impl->EndConfiguration();
-    impl->mapping_factory->StopMapping();
+std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers(
+    [[maybe_unused]] Polling::DeviceType type) const {
+#ifdef HAVE_SDL2
+    return impl->sdl->GetPollers(type);
+#else
+    return {};
+#endif
 }
 
 std::string GenerateKeyboardParam(int key_code) {
@@ -402,15 +363,6 @@ std::string GenerateKeyboardParam(int key_code) {
     return param.Serialize();
 }
 
-std::string GenerateModdifierKeyboardParam(int key_code) {
-    Common::ParamPackage param;
-    param.Set("engine", "keyboard");
-    param.Set("code", key_code);
-    param.Set("toggle", false);
-    param.Set("pad", 1);
-    return param.Serialize();
-}
-
 std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
                                         int key_modifier, float modifier_scale) {
     Common::ParamPackage circle_pad_param{
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 9ea395465..6390d3f09 100755
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -25,26 +25,56 @@ namespace Settings::NativeMotion {
 enum Values : int;
 }
 
-namespace InputCommon {
-class Keyboard;
+namespace MouseInput {
 class Mouse;
-class TouchScreen;
-struct MappingData;
-} // namespace InputCommon
+}
 
-namespace InputCommon::TasInput {
+namespace TasInput {
 class Tas;
-} // namespace InputCommon::TasInput
+}
 
 namespace InputCommon {
 namespace Polling {
-/// Type of input desired for mapping purposes
-enum class InputType { None, Button, Stick, Motion, Touch };
+
+enum class DeviceType { Button, AnalogPreferred, Motion };
+
+/**
+ * A class that can be used to get inputs from an input device like controllers without having to
+ * poll the device's status yourself
+ */
+class DevicePoller {
+public:
+    virtual ~DevicePoller() = default;
+    /// Setup and start polling for inputs, should be called before GetNextInput
+    /// If a device_id is provided, events should be filtered to only include events from this
+    /// device id
+    virtual void Start(const std::string& device_id = "") = 0;
+    /// Stop polling
+    virtual void Stop() = 0;
+    /**
+     * Every call to this function returns the next input recorded since calling Start
+     * @return A ParamPackage of the recorded input, which can be used to create an InputDevice.
+     *         If there has been no input, the package is empty
+     */
+    virtual Common::ParamPackage GetNextInput() = 0;
+};
 } // namespace Polling
 
+class GCAnalogFactory;
+class GCButtonFactory;
+class UDPMotionFactory;
+class UDPTouchFactory;
+class MouseButtonFactory;
+class MouseAnalogFactory;
+class MouseMotionFactory;
+class MouseTouchFactory;
+class TasButtonFactory;
+class TasAnalogFactory;
+class Keyboard;
+
 /**
  * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
- * mapping for the device.
+ * mapping for the device. This is currently only implemented for the SDL backend devices.
  */
 using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
 using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
@@ -74,27 +104,20 @@ public:
     [[nodiscard]] const Keyboard* GetKeyboard() const;
 
     /// Retrieves the underlying mouse device.
-    [[nodiscard]] Mouse* GetMouse();
+    [[nodiscard]] MouseInput::Mouse* GetMouse();
 
     /// Retrieves the underlying mouse device.
-    [[nodiscard]] const Mouse* GetMouse() const;
+    [[nodiscard]] const MouseInput::Mouse* GetMouse() const;
 
-    /// Retrieves the underlying touch screen device.
-    [[nodiscard]] TouchScreen* GetTouchScreen();
-
-    /// Retrieves the underlying touch screen device.
-    [[nodiscard]] const TouchScreen* GetTouchScreen() const;
-
-    /// Retrieves the underlying tas input device.
+    /// Retrieves the underlying tas device.
     [[nodiscard]] TasInput::Tas* GetTas();
 
-    /// Retrieves the underlying  tas input  device.
+    /// Retrieves the underlying tas device.
     [[nodiscard]] const TasInput::Tas* GetTas() const;
-
     /**
      * Returns all available input devices that this Factory can create a new device with.
-     * Each returned ParamPackage should have a `display` field used for display, a `engine` field
-     * for backends to determine if this backend is meant to service the request and any other
+     * Each returned ParamPackage should have a `display` field used for display, a class field for
+     * backends to determine if this backend is meant to service the request and any other
      * information needed to identify this in the backend later.
      */
     [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const;
@@ -108,36 +131,83 @@ public:
     /// Retrieves the motion mappings for the given device.
     [[nodiscard]] MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& device) const;
 
-    /// Returns a string contaning the name of the button from the input engine.
-    [[nodiscard]] std::string GetButtonName(const Common::ParamPackage& params) const;
+    /// Retrieves the underlying GameCube analog handler.
+    [[nodiscard]] GCAnalogFactory* GetGCAnalogs();
 
-    /// Returns true if device is a controller.
-    [[nodiscard]] bool IsController(const Common::ParamPackage& params) const;
+    /// Retrieves the underlying GameCube analog handler.
+    [[nodiscard]] const GCAnalogFactory* GetGCAnalogs() const;
 
-    /// Reloads the input devices.
+    /// Retrieves the underlying GameCube button handler.
+    [[nodiscard]] GCButtonFactory* GetGCButtons();
+
+    /// Retrieves the underlying GameCube button handler.
+    [[nodiscard]] const GCButtonFactory* GetGCButtons() const;
+
+    /// Retrieves the underlying udp motion handler.
+    [[nodiscard]] UDPMotionFactory* GetUDPMotions();
+
+    /// Retrieves the underlying udp motion handler.
+    [[nodiscard]] const UDPMotionFactory* GetUDPMotions() const;
+
+    /// Retrieves the underlying udp touch handler.
+    [[nodiscard]] UDPTouchFactory* GetUDPTouch();
+
+    /// Retrieves the underlying udp touch handler.
+    [[nodiscard]] const UDPTouchFactory* GetUDPTouch() const;
+
+    /// Retrieves the underlying mouse button handler.
+    [[nodiscard]] MouseButtonFactory* GetMouseButtons();
+
+    /// Retrieves the underlying mouse button handler.
+    [[nodiscard]] const MouseButtonFactory* GetMouseButtons() const;
+
+    /// Retrieves the underlying mouse analog handler.
+    [[nodiscard]] MouseAnalogFactory* GetMouseAnalogs();
+
+    /// Retrieves the underlying mouse analog handler.
+    [[nodiscard]] const MouseAnalogFactory* GetMouseAnalogs() const;
+
+    /// Retrieves the underlying mouse motion handler.
+    [[nodiscard]] MouseMotionFactory* GetMouseMotions();
+
+    /// Retrieves the underlying mouse motion handler.
+    [[nodiscard]] const MouseMotionFactory* GetMouseMotions() const;
+
+    /// Retrieves the underlying mouse touch handler.
+    [[nodiscard]] MouseTouchFactory* GetMouseTouch();
+
+    /// Retrieves the underlying mouse touch handler.
+    [[nodiscard]] const MouseTouchFactory* GetMouseTouch() const;
+
+    /// Retrieves the underlying tas button handler.
+    [[nodiscard]] TasButtonFactory* GetTasButtons();
+
+    /// Retrieves the underlying tas button handler.
+    [[nodiscard]] const TasButtonFactory* GetTasButtons() const;
+
+    /// Retrieves the underlying tas analogs handler.
+    [[nodiscard]] TasAnalogFactory* GetTasAnalogs();
+
+    /// Retrieves the underlying tas analogs handler.
+    [[nodiscard]] const TasAnalogFactory* GetTasAnalogs() const;
+
+    /// Reloads the input devices
     void ReloadInputDevices();
 
-    /// Start polling from all backends for a desired input type.
-    void BeginMapping(Polling::InputType type);
-
-    /// Returns an input event with mapping information.
-    [[nodiscard]] const Common::ParamPackage GetNextInput() const;
-
-    /// Stop polling from all backends.
-    void StopMapping() const;
+    /// Get all DevicePoller from all backends for a specific device type
+    [[nodiscard]] std::vector<std::unique_ptr<Polling::DevicePoller>> GetPollers(
+        Polling::DeviceType type) const;
 
 private:
     struct Impl;
     std::unique_ptr<Impl> impl;
 };
 
-/// Generates a serialized param package for creating a keyboard button device.
+/// Generates a serialized param package for creating a keyboard button device
 std::string GenerateKeyboardParam(int key_code);
 
-/// Generates a serialized param package for creating a moddifier keyboard button device.
-std::string GenerateModdifierKeyboardParam(int key_code);
-
-/// Generates a serialized param package for creating an analog device taking input from keyboard.
+/// Generates a serialized param package for creating an analog device taking input from keyboard
 std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
                                         int key_modifier, float modifier_scale);
+
 } // namespace InputCommon
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 6a2cdda63..bf8445a89 100755
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -6,12 +6,8 @@
 #include <thread>
 
 #include "common/assert.h"
-#include "common/param_package.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/hle/lock.h"
 #include "core/hle/service/hid/controllers/npad.h"
 #include "core/hle/service/hid/hid.h"
@@ -27,32 +23,49 @@
 
 namespace {
 
-void UpdateController(Core::HID::EmulatedController* controller,
-                      Core::HID::NpadStyleIndex controller_type, bool connected) {
-    if (controller->IsConnected()) {
-        controller->Disconnect();
-    }
-    controller->SetNpadStyleIndex(controller_type);
-    if (connected) {
-        controller->Connect();
+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;
     }
+
+    auto& npad =
+        system.ServiceManager()
+            .GetService<Service::HID::Hid>("hid")
+            ->GetAppletResource()
+            ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
+
+    npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
 }
 
 // Returns true if the given controller type is compatible with the given parameters.
-bool IsControllerCompatible(Core::HID::NpadStyleIndex controller_type,
+bool IsControllerCompatible(Settings::ControllerType controller_type,
                             Core::Frontend::ControllerParameters parameters) {
     switch (controller_type) {
-    case Core::HID::NpadStyleIndex::ProController:
+    case Settings::ControllerType::ProController:
         return parameters.allow_pro_controller;
-    case Core::HID::NpadStyleIndex::JoyconDual:
+    case Settings::ControllerType::DualJoyconDetached:
         return parameters.allow_dual_joycons;
-    case Core::HID::NpadStyleIndex::JoyconLeft:
+    case Settings::ControllerType::LeftJoycon:
         return parameters.allow_left_joycon;
-    case Core::HID::NpadStyleIndex::JoyconRight:
+    case Settings::ControllerType::RightJoycon:
         return parameters.allow_right_joycon;
-    case Core::HID::NpadStyleIndex::Handheld:
+    case Settings::ControllerType::Handheld:
         return parameters.enable_single_mode && parameters.allow_handheld;
-    case Core::HID::NpadStyleIndex::GameCube:
+    case Settings::ControllerType::GameCube:
         return parameters.allow_gamecube_controller;
     default:
         return false;
@@ -183,7 +196,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
             connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
                     [this, i](int index) {
                         UpdateDockedState(GetControllerTypeFromIndex(index, i) ==
-                                          Core::HID::NpadStyleIndex::Handheld);
+                                          Settings::ControllerType::Handheld);
                     });
         }
     }
@@ -236,17 +249,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* controller = system.HIDCore().GetEmulatedControllerByIndex(index);
-        const auto connected = controller->IsConnected() || (index == 0 && handheld->IsConnected());
+        const auto connected =
+            Settings::values.players.GetValue()[index].connected ||
+            (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(controller->GetNpadStyleIndex(), index));
+        emulated_controllers[index]->setCurrentIndex(GetIndexFromControllerType(
+            Settings::values.players.GetValue()[index].controller_type, index));
     }
 
-    UpdateDockedState(handheld->IsConnected());
+    UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
 
     ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
     ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
@@ -402,33 +415,33 @@ void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index
     emulated_controllers[player_index]->clear();
 
     pairs.emplace_back(emulated_controllers[player_index]->count(),
-                       Core::HID::NpadStyleIndex::ProController);
+                       Settings::ControllerType::ProController);
     emulated_controllers[player_index]->addItem(tr("Pro Controller"));
 
     pairs.emplace_back(emulated_controllers[player_index]->count(),
-                       Core::HID::NpadStyleIndex::JoyconDual);
+                       Settings::ControllerType::DualJoyconDetached);
     emulated_controllers[player_index]->addItem(tr("Dual Joycons"));
 
     pairs.emplace_back(emulated_controllers[player_index]->count(),
-                       Core::HID::NpadStyleIndex::JoyconLeft);
+                       Settings::ControllerType::LeftJoycon);
     emulated_controllers[player_index]->addItem(tr("Left Joycon"));
 
     pairs.emplace_back(emulated_controllers[player_index]->count(),
-                       Core::HID::NpadStyleIndex::JoyconRight);
+                       Settings::ControllerType::RightJoycon);
     emulated_controllers[player_index]->addItem(tr("Right Joycon"));
 
     if (player_index == 0) {
         pairs.emplace_back(emulated_controllers[player_index]->count(),
-                           Core::HID::NpadStyleIndex::Handheld);
+                           Settings::ControllerType::Handheld);
         emulated_controllers[player_index]->addItem(tr("Handheld"));
     }
 
     pairs.emplace_back(emulated_controllers[player_index]->count(),
-                       Core::HID::NpadStyleIndex::GameCube);
+                       Settings::ControllerType::GameCube);
     emulated_controllers[player_index]->addItem(tr("GameCube Controller"));
 }
 
-Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex(
+Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex(
     int index, std::size_t player_index) const {
     const auto& pairs = index_controller_type_pairs[player_index];
 
@@ -436,13 +449,13 @@ Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex
                                  [index](const auto& pair) { return pair.first == index; });
 
     if (it == pairs.end()) {
-        return Core::HID::NpadStyleIndex::ProController;
+        return Settings::ControllerType::ProController;
     }
 
     return it->second;
 }
 
-int QtControllerSelectorDialog::GetIndexFromControllerType(Core::HID::NpadStyleIndex type,
+int QtControllerSelectorDialog::GetIndexFromControllerType(Settings::ControllerType type,
                                                            std::size_t player_index) const {
     const auto& pairs = index_controller_type_pairs[player_index];
 
@@ -466,16 +479,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 Core::HID::NpadStyleIndex::ProController:
-        case Core::HID::NpadStyleIndex::GameCube:
+        case Settings::ControllerType::ProController:
+        case Settings::ControllerType::GameCube:
             return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
-        case Core::HID::NpadStyleIndex::JoyconDual:
+        case Settings::ControllerType::DualJoyconDetached:
             return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ");
-        case Core::HID::NpadStyleIndex::JoyconLeft:
+        case Settings::ControllerType::LeftJoycon:
             return QStringLiteral("image: url(:/controller/applet_joycon_left%0); ");
-        case Core::HID::NpadStyleIndex::JoyconRight:
+        case Settings::ControllerType::RightJoycon:
             return QStringLiteral("image: url(:/controller/applet_joycon_right%0); ");
-        case Core::HID::NpadStyleIndex::Handheld:
+        case Settings::ControllerType::Handheld:
             return QStringLiteral("image: url(:/controller/applet_handheld%0); ");
         default:
             return QString{};
@@ -503,42 +516,54 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
 }
 
 void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
-    auto* controller = system.HIDCore().GetEmulatedControllerByIndex(player_index);
+    auto& player = Settings::values.players.GetValue()[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;
+                                  controller_type != Settings::ControllerType::Handheld;
 
-    if (controller->GetNpadStyleIndex() == controller_type &&
-        controller->IsConnected() == player_connected) {
+    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, controller_type, false);
+    UpdateController(controller_type, player_index, false, system);
+
+    player.controller_type = controller_type;
+    player.connected = player_connected;
 
     ConfigureVibration::SetVibrationDevices(player_index);
 
     // Handheld
     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());
+        auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
+        if (controller_type == Settings::ControllerType::Handheld) {
+            handheld = player;
         }
+        handheld.connected = player_groupboxes[player_index]->isChecked() &&
+                             controller_type == Settings::ControllerType::Handheld;
+        UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected, system);
     }
 
-    UpdateController(controller, controller_type, player_connected);
+    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);
 }
 
 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) {
+                                   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);
@@ -546,12 +571,10 @@ void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
         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);
+    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]);
 }
 
 void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) {
@@ -631,9 +654,10 @@ 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.
-        UpdateController(controller, controller->GetNpadStyleIndex(), false);
+        Settings::values.players.GetValue()[index].connected = false;
+        UpdateController(Settings::values.players.GetValue()[index].controller_type, index, false,
+                         system);
         // 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 cc343e5ae..037325f50 100755
--- a/src/yuzu/applets/qt_controller.h
+++ b/src/yuzu/applets/qt_controller.h
@@ -23,18 +23,14 @@ namespace InputCommon {
 class InputSubsystem;
 }
 
+namespace Settings {
+enum class ControllerType;
+}
+
 namespace Ui {
 class QtControllerSelectorDialog;
 }
 
-namespace Core {
-class System;
-}
-
-namespace Core::HID {
-enum class NpadStyleIndex : u8;
-}
-
 class QtControllerSelectorDialog final : public QDialog {
     Q_OBJECT
 
@@ -74,10 +70,10 @@ private:
     void SetEmulatedControllers(std::size_t player_index);
 
     // Gets the Controller Type for a given controller combobox index per player.
-    Core::HID::NpadStyleIndex GetControllerTypeFromIndex(int index, std::size_t player_index) const;
+    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(Core::HID::NpadStyleIndex type, std::size_t player_index) const;
+    int GetIndexFromControllerType(Settings::ControllerType type, std::size_t player_index) const;
 
     // Updates the controller icons per player.
     void UpdateControllerIcon(std::size_t player_index);
@@ -139,7 +135,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, Core::HID::NpadStyleIndex>>, NUM_PLAYERS>
+    std::array<std::vector<std::pair<int, Settings::ControllerType>>, 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 de7f98c4f..a83a11a95 100755
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -10,10 +10,7 @@
 #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 "core/frontend/input_interpreter.h"
 #include "ui_qt_software_keyboard.h"
 #include "yuzu/applets/qt_software_keyboard.h"
 #include "yuzu/main.h"
@@ -487,7 +484,7 @@ void QtSoftwareKeyboardDialog::open() {
 void QtSoftwareKeyboardDialog::reject() {
     // Pressing the ESC key in a dialog calls QDialog::reject().
     // We will override this behavior to the "Cancel" action on the software keyboard.
-    TranslateButtonPress(Core::HID::NpadButton::X);
+    TranslateButtonPress(HIDButton::X);
 }
 
 void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) {
@@ -725,7 +722,7 @@ void QtSoftwareKeyboardDialog::SetTextDrawType() {
 
         connect(
             ui->line_edit_osk, &QLineEdit::returnPressed, this,
-            [this] { TranslateButtonPress(Core::HID::NpadButton::Plus); }, Qt::QueuedConnection);
+            [this] { TranslateButtonPress(HIDButton::Plus); }, Qt::QueuedConnection);
 
         ui->line_edit_osk->setPlaceholderText(
             QString::fromStdU16String(initialize_parameters.guide_text));
@@ -798,10 +795,9 @@ void QtSoftwareKeyboardDialog::SetTextDrawType() {
 }
 
 void QtSoftwareKeyboardDialog::SetControllerImage() {
-    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->GetNpadStyleIndex() : player_1->GetNpadStyleIndex();
+    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 QString theme = [] {
         if (QIcon::themeName().contains(QStringLiteral("dark")) ||
@@ -813,8 +809,8 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
     }();
 
     switch (controller_type) {
-    case Core::HID::NpadStyleIndex::ProController:
-    case Core::HID::NpadStyleIndex::GameCube:
+    case Settings::ControllerType::ProController:
+    case Settings::ControllerType::GameCube:
         ui->icon_controller->setStyleSheet(
             QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
         ui->icon_controller_shift->setStyleSheet(
@@ -822,7 +818,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
         ui->icon_controller_num->setStyleSheet(
             QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
         break;
-    case Core::HID::NpadStyleIndex::JoyconDual:
+    case Settings::ControllerType::DualJoyconDetached:
         ui->icon_controller->setStyleSheet(
             QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme));
         ui->icon_controller_shift->setStyleSheet(
@@ -830,7 +826,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
         ui->icon_controller_num->setStyleSheet(
             QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme));
         break;
-    case Core::HID::NpadStyleIndex::JoyconLeft:
+    case Settings::ControllerType::LeftJoycon:
         ui->icon_controller->setStyleSheet(
             QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);")
                 .arg(theme));
@@ -841,7 +837,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
             QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);")
                 .arg(theme));
         break;
-    case Core::HID::NpadStyleIndex::JoyconRight:
+    case Settings::ControllerType::RightJoycon:
         ui->icon_controller->setStyleSheet(
             QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);")
                 .arg(theme));
@@ -852,7 +848,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
             QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);")
                 .arg(theme));
         break;
-    case Core::HID::NpadStyleIndex::Handheld:
+    case Settings::ControllerType::Handheld:
         ui->icon_controller->setStyleSheet(
             QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme));
         ui->icon_controller_shift->setStyleSheet(
@@ -1212,9 +1208,9 @@ void QtSoftwareKeyboardDialog::SetupMouseHover() {
     }
 }
 
-template <Core::HID::NpadButton... T>
+template <HIDButton... T>
 void QtSoftwareKeyboardDialog::HandleButtonPressedOnce() {
-    const auto f = [this](Core::HID::NpadButton button) {
+    const auto f = [this](HIDButton button) {
         if (input_interpreter->IsButtonPressedOnce(button)) {
             TranslateButtonPress(button);
         }
@@ -1223,9 +1219,9 @@ void QtSoftwareKeyboardDialog::HandleButtonPressedOnce() {
     (f(T), ...);
 }
 
-template <Core::HID::NpadButton... T>
+template <HIDButton... T>
 void QtSoftwareKeyboardDialog::HandleButtonHold() {
-    const auto f = [this](Core::HID::NpadButton button) {
+    const auto f = [this](HIDButton button) {
         if (input_interpreter->IsButtonHeld(button)) {
             TranslateButtonPress(button);
         }
@@ -1234,9 +1230,9 @@ void QtSoftwareKeyboardDialog::HandleButtonHold() {
     (f(T), ...);
 }
 
-void QtSoftwareKeyboardDialog::TranslateButtonPress(Core::HID::NpadButton button) {
+void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
     switch (button) {
-    case Core::HID::NpadButton::A:
+    case HIDButton::A:
         switch (bottom_osk_index) {
         case BottomOSKIndex::LowerCase:
         case BottomOSKIndex::UpperCase:
@@ -1249,7 +1245,7 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(Core::HID::NpadButton button
             break;
         }
         break;
-    case Core::HID::NpadButton::B:
+    case HIDButton::B:
         switch (bottom_osk_index) {
         case BottomOSKIndex::LowerCase:
             ui->button_backspace->click();
@@ -1264,7 +1260,7 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(Core::HID::NpadButton button
             break;
         }
         break;
-    case Core::HID::NpadButton::X:
+    case HIDButton::X:
         if (is_inline) {
             emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position);
         } else {
@@ -1275,7 +1271,7 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(Core::HID::NpadButton button
             emit SubmitNormalText(SwkbdResult::Cancel, std::move(text));
         }
         break;
-    case Core::HID::NpadButton::Y:
+    case HIDButton::Y:
         switch (bottom_osk_index) {
         case BottomOSKIndex::LowerCase:
             ui->button_space->click();
@@ -1288,8 +1284,8 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(Core::HID::NpadButton button
             break;
         }
         break;
-    case Core::HID::NpadButton::StickL:
-    case Core::HID::NpadButton::StickR:
+    case HIDButton::LStick:
+    case HIDButton::RStick:
         switch (bottom_osk_index) {
         case BottomOSKIndex::LowerCase:
             ui->button_shift->click();
@@ -1302,13 +1298,13 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(Core::HID::NpadButton button
             break;
         }
         break;
-    case Core::HID::NpadButton::L:
+    case HIDButton::L:
         MoveTextCursorDirection(Direction::Left);
         break;
-    case Core::HID::NpadButton::R:
+    case HIDButton::R:
         MoveTextCursorDirection(Direction::Right);
         break;
-    case Core::HID::NpadButton::Plus:
+    case HIDButton::Plus:
         switch (bottom_osk_index) {
         case BottomOSKIndex::LowerCase:
             ui->button_ok->click();
@@ -1323,24 +1319,24 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(Core::HID::NpadButton button
             break;
         }
         break;
-    case Core::HID::NpadButton::Left:
-    case Core::HID::NpadButton::StickLLeft:
-    case Core::HID::NpadButton::StickRLeft:
+    case HIDButton::DLeft:
+    case HIDButton::LStickLeft:
+    case HIDButton::RStickLeft:
         MoveButtonDirection(Direction::Left);
         break;
-    case Core::HID::NpadButton::Up:
-    case Core::HID::NpadButton::StickLUp:
-    case Core::HID::NpadButton::StickRUp:
+    case HIDButton::DUp:
+    case HIDButton::LStickUp:
+    case HIDButton::RStickUp:
         MoveButtonDirection(Direction::Up);
         break;
-    case Core::HID::NpadButton::Right:
-    case Core::HID::NpadButton::StickLRight:
-    case Core::HID::NpadButton::StickRRight:
+    case HIDButton::DRight:
+    case HIDButton::LStickRight:
+    case HIDButton::RStickRight:
         MoveButtonDirection(Direction::Right);
         break;
-    case Core::HID::NpadButton::Down:
-    case Core::HID::NpadButton::StickLDown:
-    case Core::HID::NpadButton::StickRDown:
+    case HIDButton::DDown:
+    case HIDButton::LStickDown:
+    case HIDButton::RStickDown:
         MoveButtonDirection(Direction::Down);
         break;
     default:
@@ -1471,25 +1467,19 @@ void QtSoftwareKeyboardDialog::InputThread() {
     while (input_thread_running) {
         input_interpreter->PollInput();
 
-        HandleButtonPressedOnce<
-            Core::HID::NpadButton::A, Core::HID::NpadButton::B, Core::HID::NpadButton::X,
-            Core::HID::NpadButton::Y, Core::HID::NpadButton::StickL, Core::HID::NpadButton::StickR,
-            Core::HID::NpadButton::L, Core::HID::NpadButton::R, Core::HID::NpadButton::Plus,
-            Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right,
-            Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft,
-            Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight,
-            Core::HID::NpadButton::StickLDown, Core::HID::NpadButton::StickRLeft,
-            Core::HID::NpadButton::StickRUp, Core::HID::NpadButton::StickRRight,
-            Core::HID::NpadButton::StickRDown>();
+        HandleButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::X, HIDButton::Y,
+                                HIDButton::LStick, HIDButton::RStick, HIDButton::L, HIDButton::R,
+                                HIDButton::Plus, HIDButton::DLeft, HIDButton::DUp,
+                                HIDButton::DRight, HIDButton::DDown, HIDButton::LStickLeft,
+                                HIDButton::LStickUp, HIDButton::LStickRight, HIDButton::LStickDown,
+                                HIDButton::RStickLeft, HIDButton::RStickUp, HIDButton::RStickRight,
+                                HIDButton::RStickDown>();
 
-        HandleButtonHold<Core::HID::NpadButton::B, Core::HID::NpadButton::L,
-                         Core::HID::NpadButton::R, Core::HID::NpadButton::Left,
-                         Core::HID::NpadButton::Up, Core::HID::NpadButton::Right,
-                         Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft,
-                         Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight,
-                         Core::HID::NpadButton::StickLDown, Core::HID::NpadButton::StickRLeft,
-                         Core::HID::NpadButton::StickRUp, Core::HID::NpadButton::StickRRight,
-                         Core::HID::NpadButton::StickRDown>();
+        HandleButtonHold<HIDButton::B, HIDButton::L, HIDButton::R, HIDButton::DLeft, HIDButton::DUp,
+                         HIDButton::DRight, HIDButton::DDown, HIDButton::LStickLeft,
+                         HIDButton::LStickUp, HIDButton::LStickRight, HIDButton::LStickDown,
+                         HIDButton::RStickLeft, HIDButton::RStickUp, HIDButton::RStickRight,
+                         HIDButton::RStickDown>();
 
         std::this_thread::sleep_for(std::chrono::milliseconds(50));
     }
diff --git a/src/yuzu/applets/qt_software_keyboard.h b/src/yuzu/applets/qt_software_keyboard.h
index b030cdcf7..592d9c085 100755
--- a/src/yuzu/applets/qt_software_keyboard.h
+++ b/src/yuzu/applets/qt_software_keyboard.h
@@ -14,16 +14,14 @@
 
 #include "core/frontend/applets/software_keyboard.h"
 
+enum class HIDButton : u8;
+
 class InputInterpreter;
 
 namespace Core {
 class System;
 }
 
-namespace Core::HID {
-enum class NpadButton : u64;
-}
-
 namespace Ui {
 class QtSoftwareKeyboardDialog;
 }
@@ -148,7 +146,7 @@ private:
      *
      * @tparam HIDButton The list of buttons that can be converted into keyboard input.
      */
-    template <Core::HID::NpadButton... T>
+    template <HIDButton... T>
     void HandleButtonPressedOnce();
 
     /**
@@ -156,7 +154,7 @@ private:
      *
      * @tparam HIDButton The list of buttons that can be converted into keyboard input.
      */
-    template <Core::HID::NpadButton... T>
+    template <HIDButton... T>
     void HandleButtonHold();
 
     /**
@@ -164,7 +162,7 @@ private:
      *
      * @param button The button press to process.
      */
-    void TranslateButtonPress(Core::HID::NpadButton button);
+    void TranslateButtonPress(HIDButton button);
 
     /**
      * Moves the focus of a button in a certain direction.
diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp
index cb3c5d826..da8c6882a 100755
--- a/src/yuzu/applets/qt_web_browser.cpp
+++ b/src/yuzu/applets/qt_web_browser.cpp
@@ -14,11 +14,9 @@
 #endif
 
 #include "common/fs/path_util.h"
-#include "common/param_package.h"
 #include "core/core.h"
-#include "core/hid/hid_types.h"
-#include "core/hid/input_interpreter.h"
-#include "input_common/drivers/keyboard.h"
+#include "core/frontend/input_interpreter.h"
+#include "input_common/keyboard.h"
 #include "input_common/main.h"
 #include "yuzu/applets/qt_web_browser.h"
 #include "yuzu/applets/qt_web_browser_scripts.h"
@@ -29,19 +27,19 @@
 
 namespace {
 
-constexpr int HIDButtonToKey(Core::HID::NpadButton button) {
+constexpr int HIDButtonToKey(HIDButton button) {
     switch (button) {
-    case Core::HID::NpadButton::Left:
-    case Core::HID::NpadButton::StickLLeft:
+    case HIDButton::DLeft:
+    case HIDButton::LStickLeft:
         return Qt::Key_Left;
-    case Core::HID::NpadButton::Up:
-    case Core::HID::NpadButton::StickLUp:
+    case HIDButton::DUp:
+    case HIDButton::LStickUp:
         return Qt::Key_Up;
-    case Core::HID::NpadButton::Right:
-    case Core::HID::NpadButton::StickLRight:
+    case HIDButton::DRight:
+    case HIDButton::LStickRight:
         return Qt::Key_Right;
-    case Core::HID::NpadButton::Down:
-    case Core::HID::NpadButton::StickLDown:
+    case HIDButton::DDown:
+    case HIDButton::LStickDown:
         return Qt::Key_Down;
     default:
         return 0;
@@ -210,25 +208,25 @@ void QtNXWebEngineView::keyReleaseEvent(QKeyEvent* event) {
     }
 }
 
-template <Core::HID::NpadButton... T>
+template <HIDButton... T>
 void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() {
-    const auto f = [this](Core::HID::NpadButton button) {
+    const auto f = [this](HIDButton button) {
         if (input_interpreter->IsButtonPressedOnce(button)) {
             page()->runJavaScript(
                 QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)),
                 [this, button](const QVariant& variant) {
                     if (variant.toBool()) {
                         switch (button) {
-                        case Core::HID::NpadButton::A:
+                        case HIDButton::A:
                             SendMultipleKeyPressEvents<Qt::Key_A, Qt::Key_Space, Qt::Key_Return>();
                             break;
-                        case Core::HID::NpadButton::B:
+                        case HIDButton::B:
                             SendKeyPressEvent(Qt::Key_B);
                             break;
-                        case Core::HID::NpadButton::X:
+                        case HIDButton::X:
                             SendKeyPressEvent(Qt::Key_X);
                             break;
-                        case Core::HID::NpadButton::Y:
+                        case HIDButton::Y:
                             SendKeyPressEvent(Qt::Key_Y);
                             break;
                         default:
@@ -246,9 +244,9 @@ void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() {
     (f(T), ...);
 }
 
-template <Core::HID::NpadButton... T>
+template <HIDButton... T>
 void QtNXWebEngineView::HandleWindowKeyButtonPressedOnce() {
-    const auto f = [this](Core::HID::NpadButton button) {
+    const auto f = [this](HIDButton button) {
         if (input_interpreter->IsButtonPressedOnce(button)) {
             SendKeyPressEvent(HIDButtonToKey(button));
         }
@@ -257,9 +255,9 @@ void QtNXWebEngineView::HandleWindowKeyButtonPressedOnce() {
     (f(T), ...);
 }
 
-template <Core::HID::NpadButton... T>
+template <HIDButton... T>
 void QtNXWebEngineView::HandleWindowKeyButtonHold() {
-    const auto f = [this](Core::HID::NpadButton button) {
+    const auto f = [this](HIDButton button) {
         if (input_interpreter->IsButtonHeld(button)) {
             SendKeyPressEvent(HIDButtonToKey(button));
         }
@@ -310,21 +308,17 @@ void QtNXWebEngineView::InputThread() {
     while (input_thread_running) {
         input_interpreter->PollInput();
 
-        HandleWindowFooterButtonPressedOnce<Core::HID::NpadButton::A, Core::HID::NpadButton::B,
-                                            Core::HID::NpadButton::X, Core::HID::NpadButton::Y,
-                                            Core::HID::NpadButton::L, Core::HID::NpadButton::R>();
+        HandleWindowFooterButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::X, HIDButton::Y,
+                                            HIDButton::L, HIDButton::R>();
 
-        HandleWindowKeyButtonPressedOnce<
-            Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right,
-            Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft,
-            Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight,
-            Core::HID::NpadButton::StickLDown>();
+        HandleWindowKeyButtonPressedOnce<HIDButton::DLeft, HIDButton::DUp, HIDButton::DRight,
+                                         HIDButton::DDown, HIDButton::LStickLeft,
+                                         HIDButton::LStickUp, HIDButton::LStickRight,
+                                         HIDButton::LStickDown>();
 
-        HandleWindowKeyButtonHold<
-            Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right,
-            Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft,
-            Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight,
-            Core::HID::NpadButton::StickLDown>();
+        HandleWindowKeyButtonHold<HIDButton::DLeft, HIDButton::DUp, HIDButton::DRight,
+                                  HIDButton::DDown, HIDButton::LStickLeft, HIDButton::LStickUp,
+                                  HIDButton::LStickRight, HIDButton::LStickDown>();
 
         std::this_thread::sleep_for(std::chrono::milliseconds(50));
     }
diff --git a/src/yuzu/applets/qt_web_browser.h b/src/yuzu/applets/qt_web_browser.h
index fa18aecac..7e9f703fc 100755
--- a/src/yuzu/applets/qt_web_browser.h
+++ b/src/yuzu/applets/qt_web_browser.h
@@ -16,6 +16,8 @@
 
 #include "core/frontend/applets/web_browser.h"
 
+enum class HIDButton : u8;
+
 class GMainWindow;
 class InputInterpreter;
 class UrlRequestInterceptor;
@@ -24,10 +26,6 @@ namespace Core {
 class System;
 }
 
-namespace Core::HID {
-enum class NpadButton : u64;
-}
-
 namespace InputCommon {
 class InputSubsystem;
 }
@@ -116,7 +114,7 @@ private:
      *
      * @tparam HIDButton The list of buttons contained in yuzu_key_callbacks
      */
-    template <Core::HID::NpadButton... T>
+    template <HIDButton... T>
     void HandleWindowFooterButtonPressedOnce();
 
     /**
@@ -125,7 +123,7 @@ private:
      *
      * @tparam HIDButton The list of buttons that can be converted into keyboard input.
      */
-    template <Core::HID::NpadButton... T>
+    template <HIDButton... T>
     void HandleWindowKeyButtonPressedOnce();
 
     /**
@@ -134,7 +132,7 @@ private:
      *
      * @tparam HIDButton The list of buttons that can be converted into keyboard input.
      */
-    template <Core::HID::NpadButton... T>
+    template <HIDButton... T>
     void HandleWindowKeyButtonHold();
 
     /**
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index f12312ab9..976acd176 100755
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -32,11 +32,10 @@
 #include "common/settings.h"
 #include "core/core.h"
 #include "core/frontend/framebuffer_layout.h"
-#include "input_common/drivers/keyboard.h"
-#include "input_common/drivers/mouse.h"
-#include "input_common/drivers/tas_input.h"
-#include "input_common/drivers/touch_screen.h"
+#include "input_common/keyboard.h"
 #include "input_common/main.h"
+#include "input_common/mouse/mouse_input.h"
+#include "input_common/tas/tas_input.h"
 #include "video_core/renderer_base.h"
 #include "video_core/video_core.h"
 #include "yuzu/bootmanager.h"
@@ -297,6 +296,7 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
     layout->setContentsMargins(0, 0, 0, 0);
     setLayout(layout);
     input_subsystem->Initialize();
+
     this->setMouseTracking(true);
 
     connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
@@ -383,306 +383,34 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
     QWidget::closeEvent(event);
 }
 
-int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) {
-    switch (qt_key) {
-    case Qt::Key_A:
-        return Settings::NativeKeyboard::A;
-    case Qt::Key_B:
-        return Settings::NativeKeyboard::B;
-    case Qt::Key_C:
-        return Settings::NativeKeyboard::C;
-    case Qt::Key_D:
-        return Settings::NativeKeyboard::D;
-    case Qt::Key_E:
-        return Settings::NativeKeyboard::E;
-    case Qt::Key_F:
-        return Settings::NativeKeyboard::F;
-    case Qt::Key_G:
-        return Settings::NativeKeyboard::G;
-    case Qt::Key_H:
-        return Settings::NativeKeyboard::H;
-    case Qt::Key_I:
-        return Settings::NativeKeyboard::I;
-    case Qt::Key_J:
-        return Settings::NativeKeyboard::J;
-    case Qt::Key_K:
-        return Settings::NativeKeyboard::K;
-    case Qt::Key_L:
-        return Settings::NativeKeyboard::L;
-    case Qt::Key_M:
-        return Settings::NativeKeyboard::M;
-    case Qt::Key_N:
-        return Settings::NativeKeyboard::N;
-    case Qt::Key_O:
-        return Settings::NativeKeyboard::O;
-    case Qt::Key_P:
-        return Settings::NativeKeyboard::P;
-    case Qt::Key_Q:
-        return Settings::NativeKeyboard::Q;
-    case Qt::Key_R:
-        return Settings::NativeKeyboard::R;
-    case Qt::Key_S:
-        return Settings::NativeKeyboard::S;
-    case Qt::Key_T:
-        return Settings::NativeKeyboard::T;
-    case Qt::Key_U:
-        return Settings::NativeKeyboard::U;
-    case Qt::Key_V:
-        return Settings::NativeKeyboard::V;
-    case Qt::Key_W:
-        return Settings::NativeKeyboard::W;
-    case Qt::Key_X:
-        return Settings::NativeKeyboard::X;
-    case Qt::Key_Y:
-        return Settings::NativeKeyboard::Y;
-    case Qt::Key_Z:
-        return Settings::NativeKeyboard::Z;
-    case Qt::Key_1:
-        return Settings::NativeKeyboard::N1;
-    case Qt::Key_2:
-        return Settings::NativeKeyboard::N2;
-    case Qt::Key_3:
-        return Settings::NativeKeyboard::N3;
-    case Qt::Key_4:
-        return Settings::NativeKeyboard::N4;
-    case Qt::Key_5:
-        return Settings::NativeKeyboard::N5;
-    case Qt::Key_6:
-        return Settings::NativeKeyboard::N6;
-    case Qt::Key_7:
-        return Settings::NativeKeyboard::N7;
-    case Qt::Key_8:
-        return Settings::NativeKeyboard::N8;
-    case Qt::Key_9:
-        return Settings::NativeKeyboard::N9;
-    case Qt::Key_0:
-        return Settings::NativeKeyboard::N0;
-    case Qt::Key_Return:
-        return Settings::NativeKeyboard::Return;
-    case Qt::Key_Escape:
-        return Settings::NativeKeyboard::Escape;
-    case Qt::Key_Backspace:
-        return Settings::NativeKeyboard::Backspace;
-    case Qt::Key_Tab:
-        return Settings::NativeKeyboard::Tab;
-    case Qt::Key_Space:
-        return Settings::NativeKeyboard::Space;
-    case Qt::Key_Minus:
-        return Settings::NativeKeyboard::Minus;
-    case Qt::Key_Plus:
-    case Qt::Key_questiondown:
-        return Settings::NativeKeyboard::Plus;
-    case Qt::Key_BracketLeft:
-    case Qt::Key_BraceLeft:
-        return Settings::NativeKeyboard::OpenBracket;
-    case Qt::Key_BracketRight:
-    case Qt::Key_BraceRight:
-        return Settings::NativeKeyboard::CloseBracket;
-    case Qt::Key_Bar:
-        return Settings::NativeKeyboard::Pipe;
-    case Qt::Key_Dead_Tilde:
-        return Settings::NativeKeyboard::Tilde;
-    case Qt::Key_Ntilde:
-    case Qt::Key_Semicolon:
-        return Settings::NativeKeyboard::Semicolon;
-    case Qt::Key_Apostrophe:
-        return Settings::NativeKeyboard::Quote;
-    case Qt::Key_Dead_Grave:
-        return Settings::NativeKeyboard::Backquote;
-    case Qt::Key_Comma:
-        return Settings::NativeKeyboard::Comma;
-    case Qt::Key_Period:
-        return Settings::NativeKeyboard::Period;
-    case Qt::Key_Slash:
-        return Settings::NativeKeyboard::Slash;
-    case Qt::Key_CapsLock:
-        return Settings::NativeKeyboard::CapsLock;
-    case Qt::Key_F1:
-        return Settings::NativeKeyboard::F1;
-    case Qt::Key_F2:
-        return Settings::NativeKeyboard::F2;
-    case Qt::Key_F3:
-        return Settings::NativeKeyboard::F3;
-    case Qt::Key_F4:
-        return Settings::NativeKeyboard::F4;
-    case Qt::Key_F5:
-        return Settings::NativeKeyboard::F5;
-    case Qt::Key_F6:
-        return Settings::NativeKeyboard::F6;
-    case Qt::Key_F7:
-        return Settings::NativeKeyboard::F7;
-    case Qt::Key_F8:
-        return Settings::NativeKeyboard::F8;
-    case Qt::Key_F9:
-        return Settings::NativeKeyboard::F9;
-    case Qt::Key_F10:
-        return Settings::NativeKeyboard::F10;
-    case Qt::Key_F11:
-        return Settings::NativeKeyboard::F11;
-    case Qt::Key_F12:
-        return Settings::NativeKeyboard::F12;
-    case Qt::Key_Print:
-        return Settings::NativeKeyboard::PrintScreen;
-    case Qt::Key_ScrollLock:
-        return Settings::NativeKeyboard::ScrollLock;
-    case Qt::Key_Pause:
-        return Settings::NativeKeyboard::Pause;
-    case Qt::Key_Insert:
-        return Settings::NativeKeyboard::Insert;
-    case Qt::Key_Home:
-        return Settings::NativeKeyboard::Home;
-    case Qt::Key_PageUp:
-        return Settings::NativeKeyboard::PageUp;
-    case Qt::Key_Delete:
-        return Settings::NativeKeyboard::Delete;
-    case Qt::Key_End:
-        return Settings::NativeKeyboard::End;
-    case Qt::Key_PageDown:
-        return Settings::NativeKeyboard::PageDown;
-    case Qt::Key_Right:
-        return Settings::NativeKeyboard::Right;
-    case Qt::Key_Left:
-        return Settings::NativeKeyboard::Left;
-    case Qt::Key_Down:
-        return Settings::NativeKeyboard::Down;
-    case Qt::Key_Up:
-        return Settings::NativeKeyboard::Up;
-    case Qt::Key_NumLock:
-        return Settings::NativeKeyboard::NumLock;
-    // Numpad keys are missing here
-    case Qt::Key_F13:
-        return Settings::NativeKeyboard::F13;
-    case Qt::Key_F14:
-        return Settings::NativeKeyboard::F14;
-    case Qt::Key_F15:
-        return Settings::NativeKeyboard::F15;
-    case Qt::Key_F16:
-        return Settings::NativeKeyboard::F16;
-    case Qt::Key_F17:
-        return Settings::NativeKeyboard::F17;
-    case Qt::Key_F18:
-        return Settings::NativeKeyboard::F18;
-    case Qt::Key_F19:
-        return Settings::NativeKeyboard::F19;
-    case Qt::Key_F20:
-        return Settings::NativeKeyboard::F20;
-    case Qt::Key_F21:
-        return Settings::NativeKeyboard::F21;
-    case Qt::Key_F22:
-        return Settings::NativeKeyboard::F22;
-    case Qt::Key_F23:
-        return Settings::NativeKeyboard::F23;
-    case Qt::Key_F24:
-        return Settings::NativeKeyboard::F24;
-    // case Qt:::
-    //    return Settings::NativeKeyboard::KPComma;
-    // case Qt:::
-    //    return Settings::NativeKeyboard::Ro;
-    case Qt::Key_Hiragana_Katakana:
-        return Settings::NativeKeyboard::KatakanaHiragana;
-    case Qt::Key_yen:
-        return Settings::NativeKeyboard::Yen;
-    case Qt::Key_Henkan:
-        return Settings::NativeKeyboard::Henkan;
-    case Qt::Key_Muhenkan:
-        return Settings::NativeKeyboard::Muhenkan;
-    // case Qt:::
-    //    return Settings::NativeKeyboard::NumPadCommaPc98;
-    case Qt::Key_Hangul:
-        return Settings::NativeKeyboard::HangulEnglish;
-    case Qt::Key_Hangul_Hanja:
-        return Settings::NativeKeyboard::Hanja;
-    case Qt::Key_Katakana:
-        return Settings::NativeKeyboard::KatakanaKey;
-    case Qt::Key_Hiragana:
-        return Settings::NativeKeyboard::HiraganaKey;
-    case Qt::Key_Zenkaku_Hankaku:
-        return Settings::NativeKeyboard::ZenkakuHankaku;
-    // Modifier keys are handled by the modifier property
-    default:
-        return 0;
-    }
-}
-
-int GRenderWindow::QtModifierToSwitchModdifier(quint32 qt_moddifiers) {
-    int moddifier = 0;
-    // The values are obtained through testing, Qt doesn't seem to provide a proper enum
-    if ((qt_moddifiers & 0x1) != 0) {
-        moddifier |= 1 << Settings::NativeKeyboard::LeftShift;
-    }
-    if ((qt_moddifiers & 0x2) != 0) {
-        moddifier |= 1 << Settings::NativeKeyboard::LeftControl;
-    }
-    if ((qt_moddifiers & 0x4) != 0) {
-        moddifier |= 1 << Settings::NativeKeyboard::LeftAlt;
-    }
-    if ((qt_moddifiers & 0x08) != 0) {
-        moddifier |= 1 << Settings::NativeKeyboard::LeftMeta;
-    }
-    if ((qt_moddifiers & 0x10) != 0) {
-        moddifier |= 1 << Settings::NativeKeyboard::RightShift;
-    }
-    if ((qt_moddifiers & 0x20) != 0) {
-        moddifier |= 1 << Settings::NativeKeyboard::RightControl;
-    }
-    if ((qt_moddifiers & 0x40) != 0) {
-        moddifier |= 1 << Settings::NativeKeyboard::RightAlt;
-    }
-    if ((qt_moddifiers & 0x80) != 0) {
-        moddifier |= 1 << Settings::NativeKeyboard::RightMeta;
-    }
-    if ((qt_moddifiers & 0x100) != 0) {
-        moddifier |= 1 << Settings::NativeKeyboard::CapsLock;
-    }
-    if ((qt_moddifiers & 0x200) != 0) {
-        moddifier |= 1 << Settings::NativeKeyboard::NumLock;
-    }
-    // Verify the last two keys
-    if ((qt_moddifiers & 0x400) != 0) {
-        moddifier |= 1 << Settings::NativeKeyboard::Katakana;
-    }
-    if ((qt_moddifiers & 0x800) != 0) {
-        moddifier |= 1 << Settings::NativeKeyboard::Hiragana;
-    }
-    return moddifier;
-}
-
 void GRenderWindow::keyPressEvent(QKeyEvent* event) {
     if (!event->isAutoRepeat()) {
-        const auto moddifier = QtModifierToSwitchModdifier(event->nativeModifiers());
-        // Replace event->key() with event->nativeVirtualKey() since the second one provides raw key
-        // buttons
-        const auto key = QtKeyToSwitchKey(Qt::Key(event->key()));
-        input_subsystem->GetKeyboard()->SetModifiers(moddifier);
-        input_subsystem->GetKeyboard()->PressKey(key);
+        input_subsystem->GetKeyboard()->PressKey(event->key());
     }
 }
 
 void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
     if (!event->isAutoRepeat()) {
-        const auto moddifier = QtModifierToSwitchModdifier(event->nativeModifiers());
-        const auto key = QtKeyToSwitchKey(Qt::Key(event->key()));
-        input_subsystem->GetKeyboard()->SetModifiers(moddifier);
-        input_subsystem->GetKeyboard()->ReleaseKey(key);
+        input_subsystem->GetKeyboard()->ReleaseKey(event->key());
     }
 }
 
-InputCommon::MouseButton GRenderWindow::QtButtonToMouseButton(Qt::MouseButton button) {
+MouseInput::MouseButton GRenderWindow::QtButtonToMouseButton(Qt::MouseButton button) {
     switch (button) {
     case Qt::LeftButton:
-        return InputCommon::MouseButton::Left;
+        return MouseInput::MouseButton::Left;
     case Qt::RightButton:
-        return InputCommon::MouseButton::Right;
+        return MouseInput::MouseButton::Right;
     case Qt::MiddleButton:
-        return InputCommon::MouseButton::Wheel;
+        return MouseInput::MouseButton::Wheel;
     case Qt::BackButton:
-        return InputCommon::MouseButton::Backward;
+        return MouseInput::MouseButton::Backward;
     case Qt::ForwardButton:
-        return InputCommon::MouseButton::Forward;
+        return MouseInput::MouseButton::Forward;
     case Qt::TaskButton:
-        return InputCommon::MouseButton::Task;
+        return MouseInput::MouseButton::Task;
     default:
-        return InputCommon::MouseButton::Extra;
+        return MouseInput::MouseButton::Extra;
     }
 }
 
@@ -695,9 +423,12 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
     // coordinates and map them to the current render area
     const auto pos = mapFromGlobal(QCursor::pos());
     const auto [x, y] = ScaleTouch(pos);
-    const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
     const auto button = QtButtonToMouseButton(event->button());
-    input_subsystem->GetMouse()->PressButton(x, y, touch_x, touch_y, button);
+    input_subsystem->GetMouse()->PressButton(x, y, button);
+
+    if (event->button() == Qt::LeftButton) {
+        this->TouchPressed(x, y, 0);
+    }
 
     emit MouseActivity();
 }
@@ -711,10 +442,10 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
     // coordinates and map them to the current render area
     const auto pos = mapFromGlobal(QCursor::pos());
     const auto [x, y] = ScaleTouch(pos);
-    const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
     const int center_x = width() / 2;
     const int center_y = height() / 2;
-    input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y);
+    input_subsystem->GetMouse()->MouseMove(x, y, center_x, center_y);
+    this->TouchMoved(x, y, 0);
 
     if (Settings::values.mouse_panning) {
         QCursor::setPos(mapToGlobal({center_x, center_y}));
@@ -731,6 +462,10 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
 
     const auto button = QtButtonToMouseButton(event->button());
     input_subsystem->GetMouse()->ReleaseButton(button);
+
+    if (event->button() == Qt::LeftButton) {
+        this->TouchReleased(0);
+    }
 }
 
 void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
@@ -753,7 +488,7 @@ void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
     for (std::size_t id = 0; id < touch_ids.size(); ++id) {
         if (!TouchExist(touch_ids[id], touch_points)) {
             touch_ids[id] = 0;
-            input_subsystem->GetTouchScreen()->TouchReleased(id);
+            this->TouchReleased(id + 1);
         }
     }
 }
@@ -762,28 +497,28 @@ void GRenderWindow::TouchEndEvent() {
     for (std::size_t id = 0; id < touch_ids.size(); ++id) {
         if (touch_ids[id] != 0) {
             touch_ids[id] = 0;
-            input_subsystem->GetTouchScreen()->TouchReleased(id);
+            this->TouchReleased(id + 1);
         }
     }
 }
 
-void GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) {
+bool GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) {
     for (std::size_t id = 0; id < touch_ids.size(); ++id) {
         if (touch_ids[id] == 0) {
             touch_ids[id] = touch_point.id() + 1;
             const auto [x, y] = ScaleTouch(touch_point.pos());
-            const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
-            input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, id);
+            this->TouchPressed(x, y, id + 1);
+            return true;
         }
     }
+    return false;
 }
 
 bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) {
     for (std::size_t id = 0; id < touch_ids.size(); ++id) {
         if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) {
             const auto [x, y] = ScaleTouch(touch_point.pos());
-            const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
-            input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, id);
+            this->TouchMoved(x, y, id + 1);
             return true;
         }
     }
@@ -816,7 +551,7 @@ void GRenderWindow::focusOutEvent(QFocusEvent* event) {
     QWidget::focusOutEvent(event);
     input_subsystem->GetKeyboard()->ReleaseAllKeys();
     input_subsystem->GetMouse()->ReleaseAllButtons();
-    input_subsystem->GetTouchScreen()->ReleaseAllTouch();
+    this->TouchReleased(0);
 }
 
 void GRenderWindow::resizeEvent(QResizeEvent* event) {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 8f8091b71..40fd4a9d6 100755
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -30,8 +30,11 @@ class System;
 
 namespace InputCommon {
 class InputSubsystem;
+}
+
+namespace MouseInput {
 enum class MouseButton;
-} // namespace InputCommon
+}
 
 namespace VideoCore {
 enum class LoadCallbackStage;
@@ -154,17 +157,11 @@ public:
 
     void resizeEvent(QResizeEvent* event) override;
 
-    /// Converts a Qt keybard key into NativeKeyboard key
-    static int QtKeyToSwitchKey(Qt::Key qt_keys);
-
-    /// Converts a Qt modifier keys into NativeKeyboard modifier keys
-    static int QtModifierToSwitchModdifier(quint32 qt_moddifiers);
-
     void keyPressEvent(QKeyEvent* event) override;
     void keyReleaseEvent(QKeyEvent* event) override;
 
     /// Converts a Qt mouse button into MouseInput mouse button
-    static InputCommon::MouseButton QtButtonToMouseButton(Qt::MouseButton button);
+    static MouseInput::MouseButton QtButtonToMouseButton(Qt::MouseButton button);
 
     void mousePressEvent(QMouseEvent* event) override;
     void mouseMoveEvent(QMouseEvent* event) override;
@@ -212,7 +209,7 @@ private:
     void TouchUpdateEvent(const QTouchEvent* event);
     void TouchEndEvent();
 
-    void TouchStart(const QTouchEvent::TouchPoint& touch_point);
+    bool TouchStart(const QTouchEvent::TouchPoint& touch_point);
     bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point);
     bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const;
 
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 0a63ef51e..8227d06bc 100755
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -11,6 +11,7 @@
 #include "core/hle/service/acc/profile_manager.h"
 #include "core/hle/service/hid/controllers/npad.h"
 #include "input_common/main.h"
+#include "input_common/udp/client.h"
 #include "yuzu/configuration/config.h"
 
 namespace FS = Common::FS;
@@ -65,6 +66,157 @@ const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::defa
         Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal,
 };
 
+const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> Config::default_keyboard_keys = {
+    0,
+    0,
+    0,
+    0,
+    Qt::Key_A,
+    Qt::Key_B,
+    Qt::Key_C,
+    Qt::Key_D,
+    Qt::Key_E,
+    Qt::Key_F,
+    Qt::Key_G,
+    Qt::Key_H,
+    Qt::Key_I,
+    Qt::Key_J,
+    Qt::Key_K,
+    Qt::Key_L,
+    Qt::Key_M,
+    Qt::Key_N,
+    Qt::Key_O,
+    Qt::Key_P,
+    Qt::Key_Q,
+    Qt::Key_R,
+    Qt::Key_S,
+    Qt::Key_T,
+    Qt::Key_U,
+    Qt::Key_V,
+    Qt::Key_W,
+    Qt::Key_X,
+    Qt::Key_Y,
+    Qt::Key_Z,
+    Qt::Key_1,
+    Qt::Key_2,
+    Qt::Key_3,
+    Qt::Key_4,
+    Qt::Key_5,
+    Qt::Key_6,
+    Qt::Key_7,
+    Qt::Key_8,
+    Qt::Key_9,
+    Qt::Key_0,
+    Qt::Key_Enter,
+    Qt::Key_Escape,
+    Qt::Key_Backspace,
+    Qt::Key_Tab,
+    Qt::Key_Space,
+    Qt::Key_Minus,
+    Qt::Key_Equal,
+    Qt::Key_BracketLeft,
+    Qt::Key_BracketRight,
+    Qt::Key_Backslash,
+    Qt::Key_Dead_Tilde,
+    Qt::Key_Semicolon,
+    Qt::Key_Apostrophe,
+    Qt::Key_Dead_Grave,
+    Qt::Key_Comma,
+    Qt::Key_Period,
+    Qt::Key_Slash,
+    Qt::Key_CapsLock,
+
+    Qt::Key_F1,
+    Qt::Key_F2,
+    Qt::Key_F3,
+    Qt::Key_F4,
+    Qt::Key_F5,
+    Qt::Key_F6,
+    Qt::Key_F7,
+    Qt::Key_F8,
+    Qt::Key_F9,
+    Qt::Key_F10,
+    Qt::Key_F11,
+    Qt::Key_F12,
+
+    Qt::Key_SysReq,
+    Qt::Key_ScrollLock,
+    Qt::Key_Pause,
+    Qt::Key_Insert,
+    Qt::Key_Home,
+    Qt::Key_PageUp,
+    Qt::Key_Delete,
+    Qt::Key_End,
+    Qt::Key_PageDown,
+    Qt::Key_Right,
+    Qt::Key_Left,
+    Qt::Key_Down,
+    Qt::Key_Up,
+
+    Qt::Key_NumLock,
+    Qt::Key_Slash,
+    Qt::Key_Asterisk,
+    Qt::Key_Minus,
+    Qt::Key_Plus,
+    Qt::Key_Enter,
+    Qt::Key_1,
+    Qt::Key_2,
+    Qt::Key_3,
+    Qt::Key_4,
+    Qt::Key_5,
+    Qt::Key_6,
+    Qt::Key_7,
+    Qt::Key_8,
+    Qt::Key_9,
+    Qt::Key_0,
+    Qt::Key_Period,
+
+    0,
+    0,
+    Qt::Key_PowerOff,
+    Qt::Key_Equal,
+
+    Qt::Key_F13,
+    Qt::Key_F14,
+    Qt::Key_F15,
+    Qt::Key_F16,
+    Qt::Key_F17,
+    Qt::Key_F18,
+    Qt::Key_F19,
+    Qt::Key_F20,
+    Qt::Key_F21,
+    Qt::Key_F22,
+    Qt::Key_F23,
+    Qt::Key_F24,
+
+    Qt::Key_Open,
+    Qt::Key_Help,
+    Qt::Key_Menu,
+    0,
+    Qt::Key_Stop,
+    Qt::Key_AudioRepeat,
+    Qt::Key_Undo,
+    Qt::Key_Cut,
+    Qt::Key_Copy,
+    Qt::Key_Paste,
+    Qt::Key_Find,
+    Qt::Key_VolumeMute,
+    Qt::Key_VolumeUp,
+    Qt::Key_VolumeDown,
+    Qt::Key_CapsLock,
+    Qt::Key_NumLock,
+    Qt::Key_ScrollLock,
+    Qt::Key_Comma,
+
+    Qt::Key_ParenLeft,
+    Qt::Key_ParenRight,
+};
+
+const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default_keyboard_mods = {
+    Qt::Key_Control, Qt::Key_Shift, Qt::Key_Alt,   Qt::Key_ApplicationLeft,
+    Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight,
+};
+
 // This shouldn't have anything except static initializers (no functions). So
 // QKeySequence(...).toString() is NOT ALLOWED HERE.
 // This must be in alphabetical order according to action name as it must have the same order as
@@ -345,14 +497,14 @@ void Config::ReadDebugValues() {
 void Config::ReadKeyboardValues() {
     ReadBasicSetting(Settings::values.keyboard_enabled);
 
-    for (std::size_t i = 0; i < Settings::values.keyboard_keys.size(); ++i) {
-        Settings::values.keyboard_keys[i] = InputCommon::GenerateKeyboardParam(static_cast<int>(i));
-    }
-
-    for (std::size_t i = 0; i < Settings::values.keyboard_mods.size(); ++i) {
-        Settings::values.keyboard_mods[i] =
-            InputCommon::GenerateModdifierKeyboardParam(static_cast<int>(i));
-    }
+    std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(),
+                   Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
+    std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
+                   Settings::values.keyboard_keys.begin() +
+                       Settings::NativeKeyboard::LeftControlKey,
+                   InputCommon::GenerateKeyboardParam);
+    std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
+                   Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
 }
 
 void Config::ReadMouseValues() {
@@ -422,6 +574,7 @@ void Config::ReadControlValues() {
 
     ReadBasicSetting(Settings::values.tas_enable);
     ReadBasicSetting(Settings::values.tas_loop);
+    ReadBasicSetting(Settings::values.tas_swap_controllers);
     ReadBasicSetting(Settings::values.pause_tas_on_load);
 
     ReadGlobalSetting(Settings::values.use_docked_mode);
@@ -472,7 +625,9 @@ void Config::ReadMotionTouchValues() {
     }
     qt_config->endArray();
 
+    ReadBasicSetting(Settings::values.motion_device);
     ReadBasicSetting(Settings::values.touch_device);
+    ReadBasicSetting(Settings::values.use_touch_from_button);
     ReadBasicSetting(Settings::values.touch_from_button_map_index);
     Settings::values.touch_from_button_map_index = std::clamp(
         Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
@@ -978,7 +1133,9 @@ void Config::SaveTouchscreenValues() {
 }
 
 void Config::SaveMotionTouchValues() {
+    WriteBasicSetting(Settings::values.motion_device);
     WriteBasicSetting(Settings::values.touch_device);
+    WriteBasicSetting(Settings::values.use_touch_from_button);
     WriteBasicSetting(Settings::values.touch_from_button_map_index);
     WriteBasicSetting(Settings::values.udp_input_servers);
 
@@ -1053,6 +1210,7 @@ void Config::SaveControlValues() {
 
     WriteBasicSetting(Settings::values.tas_enable);
     WriteBasicSetting(Settings::values.tas_loop);
+    WriteBasicSetting(Settings::values.tas_swap_controllers);
     WriteBasicSetting(Settings::values.pause_tas_on_load);
 
     qt_config->endGroup();
diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp
index 9a8de92a1..31ec48384 100755
--- a/src/yuzu/configuration/configure_debug_controller.cpp
+++ b/src/yuzu/configuration/configure_debug_controller.cpp
@@ -2,18 +2,17 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
-#include "core/hid/hid_core.h"
+#include "core/core.h"
 #include "ui_configure_debug_controller.h"
 #include "yuzu/configuration/configure_debug_controller.h"
 #include "yuzu/configuration/configure_input_player.h"
 
 ConfigureDebugController::ConfigureDebugController(QWidget* parent,
                                                    InputCommon::InputSubsystem* input_subsystem,
-                                                   InputProfiles* profiles,
-                                                   Core::HID::HIDCore& hid_core, bool is_powered_on)
+                                                   InputProfiles* profiles, Core::System& system)
     : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()),
-      debug_controller(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles,
-                                                hid_core, is_powered_on, true)) {
+      debug_controller(
+          new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, system, true)) {
     ui->setupUi(this);
 
     ui->controllerLayout->addWidget(debug_controller);
diff --git a/src/yuzu/configuration/configure_debug_controller.h b/src/yuzu/configuration/configure_debug_controller.h
index d716edbc2..6e17c5aa0 100755
--- a/src/yuzu/configuration/configure_debug_controller.h
+++ b/src/yuzu/configuration/configure_debug_controller.h
@@ -13,8 +13,8 @@ class ConfigureInputPlayer;
 
 class InputProfiles;
 
-namespace Core::HID {
-class HIDCore;
+namespace Core {
+class System;
 }
 
 namespace InputCommon {
@@ -30,8 +30,7 @@ class ConfigureDebugController : public QDialog {
 
 public:
     explicit ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem,
-                                      InputProfiles* profiles, Core::HID::HIDCore& hid_core,
-                                      bool is_powered_on);
+                                      InputProfiles* profiles, Core::System& system);
     ~ConfigureDebugController() override;
 
     void ApplyConfiguration();
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 99450bc7d..1599299db 100755
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -10,8 +10,6 @@
 #include <QTimer>
 
 #include "core/core.h"
-#include "core/hid/emulated_controller.h"
-#include "core/hid/hid_core.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/am/applet_ae.h"
 #include "core/hle/service/am/applet_oe.h"
@@ -77,25 +75,23 @@ ConfigureInput::~ConfigureInput() = default;
 
 void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
                                 std::size_t max_players) {
-    const bool is_powered_on = system.IsPoweredOn();
-    auto& hid_core = system.HIDCore();
     player_controllers = {
         new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem, profiles.get(),
-                                 hid_core, is_powered_on),
+                                 system),
         new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem, profiles.get(),
-                                 hid_core, is_powered_on),
+                                 system),
         new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem, profiles.get(),
-                                 hid_core, is_powered_on),
+                                 system),
         new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem, profiles.get(),
-                                 hid_core, is_powered_on),
+                                 system),
         new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem, profiles.get(),
-                                 hid_core, is_powered_on),
+                                 system),
         new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem, profiles.get(),
-                                 hid_core, is_powered_on),
+                                 system),
         new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem, profiles.get(),
-                                 hid_core, is_powered_on),
+                                 system),
         new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem, profiles.get(),
-                                 hid_core, is_powered_on),
+                                 system),
     };
 
     player_tabs = {
@@ -118,7 +114,6 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
         player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
         player_tabs[i]->layout()->addWidget(player_controllers[i]);
         connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) {
-            // Ensures that the controllers are always connected in sequential order
             if (is_connected) {
                 for (std::size_t index = 0; index <= i; ++index) {
                     player_connected[index]->setChecked(is_connected);
@@ -151,12 +146,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
     advanced = new ConfigureInputAdvanced(this);
     ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
     ui->tabAdvanced->layout()->addWidget(advanced);
-
-    connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog,
-            [this, input_subsystem, &hid_core, is_powered_on] {
-                CallConfigureDialog<ConfigureDebugController>(
-                    *this, input_subsystem, profiles.get(), hid_core, is_powered_on);
-            });
+    connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] {
+        CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem, profiles.get(),
+                                                      system);
+    });
     connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] {
         CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem);
     });
@@ -191,8 +184,22 @@ QList<QWidget*> ConfigureInput::GetSubTabs() const {
 void ConfigureInput::ApplyConfiguration() {
     for (auto* controller : player_controllers) {
         controller->ApplyConfiguration();
+        controller->TryDisconnectSelectedController();
     }
 
+    // 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(150ms);
+
+    for (auto* controller : player_controllers) {
+        controller->TryConnectSelectedController();
+    }
+
+    // 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.
+    std::this_thread::sleep_for(150ms);
+
     advanced->ApplyConfiguration();
 
     const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
@@ -216,10 +223,8 @@ void ConfigureInput::RetranslateUI() {
 }
 
 void ConfigureInput::LoadConfiguration() {
-    const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
-
     LoadPlayerControllerIndices();
-    UpdateDockedState(handheld->IsConnected());
+    UpdateDockedState(Settings::values.players.GetValue()[8].connected);
 
     ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
     ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
@@ -227,16 +232,9 @@ void ConfigureInput::LoadConfiguration() {
 
 void ConfigureInput::LoadPlayerControllerIndices() {
     for (std::size_t i = 0; i < player_connected.size(); ++i) {
-        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());
+        const auto connected = Settings::values.players.GetValue()[i].connected ||
+                               (i == 0 && Settings::values.players.GetValue()[8].connected);
+        player_connected[i]->setChecked(connected);
     }
 }
 
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 0254ea6fe..3aab5d5f8 100755
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -12,12 +12,14 @@
 #include <QMessageBox>
 #include <QTimer>
 #include "common/param_package.h"
-#include "core/hid/emulated_controller.h"
-#include "core/hid/hid_core.h"
-#include "core/hid/hid_types.h"
-#include "input_common/drivers/keyboard.h"
-#include "input_common/drivers/mouse.h"
+#include "core/core.h"
+#include "core/hle/service/hid/controllers/npad.h"
+#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/sm/sm.h"
+#include "input_common/gcadapter/gc_poller.h"
 #include "input_common/main.h"
+#include "input_common/mouse/mouse_poller.h"
+#include "input_common/udp/udp.h"
 #include "ui_configure_input_player.h"
 #include "yuzu/bootmanager.h"
 #include "yuzu/configuration/config.h"
@@ -27,6 +29,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",
@@ -37,8 +41,33 @@ const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
 
 namespace {
 
+constexpr std::size_t HANDHELD_INDEX = 8;
+
+void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
+                      bool connected, Core::System& system) {
+    if (!system.IsPoweredOn()) {
+        return;
+    }
+    Service::SM::ServiceManager& sm = system.ServiceManager();
+
+    auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>(
+        HidController::NPad);
+
+    npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
+}
+
 QString GetKeyName(int key_code) {
     switch (key_code) {
+    case Qt::LeftButton:
+        return QObject::tr("Click 0");
+    case Qt::RightButton:
+        return QObject::tr("Click 1");
+    case Qt::MiddleButton:
+        return QObject::tr("Click 2");
+    case Qt::BackButton:
+        return QObject::tr("Click 3");
+    case Qt::ForwardButton:
+        return QObject::tr("Click 4");
     case Qt::Key_Shift:
         return QObject::tr("Shift");
     case Qt::Key_Control:
@@ -68,26 +97,95 @@ void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackag
     }
     analog_param.Set(button_name, input_param.Serialize());
 }
-} // namespace
 
-QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
+QString ButtonToText(const Common::ParamPackage& param) {
     if (!param.Has("engine")) {
         return QObject::tr("[not set]");
     }
 
-    // Retrieve the names from Qt
     if (param.Get("engine", "") == "keyboard") {
         const QString button_str = GetKeyName(param.Get("code", 0));
         const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
         return QObject::tr("%1%2").arg(toggle, button_str);
     }
 
-    std::string button_name = input_subsystem->GetButtonName(param);
-    return QString::fromStdString(button_name);
+    if (param.Get("engine", "") == "gcpad") {
+        if (param.Has("axis")) {
+            const QString axis_str = QString::fromStdString(param.Get("axis", ""));
+            const QString direction_str = QString::fromStdString(param.Get("direction", ""));
+
+            return QObject::tr("GC Axis %1%2").arg(axis_str, direction_str);
+        }
+        if (param.Has("button")) {
+            const QString button_str = QString::number(int(std::log2(param.Get("button", 0))));
+            return QObject::tr("GC Button %1").arg(button_str);
+        }
+        return GetKeyName(param.Get("code", 0));
+    }
+
+    if (param.Get("engine", "") == "tas") {
+        if (param.Has("axis")) {
+            const QString axis_str = QString::fromStdString(param.Get("axis", ""));
+
+            return QObject::tr("TAS Axis %1").arg(axis_str);
+        }
+        if (param.Has("button")) {
+            const QString button_str = QString::number(int(std::log2(param.Get("button", 0))));
+            return QObject::tr("TAS Btn %1").arg(button_str);
+        }
+        return GetKeyName(param.Get("code", 0));
+    }
+
+    if (param.Get("engine", "") == "cemuhookudp") {
+        if (param.Has("pad_index")) {
+            const QString motion_str = QString::fromStdString(param.Get("pad_index", ""));
+            return QObject::tr("Motion %1").arg(motion_str);
+        }
+        return GetKeyName(param.Get("code", 0));
+    }
+
+    if (param.Get("engine", "") == "sdl") {
+        if (param.Has("hat")) {
+            const QString hat_str = QString::fromStdString(param.Get("hat", ""));
+            const QString direction_str = QString::fromStdString(param.Get("direction", ""));
+
+            return QObject::tr("Hat %1 %2").arg(hat_str, direction_str);
+        }
+
+        if (param.Has("axis")) {
+            const QString axis_str = QString::fromStdString(param.Get("axis", ""));
+            const QString direction_str = QString::fromStdString(param.Get("direction", ""));
+
+            return QObject::tr("Axis %1%2").arg(axis_str, direction_str);
+        }
+
+        if (param.Has("button")) {
+            const QString button_str = QString::fromStdString(param.Get("button", ""));
+            const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
+
+            return QObject::tr("%1Button %2").arg(toggle, button_str);
+        }
+
+        if (param.Has("motion")) {
+            return QObject::tr("SDL Motion");
+        }
+
+        return {};
+    }
+
+    if (param.Get("engine", "") == "mouse") {
+        if (param.Has("button")) {
+            const QString button_str = QString::number(int(param.Get("button", 0)));
+            const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
+            return QObject::tr("%1Click %2").arg(toggle, button_str);
+        }
+        return GetKeyName(param.Get("code", 0));
+    }
+
+    return QObject::tr("[unknown]");
 }
 
-QString ConfigureInputPlayer::AnalogToText(const Common::ParamPackage& param,
-                                           const std::string& dir) {
+QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
     if (!param.Has("engine")) {
         return QObject::tr("[not set]");
     }
@@ -96,69 +194,49 @@ QString ConfigureInputPlayer::AnalogToText(const Common::ParamPackage& param,
         return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
     }
 
-    if (!param.Has("axis_x") || !param.Has("axis_y")) {
-        return QObject::tr("[unknown]");
-    }
-
     const auto engine_str = param.Get("engine", "");
     const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
     const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
     const bool invert_x = param.Get("invert_x", "+") == "-";
     const bool invert_y = param.Get("invert_y", "+") == "-";
+    if (engine_str == "sdl" || engine_str == "gcpad" || engine_str == "mouse" ||
+        engine_str == "tas") {
+        if (dir == "modifier") {
+            return QObject::tr("[unused]");
+        }
 
-    if (dir == "modifier") {
-        return QObject::tr("[unused]");
-    }
+        if (dir == "left") {
+            const QString invert_x_str = QString::fromStdString(invert_x ? "+" : "-");
+            return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
+        }
+        if (dir == "right") {
+            const QString invert_x_str = QString::fromStdString(invert_x ? "-" : "+");
+            return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
+        }
+        if (dir == "up") {
+            const QString invert_y_str = QString::fromStdString(invert_y ? "-" : "+");
+            return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
+        }
+        if (dir == "down") {
+            const QString invert_y_str = QString::fromStdString(invert_y ? "+" : "-");
+            return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
+        }
 
-    if (dir == "left") {
-        const QString invert_x_str = QString::fromStdString(invert_x ? "+" : "-");
-        return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
+        return {};
     }
-    if (dir == "right") {
-        const QString invert_x_str = QString::fromStdString(invert_x ? "-" : "+");
-        return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
-    }
-    if (dir == "up") {
-        const QString invert_y_str = QString::fromStdString(invert_y ? "-" : "+");
-        return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
-    }
-    if (dir == "down") {
-        const QString invert_y_str = QString::fromStdString(invert_y ? "+" : "-");
-        return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
-    }
-
     return QObject::tr("[unknown]");
 }
+} // namespace
 
 ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index,
                                            QWidget* bottom_row,
                                            InputCommon::InputSubsystem* input_subsystem_,
-                                           InputProfiles* profiles_, Core::HID::HIDCore& hid_core_,
-                                           bool is_powered_on_, bool debug)
+                                           InputProfiles* profiles_, Core::System& system_,
+                                           bool debug)
     : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
-      debug(debug), is_powered_on{is_powered_on_}, input_subsystem{input_subsystem_},
-      profiles(profiles_), timeout_timer(std::make_unique<QTimer>()),
-      poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row), hid_core{hid_core_} {
-    if (player_index == 0) {
-        auto* emulated_controller_p1 =
-            hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
-        auto* emulated_controller_hanheld =
-            hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
-        emulated_controller_p1->SaveCurrentConfig();
-        emulated_controller_p1->EnableConfiguration();
-        emulated_controller_hanheld->SaveCurrentConfig();
-        emulated_controller_hanheld->EnableConfiguration();
-        if (emulated_controller_hanheld->IsConnected(true)) {
-            emulated_controller_p1->Disconnect();
-            emulated_controller = emulated_controller_hanheld;
-        } else {
-            emulated_controller = emulated_controller_p1;
-        }
-    } else {
-        emulated_controller = hid_core.GetEmulatedControllerByIndex(player_index);
-        emulated_controller->SaveCurrentConfig();
-        emulated_controller->EnableConfiguration();
-    }
+      debug(debug), input_subsystem{input_subsystem_}, profiles(profiles_),
+      timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()),
+      bottom_row(bottom_row), system{system_} {
     ui->setupUi(this);
 
     setFocusPolicy(Qt::ClickFocus);
@@ -200,7 +278,31 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
     analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup};
     analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange};
 
-    ui->controllerFrame->SetController(emulated_controller);
+    const auto ConfigureButtonClick = [&](QPushButton* button, std::size_t button_id,
+                                          Common::ParamPackage* param, int default_val,
+                                          InputCommon::Polling::DeviceType type) {
+        connect(button, &QPushButton::clicked, [=, this] {
+            HandleClick(
+                button, button_id,
+                [=, this](Common::ParamPackage params) {
+                    // Workaround for ZL & ZR for analog triggers like on XBOX
+                    // controllers. Analog triggers (from controllers like the XBOX
+                    // controller) would not work due to a different range of their
+                    // signals (from 0 to 255 on analog triggers instead of -32768 to
+                    // 32768 on analog joysticks). The SDL driver misinterprets analog
+                    // triggers as analog joysticks.
+                    // TODO: reinterpret the signal range for analog triggers to map the
+                    // values correctly. This is required for the correct emulation of
+                    // the analog triggers of the GameCube controller.
+                    if (button == ui->buttonZL || button == ui->buttonZR) {
+                        params.Set("direction", "+");
+                        params.Set("threshold", "0.5");
+                    }
+                    *param = std::move(params);
+                },
+                type);
+        });
+    };
 
     for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
         auto* const button = button_map[button_id];
@@ -209,52 +311,34 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
             continue;
         }
 
-        connect(button, &QPushButton::clicked, [=, this] {
-            HandleClick(
-                button, button_id,
-                [=, this](Common::ParamPackage params) {
-                    emulated_controller->SetButtonParam(button_id, params);
-                },
-                InputCommon::Polling::InputType::Button);
-        });
+        ConfigureButtonClick(button_map[button_id], button_id, &buttons_param[button_id],
+                             Config::default_buttons[button_id],
+                             InputCommon::Polling::DeviceType::Button);
 
         button->setContextMenuPolicy(Qt::CustomContextMenu);
         connect(button, &QPushButton::customContextMenuRequested,
                 [=, this](const QPoint& menu_location) {
                     QMenu context_menu;
-                    Common::ParamPackage param = emulated_controller->GetButtonParam(button_id);
                     context_menu.addAction(tr("Clear"), [&] {
-                        emulated_controller->SetButtonParam(button_id, {});
+                        buttons_param[button_id].Clear();
                         button_map[button_id]->setText(tr("[not set]"));
                     });
-                    if (param.Has("button") || param.Has("hat")) {
+                    if (buttons_param[button_id].Has("toggle")) {
                         context_menu.addAction(tr("Toggle button"), [&] {
-                            const bool toggle_value = !param.Get("toggle", false);
-                            param.Set("toggle", toggle_value);
-                            button_map[button_id]->setText(ButtonToText(param));
-                            emulated_controller->SetButtonParam(button_id, param);
-                        });
-                        context_menu.addAction(tr("Invert button"), [&] {
-                            const bool toggle_value = !param.Get("inverted", false);
-                            param.Set("inverted", toggle_value);
-                            button_map[button_id]->setText(ButtonToText(param));
-                            emulated_controller->SetButtonParam(button_id, param);
+                            const bool toggle_value =
+                                !buttons_param[button_id].Get("toggle", false);
+                            buttons_param[button_id].Set("toggle", toggle_value);
+                            button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
                         });
                     }
-                    if (param.Has("axis")) {
-                        context_menu.addAction(tr("Invert axis"), [&] {
-                            const bool toggle_value = !(param.Get("invert", "+") == "-");
-                            param.Set("invert", toggle_value ? "-" : "+");
-                            button_map[button_id]->setText(ButtonToText(param));
-                            emulated_controller->SetButtonParam(button_id, param);
-                        });
+                    if (buttons_param[button_id].Has("threshold")) {
                         context_menu.addAction(tr("Set threshold"), [&] {
-                            const int button_threshold =
-                                static_cast<int>(param.Get("threshold", 0.5f) * 100.0f);
+                            const int button_threshold = static_cast<int>(
+                                buttons_param[button_id].Get("threshold", 0.5f) * 100.0f);
                             const int new_threshold = QInputDialog::getInt(
                                 this, tr("Set threshold"), tr("Choose a value between 0% and 100%"),
                                 button_threshold, 0, 100);
-                            param.Set("threshold", new_threshold / 100.0f);
+                            buttons_param[button_id].Set("threshold", new_threshold / 100.0f);
 
                             if (button_id == Settings::NativeButton::ZL) {
                                 ui->sliderZLThreshold->setValue(new_threshold);
@@ -262,10 +346,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
                             if (button_id == Settings::NativeButton::ZR) {
                                 ui->sliderZRThreshold->setValue(new_threshold);
                             }
-                            emulated_controller->SetButtonParam(button_id, param);
                         });
                     }
+
                     context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
+                    ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
                 });
     }
 
@@ -275,14 +360,9 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
             continue;
         }
 
-        connect(button, &QPushButton::clicked, [=, this] {
-            HandleClick(
-                button, motion_id,
-                [=, this](Common::ParamPackage params) {
-                    emulated_controller->SetMotionParam(motion_id, params);
-                },
-                InputCommon::Polling::InputType::Motion);
-        });
+        ConfigureButtonClick(motion_map[motion_id], motion_id, &motions_param[motion_id],
+                             Config::default_motions[motion_id],
+                             InputCommon::Polling::DeviceType::Motion);
 
         button->setContextMenuPolicy(Qt::CustomContextMenu);
 
@@ -290,7 +370,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
                 [=, this](const QPoint& menu_location) {
                     QMenu context_menu;
                     context_menu.addAction(tr("Clear"), [&] {
-                        emulated_controller->SetMotionParam(motion_id, {});
+                        motions_param[motion_id].Clear();
                         motion_map[motion_id]->setText(tr("[not set]"));
                     });
                     context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location));
@@ -298,22 +378,16 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
     }
 
     connect(ui->sliderZLThreshold, &QSlider::valueChanged, [=, this] {
-        Common::ParamPackage param =
-            emulated_controller->GetButtonParam(Settings::NativeButton::ZL);
-        if (param.Has("threshold")) {
+        if (buttons_param[Settings::NativeButton::ZL].Has("threshold")) {
             const auto slider_value = ui->sliderZLThreshold->value();
-            param.Set("threshold", slider_value / 100.0f);
-            emulated_controller->SetButtonParam(Settings::NativeButton::ZL, param);
+            buttons_param[Settings::NativeButton::ZL].Set("threshold", slider_value / 100.0f);
         }
     });
 
     connect(ui->sliderZRThreshold, &QSlider::valueChanged, [=, this] {
-        Common::ParamPackage param =
-            emulated_controller->GetButtonParam(Settings::NativeButton::ZR);
-        if (param.Has("threshold")) {
+        if (buttons_param[Settings::NativeButton::ZR].Has("threshold")) {
             const auto slider_value = ui->sliderZRThreshold->value();
-            param.Set("threshold", slider_value / 100.0f);
-            emulated_controller->SetButtonParam(Settings::NativeButton::ZR, param);
+            buttons_param[Settings::NativeButton::ZR].Set("threshold", slider_value / 100.0f);
         }
     });
 
@@ -341,45 +415,45 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
                 HandleClick(
                     analog_map_buttons[analog_id][sub_button_id], analog_id,
                     [=, this](const Common::ParamPackage& params) {
-                        Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
-                        SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]);
-                        emulated_controller->SetStickParam(analog_id, param);
+                        SetAnalogParam(params, analogs_param[analog_id],
+                                       analog_sub_buttons[sub_button_id]);
                     },
-                    InputCommon::Polling::InputType::Stick);
+                    InputCommon::Polling::DeviceType::AnalogPreferred);
             });
 
             analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
 
-            connect(analog_button, &QPushButton::customContextMenuRequested,
-                    [=, this](const QPoint& menu_location) {
-                        QMenu context_menu;
-                        Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
-                        context_menu.addAction(tr("Clear"), [&] {
-                            emulated_controller->SetStickParam(analog_id, {});
-                            analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
-                        });
-                        context_menu.addAction(tr("Invert axis"), [&] {
-                            if (sub_button_id == 2 || sub_button_id == 3) {
-                                const bool invert_value = param.Get("invert_x", "+") == "-";
-                                const std::string invert_str = invert_value ? "+" : "-";
-                                param.Set("invert_x", invert_str);
-                                emulated_controller->SetStickParam(analog_id, param);
-                            }
-                            if (sub_button_id == 0 || sub_button_id == 1) {
-                                const bool invert_value = param.Get("invert_y", "+") == "-";
-                                const std::string invert_str = invert_value ? "+" : "-";
-                                param.Set("invert_y", invert_str);
-                                emulated_controller->SetStickParam(analog_id, param);
-                            }
-                            for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM;
-                                 ++sub_button_id) {
-                                analog_map_buttons[analog_id][sub_button_id]->setText(
-                                    AnalogToText(param, analog_sub_buttons[sub_button_id]));
-                            }
-                        });
-                        context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
-                            menu_location));
+            connect(
+                analog_button, &QPushButton::customContextMenuRequested,
+                [=, this](const QPoint& menu_location) {
+                    QMenu context_menu;
+                    context_menu.addAction(tr("Clear"), [&] {
+                        analogs_param[analog_id].Clear();
+                        analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
                     });
+                    context_menu.addAction(tr("Invert axis"), [&] {
+                        if (sub_button_id == 2 || sub_button_id == 3) {
+                            const bool invert_value =
+                                analogs_param[analog_id].Get("invert_x", "+") == "-";
+                            const std::string invert_str = invert_value ? "+" : "-";
+                            analogs_param[analog_id].Set("invert_x", invert_str);
+                        }
+                        if (sub_button_id == 0 || sub_button_id == 1) {
+                            const bool invert_value =
+                                analogs_param[analog_id].Get("invert_y", "+") == "-";
+                            const std::string invert_str = invert_value ? "+" : "-";
+                            analogs_param[analog_id].Set("invert_y", invert_str);
+                        }
+                        for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM;
+                             ++sub_button_id) {
+                            analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
+                                analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
+                        }
+                    });
+                    context_menu.exec(
+                        analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(menu_location));
+                    ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
+                });
         }
 
         // Handle clicks for the modifier buttons as well.
@@ -387,11 +461,9 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
             HandleClick(
                 analog_map_modifier_button[analog_id], analog_id,
                 [=, this](const Common::ParamPackage& params) {
-                    Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
-                    param.Set("modifier", params.Serialize());
-                    emulated_controller->SetStickParam(analog_id, param);
+                    analogs_param[analog_id].Set("modifier", params.Serialize());
                 },
-                InputCommon::Polling::InputType::Button);
+                InputCommon::Polling::DeviceType::Button);
         });
 
         analog_map_modifier_button[analog_id]->setContextMenuPolicy(Qt::CustomContextMenu);
@@ -399,21 +471,18 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
         connect(analog_map_modifier_button[analog_id], &QPushButton::customContextMenuRequested,
                 [=, this](const QPoint& menu_location) {
                     QMenu context_menu;
-                    Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
                     context_menu.addAction(tr("Clear"), [&] {
-                        param.Set("modifier", "");
+                        analogs_param[analog_id].Set("modifier", "");
                         analog_map_modifier_button[analog_id]->setText(tr("[not set]"));
-                        emulated_controller->SetStickParam(analog_id, param);
                     });
                     context_menu.addAction(tr("Toggle button"), [&] {
                         Common::ParamPackage modifier_param =
-                            Common::ParamPackage{param.Get("modifier", "")};
+                            Common::ParamPackage{analogs_param[analog_id].Get("modifier", "")};
                         const bool toggle_value = !modifier_param.Get("toggle", false);
                         modifier_param.Set("toggle", toggle_value);
-                        param.Set("modifier", modifier_param.Serialize());
+                        analogs_param[analog_id].Set("modifier", modifier_param.Serialize());
                         analog_map_modifier_button[analog_id]->setText(
                             ButtonToText(modifier_param));
-                        emulated_controller->SetStickParam(analog_id, param);
                     });
                     context_menu.exec(
                         analog_map_modifier_button[analog_id]->mapToGlobal(menu_location));
@@ -421,39 +490,37 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
 
         connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged),
                 [=, this] {
-                    Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
                     const auto spinbox_value = analog_map_range_spinbox[analog_id]->value();
-                    param.Set("range", spinbox_value / 100.0f);
-                    emulated_controller->SetStickParam(analog_id, param);
+                    analogs_param[analog_id].Set("range", spinbox_value / 100.0f);
+                    ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
                 });
 
         connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] {
-            Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
             const auto slider_value = analog_map_deadzone_slider[analog_id]->value();
             analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value));
-            param.Set("deadzone", slider_value / 100.0f);
-            emulated_controller->SetStickParam(analog_id, param);
+            analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
+            ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
         });
 
         connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] {
-            Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
             const auto slider_value = analog_map_modifier_slider[analog_id]->value();
             analog_map_modifier_label[analog_id]->setText(
                 tr("Modifier Range: %1%").arg(slider_value));
-            param.Set("modifier_scale", slider_value / 100.0f);
-            emulated_controller->SetStickParam(analog_id, param);
+            analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
         });
     }
 
     // Player Connected checkbox
-    connect(ui->groupConnectedController, &QGroupBox::toggled,
-            [this](bool checked) { emit Connected(checked); });
+    connect(ui->groupConnectedController, &QGroupBox::toggled, [this](bool checked) {
+        emit Connected(checked);
+        ui->controllerFrame->SetConnectedStatus(checked);
+    });
 
     if (player_index == 0) {
         connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
                 [this](int index) {
                     emit HandheldStateChanged(GetControllerTypeFromIndex(index) ==
-                                              Core::HID::NpadStyleIndex::Handheld);
+                                              Settings::ControllerType::Handheld);
                 });
     }
 
@@ -470,43 +537,18 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
         SetConnectableControllers();
     }
 
+    UpdateControllerIcon();
     UpdateControllerAvailableButtons();
     UpdateControllerEnabledButtons();
     UpdateControllerButtonNames();
     UpdateMotionButtons();
-    connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
-            [this, player_index](int) {
-                UpdateControllerAvailableButtons();
-                UpdateControllerEnabledButtons();
-                UpdateControllerButtonNames();
-                UpdateMotionButtons();
-                const Core::HID::NpadStyleIndex type =
-                    GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
-
-                if (player_index == 0) {
-                    auto* emulated_controller_p1 =
-                        hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
-                    auto* emulated_controller_hanheld =
-                        hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
-                    bool is_connected = emulated_controller->IsConnected(true);
-
-                    emulated_controller_p1->SetNpadStyleIndex(type);
-                    emulated_controller_hanheld->SetNpadStyleIndex(type);
-                    if (is_connected) {
-                        if (type == Core::HID::NpadStyleIndex::Handheld) {
-                            emulated_controller_p1->Disconnect();
-                            emulated_controller_hanheld->Connect();
-                            emulated_controller = emulated_controller_hanheld;
-                        } else {
-                            emulated_controller_hanheld->Disconnect();
-                            emulated_controller_p1->Connect();
-                            emulated_controller = emulated_controller_p1;
-                        }
-                    }
-                    ui->controllerFrame->SetController(emulated_controller);
-                }
-                emulated_controller->SetNpadStyleIndex(type);
-            });
+    connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {
+        UpdateControllerIcon();
+        UpdateControllerAvailableButtons();
+        UpdateControllerEnabledButtons();
+        UpdateControllerButtonNames();
+        UpdateMotionButtons();
+    });
 
     connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this,
             &ConfigureInputPlayer::UpdateMappingWithDefaults);
@@ -521,10 +563,62 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
     connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
 
     connect(poll_timer.get(), &QTimer::timeout, [this] {
-        const auto& params = input_subsystem->GetNextInput();
-        if (params.Has("engine") && IsInputAcceptable(params)) {
-            SetPollingResult(params, false);
-            return;
+        Common::ParamPackage params;
+        if (input_subsystem->GetGCButtons()->IsPolling()) {
+            params = input_subsystem->GetGCButtons()->GetNextInput();
+            if (params.Has("engine") && IsInputAcceptable(params)) {
+                SetPollingResult(params, false);
+                return;
+            }
+        }
+        if (input_subsystem->GetGCAnalogs()->IsPolling()) {
+            params = input_subsystem->GetGCAnalogs()->GetNextInput();
+            if (params.Has("engine") && IsInputAcceptable(params)) {
+                SetPollingResult(params, false);
+                return;
+            }
+        }
+        if (input_subsystem->GetUDPMotions()->IsPolling()) {
+            params = input_subsystem->GetUDPMotions()->GetNextInput();
+            if (params.Has("engine")) {
+                SetPollingResult(params, false);
+                return;
+            }
+        }
+        if (input_subsystem->GetMouseButtons()->IsPolling()) {
+            params = input_subsystem->GetMouseButtons()->GetNextInput();
+            if (params.Has("engine") && IsInputAcceptable(params)) {
+                SetPollingResult(params, false);
+                return;
+            }
+        }
+        if (input_subsystem->GetMouseAnalogs()->IsPolling()) {
+            params = input_subsystem->GetMouseAnalogs()->GetNextInput();
+            if (params.Has("engine") && IsInputAcceptable(params)) {
+                SetPollingResult(params, false);
+                return;
+            }
+        }
+        if (input_subsystem->GetMouseMotions()->IsPolling()) {
+            params = input_subsystem->GetMouseMotions()->GetNextInput();
+            if (params.Has("engine") && IsInputAcceptable(params)) {
+                SetPollingResult(params, false);
+                return;
+            }
+        }
+        if (input_subsystem->GetMouseTouch()->IsPolling()) {
+            params = input_subsystem->GetMouseTouch()->GetNextInput();
+            if (params.Has("engine") && IsInputAcceptable(params)) {
+                SetPollingResult(params, false);
+                return;
+            }
+        }
+        for (auto& poller : device_pollers) {
+            params = poller->GetNextInput();
+            if (params.Has("engine") && IsInputAcceptable(params)) {
+                SetPollingResult(params, false);
+                return;
+            }
         }
     });
 
@@ -540,38 +634,110 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
             &ConfigureInputPlayer::SaveProfile);
 
     LoadConfiguration();
+    ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
+    ui->controllerFrame->SetConnectedStatus(ui->groupConnectedController->isChecked());
 }
 
-ConfigureInputPlayer::~ConfigureInputPlayer() {
-    if (player_index == 0) {
-        auto* emulated_controller_p1 =
-            hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
-        auto* emulated_controller_hanheld =
-            hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
-        emulated_controller_p1->DisableConfiguration();
-        emulated_controller_hanheld->DisableConfiguration();
-    } else {
-        emulated_controller->DisableConfiguration();
-    }
-}
+ConfigureInputPlayer::~ConfigureInputPlayer() = default;
 
 void ConfigureInputPlayer::ApplyConfiguration() {
-    if (player_index == 0) {
-        auto* emulated_controller_p1 =
-            hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
-        auto* emulated_controller_hanheld =
-            hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
-        emulated_controller_p1->DisableConfiguration();
-        emulated_controller_p1->SaveCurrentConfig();
-        emulated_controller_p1->EnableConfiguration();
-        emulated_controller_hanheld->DisableConfiguration();
-        emulated_controller_hanheld->SaveCurrentConfig();
-        emulated_controller_hanheld->EnableConfiguration();
+    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;
+
+    std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(),
+                   [](const Common::ParamPackage& param) { return param.Serialize(); });
+    std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
+                   [](const Common::ParamPackage& param) { return param.Serialize(); });
+
+    if (debug) {
         return;
     }
-    emulated_controller->DisableConfiguration();
-    emulated_controller->SaveCurrentConfig();
-    emulated_controller->EnableConfiguration();
+
+    auto& motions = player.motions;
+
+    std::transform(motions_param.begin(), motions_param.end(), motions.begin(),
+                   [](const Common::ParamPackage& param) { return param.Serialize(); });
+
+    // Apply configuration for handheld
+    if (player_index == 0) {
+        auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
+        const auto handheld_connected = handheld.connected;
+        handheld = player;
+        handheld.connected = handheld_connected;
+    }
+}
+
+void ConfigureInputPlayer::TryConnectSelectedController() {
+    auto& player = Settings::values.players.GetValue()[player_index];
+
+    const auto controller_type =
+        GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
+    const auto player_connected = ui->groupConnectedController->isChecked() &&
+                                  controller_type != Settings::ControllerType::Handheld;
+
+    // Connect Handheld depending on Player 1's controller configuration.
+    if (player_index == 0) {
+        auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
+        const auto handheld_connected = ui->groupConnectedController->isChecked() &&
+                                        controller_type == Settings::ControllerType::Handheld;
+        // Connect only if handheld is going from disconnected to connected
+        if (!handheld.connected && handheld_connected) {
+            UpdateController(controller_type, HANDHELD_INDEX, true, system);
+        }
+        handheld.connected = handheld_connected;
+    }
+
+    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;
+    }
+
+    player.controller_type = controller_type;
+    player.connected = player_connected;
+
+    ConfigureVibration::SetVibrationDevices(player_index);
+
+    if (!player.connected) {
+        return;
+    }
+
+    UpdateController(controller_type, player_index, true, system);
+}
+
+void ConfigureInputPlayer::TryDisconnectSelectedController() {
+    const auto& player = Settings::values.players.GetValue()[player_index];
+
+    const auto controller_type =
+        GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
+    const auto player_connected = ui->groupConnectedController->isChecked() &&
+                                  controller_type != Settings::ControllerType::Handheld;
+
+    // Disconnect Handheld depending on Player 1's controller configuration.
+    if (player_index == 0 && player.controller_type == Settings::ControllerType::Handheld) {
+        const auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
+        const auto handheld_connected = ui->groupConnectedController->isChecked() &&
+                                        controller_type == Settings::ControllerType::Handheld;
+        // Disconnect only if handheld is going from connected to disconnected
+        if (handheld.connected && !handheld_connected) {
+            UpdateController(controller_type, HANDHELD_INDEX, false, system);
+        }
+        return;
+    }
+
+    // Do not do anything if the controller configuration has not changed.
+    if (player.controller_type == controller_type && player.connected == player_connected) {
+        return;
+    }
+
+    // Do not disconnect if the controller is already disconnected
+    if (!player.connected) {
+        return;
+    }
+
+    // Disconnect the controller first.
+    UpdateController(controller_type, player_index, false, system);
 }
 
 void ConfigureInputPlayer::showEvent(QShowEvent* event) {
@@ -596,7 +762,22 @@ void ConfigureInputPlayer::RetranslateUI() {
 }
 
 void ConfigureInputPlayer::LoadConfiguration() {
-    emulated_controller->ReloadFromSettings();
+    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(),
+                       [](const std::string& str) { return Common::ParamPackage(str); });
+        std::transform(Settings::values.debug_pad_analogs.begin(),
+                       Settings::values.debug_pad_analogs.end(), analogs_param.begin(),
+                       [](const std::string& str) { return Common::ParamPackage(str); });
+    } else {
+        std::transform(player.buttons.begin(), player.buttons.end(), buttons_param.begin(),
+                       [](const std::string& str) { return Common::ParamPackage(str); });
+        std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(),
+                       [](const std::string& str) { return Common::ParamPackage(str); });
+        std::transform(player.motions.begin(), player.motions.end(), motions_param.begin(),
+                       [](const std::string& str) { return Common::ParamPackage(str); });
+    }
 
     UpdateUI();
     UpdateInputDeviceCombobox();
@@ -605,19 +786,14 @@ void ConfigureInputPlayer::LoadConfiguration() {
         return;
     }
 
-    const int comboBoxIndex =
-        GetIndexFromControllerType(emulated_controller->GetNpadStyleIndex(true));
-    ui->comboControllerType->setCurrentIndex(comboBoxIndex);
-    ui->groupConnectedController->setChecked(emulated_controller->IsConnected(true));
+    ui->comboControllerType->setCurrentIndex(GetIndexFromControllerType(player.controller_type));
+    ui->groupConnectedController->setChecked(
+        player.connected ||
+        (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected));
 }
 
 void ConfigureInputPlayer::ConnectPlayer(bool connected) {
     ui->groupConnectedController->setChecked(connected);
-    if (connected) {
-        emulated_controller->Connect();
-    } else {
-        emulated_controller->Disconnect();
-    }
 }
 
 void ConfigureInputPlayer::UpdateInputDeviceCombobox() {
@@ -627,64 +803,48 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() {
         return;
     }
 
-    const auto devices =
-        emulated_controller->GetMappedDevices(Core::HID::EmulatedDeviceIndex::AllDevices);
+    // 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", "");
+
+    const bool is_keyboard_mouse = current_engine == "keyboard" || current_engine == "mouse";
+
     UpdateInputDevices();
 
-    if (devices.empty()) {
+    if (buttons_empty) {
         return;
     }
 
-    if (devices.size() > 2) {
-        ui->comboDevices->setCurrentIndex(0);
-        return;
-    }
+    const bool all_one_device =
+        std::all_of(buttons_param.begin(), buttons_param.end(),
+                    [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);
+                    });
 
-    const auto first_engine = devices[0].Get("engine", "");
-    const auto first_guid = devices[0].Get("guid", "");
-    const auto first_port = devices[0].Get("port", 0);
-
-    if (devices.size() == 1) {
-        const auto devices_it =
-            std::find_if(input_devices.begin(), input_devices.end(),
-                         [first_engine, first_guid, first_port](const Common::ParamPackage param) {
-                             return param.Get("engine", "") == first_engine &&
-                                    param.Get("guid", "") == first_guid &&
-                                    param.Get("port", 0) == first_port;
-                         });
-        const int device_index =
-            devices_it != input_devices.end()
-                ? static_cast<int>(std::distance(input_devices.begin(), devices_it))
-                : 0;
-        ui->comboDevices->setCurrentIndex(device_index);
-        return;
-    }
-
-    const auto second_engine = devices[1].Get("engine", "");
-    const auto second_guid = devices[1].Get("guid", "");
-    const auto second_port = devices[1].Get("port", 0);
-
-    const bool is_keyboard_mouse = (first_engine == "keyboard" || first_engine == "mouse") &&
-                                   (second_engine == "keyboard" || second_engine == "mouse");
-
-    if (is_keyboard_mouse) {
-        ui->comboDevices->setCurrentIndex(2);
-        return;
-    }
-
-    const bool is_engine_equal = first_engine == second_engine;
-    const bool is_port_equal = first_port == second_port;
-
-    if (is_engine_equal && is_port_equal) {
+    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(),
-            [first_engine, first_guid, second_guid, first_port](const Common::ParamPackage param) {
-                const bool is_guid_valid =
-                    (param.Get("guid", "") == first_guid &&
-                     param.Get("guid2", "") == second_guid) ||
-                    (param.Get("guid", "") == second_guid && param.Get("guid2", "") == first_guid);
-                return param.Get("engine", "") == first_engine && is_guid_valid &&
-                       param.Get("port", 0) == first_port;
+            [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()
@@ -706,7 +866,8 @@ void ConfigureInputPlayer::ClearAll() {
         if (button == nullptr) {
             continue;
         }
-        emulated_controller->SetButtonParam(button_id, {});
+
+        buttons_param[button_id].Clear();
     }
 
     for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
@@ -715,7 +876,8 @@ void ConfigureInputPlayer::ClearAll() {
             if (analog_button == nullptr) {
                 continue;
             }
-            emulated_controller->SetStickParam(analog_id, {});
+
+            analogs_param[analog_id].Clear();
         }
     }
 
@@ -724,7 +886,8 @@ void ConfigureInputPlayer::ClearAll() {
         if (motion_button == nullptr) {
             continue;
         }
-        emulated_controller->SetMotionParam(motion_id, {});
+
+        motions_param[motion_id].Clear();
     }
 
     UpdateUI();
@@ -733,31 +896,26 @@ void ConfigureInputPlayer::ClearAll() {
 
 void ConfigureInputPlayer::UpdateUI() {
     for (int button = 0; button < Settings::NativeButton::NumButtons; ++button) {
-        const Common::ParamPackage param = emulated_controller->GetButtonParam(button);
-        button_map[button]->setText(ButtonToText(param));
+        button_map[button]->setText(ButtonToText(buttons_param[button]));
     }
 
-    const Common::ParamPackage ZL_param =
-        emulated_controller->GetButtonParam(Settings::NativeButton::ZL);
-    if (ZL_param.Has("threshold")) {
-        const int button_threshold = static_cast<int>(ZL_param.Get("threshold", 0.5f) * 100.0f);
+    if (buttons_param[Settings::NativeButton::ZL].Has("threshold")) {
+        const int button_threshold = static_cast<int>(
+            buttons_param[Settings::NativeButton::ZL].Get("threshold", 0.5f) * 100.0f);
         ui->sliderZLThreshold->setValue(button_threshold);
     }
 
-    const Common::ParamPackage ZR_param =
-        emulated_controller->GetButtonParam(Settings::NativeButton::ZR);
-    if (ZR_param.Has("threshold")) {
-        const int button_threshold = static_cast<int>(ZR_param.Get("threshold", 0.5f) * 100.0f);
+    if (buttons_param[Settings::NativeButton::ZR].Has("threshold")) {
+        const int button_threshold = static_cast<int>(
+            buttons_param[Settings::NativeButton::ZR].Get("threshold", 0.5f) * 100.0f);
         ui->sliderZRThreshold->setValue(button_threshold);
     }
 
     for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
-        const Common::ParamPackage param = emulated_controller->GetMotionParam(motion_id);
-        motion_map[motion_id]->setText(ButtonToText(param));
+        motion_map[motion_id]->setText(ButtonToText(motions_param[motion_id]));
     }
 
     for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
-        const Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
         for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
             auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
 
@@ -765,11 +923,12 @@ void ConfigureInputPlayer::UpdateUI() {
                 continue;
             }
 
-            analog_button->setText(AnalogToText(param, analog_sub_buttons[sub_button_id]));
+            analog_button->setText(
+                AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
         }
 
         analog_map_modifier_button[analog_id]->setText(
-            ButtonToText(Common::ParamPackage{param.Get("modifier", "")}));
+            ButtonToText(Common::ParamPackage{analogs_param[analog_id].Get("modifier", "")}));
 
         const auto deadzone_label = analog_map_deadzone_label[analog_id];
         const auto deadzone_slider = analog_map_deadzone_slider[analog_id];
@@ -780,14 +939,26 @@ void ConfigureInputPlayer::UpdateUI() {
         const auto range_spinbox = analog_map_range_spinbox[analog_id];
 
         int slider_value;
-        const bool is_controller = input_subsystem->IsController(param);
+        auto& param = analogs_param[analog_id];
+        const bool is_controller =
+            param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad" ||
+            param.Get("engine", "") == "mouse" || param.Get("engine", "") == "tas";
 
         if (is_controller) {
-            slider_value = static_cast<int>(param.Get("deadzone", 0.15f) * 100);
+            if (!param.Has("deadzone")) {
+                param.Set("deadzone", 0.1f);
+            }
+            slider_value = static_cast<int>(param.Get("deadzone", 0.1f) * 100);
             deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value));
             deadzone_slider->setValue(slider_value);
+            if (!param.Has("range")) {
+                param.Set("range", 1.0f);
+            }
             range_spinbox->setValue(static_cast<int>(param.Get("range", 1.0f) * 100));
         } else {
+            if (!param.Has("modifier_scale")) {
+                param.Set("modifier_scale", 0.5f);
+            }
             slider_value = static_cast<int>(param.Get("modifier_scale", 0.5f) * 100);
             modifier_label->setText(tr("Modifier Range: %1%").arg(slider_value));
             modifier_slider->setValue(slider_value);
@@ -799,73 +970,79 @@ void ConfigureInputPlayer::UpdateUI() {
         modifier_label->setVisible(!is_controller);
         modifier_slider->setVisible(!is_controller);
         range_groupbox->setVisible(is_controller);
+        ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
     }
 }
 
 void ConfigureInputPlayer::SetConnectableControllers() {
     const auto add_controllers = [this](bool enable_all,
-                                        Core::HID::NpadStyleTag npad_style_set = {}) {
+                                        Controller_NPad::NpadStyleSet npad_style_set = {}) {
         index_controller_type_pairs.clear();
         ui->comboControllerType->clear();
 
         if (enable_all || npad_style_set.fullkey == 1) {
             index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
-                                                     Core::HID::NpadStyleIndex::ProController);
+                                                     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(),
-                                                     Core::HID::NpadStyleIndex::JoyconDual);
+                                                     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(),
-                                                     Core::HID::NpadStyleIndex::JoyconLeft);
+                                                     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(),
-                                                     Core::HID::NpadStyleIndex::JoyconRight);
+                                                     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(),
-                                                     Core::HID::NpadStyleIndex::Handheld);
+                                                     Settings::ControllerType::Handheld);
             ui->comboControllerType->addItem(tr("Handheld"));
         }
 
         if (enable_all || npad_style_set.gamecube == 1) {
             index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
-                                                     Core::HID::NpadStyleIndex::GameCube);
+                                                     Settings::ControllerType::GameCube);
             ui->comboControllerType->addItem(tr("GameCube Controller"));
         }
     };
 
-    if (!is_powered_on) {
+    if (!system.IsPoweredOn()) {
         add_controllers(true);
         return;
     }
 
-    add_controllers(false, hid_core.GetSupportedStyleTag());
+    Service::SM::ServiceManager& sm = system.ServiceManager();
+
+    auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>(
+        HidController::NPad);
+
+    add_controllers(false, npad.GetSupportedStyleSet());
 }
 
-Core::HID::NpadStyleIndex ConfigureInputPlayer::GetControllerTypeFromIndex(int index) const {
+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 Core::HID::NpadStyleIndex::ProController;
+        return Settings::ControllerType::ProController;
     }
 
     return it->second;
 }
 
-int ConfigureInputPlayer::GetIndexFromControllerType(Core::HID::NpadStyleIndex type) const {
+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; });
@@ -880,15 +1057,52 @@ int ConfigureInputPlayer::GetIndexFromControllerType(Core::HID::NpadStyleIndex t
 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")), {});
+    for (auto& device : input_devices) {
+        const std::string display = device.Get("display", "Unknown");
+        ui->comboDevices->addItem(QString::fromStdString(display), {});
+        if (display == "TAS") {
+            device.Set("pad", static_cast<u8>(player_index));
+        }
     }
 }
 
+void ConfigureInputPlayer::UpdateControllerIcon() {
+    // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its
+    // "nonstandard" to use an image through the icon support)
+    const QString stylesheet = [this] {
+        switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
+        case Settings::ControllerType::ProController:
+            return QStringLiteral("image: url(:/controller/pro_controller%0)");
+        case Settings::ControllerType::DualJoyconDetached:
+            return QStringLiteral("image: url(:/controller/dual_joycon%0)");
+        case Settings::ControllerType::LeftJoycon:
+            return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)");
+        case Settings::ControllerType::RightJoycon:
+            return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)");
+        case Settings::ControllerType::Handheld:
+            return QStringLiteral("image: url(:/controller/handheld%0)");
+        default:
+            return QString{};
+        }
+    }();
+
+    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{};
+        }
+    }();
+    ui->controllerFrame->SetControllerType(
+        GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()));
+}
+
 void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
     auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
     if (debug) {
-        layout = Core::HID::NpadStyleIndex::ProController;
+        layout = Settings::ControllerType::ProController;
     }
 
     // List of all the widgets that will be hidden by any of the following layouts that need
@@ -913,15 +1127,15 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
 
     std::vector<QWidget*> layout_hidden;
     switch (layout) {
-    case Core::HID::NpadStyleIndex::ProController:
-    case Core::HID::NpadStyleIndex::JoyconDual:
-    case Core::HID::NpadStyleIndex::Handheld:
+    case Settings::ControllerType::ProController:
+    case Settings::ControllerType::DualJoyconDetached:
+    case Settings::ControllerType::Handheld:
         layout_hidden = {
             ui->buttonShoulderButtonsSLSR,
             ui->horizontalSpacerShoulderButtonsWidget2,
         };
         break;
-    case Core::HID::NpadStyleIndex::JoyconLeft:
+    case Settings::ControllerType::LeftJoycon:
         layout_hidden = {
             ui->horizontalSpacerShoulderButtonsWidget2,
             ui->buttonShoulderButtonsRight,
@@ -929,7 +1143,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
             ui->bottomRight,
         };
         break;
-    case Core::HID::NpadStyleIndex::JoyconRight:
+    case Settings::ControllerType::RightJoycon:
         layout_hidden = {
             ui->horizontalSpacerShoulderButtonsWidget,
             ui->buttonShoulderButtonsLeft,
@@ -937,7 +1151,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
             ui->bottomLeft,
         };
         break;
-    case Core::HID::NpadStyleIndex::GameCube:
+    case Settings::ControllerType::GameCube:
         layout_hidden = {
             ui->buttonShoulderButtonsSLSR,
             ui->horizontalSpacerShoulderButtonsWidget2,
@@ -945,8 +1159,6 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
             ui->buttonMiscButtonsScreenshotGroup,
         };
         break;
-    default:
-        break;
     }
 
     for (auto* widget : layout_hidden) {
@@ -957,12 +1169,13 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
 void ConfigureInputPlayer::UpdateControllerEnabledButtons() {
     auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
     if (debug) {
-        layout = Core::HID::NpadStyleIndex::ProController;
+        layout = Settings::ControllerType::ProController;
     }
 
     // List of all the widgets that will be disabled by any of the following layouts that need
     // "enabled" after the controller type changes
-    const std::array<QWidget*, 3> layout_enable = {
+    const std::array<QWidget*, 4> layout_enable = {
+        ui->buttonHome,
         ui->buttonLStickPressedGroup,
         ui->groupRStickPressed,
         ui->buttonShoulderButtonsButtonLGroup,
@@ -974,13 +1187,17 @@ void ConfigureInputPlayer::UpdateControllerEnabledButtons() {
 
     std::vector<QWidget*> layout_disable;
     switch (layout) {
-    case Core::HID::NpadStyleIndex::ProController:
-    case Core::HID::NpadStyleIndex::JoyconDual:
-    case Core::HID::NpadStyleIndex::Handheld:
-    case Core::HID::NpadStyleIndex::JoyconLeft:
-    case Core::HID::NpadStyleIndex::JoyconRight:
+    case Settings::ControllerType::ProController:
+    case Settings::ControllerType::DualJoyconDetached:
+    case Settings::ControllerType::Handheld:
+    case Settings::ControllerType::LeftJoycon:
+    case Settings::ControllerType::RightJoycon:
+        // TODO(wwylele): enable this when we actually emulate it
+        layout_disable = {
+            ui->buttonHome,
+        };
         break;
-    case Core::HID::NpadStyleIndex::GameCube:
+    case Settings::ControllerType::GameCube:
         layout_disable = {
             ui->buttonHome,
             ui->buttonLStickPressedGroup,
@@ -988,8 +1205,6 @@ void ConfigureInputPlayer::UpdateControllerEnabledButtons() {
             ui->buttonShoulderButtonsButtonLGroup,
         };
         break;
-    default:
-        break;
     }
 
     for (auto* widget : layout_disable) {
@@ -1007,24 +1222,24 @@ void ConfigureInputPlayer::UpdateMotionButtons() {
 
     // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller.
     switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
-    case Core::HID::NpadStyleIndex::ProController:
-    case Core::HID::NpadStyleIndex::JoyconLeft:
-    case Core::HID::NpadStyleIndex::Handheld:
+    case Settings::ControllerType::ProController:
+    case Settings::ControllerType::LeftJoycon:
+    case Settings::ControllerType::Handheld:
         // Show "Motion 1" and hide "Motion 2".
         ui->buttonMotionLeftGroup->show();
         ui->buttonMotionRightGroup->hide();
         break;
-    case Core::HID::NpadStyleIndex::JoyconRight:
+    case Settings::ControllerType::RightJoycon:
         // Show "Motion 2" and hide "Motion 1".
         ui->buttonMotionLeftGroup->hide();
         ui->buttonMotionRightGroup->show();
         break;
-    case Core::HID::NpadStyleIndex::GameCube:
+    case Settings::ControllerType::GameCube:
         // Hide both "Motion 1/2".
         ui->buttonMotionLeftGroup->hide();
         ui->buttonMotionRightGroup->hide();
         break;
-    case Core::HID::NpadStyleIndex::JoyconDual:
+    case Settings::ControllerType::DualJoyconDetached:
     default:
         // Show both "Motion 1/2".
         ui->buttonMotionLeftGroup->show();
@@ -1036,15 +1251,15 @@ void ConfigureInputPlayer::UpdateMotionButtons() {
 void ConfigureInputPlayer::UpdateControllerButtonNames() {
     auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
     if (debug) {
-        layout = Core::HID::NpadStyleIndex::ProController;
+        layout = Settings::ControllerType::ProController;
     }
 
     switch (layout) {
-    case Core::HID::NpadStyleIndex::ProController:
-    case Core::HID::NpadStyleIndex::JoyconDual:
-    case Core::HID::NpadStyleIndex::Handheld:
-    case Core::HID::NpadStyleIndex::JoyconLeft:
-    case Core::HID::NpadStyleIndex::JoyconRight:
+    case Settings::ControllerType::ProController:
+    case Settings::ControllerType::DualJoyconDetached:
+    case Settings::ControllerType::Handheld:
+    case Settings::ControllerType::LeftJoycon:
+    case Settings::ControllerType::RightJoycon:
         ui->buttonMiscButtonsPlusGroup->setTitle(tr("Plus"));
         ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("ZL"));
         ui->buttonShoulderButtonsZRGroup->setTitle(tr("ZR"));
@@ -1052,7 +1267,7 @@ void ConfigureInputPlayer::UpdateControllerButtonNames() {
         ui->LStick->setTitle(tr("Left Stick"));
         ui->RStick->setTitle(tr("Right Stick"));
         break;
-    case Core::HID::NpadStyleIndex::GameCube:
+    case Settings::ControllerType::GameCube:
         ui->buttonMiscButtonsPlusGroup->setTitle(tr("Start / Pause"));
         ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("L"));
         ui->buttonShoulderButtonsZRGroup->setTitle(tr("R"));
@@ -1060,8 +1275,6 @@ void ConfigureInputPlayer::UpdateControllerButtonNames() {
         ui->LStick->setTitle(tr("Control Stick"));
         ui->RStick->setTitle(tr("C-Stick"));
         break;
-    default:
-        break;
     }
 }
 
@@ -1070,82 +1283,45 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
         return;
     }
 
-    for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
-        const auto* const button = button_map[button_id];
-        if (button == nullptr) {
-            continue;
-        }
-        emulated_controller->SetButtonParam(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) {
-            const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
-            if (analog_button == nullptr) {
-                continue;
-            }
-            emulated_controller->SetStickParam(analog_id, {});
-        }
-    }
-
-    for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
-        const auto* const motion_button = motion_map[motion_id];
-        if (motion_button == nullptr) {
-            continue;
-        }
-        emulated_controller->SetMotionParam(motion_id, {});
-    }
-
-    // Reset keyboard or mouse bindings
-    if (ui->comboDevices->currentIndex() == 1 || ui->comboDevices->currentIndex() == 2) {
+    if (ui->comboDevices->currentIndex() == 1) {
+        // Reset keyboard bindings
         for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
-            emulated_controller->SetButtonParam(
-                button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
-                               Config::default_buttons[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) {
-            Common::ParamPackage analog_param{};
             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, analog_param, analog_sub_buttons[sub_button_id]);
+                SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
             }
 
-            analog_param.Set("modifier", InputCommon::GenerateKeyboardParam(
-                                             Config::default_stick_mod[analog_id]));
-            emulated_controller->SetStickParam(analog_id, analog_param);
+            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) {
-            emulated_controller->SetMotionParam(
-                motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
-                               Config::default_motions[motion_id])});
+            motions_param[motion_id] = Common::ParamPackage{
+                InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])};
         }
 
-        // If mouse is selected we want to override with mappings from the driver
-        if (ui->comboDevices->currentIndex() == 1) {
-            UpdateUI();
-            return;
-        }
+        UpdateUI();
+        return;
     }
 
     // Reset controller bindings
     const auto& device = input_devices[ui->comboDevices->currentIndex()];
-    auto button_mappings = input_subsystem->GetButtonMappingForDevice(device);
-    auto analog_mappings = input_subsystem->GetAnalogMappingForDevice(device);
-    auto motion_mappings = input_subsystem->GetMotionMappingForDevice(device);
-
-    for (const auto& button_mapping : button_mappings) {
-        const std::size_t index = button_mapping.first;
-        emulated_controller->SetButtonParam(index, button_mapping.second);
+    auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
+    auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
+    auto motion_mapping = input_subsystem->GetMotionMappingForDevice(device);
+    for (std::size_t i = 0; i < buttons_param.size(); ++i) {
+        buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)];
     }
-    for (const auto& analog_mapping : analog_mappings) {
-        const std::size_t index = analog_mapping.first;
-        emulated_controller->SetStickParam(index, analog_mapping.second);
+    for (std::size_t i = 0; i < analogs_param.size(); ++i) {
+        analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)];
     }
-    for (const auto& motion_mapping : motion_mappings) {
-        const std::size_t index = motion_mapping.first;
-        emulated_controller->SetMotionParam(index, motion_mapping.second);
+    for (std::size_t i = 0; i < motions_param.size(); ++i) {
+        motions_param[i] = motion_mapping[static_cast<Settings::NativeMotion::Values>(i)];
     }
 
     UpdateUI();
@@ -1154,7 +1330,7 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
 void ConfigureInputPlayer::HandleClick(
     QPushButton* button, std::size_t button_id,
     std::function<void(const Common::ParamPackage&)> new_input_setter,
-    InputCommon::Polling::InputType type) {
+    InputCommon::Polling::DeviceType type) {
     if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
         button->setText(tr("Shake!"));
     } else {
@@ -1162,31 +1338,71 @@ void ConfigureInputPlayer::HandleClick(
     }
     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;
 
-    input_subsystem->BeginMapping(type);
+    device_pollers = input_subsystem->GetPollers(type);
+
+    for (auto& poller : device_pollers) {
+        poller->Start();
+    }
 
     QWidget::grabMouse();
     QWidget::grabKeyboard();
 
-    if (type == InputCommon::Polling::InputType::Button) {
+    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();
+    }
+
+    if (type == InputCommon::Polling::DeviceType::Button) {
+        input_subsystem->GetMouseButtons()->BeginConfiguration();
+    } else if (type == InputCommon::Polling::DeviceType::AnalogPreferred) {
+        input_subsystem->GetMouseAnalogs()->BeginConfiguration();
+    } else if (type == InputCommon::Polling::DeviceType::Motion) {
+        input_subsystem->GetMouseMotions()->BeginConfiguration();
+    } else {
+        input_subsystem->GetMouseTouch()->BeginConfiguration();
+    }
+
+    if (type == InputCommon::Polling::DeviceType::Button) {
         ui->controllerFrame->BeginMappingButton(button_id);
-    } else if (type == InputCommon::Polling::InputType::Stick) {
+    } else if (type == InputCommon::Polling::DeviceType::AnalogPreferred) {
         ui->controllerFrame->BeginMappingAnalog(button_id);
     }
 
     timeout_timer->start(2500); // Cancel after 2.5 seconds
-    poll_timer->start(25);      // Check for new inputs every 25ms
+    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();
-    input_subsystem->StopMapping();
+    for (auto& poller : device_pollers) {
+        poller->Stop();
+    }
 
     QWidget::releaseMouse();
     QWidget::releaseKeyboard();
 
+    input_subsystem->GetGCButtons()->EndConfiguration();
+    input_subsystem->GetGCAnalogs()->EndConfiguration();
+
+    input_subsystem->GetUDPMotions()->EndConfiguration();
+
+    input_subsystem->GetMouseButtons()->EndConfiguration();
+    input_subsystem->GetMouseAnalogs()->EndConfiguration();
+    input_subsystem->GetMouseMotions()->EndConfiguration();
+    input_subsystem->GetMouseTouch()->EndConfiguration();
+
     if (!abort) {
         (*input_setter)(params);
     }
@@ -1203,20 +1419,15 @@ bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params)
         return true;
     }
 
-    if (params.Has("motion")) {
-        return true;
-    }
-
     // Keyboard/Mouse
-    if (ui->comboDevices->currentIndex() == 1 || ui->comboDevices->currentIndex() == 2) {
+    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("engine", "") &&
-           (params.Get("guid", "") == current_input_device.Get("guid", "") ||
-            params.Get("guid", "") == current_input_device.Get("guid2", "")) &&
-           params.Get("port", 0) == current_input_device.Get("port", 0);
+    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) {
@@ -1225,17 +1436,25 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
     }
 
     const auto button = GRenderWindow::QtButtonToMouseButton(event->button());
-    input_subsystem->GetMouse()->PressButton(0, 0, 0, 0, button);
+    input_subsystem->GetMouse()->PressButton(0, 0, button);
 }
 
 void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
-    event->ignore();
     if (!input_setter || !event) {
         return;
     }
+
     if (event->key() != Qt::Key_Escape) {
-        input_subsystem->GetKeyboard()->PressKey(event->key());
+        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);
 }
 
 void ConfigureInputPlayer::CreateProfile() {
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 47df6b3d3..39b44b8a5 100755
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -29,37 +29,48 @@ class QWidget;
 
 class InputProfiles;
 
+namespace Core {
+class System;
+}
+
 namespace InputCommon {
 class InputSubsystem;
 }
 
 namespace InputCommon::Polling {
-enum class InputType;
+class DevicePoller;
+enum class DeviceType;
 } // namespace InputCommon::Polling
 
 namespace Ui {
 class ConfigureInputPlayer;
 }
 
-namespace Core::HID {
-class HIDCore;
-class EmulatedController;
-enum class NpadStyleIndex : u8;
-} // namespace Core::HID
-
 class ConfigureInputPlayer : public QWidget {
     Q_OBJECT
 
 public:
     explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row,
                                   InputCommon::InputSubsystem* input_subsystem_,
-                                  InputProfiles* profiles_, Core::HID::HIDCore& hid_core_,
-                                  bool is_powered_on_, bool debug = false);
+                                  InputProfiles* profiles_, Core::System& system_,
+                                  bool debug = false);
     ~ConfigureInputPlayer() override;
 
     /// Save all button configurations to settings file.
     void ApplyConfiguration();
 
+    /**
+     * Attempts to connect the currently selected controller in the HID backend.
+     * This function will not do anything if it is not connected in the frontend.
+     */
+    void TryConnectSelectedController();
+
+    /**
+     * Attempts to disconnect the currently selected controller in the HID backend.
+     * This function will not do anything if the configuration has not changed.
+     */
+    void TryDisconnectSelectedController();
+
     /// Set the connection state checkbox (used to sync state).
     void ConnectPlayer(bool connected);
 
@@ -93,10 +104,6 @@ protected:
     void showEvent(QShowEvent* event) override;
 
 private:
-    QString ButtonToText(const Common::ParamPackage& param);
-
-    QString AnalogToText(const Common::ParamPackage& param, const std::string& dir);
-
     void changeEvent(QEvent* event) override;
     void RetranslateUI();
 
@@ -106,7 +113,7 @@ private:
     /// Called when the button was pressed.
     void HandleClick(QPushButton* button, std::size_t button_id,
                      std::function<void(const Common::ParamPackage&)> new_input_setter,
-                     InputCommon::Polling::InputType type);
+                     InputCommon::Polling::DeviceType type);
 
     /// Finish polling and configure input using the input_setter.
     void SetPollingResult(const Common::ParamPackage& params, bool abort);
@@ -127,14 +134,17 @@ private:
     void SetConnectableControllers();
 
     /// Gets the Controller Type for a given controller combobox index.
-    Core::HID::NpadStyleIndex GetControllerTypeFromIndex(int index) const;
+    Settings::ControllerType GetControllerTypeFromIndex(int index) const;
 
     /// Gets the controller combobox index for a given Controller Type.
-    int GetIndexFromControllerType(Core::HID::NpadStyleIndex type) const;
+    int GetIndexFromControllerType(Settings::ControllerType type) const;
 
     /// Update the available input devices.
     void UpdateInputDevices();
 
+    /// Update the current controller icon.
+    void UpdateControllerIcon();
+
     /// Hides and disables controller settings based on the current controller type.
     void UpdateControllerAvailableButtons();
 
@@ -166,7 +176,6 @@ private:
 
     std::size_t player_index;
     bool debug;
-    bool is_powered_on;
 
     InputCommon::InputSubsystem* input_subsystem;
 
@@ -176,7 +185,7 @@ private:
     std::unique_ptr<QTimer> poll_timer;
 
     /// Stores a pair of "Connected Controllers" combobox index and Controller Type enum.
-    std::vector<std::pair<int, Core::HID::NpadStyleIndex>> index_controller_type_pairs;
+    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;
@@ -184,7 +193,9 @@ private:
     /// This will be the the setting function when an input is awaiting configuration.
     std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
 
-    Core::HID::EmulatedController* emulated_controller;
+    std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
+    std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
+    std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions> motions_param;
 
     static constexpr int ANALOG_SUB_BUTTONS_NUM = 4;
 
@@ -210,9 +221,15 @@ private:
 
     static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
 
+    std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
+
     /// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once.
     bool map_analog_stick_accepted{};
 
+    /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
+    /// keyboard events are ignored.
+    bool want_keyboard_mouse{};
+
     /// List of physical devices users can map with. If a SDL backed device is selected, then you
     /// can use this device to get a default mapping.
     std::vector<Common::ParamPackage> input_devices;
@@ -222,5 +239,5 @@ private:
     /// parent of the widget to this widget (but thats fine).
     QWidget* bottom_row;
 
-    Core::HID::HIDCore& hid_core;
+    Core::System& system;
 };
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index 756a414b5..e7433912b 100755
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -89,6 +89,31 @@
               <height>21</height>
              </size>
             </property>
+            <item>
+             <property name="text">
+              <string>Pro Controller</string>
+             </property>
+            </item>
+            <item>
+             <property name="text">
+              <string>Dual Joycons</string>
+             </property>
+            </item>
+            <item>
+             <property name="text">
+              <string>Left Joycon</string>
+             </property>
+            </item>
+            <item>
+             <property name="text">
+              <string>Right Joycon</string>
+             </property>
+            </item>
+            <item>
+             <property name="text">
+              <string>Handheld</string>
+             </property>
+            </item>
            </widget>
           </item>
          </layout>
@@ -117,9 +142,22 @@
           </property>
           <item>
            <widget class="QComboBox" name="comboDevices">
-            <property name="minimumContentsLength">
-             <number>60</number>
+            <property name="minimumSize">
+             <size>
+              <width>0</width>
+              <height>21</height>
+             </size>
             </property>
+            <item>
+             <property name="text">
+              <string>Any</string>
+             </property>
+            </item>
+            <item>
+             <property name="text">
+              <string>Keyboard/Mouse</string>
+             </property>
+            </item>
            </widget>
           </item>
           <item>
@@ -304,7 +342,7 @@
                <number>3</number>
               </property>
               <property name="topMargin">
-               <number>6</number>
+               <number>0</number>
               </property>
               <property name="rightMargin">
                <number>3</number>
@@ -880,7 +918,7 @@
                <number>3</number>
               </property>
               <property name="topMargin">
-               <number>6</number>
+               <number>0</number>
               </property>
               <property name="rightMargin">
                <number>3</number>
@@ -2183,7 +2221,7 @@
                <number>3</number>
               </property>
               <property name="topMargin">
-               <number>6</number>
+               <number>0</number>
               </property>
               <property name="rightMargin">
                <number>3</number>
@@ -2532,7 +2570,7 @@
                <number>3</number>
               </property>
               <property name="topMargin">
-               <number>6</number>
+               <number>0</number>
               </property>
               <property name="rightMargin">
                <number>3</number>
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index 6630321cb..f31f86339 100755
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -6,12 +6,10 @@
 #include <QMenu>
 #include <QPainter>
 #include <QTimer>
-
-#include "core/hid/emulated_controller.h"
 #include "yuzu/configuration/configure_input_player_widget.h"
 
 PlayerControlPreview::PlayerControlPreview(QWidget* parent) : QFrame(parent) {
-    is_controller_set = false;
+    UpdateColors();
     QTimer* timer = new QTimer(this);
     connect(timer, &QTimer::timeout, this, QOverload<>::of(&PlayerControlPreview::UpdateInput));
 
@@ -19,37 +17,91 @@ PlayerControlPreview::PlayerControlPreview(QWidget* parent) : QFrame(parent) {
     timer->start(16);
 }
 
-PlayerControlPreview::~PlayerControlPreview() {
-    UnloadController();
-};
+PlayerControlPreview::~PlayerControlPreview() = default;
 
-void PlayerControlPreview::SetController(Core::HID::EmulatedController* controller_) {
-    UnloadController();
-    is_controller_set = true;
-    controller = controller_;
-    Core::HID::ControllerUpdateCallback engine_callback{
-        .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); },
-        .is_npad_service = false,
-    };
-    callback_key = controller->SetCallback(engine_callback);
-    ControllerUpdate(Core::HID::ControllerTriggerType::All);
+void PlayerControlPreview::SetPlayerInput(std::size_t index, const ButtonParam& buttons_param,
+                                          const AnalogParam& analogs_param) {
+    player_index = index;
+    Settings::ButtonsRaw buttonss;
+    Settings::AnalogsRaw analogs;
+    std::transform(buttons_param.begin(), buttons_param.end(), buttonss.begin(),
+                   [](const Common::ParamPackage& param) { return param.Serialize(); });
+    std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
+                   [](const Common::ParamPackage& param) { return param.Serialize(); });
+
+    std::transform(buttonss.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
+                   buttonss.begin() + Settings::NativeButton::BUTTON_NS_END, buttons.begin(),
+                   Input::CreateDevice<Input::ButtonDevice>);
+    std::transform(analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
+                   analogs.begin() + Settings::NativeAnalog::STICK_HID_END, sticks.begin(),
+                   Input::CreateDevice<Input::AnalogDevice>);
+    UpdateColors();
+}
+void PlayerControlPreview::SetPlayerInputRaw(std::size_t index,
+                                             const Settings::ButtonsRaw& buttons_,
+                                             Settings::AnalogsRaw analogs_) {
+    player_index = index;
+    std::transform(buttons_.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
+                   buttons_.begin() + Settings::NativeButton::BUTTON_NS_END, buttons.begin(),
+                   Input::CreateDevice<Input::ButtonDevice>);
+    std::transform(analogs_.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
+                   analogs_.begin() + Settings::NativeAnalog::STICK_HID_END, sticks.begin(),
+                   Input::CreateDevice<Input::AnalogDevice>);
+    UpdateColors();
 }
 
-void PlayerControlPreview::UnloadController() {
-    if (is_controller_set) {
-        controller->DeleteCallback(callback_key);
-        is_controller_set = false;
+PlayerControlPreview::LedPattern PlayerControlPreview::GetColorPattern(std::size_t index,
+                                                                       bool player_on) {
+    if (!player_on) {
+        return {0, 0, 0, 0};
+    }
+
+    switch (index) {
+    case 0:
+        return {1, 0, 0, 0};
+    case 1:
+        return {1, 1, 0, 0};
+    case 2:
+        return {1, 1, 1, 0};
+    case 3:
+        return {1, 1, 1, 1};
+    case 4:
+        return {1, 0, 0, 1};
+    case 5:
+        return {1, 0, 1, 0};
+    case 6:
+        return {1, 0, 1, 1};
+    case 7:
+        return {0, 1, 1, 0};
+    default:
+        return {0, 0, 0, 0};
     }
 }
 
-void PlayerControlPreview::BeginMappingButton(std::size_t button_id) {
-    button_mapping_index = button_id;
+void PlayerControlPreview::SetConnectedStatus(bool checked) {
+    LedPattern led_pattern = GetColorPattern(player_index, checked);
+
+    led_color[0] = led_pattern.position1 ? colors.led_on : colors.led_off;
+    led_color[1] = led_pattern.position2 ? colors.led_on : colors.led_off;
+    led_color[2] = led_pattern.position3 ? colors.led_on : colors.led_off;
+    led_color[3] = led_pattern.position4 ? colors.led_on : colors.led_off;
+    is_enabled = checked;
+    ResetInputs();
+}
+
+void PlayerControlPreview::SetControllerType(const Settings::ControllerType type) {
+    controller_type = type;
+    UpdateColors();
+}
+
+void PlayerControlPreview::BeginMappingButton(std::size_t index) {
+    button_mapping_index = index;
     mapping_active = true;
 }
 
-void PlayerControlPreview::BeginMappingAnalog(std::size_t stick_id) {
-    button_mapping_index = Settings::NativeButton::LStick + stick_id;
-    analog_mapping_index = stick_id;
+void PlayerControlPreview::BeginMappingAnalog(std::size_t index) {
+    button_mapping_index = Settings::NativeButton::LStick + index;
+    analog_mapping_index = index;
     mapping_active = true;
 }
 
@@ -105,109 +157,84 @@ void PlayerControlPreview::UpdateColors() {
     colors.left = colors.primary;
     colors.right = colors.primary;
     // Possible alternative to set colors from settings
-    // colors.left = QColor(controller->GetColors().left.body);
-    // colors.right = QColor(controller->GetColors().right.body);
+    // colors.left = QColor(Settings::values.players.GetValue()[player_index].body_color_left);
+    // colors.right = QColor(Settings::values.players.GetValue()[player_index].body_color_right);
 }
 
 void PlayerControlPreview::ResetInputs() {
-    button_values.fill({
-        .value = false,
-    });
-    stick_values.fill({
-        .x = {.value = 0, .properties = {0, 1, 0}},
-        .y = {.value = 0, .properties = {0, 1, 0}},
-    });
-    trigger_values.fill({
-        .analog = {.value = 0, .properties = {0, 1, 0}},
-        .pressed = {.value = false},
-    });
+    for (std::size_t index = 0; index < button_values.size(); ++index) {
+        button_values[index] = false;
+    }
+
+    for (std::size_t index = 0; index < axis_values.size(); ++index) {
+        axis_values[index].properties = {0, 1, 0};
+        axis_values[index].value = {0, 0};
+        axis_values[index].raw_value = {0, 0};
+    }
     update();
 }
 
-void PlayerControlPreview::ControllerUpdate(Core::HID::ControllerTriggerType type) {
-    if (type == Core::HID::ControllerTriggerType::All) {
-        ControllerUpdate(Core::HID::ControllerTriggerType::Color);
-        ControllerUpdate(Core::HID::ControllerTriggerType::Type);
-        ControllerUpdate(Core::HID::ControllerTriggerType::Connected);
-        ControllerUpdate(Core::HID::ControllerTriggerType::Button);
-        ControllerUpdate(Core::HID::ControllerTriggerType::Stick);
-        ControllerUpdate(Core::HID::ControllerTriggerType::Trigger);
-        ControllerUpdate(Core::HID::ControllerTriggerType::Battery);
+void PlayerControlPreview::UpdateInput() {
+    if (!is_enabled && !mapping_active && !Settings::values.tas_enable) {
         return;
     }
-
-    switch (type) {
-    case Core::HID::ControllerTriggerType::Connected:
-        is_connected = true;
-        led_pattern = controller->GetLedPattern();
-        needs_redraw = true;
-        break;
-    case Core::HID::ControllerTriggerType::Disconnected:
-        is_connected = false;
-        led_pattern.raw = 0;
-        needs_redraw = true;
-        break;
-    case Core::HID::ControllerTriggerType::Type:
-        controller_type = controller->GetNpadStyleIndex(true);
-        needs_redraw = true;
-        break;
-    case Core::HID::ControllerTriggerType::Color:
-        UpdateColors();
-        needs_redraw = true;
-        break;
-    case Core::HID::ControllerTriggerType::Button:
-        button_values = controller->GetButtonsValues();
-        needs_redraw = true;
-        break;
-    case Core::HID::ControllerTriggerType::Stick:
-        using namespace Settings::NativeAnalog;
-        stick_values = controller->GetSticksValues();
-        // Y axis is inverted
-        stick_values[LStick].y.value = -stick_values[LStick].y.value;
-        stick_values[LStick].y.raw_value = -stick_values[LStick].y.raw_value;
-        stick_values[RStick].y.value = -stick_values[RStick].y.value;
-        stick_values[RStick].y.raw_value = -stick_values[RStick].y.raw_value;
-        needs_redraw = true;
-        break;
-    case Core::HID::ControllerTriggerType::Trigger:
-        trigger_values = controller->GetTriggersValues();
-        needs_redraw = true;
-        break;
-    case Core::HID::ControllerTriggerType::Battery:
-        battery_values = controller->GetBatteryValues();
-        needs_redraw = true;
-        break;
-    default:
-        break;
+    bool input_changed = false;
+    const auto& button_state = buttons;
+    for (std::size_t index = 0; index < button_values.size(); ++index) {
+        bool value = false;
+        if (index < Settings::NativeButton::BUTTON_NS_END) {
+            value = button_state[index]->GetStatus();
+        }
+        bool blink = mapping_active && index == button_mapping_index;
+        if (analog_mapping_index == Settings::NativeAnalog::NUM_STICKS_HID) {
+            blink &= blink_counter > 25;
+        }
+        if (button_values[index] != value || blink) {
+            input_changed = true;
+        }
+        button_values[index] = value || blink;
     }
-}
 
-void PlayerControlPreview::UpdateInput() {
-    if (mapping_active) {
+    const auto& analog_state = sticks;
+    for (std::size_t index = 0; index < axis_values.size(); ++index) {
+        const auto [stick_x_f, stick_y_f] = analog_state[index]->GetStatus();
+        const auto [stick_x_rf, stick_y_rf] = analog_state[index]->GetRawStatus();
 
-        for (std::size_t index = 0; index < button_values.size(); ++index) {
-            bool blink = index == button_mapping_index;
-            if (analog_mapping_index == Settings::NativeAnalog::NumAnalogs) {
-                blink &= blink_counter > 25;
-            }
-            if (button_values[index].value != blink) {
-                needs_redraw = true;
-            }
-            button_values[index].value = blink;
+        if (static_cast<int>(stick_x_rf * 45) !=
+                static_cast<int>(axis_values[index].raw_value.x() * 45) ||
+            static_cast<int>(-stick_y_rf * 45) !=
+                static_cast<int>(axis_values[index].raw_value.y() * 45)) {
+            input_changed = true;
         }
 
-        for (std::size_t index = 0; index < stick_values.size(); ++index) {
-            const bool blink_analog = index == analog_mapping_index;
-            if (blink_analog) {
-                needs_redraw = true;
-                stick_values[index].x.value = blink_counter < 25 ? -blink_counter / 25.0f : 0;
-                stick_values[index].y.value =
-                    blink_counter > 25 ? -(blink_counter - 25) / 25.0f : 0;
-            }
+        axis_values[index].properties = analog_state[index]->GetAnalogProperties();
+        axis_values[index].value = QPointF(stick_x_f, -stick_y_f);
+        axis_values[index].raw_value = QPointF(stick_x_rf, -stick_y_rf);
+
+        const bool blink_analog = mapping_active && index == analog_mapping_index;
+        if (blink_analog) {
+            input_changed = true;
+            axis_values[index].value =
+                QPointF(blink_counter < 25 ? -blink_counter / 25.0f : 0,
+                        blink_counter > 25 ? -(blink_counter - 25) / 25.0f : 0);
         }
     }
-    if (needs_redraw) {
+
+    if (input_changed) {
         update();
+        if (controller_callback.input != nullptr) {
+            ControllerInput input{
+                .axis_values = {std::pair<float, float>{
+                                    axis_values[Settings::NativeAnalog::LStick].value.x(),
+                                    axis_values[Settings::NativeAnalog::LStick].value.y()},
+                                std::pair<float, float>{
+                                    axis_values[Settings::NativeAnalog::RStick].value.x(),
+                                    axis_values[Settings::NativeAnalog::RStick].value.y()}},
+                .button_values = button_values,
+                .changed = true,
+            };
+            controller_callback.input(std::move(input));
+        }
     }
 
     if (mapping_active) {
@@ -215,6 +242,10 @@ void PlayerControlPreview::UpdateInput() {
     }
 }
 
+void PlayerControlPreview::SetCallBack(ControllerCallback callback_) {
+    controller_callback = std::move(callback_);
+}
+
 void PlayerControlPreview::paintEvent(QPaintEvent* event) {
     QFrame::paintEvent(event);
     QPainter p(this);
@@ -222,22 +253,22 @@ void PlayerControlPreview::paintEvent(QPaintEvent* event) {
     const QPointF center = rect().center();
 
     switch (controller_type) {
-    case Core::HID::NpadStyleIndex::Handheld:
+    case Settings::ControllerType::Handheld:
         DrawHandheldController(p, center);
         break;
-    case Core::HID::NpadStyleIndex::JoyconDual:
+    case Settings::ControllerType::DualJoyconDetached:
         DrawDualController(p, center);
         break;
-    case Core::HID::NpadStyleIndex::JoyconLeft:
+    case Settings::ControllerType::LeftJoycon:
         DrawLeftController(p, center);
         break;
-    case Core::HID::NpadStyleIndex::JoyconRight:
+    case Settings::ControllerType::RightJoycon:
         DrawRightController(p, center);
         break;
-    case Core::HID::NpadStyleIndex::GameCube:
+    case Settings::ControllerType::GameCube:
         DrawGCController(p, center);
         break;
-    case Core::HID::NpadStyleIndex::ProController:
+    case Settings::ControllerType::ProController:
     default:
         DrawProController(p, center);
         break;
@@ -250,7 +281,7 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
 
         // Sideview left joystick
         DrawJoystickSideview(p, center + QPoint(142, -69),
-                             -stick_values[Settings::NativeAnalog::LStick].y.value, 1.15f,
+                             -axis_values[Settings::NativeAnalog::LStick].value.y(), 1.15f,
                              button_values[LStick]);
 
         // Topview D-pad buttons
@@ -261,7 +292,7 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
 
         // Topview left joystick
         DrawJoystickSideview(p, center + QPointF(-140.5f, -28),
-                             -stick_values[Settings::NativeAnalog::LStick].x.value + 15.0f, 1.15f,
+                             -axis_values[Settings::NativeAnalog::LStick].value.x() + 15.0f, 1.15f,
                              button_values[LStick]);
 
         // Topview minus button
@@ -303,10 +334,8 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
     {
         // Draw joysticks
         using namespace Settings::NativeAnalog;
-        DrawJoystick(p,
-                     center + QPointF(9, -69) +
-                         (QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value) * 8),
-                     1.8f, button_values[Settings::NativeButton::LStick]);
+        DrawJoystick(p, center + QPointF(9, -69) + (axis_values[LStick].value * 8), 1.8f,
+                     button_values[Settings::NativeButton::LStick]);
         DrawRawJoystick(p, center + QPointF(-140, 90), QPointF(0, 0));
     }
 
@@ -355,10 +384,6 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
     p.setPen(colors.font2);
     p.setBrush(colors.font2);
     DrawCircle(p, center + QPoint(26, 71), 5);
-
-    // Draw battery
-    DrawBattery(p, center + QPoint(-170, -140),
-                battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
 }
 
 void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center) {
@@ -367,22 +392,20 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
 
         // Sideview right joystick
         DrawJoystickSideview(p, center + QPoint(173 - 315, 11),
-                             stick_values[Settings::NativeAnalog::RStick].y.value + 10.0f, 1.15f,
+                             axis_values[Settings::NativeAnalog::RStick].value.y() + 10.0f, 1.15f,
                              button_values[Settings::NativeButton::RStick]);
 
-        // Topview right joystick
-        DrawJoystickSideview(p, center + QPointF(140, -28),
-                             -stick_values[Settings::NativeAnalog::RStick].x.value + 15.0f, 1.15f,
-                             button_values[RStick]);
-
         // Topview face buttons
         p.setPen(colors.outline);
         button_color = colors.button;
         DrawRoundButton(p, center + QPoint(163, -21), button_values[A], 11, 5, Direction::Up);
-        DrawRoundButton(p, center + QPoint(140, -21), button_values[B], 11, 5, Direction::Up);
-        DrawRoundButton(p, center + QPoint(140, -21), button_values[X], 11, 5, Direction::Up);
         DrawRoundButton(p, center + QPoint(117, -21), button_values[Y], 11, 5, Direction::Up);
 
+        // Topview right joystick
+        DrawJoystickSideview(p, center + QPointF(140, -28),
+                             -axis_values[Settings::NativeAnalog::RStick].value.x() + 15.0f, 1.15f,
+                             button_values[RStick]);
+
         // Topview plus button
         p.setPen(colors.outline);
         button_color = colors.button;
@@ -425,10 +448,8 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
     {
         // Draw joysticks
         using namespace Settings::NativeAnalog;
-        DrawJoystick(p,
-                     center + QPointF(-9, 11) +
-                         (QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value) * 8),
-                     1.8f, button_values[Settings::NativeButton::RStick]);
+        DrawJoystick(p, center + QPointF(-9, 11) + (axis_values[RStick].value * 8), 1.8f,
+                     button_values[Settings::NativeButton::RStick]);
         DrawRawJoystick(p, QPointF(0, 0), center + QPointF(140, 90));
     }
 
@@ -482,10 +503,6 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
     p.setPen(colors.transparent);
     p.setBrush(colors.font2);
     DrawSymbol(p, center + QPoint(-26, 66), Symbol::House, 5);
-
-    // Draw battery
-    DrawBattery(p, center + QPoint(110, -140),
-                battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
 }
 
 void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) {
@@ -495,19 +512,17 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
         // Left/Right trigger
         DrawDualTriggers(p, center, button_values[L], button_values[R]);
 
-        // Topview right joystick
-        DrawJoystickSideview(p, center + QPointF(180, -78),
-                             -stick_values[Settings::NativeAnalog::RStick].x.value + 15.0f, 1,
-                             button_values[RStick]);
-
         // Topview face buttons
         p.setPen(colors.outline);
         button_color = colors.button;
         DrawRoundButton(p, center + QPoint(200, -71), button_values[A], 10, 5, Direction::Up);
-        DrawRoundButton(p, center + QPoint(180, -71), button_values[B], 10, 5, Direction::Up);
-        DrawRoundButton(p, center + QPoint(180, -71), button_values[X], 10, 5, Direction::Up);
         DrawRoundButton(p, center + QPoint(160, -71), button_values[Y], 10, 5, Direction::Up);
 
+        // Topview right joystick
+        DrawJoystickSideview(p, center + QPointF(180, -78),
+                             -axis_values[Settings::NativeAnalog::RStick].value.x() + 15.0f, 1,
+                             button_values[RStick]);
+
         // Topview plus button
         p.setPen(colors.outline);
         button_color = colors.button;
@@ -523,7 +538,7 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
 
         // Topview left joystick
         DrawJoystickSideview(p, center + QPointF(-180.5f, -78),
-                             -stick_values[Settings::NativeAnalog::LStick].x.value + 15.0f, 1,
+                             -axis_values[Settings::NativeAnalog::LStick].value.x() + 15.0f, 1,
                              button_values[LStick]);
 
         // Topview minus button
@@ -542,13 +557,13 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
     {
         // Draw joysticks
         using namespace Settings::NativeAnalog;
-        const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value);
+        const auto& l_stick = axis_values[LStick];
         const auto l_button = button_values[Settings::NativeButton::LStick];
-        const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value);
+        const auto& r_stick = axis_values[RStick];
         const auto r_button = button_values[Settings::NativeButton::RStick];
 
-        DrawJoystick(p, center + QPointF(-65, -65) + (l_stick * 7), 1.62f, l_button);
-        DrawJoystick(p, center + QPointF(65, 12) + (r_stick * 7), 1.62f, r_button);
+        DrawJoystick(p, center + QPointF(-65, -65) + (l_stick.value * 7), 1.62f, l_button);
+        DrawJoystick(p, center + QPointF(65, 12) + (r_stick.value * 7), 1.62f, r_button);
         DrawRawJoystick(p, center + QPointF(-180, 90), center + QPointF(180, 90));
     }
 
@@ -619,12 +634,6 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
     p.setPen(colors.transparent);
     p.setBrush(colors.font2);
     DrawSymbol(p, center + QPoint(50, 60), Symbol::House, 4.2f);
-
-    // Draw battery
-    DrawBattery(p, center + QPoint(-100, -160),
-                battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
-    DrawBattery(p, center + QPoint(40, -160),
-                battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
 }
 
 void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF center) {
@@ -634,13 +643,13 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
     {
         // Draw joysticks
         using namespace Settings::NativeAnalog;
-        const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value);
+        const auto& l_stick = axis_values[LStick];
         const auto l_button = button_values[Settings::NativeButton::LStick];
-        const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value);
+        const auto& r_stick = axis_values[RStick];
         const auto r_button = button_values[Settings::NativeButton::RStick];
 
-        DrawJoystick(p, center + QPointF(-171, -41) + (l_stick * 4), 1.0f, l_button);
-        DrawJoystick(p, center + QPointF(171, 8) + (r_stick * 4), 1.0f, r_button);
+        DrawJoystick(p, center + QPointF(-171, -41) + (l_stick.value * 4), 1.0f, l_button);
+        DrawJoystick(p, center + QPointF(171, 8) + (r_stick.value * 4), 1.0f, r_button);
         DrawRawJoystick(p, center + QPointF(-50, 0), center + QPointF(50, 0));
     }
 
@@ -723,12 +732,6 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
     p.setPen(colors.transparent);
     p.setBrush(colors.font2);
     DrawSymbol(p, center + QPoint(161, 37), Symbol::House, 2.75f);
-
-    // Draw battery
-    DrawBattery(p, center + QPoint(-200, 110),
-                battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
-    DrawBattery(p, center + QPoint(130, 110),
-                battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
 }
 
 void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center) {
@@ -738,11 +741,9 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
     {
         // Draw joysticks
         using namespace Settings::NativeAnalog;
-        const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value);
-        const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value);
-        DrawProJoystick(p, center + QPointF(-111, -55), l_stick, 11,
+        DrawProJoystick(p, center + QPointF(-111, -55), axis_values[LStick].value, 11,
                         button_values[Settings::NativeButton::LStick]);
-        DrawProJoystick(p, center + QPointF(51, 0), r_stick, 11,
+        DrawProJoystick(p, center + QPointF(51, 0), axis_values[RStick].value, 11,
                         button_values[Settings::NativeButton::RStick]);
         DrawRawJoystick(p, center + QPointF(-50, 105), center + QPointF(50, 105));
     }
@@ -816,27 +817,24 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
     p.setPen(colors.transparent);
     p.setBrush(colors.font2);
     DrawSymbol(p, center + QPoint(29, -56), Symbol::House, 3.9f);
-
-    // Draw battery
-    DrawBattery(p, center + QPoint(-30, -160),
-                battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
 }
 
 void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) {
-    DrawGCTriggers(p, center, trigger_values[0], trigger_values[1]);
+    DrawGCTriggers(p, center, button_values[Settings::NativeButton::ZL],
+                   button_values[Settings::NativeButton::ZR]);
     DrawGCButtonZ(p, center, button_values[Settings::NativeButton::R]);
     DrawGCBody(p, center);
     {
         // Draw joysticks
         using namespace Settings::NativeAnalog;
-        const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value);
-        const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value);
-        DrawGCJoystick(p, center + QPointF(-111, -44) + (l_stick * 10), {});
+        DrawGCJoystick(p, center + QPointF(-111, -44) + (axis_values[LStick].value * 10), false);
         button_color = colors.button2;
-        DrawCircleButton(p, center + QPointF(61, 37) + (r_stick * 9.5f), {}, 15);
+        DrawCircleButton(p, center + QPointF(61, 37) + (axis_values[RStick].value * 9.5f), false,
+                         15);
         p.setPen(colors.transparent);
         p.setBrush(colors.font);
-        DrawSymbol(p, center + QPointF(61, 37) + (r_stick * 9.5f), Symbol::C, 1.0f);
+        DrawSymbol(p, center + QPointF(61, 37) + (axis_values[RStick].value * 9.5f), Symbol::C,
+                   1.0f);
         DrawRawJoystick(p, center + QPointF(-198, -125), center + QPointF(198, -125));
     }
 
@@ -873,10 +871,6 @@ void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) {
     // Minus and Plus buttons
     p.setPen(colors.outline);
     DrawCircleButton(p, center + QPoint(0, -44), button_values[Plus], 8);
-
-    // Draw battery
-    DrawBattery(p, center + QPoint(-30, -165),
-                battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
 }
 
 constexpr std::array<float, 13 * 2> symbol_a = {
@@ -1843,14 +1837,10 @@ void PlayerControlPreview::DrawLeftBody(QPainter& p, const QPointF center) {
     const float led_size = 5.0f;
     const QPointF led_position = sideview_center + QPointF(0, -36);
     int led_count = 0;
-    p.setBrush(led_pattern.position1 ? colors.led_on : colors.led_off);
-    DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
-    p.setBrush(led_pattern.position2 ? colors.led_on : colors.led_off);
-    DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
-    p.setBrush(led_pattern.position3 ? colors.led_on : colors.led_off);
-    DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
-    p.setBrush(led_pattern.position4 ? colors.led_on : colors.led_off);
-    DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
+    for (const auto& color : led_color) {
+        p.setBrush(color);
+        DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
+    }
 }
 
 void PlayerControlPreview::DrawRightBody(QPainter& p, const QPointF center) {
@@ -1943,19 +1933,14 @@ void PlayerControlPreview::DrawRightBody(QPainter& p, const QPointF center) {
     const float led_size = 5.0f;
     const QPointF led_position = sideview_center + QPointF(0, -36);
     int led_count = 0;
-    p.setBrush(led_pattern.position1 ? colors.led_on : colors.led_off);
-    DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
-    p.setBrush(led_pattern.position2 ? colors.led_on : colors.led_off);
-    DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
-    p.setBrush(led_pattern.position3 ? colors.led_on : colors.led_off);
-    DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
-    p.setBrush(led_pattern.position4 ? colors.led_on : colors.led_off);
-    DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
+    for (const auto& color : led_color) {
+        p.setBrush(color);
+        DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
+    }
 }
 
-void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center,
-                                           const Common::Input::ButtonStatus& left_pressed,
-                                           const Common::Input::ButtonStatus& right_pressed) {
+void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center, bool left_pressed,
+                                           bool right_pressed) {
     std::array<QPointF, pro_left_trigger.size() / 2> qleft_trigger;
     std::array<QPointF, pro_left_trigger.size() / 2> qright_trigger;
     std::array<QPointF, pro_body_top.size()> qbody_top;
@@ -1964,10 +1949,8 @@ void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center,
         const float trigger_x = pro_left_trigger[point * 2 + 0];
         const float trigger_y = pro_left_trigger[point * 2 + 1];
 
-        qleft_trigger[point] =
-            center + QPointF(trigger_x, trigger_y + (left_pressed.value ? 2 : 0));
-        qright_trigger[point] =
-            center + QPointF(-trigger_x, trigger_y + (right_pressed.value ? 2 : 0));
+        qleft_trigger[point] = center + QPointF(trigger_x, trigger_y + (left_pressed ? 2 : 0));
+        qright_trigger[point] = center + QPointF(-trigger_x, trigger_y + (right_pressed ? 2 : 0));
     }
 
     for (std::size_t point = 0; point < pro_body_top.size() / 2; ++point) {
@@ -1984,17 +1967,16 @@ void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center,
     DrawPolygon(p, qbody_top);
 
     // Left trigger
-    p.setBrush(left_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(left_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qleft_trigger);
 
     // Right trigger
-    p.setBrush(right_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(right_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qright_trigger);
 }
 
-void PlayerControlPreview::DrawGCTriggers(QPainter& p, const QPointF center,
-                                          Common::Input::TriggerStatus left_trigger,
-                                          Common::Input::TriggerStatus right_trigger) {
+void PlayerControlPreview::DrawGCTriggers(QPainter& p, const QPointF center, bool left_pressed,
+                                          bool right_pressed) {
     std::array<QPointF, left_gc_trigger.size() / 2> qleft_trigger;
     std::array<QPointF, left_gc_trigger.size() / 2> qright_trigger;
 
@@ -2002,37 +1984,32 @@ void PlayerControlPreview::DrawGCTriggers(QPainter& p, const QPointF center,
         const float trigger_x = left_gc_trigger[point * 2 + 0];
         const float trigger_y = left_gc_trigger[point * 2 + 1];
 
-        qleft_trigger[point] =
-            center + QPointF(trigger_x, trigger_y + (left_trigger.analog.value * 10.0f));
-        qright_trigger[point] =
-            center + QPointF(-trigger_x, trigger_y + (right_trigger.analog.value * 10.0f));
+        qleft_trigger[point] = center + QPointF(trigger_x, trigger_y + (left_pressed ? 10 : 0));
+        qright_trigger[point] = center + QPointF(-trigger_x, trigger_y + (right_pressed ? 10 : 0));
     }
 
     // Left trigger
     p.setPen(colors.outline);
-    p.setBrush(left_trigger.pressed.value ? colors.highlight : colors.button);
+    p.setBrush(left_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qleft_trigger);
 
     // Right trigger
-    p.setBrush(right_trigger.pressed.value ? colors.highlight : colors.button);
+    p.setBrush(right_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qright_trigger);
 
     // Draw L text
     p.setPen(colors.transparent);
     p.setBrush(colors.font);
-    DrawSymbol(p, center + QPointF(-132, -119 + (left_trigger.analog.value * 10.0f)), Symbol::L,
-               1.7f);
+    DrawSymbol(p, center + QPointF(-132, -119 + (left_pressed ? 10 : 0)), Symbol::L, 1.7f);
 
     // Draw R text
     p.setPen(colors.transparent);
     p.setBrush(colors.font);
-    DrawSymbol(p, center + QPointF(121.5f, -119 + (right_trigger.analog.value * 10.0f)), Symbol::R,
-               1.7f);
+    DrawSymbol(p, center + QPointF(121.5f, -119 + (right_pressed ? 10 : 0)), Symbol::R, 1.7f);
 }
 
 void PlayerControlPreview::DrawHandheldTriggers(QPainter& p, const QPointF center,
-                                                const Common::Input::ButtonStatus& left_pressed,
-                                                const Common::Input::ButtonStatus& right_pressed) {
+                                                bool left_pressed, bool right_pressed) {
     std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger;
     std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger;
 
@@ -2041,24 +2018,23 @@ void PlayerControlPreview::DrawHandheldTriggers(QPainter& p, const QPointF cente
         const float left_trigger_y = left_joycon_trigger[point * 2 + 1];
 
         qleft_trigger[point] =
-            center + QPointF(left_trigger_x, left_trigger_y + (left_pressed.value ? 0.5f : 0));
+            center + QPointF(left_trigger_x, left_trigger_y + (left_pressed ? 0.5f : 0));
         qright_trigger[point] =
-            center + QPointF(-left_trigger_x, left_trigger_y + (right_pressed.value ? 0.5f : 0));
+            center + QPointF(-left_trigger_x, left_trigger_y + (right_pressed ? 0.5f : 0));
     }
 
     // Left trigger
     p.setPen(colors.outline);
-    p.setBrush(left_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(left_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qleft_trigger);
 
     // Right trigger
-    p.setBrush(right_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(right_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qright_trigger);
 }
 
-void PlayerControlPreview::DrawDualTriggers(QPainter& p, const QPointF center,
-                                            const Common::Input::ButtonStatus& left_pressed,
-                                            const Common::Input::ButtonStatus& right_pressed) {
+void PlayerControlPreview::DrawDualTriggers(QPainter& p, const QPointF center, bool left_pressed,
+                                            bool right_pressed) {
     std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger;
     std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger;
     constexpr float size = 1.62f;
@@ -2067,27 +2043,25 @@ void PlayerControlPreview::DrawDualTriggers(QPainter& p, const QPointF center,
         const float left_trigger_x = left_joycon_trigger[point * 2 + 0];
         const float left_trigger_y = left_joycon_trigger[point * 2 + 1];
 
-        qleft_trigger[point] =
-            center + QPointF(left_trigger_x * size + offset,
-                             left_trigger_y * size + (left_pressed.value ? 0.5f : 0));
+        qleft_trigger[point] = center + QPointF(left_trigger_x * size + offset,
+                                                left_trigger_y * size + (left_pressed ? 0.5f : 0));
         qright_trigger[point] =
             center + QPointF(-left_trigger_x * size - offset,
-                             left_trigger_y * size + (right_pressed.value ? 0.5f : 0));
+                             left_trigger_y * size + (right_pressed ? 0.5f : 0));
     }
 
     // Left trigger
     p.setPen(colors.outline);
-    p.setBrush(left_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(left_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qleft_trigger);
 
     // Right trigger
-    p.setBrush(right_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(right_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qright_trigger);
 }
 
-void PlayerControlPreview::DrawDualTriggersTopView(
-    QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed,
-    const Common::Input::ButtonStatus& right_pressed) {
+void PlayerControlPreview::DrawDualTriggersTopView(QPainter& p, const QPointF center,
+                                                   bool left_pressed, bool right_pressed) {
     std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger;
     std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger;
     constexpr float size = 0.9f;
@@ -2106,9 +2080,9 @@ void PlayerControlPreview::DrawDualTriggersTopView(
     }
 
     p.setPen(colors.outline);
-    p.setBrush(left_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(left_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qleft_trigger);
-    p.setBrush(right_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(right_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qright_trigger);
 
     // Draw L text
@@ -2122,9 +2096,8 @@ void PlayerControlPreview::DrawDualTriggersTopView(
     DrawSymbol(p, center + QPointF(177, -84), Symbol::R, 1.0f);
 }
 
-void PlayerControlPreview::DrawDualZTriggersTopView(
-    QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed,
-    const Common::Input::ButtonStatus& right_pressed) {
+void PlayerControlPreview::DrawDualZTriggersTopView(QPainter& p, const QPointF center,
+                                                    bool left_pressed, bool right_pressed) {
     std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger;
     std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger;
     constexpr float size = 0.9f;
@@ -2141,9 +2114,9 @@ void PlayerControlPreview::DrawDualZTriggersTopView(
     }
 
     p.setPen(colors.outline);
-    p.setBrush(left_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(left_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qleft_trigger);
-    p.setBrush(right_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(right_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qright_trigger);
 
     // Draw ZL text
@@ -2157,8 +2130,7 @@ void PlayerControlPreview::DrawDualZTriggersTopView(
     DrawSymbol(p, center + QPointF(180, -113), Symbol::ZR, 1.0f);
 }
 
-void PlayerControlPreview::DrawLeftTriggers(QPainter& p, const QPointF center,
-                                            const Common::Input::ButtonStatus& left_pressed) {
+void PlayerControlPreview::DrawLeftTriggers(QPainter& p, const QPointF center, bool left_pressed) {
     std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger;
     constexpr float size = 1.78f;
     constexpr float offset = 311.5f;
@@ -2166,16 +2138,15 @@ void PlayerControlPreview::DrawLeftTriggers(QPainter& p, const QPointF center,
     for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) {
         qleft_trigger[point] = center + QPointF(left_joycon_trigger[point * 2] * size + offset,
                                                 left_joycon_trigger[point * 2 + 1] * size -
-                                                    (left_pressed.value ? 0.5f : 1.0f));
+                                                    (left_pressed ? 0.5f : 1.0f));
     }
 
     p.setPen(colors.outline);
-    p.setBrush(left_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(left_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qleft_trigger);
 }
 
-void PlayerControlPreview::DrawLeftZTriggers(QPainter& p, const QPointF center,
-                                             const Common::Input::ButtonStatus& left_pressed) {
+void PlayerControlPreview::DrawLeftZTriggers(QPainter& p, const QPointF center, bool left_pressed) {
     std::array<QPointF, left_joycon_sideview_zl.size() / 2> qleft_trigger;
     constexpr float size = 1.1115f;
     constexpr float offset2 = 335;
@@ -2183,18 +2154,18 @@ void PlayerControlPreview::DrawLeftZTriggers(QPainter& p, const QPointF center,
     for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) {
         qleft_trigger[point] = center + QPointF(left_joycon_sideview_zl[point * 2] * size + offset2,
                                                 left_joycon_sideview_zl[point * 2 + 1] * size +
-                                                    (left_pressed.value ? 1.5f : 1.0f));
+                                                    (left_pressed ? 1.5f : 1.0f));
     }
 
     p.setPen(colors.outline);
-    p.setBrush(left_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(left_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qleft_trigger);
-    p.drawArc(center.x() + 158, center.y() + (left_pressed.value ? -203.5f : -204.0f), 77, 77,
-              225 * 16, 44 * 16);
+    p.drawArc(center.x() + 158, center.y() + (left_pressed ? -203.5f : -204.0f), 77, 77, 225 * 16,
+              44 * 16);
 }
 
-void PlayerControlPreview::DrawLeftTriggersTopView(
-    QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed) {
+void PlayerControlPreview::DrawLeftTriggersTopView(QPainter& p, const QPointF center,
+                                                   bool left_pressed) {
     std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger;
 
     for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) {
@@ -2203,7 +2174,7 @@ void PlayerControlPreview::DrawLeftTriggersTopView(
     }
 
     p.setPen(colors.outline);
-    p.setBrush(left_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(left_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qleft_trigger);
 
     // Draw L text
@@ -2212,8 +2183,8 @@ void PlayerControlPreview::DrawLeftTriggersTopView(
     DrawSymbol(p, center + QPointF(-143, -36), Symbol::L, 1.0f);
 }
 
-void PlayerControlPreview::DrawLeftZTriggersTopView(
-    QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed) {
+void PlayerControlPreview::DrawLeftZTriggersTopView(QPainter& p, const QPointF center,
+                                                    bool left_pressed) {
     std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger;
 
     for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) {
@@ -2222,7 +2193,7 @@ void PlayerControlPreview::DrawLeftZTriggersTopView(
     }
 
     p.setPen(colors.outline);
-    p.setBrush(left_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(left_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qleft_trigger);
 
     // Draw ZL text
@@ -2232,7 +2203,7 @@ void PlayerControlPreview::DrawLeftZTriggersTopView(
 }
 
 void PlayerControlPreview::DrawRightTriggers(QPainter& p, const QPointF center,
-                                             const Common::Input::ButtonStatus& right_pressed) {
+                                             bool right_pressed) {
     std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger;
     constexpr float size = 1.78f;
     constexpr float offset = 311.5f;
@@ -2240,36 +2211,36 @@ void PlayerControlPreview::DrawRightTriggers(QPainter& p, const QPointF center,
     for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) {
         qright_trigger[point] = center + QPointF(-left_joycon_trigger[point * 2] * size - offset,
                                                  left_joycon_trigger[point * 2 + 1] * size -
-                                                     (right_pressed.value ? 0.5f : 1.0f));
+                                                     (right_pressed ? 0.5f : 1.0f));
     }
 
     p.setPen(colors.outline);
-    p.setBrush(right_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(right_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qright_trigger);
 }
 
 void PlayerControlPreview::DrawRightZTriggers(QPainter& p, const QPointF center,
-                                              const Common::Input::ButtonStatus& right_pressed) {
+                                              bool right_pressed) {
     std::array<QPointF, left_joycon_sideview_zl.size() / 2> qright_trigger;
     constexpr float size = 1.1115f;
     constexpr float offset2 = 335;
 
     for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) {
         qright_trigger[point] =
-            center + QPointF(-left_joycon_sideview_zl[point * 2] * size - offset2,
-                             left_joycon_sideview_zl[point * 2 + 1] * size +
-                                 (right_pressed.value ? 0.5f : 0) + 1);
+            center +
+            QPointF(-left_joycon_sideview_zl[point * 2] * size - offset2,
+                    left_joycon_sideview_zl[point * 2 + 1] * size + (right_pressed ? 0.5f : 0) + 1);
     }
 
     p.setPen(colors.outline);
-    p.setBrush(right_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(right_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qright_trigger);
-    p.drawArc(center.x() - 236, center.y() + (right_pressed.value ? -203.5f : -204.0f), 77, 77,
-              271 * 16, 44 * 16);
+    p.drawArc(center.x() - 236, center.y() + (right_pressed ? -203.5f : -204.0f), 77, 77, 271 * 16,
+              44 * 16);
 }
 
-void PlayerControlPreview::DrawRightTriggersTopView(
-    QPainter& p, const QPointF center, const Common::Input::ButtonStatus& right_pressed) {
+void PlayerControlPreview::DrawRightTriggersTopView(QPainter& p, const QPointF center,
+                                                    bool right_pressed) {
     std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger;
 
     for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) {
@@ -2278,7 +2249,7 @@ void PlayerControlPreview::DrawRightTriggersTopView(
     }
 
     p.setPen(colors.outline);
-    p.setBrush(right_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(right_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qright_trigger);
 
     // Draw R text
@@ -2287,8 +2258,8 @@ void PlayerControlPreview::DrawRightTriggersTopView(
     DrawSymbol(p, center + QPointF(137, -36), Symbol::R, 1.0f);
 }
 
-void PlayerControlPreview::DrawRightZTriggersTopView(
-    QPainter& p, const QPointF center, const Common::Input::ButtonStatus& right_pressed) {
+void PlayerControlPreview::DrawRightZTriggersTopView(QPainter& p, const QPointF center,
+                                                     bool right_pressed) {
     std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger;
 
     for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) {
@@ -2297,7 +2268,7 @@ void PlayerControlPreview::DrawRightZTriggersTopView(
     }
 
     p.setPen(colors.outline);
-    p.setBrush(right_pressed.value ? colors.highlight : colors.button);
+    p.setBrush(right_pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qright_trigger);
 
     // Draw ZR text
@@ -2307,13 +2278,13 @@ void PlayerControlPreview::DrawRightZTriggersTopView(
 }
 
 void PlayerControlPreview::DrawJoystick(QPainter& p, const QPointF center, float size,
-                                        const Common::Input::ButtonStatus& pressed) {
+                                        bool pressed) {
     const float radius1 = 13.0f * size;
     const float radius2 = 9.0f * size;
 
     // Outer circle
     p.setPen(colors.outline);
-    p.setBrush(pressed.value ? colors.highlight : colors.button);
+    p.setBrush(pressed ? colors.highlight : colors.button);
     DrawCircle(p, center, radius1);
 
     // Cross
@@ -2321,18 +2292,17 @@ void PlayerControlPreview::DrawJoystick(QPainter& p, const QPointF center, float
     p.drawLine(center - QPoint(0, radius1), center + QPoint(0, radius1));
 
     // Inner circle
-    p.setBrush(pressed.value ? colors.highlight2 : colors.button2);
+    p.setBrush(pressed ? colors.highlight2 : colors.button2);
     DrawCircle(p, center, radius2);
 }
 
 void PlayerControlPreview::DrawJoystickSideview(QPainter& p, const QPointF center, float angle,
-                                                float size,
-                                                const Common::Input::ButtonStatus& pressed) {
+                                                float size, bool pressed) {
     QVector<QPointF> joystick;
     joystick.reserve(static_cast<int>(left_joystick_sideview.size() / 2));
 
     for (std::size_t point = 0; point < left_joystick_sideview.size() / 2; ++point) {
-        joystick.append(QPointF(left_joystick_sideview[point * 2] * size + (pressed.value ? 1 : 0),
+        joystick.append(QPointF(left_joystick_sideview[point * 2] * size + (pressed ? 1 : 0),
                                 left_joystick_sideview[point * 2 + 1] * size - 1));
     }
 
@@ -2344,15 +2314,14 @@ void PlayerControlPreview::DrawJoystickSideview(QPainter& p, const QPointF cente
 
     // Draw joystick
     p.setPen(colors.outline);
-    p.setBrush(pressed.value ? colors.highlight : colors.button);
+    p.setBrush(pressed ? colors.highlight : colors.button);
     p.drawPolygon(p2);
     p.drawLine(p2.at(1), p2.at(30));
     p.drawLine(p2.at(32), p2.at(71));
 }
 
 void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, const QPointF offset,
-                                           float offset_scalar,
-                                           const Common::Input::ButtonStatus& pressed) {
+                                           float offset_scalar, bool pressed) {
     const float radius1 = 24.0f;
     const float radius2 = 17.0f;
 
@@ -2370,11 +2339,11 @@ void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, co
 
     // Outer circle
     p.setPen(colors.outline);
-    p.setBrush(pressed.value ? colors.highlight : colors.button);
+    p.setBrush(pressed ? colors.highlight : colors.button);
     p.drawEllipse(QPointF(0, 0), radius1 * amplitude, radius1);
 
     // Inner circle
-    p.setBrush(pressed.value ? colors.highlight2 : colors.button2);
+    p.setBrush(pressed ? colors.highlight2 : colors.button2);
 
     const float inner_offset =
         (radius1 - radius2) * 0.4f * ((offset.x() == 0 && offset.y() < 0) ? -1.0f : 1.0f);
@@ -2386,15 +2355,14 @@ void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, co
     p.restore();
 }
 
-void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center,
-                                          const Common::Input::ButtonStatus& pressed) {
+void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center, bool pressed) {
     // Outer circle
     p.setPen(colors.outline);
-    p.setBrush(pressed.value ? colors.highlight : colors.button);
+    p.setBrush(pressed ? colors.highlight : colors.button);
     DrawCircle(p, center, 26.0f);
 
     // Inner circle
-    p.setBrush(pressed.value ? colors.highlight2 : colors.button2);
+    p.setBrush(pressed ? colors.highlight2 : colors.button2);
     DrawCircle(p, center, 19.0f);
     p.setBrush(colors.transparent);
     DrawCircle(p, center, 13.5f);
@@ -2403,29 +2371,31 @@ void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center,
 
 void PlayerControlPreview::DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right) {
     using namespace Settings::NativeAnalog;
-    if (center_right != QPointF(0, 0)) {
-        DrawJoystickProperties(p, center_right, stick_values[RStick].x.properties);
+    if (controller_type != Settings::ControllerType::LeftJoycon) {
+        DrawJoystickProperties(p, center_right, axis_values[RStick].properties);
         p.setPen(colors.indicator);
         p.setBrush(colors.indicator);
-        DrawJoystickDot(p, center_right, stick_values[RStick], true);
+        DrawJoystickDot(p, center_right, axis_values[RStick].raw_value,
+                        axis_values[RStick].properties);
         p.setPen(colors.indicator2);
         p.setBrush(colors.indicator2);
-        DrawJoystickDot(p, center_right, stick_values[RStick], false);
+        DrawJoystickDot(p, center_right, axis_values[RStick].value, axis_values[RStick].properties);
     }
 
-    if (center_left != QPointF(0, 0)) {
-        DrawJoystickProperties(p, center_left, stick_values[LStick].x.properties);
+    if (controller_type != Settings::ControllerType::RightJoycon) {
+        DrawJoystickProperties(p, center_left, axis_values[LStick].properties);
         p.setPen(colors.indicator);
         p.setBrush(colors.indicator);
-        DrawJoystickDot(p, center_left, stick_values[LStick], true);
+        DrawJoystickDot(p, center_left, axis_values[LStick].raw_value,
+                        axis_values[LStick].properties);
         p.setPen(colors.indicator2);
         p.setBrush(colors.indicator2);
-        DrawJoystickDot(p, center_left, stick_values[LStick], false);
+        DrawJoystickDot(p, center_left, axis_values[LStick].value, axis_values[LStick].properties);
     }
 }
 
-void PlayerControlPreview::DrawJoystickProperties(
-    QPainter& p, const QPointF center, const Common::Input::AnalogProperties& properties) {
+void PlayerControlPreview::DrawJoystickProperties(QPainter& p, const QPointF center,
+                                                  const Input::AnalogProperties& properties) {
     constexpr float size = 45.0f;
     const float range = size * properties.range;
     const float deadzone = size * properties.deadzone;
@@ -2444,26 +2414,19 @@ void PlayerControlPreview::DrawJoystickProperties(
     DrawCircle(p, center, deadzone);
 }
 
-void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center,
-                                           const Common::Input::StickStatus& stick, bool raw) {
+void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center, const QPointF value,
+                                           const Input::AnalogProperties& properties) {
     constexpr float size = 45.0f;
-    const float range = size * stick.x.properties.range;
+    const float range = size * properties.range;
 
-    if (raw) {
-        const QPointF value = QPointF(stick.x.raw_value, stick.y.raw_value) * size;
-        DrawCircle(p, center + value, 2);
-        return;
-    }
-
-    const QPointF value = QPointF(stick.x.value, stick.y.value) * range;
-    DrawCircle(p, center + value, 2);
+    // Dot pointer
+    DrawCircle(p, center + (value * range), 2);
 }
 
-void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center,
-                                           const Common::Input::ButtonStatus& pressed, float width,
+void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center, bool pressed, float width,
                                            float height, Direction direction, float radius) {
     p.setBrush(button_color);
-    if (pressed.value) {
+    if (pressed) {
         switch (direction) {
         case Direction::Left:
             center.setX(center.x() - 1);
@@ -2485,19 +2448,17 @@ void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center,
     QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f};
     p.drawRoundedRect(rect, radius, radius);
 }
-void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center,
-                                           const Common::Input::ButtonStatus& pressed,
+void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center, bool pressed,
                                            int button_size) {
     p.setPen(colors.outline);
-    p.setBrush(pressed.value ? colors.highlight : colors.button);
+    p.setBrush(pressed ? colors.highlight : colors.button);
     DrawRectangle(p, center, button_size, button_size / 3.0f);
 }
-void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center,
-                                          const Common::Input::ButtonStatus& pressed,
+void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center, bool pressed,
                                           int button_size) {
     // Draw outer line
     p.setPen(colors.outline);
-    p.setBrush(pressed.value ? colors.highlight : colors.button);
+    p.setBrush(pressed ? colors.highlight : colors.button);
     DrawRectangle(p, center, button_size, button_size / 3.0f);
     DrawRectangle(p, center, button_size / 3.0f, button_size);
 
@@ -2510,8 +2471,7 @@ void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center,
     DrawRectangle(p, center, button_size / 3.0f, button_size);
 }
 
-void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center,
-                                         const Common::Input::ButtonStatus& pressed) {
+void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center, bool pressed) {
     std::array<QPointF, gc_button_x.size() / 2> button_x;
 
     for (std::size_t point = 0; point < gc_button_x.size() / 2; ++point) {
@@ -2519,12 +2479,11 @@ void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center,
     }
 
     p.setPen(colors.outline);
-    p.setBrush(pressed.value ? colors.highlight : colors.button);
+    p.setBrush(pressed ? colors.highlight : colors.button);
     DrawPolygon(p, button_x);
 }
 
-void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center,
-                                         const Common::Input::ButtonStatus& pressed) {
+void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center, bool pressed) {
     std::array<QPointF, gc_button_y.size() / 2> button_x;
 
     for (std::size_t point = 0; point < gc_button_y.size() / 2; ++point) {
@@ -2532,29 +2491,27 @@ void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center,
     }
 
     p.setPen(colors.outline);
-    p.setBrush(pressed.value ? colors.highlight : colors.button);
+    p.setBrush(pressed ? colors.highlight : colors.button);
     DrawPolygon(p, button_x);
 }
 
-void PlayerControlPreview::DrawGCButtonZ(QPainter& p, const QPointF center,
-                                         const Common::Input::ButtonStatus& pressed) {
+void PlayerControlPreview::DrawGCButtonZ(QPainter& p, const QPointF center, bool pressed) {
     std::array<QPointF, gc_button_z.size() / 2> button_x;
 
     for (std::size_t point = 0; point < gc_button_z.size() / 2; ++point) {
         button_x[point] = center + QPointF(gc_button_z[point * 2],
-                                           gc_button_z[point * 2 + 1] + (pressed.value ? 1 : 0));
+                                           gc_button_z[point * 2 + 1] + (pressed ? 1 : 0));
     }
 
     p.setPen(colors.outline);
-    p.setBrush(pressed.value ? colors.highlight : colors.button2);
+    p.setBrush(pressed ? colors.highlight : colors.button2);
     DrawPolygon(p, button_x);
 }
 
-void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center,
-                                            const Common::Input::ButtonStatus& pressed,
+void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center, bool pressed,
                                             float button_size) {
     p.setBrush(button_color);
-    if (pressed.value) {
+    if (pressed) {
         p.setBrush(colors.highlight);
     }
     p.drawEllipse(center, button_size, button_size);
@@ -2583,8 +2540,7 @@ void PlayerControlPreview::DrawArrowButtonOutline(QPainter& p, const QPointF cen
 }
 
 void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center,
-                                           const Direction direction,
-                                           const Common::Input::ButtonStatus& pressed, float size) {
+                                           const Direction direction, bool pressed, float size) {
     std::array<QPointF, up_arrow_button.size() / 2> arrow_button;
     QPoint offset;
 
@@ -2596,39 +2552,38 @@ void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center,
         case Direction::Up:
             arrow_button[point] = center + QPointF(up_arrow_x * size, up_arrow_y * size);
             break;
+        case Direction::Left:
+            arrow_button[point] = center + QPointF(up_arrow_y * size, up_arrow_x * size);
+            break;
         case Direction::Right:
             arrow_button[point] = center + QPointF(-up_arrow_y * size, up_arrow_x * size);
             break;
         case Direction::Down:
             arrow_button[point] = center + QPointF(up_arrow_x * size, -up_arrow_y * size);
             break;
-        case Direction::Left:
-            // Compiler doesn't optimize this correctly check why
-            arrow_button[point] = center + QPointF(up_arrow_y * size, up_arrow_x * size);
-            break;
         case Direction::None:
             break;
         }
     }
 
     // Draw arrow button
-    p.setPen(pressed.value ? colors.highlight : colors.button);
-    p.setBrush(pressed.value ? colors.highlight : colors.button);
+    p.setPen(pressed ? colors.highlight : colors.button);
+    p.setBrush(pressed ? colors.highlight : colors.button);
     DrawPolygon(p, arrow_button);
 
     switch (direction) {
     case Direction::Up:
         offset = QPoint(0, -20 * size);
         break;
+    case Direction::Left:
+        offset = QPoint(-20 * size, 0);
+        break;
     case Direction::Right:
         offset = QPoint(20 * size, 0);
         break;
     case Direction::Down:
         offset = QPoint(0, 20 * size);
         break;
-    case Direction::Left:
-        offset = QPoint(-20 * size, 0);
-        break;
     case Direction::None:
         offset = QPoint(0, 0);
         break;
@@ -2641,8 +2596,7 @@ void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center,
 }
 
 void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center,
-                                             const Direction direction,
-                                             const Common::Input::ButtonStatus& pressed) {
+                                             const Direction direction, bool pressed) {
     std::array<QPointF, trigger_button.size() / 2> qtrigger_button;
 
     for (std::size_t point = 0; point < trigger_button.size() / 2; ++point) {
@@ -2665,51 +2619,10 @@ void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center,
 
     // Draw arrow button
     p.setPen(colors.outline);
-    p.setBrush(pressed.value ? colors.highlight : colors.button);
+    p.setBrush(pressed ? colors.highlight : colors.button);
     DrawPolygon(p, qtrigger_button);
 }
 
-void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center,
-                                       Common::Input::BatteryLevel battery) {
-    if (battery == Common::Input::BatteryLevel::None) {
-        return;
-    }
-    p.setPen(colors.outline);
-    p.setBrush(colors.transparent);
-    p.drawRect(center.x(), center.y(), 56, 20);
-    p.drawRect(center.x() + 56, center.y() + 6, 3, 8);
-    p.setBrush(colors.deadzone);
-    switch (battery) {
-    case Common::Input::BatteryLevel::Charging:
-        p.setBrush(colors.indicator2);
-        p.drawText(center + QPoint(2, 14), tr("Charging"));
-        break;
-    case Common::Input::BatteryLevel::Full:
-        p.drawRect(center.x() + 42, center.y(), 14, 20);
-        p.drawRect(center.x() + 28, center.y(), 14, 20);
-        p.drawRect(center.x() + 14, center.y(), 14, 20);
-        p.drawRect(center.x(), center.y(), 14, 20);
-        break;
-    case Common::Input::BatteryLevel::Medium:
-        p.drawRect(center.x() + 28, center.y(), 14, 20);
-        p.drawRect(center.x() + 14, center.y(), 14, 20);
-        p.drawRect(center.x(), center.y(), 14, 20);
-        break;
-    case Common::Input::BatteryLevel::Low:
-        p.drawRect(center.x() + 14, center.y(), 14, 20);
-        p.drawRect(center.x(), center.y(), 14, 20);
-        break;
-    case Common::Input::BatteryLevel::Critical:
-        p.drawRect(center.x(), center.y(), 14, 20);
-        break;
-    case Common::Input::BatteryLevel::Empty:
-        p.drawRect(center.x(), center.y(), 5, 20);
-        break;
-    default:
-        break;
-    }
-}
-
 void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol symbol,
                                       float icon_size) {
     std::array<QPointF, house.size() / 2> house_icon;
diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h
index 4cd5c3be0..f4bbfa528 100755
--- a/src/yuzu/configuration/configure_input_player_widget.h
+++ b/src/yuzu/configuration/configure_input_player_widget.h
@@ -7,11 +7,9 @@
 #include <array>
 #include <QFrame>
 #include <QPointer>
-
-#include "common/input.h"
-#include "common/settings_input.h"
-#include "core/hid/emulated_controller.h"
-#include "core/hid/hid_types.h"
+#include "common/settings.h"
+#include "core/frontend/input.h"
+#include "yuzu/debugger/controller.h"
 
 class QLabel;
 
@@ -26,26 +24,17 @@ public:
     explicit PlayerControlPreview(QWidget* parent);
     ~PlayerControlPreview() override;
 
-    // Sets the emulated controller to be displayed
-    void SetController(Core::HID::EmulatedController* controller);
-
-    // Disables events from the emulated controller
-    void UnloadController();
-
-    // Starts blinking animation at the button specified
+    void SetPlayerInput(std::size_t index, const ButtonParam& buttons_param,
+                        const AnalogParam& analogs_param);
+    void SetPlayerInputRaw(std::size_t index, const Settings::ButtonsRaw& buttons_,
+                           Settings::AnalogsRaw analogs_);
+    void SetConnectedStatus(bool checked);
+    void SetControllerType(Settings::ControllerType type);
     void BeginMappingButton(std::size_t button_id);
-
-    // Starts moving animation at the stick specified
-    void BeginMappingAnalog(std::size_t stick_id);
-
-    // Stops any ongoing animation
+    void BeginMappingAnalog(std::size_t button_id);
     void EndMapping();
-
-    // Handles emulated controller events
-    void ControllerUpdate(Core::HID::ControllerTriggerType type);
-
-    // Updates input on sheduled interval
     void UpdateInput();
+    void SetCallBack(ControllerCallback callback_);
 
 protected:
     void paintEvent(QPaintEvent* event) override;
@@ -74,6 +63,22 @@ private:
         SR,
     };
 
+    struct AxisValue {
+        QPointF value{};
+        QPointF raw_value{};
+        Input::AnalogProperties properties{};
+        int size{};
+        QPoint offset{};
+        bool active{};
+    };
+
+    struct LedPattern {
+        bool position1;
+        bool position2;
+        bool position3;
+        bool position4;
+    };
+
     struct ColorMapping {
         QColor outline{};
         QColor primary{};
@@ -96,6 +101,7 @@ private:
         QColor deadzone{};
     };
 
+    static LedPattern GetColorPattern(std::size_t index, bool player_on);
     void UpdateColors();
     void ResetInputs();
 
@@ -116,75 +122,47 @@ private:
     void DrawGCBody(QPainter& p, QPointF center);
 
     // Draw triggers functions
-    void DrawProTriggers(QPainter& p, QPointF center,
-                         const Common::Input::ButtonStatus& left_pressed,
-                         const Common::Input::ButtonStatus& right_pressed);
-    void DrawGCTriggers(QPainter& p, QPointF center, Common::Input::TriggerStatus left_trigger,
-                        Common::Input::TriggerStatus right_trigger);
-    void DrawHandheldTriggers(QPainter& p, QPointF center,
-                              const Common::Input::ButtonStatus& left_pressed,
-                              const Common::Input::ButtonStatus& right_pressed);
-    void DrawDualTriggers(QPainter& p, QPointF center,
-                          const Common::Input::ButtonStatus& left_pressed,
-                          const Common::Input::ButtonStatus& right_pressed);
-    void DrawDualTriggersTopView(QPainter& p, QPointF center,
-                                 const Common::Input::ButtonStatus& left_pressed,
-                                 const Common::Input::ButtonStatus& right_pressed);
-    void DrawDualZTriggersTopView(QPainter& p, QPointF center,
-                                  const Common::Input::ButtonStatus& left_pressed,
-                                  const Common::Input::ButtonStatus& right_pressed);
-    void DrawLeftTriggers(QPainter& p, QPointF center,
-                          const Common::Input::ButtonStatus& left_pressed);
-    void DrawLeftZTriggers(QPainter& p, QPointF center,
-                           const Common::Input::ButtonStatus& left_pressed);
-    void DrawLeftTriggersTopView(QPainter& p, QPointF center,
-                                 const Common::Input::ButtonStatus& left_pressed);
-    void DrawLeftZTriggersTopView(QPainter& p, QPointF center,
-                                  const Common::Input::ButtonStatus& left_pressed);
-    void DrawRightTriggers(QPainter& p, QPointF center,
-                           const Common::Input::ButtonStatus& right_pressed);
-    void DrawRightZTriggers(QPainter& p, QPointF center,
-                            const Common::Input::ButtonStatus& right_pressed);
-    void DrawRightTriggersTopView(QPainter& p, QPointF center,
-                                  const Common::Input::ButtonStatus& right_pressed);
-    void DrawRightZTriggersTopView(QPainter& p, QPointF center,
-                                   const Common::Input::ButtonStatus& right_pressed);
+    void DrawProTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed);
+    void DrawGCTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed);
+    void DrawHandheldTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed);
+    void DrawDualTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed);
+    void DrawDualTriggersTopView(QPainter& p, QPointF center, bool left_pressed,
+                                 bool right_pressed);
+    void DrawDualZTriggersTopView(QPainter& p, QPointF center, bool left_pressed,
+                                  bool right_pressed);
+    void DrawLeftTriggers(QPainter& p, QPointF center, bool left_pressed);
+    void DrawLeftZTriggers(QPainter& p, QPointF center, bool left_pressed);
+    void DrawLeftTriggersTopView(QPainter& p, QPointF center, bool left_pressed);
+    void DrawLeftZTriggersTopView(QPainter& p, QPointF center, bool left_pressed);
+    void DrawRightTriggers(QPainter& p, QPointF center, bool right_pressed);
+    void DrawRightZTriggers(QPainter& p, QPointF center, bool right_pressed);
+    void DrawRightTriggersTopView(QPainter& p, QPointF center, bool right_pressed);
+    void DrawRightZTriggersTopView(QPainter& p, QPointF center, bool right_pressed);
 
     // Draw joystick functions
-    void DrawJoystick(QPainter& p, QPointF center, float size,
-                      const Common::Input::ButtonStatus& pressed);
-    void DrawJoystickSideview(QPainter& p, QPointF center, float angle, float size,
-                              const Common::Input::ButtonStatus& pressed);
+    void DrawJoystick(QPainter& p, QPointF center, float size, bool pressed);
+    void DrawJoystickSideview(QPainter& p, QPointF center, float angle, float size, bool pressed);
     void DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right);
     void DrawJoystickProperties(QPainter& p, QPointF center,
-                                const Common::Input::AnalogProperties& properties);
-    void DrawJoystickDot(QPainter& p, QPointF center, const Common::Input::StickStatus& stick,
-                         bool raw);
-    void DrawProJoystick(QPainter& p, QPointF center, QPointF offset, float scalar,
-                         const Common::Input::ButtonStatus& pressed);
-    void DrawGCJoystick(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed);
+                                const Input::AnalogProperties& properties);
+    void DrawJoystickDot(QPainter& p, QPointF center, QPointF value,
+                         const Input::AnalogProperties& properties);
+    void DrawProJoystick(QPainter& p, QPointF center, QPointF offset, float scalar, bool pressed);
+    void DrawGCJoystick(QPainter& p, QPointF center, bool pressed);
 
     // Draw button functions
-    void DrawCircleButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed,
-                          float button_size);
-    void DrawRoundButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed,
-                         float width, float height, Direction direction = Direction::None,
-                         float radius = 2);
-    void DrawMinusButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed,
-                         int button_size);
-    void DrawPlusButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed,
-                        int button_size);
-    void DrawGCButtonX(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed);
-    void DrawGCButtonY(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed);
-    void DrawGCButtonZ(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed);
+    void DrawCircleButton(QPainter& p, QPointF center, bool pressed, float button_size);
+    void DrawRoundButton(QPainter& p, QPointF center, bool pressed, float width, float height,
+                         Direction direction = Direction::None, float radius = 2);
+    void DrawMinusButton(QPainter& p, QPointF center, bool pressed, int button_size);
+    void DrawPlusButton(QPainter& p, QPointF center, bool pressed, int button_size);
+    void DrawGCButtonX(QPainter& p, QPointF center, bool pressed);
+    void DrawGCButtonY(QPainter& p, QPointF center, bool pressed);
+    void DrawGCButtonZ(QPainter& p, QPointF center, bool pressed);
     void DrawArrowButtonOutline(QPainter& p, const QPointF center, float size = 1.0f);
-    void DrawArrowButton(QPainter& p, QPointF center, Direction direction,
-                         const Common::Input::ButtonStatus& pressed, float size = 1.0f);
-    void DrawTriggerButton(QPainter& p, QPointF center, Direction direction,
-                           const Common::Input::ButtonStatus& pressed);
-
-    // Draw battery functions
-    void DrawBattery(QPainter& p, QPointF center, Common::Input::BatteryLevel battery);
+    void DrawArrowButton(QPainter& p, QPointF center, Direction direction, bool pressed,
+                         float size = 1.0f);
+    void DrawTriggerButton(QPainter& p, QPointF center, Direction direction, bool pressed);
 
     // Draw icon functions
     void DrawSymbol(QPainter& p, QPointF center, Symbol symbol, float icon_size);
@@ -200,23 +178,24 @@ private:
     void SetTextFont(QPainter& p, float text_size,
                      const QString& font_family = QStringLiteral("sans-serif"));
 
-    bool is_controller_set{};
-    bool is_connected{};
-    bool needs_redraw{};
-    Core::HID::NpadStyleIndex controller_type;
+    using ButtonArray =
+        std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::BUTTON_NS_END>;
+    using StickArray =
+        std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>;
 
+    ControllerCallback controller_callback;
+    bool is_enabled{};
     bool mapping_active{};
     int blink_counter{};
-    int callback_key;
     QColor button_color{};
     ColorMapping colors{};
-    Core::HID::LedPattern led_pattern{0, 0, 0, 0};
+    std::array<QColor, 4> led_color{};
+    ButtonArray buttons{};
+    StickArray sticks{};
     std::size_t player_index{};
-    Core::HID::EmulatedController* controller;
-    std::size_t button_mapping_index{Settings::NativeButton::NumButtons};
-    std::size_t analog_mapping_index{Settings::NativeAnalog::NumAnalogs};
-    Core::HID::ButtonValues button_values{};
-    Core::HID::SticksValues stick_values{};
-    Core::HID::TriggerValues trigger_values{};
-    Core::HID::BatteryValues battery_values{};
+    std::size_t button_mapping_index{Settings::NativeButton::BUTTON_NS_END};
+    std::size_t analog_mapping_index{Settings::NativeAnalog::NUM_STICKS_HID};
+    std::array<AxisValue, Settings::NativeAnalog::NUM_STICKS_HID> axis_values{};
+    std::array<bool, Settings::NativeButton::NumButtons> button_values{};
+    Settings::ControllerType controller_type{Settings::ControllerType::ProController};
 };
diff --git a/src/yuzu/configuration/configure_input_profile_dialog.cpp b/src/yuzu/configuration/configure_input_profile_dialog.cpp
index 17bbe6b61..cd5a88cea 100755
--- a/src/yuzu/configuration/configure_input_profile_dialog.cpp
+++ b/src/yuzu/configuration/configure_input_profile_dialog.cpp
@@ -11,8 +11,8 @@ ConfigureInputProfileDialog::ConfigureInputProfileDialog(
     QWidget* parent, InputCommon::InputSubsystem* input_subsystem, InputProfiles* profiles,
     Core::System& system)
     : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputProfileDialog>()),
-      profile_widget(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles,
-                                              system.HIDCore(), system.IsPoweredOn(), false)) {
+      profile_widget(
+          new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, system, false)) {
     ui->setupUi(this);
 
     ui->controllerLayout->addWidget(profile_widget);
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
index 8539a5c8b..f8e08c422 100755
--- a/src/yuzu/configuration/configure_motion_touch.cpp
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -15,9 +15,9 @@
 
 #include "common/logging/log.h"
 #include "common/settings.h"
-#include "input_common/drivers/udp_client.h"
-#include "input_common/helpers/udp_protocol.h"
 #include "input_common/main.h"
+#include "input_common/udp/client.h"
+#include "input_common/udp/udp.h"
 #include "ui_configure_motion_touch.h"
 #include "yuzu/configuration/configure_motion_touch.h"
 #include "yuzu/configuration/configure_touch_from_button.h"
@@ -93,7 +93,6 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
            "using-a-controller-or-android-phone-for-motion-or-touch-input'><span "
            "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>"));
 
-    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
     SetConfiguration();
     UpdateUiDisplay();
     ConnectEvents();
@@ -102,14 +101,17 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
 ConfigureMotionTouch::~ConfigureMotionTouch() = default;
 
 void ConfigureMotionTouch::SetConfiguration() {
+    const Common::ParamPackage motion_param(Settings::values.motion_device.GetValue());
     const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
 
+    ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button.GetValue());
     touch_from_button_maps = Settings::values.touch_from_button_maps;
     for (const auto& touch_map : touch_from_button_maps) {
         ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name));
     }
     ui->touch_from_button_map->setCurrentIndex(
         Settings::values.touch_from_button_map_index.GetValue());
+    ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f));
 
     min_x = touch_param.Get("min_x", 100);
     min_y = touch_param.Get("min_y", 50);
@@ -137,6 +139,9 @@ void ConfigureMotionTouch::SetConfiguration() {
 void ConfigureMotionTouch::UpdateUiDisplay() {
     const QString cemuhook_udp = QStringLiteral("cemuhookudp");
 
+    ui->motion_sensitivity_label->setVisible(true);
+    ui->motion_sensitivity->setVisible(true);
+
     ui->touch_calibration->setVisible(true);
     ui->touch_calibration_config->setVisible(true);
     ui->touch_calibration_label->setVisible(true);
@@ -307,6 +312,7 @@ void ConfigureMotionTouch::ApplyConfiguration() {
     touch_param.Set("max_y", max_y);
 
     Settings::values.touch_device = touch_param.Serialize();
+    Settings::values.use_touch_from_button = ui->touch_from_button_checkbox->isChecked();
     Settings::values.touch_from_button_map_index = ui->touch_from_button_map->currentIndex();
     Settings::values.touch_from_button_maps = touch_from_button_maps;
     Settings::values.udp_input_servers = GetUDPServerString();
diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui
index c75a84ae4..1e35ea946 100755
--- a/src/yuzu/configuration/configure_motion_touch.ui
+++ b/src/yuzu/configuration/configure_motion_touch.ui
@@ -2,6 +2,14 @@
 <ui version="4.0">
  <class>ConfigureMotionTouch</class>
  <widget class="QDialog" name="ConfigureMotionTouch">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>482</height>
+   </rect>
+  </property>
   <property name="windowTitle">
    <string>Configure Motion / Touch</string>
   </property>
@@ -9,6 +17,48 @@
    <string notr="true"/>
   </property>
   <layout class="QVBoxLayout">
+   <item>
+    <widget class="QGroupBox" name="motion_group_box">
+     <property name="title">
+      <string>Mouse Motion</string>
+     </property>
+     <layout class="QVBoxLayout">
+      <item>
+       <layout class="QHBoxLayout">
+        <item>
+         <widget class="QLabel" name="motion_sensitivity_label">
+          <property name="text">
+           <string>Sensitivity:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QDoubleSpinBox" name="motion_sensitivity">
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="decimals">
+           <number>4</number>
+          </property>
+          <property name="minimum">
+           <double>0.010000000000000</double>
+          </property>
+          <property name="maximum">
+           <double>10.000000000000000</double>
+          </property>
+          <property name="singleStep">
+           <double>0.001000000000000</double>
+          </property>
+          <property name="value">
+           <double>0.010000000000000</double>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
    <item>
     <widget class="QGroupBox" name="touch_group_box">
      <property name="title">
@@ -51,13 +101,19 @@
       </item>
       <item>
        <layout class="QHBoxLayout">
-         <item>
-           <widget class="QLabel" name="touch_from_button_label">
-             <property name="text">
-               <string>Touch from button profile:</string>
-             </property>
-           </widget>
-         </item>
+        <item>
+         <widget class="QCheckBox" name="touch_from_button_checkbox">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="text">
+           <string>Use button mapping:</string>
+          </property>
+         </widget>
+        </item>
         <item>
          <widget class="QComboBox" name="touch_from_button_map"/>
         </item>
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp
index 1e7a3751d..2af3afda8 100755
--- a/src/yuzu/configuration/configure_mouse_advanced.cpp
+++ b/src/yuzu/configuration/configure_mouse_advanced.cpp
@@ -11,11 +11,8 @@
 
 #include "common/assert.h"
 #include "common/param_package.h"
-#include "input_common/drivers/keyboard.h"
-#include "input_common/drivers/mouse.h"
 #include "input_common/main.h"
 #include "ui_configure_mouse_advanced.h"
-#include "yuzu/bootmanager.h"
 #include "yuzu/configuration/config.h"
 #include "yuzu/configuration/configure_mouse_advanced.h"
 
@@ -104,7 +101,7 @@ ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent,
                 [=, this](const Common::ParamPackage& params) {
                     buttons_param[button_id] = params;
                 },
-                InputCommon::Polling::InputType::Button);
+                InputCommon::Polling::DeviceType::Button);
         });
         connect(button, &QPushButton::customContextMenuRequested,
                 [=, this](const QPoint& menu_location) {
@@ -130,10 +127,13 @@ ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent,
     connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
 
     connect(poll_timer.get(), &QTimer::timeout, [this] {
-        const auto& params = input_subsystem->GetNextInput();
-        if (params.Has("engine")) {
-            SetPollingResult(params, false);
-            return;
+        Common::ParamPackage params;
+        for (auto& poller : device_pollers) {
+            params = poller->GetNextInput();
+            if (params.Has("engine")) {
+                SetPollingResult(params, false);
+                return;
+            }
         }
     });
 
@@ -196,13 +196,26 @@ void ConfigureMouseAdvanced::UpdateButtonLabels() {
 
 void ConfigureMouseAdvanced::HandleClick(
     QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
-    InputCommon::Polling::InputType type) {
+    InputCommon::Polling::DeviceType type) {
     button->setText(tr("[press key]"));
     button->setFocus();
 
+    // Keyboard keys or mouse buttons can only be used as button devices
+    want_keyboard_mouse = type == InputCommon::Polling::DeviceType::Button;
+    if (want_keyboard_mouse) {
+        const auto iter = std::find(button_map.begin(), button_map.end(), button);
+        ASSERT(iter != button_map.end());
+        const auto index = std::distance(button_map.begin(), iter);
+        ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
+    }
+
     input_setter = new_input_setter;
 
-    input_subsystem->BeginMapping(type);
+    device_pollers = input_subsystem->GetPollers(type);
+
+    for (auto& poller : device_pollers) {
+        poller->Start();
+    }
 
     QWidget::grabMouse();
     QWidget::grabKeyboard();
@@ -214,7 +227,9 @@ void ConfigureMouseAdvanced::HandleClick(
 void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) {
     timeout_timer->stop();
     poll_timer->stop();
-    input_subsystem->StopMapping();
+    for (auto& poller : device_pollers) {
+        poller->Stop();
+    }
 
     QWidget::releaseMouse();
     QWidget::releaseKeyboard();
@@ -232,8 +247,15 @@ void ConfigureMouseAdvanced::mousePressEvent(QMouseEvent* event) {
         return;
     }
 
-    const auto button = GRenderWindow::QtButtonToMouseButton(event->button());
-    input_subsystem->GetMouse()->PressButton(0, 0, 0, 0, button);
+    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 ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) {
@@ -242,6 +264,13 @@ void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) {
     }
 
     if (event->key() != Qt::Key_Escape) {
-        input_subsystem->GetKeyboard()->PressKey(event->key());
+        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);
 }
diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h
index 5fa534eaf..65b6fca9a 100755
--- a/src/yuzu/configuration/configure_mouse_advanced.h
+++ b/src/yuzu/configuration/configure_mouse_advanced.h
@@ -46,7 +46,7 @@ private:
     /// Called when the button was pressed.
     void HandleClick(QPushButton* button,
                      std::function<void(const Common::ParamPackage&)> new_input_setter,
-                     InputCommon::Polling::InputType type);
+                     InputCommon::Polling::DeviceType type);
 
     /// Finish polling and configure input using the input_setter
     void SetPollingResult(const Common::ParamPackage& params, bool abort);
@@ -67,6 +67,12 @@ private:
     std::array<QPushButton*, Settings::NativeMouseButton::NumMouseButtons> button_map;
     std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons> buttons_param;
 
+    std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
+
     std::unique_ptr<QTimer> timeout_timer;
     std::unique_ptr<QTimer> poll_timer;
+
+    /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
+    /// keyboard events are ignored.
+    bool want_keyboard_mouse = false;
 };
diff --git a/src/yuzu/configuration/configure_tas.cpp b/src/yuzu/configuration/configure_tas.cpp
index 979a8db61..8e5a4c72d 100755
--- a/src/yuzu/configuration/configure_tas.cpp
+++ b/src/yuzu/configuration/configure_tas.cpp
@@ -32,6 +32,7 @@ void ConfigureTasDialog::LoadConfiguration() {
     ui->tas_path_edit->setText(
         QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASDir)));
     ui->tas_enable->setChecked(Settings::values.tas_enable.GetValue());
+    ui->tas_control_swap->setChecked(Settings::values.tas_swap_controllers.GetValue());
     ui->tas_loop_script->setChecked(Settings::values.tas_loop.GetValue());
     ui->tas_pause_on_load->setChecked(Settings::values.pause_tas_on_load.GetValue());
 }
@@ -39,6 +40,7 @@ void ConfigureTasDialog::LoadConfiguration() {
 void ConfigureTasDialog::ApplyConfiguration() {
     Common::FS::SetYuzuPath(Common::FS::YuzuPath::TASDir, ui->tas_path_edit->text().toStdString());
     Settings::values.tas_enable.SetValue(ui->tas_enable->isChecked());
+    Settings::values.tas_swap_controllers.SetValue(ui->tas_control_swap->isChecked());
     Settings::values.tas_loop.SetValue(ui->tas_loop_script->isChecked());
     Settings::values.pause_tas_on_load.SetValue(ui->tas_pause_on_load->isChecked());
 }
diff --git a/src/yuzu/configuration/configure_tas.ui b/src/yuzu/configuration/configure_tas.ui
index cf88a5bf0..7d44895c4 100755
--- a/src/yuzu/configuration/configure_tas.ui
+++ b/src/yuzu/configuration/configure_tas.ui
@@ -59,13 +59,20 @@
          </widget>
         </item>
         <item row="1" column="0" colspan="4">
+         <widget class="QCheckBox" name="tas_control_swap">
+          <property name="text">
+           <string>Automatic controller profile swapping</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="0" colspan="4">
          <widget class="QCheckBox" name="tas_loop_script">
           <property name="text">
            <string>Loop script</string>
           </property>
          </widget>
         </item>
-        <item row="2" column="0" colspan="4">
+        <item row="3" column="0" colspan="4">
          <widget class="QCheckBox" name="tas_pause_on_load">
           <property name="enabled">
            <bool>false</bool>
diff --git a/src/yuzu/configuration/configure_touch_from_button.cpp b/src/yuzu/configuration/configure_touch_from_button.cpp
index bde0a08c4..40129f228 100755
--- a/src/yuzu/configuration/configure_touch_from_button.cpp
+++ b/src/yuzu/configuration/configure_touch_from_button.cpp
@@ -163,10 +163,13 @@ void ConfigureTouchFromButton::ConnectEvents() {
     connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); });
 
     connect(poll_timer.get(), &QTimer::timeout, [this]() {
-        const auto& params = input_subsystem->GetNextInput();
-        if (params.Has("engine")) {
-            SetPollingResult(params, false);
-            return;
+        Common::ParamPackage params;
+        for (auto& poller : device_pollers) {
+            params = poller->GetNextInput();
+            if (params.Has("engine")) {
+                SetPollingResult(params, false);
+                return;
+            }
         }
     });
 }
@@ -245,7 +248,11 @@ void ConfigureTouchFromButton::GetButtonInput(const int row_index, const bool is
         }
     };
 
-    input_subsystem->BeginMapping(InputCommon::Polling::InputType::Button);
+    device_pollers = input_subsystem->GetPollers(InputCommon::Polling::DeviceType::Button);
+
+    for (auto& poller : device_pollers) {
+        poller->Start();
+    }
 
     grabKeyboard();
     grabMouse();
@@ -358,14 +365,14 @@ void ConfigureTouchFromButton::SetCoordinates(const int dot_id, const QPoint& po
 
 void ConfigureTouchFromButton::SetPollingResult(const Common::ParamPackage& params,
                                                 const bool cancel) {
-    timeout_timer->stop();
-    poll_timer->stop();
-    input_subsystem->StopMapping();
-
     releaseKeyboard();
     releaseMouse();
     qApp->restoreOverrideCursor();
-
+    timeout_timer->stop();
+    poll_timer->stop();
+    for (auto& poller : device_pollers) {
+        poller->Stop();
+    }
     if (input_setter) {
         (*input_setter)(params, cancel);
         input_setter.reset();
diff --git a/src/yuzu/configuration/configure_touch_from_button.h b/src/yuzu/configuration/configure_touch_from_button.h
index e1400481a..d9513e3bc 100755
--- a/src/yuzu/configuration/configure_touch_from_button.h
+++ b/src/yuzu/configuration/configure_touch_from_button.h
@@ -24,6 +24,10 @@ namespace InputCommon {
 class InputSubsystem;
 }
 
+namespace InputCommon::Polling {
+class DevicePoller;
+}
+
 namespace Settings {
 struct TouchFromButtonMap;
 }
@@ -81,6 +85,7 @@ private:
 
     std::unique_ptr<QTimer> timeout_timer;
     std::unique_ptr<QTimer> poll_timer;
+    std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
     std::optional<std::function<void(const Common::ParamPackage&, bool)>> input_setter;
 
     static constexpr int DataRoleDot = Qt::ItemDataRole::UserRole + 2;
diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp
index f1ce7205d..46a0f3025 100755
--- a/src/yuzu/configuration/configure_vibration.cpp
+++ b/src/yuzu/configuration/configure_vibration.cpp
@@ -97,7 +97,7 @@ void ConfigureVibration::SetVibrationDevices(std::size_t player_index) {
 
         const auto engine = param.Get("engine", "");
         const auto guid = param.Get("guid", "");
-        const auto port = param.Get("port", 0);
+        const auto port = param.Get("port", "");
 
         if (engine.empty() || engine == "keyboard" || engine == "mouse" || engine == "tas") {
             continue;
@@ -105,7 +105,7 @@ void ConfigureVibration::SetVibrationDevices(std::size_t player_index) {
 
         vibration_param_str += fmt::format("engine:{}", engine);
 
-        if (port != 0) {
+        if (!port.empty()) {
             vibration_param_str += fmt::format(",port:{}", port);
         }
         if (!guid.empty()) {
diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp
index 6b834c42e..5a844409b 100755
--- a/src/yuzu/debugger/controller.cpp
+++ b/src/yuzu/debugger/controller.cpp
@@ -6,17 +6,13 @@
 #include <QLayout>
 #include <QString>
 #include "common/settings.h"
-#include "core/hid/emulated_controller.h"
-#include "core/hid/hid_core.h"
-#include "input_common/drivers/tas_input.h"
 #include "input_common/main.h"
+#include "input_common/tas/tas_input.h"
 #include "yuzu/configuration/configure_input_player_widget.h"
 #include "yuzu/debugger/controller.h"
 
-ControllerDialog::ControllerDialog(Core::HID::HIDCore& hid_core_,
-                                   std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
-                                   QWidget* parent)
-    : QWidget(parent, Qt::Dialog), hid_core{hid_core_}, input_subsystem{input_subsystem_} {
+ControllerDialog::ControllerDialog(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_)
+    : QWidget(parent, Qt::Dialog), input_subsystem{input_subsystem_} {
     setObjectName(QStringLiteral("Controller"));
     setWindowTitle(tr("Controller P1"));
     resize(500, 350);
@@ -35,24 +31,20 @@ ControllerDialog::ControllerDialog(Core::HID::HIDCore& hid_core_,
     // Configure focus so that widget is focusable and the dialog automatically forwards focus to
     // it.
     setFocusProxy(widget);
+    widget->SetConnectedStatus(false);
     widget->setFocusPolicy(Qt::StrongFocus);
     widget->setFocus();
 }
 
 void ControllerDialog::refreshConfiguration() {
-    UnloadController();
-    auto* player_1 = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
-    auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
-    // Display the correct controller
-    controller = handheld->IsConnected() ? handheld : player_1;
-
-    Core::HID::ControllerUpdateCallback engine_callback{
-        .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); },
-        .is_npad_service = true,
-    };
-    callback_key = controller->SetCallback(engine_callback);
-    widget->SetController(controller);
-    is_controller_set = true;
+    const auto& players = Settings::values.players.GetValue();
+    constexpr std::size_t player = 0;
+    widget->SetPlayerInputRaw(player, players[player].buttons, players[player].analogs);
+    widget->SetControllerType(players[player].controller_type);
+    ControllerCallback callback{[this](ControllerInput input) { InputController(input); }};
+    widget->SetCallBack(callback);
+    widget->repaint();
+    widget->SetConnectedStatus(players[player].connected);
 }
 
 QAction* ControllerDialog::toggleViewAction() {
@@ -66,18 +58,11 @@ QAction* ControllerDialog::toggleViewAction() {
     return toggle_view_action;
 }
 
-void ControllerDialog::UnloadController() {
-    widget->UnloadController();
-    if (is_controller_set) {
-        controller->DeleteCallback(callback_key);
-        is_controller_set = false;
-    }
-}
-
 void ControllerDialog::showEvent(QShowEvent* ev) {
     if (toggle_view_action) {
         toggle_view_action->setChecked(isVisible());
     }
+    refreshConfiguration();
     QWidget::showEvent(ev);
 }
 
@@ -85,34 +70,16 @@ void ControllerDialog::hideEvent(QHideEvent* ev) {
     if (toggle_view_action) {
         toggle_view_action->setChecked(isVisible());
     }
+    widget->SetConnectedStatus(false);
     QWidget::hideEvent(ev);
 }
 
-void ControllerDialog::ControllerUpdate(Core::HID::ControllerTriggerType type) {
-    // TODO(german77): Remove TAS from here
-    switch (type) {
-    case Core::HID::ControllerTriggerType::Button:
-    case Core::HID::ControllerTriggerType::Stick: {
-        const auto buttons_values = controller->GetButtonsValues();
-        const auto stick_values = controller->GetSticksValues();
-        u64 buttons = 0;
-        std::size_t index = 0;
-        for (const auto& button : buttons_values) {
-            buttons |= button.value ? 1LLU << index : 0;
-            index++;
-        }
-        const InputCommon::TasInput::TasAnalog left_axis = {
-            .x = stick_values[Settings::NativeAnalog::LStick].x.value,
-            .y = stick_values[Settings::NativeAnalog::LStick].y.value,
-        };
-        const InputCommon::TasInput::TasAnalog right_axis = {
-            .x = stick_values[Settings::NativeAnalog::RStick].x.value,
-            .y = stick_values[Settings::NativeAnalog::RStick].y.value,
-        };
-        input_subsystem->GetTas()->RecordInput(buttons, left_axis, right_axis);
-        break;
-    }
-    default:
-        break;
+void ControllerDialog::InputController(ControllerInput input) {
+    u32 buttons = 0;
+    int index = 0;
+    for (bool btn : input.button_values) {
+        buttons |= (btn ? 1U : 0U) << index;
+        index++;
     }
+    input_subsystem->GetTas()->RecordInput(buttons, input.axis_values);
 }
diff --git a/src/yuzu/debugger/controller.h b/src/yuzu/debugger/controller.h
index 52cea3326..7742db58b 100755
--- a/src/yuzu/debugger/controller.h
+++ b/src/yuzu/debugger/controller.h
@@ -4,7 +4,9 @@
 
 #pragma once
 
+#include <QFileSystemWatcher>
 #include <QWidget>
+#include "common/settings.h"
 
 class QAction;
 class QHideEvent;
@@ -15,43 +17,35 @@ namespace InputCommon {
 class InputSubsystem;
 }
 
-namespace Core::HID {
-class HIDCore;
-class EmulatedController;
-enum class ControllerTriggerType;
-} // namespace Core::HID
+struct ControllerInput {
+    std::array<std::pair<float, float>, Settings::NativeAnalog::NUM_STICKS_HID> axis_values{};
+    std::array<bool, Settings::NativeButton::NumButtons> button_values{};
+    bool changed{};
+};
+
+struct ControllerCallback {
+    std::function<void(ControllerInput)> input;
+};
 
 class ControllerDialog : public QWidget {
     Q_OBJECT
 
 public:
-    explicit ControllerDialog(Core::HID::HIDCore& hid_core_,
-                              std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
-                              QWidget* parent = nullptr);
+    explicit ControllerDialog(QWidget* parent = nullptr,
+                              InputCommon::InputSubsystem* input_subsystem_ = nullptr);
 
     /// Returns a QAction that can be used to toggle visibility of this dialog.
     QAction* toggleViewAction();
-
-    /// Reloads the widget to apply any changes in the configuration
     void refreshConfiguration();
 
-    /// Disables events from the emulated controller
-    void UnloadController();
-
 protected:
     void showEvent(QShowEvent* ev) override;
     void hideEvent(QHideEvent* ev) override;
 
 private:
-    /// Redirects input from the widget to the TAS driver
-    void ControllerUpdate(Core::HID::ControllerTriggerType type);
-
-    int callback_key;
-    bool is_controller_set{};
-    Core::HID::EmulatedController* controller;
-
+    void InputController(ControllerInput input);
     QAction* toggle_view_action = nullptr;
+    QFileSystemWatcher* watcher = nullptr;
     PlayerControlPreview* widget;
-    Core::HID::HIDCore& hid_core;
-    std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
+    InputCommon::InputSubsystem* input_subsystem;
 };
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 5ffa0a27a..d057dc889 100755
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -26,8 +26,6 @@
 #include "core/frontend/applets/controller.h"
 #include "core/frontend/applets/general_frontend.h"
 #include "core/frontend/applets/software_keyboard.h"
-#include "core/hid/emulated_controller.h"
-#include "core/hid/hid_core.h"
 #include "core/hle/service/acc/profile_manager.h"
 #include "core/hle/service/am/applet_ae.h"
 #include "core/hle/service/am/applet_oe.h"
@@ -108,8 +106,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
 #include "core/loader/loader.h"
 #include "core/perf_stats.h"
 #include "core/telemetry_session.h"
-#include "input_common/drivers/tas_input.h"
 #include "input_common/main.h"
+#include "input_common/tas/tas_input.h"
 #include "ui_main.h"
 #include "util/overlay_dialog.h"
 #include "video_core/gpu.h"
@@ -229,9 +227,6 @@ GMainWindow::GMainWindow()
     ConnectMenuEvents();
     ConnectWidgetEvents();
 
-    system->HIDCore().ReloadInputDevices();
-    controller_dialog->refreshConfiguration();
-
     const auto branch_name = std::string(Common::g_scm_branch);
     const auto description = std::string(Common::g_scm_desc);
     const auto build_id = std::string(Common::g_build_id);
@@ -834,16 +829,15 @@ 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* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
-        auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
+        auto& controller_type = Settings::values.players.GetValue()[0].controller_type;
 
-        if (!is_docked && handheld->IsConnected()) {
+        if (!is_docked && controller_type == Settings::ControllerType::Handheld) {
             QMessageBox::warning(this, tr("Invalid config detected"),
                                  tr("Handheld controller can't be used on docked mode. Pro "
                                     "controller will be selected."));
-            handheld->Disconnect();
-            player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
-            player_1->Connect();
+            controller_type = Settings::ControllerType::ProController;
+            ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), *system);
+            configure_dialog.ApplyConfiguration();
             controller_dialog->refreshConfiguration();
         }
 
@@ -928,7 +922,7 @@ void GMainWindow::InitializeDebugWidgets() {
     waitTreeWidget->hide();
     debug_menu->addAction(waitTreeWidget->toggleViewAction());
 
-    controller_dialog = new ControllerDialog(system->HIDCore(), input_subsystem, this);
+    controller_dialog = new ControllerDialog(this, input_subsystem.get());
     controller_dialog->hide();
     debug_menu->addAction(controller_dialog->toggleViewAction());
 
@@ -2780,6 +2774,7 @@ void GMainWindow::OnConfigure() {
 
         ShowTelemetryCallout();
     }
+    controller_dialog->refreshConfiguration();
     InitializeHotkeys();
 
     if (UISettings::values.theme != old_theme) {
@@ -2812,7 +2807,6 @@ void GMainWindow::OnConfigure() {
     }
 
     UpdateStatusButtons();
-    controller_dialog->refreshConfiguration();
 }
 
 void GMainWindow::OnConfigureTas() {
@@ -3009,11 +3003,11 @@ void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_vie
 QString GMainWindow::GetTasStateDescription() const {
     auto [tas_status, current_tas_frame, total_tas_frames] = input_subsystem->GetTas()->GetStatus();
     switch (tas_status) {
-    case InputCommon::TasInput::TasState::Running:
+    case TasInput::TasState::Running:
         return tr("TAS state: Running %1/%2").arg(current_tas_frame).arg(total_tas_frames);
-    case InputCommon::TasInput::TasState::Recording:
+    case TasInput::TasState::Recording:
         return tr("TAS state: Recording %1").arg(total_tas_frames);
-    case InputCommon::TasInput::TasState::Stopped:
+    case TasInput::TasState::Stopped:
         return tr("TAS state: Idle %1/%2").arg(current_tas_frame).arg(total_tas_frames);
     default:
         return tr("TAS State: Invalid");
@@ -3392,8 +3386,6 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
     UpdateUISettings();
     game_list->SaveInterfaceLayout();
     hotkey_registry.SaveHotkeys();
-    controller_dialog->UnloadController();
-    system->HIDCore().UnloadInputDevices();
 
     // Shutdown session if the emu thread is active...
     if (emu_thread != nullptr) {
diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp
index c66dfbdff..95b148545 100755
--- a/src/yuzu/util/overlay_dialog.cpp
+++ b/src/yuzu/util/overlay_dialog.cpp
@@ -6,8 +6,7 @@
 #include <QScreen>
 
 #include "core/core.h"
-#include "core/hid/hid_types.h"
-#include "core/hid/input_interpreter.h"
+#include "core/frontend/input_interpreter.h"
 #include "ui_overlay_dialog.h"
 #include "yuzu/util/overlay_dialog.h"
 
@@ -180,9 +179,9 @@ void OverlayDialog::MoveAndResizeWindow() {
     QDialog::resize(width, height);
 }
 
-template <Core::HID::NpadButton... T>
+template <HIDButton... T>
 void OverlayDialog::HandleButtonPressedOnce() {
-    const auto f = [this](Core::HID::NpadButton button) {
+    const auto f = [this](HIDButton button) {
         if (input_interpreter->IsButtonPressedOnce(button)) {
             TranslateButtonPress(button);
         }
@@ -191,7 +190,7 @@ void OverlayDialog::HandleButtonPressedOnce() {
     (f(T), ...);
 }
 
-void OverlayDialog::TranslateButtonPress(Core::HID::NpadButton button) {
+void OverlayDialog::TranslateButtonPress(HIDButton button) {
     QPushButton* left_button = use_rich_text ? ui->button_cancel_rich : ui->button_cancel;
     QPushButton* right_button = use_rich_text ? ui->button_ok_rich : ui->button_ok_label;
 
@@ -199,20 +198,20 @@ void OverlayDialog::TranslateButtonPress(Core::HID::NpadButton button) {
     // TODO (Morph): focusPrevious/NextChild() doesn't work well with the rich text dialog, fix it
 
     switch (button) {
-    case Core::HID::NpadButton::A:
-    case Core::HID::NpadButton::B:
+    case HIDButton::A:
+    case HIDButton::B:
         if (left_button->hasFocus()) {
             left_button->click();
         } else if (right_button->hasFocus()) {
             right_button->click();
         }
         break;
-    case Core::HID::NpadButton::Left:
-    case Core::HID::NpadButton::StickLLeft:
+    case HIDButton::DLeft:
+    case HIDButton::LStickLeft:
         focusPreviousChild();
         break;
-    case Core::HID::NpadButton::Right:
-    case Core::HID::NpadButton::StickLRight:
+    case HIDButton::DRight:
+    case HIDButton::LStickRight:
         focusNextChild();
         break;
     default:
@@ -242,10 +241,8 @@ void OverlayDialog::InputThread() {
     while (input_thread_running) {
         input_interpreter->PollInput();
 
-        HandleButtonPressedOnce<Core::HID::NpadButton::A, Core::HID::NpadButton::B,
-                                Core::HID::NpadButton::Left, Core::HID::NpadButton::Right,
-                                Core::HID::NpadButton::StickLLeft,
-                                Core::HID::NpadButton::StickLRight>();
+        HandleButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::DLeft, HIDButton::DRight,
+                                HIDButton::LStickLeft, HIDButton::LStickRight>();
 
         std::this_thread::sleep_for(std::chrono::milliseconds(50));
     }
diff --git a/src/yuzu/util/overlay_dialog.h b/src/yuzu/util/overlay_dialog.h
index d8a140ff3..e8c388bd0 100755
--- a/src/yuzu/util/overlay_dialog.h
+++ b/src/yuzu/util/overlay_dialog.h
@@ -13,16 +13,14 @@
 
 #include "common/common_types.h"
 
+enum class HIDButton : u8;
+
 class InputInterpreter;
 
 namespace Core {
 class System;
 }
 
-namespace Core::HID {
-enum class NpadButton : u64;
-}
-
 namespace Ui {
 class OverlayDialog;
 }
@@ -81,7 +79,7 @@ private:
      *
      * @tparam HIDButton The list of buttons that can be converted into keyboard input.
      */
-    template <Core::HID::NpadButton... T>
+    template <HIDButton... T>
     void HandleButtonPressedOnce();
 
     /**
@@ -89,7 +87,7 @@ private:
      *
      * @param button The button press to process.
      */
-    void TranslateButtonPress(Core::HID::NpadButton button);
+    void TranslateButtonPress(HIDButton button);
 
     void StartInputThread();
     void StopInputThread();
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 7ca09a635..33241ea98 100755
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -24,6 +24,7 @@
 #include "common/settings.h"
 #include "core/hle/service/acc/profile_manager.h"
 #include "input_common/main.h"
+#include "input_common/udp/client.h"
 #include "yuzu_cmd/config.h"
 #include "yuzu_cmd/default_ini.h"
 
@@ -292,6 +293,8 @@ void Config::ReadValues() {
             Settings::values.mouse_buttons[i] = default_param;
     }
 
+    ReadSetting("ControlsGeneral", Settings::values.motion_device);
+
     ReadSetting("ControlsGeneral", Settings::values.touch_device);
 
     ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled);
@@ -360,6 +363,7 @@ void Config::ReadValues() {
             Settings::TouchFromButtonMap{"default", {}});
         num_touch_from_button_maps = 1;
     }
+    ReadSetting("ControlsGeneral", Settings::values.use_touch_from_button);
     Settings::values.touch_from_button_map_index = std::clamp(
         Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
 
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 6d613bf7a..ecdc271a8 100755
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -84,10 +84,23 @@ enable_accurate_vibrations=
 # 0: Disabled, 1 (default): Enabled
 motion_enabled =
 
-# Defines the udp device's touch screen coordinate system for cemuhookudp devices
-#  - "min_x", "min_y", "max_x", "max_y"
+# for motion input, the following devices are available:
+#  - "motion_emu" (default) for emulating motion input from mouse input. Required parameters:
+#      - "update_period": update period in milliseconds (default to 100)
+#      - "sensitivity": the coefficient converting mouse movement to tilting angle (default to 0.01)
+#  - "cemuhookudp" reads motion input from a udp server that uses cemuhook's udp protocol
+motion_device=
+
+# for touch input, the following devices are available:
+#  - "emu_window" (default) for emulating touch input from mouse input to the emulation window. No parameters required
+#  - "cemuhookudp" reads touch input from a udp server that uses cemuhook's udp protocol
+#      - "min_x", "min_y", "max_x", "max_y": defines the udp device's touch screen coordinate system
 touch_device=
 
+# Whether to enable or disable touch input from button
+# 0 (default): Disabled, 1: Enabled
+use_touch_from_button=
+
 # for mapping buttons to touch inputs.
 #touch_from_button_map=1
 #touch_from_button_maps_0_name=default
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 57f807826..87fce0c23 100755
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -9,10 +9,10 @@
 #include "common/settings.h"
 #include "core/core.h"
 #include "core/perf_stats.h"
-#include "input_common/drivers/keyboard.h"
-#include "input_common/drivers/mouse.h"
-#include "input_common/drivers/touch_screen.h"
+#include "input_common/keyboard.h"
 #include "input_common/main.h"
+#include "input_common/mouse/mouse_input.h"
+#include "input_common/sdl/sdl.h"
 #include "yuzu_cmd/emu_window/emu_window_sdl2.h"
 #include "yuzu_cmd/yuzu_icon.h"
 
@@ -32,32 +32,42 @@ EmuWindow_SDL2::~EmuWindow_SDL2() {
 }
 
 void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
-    input_subsystem->GetMouse()->MouseMove(x, y, 0, 0, 0, 0);
+    TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0);
+
+    input_subsystem->GetMouse()->MouseMove(x, y, 0, 0);
 }
 
-InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const {
+MouseInput::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const {
     switch (button) {
     case SDL_BUTTON_LEFT:
-        return InputCommon::MouseButton::Left;
+        return MouseInput::MouseButton::Left;
     case SDL_BUTTON_RIGHT:
-        return InputCommon::MouseButton::Right;
+        return MouseInput::MouseButton::Right;
     case SDL_BUTTON_MIDDLE:
-        return InputCommon::MouseButton::Wheel;
+        return MouseInput::MouseButton::Wheel;
     case SDL_BUTTON_X1:
-        return InputCommon::MouseButton::Backward;
+        return MouseInput::MouseButton::Backward;
     case SDL_BUTTON_X2:
-        return InputCommon::MouseButton::Forward;
+        return MouseInput::MouseButton::Forward;
     default:
-        return InputCommon::MouseButton::Undefined;
+        return MouseInput::MouseButton::Undefined;
     }
 }
 
 void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
     const auto mouse_button = SDLButtonToMouseButton(button);
-    if (state == SDL_PRESSED) {
-        input_subsystem->GetMouse()->PressButton(x, y, 0, 0, mouse_button);
+    if (button == SDL_BUTTON_LEFT) {
+        if (state == SDL_PRESSED) {
+            TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0);
+        } else {
+            TouchReleased(0);
+        }
     } else {
-        input_subsystem->GetMouse()->ReleaseButton(mouse_button);
+        if (state == SDL_PRESSED) {
+            input_subsystem->GetMouse()->PressButton(x, y, mouse_button);
+        } else {
+            input_subsystem->GetMouse()->ReleaseButton(mouse_button);
+        }
     }
 }
 
@@ -72,35 +82,29 @@ std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, flo
             static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))};
 }
 
-void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) {
-    int width, height;
-    SDL_GetWindowSize(render_window, &width, &height);
-    const auto [px, py] = TouchToPixelPos(x, y);
-    const float fx = px * 1.0f / width;
-    const float fy = py * 1.0f / height;
+void EmuWindow_SDL2::OnFingerDown(float x, float y) {
+    // TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind
+    // This isn't critical because the best we can do when we have that is to average them, like the
+    // 3DS does
 
-    input_subsystem->GetTouchScreen()->TouchPressed(fx, fy, id);
+    const auto [px, py] = TouchToPixelPos(x, y);
+    TouchPressed(px, py, 0);
 }
 
-void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) {
-    int width, height;
-    SDL_GetWindowSize(render_window, &width, &height);
+void EmuWindow_SDL2::OnFingerMotion(float x, float y) {
     const auto [px, py] = TouchToPixelPos(x, y);
-    const float fx = px * 1.0f / width;
-    const float fy = py * 1.0f / height;
-
-    input_subsystem->GetTouchScreen()->TouchMoved(fx, fy, id);
+    TouchMoved(px, py, 0);
 }
 
 void EmuWindow_SDL2::OnFingerUp() {
-    input_subsystem->GetTouchScreen()->TouchReleased(0);
+    TouchReleased(0);
 }
 
 void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
     if (state == SDL_PRESSED) {
-        input_subsystem->GetKeyboard()->PressKey(static_cast<std::size_t>(key));
+        input_subsystem->GetKeyboard()->PressKey(key);
     } else if (state == SDL_RELEASED) {
-        input_subsystem->GetKeyboard()->ReleaseKey(static_cast<std::size_t>(key));
+        input_subsystem->GetKeyboard()->ReleaseKey(key);
     }
 }
 
@@ -201,12 +205,10 @@ void EmuWindow_SDL2::WaitEvent() {
         }
         break;
     case SDL_FINGERDOWN:
-        OnFingerDown(event.tfinger.x, event.tfinger.y,
-                     static_cast<std::size_t>(event.tfinger.touchId));
+        OnFingerDown(event.tfinger.x, event.tfinger.y);
         break;
     case SDL_FINGERMOTION:
-        OnFingerMotion(event.tfinger.x, event.tfinger.y,
-                       static_cast<std::size_t>(event.tfinger.touchId));
+        OnFingerMotion(event.tfinger.x, event.tfinger.y);
         break;
     case SDL_FINGERUP:
         OnFingerUp();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 0af002693..4810f8775 100755
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -16,8 +16,11 @@ class System;
 
 namespace InputCommon {
 class InputSubsystem;
+}
+
+namespace MouseInput {
 enum class MouseButton;
-} // namespace InputCommon
+}
 
 class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
 public:
@@ -44,7 +47,7 @@ protected:
     void OnMouseMotion(s32 x, s32 y);
 
     /// Converts a SDL mouse button into MouseInput mouse button
-    InputCommon::MouseButton SDLButtonToMouseButton(u32 button) const;
+    MouseInput::MouseButton SDLButtonToMouseButton(u32 button) const;
 
     /// Called by WaitEvent when a mouse button is pressed or released
     void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
@@ -53,10 +56,10 @@ protected:
     std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const;
 
     /// Called by WaitEvent when a finger starts touching the touchscreen
-    void OnFingerDown(float x, float y, std::size_t id);
+    void OnFingerDown(float x, float y);
 
     /// Called by WaitEvent when a finger moves while touching the touchscreen
-    void OnFingerMotion(float x, float y, std::size_t id);
+    void OnFingerMotion(float x, float y);
 
     /// Called by WaitEvent when a finger stops touching the touchscreen
     void OnFingerUp();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index 70db865ec..a075ad08a 100755
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -17,6 +17,7 @@
 #include "common/settings.h"
 #include "common/string_util.h"
 #include "core/core.h"
+#include "input_common/keyboard.h"
 #include "input_common/main.h"
 #include "video_core/renderer_base.h"
 #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"