From 575183d6dcd8da9b10ee41e47be4b7d4f8631783 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Tue, 16 Jan 2024 12:17:18 -0600 Subject: [PATCH] service: hid: Fully implement touch resource --- src/core/hle/service/hid/hid.cpp | 5 +- src/core/hle/service/hid/hid_debug_server.cpp | 196 +++++- src/core/hle/service/hid/hid_debug_server.h | 15 +- src/core/hle/service/hid/hid_server.cpp | 24 +- .../hle/service/hid/hid_system_server.cpp | 66 +- src/core/hle/service/hid/hid_system_server.h | 3 + .../set/setting_formats/system_settings.h | 4 +- .../service/set/system_settings_server.cpp | 37 +- .../hle/service/set/system_settings_server.h | 4 + src/frontend_common/config.cpp | 8 +- src/hid_core/CMakeLists.txt | 7 +- src/hid_core/hid_result.h | 8 + src/hid_core/hid_types.h | 28 +- src/hid_core/resource_manager.cpp | 61 +- src/hid_core/resource_manager.h | 85 ++- src/hid_core/resources/applet_resource.h | 8 +- src/hid_core/resources/npad/npad.cpp | 3 + src/hid_core/resources/npad/npad.h | 4 +- .../resources/touch_screen/gesture.cpp | 375 +----------- src/hid_core/resources/touch_screen/gesture.h | 87 +-- .../touch_screen/gesture_handler.cpp | 260 ++++++++ .../resources/touch_screen/gesture_handler.h | 55 ++ .../resources/touch_screen/gesture_types.h | 77 --- .../resources/touch_screen/touch_screen.cpp | 215 +++---- .../resources/touch_screen/touch_screen.h | 71 ++- .../touch_screen/touch_screen_driver.cpp | 114 ++++ .../touch_screen/touch_screen_driver.h | 47 ++ .../touch_screen/touch_screen_resource.cpp | 579 ++++++++++++++++++ .../touch_screen/touch_screen_resource.h | 126 ++++ .../resources/touch_screen/touch_types.h | 61 +- 30 files changed, 1888 insertions(+), 745 deletions(-) create mode 100644 src/hid_core/resources/touch_screen/gesture_handler.cpp create mode 100644 src/hid_core/resources/touch_screen/gesture_handler.h delete mode 100644 src/hid_core/resources/touch_screen/gesture_types.h create mode 100644 src/hid_core/resources/touch_screen/touch_screen_driver.cpp create mode 100644 src/hid_core/resources/touch_screen/touch_screen_driver.h create mode 100644 src/hid_core/resources/touch_screen/touch_screen_resource.cpp create mode 100644 src/hid_core/resources/touch_screen/touch_screen_resource.h diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 5b28be577..b60fb9139 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(system); - std::shared_ptr resource_manager = std::make_shared(system); std::shared_ptr firmware_settings = std::make_shared(system); + std::shared_ptr resource_manager = + std::make_shared(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(system, resource_manager, firmware_settings)); server_manager->RegisterNamedService( - "hid:dbg", std::make_shared(system, resource_manager)); + "hid:dbg", std::make_shared(system, resource_manager, firmware_settings)); server_manager->RegisterNamedService( "hid:sys", std::make_shared(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 f2a767d37..610af34dd 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 + #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 resource) - : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource} { +IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr resource, + std::shared_ptr 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_ptrIsDeviceManaged()) { + 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(); + const auto buffer = ctx.ReadBuffer(); + + auto_pilot.count = std::min(auto_pilot.count, static_cast(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()}; + + 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()}; + + 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 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 406db2211..7d5b082b3 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 { public: - explicit IHidDebugServer(Core::System& system_, std::shared_ptr resource); + explicit IHidDebugServer(Core::System& system_, std::shared_ptr resource, + std::shared_ptr 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 GetResourceManager(); std::shared_ptr resource_manager; + std::shared_ptr 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 09c47b5e3..9ffe027ff 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()}; + auto touchscreen_config{rp.PopRaw()}; const auto applet_resource_user_id{rp.Pop()}; - 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()}; const auto applet_resource_user_id{rp.Pop()}; - 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 d1ec42edc..22471e9e2 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()}; + const auto point1y{rp.Pop()}; + const auto point2x{rp.Pop()}; + const auto point2y{rp.Pop()}; - 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()}; + + 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 4ab4d3931..738313e08 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 ebc373da5..40230182a 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 analog_stick_user_calibration_left; std::array 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 100cb2db4..c889aec47 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(); + 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 1982b9723..9a3b36f0c 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 905f35118..d34624d28 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(ReadIntegerSetting(std::string("touchscreen_angle"), 0)); Settings::values.touchscreen.diameter_x = - static_cast(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 15)); + static_cast(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 90)); Settings::values.touchscreen.diameter_y = - static_cast(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 15)); + static_cast(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(0))); WriteIntegerSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x, - std::make_optional(static_cast(15))); + std::make_optional(static_cast(90))); WriteIntegerSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y, - std::make_optional(static_cast(15))); + std::make_optional(static_cast(90))); } void Config::SaveMotionTouchValues() { diff --git a/src/hid_core/CMakeLists.txt b/src/hid_core/CMakeLists.txt index aa85502b5..055954224 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 df9b28c9a..c8dd07bfe 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 a01292a70..3cf12f6e5 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 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 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 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 e78665d31..68ce2c7ae 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 settings) + : firmware_settings{settings}, system{system_}, service_context{system_, "hid"} { applet_resource = std::make_shared(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(); + 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(); + 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(system.HIDCore()); mouse = std::make_shared(system.HIDCore()); @@ -170,7 +191,6 @@ void ResourceManager::InitializeHidCommonSampler() { keyboard = std::make_shared(system.HIDCore()); unique_pad = std::make_shared(system.HIDCore()); npad = std::make_shared(system.HIDCore(), service_context); - gesture = std::make_shared(system.HIDCore()); home_button = std::make_shared(system.HIDCore()); sleep_button = std::make_shared(system.HIDCore()); capture_button = std::make_shared(system.HIDCore()); @@ -185,7 +205,8 @@ void ResourceManager::InitializeHidCommonSampler() { const auto settings = system.ServiceManager().GetService("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(system.HIDCore()); - touch_screen = std::make_shared(system.HIDCore()); + // This is nn.hid.TouchScreenSampler + touch_resource = std::make_shared(system); + touch_driver = std::make_shared(system.HIDCore()); + touch_screen = std::make_shared(touch_resource); + gesture = std::make_shared(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 { + 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 128e00125..0bfe09511 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 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 applet_resource = nullptr; + std::shared_ptr applet_resource{nullptr}; - std::shared_ptr capture_button = nullptr; - std::shared_ptr console_six_axis = nullptr; - std::shared_ptr debug_mouse = nullptr; - std::shared_ptr debug_pad = nullptr; - std::shared_ptr digitizer = nullptr; - std::shared_ptr gesture = nullptr; - std::shared_ptr home_button = nullptr; - std::shared_ptr keyboard = nullptr; - std::shared_ptr mouse = nullptr; - std::shared_ptr npad = nullptr; - std::shared_ptr palma = nullptr; - std::shared_ptr seven_six_axis = nullptr; - std::shared_ptr six_axis = nullptr; - std::shared_ptr sleep_button = nullptr; - std::shared_ptr touch_screen = nullptr; - std::shared_ptr unique_pad = nullptr; + mutable std::mutex input_mutex; + Kernel::KEvent* input_event{nullptr}; - std::shared_ptr handheld_config = nullptr; + std::shared_ptr handheld_config{nullptr}; + std::shared_ptr firmware_settings{nullptr}; + + std::shared_ptr capture_button{nullptr}; + std::shared_ptr console_six_axis{nullptr}; + std::shared_ptr debug_mouse{nullptr}; + std::shared_ptr debug_pad{nullptr}; + std::shared_ptr digitizer{nullptr}; + std::shared_ptr home_button{nullptr}; + std::shared_ptr keyboard{nullptr}; + std::shared_ptr mouse{nullptr}; + std::shared_ptr npad{nullptr}; + std::shared_ptr palma{nullptr}; + std::shared_ptr seven_six_axis{nullptr}; + std::shared_ptr six_axis{nullptr}; + std::shared_ptr sleep_button{nullptr}; + std::shared_ptr unique_pad{nullptr}; // TODO: Create these resources - // std::shared_ptr audio_control = nullptr; - // std::shared_ptr button_config = nullptr; - // std::shared_ptr config = nullptr; - // std::shared_ptr connection = nullptr; - // std::shared_ptr custom_config = nullptr; - // std::shared_ptr digitizer = nullptr; - // std::shared_ptr hdls = nullptr; - // std::shared_ptr play_report = nullptr; - // std::shared_ptr rail = nullptr; + // std::shared_ptr audio_control{nullptr}; + // std::shared_ptr button_config{nullptr}; + // std::shared_ptr config{nullptr}; + // std::shared_ptr connection{nullptr}; + // std::shared_ptr custom_config{nullptr}; + // std::shared_ptr digitizer{nullptr}; + // std::shared_ptr hdls{nullptr}; + // std::shared_ptr play_report{nullptr}; + // std::shared_ptr rail{nullptr}; + + // Touch Resources + std::shared_ptr gesture{nullptr}; + std::shared_ptr touch_screen{nullptr}; + std::shared_ptr touch_resource{nullptr}; + std::shared_ptr touch_driver{nullptr}; + std::shared_ptr touch_update_event{nullptr}; Core::System& system; KernelHelpers::ServiceContext service_context; @@ -162,12 +179,12 @@ public: private: void GetSharedMemoryHandle(HLERequestContext& ctx); - std::shared_ptr npad_update_event; - std::shared_ptr default_update_event; - std::shared_ptr mouse_keyboard_update_event; - std::shared_ptr motion_update_event; + std::shared_ptr npad_update_event{nullptr}; + std::shared_ptr default_update_event{nullptr}; + std::shared_ptr mouse_keyboard_update_event{nullptr}; + std::shared_ptr motion_update_event{nullptr}; - u64 aruid; + u64 aruid{}; std::shared_ptr resource_manager; }; diff --git a/src/hid_core/resources/applet_resource.h b/src/hid_core/resources/applet_resource.h index e9710d306..4a5416fb2 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 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 cde84b1bb..8ab26bc36 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 resource, std::recursive_mutex* shared_mutex, std::shared_ptr handheld_config, + Kernel::KEvent* input_event, std::mutex* input_mutex, std::shared_ptr 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 502cb9b55..e81cc3abe 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 resource, std::recursive_mutex* shared_mutex, std::shared_ptr handheld_config, + Kernel::KEvent* input_event, std::mutex* input_mutex, std::shared_ptr settings); AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); @@ -206,9 +207,6 @@ private: std::array abstracted_pads; NpadVibration vibration_handler{}; - Kernel::KEvent* input_event{nullptr}; - std::mutex* input_mutex{nullptr}; - std::atomic press_state{}; std::array, AruidIndexMax> controller_data{}; diff --git a/src/hid_core/resources/touch_screen/gesture.cpp b/src/hid_core/resources/touch_screen/gesture.cpp index 0ecc0941f..eaa0cc7d0 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(num * num); -} +Gesture::Gesture(std::shared_ptr 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(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(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(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(next_state.delta.x) / time_difference; - next_state.vel_y = static_cast(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(last_entry.delta.x) / (last_pan_time_difference + time_difference); - next_state.vel_y = - static_cast(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 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::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(active_x * Layout::ScreenUndocked::Width), - .y = static_cast(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(active_x * Layout::ScreenDocked::Width), - .y = static_cast(active_y * Layout::ScreenDocked::Height), - }; - } - - gesture.mid_point.x += static_cast(gesture.points[id].x / gesture.active_points); - gesture.mid_point.y += static_cast(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(gesture.active_points); - } - - gesture.angle = std::atan2(static_cast(gesture.mid_point.y - gesture.points[0].y), - static_cast(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 32e9a8690..d92912bb6 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 +#include #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 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 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 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 000000000..4fcaf6ecf --- /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(num * num); +} + +GestureHandler::GestureHandler() {} + +GestureHandler::~GestureHandler() {} + +void GestureHandler::SetTouchState(std::span touch_state, u32 count, s64 timestamp) { + gesture = {}; + gesture.active_points = std::min(MaxPoints, static_cast(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(active_x), + .y = static_cast(active_y), + }; + + gesture.mid_point.x += static_cast(gesture.points[id].x / gesture.active_points); + gesture.mid_point.y += static_cast(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(gesture.active_points); + } + + gesture.angle = std::atan2(static_cast(gesture.mid_point.y - gesture.points[0].y), + static_cast(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(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(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(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(next_state.delta.x) / time_difference; + next_state.vel_y = static_cast(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(last_gesture_state.delta.x) / (last_pan_time_difference + time_difference); + next_state.vel_y = + static_cast(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 000000000..fda2040c9 --- /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 + +#include "hid_core/resources/touch_screen/touch_types.h" + +namespace Service::HID { + +class GestureHandler { +public: + GestureHandler(); + ~GestureHandler(); + + void SetTouchState(std::span 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 b4f034cd3..000000000 --- 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 -#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 pos{}; - Common::Point delta{}; - f32 vel_x{}; - f32 vel_y{}; - GestureAttribute attributes{}; - f32 scale{}; - f32 rotation_angle{}; - s32 point_count{}; - std::array, 4> points{}; -}; -static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); - -struct GestureProperties { - std::array, MAX_POINTS> points{}; - std::size_t active_points{}; - Common::Point 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 48d956c51..35efb1786 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 -#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 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 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::distance(active_fingers.begin(), end_iter)); - - const u64 timestamp = static_cast(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(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(active_x * static_cast(touchscreen_width)), - .y = static_cast(active_y * static_cast(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 4b3824742..2fcb6247f 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 +#include -#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 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 fingers{}; - u32 touchscreen_width; - u32 touchscreen_height; + mutable std::mutex mutex; + std::shared_ptr touch_resource; + std::shared_ptr 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 000000000..6a64c75b3 --- /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 +#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 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::distance(active_fingers.begin(), end_iter)); + + touch_status.entry_count = static_cast(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(active_x * TouchSensorWidth), + .y = static_cast(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 000000000..ca7e4970e --- /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 000000000..56e8e8e51 --- /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("set:sys"); +} + +TouchResource::~TouchResource() { + Finalize(); +}; + +Result TouchResource::ActivateTouch() { + if (global_ref_counter == std::numeric_limits::max() - 1 || + touch_ref_counter == std::numeric_limits::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(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::max() - 1 || + gesture_ref_counter == std::numeric_limits::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 driver) { + touch_driver = driver; +} + +void TouchResource::SetAppletResource(std::shared_ptr 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 config) { + handheld_config = config; +} + +void TouchResource::SetTimerEvent(std::shared_ptr 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(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(width); + data.resolution_height = static_cast(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(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(auto_pilot.count); + current_touch_state.entry_count = static_cast(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(current_touch_state.entry_count); i++) { + auto& state = current_touch_state.states[i]; + state.position.x = static_cast((magnification.y * static_cast(state.position.x)) + + (offset.x * static_cast(TouchSensorWidth))); + state.position.y = static_cast((magnification.y * static_cast(state.position.y)) + + (offset.x * static_cast(TouchSensorHeight))); + state.diameter_x = static_cast(magnification.x * static_cast(state.diameter_x)); + state.diameter_y = static_cast(magnification.y * static_cast(state.diameter_y)); + } + + std::size_t index = 0; + for (std::size_t i = 0; i < static_cast(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(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(current_touch_state.entry_count); + i++) { + s32 delta_x = std::abs(static_cast(current_touch_state.states[i].position.x) - + static_cast(previous_touch_state.states[i].position.x)); + s32 delta_y = std::abs(static_cast(current_touch_state.states[i].position.y) - + static_cast(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()); + touch_shared.touch_screen_lifo.WriteNextEntry(current_touch_state); + } + } +} + +void TouchResource::SanitizeInput(TouchScreenState& state) const { + for (std::size_t i = 0; i < static_cast(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 000000000..095cddd76 --- /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 +#include + +#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 driver); + void SetAppletResource(std::shared_ptr shared, std::recursive_mutex* mutex); + void SetInputEvent(Kernel::KEvent* event, std::mutex* mutex); + void SetHandheldConfig(std::shared_ptr config); + void SetTimerEvent(std::shared_ptr 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 timer_event{nullptr}; + std::shared_ptr touch_driver{nullptr}; + std::shared_ptr applet_resource{nullptr}; + std::recursive_mutex* shared_mutex{nullptr}; + std::shared_ptr 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 aruid_data{}; + Common::Point magnification{1.0f, 1.0f}; + Common::Point offset{0.0f, 0.0f}; + Core::HID::TouchScreenModeForNx default_touch_screen_mode{ + Core::HID::TouchScreenModeForNx::Finger}; + + Core::System& system; + std::shared_ptr 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 97ee847da..362088939 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, MAX_POINTS> points{}; + std::array, MaxPoints> points{}; std::size_t active_points{}; Common::Point 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 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 states{}; + std::array 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 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 state; +}; +static_assert(sizeof(AutoPilotState) == 0x288, "AutoPilotState is an invalid size"); + } // namespace Service::HID