diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 5b28be5777..b60fb9139b 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -18,9 +18,10 @@ namespace Service::HID {
 
 void LoopProcess(Core::System& system) {
     auto server_manager = std::make_unique<ServerManager>(system);
-    std::shared_ptr<ResourceManager> resource_manager = std::make_shared<ResourceManager>(system);
     std::shared_ptr<HidFirmwareSettings> firmware_settings =
         std::make_shared<HidFirmwareSettings>(system);
+    std::shared_ptr<ResourceManager> resource_manager =
+        std::make_shared<ResourceManager>(system, firmware_settings);
 
     // TODO: Remove this hack when am is emulated properly.
     resource_manager->Initialize();
@@ -31,7 +32,7 @@ void LoopProcess(Core::System& system) {
     server_manager->RegisterNamedService(
         "hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings));
     server_manager->RegisterNamedService(
-        "hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager));
+        "hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager, firmware_settings));
     server_manager->RegisterNamedService(
         "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings));
 
diff --git a/src/core/hle/service/hid/hid_debug_server.cpp b/src/core/hle/service/hid/hid_debug_server.cpp
index f2a767d374..610af34dd4 100644
--- a/src/core/hle/service/hid/hid_debug_server.cpp
+++ b/src/core/hle/service/hid/hid_debug_server.cpp
@@ -1,27 +1,37 @@
 // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-3.0-or-later
 
+#include <algorithm>
+
 #include "core/hle/service/hid/hid_debug_server.h"
 #include "core/hle/service/ipc_helpers.h"
+#include "hid_core/hid_types.h"
 #include "hid_core/resource_manager.h"
+#include "hid_core/resources/hid_firmware_settings.h"
+
+#include "hid_core/resources/touch_screen/gesture.h"
+#include "hid_core/resources/touch_screen/touch_screen.h"
+#include "hid_core/resources/touch_screen/touch_types.h"
 
 namespace Service::HID {
 
-IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
-    : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource} {
+IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
+                                 std::shared_ptr<HidFirmwareSettings> settings)
+    : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource}, firmware_settings{
+                                                                            settings} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, nullptr, "DeactivateDebugPad"},
         {1, nullptr, "SetDebugPadAutoPilotState"},
         {2, nullptr, "UnsetDebugPadAutoPilotState"},
-        {10, nullptr, "DeactivateTouchScreen"},
-        {11, nullptr, "SetTouchScreenAutoPilotState"},
-        {12, nullptr, "UnsetTouchScreenAutoPilotState"},
-        {13, nullptr, "GetTouchScreenConfiguration"},
-        {14, nullptr, "ProcessTouchScreenAutoTune"},
-        {15, nullptr, "ForceStopTouchScreenManagement"},
-        {16, nullptr, "ForceRestartTouchScreenManagement"},
-        {17, nullptr, "IsTouchScreenManaged"},
+        {10, &IHidDebugServer::DeactivateTouchScreen, "DeactivateTouchScreen"},
+        {11, &IHidDebugServer::SetTouchScreenAutoPilotState, "SetTouchScreenAutoPilotState"},
+        {12, &IHidDebugServer::UnsetTouchScreenAutoPilotState, "UnsetTouchScreenAutoPilotState"},
+        {13, &IHidDebugServer::GetTouchScreenConfiguration, "GetTouchScreenConfiguration"},
+        {14, &IHidDebugServer::ProcessTouchScreenAutoTune, "ProcessTouchScreenAutoTune"},
+        {15, &IHidDebugServer::ForceStopTouchScreenManagement, "ForceStopTouchScreenManagement"},
+        {16, &IHidDebugServer::ForceRestartTouchScreenManagement, "ForceRestartTouchScreenManagement"},
+        {17, &IHidDebugServer::IsTouchScreenManaged, "IsTouchScreenManaged"},
         {20, nullptr, "DeactivateMouse"},
         {21, nullptr, "SetMouseAutoPilotState"},
         {22, nullptr, "UnsetMouseAutoPilotState"},
@@ -37,7 +47,7 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
         {60, nullptr, "ClearNpadSystemCommonPolicy"},
         {61, nullptr, "DeactivateNpad"},
         {62, nullptr, "ForceDisconnectNpad"},
-        {91, nullptr, "DeactivateGesture"},
+        {91, &IHidDebugServer::DeactivateGesture, "DeactivateGesture"},
         {110, nullptr, "DeactivateHomeButton"},
         {111, nullptr, "SetHomeButtonAutoPilotState"},
         {112, nullptr, "UnsetHomeButtonAutoPilotState"},
@@ -150,6 +160,170 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
 }
 
 IHidDebugServer::~IHidDebugServer() = default;
+void IHidDebugServer::DeactivateTouchScreen(HLERequestContext& ctx) {
+    LOG_INFO(Service_HID, "called");
+
+    Result result = ResultSuccess;
+
+    if (!firmware_settings->IsDeviceManaged()) {
+        result = GetResourceManager()->GetTouchScreen()->Deactivate();
+    }
+
+    IPC::ResponseBuilder rb{ctx, 2};
+    rb.Push(result);
+}
+
+void IHidDebugServer::SetTouchScreenAutoPilotState(HLERequestContext& ctx) {
+    AutoPilotState auto_pilot{};
+    auto_pilot.count = ctx.GetReadBufferNumElements<TouchState>();
+    const auto buffer = ctx.ReadBuffer();
+
+    auto_pilot.count = std::min(auto_pilot.count, static_cast<u64>(auto_pilot.state.size()));
+    memcpy(auto_pilot.state.data(), buffer.data(), auto_pilot.count * sizeof(TouchState));
+
+    LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count);
+
+    const Result result =
+        GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot);
+
+    IPC::ResponseBuilder rb{ctx, 2};
+    rb.Push(result);
+}
+
+void IHidDebugServer::UnsetTouchScreenAutoPilotState(HLERequestContext& ctx) {
+    LOG_INFO(Service_HID, "called");
+
+    const Result result = GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState();
+
+    IPC::ResponseBuilder rb{ctx, 2};
+    rb.Push(result);
+}
+
+void IHidDebugServer::GetTouchScreenConfiguration(HLERequestContext& ctx) {
+    IPC::RequestParser rp{ctx};
+    const auto applet_resource_user_id{rp.Pop<u64>()};
+
+    LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
+    Core::HID::TouchScreenConfigurationForNx touchscreen_config{};
+    const Result result = GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration(
+        touchscreen_config, applet_resource_user_id);
+
+    if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
+        touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
+        touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
+    }
+
+    IPC::ResponseBuilder rb{ctx, 6};
+    rb.Push(result);
+    rb.PushRaw(touchscreen_config);
+}
+
+void IHidDebugServer::ProcessTouchScreenAutoTune(HLERequestContext& ctx) {
+    LOG_INFO(Service_HID, "called");
+
+    Result result = GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune();
+
+    IPC::ResponseBuilder rb{ctx, 2};
+    rb.Push(result);
+}
+
+void IHidDebugServer::ForceStopTouchScreenManagement(HLERequestContext& ctx) {
+    LOG_INFO(Service_HID, "called");
+
+    if (!firmware_settings->IsDeviceManaged()) {
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(ResultSuccess);
+        return;
+    }
+
+    Result result = ResultSuccess;
+    bool is_touch_active{};
+    bool is_gesture_active{};
+    auto touch_screen = GetResourceManager()->GetTouchScreen();
+    auto gesture = GetResourceManager()->GetGesture();
+
+    if (firmware_settings->IsTouchI2cManaged()) {
+        result = touch_screen->IsActive(is_touch_active);
+        if (result.IsSuccess()) {
+            result = gesture->IsActive(is_gesture_active);
+        }
+        if (result.IsSuccess() && is_touch_active) {
+            result = touch_screen->Deactivate();
+        }
+        if (result.IsSuccess() && is_gesture_active) {
+            result = gesture->Deactivate();
+        }
+    }
+
+    IPC::ResponseBuilder rb{ctx, 2};
+    rb.Push(result);
+}
+
+void IHidDebugServer::ForceRestartTouchScreenManagement(HLERequestContext& ctx) {
+    IPC::RequestParser rp{ctx};
+    struct Parameters {
+        u32 basic_gesture_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>()};
+
+    LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}",
+             parameters.basic_gesture_id, parameters.applet_resource_user_id);
+
+    Result result = ResultSuccess;
+    auto touch_screen = GetResourceManager()->GetTouchScreen();
+    auto gesture = GetResourceManager()->GetGesture();
+
+    if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) {
+        result = gesture->Activate();
+        if (result.IsSuccess()) {
+            result =
+                gesture->Activate(parameters.applet_resource_user_id, parameters.basic_gesture_id);
+        }
+        if (result.IsSuccess()) {
+            result = touch_screen->Activate();
+        }
+        if (result.IsSuccess()) {
+            result = touch_screen->Activate(parameters.applet_resource_user_id);
+        }
+    }
+
+    IPC::ResponseBuilder rb{ctx, 2};
+    rb.Push(result);
+}
+
+void IHidDebugServer::IsTouchScreenManaged(HLERequestContext& ctx) {
+    LOG_INFO(Service_HID, "called");
+
+    bool is_touch_active{};
+    bool is_gesture_active{};
+
+    Result result = GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active);
+    if (result.IsSuccess()) {
+        result = GetResourceManager()->GetGesture()->IsActive(is_gesture_active);
+    }
+
+    IPC::ResponseBuilder rb{ctx, 3};
+    rb.Push(result);
+    rb.Push(is_touch_active | is_gesture_active);
+}
+
+void IHidDebugServer::DeactivateGesture(HLERequestContext& ctx) {
+    LOG_INFO(Service_HID, "called");
+
+    Result result = ResultSuccess;
+
+    if (!firmware_settings->IsDeviceManaged()) {
+        result = GetResourceManager()->GetGesture()->Deactivate();
+    }
+
+    IPC::ResponseBuilder rb{ctx, 2};
+    rb.Push(result);
+}
 
 std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {
     resource_manager->Initialize();
diff --git a/src/core/hle/service/hid/hid_debug_server.h b/src/core/hle/service/hid/hid_debug_server.h
index 406db2211a..7d5b082b3a 100644
--- a/src/core/hle/service/hid/hid_debug_server.h
+++ b/src/core/hle/service/hid/hid_debug_server.h
@@ -11,16 +11,29 @@ class System;
 
 namespace Service::HID {
 class ResourceManager;
+class HidFirmwareSettings;
 
 class IHidDebugServer final : public ServiceFramework<IHidDebugServer> {
 public:
-    explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
+    explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
+                             std::shared_ptr<HidFirmwareSettings> settings);
     ~IHidDebugServer() override;
 
 private:
+    void DeactivateTouchScreen(HLERequestContext& ctx);
+    void SetTouchScreenAutoPilotState(HLERequestContext& ctx);
+    void UnsetTouchScreenAutoPilotState(HLERequestContext& ctx);
+    void GetTouchScreenConfiguration(HLERequestContext& ctx);
+    void ProcessTouchScreenAutoTune(HLERequestContext& ctx);
+    void ForceStopTouchScreenManagement(HLERequestContext& ctx);
+    void ForceRestartTouchScreenManagement(HLERequestContext& ctx);
+    void IsTouchScreenManaged(HLERequestContext& ctx);
+    void DeactivateGesture(HLERequestContext& ctx);
+
     std::shared_ptr<ResourceManager> GetResourceManager();
 
     std::shared_ptr<ResourceManager> resource_manager;
+    std::shared_ptr<HidFirmwareSettings> firmware_settings;
 };
 
 } // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
index 09c47b5e30..9ffe027ff8 100644
--- a/src/core/hle/service/hid/hid_server.cpp
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -989,8 +989,7 @@ void IHidServer::ActivateGesture(HLERequestContext& ctx) {
     }
 
     if (result.IsSuccess()) {
-        // TODO: Use gesture id here
-        result = gesture->Activate(parameters.applet_resource_user_id);
+        result = gesture->Activate(parameters.applet_resource_user_id, parameters.basic_gesture_id);
     }
 
     IPC::ResponseBuilder rb{ctx, 2};
@@ -2449,14 +2448,22 @@ void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) {
 
 void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
+    auto touchscreen_config{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
     const auto applet_resource_user_id{rp.Pop<u64>()};
 
-    LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}",
-                touchscreen_mode.mode, applet_resource_user_id);
+    LOG_INFO(Service_HID, "called, touchscreen_config={}, applet_resource_user_id={}",
+             touchscreen_config.mode, applet_resource_user_id);
+
+    if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
+        touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
+        touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
+    }
+
+    const Result result = GetResourceManager()->GetTouchScreen()->SetTouchScreenConfiguration(
+        touchscreen_config, applet_resource_user_id);
 
     IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
+    rb.Push(result);
 }
 
 void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) {
@@ -2484,11 +2491,12 @@ void IHidServer::SetTouchScreenResolution(HLERequestContext& ctx) {
     const auto height{rp.Pop<u32>()};
     const auto applet_resource_user_id{rp.Pop<u64>()};
 
-    GetResourceManager()->GetTouchScreen()->SetTouchscreenDimensions(width, height);
-
     LOG_INFO(Service_HID, "called, width={}, height={}, applet_resource_user_id={}", width, height,
              applet_resource_user_id);
 
+    GetResourceManager()->GetTouchScreen()->SetTouchScreenResolution(width, height,
+                                                                     applet_resource_user_id);
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp
index d1ec42edc3..22471e9e29 100644
--- a/src/core/hle/service/hid/hid_system_server.cpp
+++ b/src/core/hle/service/hid/hid_system_server.cpp
@@ -155,9 +155,9 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
         {1133, nullptr, "StartUsbFirmwareUpdate"},
         {1134, nullptr, "GetUsbFirmwareUpdateState"},
         {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"},
-        {1150, nullptr, "SetTouchScreenMagnification"},
-        {1151, nullptr, "GetTouchScreenFirmwareVersion"},
-        {1152, nullptr, "SetTouchScreenDefaultConfiguration"},
+        {1150, &IHidSystemServer::SetTouchScreenMagnification, "SetTouchScreenMagnification"},
+        {1151, &IHidSystemServer::GetTouchScreenFirmwareVersion, "GetTouchScreenFirmwareVersion"},
+        {1152, &IHidSystemServer::SetTouchScreenDefaultConfiguration, "SetTouchScreenDefaultConfiguration"},
         {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
         {1154, nullptr, "IsFirmwareAvailableForNotification"},
         {1155, &IHidSystemServer::SetForceHandheldStyleVibration, "SetForceHandheldStyleVibration"},
@@ -845,12 +845,60 @@ void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContex
     rb.Push(ResultSuccess);
 }
 
-void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
-    LOG_WARNING(Service_HID, "(STUBBED) called");
+void IHidSystemServer::SetTouchScreenMagnification(HLERequestContext& ctx) {
+    IPC::RequestParser rp{ctx};
+    const auto point1x{rp.Pop<f32>()};
+    const auto point1y{rp.Pop<f32>()};
+    const auto point2x{rp.Pop<f32>()};
+    const auto point2y{rp.Pop<f32>()};
 
-    Core::HID::TouchScreenConfigurationForNx touchscreen_config{
-        .mode = Core::HID::TouchScreenModeForNx::Finger,
-    };
+    LOG_INFO(Service_HID, "called, point1=-({},{}), point2=({},{})", point1x, point1y, point2x,
+             point2y);
+
+    const Result result = GetResourceManager()->GetTouchScreen()->SetTouchScreenMagnification(
+        point1x, point1y, point2x, point2y);
+
+    IPC::ResponseBuilder rb{ctx, 2};
+    rb.Push(result);
+}
+
+void IHidSystemServer::GetTouchScreenFirmwareVersion(HLERequestContext& ctx) {
+    LOG_INFO(Service_HID, "called");
+
+    Core::HID::FirmwareVersion firmware{};
+    const auto result = GetResourceManager()->GetTouchScreenFirmwareVersion(firmware);
+
+    IPC::ResponseBuilder rb{ctx, 6};
+    rb.Push(result);
+    rb.PushRaw(firmware);
+}
+
+void IHidSystemServer::SetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
+    IPC::RequestParser rp{ctx};
+    auto touchscreen_config{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
+
+    LOG_INFO(Service_HID, "called, touchscreen_config={}", touchscreen_config.mode);
+
+    if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
+        touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
+        touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
+    }
+
+    const Result result =
+        GetResourceManager()->GetTouchScreen()->SetTouchScreenDefaultConfiguration(
+            touchscreen_config);
+
+    IPC::ResponseBuilder rb{ctx, 2};
+    rb.Push(result);
+}
+
+void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
+    LOG_INFO(Service_HID, "called");
+
+    Core::HID::TouchScreenConfigurationForNx touchscreen_config{};
+    const Result result =
+        GetResourceManager()->GetTouchScreen()->GetTouchScreenDefaultConfiguration(
+            touchscreen_config);
 
     if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
         touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
@@ -858,7 +906,7 @@ void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx
     }
 
     IPC::ResponseBuilder rb{ctx, 6};
-    rb.Push(ResultSuccess);
+    rb.Push(result);
     rb.PushRaw(touchscreen_config);
 }
 
diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h
index 4ab4d3931f..738313e089 100644
--- a/src/core/hle/service/hid/hid_system_server.h
+++ b/src/core/hle/service/hid/hid_system_server.h
@@ -71,6 +71,9 @@ private:
     void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx);
     void CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx);
     void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
+    void SetTouchScreenMagnification(HLERequestContext& ctx);
+    void GetTouchScreenFirmwareVersion(HLERequestContext& ctx);
+    void SetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
     void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
     void SetForceHandheldStyleVibration(HLERequestContext& ctx);
     void IsUsingCustomButtonConfig(HLERequestContext& ctx);
diff --git a/src/core/hle/service/set/setting_formats/system_settings.h b/src/core/hle/service/set/setting_formats/system_settings.h
index ebc373da51..40230182ac 100644
--- a/src/core/hle/service/set/setting_formats/system_settings.h
+++ b/src/core/hle/service/set/setting_formats/system_settings.h
@@ -12,6 +12,7 @@
 #include "common/vector_math.h"
 #include "core/hle/service/set/setting_formats/private_settings.h"
 #include "core/hle/service/set/settings_types.h"
+#include "hid_core/resources/touch_screen/touch_types.h"
 
 namespace Service::Set {
 
@@ -257,8 +258,7 @@ struct SystemSettings {
     std::array<u8, 0x10> analog_stick_user_calibration_left;
     std::array<u8, 0x10> analog_stick_user_calibration_right;
 
-    // nn::settings::system::TouchScreenMode
-    s32 touch_screen_mode;
+    TouchScreenMode touch_screen_mode;
     INSERT_PADDING_BYTES(0x14); // Reserved
 
     TvSettings tv_settings;
diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp
index 100cb2db45..c889aec47a 100644
--- a/src/core/hle/service/set/system_settings_server.cpp
+++ b/src/core/hle/service/set/system_settings_server.cpp
@@ -275,8 +275,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
         {184, nullptr, "SetPlatformRegion"},
         {185, &ISystemSettingsServer::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"},
         {186, nullptr, "GetMemoryUsageRateFlag"},
-        {187, nullptr, "GetTouchScreenMode"},
-        {188, nullptr, "SetTouchScreenMode"},
+        {187, &ISystemSettingsServer::GetTouchScreenMode, "GetTouchScreenMode"},
+        {188, &ISystemSettingsServer::SetTouchScreenMode, "SetTouchScreenMode"},
         {189, nullptr, "GetButtonConfigSettingsFull"},
         {190, nullptr, "SetButtonConfigSettingsFull"},
         {191, nullptr, "GetButtonConfigSettingsEmbedded"},
@@ -1395,6 +1395,28 @@ void ISystemSettingsServer::GetHomeMenuSchemeModel(HLERequestContext& ctx) {
     rb.Push(0);
 }
 
+void ISystemSettingsServer::GetTouchScreenMode(HLERequestContext& ctx) {
+    TouchScreenMode touch_screen_mode{};
+    auto res = GetTouchScreenMode(touch_screen_mode);
+
+    LOG_INFO(Service_SET, "called, touch_screen_mode={}", touch_screen_mode);
+
+    IPC::ResponseBuilder rb{ctx, 3};
+    rb.Push(res);
+    rb.PushEnum(touch_screen_mode);
+}
+
+void ISystemSettingsServer::SetTouchScreenMode(HLERequestContext& ctx) {
+    IPC::RequestParser rp{ctx};
+    const auto touch_screen_mode = rp.PopEnum<TouchScreenMode>();
+    auto res = SetTouchScreenMode(touch_screen_mode);
+
+    LOG_INFO(Service_SET, "called, touch_screen_mode={}", touch_screen_mode);
+
+    IPC::ResponseBuilder rb{ctx, 2};
+    rb.Push(res);
+}
+
 void ISystemSettingsServer::GetFieldTestingFlag(HLERequestContext& ctx) {
     LOG_INFO(Service_SET, "called, field_testing_flag={}", m_system_settings.field_testing_flag);
 
@@ -1670,4 +1692,15 @@ Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
     R_SUCCEED();
 }
 
+Result ISystemSettingsServer::GetTouchScreenMode(TouchScreenMode& touch_screen_mode) const {
+    touch_screen_mode = m_system_settings.touch_screen_mode;
+    R_SUCCEED();
+}
+
+Result ISystemSettingsServer::SetTouchScreenMode(TouchScreenMode touch_screen_mode) {
+    m_system_settings.touch_screen_mode = touch_screen_mode;
+    SetSaveNeeded();
+    R_SUCCEED();
+}
+
 } // namespace Service::Set
diff --git a/src/core/hle/service/set/system_settings_server.h b/src/core/hle/service/set/system_settings_server.h
index 1982b97232..9a3b36f0ca 100644
--- a/src/core/hle/service/set/system_settings_server.h
+++ b/src/core/hle/service/set/system_settings_server.h
@@ -74,6 +74,8 @@ public:
         Service::PSC::Time::SteadyClockTimePoint& out_time_point) const;
     Result SetUserSystemClockAutomaticCorrectionUpdatedTime(
         const Service::PSC::Time::SteadyClockTimePoint& time_point);
+    Result GetTouchScreenMode(TouchScreenMode& touch_screen_mode) const;
+    Result SetTouchScreenMode(TouchScreenMode touch_screen_mode);
 
 private:
     void SetLanguageCode(HLERequestContext& ctx);
@@ -154,6 +156,8 @@ private:
     void GetChineseTraditionalInputMethod(HLERequestContext& ctx);
     void GetHomeMenuScheme(HLERequestContext& ctx);
     void GetHomeMenuSchemeModel(HLERequestContext& ctx);
+    void GetTouchScreenMode(HLERequestContext& ctx);
+    void SetTouchScreenMode(HLERequestContext& ctx);
     void GetFieldTestingFlag(HLERequestContext& ctx);
     void GetPanelCrcMode(HLERequestContext& ctx);
     void SetPanelCrcMode(HLERequestContext& ctx);
diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp
index 905f351184..d34624d284 100644
--- a/src/frontend_common/config.cpp
+++ b/src/frontend_common/config.cpp
@@ -190,9 +190,9 @@ void Config::ReadTouchscreenValues() {
     Settings::values.touchscreen.rotation_angle =
         static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_angle"), 0));
     Settings::values.touchscreen.diameter_x =
-        static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 15));
+        static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 90));
     Settings::values.touchscreen.diameter_y =
-        static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 15));
+        static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 90));
 }
 
 void Config::ReadAudioValues() {
@@ -478,9 +478,9 @@ void Config::SaveTouchscreenValues() {
     WriteIntegerSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle,
                         std::make_optional(static_cast<u32>(0)));
     WriteIntegerSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x,
-                        std::make_optional(static_cast<u32>(15)));
+                        std::make_optional(static_cast<u32>(90)));
     WriteIntegerSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y,
-                        std::make_optional(static_cast<u32>(15)));
+                        std::make_optional(static_cast<u32>(90)));
 }
 
 void Config::SaveMotionTouchValues() {
diff --git a/src/hid_core/CMakeLists.txt b/src/hid_core/CMakeLists.txt
index aa85502b56..0559542249 100644
--- a/src/hid_core/CMakeLists.txt
+++ b/src/hid_core/CMakeLists.txt
@@ -98,9 +98,14 @@ add_library(hid_core STATIC
     resources/system_buttons/sleep_button.h
     resources/touch_screen/gesture.cpp
     resources/touch_screen/gesture.h
-    resources/touch_screen/gesture_types.h
+    resources/touch_screen/gesture_handler.cpp
+    resources/touch_screen/gesture_handler.h
     resources/touch_screen/touch_screen.cpp
     resources/touch_screen/touch_screen.h
+    resources/touch_screen/touch_screen_driver.cpp
+    resources/touch_screen/touch_screen_driver.h
+    resources/touch_screen/touch_screen_resource.cpp
+    resources/touch_screen/touch_screen_resource.h
     resources/touch_screen/touch_types.h
     resources/unique_pad/unique_pad.cpp
     resources/unique_pad/unique_pad.h
diff --git a/src/hid_core/hid_result.h b/src/hid_core/hid_result.h
index df9b28c9a8..c8dd07bfe7 100644
--- a/src/hid_core/hid_result.h
+++ b/src/hid_core/hid_result.h
@@ -8,6 +8,10 @@
 namespace Service::HID {
 
 constexpr Result PalmaResultSuccess{ErrorModule::HID, 0};
+
+constexpr Result ResultTouchNotInitialized{ErrorModule::HID, 41};
+constexpr Result ResultTouchOverflow{ErrorModule::HID, 42};
+
 constexpr Result NpadInvalidHandle{ErrorModule::HID, 100};
 constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
 
@@ -23,6 +27,10 @@ constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423};
 constexpr Result ResultNfcIsNotReady{ErrorModule::HID, 461};
 constexpr Result ResultNfcXcdHandleIsNotInitialized{ErrorModule::HID, 464};
 constexpr Result ResultIrSensorIsNotReady{ErrorModule::HID, 501};
+
+constexpr Result ResultGestureOverflow{ErrorModule::HID, 522};
+constexpr Result ResultGestureNotInitialized{ErrorModule::HID, 523};
+
 constexpr Result ResultMcuIsNotReady{ErrorModule::HID, 541};
 
 constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
diff --git a/src/hid_core/hid_types.h b/src/hid_core/hid_types.h
index a01292a702..3cf12f6e50 100644
--- a/src/hid_core/hid_types.h
+++ b/src/hid_core/hid_types.h
@@ -299,12 +299,6 @@ enum class GyroscopeZeroDriftMode : u32 {
     Tight = 2,
 };
 
-// This is nn::settings::system::TouchScreenMode
-enum class TouchScreenMode : u32 {
-    Stylus = 0,
-    Standard = 1,
-};
-
 // This is nn::hid::TouchScreenModeForNx
 enum class TouchScreenModeForNx : u8 {
     UseSystemSetting,
@@ -354,18 +348,6 @@ struct TouchAttribute {
 };
 static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size");
 
-// This is nn::hid::TouchState
-struct TouchState {
-    u64 delta_time{};
-    TouchAttribute attribute{};
-    u32 finger{};
-    Common::Point<u32> position{};
-    u32 diameter_x{};
-    u32 diameter_y{};
-    u32 rotation_angle{};
-};
-static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
-
 struct TouchFinger {
     u64 last_touch{};
     Common::Point<float> position{};
@@ -743,4 +725,14 @@ struct UniquePadId {
 };
 static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size");
 
+// This is nn::hid::system::FirmwareVersion
+struct FirmwareVersion {
+    u8 major;
+    u8 minor;
+    u8 micro;
+    u8 revision;
+    std::array<char, 0xc> device_identifier;
+};
+static_assert(sizeof(FirmwareVersion) == 0x10, "FirmwareVersion is an invalid size");
+
 } // namespace Core::HID
diff --git a/src/hid_core/resource_manager.cpp b/src/hid_core/resource_manager.cpp
index e78665d316..68ce2c7aef 100644
--- a/src/hid_core/resource_manager.cpp
+++ b/src/hid_core/resource_manager.cpp
@@ -15,6 +15,7 @@
 #include "hid_core/resources/applet_resource.h"
 #include "hid_core/resources/debug_pad/debug_pad.h"
 #include "hid_core/resources/digitizer/digitizer.h"
+#include "hid_core/resources/hid_firmware_settings.h"
 #include "hid_core/resources/keyboard/keyboard.h"
 #include "hid_core/resources/mouse/debug_mouse.h"
 #include "hid_core/resources/mouse/mouse.h"
@@ -29,6 +30,8 @@
 #include "hid_core/resources/system_buttons/sleep_button.h"
 #include "hid_core/resources/touch_screen/gesture.h"
 #include "hid_core/resources/touch_screen/touch_screen.h"
+#include "hid_core/resources/touch_screen/touch_screen_driver.h"
+#include "hid_core/resources/touch_screen/touch_screen_resource.h"
 #include "hid_core/resources/unique_pad/unique_pad.h"
 #include "hid_core/resources/vibration/gc_vibration_device.h"
 #include "hid_core/resources/vibration/n64_vibration_device.h"
@@ -45,12 +48,16 @@ constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; //
 constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
 constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000};         // (5ms, 200Hz)
 
-ResourceManager::ResourceManager(Core::System& system_)
-    : system{system_}, service_context{system_, "hid"} {
+ResourceManager::ResourceManager(Core::System& system_,
+                                 std::shared_ptr<HidFirmwareSettings> settings)
+    : firmware_settings{settings}, system{system_}, service_context{system_, "hid"} {
     applet_resource = std::make_shared<AppletResource>(system);
 }
 
-ResourceManager::~ResourceManager() = default;
+ResourceManager::~ResourceManager() {
+    system.CoreTiming().UnscheduleEvent(touch_update_event);
+    input_event->Finalize();
+};
 
 void ResourceManager::Initialize() {
     if (is_initialized) {
@@ -59,7 +66,9 @@ void ResourceManager::Initialize() {
 
     system.HIDCore().ReloadInputDevices();
 
-    handheld_config = std::make_shared<HandheldConfig>();
+    input_event = service_context.CreateEvent("ResourceManager:InputEvent");
+
+    InitializeHandheldConfig();
     InitializeHidCommonSampler();
     InitializeTouchScreenSampler();
     InitializeConsoleSixAxisSampler();
@@ -154,6 +163,7 @@ Result ResourceManager::CreateAppletResource(u64 aruid) {
     npad->Activate();
     six_axis->Activate();
     touch_screen->Activate();
+    gesture->Activate();
 
     return GetNpad()->ActivateNpadResource(aruid);
 }
@@ -163,6 +173,17 @@ Result ResourceManager::CreateAppletResourceImpl(u64 aruid) {
     return applet_resource->CreateAppletResource(aruid);
 }
 
+void ResourceManager::InitializeHandheldConfig() {
+    handheld_config = std::make_shared<HandheldConfig>();
+    handheld_config->is_handheld_hid_enabled = true;
+    handheld_config->is_joycon_rail_enabled = true;
+    handheld_config->is_force_handheld_style_vibration = false;
+    handheld_config->is_force_handheld = false;
+    if (firmware_settings->IsHandheldForced()) {
+        handheld_config->is_joycon_rail_enabled = false;
+    }
+}
+
 void ResourceManager::InitializeHidCommonSampler() {
     debug_pad = std::make_shared<DebugPad>(system.HIDCore());
     mouse = std::make_shared<Mouse>(system.HIDCore());
@@ -170,7 +191,6 @@ void ResourceManager::InitializeHidCommonSampler() {
     keyboard = std::make_shared<Keyboard>(system.HIDCore());
     unique_pad = std::make_shared<UniquePad>(system.HIDCore());
     npad = std::make_shared<NPad>(system.HIDCore(), service_context);
-    gesture = std::make_shared<Gesture>(system.HIDCore());
     home_button = std::make_shared<HomeButton>(system.HIDCore());
     sleep_button = std::make_shared<SleepButton>(system.HIDCore());
     capture_button = std::make_shared<CaptureButton>(system.HIDCore());
@@ -185,7 +205,8 @@ void ResourceManager::InitializeHidCommonSampler() {
 
     const auto settings =
         system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
-    npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config, settings);
+    npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config, input_event,
+                           &input_mutex, settings);
 
     six_axis->SetAppletResource(applet_resource, &shared_mutex);
     mouse->SetAppletResource(applet_resource, &shared_mutex);
@@ -196,11 +217,25 @@ void ResourceManager::InitializeHidCommonSampler() {
 }
 
 void ResourceManager::InitializeTouchScreenSampler() {
-    gesture = std::make_shared<Gesture>(system.HIDCore());
-    touch_screen = std::make_shared<TouchScreen>(system.HIDCore());
+    // This is nn.hid.TouchScreenSampler
+    touch_resource = std::make_shared<TouchResource>(system);
+    touch_driver = std::make_shared<TouchDriver>(system.HIDCore());
+    touch_screen = std::make_shared<TouchScreen>(touch_resource);
+    gesture = std::make_shared<Gesture>(touch_resource);
 
-    touch_screen->SetAppletResource(applet_resource, &shared_mutex);
-    gesture->SetAppletResource(applet_resource, &shared_mutex);
+    touch_update_event = Core::Timing::CreateEvent(
+        "HID::TouchUpdateCallback",
+        [this](s64 time,
+               std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
+            touch_resource->OnTouchUpdate(time);
+            return std::nullopt;
+        });
+
+    touch_resource->SetTouchDriver(touch_driver);
+    touch_resource->SetAppletResource(applet_resource, &shared_mutex);
+    touch_resource->SetInputEvent(input_event, &input_mutex);
+    touch_resource->SetHandheldConfig(handheld_config);
+    touch_resource->SetTimerEvent(touch_update_event);
 }
 
 void ResourceManager::InitializeConsoleSixAxisSampler() {
@@ -388,13 +423,15 @@ Result ResourceManager::SendVibrationValue(u64 aruid,
     return result;
 }
 
+Result ResourceManager::GetTouchScreenFirmwareVersion(Core::HID::FirmwareVersion& firmware) const {
+    return ResultSuccess;
+}
+
 void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) {
     auto& core_timing = system.CoreTiming();
     debug_pad->OnUpdate(core_timing);
     digitizer->OnUpdate(core_timing);
     unique_pad->OnUpdate(core_timing);
-    gesture->OnUpdate(core_timing);
-    touch_screen->OnUpdate(core_timing);
     palma->OnUpdate(core_timing);
     home_button->OnUpdate(core_timing);
     sleep_button->OnUpdate(core_timing);
diff --git a/src/hid_core/resource_manager.h b/src/hid_core/resource_manager.h
index 128e001252..0bfe095113 100644
--- a/src/hid_core/resource_manager.h
+++ b/src/hid_core/resource_manager.h
@@ -11,6 +11,7 @@ class System;
 }
 
 namespace Core::HID {
+struct FirmwareVersion;
 struct VibrationDeviceHandle;
 struct VibrationValue;
 struct VibrationDeviceInfo;
@@ -21,8 +22,9 @@ struct EventType;
 }
 
 namespace Kernel {
+class KEvent;
 class KSharedMemory;
-}
+} // namespace Kernel
 
 namespace Service::HID {
 class AppletResource;
@@ -33,6 +35,7 @@ class DebugMouse;
 class DebugPad;
 class Digitizer;
 class Gesture;
+class HidFirmwareSettings;
 class HomeButton;
 class Keyboard;
 class Mouse;
@@ -42,6 +45,8 @@ class SevenSixAxis;
 class SixAxis;
 class SleepButton;
 class TouchScreen;
+class TouchDriver;
+class TouchResource;
 class UniquePad;
 class NpadVibrationBase;
 class NpadN64VibrationDevice;
@@ -52,7 +57,7 @@ struct HandheldConfig;
 class ResourceManager {
 
 public:
-    explicit ResourceManager(Core::System& system_);
+    explicit ResourceManager(Core::System& system_, std::shared_ptr<HidFirmwareSettings> settings);
     ~ResourceManager();
 
     void Initialize();
@@ -102,6 +107,8 @@ public:
     Result SendVibrationValue(u64 aruid, const Core::HID::VibrationDeviceHandle& handle,
                               const Core::HID::VibrationValue& value);
 
+    Result GetTouchScreenFirmwareVersion(Core::HID::FirmwareVersion& firmware) const;
+
     void UpdateControllers(std::chrono::nanoseconds ns_late);
     void UpdateNpad(std::chrono::nanoseconds ns_late);
     void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late);
@@ -109,6 +116,7 @@ public:
 
 private:
     Result CreateAppletResourceImpl(u64 aruid);
+    void InitializeHandheldConfig();
     void InitializeHidCommonSampler();
     void InitializeTouchScreenSampler();
     void InitializeConsoleSixAxisSampler();
@@ -117,37 +125,46 @@ private:
     bool is_initialized{false};
 
     mutable std::recursive_mutex shared_mutex;
-    std::shared_ptr<AppletResource> applet_resource = nullptr;
+    std::shared_ptr<AppletResource> applet_resource{nullptr};
 
-    std::shared_ptr<CaptureButton> capture_button = nullptr;
-    std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr;
-    std::shared_ptr<DebugMouse> debug_mouse = nullptr;
-    std::shared_ptr<DebugPad> debug_pad = nullptr;
-    std::shared_ptr<Digitizer> digitizer = nullptr;
-    std::shared_ptr<Gesture> gesture = nullptr;
-    std::shared_ptr<HomeButton> home_button = nullptr;
-    std::shared_ptr<Keyboard> keyboard = nullptr;
-    std::shared_ptr<Mouse> mouse = nullptr;
-    std::shared_ptr<NPad> npad = nullptr;
-    std::shared_ptr<Palma> palma = nullptr;
-    std::shared_ptr<SevenSixAxis> seven_six_axis = nullptr;
-    std::shared_ptr<SixAxis> six_axis = nullptr;
-    std::shared_ptr<SleepButton> sleep_button = nullptr;
-    std::shared_ptr<TouchScreen> touch_screen = nullptr;
-    std::shared_ptr<UniquePad> unique_pad = nullptr;
+    mutable std::mutex input_mutex;
+    Kernel::KEvent* input_event{nullptr};
 
-    std::shared_ptr<HandheldConfig> handheld_config = nullptr;
+    std::shared_ptr<HandheldConfig> handheld_config{nullptr};
+    std::shared_ptr<HidFirmwareSettings> firmware_settings{nullptr};
+
+    std::shared_ptr<CaptureButton> capture_button{nullptr};
+    std::shared_ptr<ConsoleSixAxis> console_six_axis{nullptr};
+    std::shared_ptr<DebugMouse> debug_mouse{nullptr};
+    std::shared_ptr<DebugPad> debug_pad{nullptr};
+    std::shared_ptr<Digitizer> digitizer{nullptr};
+    std::shared_ptr<HomeButton> home_button{nullptr};
+    std::shared_ptr<Keyboard> keyboard{nullptr};
+    std::shared_ptr<Mouse> mouse{nullptr};
+    std::shared_ptr<NPad> npad{nullptr};
+    std::shared_ptr<Palma> palma{nullptr};
+    std::shared_ptr<SevenSixAxis> seven_six_axis{nullptr};
+    std::shared_ptr<SixAxis> six_axis{nullptr};
+    std::shared_ptr<SleepButton> sleep_button{nullptr};
+    std::shared_ptr<UniquePad> unique_pad{nullptr};
 
     // TODO: Create these resources
-    // std::shared_ptr<AudioControl> audio_control = nullptr;
-    // std::shared_ptr<ButtonConfig> button_config = nullptr;
-    // std::shared_ptr<Config> config = nullptr;
-    // std::shared_ptr<Connection> connection = nullptr;
-    // std::shared_ptr<CustomConfig> custom_config = nullptr;
-    // std::shared_ptr<Digitizer> digitizer = nullptr;
-    // std::shared_ptr<Hdls> hdls = nullptr;
-    // std::shared_ptr<PlayReport> play_report = nullptr;
-    // std::shared_ptr<Rail> rail = nullptr;
+    // std::shared_ptr<AudioControl> audio_control{nullptr};
+    // std::shared_ptr<ButtonConfig> button_config{nullptr};
+    // std::shared_ptr<Config> config{nullptr};
+    // std::shared_ptr<Connection> connection{nullptr};
+    // std::shared_ptr<CustomConfig> custom_config{nullptr};
+    // std::shared_ptr<Digitizer> digitizer{nullptr};
+    // std::shared_ptr<Hdls> hdls{nullptr};
+    // std::shared_ptr<PlayReport> play_report{nullptr};
+    // std::shared_ptr<Rail> rail{nullptr};
+
+    // Touch Resources
+    std::shared_ptr<Gesture> gesture{nullptr};
+    std::shared_ptr<TouchScreen> touch_screen{nullptr};
+    std::shared_ptr<TouchResource> touch_resource{nullptr};
+    std::shared_ptr<TouchDriver> touch_driver{nullptr};
+    std::shared_ptr<Core::Timing::EventType> touch_update_event{nullptr};
 
     Core::System& system;
     KernelHelpers::ServiceContext service_context;
@@ -162,12 +179,12 @@ public:
 private:
     void GetSharedMemoryHandle(HLERequestContext& ctx);
 
-    std::shared_ptr<Core::Timing::EventType> npad_update_event;
-    std::shared_ptr<Core::Timing::EventType> default_update_event;
-    std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
-    std::shared_ptr<Core::Timing::EventType> motion_update_event;
+    std::shared_ptr<Core::Timing::EventType> npad_update_event{nullptr};
+    std::shared_ptr<Core::Timing::EventType> default_update_event{nullptr};
+    std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event{nullptr};
+    std::shared_ptr<Core::Timing::EventType> motion_update_event{nullptr};
 
-    u64 aruid;
+    u64 aruid{};
     std::shared_ptr<ResourceManager> resource_manager;
 };
 
diff --git a/src/hid_core/resources/applet_resource.h b/src/hid_core/resources/applet_resource.h
index e9710d3063..4a5416fb29 100644
--- a/src/hid_core/resources/applet_resource.h
+++ b/src/hid_core/resources/applet_resource.h
@@ -13,11 +13,12 @@
 
 namespace Core {
 class System;
-}
+} // namespace Core
 
 namespace Kernel {
+class KEvent;
 class KSharedMemory;
-}
+} // namespace Kernel
 
 namespace Service::HID {
 struct SharedMemoryFormat;
@@ -73,7 +74,8 @@ struct AppletResourceHolder {
     std::recursive_mutex* shared_mutex{nullptr};
     NPadResource* shared_npad_resource{nullptr};
     std::shared_ptr<HandheldConfig> handheld_config{nullptr};
-    long* handle_1;
+    Kernel::KEvent* input_event{nullptr};
+    std::mutex* input_mutex{nullptr};
 };
 
 class AppletResource {
diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp
index cde84b1bb2..8ab26bc360 100644
--- a/src/hid_core/resources/npad/npad.cpp
+++ b/src/hid_core/resources/npad/npad.cpp
@@ -1081,11 +1081,14 @@ void NPad::UnregisterAppletResourceUserId(u64 aruid) {
 void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource,
                             std::recursive_mutex* shared_mutex,
                             std::shared_ptr<HandheldConfig> handheld_config,
+                            Kernel::KEvent* input_event, std::mutex* input_mutex,
                             std::shared_ptr<Service::Set::ISystemSettingsServer> settings) {
     applet_resource_holder.applet_resource = resource;
     applet_resource_holder.shared_mutex = shared_mutex;
     applet_resource_holder.shared_npad_resource = &npad_resource;
     applet_resource_holder.handheld_config = handheld_config;
+    applet_resource_holder.input_event = input_event;
+    applet_resource_holder.input_mutex = input_mutex;
 
     vibration_handler.SetSettingsService(settings);
 
diff --git a/src/hid_core/resources/npad/npad.h b/src/hid_core/resources/npad/npad.h
index 502cb9b55c..e81cc3abe3 100644
--- a/src/hid_core/resources/npad/npad.h
+++ b/src/hid_core/resources/npad/npad.h
@@ -133,6 +133,7 @@ public:
     void SetNpadExternals(std::shared_ptr<AppletResource> resource,
                           std::recursive_mutex* shared_mutex,
                           std::shared_ptr<HandheldConfig> handheld_config,
+                          Kernel::KEvent* input_event, std::mutex* input_mutex,
                           std::shared_ptr<Service::Set::ISystemSettingsServer> settings);
 
     AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
@@ -206,9 +207,6 @@ private:
     std::array<AbstractPad, MaxSupportedNpadIdTypes> abstracted_pads;
     NpadVibration vibration_handler{};
 
-    Kernel::KEvent* input_event{nullptr};
-    std::mutex* input_mutex{nullptr};
-
     std::atomic<u64> press_state{};
     std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax>
         controller_data{};
diff --git a/src/hid_core/resources/touch_screen/gesture.cpp b/src/hid_core/resources/touch_screen/gesture.cpp
index 0ecc0941f9..eaa0cc7d0b 100644
--- a/src/hid_core/resources/touch_screen/gesture.cpp
+++ b/src/hid_core/resources/touch_screen/gesture.cpp
@@ -1,366 +1,53 @@
-// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
 
-#include "common/math_util.h"
-#include "common/settings.h"
-#include "core/frontend/emu_window.h"
-#include "hid_core/frontend/emulated_console.h"
-#include "hid_core/hid_core.h"
-#include "hid_core/resources/applet_resource.h"
-#include "hid_core/resources/shared_memory_format.h"
 #include "hid_core/resources/touch_screen/gesture.h"
+#include "hid_core/resources/touch_screen/touch_screen_resource.h"
 
 namespace Service::HID {
-// HW is around 700, value is set to 400 to make it easier to trigger with mouse
-constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s
-constexpr f32 angle_threshold = 0.015f; // Threshold in radians
-constexpr f32 pinch_threshold = 0.5f;   // Threshold in pixels
-constexpr f32 press_delay = 0.5f;       // Time in seconds
-constexpr f32 double_tap_delay = 0.35f; // Time in seconds
 
-constexpr f32 Square(s32 num) {
-    return static_cast<f32>(num * num);
-}
+Gesture::Gesture(std::shared_ptr<TouchResource> resource) : touch_resource{resource} {}
 
-Gesture::Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) {
-    console = hid_core.GetEmulatedConsole();
-}
 Gesture::~Gesture() = default;
 
-void Gesture::OnInit() {
-    std::scoped_lock shared_lock{*shared_mutex};
-    const u64 aruid = applet_resource->GetActiveAruid();
-    auto* data = applet_resource->GetAruidData(aruid);
+Result Gesture::Activate() {
+    std::scoped_lock lock{mutex};
 
-    if (data == nullptr || !data->flag.is_assigned) {
-        return;
+    // TODO: Result result = CreateThread();
+    Result result = ResultSuccess;
+    if (result.IsError()) {
+        return result;
     }
 
-    shared_memory = &data->shared_memory_format->gesture;
-    shared_memory->gesture_lifo.buffer_count = 0;
-    shared_memory->gesture_lifo.buffer_tail = 0;
-    force_update = true;
+    result = touch_resource->ActivateGesture();
+
+    if (result.IsError()) {
+        // TODO: StopThread();
+    }
+
+    return result;
 }
 
-void Gesture::OnRelease() {}
-
-void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
-    std::scoped_lock shared_lock{*shared_mutex};
-    const u64 aruid = applet_resource->GetActiveAruid();
-    auto* data = applet_resource->GetAruidData(aruid);
-
-    if (data == nullptr || !data->flag.is_assigned) {
-        return;
-    }
-
-    shared_memory = &data->shared_memory_format->gesture;
-
-    if (!IsControllerActivated()) {
-        shared_memory->gesture_lifo.buffer_count = 0;
-        shared_memory->gesture_lifo.buffer_tail = 0;
-        return;
-    }
-
-    ReadTouchInput();
-
-    GestureProperties gesture = GetGestureProperties();
-    f32 time_difference =
-        static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) /
-        (1000 * 1000 * 1000);
-
-    // Only update if necessary
-    if (!ShouldUpdateGesture(gesture, time_difference)) {
-        return;
-    }
-
-    last_update_timestamp = shared_memory->gesture_lifo.timestamp;
-    UpdateGestureSharedMemory(gesture, time_difference);
+Result Gesture::Activate(u64 aruid, u32 basic_gesture_id) {
+    std::scoped_lock lock{mutex};
+    return touch_resource->ActivateGesture(aruid, basic_gesture_id);
 }
 
-void Gesture::ReadTouchInput() {
-    if (!Settings::values.touchscreen.enabled) {
-        fingers = {};
-        return;
+Result Gesture::Deactivate() {
+    std::scoped_lock lock{mutex};
+    const auto result = touch_resource->DeactivateGesture();
+
+    if (result.IsError()) {
+        return result;
     }
 
-    const auto touch_status = console->GetTouch();
-    for (std::size_t id = 0; id < fingers.size(); ++id) {
-        fingers[id] = touch_status[id];
-    }
+    // TODO: return StopThread();
+    return ResultSuccess;
 }
 
-bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) {
-    const auto& last_entry = GetLastGestureEntry();
-    if (force_update) {
-        force_update = false;
-        return true;
-    }
-
-    // Update if coordinates change
-    for (size_t id = 0; id < MAX_POINTS; id++) {
-        if (gesture.points[id] != last_gesture.points[id]) {
-            return true;
-        }
-    }
-
-    // Update on press and hold event after 0.5 seconds
-    if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 &&
-        time_difference > press_delay) {
-        return enable_press_and_tap;
-    }
-
-    return false;
-}
-
-void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) {
-    GestureType type = GestureType::Idle;
-    GestureAttribute attributes{};
-
-    const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state;
-
-    // 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 (gesture.active_points > 0) {
-        if (last_gesture.active_points == 0) {
-            NewGesture(gesture, type, attributes);
-        } else {
-            UpdateExistingGesture(gesture, type, time_difference);
-        }
-    } else {
-        EndGesture(gesture, last_gesture, type, attributes, time_difference);
-    }
-
-    // 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;
-    last_gesture = gesture;
-
-    shared_memory->gesture_lifo.WriteNextEntry(next_state);
-}
-
-void Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
-                         GestureAttribute& attributes) {
-    const auto& last_entry = GetLastGestureEntry();
-
-    gesture.detection_count++;
-    type = GestureType::Touch;
-
-    // New touch after cancel is not considered new
-    if (last_entry.type != GestureType::Cancel) {
-        attributes.is_new_touch.Assign(1);
-        enable_press_and_tap = true;
-    }
-}
-
-void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& 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;
-            break;
-        }
-    }
-
-    // Number of fingers changed cancel the last event and clear data
-    if (gesture.active_points != last_gesture.active_points) {
-        type = GestureType::Cancel;
-        enable_press_and_tap = false;
-        gesture.active_points = 0;
-        gesture.mid_point = {};
-        gesture.points.fill({});
-        return;
-    }
-
-    // Calculate extra parameters of panning
-    if (type == GestureType::Pan) {
-        UpdatePanEvent(gesture, last_gesture, type, time_difference);
-        return;
-    }
-
-    // Promote to press type
-    if (last_entry.type == GestureType::Touch) {
-        type = GestureType::Press;
-    }
-}
-
-void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
-                         GestureType& type, GestureAttribute& attributes, f32 time_difference) {
-    const auto& last_entry = GetLastGestureEntry();
-
-    if (last_gesture_props.active_points != 0) {
-        switch (last_entry.type) {
-        case GestureType::Touch:
-            if (enable_press_and_tap) {
-                SetTapEvent(gesture, last_gesture_props, type, attributes);
-                return;
-            }
-            type = GestureType::Cancel;
-            force_update = true;
-            break;
-        case GestureType::Press:
-        case GestureType::Tap:
-        case GestureType::Swipe:
-        case GestureType::Pinch:
-        case GestureType::Rotate:
-            type = GestureType::Complete;
-            force_update = true;
-            break;
-        case GestureType::Pan:
-            EndPanEvent(gesture, last_gesture_props, type, time_difference);
-            break;
-        default:
-            break;
-        }
-        return;
-    }
-    if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) {
-        gesture.detection_count++;
-    }
-}
-
-void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
-                          GestureType& type, GestureAttribute& attributes) {
-    type = GestureType::Tap;
-    gesture = last_gesture_props;
-    force_update = true;
-    f32 tap_time_difference =
-        static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000);
-    last_tap_timestamp = last_update_timestamp;
-    if (tap_time_difference < double_tap_delay) {
-        attributes.is_double_tap.Assign(1);
-    }
-}
-
-void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
-                             GestureType& type, f32 time_difference) {
-    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;
-    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;
-    }
-
-    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;
-    }
-}
-
-void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
-                          GestureType& type, f32 time_difference) {
-    const auto& last_entry = GetLastGestureEntry();
-    next_state.vel_x =
-        static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
-    next_state.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));
-
-    // Set swipe event with parameters
-    if (curr_vel > swipe_threshold) {
-        SetSwipeEvent(gesture, last_gesture_props, type);
-        return;
-    }
-
-    // End panning without swipe
-    type = GestureType::Complete;
-    next_state.vel_x = 0;
-    next_state.vel_y = 0;
-    force_update = true;
-}
-
-void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
-                            GestureType& type) {
-    const auto& last_entry = GetLastGestureEntry();
-
-    type = GestureType::Swipe;
-    gesture = last_gesture_props;
-    force_update = true;
-    next_state.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;
-            return;
-        }
-        next_state.direction = GestureDirection::Left;
-        return;
-    }
-    if (next_state.delta.y > 0) {
-        next_state.direction = GestureDirection::Down;
-        return;
-    }
-    next_state.direction = GestureDirection::Up;
-}
-
-const GestureState& Gesture::GetLastGestureEntry() const {
-    return shared_memory->gesture_lifo.ReadCurrentEntry().state;
-}
-
-GestureProperties Gesture::GetGestureProperties() {
-    GestureProperties gesture;
-    std::array<Core::HID::TouchFinger, 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;
-        gesture.points[id] = {
-            .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width),
-            .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height),
-        };
-
-        // Hack: There is no touch in docked but games still allow it
-        if (Settings::IsDockedMode()) {
-            gesture.points[id] = {
-                .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width),
-                .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height),
-            };
-        }
-
-        gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points);
-        gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points);
-    }
-
-    for (size_t id = 0; id < gesture.active_points; ++id) {
-        const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) +
-                                       Square(gesture.mid_point.y - gesture.points[id].y));
-        gesture.average_distance += distance / static_cast<f32>(gesture.active_points);
-    }
-
-    gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y),
-                               static_cast<f32>(gesture.mid_point.x - gesture.points[0].x));
-
-    gesture.detection_count = last_gesture.detection_count;
-
-    return gesture;
+Result Gesture::IsActive(bool& out_is_active) const {
+    out_is_active = touch_resource->IsGestureActive();
+    return ResultSuccess;
 }
 
 } // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/gesture.h b/src/hid_core/resources/touch_screen/gesture.h
index 32e9a8690e..d92912bb6e 100644
--- a/src/hid_core/resources/touch_screen/gesture.h
+++ b/src/hid_core/resources/touch_screen/gesture.h
@@ -1,87 +1,32 @@
-// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
 
 #pragma once
 
-#include <array>
+#include <mutex>
 
 #include "common/common_types.h"
-#include "hid_core/resources/controller_base.h"
-#include "hid_core/resources/touch_screen/touch_types.h"
-
-namespace Core::HID {
-class EmulatedConsole;
-}
+#include "core/hle/result.h"
 
 namespace Service::HID {
-struct GestureSharedMemoryFormat;
+class TouchResource;
 
-class Gesture final : public ControllerBase {
+/// Handles gesture request from HID interfaces
+class Gesture {
 public:
-    explicit Gesture(Core::HID::HIDCore& hid_core_);
-    ~Gesture() override;
+    Gesture(std::shared_ptr<TouchResource> resource);
+    ~Gesture();
 
-    // Called when the controller is initialized
-    void OnInit() override;
+    Result Activate();
+    Result Activate(u64 aruid, u32 basic_gesture_id);
 
-    // When the controller is released
-    void OnRelease() override;
+    Result Deactivate();
 
-    // When the controller is requesting an update for the shared memory
-    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
+    Result IsActive(bool& out_is_active) const;
 
 private:
-    // Reads input from all available input engines
-    void ReadTouchInput();
-
-    // Returns true if gesture state needs to be updated
-    bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference);
-
-    // Updates the shared memory to the next state
-    void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference);
-
-    // Initializes new gesture
-    void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
-
-    // Updates existing gesture state
-    void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference);
-
-    // Terminates exiting gesture
-    void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
-                    GestureType& type, GestureAttribute& attributes, f32 time_difference);
-
-    // Set current event to a tap event
-    void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
-                     GestureType& type, GestureAttribute& 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);
-
-    // Terminates the pan event
-    void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
-                     GestureType& type, f32 time_difference);
-
-    // Set current event to a swipe event
-    void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
-                       GestureType& type);
-
-    // Retrieves the last gesture entry, as indicated by shared memory indices.
-    [[nodiscard]] const GestureState& GetLastGestureEntry() const;
-
-    // Returns the average distance, angle and middle point of the active fingers
-    GestureProperties GetGestureProperties();
-
-    GestureState next_state{};
-    GestureSharedMemoryFormat* shared_memory;
-    Core::HID::EmulatedConsole* console = nullptr;
-
-    std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
-    GestureProperties last_gesture{};
-    s64 last_update_timestamp{};
-    s64 last_tap_timestamp{};
-    f32 last_pan_time_difference{};
-    bool force_update{false};
-    bool enable_press_and_tap{false};
+    mutable std::mutex mutex;
+    std::shared_ptr<TouchResource> touch_resource;
 };
+
 } // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/gesture_handler.cpp b/src/hid_core/resources/touch_screen/gesture_handler.cpp
new file mode 100644
index 0000000000..4fcaf6ecf1
--- /dev/null
+++ b/src/hid_core/resources/touch_screen/gesture_handler.cpp
@@ -0,0 +1,260 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "common/math_util.h"
+#include "hid_core/resources/touch_screen/gesture_handler.h"
+
+namespace Service::HID {
+
+constexpr f32 Square(s32 num) {
+    return static_cast<f32>(num * num);
+}
+
+GestureHandler::GestureHandler() {}
+
+GestureHandler::~GestureHandler() {}
+
+void GestureHandler::SetTouchState(std::span<TouchState> touch_state, u32 count, s64 timestamp) {
+    gesture = {};
+    gesture.active_points = std::min(MaxPoints, static_cast<std::size_t>(count));
+
+    for (size_t id = 0; id < gesture.active_points; ++id) {
+        const auto& [active_x, active_y] = touch_state[id].position;
+        gesture.points[id] = {
+            .x = static_cast<s32>(active_x),
+            .y = static_cast<s32>(active_y),
+        };
+
+        gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points);
+        gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points);
+    }
+
+    for (size_t id = 0; id < gesture.active_points; ++id) {
+        const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) +
+                                       Square(gesture.mid_point.y - gesture.points[id].y));
+        gesture.average_distance += distance / static_cast<f32>(gesture.active_points);
+    }
+
+    gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y),
+                               static_cast<f32>(gesture.mid_point.x - gesture.points[0].x));
+
+    gesture.detection_count = last_gesture.detection_count;
+
+    if (last_update_timestamp > timestamp) {
+        timestamp = last_tap_timestamp;
+    }
+
+    time_difference = static_cast<f32>(timestamp - last_update_timestamp) / (1000 * 1000 * 1000);
+}
+
+bool GestureHandler::NeedsUpdate() {
+    if (force_update) {
+        force_update = false;
+        return true;
+    }
+
+    // Update if coordinates change
+    for (size_t id = 0; id < MaxPoints; id++) {
+        if (gesture.points[id] != last_gesture.points[id]) {
+            return true;
+        }
+    }
+
+    // Update on press and hold event after 0.5 seconds
+    if (last_gesture_state.type == GestureType::Touch && last_gesture_state.point_count == 1 &&
+        time_difference > PressDelay) {
+        return enable_press_and_tap;
+    }
+
+    return false;
+}
+
+void GestureHandler::UpdateGestureState(GestureState& next_state, s64 timestamp) {
+    last_update_timestamp = timestamp;
+
+    GestureType type = GestureType::Idle;
+    GestureAttribute attributes{};
+
+    // Reset next state to default
+    next_state.sampling_number = last_gesture_state.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 (gesture.active_points > 0) {
+        if (last_gesture.active_points == 0) {
+            NewGesture(type, attributes);
+        } else {
+            UpdateExistingGesture(next_state, type);
+        }
+    } else {
+        EndGesture(next_state, type, attributes);
+    }
+
+    // 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;
+    last_gesture = gesture;
+    last_gesture_state = next_state;
+}
+
+void GestureHandler::NewGesture(GestureType& type, GestureAttribute& attributes) {
+    gesture.detection_count++;
+    type = GestureType::Touch;
+
+    // New touch after cancel is not considered new
+    if (last_gesture_state.type != GestureType::Cancel) {
+        attributes.is_new_touch.Assign(1);
+        enable_press_and_tap = true;
+    }
+}
+
+void GestureHandler::UpdateExistingGesture(GestureState& next_state, GestureType& type) {
+    // Promote to pan type if touch moved
+    for (size_t id = 0; id < MaxPoints; id++) {
+        if (gesture.points[id] != last_gesture.points[id]) {
+            type = GestureType::Pan;
+            break;
+        }
+    }
+
+    // Number of fingers changed cancel the last event and clear data
+    if (gesture.active_points != last_gesture.active_points) {
+        type = GestureType::Cancel;
+        enable_press_and_tap = false;
+        gesture.active_points = 0;
+        gesture.mid_point = {};
+        gesture.points.fill({});
+        return;
+    }
+
+    // Calculate extra parameters of panning
+    if (type == GestureType::Pan) {
+        UpdatePanEvent(next_state, type);
+        return;
+    }
+
+    // Promote to press type
+    if (last_gesture_state.type == GestureType::Touch) {
+        type = GestureType::Press;
+    }
+}
+
+void GestureHandler::EndGesture(GestureState& next_state, GestureType& type,
+                                GestureAttribute& attributes) {
+    if (last_gesture.active_points != 0) {
+        switch (last_gesture_state.type) {
+        case GestureType::Touch:
+            if (enable_press_and_tap) {
+                SetTapEvent(type, attributes);
+                return;
+            }
+            type = GestureType::Cancel;
+            force_update = true;
+            break;
+        case GestureType::Press:
+        case GestureType::Tap:
+        case GestureType::Swipe:
+        case GestureType::Pinch:
+        case GestureType::Rotate:
+            type = GestureType::Complete;
+            force_update = true;
+            break;
+        case GestureType::Pan:
+            EndPanEvent(next_state, type);
+            break;
+        default:
+            break;
+        }
+        return;
+    }
+    if (last_gesture_state.type == GestureType::Complete ||
+        last_gesture_state.type == GestureType::Cancel) {
+        gesture.detection_count++;
+    }
+}
+
+void GestureHandler::SetTapEvent(GestureType& type, GestureAttribute& attributes) {
+    type = GestureType::Tap;
+    gesture = last_gesture;
+    force_update = true;
+    f32 tap_time_difference =
+        static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000);
+    last_tap_timestamp = last_update_timestamp;
+    if (tap_time_difference < DoubleTapDelay) {
+        attributes.is_double_tap.Assign(1);
+    }
+}
+
+void GestureHandler::UpdatePanEvent(GestureState& next_state, GestureType& type) {
+    next_state.delta = gesture.mid_point - last_gesture_state.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;
+    last_pan_time_difference = time_difference;
+
+    // Promote to pinch type
+    if (std::abs(gesture.average_distance - last_gesture.average_distance) > PinchThreshold) {
+        type = GestureType::Pinch;
+        next_state.scale = gesture.average_distance / last_gesture.average_distance;
+    }
+
+    const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture.angle) /
+                                                  (1 + (gesture.angle * last_gesture.angle)));
+    // Promote to rotate type
+    if (std::abs(angle_between_two_lines) > AngleThreshold) {
+        type = GestureType::Rotate;
+        next_state.scale = 0;
+        next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
+    }
+}
+
+void GestureHandler::EndPanEvent(GestureState& next_state, GestureType& type) {
+    next_state.vel_x =
+        static_cast<f32>(last_gesture_state.delta.x) / (last_pan_time_difference + time_difference);
+    next_state.vel_y =
+        static_cast<f32>(last_gesture_state.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));
+
+    // Set swipe event with parameters
+    if (curr_vel > SwipeThreshold) {
+        SetSwipeEvent(next_state, type);
+        return;
+    }
+
+    // End panning without swipe
+    type = GestureType::Complete;
+    next_state.vel_x = 0;
+    next_state.vel_y = 0;
+    force_update = true;
+}
+
+void GestureHandler::SetSwipeEvent(GestureState& next_state, GestureType& type) {
+    type = GestureType::Swipe;
+    gesture = last_gesture;
+    force_update = true;
+    next_state.delta = last_gesture_state.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;
+            return;
+        }
+        next_state.direction = GestureDirection::Left;
+        return;
+    }
+    if (next_state.delta.y > 0) {
+        next_state.direction = GestureDirection::Down;
+        return;
+    }
+    next_state.direction = GestureDirection::Up;
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/gesture_handler.h b/src/hid_core/resources/touch_screen/gesture_handler.h
new file mode 100644
index 0000000000..fda2040c9a
--- /dev/null
+++ b/src/hid_core/resources/touch_screen/gesture_handler.h
@@ -0,0 +1,55 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <span>
+
+#include "hid_core/resources/touch_screen/touch_types.h"
+
+namespace Service::HID {
+
+class GestureHandler {
+public:
+    GestureHandler();
+    ~GestureHandler();
+
+    void SetTouchState(std::span<TouchState> touch_state, u32 count, s64 timestamp);
+
+    bool NeedsUpdate();
+    void UpdateGestureState(GestureState& next_state, s64 timestamp);
+
+private:
+    // Initializes new gesture
+    void NewGesture(GestureType& type, GestureAttribute& attributes);
+
+    // Updates existing gesture state
+    void UpdateExistingGesture(GestureState& next_state, GestureType& type);
+
+    // Terminates exiting gesture
+    void EndGesture(GestureState& next_state, GestureType& type, GestureAttribute& attributes);
+
+    // Set current event to a tap event
+    void SetTapEvent(GestureType& type, GestureAttribute& attributes);
+
+    // Calculates and set the extra parameters related to a pan event
+    void UpdatePanEvent(GestureState& next_state, GestureType& type);
+
+    // Terminates the pan event
+    void EndPanEvent(GestureState& next_state, GestureType& type);
+
+    // Set current event to a swipe event
+    void SetSwipeEvent(GestureState& next_state, GestureType& type);
+
+    GestureProperties gesture{};
+    GestureProperties last_gesture{};
+    GestureState last_gesture_state{};
+    s64 last_update_timestamp{};
+    s64 last_tap_timestamp{};
+    f32 last_pan_time_difference{};
+    f32 time_difference{};
+    bool force_update{true};
+    bool enable_press_and_tap{false};
+};
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/gesture_types.h b/src/hid_core/resources/touch_screen/gesture_types.h
deleted file mode 100644
index b4f034cd33..0000000000
--- a/src/hid_core/resources/touch_screen/gesture_types.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <array>
-#include "common/bit_field.h"
-#include "common/common_types.h"
-#include "common/point.h"
-
-namespace Service::HID {
-static constexpr size_t MAX_FINGERS = 16;
-static constexpr size_t MAX_POINTS = 4;
-
-// This is nn::hid::GestureType
-enum class GestureType : u32 {
-    Idle,     // Nothing touching the screen
-    Complete, // Set at the end of a touch event
-    Cancel,   // Set when the number of fingers change
-    Touch,    // A finger just touched the screen
-    Press,    // Set if last type is touch and the finger hasn't moved
-    Tap,      // Fast press then release
-    Pan,      // All points moving together across the screen
-    Swipe,    // Fast press movement and release of a single point
-    Pinch,    // All points moving away/closer to the midpoint
-    Rotate,   // All points rotating from the midpoint
-};
-
-// This is nn::hid::GestureDirection
-enum class GestureDirection : u32 {
-    None,
-    Left,
-    Up,
-    Right,
-    Down,
-};
-
-// This is nn::hid::GestureAttribute
-struct GestureAttribute {
-    union {
-        u32 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");
-
-// This is nn::hid::GestureState
-struct GestureState {
-    s64 sampling_number{};
-    s64 detection_count{};
-    GestureType type{GestureType::Idle};
-    GestureDirection direction{GestureDirection::None};
-    Common::Point<s32> pos{};
-    Common::Point<s32> delta{};
-    f32 vel_x{};
-    f32 vel_y{};
-    GestureAttribute attributes{};
-    f32 scale{};
-    f32 rotation_angle{};
-    s32 point_count{};
-    std::array<Common::Point<s32>, 4> points{};
-};
-static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
-
-struct GestureProperties {
-    std::array<Common::Point<s32>, MAX_POINTS> points{};
-    std::size_t active_points{};
-    Common::Point<s32> mid_point{};
-    s64 detection_count{};
-    u64 delta_time{};
-    f32 average_distance{};
-    f32 angle{};
-};
-
-} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/touch_screen.cpp b/src/hid_core/resources/touch_screen/touch_screen.cpp
index 48d956c518..35efb1786a 100644
--- a/src/hid_core/resources/touch_screen/touch_screen.cpp
+++ b/src/hid_core/resources/touch_screen/touch_screen.cpp
@@ -1,132 +1,119 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
 
-#include <algorithm>
-#include "common/common_types.h"
-#include "common/settings.h"
-#include "core/core_timing.h"
-#include "core/frontend/emu_window.h"
-#include "hid_core/frontend/emulated_console.h"
-#include "hid_core/hid_core.h"
-#include "hid_core/resources/applet_resource.h"
-#include "hid_core/resources/shared_memory_format.h"
+#include "hid_core/hid_types.h"
 #include "hid_core/resources/touch_screen/touch_screen.h"
+#include "hid_core/resources/touch_screen/touch_screen_resource.h"
 
 namespace Service::HID {
 
-TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_)
-    : ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width),
-      touchscreen_height(Layout::ScreenUndocked::Height) {
-    console = hid_core.GetEmulatedConsole();
-}
+TouchScreen::TouchScreen(std::shared_ptr<TouchResource> resource) : touch_resource{resource} {}
 
 TouchScreen::~TouchScreen() = default;
 
-void TouchScreen::OnInit() {}
+Result TouchScreen::Activate() {
+    std::scoped_lock lock{mutex};
 
-void TouchScreen::OnRelease() {}
-
-void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
-    const u64 aruid = applet_resource->GetActiveAruid();
-    auto* data = applet_resource->GetAruidData(aruid);
-
-    if (data == nullptr || !data->flag.is_assigned) {
-        return;
+    // TODO: Result result = CreateThread();
+    Result result = ResultSuccess;
+    if (result.IsError()) {
+        return result;
     }
 
-    TouchScreenSharedMemoryFormat& shared_memory = data->shared_memory_format->touch_screen;
-    shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
-
-    if (!IsControllerActivated()) {
-        shared_memory.touch_screen_lifo.buffer_count = 0;
-        shared_memory.touch_screen_lifo.buffer_tail = 0;
-        return;
+    result = touch_resource->ActivateTouch();
+    if (result.IsError()) {
+        // TODO: StopThread();
     }
 
-    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.id = current_touch.id;
-
-        if (finger.attribute.start_touch) {
-            finger.attribute.raw = 0;
-            continue;
-        }
-
-        if (finger.attribute.end_touch) {
-            finger.attribute.raw = 0;
-            finger.pressed = false;
-            continue;
-        }
-
-        if (!finger.pressed && current_touch.pressed) {
-            // Ignore all touch fingers if disabled
-            if (!Settings::values.touchscreen.enabled) {
-                continue;
-            }
-
-            finger.attribute.start_touch.Assign(1);
-            finger.pressed = true;
-            finger.position = current_touch.position;
-            continue;
-        }
-
-        if (finger.pressed && !current_touch.pressed) {
-            finger.attribute.raw = 0;
-            finger.attribute.end_touch.Assign(1);
-            continue;
-        }
-
-        // Only update position if touch is not on a special frame
-        finger.position = current_touch.position;
-    }
-
-    std::array<Core::HID::TouchFinger, MAX_FINGERS> 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 timestamp = static_cast<u64>(core_timing.GetGlobalTimeNs().count());
-    const auto& last_entry = shared_memory.touch_screen_lifo.ReadCurrentEntry().state;
-
-    next_state.sampling_number = last_entry.sampling_number + 1;
-    next_state.entry_count = static_cast<s32>(active_fingers_count);
-
-    for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
-        auto& touch_entry = next_state.states[id];
-        if (id < active_fingers_count) {
-            const auto& [active_x, active_y] = active_fingers[id].position;
-            touch_entry.position = {
-                .x = static_cast<u16>(active_x * static_cast<float>(touchscreen_width)),
-                .y = static_cast<u16>(active_y * static_cast<float>(touchscreen_height)),
-            };
-            touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
-            touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
-            touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
-            touch_entry.delta_time = timestamp - active_fingers[id].last_touch;
-            fingers[active_fingers[id].id].last_touch = timestamp;
-            touch_entry.finger = active_fingers[id].id;
-            touch_entry.attribute.raw = active_fingers[id].attribute.raw;
-        } else {
-            // Clear touch entry
-            touch_entry.attribute.raw = 0;
-            touch_entry.position = {};
-            touch_entry.diameter_x = 0;
-            touch_entry.diameter_y = 0;
-            touch_entry.rotation_angle = 0;
-            touch_entry.delta_time = 0;
-            touch_entry.finger = 0;
-        }
-    }
-
-    shared_memory.touch_screen_lifo.WriteNextEntry(next_state);
+    return result;
 }
 
-void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) {
-    touchscreen_width = width;
-    touchscreen_height = height;
+Result TouchScreen::Activate(u64 aruid) {
+    std::scoped_lock lock{mutex};
+    return touch_resource->ActivateTouch(aruid);
+}
+
+Result TouchScreen::Deactivate() {
+    std::scoped_lock lock{mutex};
+    const auto result = touch_resource->DeactivateTouch();
+
+    if (result.IsError()) {
+        return result;
+    }
+
+    // TODO: return StopThread();
+    return ResultSuccess;
+}
+
+Result TouchScreen::IsActive(bool& out_is_active) const {
+    out_is_active = touch_resource->IsTouchActive();
+    return ResultSuccess;
+}
+
+Result TouchScreen::SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state) {
+    std::scoped_lock lock{mutex};
+    return touch_resource->SetTouchScreenAutoPilotState(auto_pilot_state);
+}
+
+Result TouchScreen::UnsetTouchScreenAutoPilotState() {
+    std::scoped_lock lock{mutex};
+    return touch_resource->UnsetTouchScreenAutoPilotState();
+}
+
+Result TouchScreen::RequestNextTouchInput() {
+    std::scoped_lock lock{mutex};
+    return touch_resource->RequestNextTouchInput();
+}
+
+Result TouchScreen::RequestNextDummyInput() {
+    std::scoped_lock lock{mutex};
+    return touch_resource->RequestNextDummyInput();
+}
+
+Result TouchScreen::ProcessTouchScreenAutoTune() {
+    std::scoped_lock lock{mutex};
+    return touch_resource->ProcessTouchScreenAutoTune();
+}
+
+Result TouchScreen::SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x,
+                                                f32 point2_y) {
+    std::scoped_lock lock{mutex};
+    touch_resource->SetTouchScreenMagnification(point1_x, point1_y, point2_x, point2_y);
+    return ResultSuccess;
+}
+
+Result TouchScreen::SetTouchScreenResolution(u32 width, u32 height, u64 aruid) {
+    std::scoped_lock lock{mutex};
+    return touch_resource->SetTouchScreenResolution(width, height, aruid);
+}
+
+Result TouchScreen::SetTouchScreenConfiguration(
+    const Core::HID::TouchScreenConfigurationForNx& mode, u64 aruid) {
+    std::scoped_lock lock{mutex};
+    return touch_resource->SetTouchScreenConfiguration(mode, aruid);
+}
+
+Result TouchScreen::GetTouchScreenConfiguration(Core::HID::TouchScreenConfigurationForNx& out_mode,
+                                                u64 aruid) const {
+    std::scoped_lock lock{mutex};
+    return touch_resource->GetTouchScreenConfiguration(out_mode, aruid);
+}
+
+Result TouchScreen::SetTouchScreenDefaultConfiguration(
+    const Core::HID::TouchScreenConfigurationForNx& mode) {
+    std::scoped_lock lock{mutex};
+    return touch_resource->SetTouchScreenDefaultConfiguration(mode);
+}
+
+Result TouchScreen::GetTouchScreenDefaultConfiguration(
+    Core::HID::TouchScreenConfigurationForNx& out_mode) const {
+    std::scoped_lock lock{mutex};
+    return touch_resource->GetTouchScreenDefaultConfiguration(out_mode);
+}
+
+void TouchScreen::OnTouchUpdate(u64 timestamp) {
+    std::scoped_lock lock{mutex};
+    touch_resource->OnTouchUpdate(timestamp);
 }
 
 } // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/touch_screen.h b/src/hid_core/resources/touch_screen/touch_screen.h
index 4b3824742e..2fcb6247f1 100644
--- a/src/hid_core/resources/touch_screen/touch_screen.h
+++ b/src/hid_core/resources/touch_screen/touch_screen.h
@@ -1,43 +1,64 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
 
 #pragma once
 
-#include <array>
+#include <mutex>
 
-#include "hid_core/hid_types.h"
-#include "hid_core/resources/controller_base.h"
-#include "hid_core/resources/touch_screen/touch_types.h"
+#include "common/common_types.h"
+#include "core/hle/result.h"
 
 namespace Core::HID {
-class EmulatedConsole;
-} // namespace Core::HID
+struct TouchScreenConfigurationForNx;
+}
+
+namespace Core::Timing {
+struct EventType;
+}
 
 namespace Service::HID {
-struct TouchScreenSharedMemoryFormat;
+class TouchResource;
+struct AutoPilotState;
 
-class TouchScreen final : public ControllerBase {
+/// Handles touch request from HID interfaces
+class TouchScreen {
 public:
-    explicit TouchScreen(Core::HID::HIDCore& hid_core_);
-    ~TouchScreen() override;
+    TouchScreen(std::shared_ptr<TouchResource> resource);
+    ~TouchScreen();
 
-    // Called when the controller is initialized
-    void OnInit() override;
+    Result Activate();
+    Result Activate(u64 aruid);
 
-    // When the controller is released
-    void OnRelease() override;
+    Result Deactivate();
 
-    // When the controller is requesting an update for the shared memory
-    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
+    Result IsActive(bool& out_is_active) const;
 
-    void SetTouchscreenDimensions(u32 width, u32 height);
+    Result SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state);
+    Result UnsetTouchScreenAutoPilotState();
+
+    Result RequestNextTouchInput();
+    Result RequestNextDummyInput();
+
+    Result ProcessTouchScreenAutoTune();
+
+    Result SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, f32 point2_y);
+    Result SetTouchScreenResolution(u32 width, u32 height, u64 aruid);
+
+    Result SetTouchScreenConfiguration(const Core::HID::TouchScreenConfigurationForNx& mode,
+                                       u64 aruid);
+    Result GetTouchScreenConfiguration(Core::HID::TouchScreenConfigurationForNx& out_mode,
+                                       u64 aruid) const;
+
+    Result SetTouchScreenDefaultConfiguration(const Core::HID::TouchScreenConfigurationForNx& mode);
+    Result GetTouchScreenDefaultConfiguration(
+        Core::HID::TouchScreenConfigurationForNx& out_mode) const;
+
+    void OnTouchUpdate(u64 timestamp);
 
 private:
-    TouchScreenState next_state{};
-    Core::HID::EmulatedConsole* console = nullptr;
-
-    std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};
-    u32 touchscreen_width;
-    u32 touchscreen_height;
+    mutable std::mutex mutex;
+    std::shared_ptr<TouchResource> touch_resource;
+    std::shared_ptr<Core::Timing::EventType> touch_update_event;
 };
+
 } // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/touch_screen_driver.cpp b/src/hid_core/resources/touch_screen/touch_screen_driver.cpp
new file mode 100644
index 0000000000..6a64c75b32
--- /dev/null
+++ b/src/hid_core/resources/touch_screen/touch_screen_driver.cpp
@@ -0,0 +1,114 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <algorithm>
+#include "common/settings.h"
+#include "core/frontend/emu_window.h"
+#include "hid_core/hid_core.h"
+#include "hid_core/resources/touch_screen/touch_screen_driver.h"
+
+namespace Service::HID {
+
+TouchDriver::TouchDriver(Core::HID::HIDCore& hid_core) {
+    console = hid_core.GetEmulatedConsole();
+}
+
+TouchDriver::~TouchDriver() = default;
+
+Result TouchDriver::StartTouchSensor() {
+    is_running = true;
+    return ResultSuccess;
+}
+
+Result TouchDriver::StopTouchSensor() {
+    is_running = false;
+    return ResultSuccess;
+}
+
+bool TouchDriver::IsRunning() const {
+    return is_running;
+}
+
+void TouchDriver::ProcessTouchScreenAutoTune() const {
+    // TODO
+}
+
+Result TouchDriver::WaitForDummyInput() {
+    touch_status = {};
+    return ResultSuccess;
+}
+
+Result TouchDriver::WaitForInput() {
+    touch_status = {};
+    const auto touch_input = console->GetTouch();
+    for (std::size_t id = 0; id < touch_status.states.size(); id++) {
+        const auto& current_touch = touch_input[id];
+        auto& finger = fingers[id];
+        finger.id = current_touch.id;
+
+        if (finger.attribute.start_touch) {
+            finger.attribute.raw = 0;
+            continue;
+        }
+
+        if (finger.attribute.end_touch) {
+            finger.attribute.raw = 0;
+            finger.pressed = false;
+            continue;
+        }
+
+        if (!finger.pressed && current_touch.pressed) {
+            finger.attribute.start_touch.Assign(1);
+            finger.pressed = true;
+            finger.position = current_touch.position;
+            continue;
+        }
+
+        if (finger.pressed && !current_touch.pressed) {
+            finger.attribute.raw = 0;
+            finger.attribute.end_touch.Assign(1);
+            continue;
+        }
+
+        // Only update position if touch is not on a special frame
+        finger.position = current_touch.position;
+    }
+
+    std::array<Core::HID::TouchFinger, MaxFingers> 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));
+
+    touch_status.entry_count = static_cast<s32>(active_fingers_count);
+    for (std::size_t id = 0; id < MaxFingers; ++id) {
+        auto& touch_entry = touch_status.states[id];
+        if (id < active_fingers_count) {
+            const auto& [active_x, active_y] = active_fingers[id].position;
+            touch_entry.position = {
+                .x = static_cast<u16>(active_x * TouchSensorWidth),
+                .y = static_cast<u16>(active_y * TouchSensorHeight),
+            };
+            touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
+            touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
+            touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
+            touch_entry.finger = active_fingers[id].id;
+            touch_entry.attribute.raw = active_fingers[id].attribute.raw;
+        }
+    }
+    return ResultSuccess;
+}
+
+void TouchDriver::GetNextTouchState(TouchScreenState& out_state) const {
+    out_state = touch_status;
+}
+
+void TouchDriver::SetTouchMode(Core::HID::TouchScreenModeForNx mode) {
+    touch_mode = mode;
+}
+
+Core::HID::TouchScreenModeForNx TouchDriver::GetTouchMode() const {
+    return touch_mode;
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/touch_screen_driver.h b/src/hid_core/resources/touch_screen/touch_screen_driver.h
new file mode 100644
index 0000000000..ca7e4970e4
--- /dev/null
+++ b/src/hid_core/resources/touch_screen/touch_screen_driver.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/frontend/emulated_console.h"
+#include "hid_core/hid_types.h"
+#include "hid_core/resources/touch_screen/touch_types.h"
+
+namespace Core::HID {
+class HIDCore;
+} // namespace Core::HID
+
+namespace Service::HID {
+
+/// This handles all request to Ftm3bd56(TouchPanel) hardware
+class TouchDriver {
+public:
+    explicit TouchDriver(Core::HID::HIDCore& hid_core);
+    ~TouchDriver();
+
+    Result StartTouchSensor();
+    Result StopTouchSensor();
+    bool IsRunning() const;
+
+    void ProcessTouchScreenAutoTune() const;
+
+    Result WaitForDummyInput();
+    Result WaitForInput();
+
+    void GetNextTouchState(TouchScreenState& out_state) const;
+
+    void SetTouchMode(Core::HID::TouchScreenModeForNx mode);
+    Core::HID::TouchScreenModeForNx GetTouchMode() const;
+
+private:
+    bool is_running{};
+    TouchScreenState touch_status{};
+    Core::HID::TouchFingerState fingers{};
+    Core::HID::TouchScreenModeForNx touch_mode{};
+
+    Core::HID::EmulatedConsole* console = nullptr;
+};
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/touch_screen_resource.cpp b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp
new file mode 100644
index 0000000000..56e8e8e514
--- /dev/null
+++ b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp
@@ -0,0 +1,579 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "common/logging/log.h"
+#include "core/core_timing.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_shared_memory.h"
+#include "core/hle/service/set/system_settings_server.h"
+#include "core/hle/service/sm/sm.h"
+#include "hid_core/hid_result.h"
+#include "hid_core/resources/applet_resource.h"
+#include "hid_core/resources/shared_memory_format.h"
+#include "hid_core/resources/touch_screen/touch_screen_driver.h"
+#include "hid_core/resources/touch_screen/touch_screen_resource.h"
+
+namespace Service::HID {
+constexpr auto GestureUpdatePeriod = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
+
+TouchResource::TouchResource(Core::System& system_) : system{system_} {
+    m_set_sys = system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys");
+}
+
+TouchResource::~TouchResource() {
+    Finalize();
+};
+
+Result TouchResource::ActivateTouch() {
+    if (global_ref_counter == std::numeric_limits<s32>::max() - 1 ||
+        touch_ref_counter == std::numeric_limits<s32>::max() - 1) {
+        return ResultTouchOverflow;
+    }
+
+    if (global_ref_counter == 0) {
+        std::scoped_lock lock{*shared_mutex};
+
+        const auto result = touch_driver->StartTouchSensor();
+        if (result.IsError()) {
+            return result;
+        }
+
+        is_initalized = true;
+        system.CoreTiming().ScheduleLoopingEvent(GestureUpdatePeriod, GestureUpdatePeriod,
+                                                 timer_event);
+        current_touch_state = {};
+        ReadTouchInput();
+        gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count,
+                                      0);
+    }
+
+    Set::TouchScreenMode touch_mode{Set::TouchScreenMode::Standard};
+    m_set_sys->GetTouchScreenMode(touch_mode);
+    default_touch_screen_mode = static_cast<Core::HID::TouchScreenModeForNx>(touch_mode);
+
+    global_ref_counter++;
+    touch_ref_counter++;
+    return ResultSuccess;
+}
+
+Result TouchResource::ActivateTouch(u64 aruid) {
+    std::scoped_lock lock{*shared_mutex};
+
+    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
+        auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
+        TouchAruidData& touch_data = aruid_data[aruid_index];
+
+        if (!applet_data->flag.is_assigned) {
+            touch_data = {};
+            continue;
+        }
+
+        const u64 aruid_id = applet_data->aruid;
+        if (touch_data.aruid != aruid_id) {
+            touch_data = {};
+            touch_data.aruid = aruid_id;
+        }
+
+        if (aruid != aruid_id) {
+            continue;
+        }
+
+        auto& touch_shared = applet_data->shared_memory_format->touch_screen;
+
+        if (touch_shared.touch_screen_lifo.buffer_count == 0) {
+            StorePreviousTouchState(previous_touch_state, touch_data.finger_map,
+                                    current_touch_state,
+                                    applet_data->flag.enable_touchscreen.Value() != 0);
+            touch_shared.touch_screen_lifo.WriteNextEntry(previous_touch_state);
+        }
+    }
+    return ResultSuccess;
+}
+
+Result TouchResource::ActivateGesture() {
+    if (global_ref_counter == std::numeric_limits<s32>::max() - 1 ||
+        gesture_ref_counter == std::numeric_limits<s32>::max() - 1) {
+        return ResultGestureOverflow;
+    }
+
+    // Initialize first instance
+    if (global_ref_counter == 0) {
+        const auto result = touch_driver->StartTouchSensor();
+        if (result.IsError()) {
+            return result;
+        }
+
+        is_initalized = true;
+        system.CoreTiming().ScheduleLoopingEvent(GestureUpdatePeriod, GestureUpdatePeriod,
+                                                 timer_event);
+        current_touch_state = {};
+        ReadTouchInput();
+        gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count,
+                                      0);
+    }
+
+    global_ref_counter++;
+    gesture_ref_counter++;
+    return ResultSuccess;
+}
+
+Result TouchResource::ActivateGesture(u64 aruid, u32 basic_gesture_id) {
+    std::scoped_lock lock{*shared_mutex};
+
+    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
+        auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
+        TouchAruidData& touch_data = aruid_data[aruid_index];
+
+        if (!applet_data->flag.is_assigned) {
+            touch_data = {};
+            continue;
+        }
+
+        const u64 aruid_id = applet_data->aruid;
+        if (touch_data.aruid != aruid_id) {
+            touch_data = {};
+            touch_data.aruid = aruid_id;
+        }
+
+        if (aruid != aruid_id) {
+            continue;
+        }
+
+        auto& gesture_shared = applet_data->shared_memory_format->gesture;
+        if (touch_data.basic_gesture_id != basic_gesture_id) {
+            gesture_shared.gesture_lifo.buffer_count = 0;
+        }
+
+        if (gesture_shared.gesture_lifo.buffer_count == 0) {
+            touch_data.basic_gesture_id = basic_gesture_id;
+
+            gesture_shared.gesture_lifo.WriteNextEntry(gesture_state);
+        }
+    }
+
+    return ResultSuccess;
+}
+
+Result TouchResource::DeactivateTouch() {
+    if (touch_ref_counter == 0 || global_ref_counter == 0) {
+        return ResultTouchNotInitialized;
+    }
+
+    global_ref_counter--;
+    touch_ref_counter--;
+
+    if (touch_ref_counter + global_ref_counter != 0) {
+        return ResultSuccess;
+    }
+
+    return Finalize();
+}
+
+Result TouchResource::DeactivateGesture() {
+    if (gesture_ref_counter == 0 || global_ref_counter == 0) {
+        return ResultGestureNotInitialized;
+    }
+
+    global_ref_counter--;
+    gesture_ref_counter--;
+
+    if (touch_ref_counter + global_ref_counter != 0) {
+        return ResultSuccess;
+    }
+
+    return Finalize();
+}
+
+bool TouchResource::IsTouchActive() const {
+    return touch_ref_counter != 0;
+}
+
+bool TouchResource::IsGestureActive() const {
+    return gesture_ref_counter != 0;
+}
+
+void TouchResource::SetTouchDriver(std::shared_ptr<TouchDriver> driver) {
+    touch_driver = driver;
+}
+
+void TouchResource::SetAppletResource(std::shared_ptr<AppletResource> shared,
+                                      std::recursive_mutex* mutex) {
+    applet_resource = shared;
+    shared_mutex = mutex;
+}
+
+void TouchResource::SetInputEvent(Kernel::KEvent* event, std::mutex* mutex) {
+    input_event = event;
+    input_mutex = mutex;
+}
+
+void TouchResource::SetHandheldConfig(std::shared_ptr<HandheldConfig> config) {
+    handheld_config = config;
+}
+
+void TouchResource::SetTimerEvent(std::shared_ptr<Core::Timing::EventType> event) {
+    timer_event = event;
+}
+
+Result TouchResource::SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state) {
+    if (global_ref_counter == 0) {
+        return ResultTouchNotInitialized;
+    }
+
+    if (!is_auto_pilot_initialized) {
+        is_auto_pilot_initialized = true;
+        auto_pilot = {};
+    }
+
+    TouchScreenState state = {
+        .entry_count = static_cast<s32>(auto_pilot_state.count),
+        .states = auto_pilot_state.state,
+    };
+
+    SanitizeInput(state);
+
+    auto_pilot.count = state.entry_count;
+    auto_pilot.state = state.states;
+    return ResultSuccess;
+}
+
+Result TouchResource::UnsetTouchScreenAutoPilotState() {
+    if (global_ref_counter == 0) {
+        return ResultTouchNotInitialized;
+    }
+
+    is_auto_pilot_initialized = false;
+    auto_pilot = {};
+    return ResultSuccess;
+}
+
+Result TouchResource::RequestNextTouchInput() {
+    if (global_ref_counter == 0) {
+        return ResultTouchNotInitialized;
+    }
+
+    if (handheld_config->is_handheld_hid_enabled) {
+        const Result result = touch_driver->WaitForInput();
+        if (result.IsError()) {
+            return result;
+        }
+    }
+
+    is_initalized = true;
+    return ResultSuccess;
+}
+
+Result TouchResource::RequestNextDummyInput() {
+    if (global_ref_counter == 0) {
+        return ResultTouchNotInitialized;
+    }
+
+    if (handheld_config->is_handheld_hid_enabled) {
+        const Result result = touch_driver->WaitForDummyInput();
+        if (result.IsError()) {
+            return result;
+        }
+    }
+
+    is_initalized = false;
+    return ResultSuccess;
+}
+
+Result TouchResource::ProcessTouchScreenAutoTune() {
+    touch_driver->ProcessTouchScreenAutoTune();
+    return ResultSuccess;
+}
+
+void TouchResource::SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x,
+                                                f32 point2_y) {
+    offset = {
+        .x = point1_x,
+        .y = point1_y,
+    };
+    magnification = {
+        .x = point2_x,
+        .y = point2_y,
+    };
+}
+
+Result TouchResource::SetTouchScreenResolution(u32 width, u32 height, u64 aruid) {
+    std::scoped_lock lock{*shared_mutex};
+
+    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
+        const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
+        TouchAruidData& data = aruid_data[aruid_index];
+
+        if (!applet_data->flag.is_assigned) {
+            continue;
+        }
+        if (aruid != data.aruid) {
+            continue;
+        }
+        data.resolution_width = static_cast<u16>(width);
+        data.resolution_height = static_cast<u16>(height);
+    }
+
+    return ResultSuccess;
+}
+
+Result TouchResource::SetTouchScreenConfiguration(
+    const Core::HID::TouchScreenConfigurationForNx& touch_configuration, u64 aruid) {
+    std::scoped_lock lock{*shared_mutex};
+
+    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
+        const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
+        TouchAruidData& data = aruid_data[aruid_index];
+
+        if (!applet_data->flag.is_assigned) {
+            continue;
+        }
+        if (aruid != data.aruid) {
+            continue;
+        }
+        data.finger_map.touch_mode = touch_configuration.mode;
+    }
+
+    return ResultSuccess;
+}
+
+Result TouchResource::GetTouchScreenConfiguration(
+    Core::HID::TouchScreenConfigurationForNx& out_touch_configuration, u64 aruid) const {
+    std::scoped_lock lock{*shared_mutex};
+
+    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
+        const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
+        const TouchAruidData& data = aruid_data[aruid_index];
+
+        if (!applet_data->flag.is_assigned) {
+            continue;
+        }
+        if (aruid != data.aruid) {
+            continue;
+        }
+        out_touch_configuration.mode = data.finger_map.touch_mode;
+    }
+
+    return ResultSuccess;
+}
+
+Result TouchResource::SetTouchScreenDefaultConfiguration(
+    const Core::HID::TouchScreenConfigurationForNx& touch_configuration) {
+    default_touch_screen_mode = touch_configuration.mode;
+    return ResultSuccess;
+}
+
+Result TouchResource::GetTouchScreenDefaultConfiguration(
+    Core::HID::TouchScreenConfigurationForNx& out_touch_configuration) const {
+    out_touch_configuration.mode = default_touch_screen_mode;
+    return ResultSuccess;
+}
+
+Result TouchResource::Finalize() {
+    is_auto_pilot_initialized = false;
+    auto_pilot = {};
+    system.CoreTiming().UnscheduleEvent(timer_event);
+
+    const auto result = touch_driver->StopTouchSensor();
+    if (result.IsError()) {
+        return result;
+    }
+
+    is_initalized = false;
+    return ResultSuccess;
+}
+
+void TouchResource::StorePreviousTouchState(TouchScreenState& out_previous_touch,
+                                            TouchFingerMap& out_finger_map,
+                                            const TouchScreenState& current_touch,
+                                            bool is_touch_enabled) const {
+    s32 finger_count{};
+
+    if (is_touch_enabled) {
+        finger_count = current_touch.entry_count;
+        if (finger_count < 1) {
+            out_finger_map.finger_count = 0;
+            out_finger_map.finger_ids = {};
+            out_previous_touch.sampling_number = current_touch.sampling_number;
+            out_previous_touch.entry_count = 0;
+            out_previous_touch.states = {};
+            return;
+        }
+        for (std::size_t i = 0; i < static_cast<u32>(finger_count); i++) {
+            out_finger_map.finger_ids[i] = current_touch.states[i].finger;
+            out_previous_touch.states[i] = current_touch.states[i];
+        }
+        out_finger_map.finger_count = finger_count;
+        return;
+    }
+
+    if (!is_touch_enabled && out_finger_map.finger_count > 0 && current_touch.entry_count > 0) {
+        // TODO
+    }
+
+    // Zero out unused entries
+    for (std::size_t i = finger_count; i < MaxFingers; i++) {
+        out_finger_map.finger_ids[i] = 0;
+        out_previous_touch.states[i] = {};
+    }
+
+    out_previous_touch.sampling_number = current_touch.sampling_number;
+    out_previous_touch.entry_count = finger_count;
+}
+
+void TouchResource::ReadTouchInput() {
+    previous_touch_state = current_touch_state;
+
+    if (!is_initalized || !handheld_config->is_handheld_hid_enabled || !touch_driver->IsRunning()) {
+        touch_driver->WaitForDummyInput();
+    } else {
+        touch_driver->WaitForInput();
+    }
+
+    touch_driver->GetNextTouchState(current_touch_state);
+    SanitizeInput(current_touch_state);
+    current_touch_state.sampling_number = sample_number;
+    sample_number++;
+
+    if (is_auto_pilot_initialized && current_touch_state.entry_count == 0) {
+        const std::size_t finger_count = static_cast<std::size_t>(auto_pilot.count);
+        current_touch_state.entry_count = static_cast<s32>(finger_count);
+        for (std::size_t i = 0; i < finger_count; i++) {
+            current_touch_state.states[i] = auto_pilot.state[i];
+        }
+
+        std::size_t index = 0;
+        for (std::size_t i = 0; i < finger_count; i++) {
+            if (auto_pilot.state[i].attribute.end_touch) {
+                continue;
+            }
+            auto_pilot.state[i].attribute.raw = 0;
+            auto_pilot.state[index] = auto_pilot.state[i];
+            index++;
+        }
+
+        auto_pilot.count = index;
+        for (std::size_t i = index; i < auto_pilot.state.size(); i++) {
+            auto_pilot.state[i] = {};
+        }
+    }
+
+    for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); i++) {
+        auto& state = current_touch_state.states[i];
+        state.position.x = static_cast<u32>((magnification.y * static_cast<f32>(state.position.x)) +
+                                            (offset.x * static_cast<f32>(TouchSensorWidth)));
+        state.position.y = static_cast<u32>((magnification.y * static_cast<f32>(state.position.y)) +
+                                            (offset.x * static_cast<f32>(TouchSensorHeight)));
+        state.diameter_x = static_cast<u32>(magnification.x * static_cast<f32>(state.diameter_x));
+        state.diameter_y = static_cast<u32>(magnification.y * static_cast<f32>(state.diameter_y));
+    }
+
+    std::size_t index = 0;
+    for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); i++) {
+        const auto& old_state = current_touch_state.states[i];
+        auto& state = current_touch_state.states[index];
+        if ((TouchSensorWidth <= old_state.position.x) ||
+            (TouchSensorHeight <= old_state.position.y)) {
+            continue;
+        }
+        state = old_state;
+        index++;
+    }
+    current_touch_state.entry_count = static_cast<s32>(index);
+
+    SanitizeInput(current_touch_state);
+
+    std::scoped_lock lock{*input_mutex};
+    if (current_touch_state.entry_count == previous_touch_state.entry_count) {
+        if (current_touch_state.entry_count < 1) {
+            return;
+        }
+        bool has_moved = false;
+        for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count);
+             i++) {
+            s32 delta_x = std::abs(static_cast<s32>(current_touch_state.states[i].position.x) -
+                                   static_cast<s32>(previous_touch_state.states[i].position.x));
+            s32 delta_y = std::abs(static_cast<s32>(current_touch_state.states[i].position.y) -
+                                   static_cast<s32>(previous_touch_state.states[i].position.y));
+            if (delta_x > 1 || delta_y > 1) {
+                has_moved = true;
+            }
+        }
+        if (!has_moved) {
+            return;
+        }
+    }
+
+    input_event->Signal();
+}
+
+void TouchResource::OnTouchUpdate(s64 timestamp) {
+    if (global_ref_counter == 0) {
+        return;
+    }
+
+    ReadTouchInput();
+    gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count,
+                                  timestamp);
+
+    std::scoped_lock lock{*shared_mutex};
+
+    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
+        const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
+        TouchAruidData& data = aruid_data[aruid_index];
+
+        if (applet_data == nullptr || !applet_data->flag.is_assigned) {
+            data = {};
+            continue;
+        }
+
+        if (data.aruid != applet_data->aruid) {
+            data = {};
+            data.aruid = applet_data->aruid;
+        }
+
+        if (gesture_ref_counter != 0) {
+            if (!applet_data->flag.enable_touchscreen) {
+                gesture_state = {};
+            }
+            if (gesture_handler.NeedsUpdate()) {
+                gesture_handler.UpdateGestureState(gesture_state, timestamp);
+                auto& gesture_shared = applet_data->shared_memory_format->gesture;
+                gesture_shared.gesture_lifo.WriteNextEntry(gesture_state);
+            }
+        }
+
+        if (touch_ref_counter != 0) {
+            auto touch_mode = data.finger_map.touch_mode;
+            if (touch_mode == Core::HID::TouchScreenModeForNx::UseSystemSetting) {
+                touch_mode = default_touch_screen_mode;
+            }
+
+            if (applet_resource->GetActiveAruid() == applet_data->aruid &&
+                touch_mode != Core::HID::TouchScreenModeForNx::UseSystemSetting && is_initalized &&
+                handheld_config->is_handheld_hid_enabled && touch_driver->IsRunning()) {
+                touch_driver->SetTouchMode(touch_mode);
+            }
+
+            auto& touch_shared = applet_data->shared_memory_format->touch_screen;
+            StorePreviousTouchState(previous_touch_state, data.finger_map, current_touch_state,
+                                    applet_data->flag.enable_touchscreen.As<bool>());
+            touch_shared.touch_screen_lifo.WriteNextEntry(current_touch_state);
+        }
+    }
+}
+
+void TouchResource::SanitizeInput(TouchScreenState& state) const {
+    for (std::size_t i = 0; i < static_cast<std::size_t>(state.entry_count); i++) {
+        auto& entry = state.states[i];
+        entry.position.x =
+            std::clamp(entry.position.x, TouchBorders, TouchSensorWidth - TouchBorders - 1);
+        entry.position.y =
+            std::clamp(entry.position.y, TouchBorders, TouchSensorHeight - TouchBorders - 1);
+        entry.diameter_x = std::clamp(entry.diameter_x, 0u, TouchSensorWidth - MaxTouchDiameter);
+        entry.diameter_y = std::clamp(entry.diameter_y, 0u, TouchSensorHeight - MaxTouchDiameter);
+        entry.rotation_angle =
+            std::clamp(entry.rotation_angle, -MaxRotationAngle, MaxRotationAngle);
+    }
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/touch_screen_resource.h b/src/hid_core/resources/touch_screen/touch_screen_resource.h
new file mode 100644
index 0000000000..095cddd762
--- /dev/null
+++ b/src/hid_core/resources/touch_screen/touch_screen_resource.h
@@ -0,0 +1,126 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+#include <mutex>
+
+#include "common/common_types.h"
+#include "common/point.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+#include "hid_core/resources/touch_screen/gesture_handler.h"
+#include "hid_core/resources/touch_screen/touch_types.h"
+
+namespace Core {
+class System;
+}
+
+namespace Core::Timing {
+struct EventType;
+}
+
+namespace Kernel {
+class KEvent;
+} // namespace Kernel
+
+namespace Service::Set {
+class ISystemSettingsServer;
+}
+
+namespace Service::HID {
+class AppletResource;
+class TouchSharedMemoryManager;
+class TouchDriver;
+struct HandheldConfig;
+
+class TouchResource {
+public:
+    TouchResource(Core::System& system_);
+    ~TouchResource();
+
+    Result ActivateTouch();
+    Result ActivateTouch(u64 aruid);
+
+    Result ActivateGesture();
+    Result ActivateGesture(u64 aruid, u32 basic_gesture_id);
+
+    Result DeactivateTouch();
+    Result DeactivateGesture();
+
+    bool IsTouchActive() const;
+    bool IsGestureActive() const;
+
+    void SetTouchDriver(std::shared_ptr<TouchDriver> driver);
+    void SetAppletResource(std::shared_ptr<AppletResource> shared, std::recursive_mutex* mutex);
+    void SetInputEvent(Kernel::KEvent* event, std::mutex* mutex);
+    void SetHandheldConfig(std::shared_ptr<HandheldConfig> config);
+    void SetTimerEvent(std::shared_ptr<Core::Timing::EventType> event);
+
+    Result SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state);
+    Result UnsetTouchScreenAutoPilotState();
+
+    Result RequestNextTouchInput();
+    Result RequestNextDummyInput();
+
+    Result ProcessTouchScreenAutoTune();
+    void SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, f32 point2_y);
+    Result SetTouchScreenResolution(u32 width, u32 height, u64 aruid);
+
+    Result SetTouchScreenConfiguration(
+        const Core::HID::TouchScreenConfigurationForNx& touch_configuration, u64 aruid);
+    Result GetTouchScreenConfiguration(
+        Core::HID::TouchScreenConfigurationForNx& out_touch_configuration, u64 aruid) const;
+
+    Result SetTouchScreenDefaultConfiguration(
+        const Core::HID::TouchScreenConfigurationForNx& touch_configuration);
+    Result GetTouchScreenDefaultConfiguration(
+        Core::HID::TouchScreenConfigurationForNx& out_touch_configuration) const;
+
+    void OnTouchUpdate(s64 timestamp);
+
+private:
+    Result Finalize();
+
+    void StorePreviousTouchState(TouchScreenState& out_previous_touch,
+                                 TouchFingerMap& out_finger_map,
+                                 const TouchScreenState& current_touch,
+                                 bool is_touch_enabled) const;
+    void ReadTouchInput();
+
+    void SanitizeInput(TouchScreenState& state) const;
+
+    s32 global_ref_counter{};
+    s32 gesture_ref_counter{};
+    s32 touch_ref_counter{};
+    bool is_initalized{};
+    u64 sample_number{};
+
+    // External resources
+    std::shared_ptr<Core::Timing::EventType> timer_event{nullptr};
+    std::shared_ptr<TouchDriver> touch_driver{nullptr};
+    std::shared_ptr<AppletResource> applet_resource{nullptr};
+    std::recursive_mutex* shared_mutex{nullptr};
+    std::shared_ptr<HandheldConfig> handheld_config{nullptr};
+    Kernel::KEvent* input_event{nullptr};
+    std::mutex* input_mutex{nullptr};
+
+    // Internal state
+    TouchScreenState current_touch_state{};
+    TouchScreenState previous_touch_state{};
+    GestureState gesture_state{};
+    bool is_auto_pilot_initialized{};
+    AutoPilotState auto_pilot{};
+    GestureHandler gesture_handler{};
+    std::array<TouchAruidData, 0x20> aruid_data{};
+    Common::Point<f32> magnification{1.0f, 1.0f};
+    Common::Point<f32> offset{0.0f, 0.0f};
+    Core::HID::TouchScreenModeForNx default_touch_screen_mode{
+        Core::HID::TouchScreenModeForNx::Finger};
+
+    Core::System& system;
+    std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
+};
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/touch_types.h b/src/hid_core/resources/touch_screen/touch_types.h
index 97ee847da4..3620889392 100644
--- a/src/hid_core/resources/touch_screen/touch_types.h
+++ b/src/hid_core/resources/touch_screen/touch_types.h
@@ -13,8 +13,20 @@
 #include "hid_core/hid_types.h"
 
 namespace Service::HID {
-static constexpr std::size_t MAX_FINGERS = 16;
-static constexpr size_t MAX_POINTS = 4;
+constexpr std::size_t MaxFingers = 16;
+constexpr std::size_t MaxPoints = 4;
+constexpr u32 TouchSensorWidth = 1280;
+constexpr u32 TouchSensorHeight = 720;
+constexpr s32 MaxRotationAngle = 270;
+constexpr u32 MaxTouchDiameter = 30;
+constexpr u32 TouchBorders = 15;
+
+// HW is around 700, value is set to 400 to make it easier to trigger with mouse
+constexpr f32 SwipeThreshold = 400.0f; // Threshold in pixels/s
+constexpr f32 AngleThreshold = 0.015f; // Threshold in radians
+constexpr f32 PinchThreshold = 0.5f;   // Threshold in pixels
+constexpr f32 PressDelay = 0.5f;       // Time in seconds
+constexpr f32 DoubleTapDelay = 0.35f;  // Time in seconds
 
 // This is nn::hid::GestureType
 enum class GestureType : u32 {
@@ -28,6 +40,7 @@ enum class GestureType : u32 {
     Swipe,    // Fast press movement and release of a single point
     Pinch,    // All points moving away/closer to the midpoint
     Rotate,   // All points rotating from the midpoint
+    GestureTypeMax = Rotate,
 };
 
 // This is nn::hid::GestureDirection
@@ -69,7 +82,7 @@ struct GestureState {
 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>, MaxPoints> points{};
     std::size_t active_points{};
     Common::Point<s32> mid_point{};
     s64 detection_count{};
@@ -78,13 +91,53 @@ struct GestureProperties {
     f32 angle{};
 };
 
+// This is nn::hid::TouchState
+struct TouchState {
+    u64 delta_time{};
+    Core::HID::TouchAttribute attribute{};
+    u32 finger{};
+    Common::Point<u32> position{};
+    u32 diameter_x{};
+    u32 diameter_y{};
+    s32 rotation_angle{};
+};
+static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
+
 // 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{};
+    std::array<TouchState, MaxFingers> states{};
 };
 static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
 
+struct TouchFingerMap {
+    s32 finger_count{};
+    Core::HID::TouchScreenModeForNx touch_mode;
+    INSERT_PADDING_BYTES(3);
+    std::array<u32, MaxFingers> finger_ids{};
+};
+static_assert(sizeof(TouchFingerMap) == 0x48, "TouchFingerMap is an invalid size");
+
+struct TouchAruidData {
+    u64 aruid;
+    u32 basic_gesture_id;
+    u64 used_1;
+    u64 used_2;
+    u64 used_3;
+    u64 used_4;
+    GestureType gesture_type;
+    u16 resolution_width;
+    u16 resolution_height;
+    TouchFingerMap finger_map;
+};
+static_assert(sizeof(TouchAruidData) == 0x80, "TouchAruidData is an invalid size");
+
+struct AutoPilotState {
+    u64 count;
+    std::array<TouchState, 16> state;
+};
+static_assert(sizeof(AutoPilotState) == 0x288, "AutoPilotState is an invalid size");
+
 } // namespace Service::HID