diff options
| author | 2024-01-16 12:17:18 -0600 | |
|---|---|---|
| committer | 2024-01-28 18:27:25 -0600 | |
| commit | 575183d6dcd8da9b10ee41e47be4b7d4f8631783 (patch) | |
| tree | d2898bdefae5be2fb68e7df97465422c0fae3991 /src/hid_core/resources | |
| parent | Merge pull request #12555 from flodavid/fix-gamemode-setting (diff) | |
| download | yuzu-575183d6dcd8da9b10ee41e47be4b7d4f8631783.tar.gz yuzu-575183d6dcd8da9b10ee41e47be4b7d4f8631783.tar.xz yuzu-575183d6dcd8da9b10ee41e47be4b7d4f8631783.zip | |
service: hid: Fully implement touch resource
Diffstat (limited to 'src/hid_core/resources')
| -rw-r--r-- | src/hid_core/resources/applet_resource.h | 8 | ||||
| -rw-r--r-- | src/hid_core/resources/npad/npad.cpp | 3 | ||||
| -rw-r--r-- | src/hid_core/resources/npad/npad.h | 4 | ||||
| -rw-r--r-- | src/hid_core/resources/touch_screen/gesture.cpp | 367 | ||||
| -rw-r--r-- | src/hid_core/resources/touch_screen/gesture.h | 87 | ||||
| -rw-r--r-- | src/hid_core/resources/touch_screen/gesture_handler.cpp | 260 | ||||
| -rw-r--r-- | src/hid_core/resources/touch_screen/gesture_handler.h | 55 | ||||
| -rw-r--r-- | src/hid_core/resources/touch_screen/gesture_types.h | 77 | ||||
| -rw-r--r-- | src/hid_core/resources/touch_screen/touch_screen.cpp | 209 | ||||
| -rw-r--r-- | src/hid_core/resources/touch_screen/touch_screen.h | 71 | ||||
| -rw-r--r-- | src/hid_core/resources/touch_screen/touch_screen_driver.cpp | 114 | ||||
| -rw-r--r-- | src/hid_core/resources/touch_screen/touch_screen_driver.h | 47 | ||||
| -rw-r--r-- | src/hid_core/resources/touch_screen/touch_screen_resource.cpp | 579 | ||||
| -rw-r--r-- | src/hid_core/resources/touch_screen/touch_screen_resource.h | 126 | ||||
| -rw-r--r-- | src/hid_core/resources/touch_screen/touch_types.h | 61 |
15 files changed, 1434 insertions, 634 deletions
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 @@ | |||
| 13 | 13 | ||
| 14 | namespace Core { | 14 | namespace Core { |
| 15 | class System; | 15 | class System; |
| 16 | } | 16 | } // namespace Core |
| 17 | 17 | ||
| 18 | namespace Kernel { | 18 | namespace Kernel { |
| 19 | class KEvent; | ||
| 19 | class KSharedMemory; | 20 | class KSharedMemory; |
| 20 | } | 21 | } // namespace Kernel |
| 21 | 22 | ||
| 22 | namespace Service::HID { | 23 | namespace Service::HID { |
| 23 | struct SharedMemoryFormat; | 24 | struct SharedMemoryFormat; |
| @@ -73,7 +74,8 @@ struct AppletResourceHolder { | |||
| 73 | std::recursive_mutex* shared_mutex{nullptr}; | 74 | std::recursive_mutex* shared_mutex{nullptr}; |
| 74 | NPadResource* shared_npad_resource{nullptr}; | 75 | NPadResource* shared_npad_resource{nullptr}; |
| 75 | std::shared_ptr<HandheldConfig> handheld_config{nullptr}; | 76 | std::shared_ptr<HandheldConfig> handheld_config{nullptr}; |
| 76 | long* handle_1; | 77 | Kernel::KEvent* input_event{nullptr}; |
| 78 | std::mutex* input_mutex{nullptr}; | ||
| 77 | }; | 79 | }; |
| 78 | 80 | ||
| 79 | class AppletResource { | 81 | 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) { | |||
| 1081 | void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource, | 1081 | void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource, |
| 1082 | std::recursive_mutex* shared_mutex, | 1082 | std::recursive_mutex* shared_mutex, |
| 1083 | std::shared_ptr<HandheldConfig> handheld_config, | 1083 | std::shared_ptr<HandheldConfig> handheld_config, |
| 1084 | Kernel::KEvent* input_event, std::mutex* input_mutex, | ||
| 1084 | std::shared_ptr<Service::Set::ISystemSettingsServer> settings) { | 1085 | std::shared_ptr<Service::Set::ISystemSettingsServer> settings) { |
| 1085 | applet_resource_holder.applet_resource = resource; | 1086 | applet_resource_holder.applet_resource = resource; |
| 1086 | applet_resource_holder.shared_mutex = shared_mutex; | 1087 | applet_resource_holder.shared_mutex = shared_mutex; |
| 1087 | applet_resource_holder.shared_npad_resource = &npad_resource; | 1088 | applet_resource_holder.shared_npad_resource = &npad_resource; |
| 1088 | applet_resource_holder.handheld_config = handheld_config; | 1089 | applet_resource_holder.handheld_config = handheld_config; |
| 1090 | applet_resource_holder.input_event = input_event; | ||
| 1091 | applet_resource_holder.input_mutex = input_mutex; | ||
| 1089 | 1092 | ||
| 1090 | vibration_handler.SetSettingsService(settings); | 1093 | vibration_handler.SetSettingsService(settings); |
| 1091 | 1094 | ||
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: | |||
| 133 | void SetNpadExternals(std::shared_ptr<AppletResource> resource, | 133 | void SetNpadExternals(std::shared_ptr<AppletResource> resource, |
| 134 | std::recursive_mutex* shared_mutex, | 134 | std::recursive_mutex* shared_mutex, |
| 135 | std::shared_ptr<HandheldConfig> handheld_config, | 135 | std::shared_ptr<HandheldConfig> handheld_config, |
| 136 | Kernel::KEvent* input_event, std::mutex* input_mutex, | ||
| 136 | std::shared_ptr<Service::Set::ISystemSettingsServer> settings); | 137 | std::shared_ptr<Service::Set::ISystemSettingsServer> settings); |
| 137 | 138 | ||
| 138 | AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); | 139 | AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); |
| @@ -206,9 +207,6 @@ private: | |||
| 206 | std::array<AbstractPad, MaxSupportedNpadIdTypes> abstracted_pads; | 207 | std::array<AbstractPad, MaxSupportedNpadIdTypes> abstracted_pads; |
| 207 | NpadVibration vibration_handler{}; | 208 | NpadVibration vibration_handler{}; |
| 208 | 209 | ||
| 209 | Kernel::KEvent* input_event{nullptr}; | ||
| 210 | std::mutex* input_mutex{nullptr}; | ||
| 211 | |||
| 212 | std::atomic<u64> press_state{}; | 210 | std::atomic<u64> press_state{}; |
| 213 | std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax> | 211 | std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax> |
| 214 | controller_data{}; | 212 | 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/math_util.h" | ||
| 5 | #include "common/settings.h" | ||
| 6 | #include "core/frontend/emu_window.h" | ||
| 7 | #include "hid_core/frontend/emulated_console.h" | ||
| 8 | #include "hid_core/hid_core.h" | ||
| 9 | #include "hid_core/resources/applet_resource.h" | ||
| 10 | #include "hid_core/resources/shared_memory_format.h" | ||
| 11 | #include "hid_core/resources/touch_screen/gesture.h" | 4 | #include "hid_core/resources/touch_screen/gesture.h" |
| 5 | #include "hid_core/resources/touch_screen/touch_screen_resource.h" | ||
| 12 | 6 | ||
| 13 | namespace Service::HID { | 7 | namespace Service::HID { |
| 14 | // HW is around 700, value is set to 400 to make it easier to trigger with mouse | ||
| 15 | constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s | ||
| 16 | constexpr f32 angle_threshold = 0.015f; // Threshold in radians | ||
| 17 | constexpr f32 pinch_threshold = 0.5f; // Threshold in pixels | ||
| 18 | constexpr f32 press_delay = 0.5f; // Time in seconds | ||
| 19 | constexpr f32 double_tap_delay = 0.35f; // Time in seconds | ||
| 20 | 8 | ||
| 21 | constexpr f32 Square(s32 num) { | 9 | Gesture::Gesture(std::shared_ptr<TouchResource> resource) : touch_resource{resource} {} |
| 22 | return static_cast<f32>(num * num); | ||
| 23 | } | ||
| 24 | 10 | ||
| 25 | Gesture::Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) { | ||
| 26 | console = hid_core.GetEmulatedConsole(); | ||
| 27 | } | ||
| 28 | Gesture::~Gesture() = default; | 11 | Gesture::~Gesture() = default; |
| 29 | 12 | ||
| 30 | void Gesture::OnInit() { | 13 | Result Gesture::Activate() { |
| 31 | std::scoped_lock shared_lock{*shared_mutex}; | 14 | std::scoped_lock lock{mutex}; |
| 32 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 33 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 34 | |||
| 35 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 36 | return; | ||
| 37 | } | ||
| 38 | |||
| 39 | shared_memory = &data->shared_memory_format->gesture; | ||
| 40 | shared_memory->gesture_lifo.buffer_count = 0; | ||
| 41 | shared_memory->gesture_lifo.buffer_tail = 0; | ||
| 42 | force_update = true; | ||
| 43 | } | ||
| 44 | |||
| 45 | void Gesture::OnRelease() {} | ||
| 46 | |||
| 47 | void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 48 | std::scoped_lock shared_lock{*shared_mutex}; | ||
| 49 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 50 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 51 | |||
| 52 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 53 | return; | ||
| 54 | } | ||
| 55 | |||
| 56 | shared_memory = &data->shared_memory_format->gesture; | ||
| 57 | |||
| 58 | if (!IsControllerActivated()) { | ||
| 59 | shared_memory->gesture_lifo.buffer_count = 0; | ||
| 60 | shared_memory->gesture_lifo.buffer_tail = 0; | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | |||
| 64 | ReadTouchInput(); | ||
| 65 | |||
| 66 | GestureProperties gesture = GetGestureProperties(); | ||
| 67 | f32 time_difference = | ||
| 68 | static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) / | ||
| 69 | (1000 * 1000 * 1000); | ||
| 70 | |||
| 71 | // Only update if necessary | ||
| 72 | if (!ShouldUpdateGesture(gesture, time_difference)) { | ||
| 73 | return; | ||
| 74 | } | ||
| 75 | |||
| 76 | last_update_timestamp = shared_memory->gesture_lifo.timestamp; | ||
| 77 | UpdateGestureSharedMemory(gesture, time_difference); | ||
| 78 | } | ||
| 79 | |||
| 80 | void Gesture::ReadTouchInput() { | ||
| 81 | if (!Settings::values.touchscreen.enabled) { | ||
| 82 | fingers = {}; | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | |||
| 86 | const auto touch_status = console->GetTouch(); | ||
| 87 | for (std::size_t id = 0; id < fingers.size(); ++id) { | ||
| 88 | fingers[id] = touch_status[id]; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) { | ||
| 93 | const auto& last_entry = GetLastGestureEntry(); | ||
| 94 | if (force_update) { | ||
| 95 | force_update = false; | ||
| 96 | return true; | ||
| 97 | } | ||
| 98 | |||
| 99 | // Update if coordinates change | ||
| 100 | for (size_t id = 0; id < MAX_POINTS; id++) { | ||
| 101 | if (gesture.points[id] != last_gesture.points[id]) { | ||
| 102 | return true; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | // Update on press and hold event after 0.5 seconds | ||
| 107 | if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 && | ||
| 108 | time_difference > press_delay) { | ||
| 109 | return enable_press_and_tap; | ||
| 110 | } | ||
| 111 | |||
| 112 | return false; | ||
| 113 | } | ||
| 114 | |||
| 115 | void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) { | ||
| 116 | GestureType type = GestureType::Idle; | ||
| 117 | GestureAttribute attributes{}; | ||
| 118 | |||
| 119 | const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state; | ||
| 120 | |||
| 121 | // Reset next state to default | ||
| 122 | next_state.sampling_number = last_entry.sampling_number + 1; | ||
| 123 | next_state.delta = {}; | ||
| 124 | next_state.vel_x = 0; | ||
| 125 | next_state.vel_y = 0; | ||
| 126 | next_state.direction = GestureDirection::None; | ||
| 127 | next_state.rotation_angle = 0; | ||
| 128 | next_state.scale = 0; | ||
| 129 | |||
| 130 | if (gesture.active_points > 0) { | ||
| 131 | if (last_gesture.active_points == 0) { | ||
| 132 | NewGesture(gesture, type, attributes); | ||
| 133 | } else { | ||
| 134 | UpdateExistingGesture(gesture, type, time_difference); | ||
| 135 | } | ||
| 136 | } else { | ||
| 137 | EndGesture(gesture, last_gesture, type, attributes, time_difference); | ||
| 138 | } | ||
| 139 | |||
| 140 | // Apply attributes | ||
| 141 | next_state.detection_count = gesture.detection_count; | ||
| 142 | next_state.type = type; | ||
| 143 | next_state.attributes = attributes; | ||
| 144 | next_state.pos = gesture.mid_point; | ||
| 145 | next_state.point_count = static_cast<s32>(gesture.active_points); | ||
| 146 | next_state.points = gesture.points; | ||
| 147 | last_gesture = gesture; | ||
| 148 | |||
| 149 | shared_memory->gesture_lifo.WriteNextEntry(next_state); | ||
| 150 | } | ||
| 151 | |||
| 152 | void Gesture::NewGesture(GestureProperties& gesture, GestureType& type, | ||
| 153 | GestureAttribute& attributes) { | ||
| 154 | const auto& last_entry = GetLastGestureEntry(); | ||
| 155 | |||
| 156 | gesture.detection_count++; | ||
| 157 | type = GestureType::Touch; | ||
| 158 | |||
| 159 | // New touch after cancel is not considered new | ||
| 160 | if (last_entry.type != GestureType::Cancel) { | ||
| 161 | attributes.is_new_touch.Assign(1); | ||
| 162 | enable_press_and_tap = true; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type, | ||
| 167 | f32 time_difference) { | ||
| 168 | const auto& last_entry = GetLastGestureEntry(); | ||
| 169 | 15 | ||
| 170 | // Promote to pan type if touch moved | 16 | // TODO: Result result = CreateThread(); |
| 171 | for (size_t id = 0; id < MAX_POINTS; id++) { | 17 | Result result = ResultSuccess; |
| 172 | if (gesture.points[id] != last_gesture.points[id]) { | 18 | if (result.IsError()) { |
| 173 | type = GestureType::Pan; | 19 | return result; |
| 174 | break; | ||
| 175 | } | ||
| 176 | } | 20 | } |
| 177 | 21 | ||
| 178 | // Number of fingers changed cancel the last event and clear data | 22 | result = touch_resource->ActivateGesture(); |
| 179 | if (gesture.active_points != last_gesture.active_points) { | ||
| 180 | type = GestureType::Cancel; | ||
| 181 | enable_press_and_tap = false; | ||
| 182 | gesture.active_points = 0; | ||
| 183 | gesture.mid_point = {}; | ||
| 184 | gesture.points.fill({}); | ||
| 185 | return; | ||
| 186 | } | ||
| 187 | |||
| 188 | // Calculate extra parameters of panning | ||
| 189 | if (type == GestureType::Pan) { | ||
| 190 | UpdatePanEvent(gesture, last_gesture, type, time_difference); | ||
| 191 | return; | ||
| 192 | } | ||
| 193 | 23 | ||
| 194 | // Promote to press type | 24 | if (result.IsError()) { |
| 195 | if (last_entry.type == GestureType::Touch) { | 25 | // TODO: StopThread(); |
| 196 | type = GestureType::Press; | ||
| 197 | } | 26 | } |
| 198 | } | ||
| 199 | |||
| 200 | void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 201 | GestureType& type, GestureAttribute& attributes, f32 time_difference) { | ||
| 202 | const auto& last_entry = GetLastGestureEntry(); | ||
| 203 | 27 | ||
| 204 | if (last_gesture_props.active_points != 0) { | 28 | return result; |
| 205 | switch (last_entry.type) { | ||
| 206 | case GestureType::Touch: | ||
| 207 | if (enable_press_and_tap) { | ||
| 208 | SetTapEvent(gesture, last_gesture_props, type, attributes); | ||
| 209 | return; | ||
| 210 | } | ||
| 211 | type = GestureType::Cancel; | ||
| 212 | force_update = true; | ||
| 213 | break; | ||
| 214 | case GestureType::Press: | ||
| 215 | case GestureType::Tap: | ||
| 216 | case GestureType::Swipe: | ||
| 217 | case GestureType::Pinch: | ||
| 218 | case GestureType::Rotate: | ||
| 219 | type = GestureType::Complete; | ||
| 220 | force_update = true; | ||
| 221 | break; | ||
| 222 | case GestureType::Pan: | ||
| 223 | EndPanEvent(gesture, last_gesture_props, type, time_difference); | ||
| 224 | break; | ||
| 225 | default: | ||
| 226 | break; | ||
| 227 | } | ||
| 228 | return; | ||
| 229 | } | ||
| 230 | if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) { | ||
| 231 | gesture.detection_count++; | ||
| 232 | } | ||
| 233 | } | 29 | } |
| 234 | 30 | ||
| 235 | void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | 31 | Result Gesture::Activate(u64 aruid, u32 basic_gesture_id) { |
| 236 | GestureType& type, GestureAttribute& attributes) { | 32 | std::scoped_lock lock{mutex}; |
| 237 | type = GestureType::Tap; | 33 | return touch_resource->ActivateGesture(aruid, basic_gesture_id); |
| 238 | gesture = last_gesture_props; | ||
| 239 | force_update = true; | ||
| 240 | f32 tap_time_difference = | ||
| 241 | static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000); | ||
| 242 | last_tap_timestamp = last_update_timestamp; | ||
| 243 | if (tap_time_difference < double_tap_delay) { | ||
| 244 | attributes.is_double_tap.Assign(1); | ||
| 245 | } | ||
| 246 | } | 34 | } |
| 247 | 35 | ||
| 248 | void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | 36 | Result Gesture::Deactivate() { |
| 249 | GestureType& type, f32 time_difference) { | 37 | std::scoped_lock lock{mutex}; |
| 250 | const auto& last_entry = GetLastGestureEntry(); | 38 | const auto result = touch_resource->DeactivateGesture(); |
| 251 | 39 | ||
| 252 | next_state.delta = gesture.mid_point - last_entry.pos; | 40 | if (result.IsError()) { |
| 253 | next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference; | 41 | return result; |
| 254 | next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference; | ||
| 255 | last_pan_time_difference = time_difference; | ||
| 256 | |||
| 257 | // Promote to pinch type | ||
| 258 | if (std::abs(gesture.average_distance - last_gesture_props.average_distance) > | ||
| 259 | pinch_threshold) { | ||
| 260 | type = GestureType::Pinch; | ||
| 261 | next_state.scale = gesture.average_distance / last_gesture_props.average_distance; | ||
| 262 | } | 42 | } |
| 263 | 43 | ||
| 264 | const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) / | 44 | // TODO: return StopThread(); |
| 265 | (1 + (gesture.angle * last_gesture_props.angle))); | 45 | return ResultSuccess; |
| 266 | // Promote to rotate type | ||
| 267 | if (std::abs(angle_between_two_lines) > angle_threshold) { | ||
| 268 | type = GestureType::Rotate; | ||
| 269 | next_state.scale = 0; | ||
| 270 | next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI; | ||
| 271 | } | ||
| 272 | } | 46 | } |
| 273 | 47 | ||
| 274 | void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | 48 | Result Gesture::IsActive(bool& out_is_active) const { |
| 275 | GestureType& type, f32 time_difference) { | 49 | out_is_active = touch_resource->IsGestureActive(); |
| 276 | const auto& last_entry = GetLastGestureEntry(); | 50 | return ResultSuccess; |
| 277 | next_state.vel_x = | ||
| 278 | static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); | ||
| 279 | next_state.vel_y = | ||
| 280 | static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference); | ||
| 281 | const f32 curr_vel = | ||
| 282 | std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y)); | ||
| 283 | |||
| 284 | // Set swipe event with parameters | ||
| 285 | if (curr_vel > swipe_threshold) { | ||
| 286 | SetSwipeEvent(gesture, last_gesture_props, type); | ||
| 287 | return; | ||
| 288 | } | ||
| 289 | |||
| 290 | // End panning without swipe | ||
| 291 | type = GestureType::Complete; | ||
| 292 | next_state.vel_x = 0; | ||
| 293 | next_state.vel_y = 0; | ||
| 294 | force_update = true; | ||
| 295 | } | ||
| 296 | |||
| 297 | void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 298 | GestureType& type) { | ||
| 299 | const auto& last_entry = GetLastGestureEntry(); | ||
| 300 | |||
| 301 | type = GestureType::Swipe; | ||
| 302 | gesture = last_gesture_props; | ||
| 303 | force_update = true; | ||
| 304 | next_state.delta = last_entry.delta; | ||
| 305 | |||
| 306 | if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) { | ||
| 307 | if (next_state.delta.x > 0) { | ||
| 308 | next_state.direction = GestureDirection::Right; | ||
| 309 | return; | ||
| 310 | } | ||
| 311 | next_state.direction = GestureDirection::Left; | ||
| 312 | return; | ||
| 313 | } | ||
| 314 | if (next_state.delta.y > 0) { | ||
| 315 | next_state.direction = GestureDirection::Down; | ||
| 316 | return; | ||
| 317 | } | ||
| 318 | next_state.direction = GestureDirection::Up; | ||
| 319 | } | ||
| 320 | |||
| 321 | const GestureState& Gesture::GetLastGestureEntry() const { | ||
| 322 | return shared_memory->gesture_lifo.ReadCurrentEntry().state; | ||
| 323 | } | ||
| 324 | |||
| 325 | GestureProperties Gesture::GetGestureProperties() { | ||
| 326 | GestureProperties gesture; | ||
| 327 | std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers; | ||
| 328 | const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), | ||
| 329 | [](const auto& finger) { return finger.pressed; }); | ||
| 330 | gesture.active_points = | ||
| 331 | static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); | ||
| 332 | |||
| 333 | for (size_t id = 0; id < gesture.active_points; ++id) { | ||
| 334 | const auto& [active_x, active_y] = active_fingers[id].position; | ||
| 335 | gesture.points[id] = { | ||
| 336 | .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width), | ||
| 337 | .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height), | ||
| 338 | }; | ||
| 339 | |||
| 340 | // Hack: There is no touch in docked but games still allow it | ||
| 341 | if (Settings::IsDockedMode()) { | ||
| 342 | gesture.points[id] = { | ||
| 343 | .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width), | ||
| 344 | .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height), | ||
| 345 | }; | ||
| 346 | } | ||
| 347 | |||
| 348 | gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points); | ||
| 349 | gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points); | ||
| 350 | } | ||
| 351 | |||
| 352 | for (size_t id = 0; id < gesture.active_points; ++id) { | ||
| 353 | const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) + | ||
| 354 | Square(gesture.mid_point.y - gesture.points[id].y)); | ||
| 355 | gesture.average_distance += distance / static_cast<f32>(gesture.active_points); | ||
| 356 | } | ||
| 357 | |||
| 358 | gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y), | ||
| 359 | static_cast<f32>(gesture.mid_point.x - gesture.points[0].x)); | ||
| 360 | |||
| 361 | gesture.detection_count = last_gesture.detection_count; | ||
| 362 | |||
| 363 | return gesture; | ||
| 364 | } | 51 | } |
| 365 | 52 | ||
| 366 | } // namespace Service::HID | 53 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <mutex> |
| 7 | 7 | ||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "hid_core/resources/controller_base.h" | 9 | #include "core/hle/result.h" |
| 10 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 11 | |||
| 12 | namespace Core::HID { | ||
| 13 | class EmulatedConsole; | ||
| 14 | } | ||
| 15 | 10 | ||
| 16 | namespace Service::HID { | 11 | namespace Service::HID { |
| 17 | struct GestureSharedMemoryFormat; | 12 | class TouchResource; |
| 18 | 13 | ||
| 19 | class Gesture final : public ControllerBase { | 14 | /// Handles gesture request from HID interfaces |
| 15 | class Gesture { | ||
| 20 | public: | 16 | public: |
| 21 | explicit Gesture(Core::HID::HIDCore& hid_core_); | 17 | Gesture(std::shared_ptr<TouchResource> resource); |
| 22 | ~Gesture() override; | 18 | ~Gesture(); |
| 23 | 19 | ||
| 24 | // Called when the controller is initialized | 20 | Result Activate(); |
| 25 | void OnInit() override; | 21 | Result Activate(u64 aruid, u32 basic_gesture_id); |
| 26 | 22 | ||
| 27 | // When the controller is released | 23 | Result Deactivate(); |
| 28 | void OnRelease() override; | ||
| 29 | 24 | ||
| 30 | // When the controller is requesting an update for the shared memory | 25 | Result IsActive(bool& out_is_active) const; |
| 31 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 32 | 26 | ||
| 33 | private: | 27 | private: |
| 34 | // Reads input from all available input engines | 28 | mutable std::mutex mutex; |
| 35 | void ReadTouchInput(); | 29 | std::shared_ptr<TouchResource> touch_resource; |
| 36 | |||
| 37 | // Returns true if gesture state needs to be updated | ||
| 38 | bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference); | ||
| 39 | |||
| 40 | // Updates the shared memory to the next state | ||
| 41 | void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference); | ||
| 42 | |||
| 43 | // Initializes new gesture | ||
| 44 | void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes); | ||
| 45 | |||
| 46 | // Updates existing gesture state | ||
| 47 | void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference); | ||
| 48 | |||
| 49 | // Terminates exiting gesture | ||
| 50 | void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 51 | GestureType& type, GestureAttribute& attributes, f32 time_difference); | ||
| 52 | |||
| 53 | // Set current event to a tap event | ||
| 54 | void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 55 | GestureType& type, GestureAttribute& attributes); | ||
| 56 | |||
| 57 | // Calculates and set the extra parameters related to a pan event | ||
| 58 | void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 59 | GestureType& type, f32 time_difference); | ||
| 60 | |||
| 61 | // Terminates the pan event | ||
| 62 | void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 63 | GestureType& type, f32 time_difference); | ||
| 64 | |||
| 65 | // Set current event to a swipe event | ||
| 66 | void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 67 | GestureType& type); | ||
| 68 | |||
| 69 | // Retrieves the last gesture entry, as indicated by shared memory indices. | ||
| 70 | [[nodiscard]] const GestureState& GetLastGestureEntry() const; | ||
| 71 | |||
| 72 | // Returns the average distance, angle and middle point of the active fingers | ||
| 73 | GestureProperties GetGestureProperties(); | ||
| 74 | |||
| 75 | GestureState next_state{}; | ||
| 76 | GestureSharedMemoryFormat* shared_memory; | ||
| 77 | Core::HID::EmulatedConsole* console = nullptr; | ||
| 78 | |||
| 79 | std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{}; | ||
| 80 | GestureProperties last_gesture{}; | ||
| 81 | s64 last_update_timestamp{}; | ||
| 82 | s64 last_tap_timestamp{}; | ||
| 83 | f32 last_pan_time_difference{}; | ||
| 84 | bool force_update{false}; | ||
| 85 | bool enable_press_and_tap{false}; | ||
| 86 | }; | 30 | }; |
| 31 | |||
| 87 | } // namespace Service::HID | 32 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "common/math_util.h" | ||
| 5 | #include "hid_core/resources/touch_screen/gesture_handler.h" | ||
| 6 | |||
| 7 | namespace Service::HID { | ||
| 8 | |||
| 9 | constexpr f32 Square(s32 num) { | ||
| 10 | return static_cast<f32>(num * num); | ||
| 11 | } | ||
| 12 | |||
| 13 | GestureHandler::GestureHandler() {} | ||
| 14 | |||
| 15 | GestureHandler::~GestureHandler() {} | ||
| 16 | |||
| 17 | void GestureHandler::SetTouchState(std::span<TouchState> touch_state, u32 count, s64 timestamp) { | ||
| 18 | gesture = {}; | ||
| 19 | gesture.active_points = std::min(MaxPoints, static_cast<std::size_t>(count)); | ||
| 20 | |||
| 21 | for (size_t id = 0; id < gesture.active_points; ++id) { | ||
| 22 | const auto& [active_x, active_y] = touch_state[id].position; | ||
| 23 | gesture.points[id] = { | ||
| 24 | .x = static_cast<s32>(active_x), | ||
| 25 | .y = static_cast<s32>(active_y), | ||
| 26 | }; | ||
| 27 | |||
| 28 | gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points); | ||
| 29 | gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points); | ||
| 30 | } | ||
| 31 | |||
| 32 | for (size_t id = 0; id < gesture.active_points; ++id) { | ||
| 33 | const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) + | ||
| 34 | Square(gesture.mid_point.y - gesture.points[id].y)); | ||
| 35 | gesture.average_distance += distance / static_cast<f32>(gesture.active_points); | ||
| 36 | } | ||
| 37 | |||
| 38 | gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y), | ||
| 39 | static_cast<f32>(gesture.mid_point.x - gesture.points[0].x)); | ||
| 40 | |||
| 41 | gesture.detection_count = last_gesture.detection_count; | ||
| 42 | |||
| 43 | if (last_update_timestamp > timestamp) { | ||
| 44 | timestamp = last_tap_timestamp; | ||
| 45 | } | ||
| 46 | |||
| 47 | time_difference = static_cast<f32>(timestamp - last_update_timestamp) / (1000 * 1000 * 1000); | ||
| 48 | } | ||
| 49 | |||
| 50 | bool GestureHandler::NeedsUpdate() { | ||
| 51 | if (force_update) { | ||
| 52 | force_update = false; | ||
| 53 | return true; | ||
| 54 | } | ||
| 55 | |||
| 56 | // Update if coordinates change | ||
| 57 | for (size_t id = 0; id < MaxPoints; id++) { | ||
| 58 | if (gesture.points[id] != last_gesture.points[id]) { | ||
| 59 | return true; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | // Update on press and hold event after 0.5 seconds | ||
| 64 | if (last_gesture_state.type == GestureType::Touch && last_gesture_state.point_count == 1 && | ||
| 65 | time_difference > PressDelay) { | ||
| 66 | return enable_press_and_tap; | ||
| 67 | } | ||
| 68 | |||
| 69 | return false; | ||
| 70 | } | ||
| 71 | |||
| 72 | void GestureHandler::UpdateGestureState(GestureState& next_state, s64 timestamp) { | ||
| 73 | last_update_timestamp = timestamp; | ||
| 74 | |||
| 75 | GestureType type = GestureType::Idle; | ||
| 76 | GestureAttribute attributes{}; | ||
| 77 | |||
| 78 | // Reset next state to default | ||
| 79 | next_state.sampling_number = last_gesture_state.sampling_number + 1; | ||
| 80 | next_state.delta = {}; | ||
| 81 | next_state.vel_x = 0; | ||
| 82 | next_state.vel_y = 0; | ||
| 83 | next_state.direction = GestureDirection::None; | ||
| 84 | next_state.rotation_angle = 0; | ||
| 85 | next_state.scale = 0; | ||
| 86 | |||
| 87 | if (gesture.active_points > 0) { | ||
| 88 | if (last_gesture.active_points == 0) { | ||
| 89 | NewGesture(type, attributes); | ||
| 90 | } else { | ||
| 91 | UpdateExistingGesture(next_state, type); | ||
| 92 | } | ||
| 93 | } else { | ||
| 94 | EndGesture(next_state, type, attributes); | ||
| 95 | } | ||
| 96 | |||
| 97 | // Apply attributes | ||
| 98 | next_state.detection_count = gesture.detection_count; | ||
| 99 | next_state.type = type; | ||
| 100 | next_state.attributes = attributes; | ||
| 101 | next_state.pos = gesture.mid_point; | ||
| 102 | next_state.point_count = static_cast<s32>(gesture.active_points); | ||
| 103 | next_state.points = gesture.points; | ||
| 104 | last_gesture = gesture; | ||
| 105 | last_gesture_state = next_state; | ||
| 106 | } | ||
| 107 | |||
| 108 | void GestureHandler::NewGesture(GestureType& type, GestureAttribute& attributes) { | ||
| 109 | gesture.detection_count++; | ||
| 110 | type = GestureType::Touch; | ||
| 111 | |||
| 112 | // New touch after cancel is not considered new | ||
| 113 | if (last_gesture_state.type != GestureType::Cancel) { | ||
| 114 | attributes.is_new_touch.Assign(1); | ||
| 115 | enable_press_and_tap = true; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | void GestureHandler::UpdateExistingGesture(GestureState& next_state, GestureType& type) { | ||
| 120 | // Promote to pan type if touch moved | ||
| 121 | for (size_t id = 0; id < MaxPoints; id++) { | ||
| 122 | if (gesture.points[id] != last_gesture.points[id]) { | ||
| 123 | type = GestureType::Pan; | ||
| 124 | break; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | // Number of fingers changed cancel the last event and clear data | ||
| 129 | if (gesture.active_points != last_gesture.active_points) { | ||
| 130 | type = GestureType::Cancel; | ||
| 131 | enable_press_and_tap = false; | ||
| 132 | gesture.active_points = 0; | ||
| 133 | gesture.mid_point = {}; | ||
| 134 | gesture.points.fill({}); | ||
| 135 | return; | ||
| 136 | } | ||
| 137 | |||
| 138 | // Calculate extra parameters of panning | ||
| 139 | if (type == GestureType::Pan) { | ||
| 140 | UpdatePanEvent(next_state, type); | ||
| 141 | return; | ||
| 142 | } | ||
| 143 | |||
| 144 | // Promote to press type | ||
| 145 | if (last_gesture_state.type == GestureType::Touch) { | ||
| 146 | type = GestureType::Press; | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | void GestureHandler::EndGesture(GestureState& next_state, GestureType& type, | ||
| 151 | GestureAttribute& attributes) { | ||
| 152 | if (last_gesture.active_points != 0) { | ||
| 153 | switch (last_gesture_state.type) { | ||
| 154 | case GestureType::Touch: | ||
| 155 | if (enable_press_and_tap) { | ||
| 156 | SetTapEvent(type, attributes); | ||
| 157 | return; | ||
| 158 | } | ||
| 159 | type = GestureType::Cancel; | ||
| 160 | force_update = true; | ||
| 161 | break; | ||
| 162 | case GestureType::Press: | ||
| 163 | case GestureType::Tap: | ||
| 164 | case GestureType::Swipe: | ||
| 165 | case GestureType::Pinch: | ||
| 166 | case GestureType::Rotate: | ||
| 167 | type = GestureType::Complete; | ||
| 168 | force_update = true; | ||
| 169 | break; | ||
| 170 | case GestureType::Pan: | ||
| 171 | EndPanEvent(next_state, type); | ||
| 172 | break; | ||
| 173 | default: | ||
| 174 | break; | ||
| 175 | } | ||
| 176 | return; | ||
| 177 | } | ||
| 178 | if (last_gesture_state.type == GestureType::Complete || | ||
| 179 | last_gesture_state.type == GestureType::Cancel) { | ||
| 180 | gesture.detection_count++; | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | void GestureHandler::SetTapEvent(GestureType& type, GestureAttribute& attributes) { | ||
| 185 | type = GestureType::Tap; | ||
| 186 | gesture = last_gesture; | ||
| 187 | force_update = true; | ||
| 188 | f32 tap_time_difference = | ||
| 189 | static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000); | ||
| 190 | last_tap_timestamp = last_update_timestamp; | ||
| 191 | if (tap_time_difference < DoubleTapDelay) { | ||
| 192 | attributes.is_double_tap.Assign(1); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | void GestureHandler::UpdatePanEvent(GestureState& next_state, GestureType& type) { | ||
| 197 | next_state.delta = gesture.mid_point - last_gesture_state.pos; | ||
| 198 | next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference; | ||
| 199 | next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference; | ||
| 200 | last_pan_time_difference = time_difference; | ||
| 201 | |||
| 202 | // Promote to pinch type | ||
| 203 | if (std::abs(gesture.average_distance - last_gesture.average_distance) > PinchThreshold) { | ||
| 204 | type = GestureType::Pinch; | ||
| 205 | next_state.scale = gesture.average_distance / last_gesture.average_distance; | ||
| 206 | } | ||
| 207 | |||
| 208 | const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture.angle) / | ||
| 209 | (1 + (gesture.angle * last_gesture.angle))); | ||
| 210 | // Promote to rotate type | ||
| 211 | if (std::abs(angle_between_two_lines) > AngleThreshold) { | ||
| 212 | type = GestureType::Rotate; | ||
| 213 | next_state.scale = 0; | ||
| 214 | next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI; | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | void GestureHandler::EndPanEvent(GestureState& next_state, GestureType& type) { | ||
| 219 | next_state.vel_x = | ||
| 220 | static_cast<f32>(last_gesture_state.delta.x) / (last_pan_time_difference + time_difference); | ||
| 221 | next_state.vel_y = | ||
| 222 | static_cast<f32>(last_gesture_state.delta.y) / (last_pan_time_difference + time_difference); | ||
| 223 | const f32 curr_vel = | ||
| 224 | std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y)); | ||
| 225 | |||
| 226 | // Set swipe event with parameters | ||
| 227 | if (curr_vel > SwipeThreshold) { | ||
| 228 | SetSwipeEvent(next_state, type); | ||
| 229 | return; | ||
| 230 | } | ||
| 231 | |||
| 232 | // End panning without swipe | ||
| 233 | type = GestureType::Complete; | ||
| 234 | next_state.vel_x = 0; | ||
| 235 | next_state.vel_y = 0; | ||
| 236 | force_update = true; | ||
| 237 | } | ||
| 238 | |||
| 239 | void GestureHandler::SetSwipeEvent(GestureState& next_state, GestureType& type) { | ||
| 240 | type = GestureType::Swipe; | ||
| 241 | gesture = last_gesture; | ||
| 242 | force_update = true; | ||
| 243 | next_state.delta = last_gesture_state.delta; | ||
| 244 | |||
| 245 | if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) { | ||
| 246 | if (next_state.delta.x > 0) { | ||
| 247 | next_state.direction = GestureDirection::Right; | ||
| 248 | return; | ||
| 249 | } | ||
| 250 | next_state.direction = GestureDirection::Left; | ||
| 251 | return; | ||
| 252 | } | ||
| 253 | if (next_state.delta.y > 0) { | ||
| 254 | next_state.direction = GestureDirection::Down; | ||
| 255 | return; | ||
| 256 | } | ||
| 257 | next_state.direction = GestureDirection::Up; | ||
| 258 | } | ||
| 259 | |||
| 260 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <span> | ||
| 7 | |||
| 8 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 9 | |||
| 10 | namespace Service::HID { | ||
| 11 | |||
| 12 | class GestureHandler { | ||
| 13 | public: | ||
| 14 | GestureHandler(); | ||
| 15 | ~GestureHandler(); | ||
| 16 | |||
| 17 | void SetTouchState(std::span<TouchState> touch_state, u32 count, s64 timestamp); | ||
| 18 | |||
| 19 | bool NeedsUpdate(); | ||
| 20 | void UpdateGestureState(GestureState& next_state, s64 timestamp); | ||
| 21 | |||
| 22 | private: | ||
| 23 | // Initializes new gesture | ||
| 24 | void NewGesture(GestureType& type, GestureAttribute& attributes); | ||
| 25 | |||
| 26 | // Updates existing gesture state | ||
| 27 | void UpdateExistingGesture(GestureState& next_state, GestureType& type); | ||
| 28 | |||
| 29 | // Terminates exiting gesture | ||
| 30 | void EndGesture(GestureState& next_state, GestureType& type, GestureAttribute& attributes); | ||
| 31 | |||
| 32 | // Set current event to a tap event | ||
| 33 | void SetTapEvent(GestureType& type, GestureAttribute& attributes); | ||
| 34 | |||
| 35 | // Calculates and set the extra parameters related to a pan event | ||
| 36 | void UpdatePanEvent(GestureState& next_state, GestureType& type); | ||
| 37 | |||
| 38 | // Terminates the pan event | ||
| 39 | void EndPanEvent(GestureState& next_state, GestureType& type); | ||
| 40 | |||
| 41 | // Set current event to a swipe event | ||
| 42 | void SetSwipeEvent(GestureState& next_state, GestureType& type); | ||
| 43 | |||
| 44 | GestureProperties gesture{}; | ||
| 45 | GestureProperties last_gesture{}; | ||
| 46 | GestureState last_gesture_state{}; | ||
| 47 | s64 last_update_timestamp{}; | ||
| 48 | s64 last_tap_timestamp{}; | ||
| 49 | f32 last_pan_time_difference{}; | ||
| 50 | f32 time_difference{}; | ||
| 51 | bool force_update{true}; | ||
| 52 | bool enable_press_and_tap{false}; | ||
| 53 | }; | ||
| 54 | |||
| 55 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include "common/bit_field.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "common/point.h" | ||
| 10 | |||
| 11 | namespace Service::HID { | ||
| 12 | static constexpr size_t MAX_FINGERS = 16; | ||
| 13 | static constexpr size_t MAX_POINTS = 4; | ||
| 14 | |||
| 15 | // This is nn::hid::GestureType | ||
| 16 | enum class GestureType : u32 { | ||
| 17 | Idle, // Nothing touching the screen | ||
| 18 | Complete, // Set at the end of a touch event | ||
| 19 | Cancel, // Set when the number of fingers change | ||
| 20 | Touch, // A finger just touched the screen | ||
| 21 | Press, // Set if last type is touch and the finger hasn't moved | ||
| 22 | Tap, // Fast press then release | ||
| 23 | Pan, // All points moving together across the screen | ||
| 24 | Swipe, // Fast press movement and release of a single point | ||
| 25 | Pinch, // All points moving away/closer to the midpoint | ||
| 26 | Rotate, // All points rotating from the midpoint | ||
| 27 | }; | ||
| 28 | |||
| 29 | // This is nn::hid::GestureDirection | ||
| 30 | enum class GestureDirection : u32 { | ||
| 31 | None, | ||
| 32 | Left, | ||
| 33 | Up, | ||
| 34 | Right, | ||
| 35 | Down, | ||
| 36 | }; | ||
| 37 | |||
| 38 | // This is nn::hid::GestureAttribute | ||
| 39 | struct GestureAttribute { | ||
| 40 | union { | ||
| 41 | u32 raw{}; | ||
| 42 | |||
| 43 | BitField<4, 1, u32> is_new_touch; | ||
| 44 | BitField<8, 1, u32> is_double_tap; | ||
| 45 | }; | ||
| 46 | }; | ||
| 47 | static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size"); | ||
| 48 | |||
| 49 | // This is nn::hid::GestureState | ||
| 50 | struct GestureState { | ||
| 51 | s64 sampling_number{}; | ||
| 52 | s64 detection_count{}; | ||
| 53 | GestureType type{GestureType::Idle}; | ||
| 54 | GestureDirection direction{GestureDirection::None}; | ||
| 55 | Common::Point<s32> pos{}; | ||
| 56 | Common::Point<s32> delta{}; | ||
| 57 | f32 vel_x{}; | ||
| 58 | f32 vel_y{}; | ||
| 59 | GestureAttribute attributes{}; | ||
| 60 | f32 scale{}; | ||
| 61 | f32 rotation_angle{}; | ||
| 62 | s32 point_count{}; | ||
| 63 | std::array<Common::Point<s32>, 4> points{}; | ||
| 64 | }; | ||
| 65 | static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); | ||
| 66 | |||
| 67 | struct GestureProperties { | ||
| 68 | std::array<Common::Point<s32>, MAX_POINTS> points{}; | ||
| 69 | std::size_t active_points{}; | ||
| 70 | Common::Point<s32> mid_point{}; | ||
| 71 | s64 detection_count{}; | ||
| 72 | u64 delta_time{}; | ||
| 73 | f32 average_distance{}; | ||
| 74 | f32 angle{}; | ||
| 75 | }; | ||
| 76 | |||
| 77 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | #include <algorithm> | 4 | #include "hid_core/hid_types.h" |
| 5 | #include "common/common_types.h" | ||
| 6 | #include "common/settings.h" | ||
| 7 | #include "core/core_timing.h" | ||
| 8 | #include "core/frontend/emu_window.h" | ||
| 9 | #include "hid_core/frontend/emulated_console.h" | ||
| 10 | #include "hid_core/hid_core.h" | ||
| 11 | #include "hid_core/resources/applet_resource.h" | ||
| 12 | #include "hid_core/resources/shared_memory_format.h" | ||
| 13 | #include "hid_core/resources/touch_screen/touch_screen.h" | 5 | #include "hid_core/resources/touch_screen/touch_screen.h" |
| 6 | #include "hid_core/resources/touch_screen/touch_screen_resource.h" | ||
| 14 | 7 | ||
| 15 | namespace Service::HID { | 8 | namespace Service::HID { |
| 16 | 9 | ||
| 17 | TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_) | 10 | TouchScreen::TouchScreen(std::shared_ptr<TouchResource> resource) : touch_resource{resource} {} |
| 18 | : ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width), | ||
| 19 | touchscreen_height(Layout::ScreenUndocked::Height) { | ||
| 20 | console = hid_core.GetEmulatedConsole(); | ||
| 21 | } | ||
| 22 | 11 | ||
| 23 | TouchScreen::~TouchScreen() = default; | 12 | TouchScreen::~TouchScreen() = default; |
| 24 | 13 | ||
| 25 | void TouchScreen::OnInit() {} | 14 | Result TouchScreen::Activate() { |
| 15 | std::scoped_lock lock{mutex}; | ||
| 26 | 16 | ||
| 27 | void TouchScreen::OnRelease() {} | 17 | // TODO: Result result = CreateThread(); |
| 28 | 18 | Result result = ResultSuccess; | |
| 29 | void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 19 | if (result.IsError()) { |
| 30 | const u64 aruid = applet_resource->GetActiveAruid(); | 20 | return result; |
| 31 | auto* data = applet_resource->GetAruidData(aruid); | 21 | } |
| 32 | 22 | ||
| 33 | if (data == nullptr || !data->flag.is_assigned) { | 23 | result = touch_resource->ActivateTouch(); |
| 34 | return; | 24 | if (result.IsError()) { |
| 25 | // TODO: StopThread(); | ||
| 35 | } | 26 | } |
| 36 | 27 | ||
| 37 | TouchScreenSharedMemoryFormat& shared_memory = data->shared_memory_format->touch_screen; | 28 | return result; |
| 38 | shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); | 29 | } |
| 39 | 30 | ||
| 40 | if (!IsControllerActivated()) { | 31 | Result TouchScreen::Activate(u64 aruid) { |
| 41 | shared_memory.touch_screen_lifo.buffer_count = 0; | 32 | std::scoped_lock lock{mutex}; |
| 42 | shared_memory.touch_screen_lifo.buffer_tail = 0; | 33 | return touch_resource->ActivateTouch(aruid); |
| 43 | return; | 34 | } |
| 44 | } | ||
| 45 | 35 | ||
| 46 | const auto touch_status = console->GetTouch(); | 36 | Result TouchScreen::Deactivate() { |
| 47 | for (std::size_t id = 0; id < MAX_FINGERS; id++) { | 37 | std::scoped_lock lock{mutex}; |
| 48 | const auto& current_touch = touch_status[id]; | 38 | const auto result = touch_resource->DeactivateTouch(); |
| 49 | auto& finger = fingers[id]; | ||
| 50 | finger.id = current_touch.id; | ||
| 51 | |||
| 52 | if (finger.attribute.start_touch) { | ||
| 53 | finger.attribute.raw = 0; | ||
| 54 | continue; | ||
| 55 | } | ||
| 56 | |||
| 57 | if (finger.attribute.end_touch) { | ||
| 58 | finger.attribute.raw = 0; | ||
| 59 | finger.pressed = false; | ||
| 60 | continue; | ||
| 61 | } | ||
| 62 | |||
| 63 | if (!finger.pressed && current_touch.pressed) { | ||
| 64 | // Ignore all touch fingers if disabled | ||
| 65 | if (!Settings::values.touchscreen.enabled) { | ||
| 66 | continue; | ||
| 67 | } | ||
| 68 | |||
| 69 | finger.attribute.start_touch.Assign(1); | ||
| 70 | finger.pressed = true; | ||
| 71 | finger.position = current_touch.position; | ||
| 72 | continue; | ||
| 73 | } | ||
| 74 | |||
| 75 | if (finger.pressed && !current_touch.pressed) { | ||
| 76 | finger.attribute.raw = 0; | ||
| 77 | finger.attribute.end_touch.Assign(1); | ||
| 78 | continue; | ||
| 79 | } | ||
| 80 | |||
| 81 | // Only update position if touch is not on a special frame | ||
| 82 | finger.position = current_touch.position; | ||
| 83 | } | ||
| 84 | 39 | ||
| 85 | std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers; | 40 | if (result.IsError()) { |
| 86 | const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), | 41 | return result; |
| 87 | [](const auto& finger) { return finger.pressed; }); | ||
| 88 | const auto active_fingers_count = | ||
| 89 | static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); | ||
| 90 | |||
| 91 | const u64 timestamp = static_cast<u64>(core_timing.GetGlobalTimeNs().count()); | ||
| 92 | const auto& last_entry = shared_memory.touch_screen_lifo.ReadCurrentEntry().state; | ||
| 93 | |||
| 94 | next_state.sampling_number = last_entry.sampling_number + 1; | ||
| 95 | next_state.entry_count = static_cast<s32>(active_fingers_count); | ||
| 96 | |||
| 97 | for (std::size_t id = 0; id < MAX_FINGERS; ++id) { | ||
| 98 | auto& touch_entry = next_state.states[id]; | ||
| 99 | if (id < active_fingers_count) { | ||
| 100 | const auto& [active_x, active_y] = active_fingers[id].position; | ||
| 101 | touch_entry.position = { | ||
| 102 | .x = static_cast<u16>(active_x * static_cast<float>(touchscreen_width)), | ||
| 103 | .y = static_cast<u16>(active_y * static_cast<float>(touchscreen_height)), | ||
| 104 | }; | ||
| 105 | touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; | ||
| 106 | touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; | ||
| 107 | touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; | ||
| 108 | touch_entry.delta_time = timestamp - active_fingers[id].last_touch; | ||
| 109 | fingers[active_fingers[id].id].last_touch = timestamp; | ||
| 110 | touch_entry.finger = active_fingers[id].id; | ||
| 111 | touch_entry.attribute.raw = active_fingers[id].attribute.raw; | ||
| 112 | } else { | ||
| 113 | // Clear touch entry | ||
| 114 | touch_entry.attribute.raw = 0; | ||
| 115 | touch_entry.position = {}; | ||
| 116 | touch_entry.diameter_x = 0; | ||
| 117 | touch_entry.diameter_y = 0; | ||
| 118 | touch_entry.rotation_angle = 0; | ||
| 119 | touch_entry.delta_time = 0; | ||
| 120 | touch_entry.finger = 0; | ||
| 121 | } | ||
| 122 | } | 42 | } |
| 123 | 43 | ||
| 124 | shared_memory.touch_screen_lifo.WriteNextEntry(next_state); | 44 | // TODO: return StopThread(); |
| 45 | return ResultSuccess; | ||
| 46 | } | ||
| 47 | |||
| 48 | Result TouchScreen::IsActive(bool& out_is_active) const { | ||
| 49 | out_is_active = touch_resource->IsTouchActive(); | ||
| 50 | return ResultSuccess; | ||
| 51 | } | ||
| 52 | |||
| 53 | Result TouchScreen::SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state) { | ||
| 54 | std::scoped_lock lock{mutex}; | ||
| 55 | return touch_resource->SetTouchScreenAutoPilotState(auto_pilot_state); | ||
| 56 | } | ||
| 57 | |||
| 58 | Result TouchScreen::UnsetTouchScreenAutoPilotState() { | ||
| 59 | std::scoped_lock lock{mutex}; | ||
| 60 | return touch_resource->UnsetTouchScreenAutoPilotState(); | ||
| 61 | } | ||
| 62 | |||
| 63 | Result TouchScreen::RequestNextTouchInput() { | ||
| 64 | std::scoped_lock lock{mutex}; | ||
| 65 | return touch_resource->RequestNextTouchInput(); | ||
| 66 | } | ||
| 67 | |||
| 68 | Result TouchScreen::RequestNextDummyInput() { | ||
| 69 | std::scoped_lock lock{mutex}; | ||
| 70 | return touch_resource->RequestNextDummyInput(); | ||
| 71 | } | ||
| 72 | |||
| 73 | Result TouchScreen::ProcessTouchScreenAutoTune() { | ||
| 74 | std::scoped_lock lock{mutex}; | ||
| 75 | return touch_resource->ProcessTouchScreenAutoTune(); | ||
| 76 | } | ||
| 77 | |||
| 78 | Result TouchScreen::SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, | ||
| 79 | f32 point2_y) { | ||
| 80 | std::scoped_lock lock{mutex}; | ||
| 81 | touch_resource->SetTouchScreenMagnification(point1_x, point1_y, point2_x, point2_y); | ||
| 82 | return ResultSuccess; | ||
| 83 | } | ||
| 84 | |||
| 85 | Result TouchScreen::SetTouchScreenResolution(u32 width, u32 height, u64 aruid) { | ||
| 86 | std::scoped_lock lock{mutex}; | ||
| 87 | return touch_resource->SetTouchScreenResolution(width, height, aruid); | ||
| 88 | } | ||
| 89 | |||
| 90 | Result TouchScreen::SetTouchScreenConfiguration( | ||
| 91 | const Core::HID::TouchScreenConfigurationForNx& mode, u64 aruid) { | ||
| 92 | std::scoped_lock lock{mutex}; | ||
| 93 | return touch_resource->SetTouchScreenConfiguration(mode, aruid); | ||
| 94 | } | ||
| 95 | |||
| 96 | Result TouchScreen::GetTouchScreenConfiguration(Core::HID::TouchScreenConfigurationForNx& out_mode, | ||
| 97 | u64 aruid) const { | ||
| 98 | std::scoped_lock lock{mutex}; | ||
| 99 | return touch_resource->GetTouchScreenConfiguration(out_mode, aruid); | ||
| 100 | } | ||
| 101 | |||
| 102 | Result TouchScreen::SetTouchScreenDefaultConfiguration( | ||
| 103 | const Core::HID::TouchScreenConfigurationForNx& mode) { | ||
| 104 | std::scoped_lock lock{mutex}; | ||
| 105 | return touch_resource->SetTouchScreenDefaultConfiguration(mode); | ||
| 106 | } | ||
| 107 | |||
| 108 | Result TouchScreen::GetTouchScreenDefaultConfiguration( | ||
| 109 | Core::HID::TouchScreenConfigurationForNx& out_mode) const { | ||
| 110 | std::scoped_lock lock{mutex}; | ||
| 111 | return touch_resource->GetTouchScreenDefaultConfiguration(out_mode); | ||
| 125 | } | 112 | } |
| 126 | 113 | ||
| 127 | void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) { | 114 | void TouchScreen::OnTouchUpdate(u64 timestamp) { |
| 128 | touchscreen_width = width; | 115 | std::scoped_lock lock{mutex}; |
| 129 | touchscreen_height = height; | 116 | touch_resource->OnTouchUpdate(timestamp); |
| 130 | } | 117 | } |
| 131 | 118 | ||
| 132 | } // namespace Service::HID | 119 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <mutex> |
| 7 | 7 | ||
| 8 | #include "hid_core/hid_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "hid_core/resources/controller_base.h" | 9 | #include "core/hle/result.h" |
| 10 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 11 | 10 | ||
| 12 | namespace Core::HID { | 11 | namespace Core::HID { |
| 13 | class EmulatedConsole; | 12 | struct TouchScreenConfigurationForNx; |
| 14 | } // namespace Core::HID | 13 | } |
| 14 | |||
| 15 | namespace Core::Timing { | ||
| 16 | struct EventType; | ||
| 17 | } | ||
| 15 | 18 | ||
| 16 | namespace Service::HID { | 19 | namespace Service::HID { |
| 17 | struct TouchScreenSharedMemoryFormat; | 20 | class TouchResource; |
| 21 | struct AutoPilotState; | ||
| 18 | 22 | ||
| 19 | class TouchScreen final : public ControllerBase { | 23 | /// Handles touch request from HID interfaces |
| 24 | class TouchScreen { | ||
| 20 | public: | 25 | public: |
| 21 | explicit TouchScreen(Core::HID::HIDCore& hid_core_); | 26 | TouchScreen(std::shared_ptr<TouchResource> resource); |
| 22 | ~TouchScreen() override; | 27 | ~TouchScreen(); |
| 23 | 28 | ||
| 24 | // Called when the controller is initialized | 29 | Result Activate(); |
| 25 | void OnInit() override; | 30 | Result Activate(u64 aruid); |
| 26 | 31 | ||
| 27 | // When the controller is released | 32 | Result Deactivate(); |
| 28 | void OnRelease() override; | ||
| 29 | 33 | ||
| 30 | // When the controller is requesting an update for the shared memory | 34 | Result IsActive(bool& out_is_active) const; |
| 31 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 32 | 35 | ||
| 33 | void SetTouchscreenDimensions(u32 width, u32 height); | 36 | Result SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state); |
| 37 | Result UnsetTouchScreenAutoPilotState(); | ||
| 34 | 38 | ||
| 35 | private: | 39 | Result RequestNextTouchInput(); |
| 36 | TouchScreenState next_state{}; | 40 | Result RequestNextDummyInput(); |
| 37 | Core::HID::EmulatedConsole* console = nullptr; | 41 | |
| 42 | Result ProcessTouchScreenAutoTune(); | ||
| 43 | |||
| 44 | Result SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, f32 point2_y); | ||
| 45 | Result SetTouchScreenResolution(u32 width, u32 height, u64 aruid); | ||
| 46 | |||
| 47 | Result SetTouchScreenConfiguration(const Core::HID::TouchScreenConfigurationForNx& mode, | ||
| 48 | u64 aruid); | ||
| 49 | Result GetTouchScreenConfiguration(Core::HID::TouchScreenConfigurationForNx& out_mode, | ||
| 50 | u64 aruid) const; | ||
| 38 | 51 | ||
| 39 | std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{}; | 52 | Result SetTouchScreenDefaultConfiguration(const Core::HID::TouchScreenConfigurationForNx& mode); |
| 40 | u32 touchscreen_width; | 53 | Result GetTouchScreenDefaultConfiguration( |
| 41 | u32 touchscreen_height; | 54 | Core::HID::TouchScreenConfigurationForNx& out_mode) const; |
| 55 | |||
| 56 | void OnTouchUpdate(u64 timestamp); | ||
| 57 | |||
| 58 | private: | ||
| 59 | mutable std::mutex mutex; | ||
| 60 | std::shared_ptr<TouchResource> touch_resource; | ||
| 61 | std::shared_ptr<Core::Timing::EventType> touch_update_event; | ||
| 42 | }; | 62 | }; |
| 63 | |||
| 43 | } // namespace Service::HID | 64 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include "common/settings.h" | ||
| 6 | #include "core/frontend/emu_window.h" | ||
| 7 | #include "hid_core/hid_core.h" | ||
| 8 | #include "hid_core/resources/touch_screen/touch_screen_driver.h" | ||
| 9 | |||
| 10 | namespace Service::HID { | ||
| 11 | |||
| 12 | TouchDriver::TouchDriver(Core::HID::HIDCore& hid_core) { | ||
| 13 | console = hid_core.GetEmulatedConsole(); | ||
| 14 | } | ||
| 15 | |||
| 16 | TouchDriver::~TouchDriver() = default; | ||
| 17 | |||
| 18 | Result TouchDriver::StartTouchSensor() { | ||
| 19 | is_running = true; | ||
| 20 | return ResultSuccess; | ||
| 21 | } | ||
| 22 | |||
| 23 | Result TouchDriver::StopTouchSensor() { | ||
| 24 | is_running = false; | ||
| 25 | return ResultSuccess; | ||
| 26 | } | ||
| 27 | |||
| 28 | bool TouchDriver::IsRunning() const { | ||
| 29 | return is_running; | ||
| 30 | } | ||
| 31 | |||
| 32 | void TouchDriver::ProcessTouchScreenAutoTune() const { | ||
| 33 | // TODO | ||
| 34 | } | ||
| 35 | |||
| 36 | Result TouchDriver::WaitForDummyInput() { | ||
| 37 | touch_status = {}; | ||
| 38 | return ResultSuccess; | ||
| 39 | } | ||
| 40 | |||
| 41 | Result TouchDriver::WaitForInput() { | ||
| 42 | touch_status = {}; | ||
| 43 | const auto touch_input = console->GetTouch(); | ||
| 44 | for (std::size_t id = 0; id < touch_status.states.size(); id++) { | ||
| 45 | const auto& current_touch = touch_input[id]; | ||
| 46 | auto& finger = fingers[id]; | ||
| 47 | finger.id = current_touch.id; | ||
| 48 | |||
| 49 | if (finger.attribute.start_touch) { | ||
| 50 | finger.attribute.raw = 0; | ||
| 51 | continue; | ||
| 52 | } | ||
| 53 | |||
| 54 | if (finger.attribute.end_touch) { | ||
| 55 | finger.attribute.raw = 0; | ||
| 56 | finger.pressed = false; | ||
| 57 | continue; | ||
| 58 | } | ||
| 59 | |||
| 60 | if (!finger.pressed && current_touch.pressed) { | ||
| 61 | finger.attribute.start_touch.Assign(1); | ||
| 62 | finger.pressed = true; | ||
| 63 | finger.position = current_touch.position; | ||
| 64 | continue; | ||
| 65 | } | ||
| 66 | |||
| 67 | if (finger.pressed && !current_touch.pressed) { | ||
| 68 | finger.attribute.raw = 0; | ||
| 69 | finger.attribute.end_touch.Assign(1); | ||
| 70 | continue; | ||
| 71 | } | ||
| 72 | |||
| 73 | // Only update position if touch is not on a special frame | ||
| 74 | finger.position = current_touch.position; | ||
| 75 | } | ||
| 76 | |||
| 77 | std::array<Core::HID::TouchFinger, MaxFingers> active_fingers; | ||
| 78 | const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), | ||
| 79 | [](const auto& finger) { return finger.pressed; }); | ||
| 80 | const auto active_fingers_count = | ||
| 81 | static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); | ||
| 82 | |||
| 83 | touch_status.entry_count = static_cast<s32>(active_fingers_count); | ||
| 84 | for (std::size_t id = 0; id < MaxFingers; ++id) { | ||
| 85 | auto& touch_entry = touch_status.states[id]; | ||
| 86 | if (id < active_fingers_count) { | ||
| 87 | const auto& [active_x, active_y] = active_fingers[id].position; | ||
| 88 | touch_entry.position = { | ||
| 89 | .x = static_cast<u16>(active_x * TouchSensorWidth), | ||
| 90 | .y = static_cast<u16>(active_y * TouchSensorHeight), | ||
| 91 | }; | ||
| 92 | touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; | ||
| 93 | touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; | ||
| 94 | touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; | ||
| 95 | touch_entry.finger = active_fingers[id].id; | ||
| 96 | touch_entry.attribute.raw = active_fingers[id].attribute.raw; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | return ResultSuccess; | ||
| 100 | } | ||
| 101 | |||
| 102 | void TouchDriver::GetNextTouchState(TouchScreenState& out_state) const { | ||
| 103 | out_state = touch_status; | ||
| 104 | } | ||
| 105 | |||
| 106 | void TouchDriver::SetTouchMode(Core::HID::TouchScreenModeForNx mode) { | ||
| 107 | touch_mode = mode; | ||
| 108 | } | ||
| 109 | |||
| 110 | Core::HID::TouchScreenModeForNx TouchDriver::GetTouchMode() const { | ||
| 111 | return touch_mode; | ||
| 112 | } | ||
| 113 | |||
| 114 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_types.h" | ||
| 7 | #include "core/hle/result.h" | ||
| 8 | #include "hid_core/frontend/emulated_console.h" | ||
| 9 | #include "hid_core/hid_types.h" | ||
| 10 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 11 | |||
| 12 | namespace Core::HID { | ||
| 13 | class HIDCore; | ||
| 14 | } // namespace Core::HID | ||
| 15 | |||
| 16 | namespace Service::HID { | ||
| 17 | |||
| 18 | /// This handles all request to Ftm3bd56(TouchPanel) hardware | ||
| 19 | class TouchDriver { | ||
| 20 | public: | ||
| 21 | explicit TouchDriver(Core::HID::HIDCore& hid_core); | ||
| 22 | ~TouchDriver(); | ||
| 23 | |||
| 24 | Result StartTouchSensor(); | ||
| 25 | Result StopTouchSensor(); | ||
| 26 | bool IsRunning() const; | ||
| 27 | |||
| 28 | void ProcessTouchScreenAutoTune() const; | ||
| 29 | |||
| 30 | Result WaitForDummyInput(); | ||
| 31 | Result WaitForInput(); | ||
| 32 | |||
| 33 | void GetNextTouchState(TouchScreenState& out_state) const; | ||
| 34 | |||
| 35 | void SetTouchMode(Core::HID::TouchScreenModeForNx mode); | ||
| 36 | Core::HID::TouchScreenModeForNx GetTouchMode() const; | ||
| 37 | |||
| 38 | private: | ||
| 39 | bool is_running{}; | ||
| 40 | TouchScreenState touch_status{}; | ||
| 41 | Core::HID::TouchFingerState fingers{}; | ||
| 42 | Core::HID::TouchScreenModeForNx touch_mode{}; | ||
| 43 | |||
| 44 | Core::HID::EmulatedConsole* console = nullptr; | ||
| 45 | }; | ||
| 46 | |||
| 47 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "common/logging/log.h" | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | #include "core/hle/kernel/k_event.h" | ||
| 7 | #include "core/hle/kernel/k_shared_memory.h" | ||
| 8 | #include "core/hle/service/set/system_settings_server.h" | ||
| 9 | #include "core/hle/service/sm/sm.h" | ||
| 10 | #include "hid_core/hid_result.h" | ||
| 11 | #include "hid_core/resources/applet_resource.h" | ||
| 12 | #include "hid_core/resources/shared_memory_format.h" | ||
| 13 | #include "hid_core/resources/touch_screen/touch_screen_driver.h" | ||
| 14 | #include "hid_core/resources/touch_screen/touch_screen_resource.h" | ||
| 15 | |||
| 16 | namespace Service::HID { | ||
| 17 | constexpr auto GestureUpdatePeriod = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz) | ||
| 18 | |||
| 19 | TouchResource::TouchResource(Core::System& system_) : system{system_} { | ||
| 20 | m_set_sys = system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys"); | ||
| 21 | } | ||
| 22 | |||
| 23 | TouchResource::~TouchResource() { | ||
| 24 | Finalize(); | ||
| 25 | }; | ||
| 26 | |||
| 27 | Result TouchResource::ActivateTouch() { | ||
| 28 | if (global_ref_counter == std::numeric_limits<s32>::max() - 1 || | ||
| 29 | touch_ref_counter == std::numeric_limits<s32>::max() - 1) { | ||
| 30 | return ResultTouchOverflow; | ||
| 31 | } | ||
| 32 | |||
| 33 | if (global_ref_counter == 0) { | ||
| 34 | std::scoped_lock lock{*shared_mutex}; | ||
| 35 | |||
| 36 | const auto result = touch_driver->StartTouchSensor(); | ||
| 37 | if (result.IsError()) { | ||
| 38 | return result; | ||
| 39 | } | ||
| 40 | |||
| 41 | is_initalized = true; | ||
| 42 | system.CoreTiming().ScheduleLoopingEvent(GestureUpdatePeriod, GestureUpdatePeriod, | ||
| 43 | timer_event); | ||
| 44 | current_touch_state = {}; | ||
| 45 | ReadTouchInput(); | ||
| 46 | gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count, | ||
| 47 | 0); | ||
| 48 | } | ||
| 49 | |||
| 50 | Set::TouchScreenMode touch_mode{Set::TouchScreenMode::Standard}; | ||
| 51 | m_set_sys->GetTouchScreenMode(touch_mode); | ||
| 52 | default_touch_screen_mode = static_cast<Core::HID::TouchScreenModeForNx>(touch_mode); | ||
| 53 | |||
| 54 | global_ref_counter++; | ||
| 55 | touch_ref_counter++; | ||
| 56 | return ResultSuccess; | ||
| 57 | } | ||
| 58 | |||
| 59 | Result TouchResource::ActivateTouch(u64 aruid) { | ||
| 60 | std::scoped_lock lock{*shared_mutex}; | ||
| 61 | |||
| 62 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 63 | auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | ||
| 64 | TouchAruidData& touch_data = aruid_data[aruid_index]; | ||
| 65 | |||
| 66 | if (!applet_data->flag.is_assigned) { | ||
| 67 | touch_data = {}; | ||
| 68 | continue; | ||
| 69 | } | ||
| 70 | |||
| 71 | const u64 aruid_id = applet_data->aruid; | ||
| 72 | if (touch_data.aruid != aruid_id) { | ||
| 73 | touch_data = {}; | ||
| 74 | touch_data.aruid = aruid_id; | ||
| 75 | } | ||
| 76 | |||
| 77 | if (aruid != aruid_id) { | ||
| 78 | continue; | ||
| 79 | } | ||
| 80 | |||
| 81 | auto& touch_shared = applet_data->shared_memory_format->touch_screen; | ||
| 82 | |||
| 83 | if (touch_shared.touch_screen_lifo.buffer_count == 0) { | ||
| 84 | StorePreviousTouchState(previous_touch_state, touch_data.finger_map, | ||
| 85 | current_touch_state, | ||
| 86 | applet_data->flag.enable_touchscreen.Value() != 0); | ||
| 87 | touch_shared.touch_screen_lifo.WriteNextEntry(previous_touch_state); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | return ResultSuccess; | ||
| 91 | } | ||
| 92 | |||
| 93 | Result TouchResource::ActivateGesture() { | ||
| 94 | if (global_ref_counter == std::numeric_limits<s32>::max() - 1 || | ||
| 95 | gesture_ref_counter == std::numeric_limits<s32>::max() - 1) { | ||
| 96 | return ResultGestureOverflow; | ||
| 97 | } | ||
| 98 | |||
| 99 | // Initialize first instance | ||
| 100 | if (global_ref_counter == 0) { | ||
| 101 | const auto result = touch_driver->StartTouchSensor(); | ||
| 102 | if (result.IsError()) { | ||
| 103 | return result; | ||
| 104 | } | ||
| 105 | |||
| 106 | is_initalized = true; | ||
| 107 | system.CoreTiming().ScheduleLoopingEvent(GestureUpdatePeriod, GestureUpdatePeriod, | ||
| 108 | timer_event); | ||
| 109 | current_touch_state = {}; | ||
| 110 | ReadTouchInput(); | ||
| 111 | gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count, | ||
| 112 | 0); | ||
| 113 | } | ||
| 114 | |||
| 115 | global_ref_counter++; | ||
| 116 | gesture_ref_counter++; | ||
| 117 | return ResultSuccess; | ||
| 118 | } | ||
| 119 | |||
| 120 | Result TouchResource::ActivateGesture(u64 aruid, u32 basic_gesture_id) { | ||
| 121 | std::scoped_lock lock{*shared_mutex}; | ||
| 122 | |||
| 123 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 124 | auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | ||
| 125 | TouchAruidData& touch_data = aruid_data[aruid_index]; | ||
| 126 | |||
| 127 | if (!applet_data->flag.is_assigned) { | ||
| 128 | touch_data = {}; | ||
| 129 | continue; | ||
| 130 | } | ||
| 131 | |||
| 132 | const u64 aruid_id = applet_data->aruid; | ||
| 133 | if (touch_data.aruid != aruid_id) { | ||
| 134 | touch_data = {}; | ||
| 135 | touch_data.aruid = aruid_id; | ||
| 136 | } | ||
| 137 | |||
| 138 | if (aruid != aruid_id) { | ||
| 139 | continue; | ||
| 140 | } | ||
| 141 | |||
| 142 | auto& gesture_shared = applet_data->shared_memory_format->gesture; | ||
| 143 | if (touch_data.basic_gesture_id != basic_gesture_id) { | ||
| 144 | gesture_shared.gesture_lifo.buffer_count = 0; | ||
| 145 | } | ||
| 146 | |||
| 147 | if (gesture_shared.gesture_lifo.buffer_count == 0) { | ||
| 148 | touch_data.basic_gesture_id = basic_gesture_id; | ||
| 149 | |||
| 150 | gesture_shared.gesture_lifo.WriteNextEntry(gesture_state); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | return ResultSuccess; | ||
| 155 | } | ||
| 156 | |||
| 157 | Result TouchResource::DeactivateTouch() { | ||
| 158 | if (touch_ref_counter == 0 || global_ref_counter == 0) { | ||
| 159 | return ResultTouchNotInitialized; | ||
| 160 | } | ||
| 161 | |||
| 162 | global_ref_counter--; | ||
| 163 | touch_ref_counter--; | ||
| 164 | |||
| 165 | if (touch_ref_counter + global_ref_counter != 0) { | ||
| 166 | return ResultSuccess; | ||
| 167 | } | ||
| 168 | |||
| 169 | return Finalize(); | ||
| 170 | } | ||
| 171 | |||
| 172 | Result TouchResource::DeactivateGesture() { | ||
| 173 | if (gesture_ref_counter == 0 || global_ref_counter == 0) { | ||
| 174 | return ResultGestureNotInitialized; | ||
| 175 | } | ||
| 176 | |||
| 177 | global_ref_counter--; | ||
| 178 | gesture_ref_counter--; | ||
| 179 | |||
| 180 | if (touch_ref_counter + global_ref_counter != 0) { | ||
| 181 | return ResultSuccess; | ||
| 182 | } | ||
| 183 | |||
| 184 | return Finalize(); | ||
| 185 | } | ||
| 186 | |||
| 187 | bool TouchResource::IsTouchActive() const { | ||
| 188 | return touch_ref_counter != 0; | ||
| 189 | } | ||
| 190 | |||
| 191 | bool TouchResource::IsGestureActive() const { | ||
| 192 | return gesture_ref_counter != 0; | ||
| 193 | } | ||
| 194 | |||
| 195 | void TouchResource::SetTouchDriver(std::shared_ptr<TouchDriver> driver) { | ||
| 196 | touch_driver = driver; | ||
| 197 | } | ||
| 198 | |||
| 199 | void TouchResource::SetAppletResource(std::shared_ptr<AppletResource> shared, | ||
| 200 | std::recursive_mutex* mutex) { | ||
| 201 | applet_resource = shared; | ||
| 202 | shared_mutex = mutex; | ||
| 203 | } | ||
| 204 | |||
| 205 | void TouchResource::SetInputEvent(Kernel::KEvent* event, std::mutex* mutex) { | ||
| 206 | input_event = event; | ||
| 207 | input_mutex = mutex; | ||
| 208 | } | ||
| 209 | |||
| 210 | void TouchResource::SetHandheldConfig(std::shared_ptr<HandheldConfig> config) { | ||
| 211 | handheld_config = config; | ||
| 212 | } | ||
| 213 | |||
| 214 | void TouchResource::SetTimerEvent(std::shared_ptr<Core::Timing::EventType> event) { | ||
| 215 | timer_event = event; | ||
| 216 | } | ||
| 217 | |||
| 218 | Result TouchResource::SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state) { | ||
| 219 | if (global_ref_counter == 0) { | ||
| 220 | return ResultTouchNotInitialized; | ||
| 221 | } | ||
| 222 | |||
| 223 | if (!is_auto_pilot_initialized) { | ||
| 224 | is_auto_pilot_initialized = true; | ||
| 225 | auto_pilot = {}; | ||
| 226 | } | ||
| 227 | |||
| 228 | TouchScreenState state = { | ||
| 229 | .entry_count = static_cast<s32>(auto_pilot_state.count), | ||
| 230 | .states = auto_pilot_state.state, | ||
| 231 | }; | ||
| 232 | |||
| 233 | SanitizeInput(state); | ||
| 234 | |||
| 235 | auto_pilot.count = state.entry_count; | ||
| 236 | auto_pilot.state = state.states; | ||
| 237 | return ResultSuccess; | ||
| 238 | } | ||
| 239 | |||
| 240 | Result TouchResource::UnsetTouchScreenAutoPilotState() { | ||
| 241 | if (global_ref_counter == 0) { | ||
| 242 | return ResultTouchNotInitialized; | ||
| 243 | } | ||
| 244 | |||
| 245 | is_auto_pilot_initialized = false; | ||
| 246 | auto_pilot = {}; | ||
| 247 | return ResultSuccess; | ||
| 248 | } | ||
| 249 | |||
| 250 | Result TouchResource::RequestNextTouchInput() { | ||
| 251 | if (global_ref_counter == 0) { | ||
| 252 | return ResultTouchNotInitialized; | ||
| 253 | } | ||
| 254 | |||
| 255 | if (handheld_config->is_handheld_hid_enabled) { | ||
| 256 | const Result result = touch_driver->WaitForInput(); | ||
| 257 | if (result.IsError()) { | ||
| 258 | return result; | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | is_initalized = true; | ||
| 263 | return ResultSuccess; | ||
| 264 | } | ||
| 265 | |||
| 266 | Result TouchResource::RequestNextDummyInput() { | ||
| 267 | if (global_ref_counter == 0) { | ||
| 268 | return ResultTouchNotInitialized; | ||
| 269 | } | ||
| 270 | |||
| 271 | if (handheld_config->is_handheld_hid_enabled) { | ||
| 272 | const Result result = touch_driver->WaitForDummyInput(); | ||
| 273 | if (result.IsError()) { | ||
| 274 | return result; | ||
| 275 | } | ||
| 276 | } | ||
| 277 | |||
| 278 | is_initalized = false; | ||
| 279 | return ResultSuccess; | ||
| 280 | } | ||
| 281 | |||
| 282 | Result TouchResource::ProcessTouchScreenAutoTune() { | ||
| 283 | touch_driver->ProcessTouchScreenAutoTune(); | ||
| 284 | return ResultSuccess; | ||
| 285 | } | ||
| 286 | |||
| 287 | void TouchResource::SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, | ||
| 288 | f32 point2_y) { | ||
| 289 | offset = { | ||
| 290 | .x = point1_x, | ||
| 291 | .y = point1_y, | ||
| 292 | }; | ||
| 293 | magnification = { | ||
| 294 | .x = point2_x, | ||
| 295 | .y = point2_y, | ||
| 296 | }; | ||
| 297 | } | ||
| 298 | |||
| 299 | Result TouchResource::SetTouchScreenResolution(u32 width, u32 height, u64 aruid) { | ||
| 300 | std::scoped_lock lock{*shared_mutex}; | ||
| 301 | |||
| 302 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 303 | const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | ||
| 304 | TouchAruidData& data = aruid_data[aruid_index]; | ||
| 305 | |||
| 306 | if (!applet_data->flag.is_assigned) { | ||
| 307 | continue; | ||
| 308 | } | ||
| 309 | if (aruid != data.aruid) { | ||
| 310 | continue; | ||
| 311 | } | ||
| 312 | data.resolution_width = static_cast<u16>(width); | ||
| 313 | data.resolution_height = static_cast<u16>(height); | ||
| 314 | } | ||
| 315 | |||
| 316 | return ResultSuccess; | ||
| 317 | } | ||
| 318 | |||
| 319 | Result TouchResource::SetTouchScreenConfiguration( | ||
| 320 | const Core::HID::TouchScreenConfigurationForNx& touch_configuration, u64 aruid) { | ||
| 321 | std::scoped_lock lock{*shared_mutex}; | ||
| 322 | |||
| 323 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 324 | const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | ||
| 325 | TouchAruidData& data = aruid_data[aruid_index]; | ||
| 326 | |||
| 327 | if (!applet_data->flag.is_assigned) { | ||
| 328 | continue; | ||
| 329 | } | ||
| 330 | if (aruid != data.aruid) { | ||
| 331 | continue; | ||
| 332 | } | ||
| 333 | data.finger_map.touch_mode = touch_configuration.mode; | ||
| 334 | } | ||
| 335 | |||
| 336 | return ResultSuccess; | ||
| 337 | } | ||
| 338 | |||
| 339 | Result TouchResource::GetTouchScreenConfiguration( | ||
| 340 | Core::HID::TouchScreenConfigurationForNx& out_touch_configuration, u64 aruid) const { | ||
| 341 | std::scoped_lock lock{*shared_mutex}; | ||
| 342 | |||
| 343 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 344 | const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | ||
| 345 | const TouchAruidData& data = aruid_data[aruid_index]; | ||
| 346 | |||
| 347 | if (!applet_data->flag.is_assigned) { | ||
| 348 | continue; | ||
| 349 | } | ||
| 350 | if (aruid != data.aruid) { | ||
| 351 | continue; | ||
| 352 | } | ||
| 353 | out_touch_configuration.mode = data.finger_map.touch_mode; | ||
| 354 | } | ||
| 355 | |||
| 356 | return ResultSuccess; | ||
| 357 | } | ||
| 358 | |||
| 359 | Result TouchResource::SetTouchScreenDefaultConfiguration( | ||
| 360 | const Core::HID::TouchScreenConfigurationForNx& touch_configuration) { | ||
| 361 | default_touch_screen_mode = touch_configuration.mode; | ||
| 362 | return ResultSuccess; | ||
| 363 | } | ||
| 364 | |||
| 365 | Result TouchResource::GetTouchScreenDefaultConfiguration( | ||
| 366 | Core::HID::TouchScreenConfigurationForNx& out_touch_configuration) const { | ||
| 367 | out_touch_configuration.mode = default_touch_screen_mode; | ||
| 368 | return ResultSuccess; | ||
| 369 | } | ||
| 370 | |||
| 371 | Result TouchResource::Finalize() { | ||
| 372 | is_auto_pilot_initialized = false; | ||
| 373 | auto_pilot = {}; | ||
| 374 | system.CoreTiming().UnscheduleEvent(timer_event); | ||
| 375 | |||
| 376 | const auto result = touch_driver->StopTouchSensor(); | ||
| 377 | if (result.IsError()) { | ||
| 378 | return result; | ||
| 379 | } | ||
| 380 | |||
| 381 | is_initalized = false; | ||
| 382 | return ResultSuccess; | ||
| 383 | } | ||
| 384 | |||
| 385 | void TouchResource::StorePreviousTouchState(TouchScreenState& out_previous_touch, | ||
| 386 | TouchFingerMap& out_finger_map, | ||
| 387 | const TouchScreenState& current_touch, | ||
| 388 | bool is_touch_enabled) const { | ||
| 389 | s32 finger_count{}; | ||
| 390 | |||
| 391 | if (is_touch_enabled) { | ||
| 392 | finger_count = current_touch.entry_count; | ||
| 393 | if (finger_count < 1) { | ||
| 394 | out_finger_map.finger_count = 0; | ||
| 395 | out_finger_map.finger_ids = {}; | ||
| 396 | out_previous_touch.sampling_number = current_touch.sampling_number; | ||
| 397 | out_previous_touch.entry_count = 0; | ||
| 398 | out_previous_touch.states = {}; | ||
| 399 | return; | ||
| 400 | } | ||
| 401 | for (std::size_t i = 0; i < static_cast<u32>(finger_count); i++) { | ||
| 402 | out_finger_map.finger_ids[i] = current_touch.states[i].finger; | ||
| 403 | out_previous_touch.states[i] = current_touch.states[i]; | ||
| 404 | } | ||
| 405 | out_finger_map.finger_count = finger_count; | ||
| 406 | return; | ||
| 407 | } | ||
| 408 | |||
| 409 | if (!is_touch_enabled && out_finger_map.finger_count > 0 && current_touch.entry_count > 0) { | ||
| 410 | // TODO | ||
| 411 | } | ||
| 412 | |||
| 413 | // Zero out unused entries | ||
| 414 | for (std::size_t i = finger_count; i < MaxFingers; i++) { | ||
| 415 | out_finger_map.finger_ids[i] = 0; | ||
| 416 | out_previous_touch.states[i] = {}; | ||
| 417 | } | ||
| 418 | |||
| 419 | out_previous_touch.sampling_number = current_touch.sampling_number; | ||
| 420 | out_previous_touch.entry_count = finger_count; | ||
| 421 | } | ||
| 422 | |||
| 423 | void TouchResource::ReadTouchInput() { | ||
| 424 | previous_touch_state = current_touch_state; | ||
| 425 | |||
| 426 | if (!is_initalized || !handheld_config->is_handheld_hid_enabled || !touch_driver->IsRunning()) { | ||
| 427 | touch_driver->WaitForDummyInput(); | ||
| 428 | } else { | ||
| 429 | touch_driver->WaitForInput(); | ||
| 430 | } | ||
| 431 | |||
| 432 | touch_driver->GetNextTouchState(current_touch_state); | ||
| 433 | SanitizeInput(current_touch_state); | ||
| 434 | current_touch_state.sampling_number = sample_number; | ||
| 435 | sample_number++; | ||
| 436 | |||
| 437 | if (is_auto_pilot_initialized && current_touch_state.entry_count == 0) { | ||
| 438 | const std::size_t finger_count = static_cast<std::size_t>(auto_pilot.count); | ||
| 439 | current_touch_state.entry_count = static_cast<s32>(finger_count); | ||
| 440 | for (std::size_t i = 0; i < finger_count; i++) { | ||
| 441 | current_touch_state.states[i] = auto_pilot.state[i]; | ||
| 442 | } | ||
| 443 | |||
| 444 | std::size_t index = 0; | ||
| 445 | for (std::size_t i = 0; i < finger_count; i++) { | ||
| 446 | if (auto_pilot.state[i].attribute.end_touch) { | ||
| 447 | continue; | ||
| 448 | } | ||
| 449 | auto_pilot.state[i].attribute.raw = 0; | ||
| 450 | auto_pilot.state[index] = auto_pilot.state[i]; | ||
| 451 | index++; | ||
| 452 | } | ||
| 453 | |||
| 454 | auto_pilot.count = index; | ||
| 455 | for (std::size_t i = index; i < auto_pilot.state.size(); i++) { | ||
| 456 | auto_pilot.state[i] = {}; | ||
| 457 | } | ||
| 458 | } | ||
| 459 | |||
| 460 | for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); i++) { | ||
| 461 | auto& state = current_touch_state.states[i]; | ||
| 462 | state.position.x = static_cast<u32>((magnification.y * static_cast<f32>(state.position.x)) + | ||
| 463 | (offset.x * static_cast<f32>(TouchSensorWidth))); | ||
| 464 | state.position.y = static_cast<u32>((magnification.y * static_cast<f32>(state.position.y)) + | ||
| 465 | (offset.x * static_cast<f32>(TouchSensorHeight))); | ||
| 466 | state.diameter_x = static_cast<u32>(magnification.x * static_cast<f32>(state.diameter_x)); | ||
| 467 | state.diameter_y = static_cast<u32>(magnification.y * static_cast<f32>(state.diameter_y)); | ||
| 468 | } | ||
| 469 | |||
| 470 | std::size_t index = 0; | ||
| 471 | for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); i++) { | ||
| 472 | const auto& old_state = current_touch_state.states[i]; | ||
| 473 | auto& state = current_touch_state.states[index]; | ||
| 474 | if ((TouchSensorWidth <= old_state.position.x) || | ||
| 475 | (TouchSensorHeight <= old_state.position.y)) { | ||
| 476 | continue; | ||
| 477 | } | ||
| 478 | state = old_state; | ||
| 479 | index++; | ||
| 480 | } | ||
| 481 | current_touch_state.entry_count = static_cast<s32>(index); | ||
| 482 | |||
| 483 | SanitizeInput(current_touch_state); | ||
| 484 | |||
| 485 | std::scoped_lock lock{*input_mutex}; | ||
| 486 | if (current_touch_state.entry_count == previous_touch_state.entry_count) { | ||
| 487 | if (current_touch_state.entry_count < 1) { | ||
| 488 | return; | ||
| 489 | } | ||
| 490 | bool has_moved = false; | ||
| 491 | for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); | ||
| 492 | i++) { | ||
| 493 | s32 delta_x = std::abs(static_cast<s32>(current_touch_state.states[i].position.x) - | ||
| 494 | static_cast<s32>(previous_touch_state.states[i].position.x)); | ||
| 495 | s32 delta_y = std::abs(static_cast<s32>(current_touch_state.states[i].position.y) - | ||
| 496 | static_cast<s32>(previous_touch_state.states[i].position.y)); | ||
| 497 | if (delta_x > 1 || delta_y > 1) { | ||
| 498 | has_moved = true; | ||
| 499 | } | ||
| 500 | } | ||
| 501 | if (!has_moved) { | ||
| 502 | return; | ||
| 503 | } | ||
| 504 | } | ||
| 505 | |||
| 506 | input_event->Signal(); | ||
| 507 | } | ||
| 508 | |||
| 509 | void TouchResource::OnTouchUpdate(s64 timestamp) { | ||
| 510 | if (global_ref_counter == 0) { | ||
| 511 | return; | ||
| 512 | } | ||
| 513 | |||
| 514 | ReadTouchInput(); | ||
| 515 | gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count, | ||
| 516 | timestamp); | ||
| 517 | |||
| 518 | std::scoped_lock lock{*shared_mutex}; | ||
| 519 | |||
| 520 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 521 | const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | ||
| 522 | TouchAruidData& data = aruid_data[aruid_index]; | ||
| 523 | |||
| 524 | if (applet_data == nullptr || !applet_data->flag.is_assigned) { | ||
| 525 | data = {}; | ||
| 526 | continue; | ||
| 527 | } | ||
| 528 | |||
| 529 | if (data.aruid != applet_data->aruid) { | ||
| 530 | data = {}; | ||
| 531 | data.aruid = applet_data->aruid; | ||
| 532 | } | ||
| 533 | |||
| 534 | if (gesture_ref_counter != 0) { | ||
| 535 | if (!applet_data->flag.enable_touchscreen) { | ||
| 536 | gesture_state = {}; | ||
| 537 | } | ||
| 538 | if (gesture_handler.NeedsUpdate()) { | ||
| 539 | gesture_handler.UpdateGestureState(gesture_state, timestamp); | ||
| 540 | auto& gesture_shared = applet_data->shared_memory_format->gesture; | ||
| 541 | gesture_shared.gesture_lifo.WriteNextEntry(gesture_state); | ||
| 542 | } | ||
| 543 | } | ||
| 544 | |||
| 545 | if (touch_ref_counter != 0) { | ||
| 546 | auto touch_mode = data.finger_map.touch_mode; | ||
| 547 | if (touch_mode == Core::HID::TouchScreenModeForNx::UseSystemSetting) { | ||
| 548 | touch_mode = default_touch_screen_mode; | ||
| 549 | } | ||
| 550 | |||
| 551 | if (applet_resource->GetActiveAruid() == applet_data->aruid && | ||
| 552 | touch_mode != Core::HID::TouchScreenModeForNx::UseSystemSetting && is_initalized && | ||
| 553 | handheld_config->is_handheld_hid_enabled && touch_driver->IsRunning()) { | ||
| 554 | touch_driver->SetTouchMode(touch_mode); | ||
| 555 | } | ||
| 556 | |||
| 557 | auto& touch_shared = applet_data->shared_memory_format->touch_screen; | ||
| 558 | StorePreviousTouchState(previous_touch_state, data.finger_map, current_touch_state, | ||
| 559 | applet_data->flag.enable_touchscreen.As<bool>()); | ||
| 560 | touch_shared.touch_screen_lifo.WriteNextEntry(current_touch_state); | ||
| 561 | } | ||
| 562 | } | ||
| 563 | } | ||
| 564 | |||
| 565 | void TouchResource::SanitizeInput(TouchScreenState& state) const { | ||
| 566 | for (std::size_t i = 0; i < static_cast<std::size_t>(state.entry_count); i++) { | ||
| 567 | auto& entry = state.states[i]; | ||
| 568 | entry.position.x = | ||
| 569 | std::clamp(entry.position.x, TouchBorders, TouchSensorWidth - TouchBorders - 1); | ||
| 570 | entry.position.y = | ||
| 571 | std::clamp(entry.position.y, TouchBorders, TouchSensorHeight - TouchBorders - 1); | ||
| 572 | entry.diameter_x = std::clamp(entry.diameter_x, 0u, TouchSensorWidth - MaxTouchDiameter); | ||
| 573 | entry.diameter_y = std::clamp(entry.diameter_y, 0u, TouchSensorHeight - MaxTouchDiameter); | ||
| 574 | entry.rotation_angle = | ||
| 575 | std::clamp(entry.rotation_angle, -MaxRotationAngle, MaxRotationAngle); | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <mutex> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "common/point.h" | ||
| 11 | #include "core/hle/result.h" | ||
| 12 | #include "hid_core/hid_types.h" | ||
| 13 | #include "hid_core/resources/touch_screen/gesture_handler.h" | ||
| 14 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 15 | |||
| 16 | namespace Core { | ||
| 17 | class System; | ||
| 18 | } | ||
| 19 | |||
| 20 | namespace Core::Timing { | ||
| 21 | struct EventType; | ||
| 22 | } | ||
| 23 | |||
| 24 | namespace Kernel { | ||
| 25 | class KEvent; | ||
| 26 | } // namespace Kernel | ||
| 27 | |||
| 28 | namespace Service::Set { | ||
| 29 | class ISystemSettingsServer; | ||
| 30 | } | ||
| 31 | |||
| 32 | namespace Service::HID { | ||
| 33 | class AppletResource; | ||
| 34 | class TouchSharedMemoryManager; | ||
| 35 | class TouchDriver; | ||
| 36 | struct HandheldConfig; | ||
| 37 | |||
| 38 | class TouchResource { | ||
| 39 | public: | ||
| 40 | TouchResource(Core::System& system_); | ||
| 41 | ~TouchResource(); | ||
| 42 | |||
| 43 | Result ActivateTouch(); | ||
| 44 | Result ActivateTouch(u64 aruid); | ||
| 45 | |||
| 46 | Result ActivateGesture(); | ||
| 47 | Result ActivateGesture(u64 aruid, u32 basic_gesture_id); | ||
| 48 | |||
| 49 | Result DeactivateTouch(); | ||
| 50 | Result DeactivateGesture(); | ||
| 51 | |||
| 52 | bool IsTouchActive() const; | ||
| 53 | bool IsGestureActive() const; | ||
| 54 | |||
| 55 | void SetTouchDriver(std::shared_ptr<TouchDriver> driver); | ||
| 56 | void SetAppletResource(std::shared_ptr<AppletResource> shared, std::recursive_mutex* mutex); | ||
| 57 | void SetInputEvent(Kernel::KEvent* event, std::mutex* mutex); | ||
| 58 | void SetHandheldConfig(std::shared_ptr<HandheldConfig> config); | ||
| 59 | void SetTimerEvent(std::shared_ptr<Core::Timing::EventType> event); | ||
| 60 | |||
| 61 | Result SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state); | ||
| 62 | Result UnsetTouchScreenAutoPilotState(); | ||
| 63 | |||
| 64 | Result RequestNextTouchInput(); | ||
| 65 | Result RequestNextDummyInput(); | ||
| 66 | |||
| 67 | Result ProcessTouchScreenAutoTune(); | ||
| 68 | void SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, f32 point2_y); | ||
| 69 | Result SetTouchScreenResolution(u32 width, u32 height, u64 aruid); | ||
| 70 | |||
| 71 | Result SetTouchScreenConfiguration( | ||
| 72 | const Core::HID::TouchScreenConfigurationForNx& touch_configuration, u64 aruid); | ||
| 73 | Result GetTouchScreenConfiguration( | ||
| 74 | Core::HID::TouchScreenConfigurationForNx& out_touch_configuration, u64 aruid) const; | ||
| 75 | |||
| 76 | Result SetTouchScreenDefaultConfiguration( | ||
| 77 | const Core::HID::TouchScreenConfigurationForNx& touch_configuration); | ||
| 78 | Result GetTouchScreenDefaultConfiguration( | ||
| 79 | Core::HID::TouchScreenConfigurationForNx& out_touch_configuration) const; | ||
| 80 | |||
| 81 | void OnTouchUpdate(s64 timestamp); | ||
| 82 | |||
| 83 | private: | ||
| 84 | Result Finalize(); | ||
| 85 | |||
| 86 | void StorePreviousTouchState(TouchScreenState& out_previous_touch, | ||
| 87 | TouchFingerMap& out_finger_map, | ||
| 88 | const TouchScreenState& current_touch, | ||
| 89 | bool is_touch_enabled) const; | ||
| 90 | void ReadTouchInput(); | ||
| 91 | |||
| 92 | void SanitizeInput(TouchScreenState& state) const; | ||
| 93 | |||
| 94 | s32 global_ref_counter{}; | ||
| 95 | s32 gesture_ref_counter{}; | ||
| 96 | s32 touch_ref_counter{}; | ||
| 97 | bool is_initalized{}; | ||
| 98 | u64 sample_number{}; | ||
| 99 | |||
| 100 | // External resources | ||
| 101 | std::shared_ptr<Core::Timing::EventType> timer_event{nullptr}; | ||
| 102 | std::shared_ptr<TouchDriver> touch_driver{nullptr}; | ||
| 103 | std::shared_ptr<AppletResource> applet_resource{nullptr}; | ||
| 104 | std::recursive_mutex* shared_mutex{nullptr}; | ||
| 105 | std::shared_ptr<HandheldConfig> handheld_config{nullptr}; | ||
| 106 | Kernel::KEvent* input_event{nullptr}; | ||
| 107 | std::mutex* input_mutex{nullptr}; | ||
| 108 | |||
| 109 | // Internal state | ||
| 110 | TouchScreenState current_touch_state{}; | ||
| 111 | TouchScreenState previous_touch_state{}; | ||
| 112 | GestureState gesture_state{}; | ||
| 113 | bool is_auto_pilot_initialized{}; | ||
| 114 | AutoPilotState auto_pilot{}; | ||
| 115 | GestureHandler gesture_handler{}; | ||
| 116 | std::array<TouchAruidData, 0x20> aruid_data{}; | ||
| 117 | Common::Point<f32> magnification{1.0f, 1.0f}; | ||
| 118 | Common::Point<f32> offset{0.0f, 0.0f}; | ||
| 119 | Core::HID::TouchScreenModeForNx default_touch_screen_mode{ | ||
| 120 | Core::HID::TouchScreenModeForNx::Finger}; | ||
| 121 | |||
| 122 | Core::System& system; | ||
| 123 | std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; | ||
| 124 | }; | ||
| 125 | |||
| 126 | } // 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 @@ | |||
| 13 | #include "hid_core/hid_types.h" | 13 | #include "hid_core/hid_types.h" |
| 14 | 14 | ||
| 15 | namespace Service::HID { | 15 | namespace Service::HID { |
| 16 | static constexpr std::size_t MAX_FINGERS = 16; | 16 | constexpr std::size_t MaxFingers = 16; |
| 17 | static constexpr size_t MAX_POINTS = 4; | 17 | constexpr std::size_t MaxPoints = 4; |
| 18 | constexpr u32 TouchSensorWidth = 1280; | ||
| 19 | constexpr u32 TouchSensorHeight = 720; | ||
| 20 | constexpr s32 MaxRotationAngle = 270; | ||
| 21 | constexpr u32 MaxTouchDiameter = 30; | ||
| 22 | constexpr u32 TouchBorders = 15; | ||
| 23 | |||
| 24 | // HW is around 700, value is set to 400 to make it easier to trigger with mouse | ||
| 25 | constexpr f32 SwipeThreshold = 400.0f; // Threshold in pixels/s | ||
| 26 | constexpr f32 AngleThreshold = 0.015f; // Threshold in radians | ||
| 27 | constexpr f32 PinchThreshold = 0.5f; // Threshold in pixels | ||
| 28 | constexpr f32 PressDelay = 0.5f; // Time in seconds | ||
| 29 | constexpr f32 DoubleTapDelay = 0.35f; // Time in seconds | ||
| 18 | 30 | ||
| 19 | // This is nn::hid::GestureType | 31 | // This is nn::hid::GestureType |
| 20 | enum class GestureType : u32 { | 32 | enum class GestureType : u32 { |
| @@ -28,6 +40,7 @@ enum class GestureType : u32 { | |||
| 28 | Swipe, // Fast press movement and release of a single point | 40 | Swipe, // Fast press movement and release of a single point |
| 29 | Pinch, // All points moving away/closer to the midpoint | 41 | Pinch, // All points moving away/closer to the midpoint |
| 30 | Rotate, // All points rotating from the midpoint | 42 | Rotate, // All points rotating from the midpoint |
| 43 | GestureTypeMax = Rotate, | ||
| 31 | }; | 44 | }; |
| 32 | 45 | ||
| 33 | // This is nn::hid::GestureDirection | 46 | // This is nn::hid::GestureDirection |
| @@ -69,7 +82,7 @@ struct GestureState { | |||
| 69 | static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); | 82 | static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); |
| 70 | 83 | ||
| 71 | struct GestureProperties { | 84 | struct GestureProperties { |
| 72 | std::array<Common::Point<s32>, MAX_POINTS> points{}; | 85 | std::array<Common::Point<s32>, MaxPoints> points{}; |
| 73 | std::size_t active_points{}; | 86 | std::size_t active_points{}; |
| 74 | Common::Point<s32> mid_point{}; | 87 | Common::Point<s32> mid_point{}; |
| 75 | s64 detection_count{}; | 88 | s64 detection_count{}; |
| @@ -78,13 +91,53 @@ struct GestureProperties { | |||
| 78 | f32 angle{}; | 91 | f32 angle{}; |
| 79 | }; | 92 | }; |
| 80 | 93 | ||
| 94 | // This is nn::hid::TouchState | ||
| 95 | struct TouchState { | ||
| 96 | u64 delta_time{}; | ||
| 97 | Core::HID::TouchAttribute attribute{}; | ||
| 98 | u32 finger{}; | ||
| 99 | Common::Point<u32> position{}; | ||
| 100 | u32 diameter_x{}; | ||
| 101 | u32 diameter_y{}; | ||
| 102 | s32 rotation_angle{}; | ||
| 103 | }; | ||
| 104 | static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); | ||
| 105 | |||
| 81 | // This is nn::hid::TouchScreenState | 106 | // This is nn::hid::TouchScreenState |
| 82 | struct TouchScreenState { | 107 | struct TouchScreenState { |
| 83 | s64 sampling_number{}; | 108 | s64 sampling_number{}; |
| 84 | s32 entry_count{}; | 109 | s32 entry_count{}; |
| 85 | INSERT_PADDING_BYTES(4); // Reserved | 110 | INSERT_PADDING_BYTES(4); // Reserved |
| 86 | std::array<Core::HID::TouchState, MAX_FINGERS> states{}; | 111 | std::array<TouchState, MaxFingers> states{}; |
| 87 | }; | 112 | }; |
| 88 | static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size"); | 113 | static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size"); |
| 89 | 114 | ||
| 115 | struct TouchFingerMap { | ||
| 116 | s32 finger_count{}; | ||
| 117 | Core::HID::TouchScreenModeForNx touch_mode; | ||
| 118 | INSERT_PADDING_BYTES(3); | ||
| 119 | std::array<u32, MaxFingers> finger_ids{}; | ||
| 120 | }; | ||
| 121 | static_assert(sizeof(TouchFingerMap) == 0x48, "TouchFingerMap is an invalid size"); | ||
| 122 | |||
| 123 | struct TouchAruidData { | ||
| 124 | u64 aruid; | ||
| 125 | u32 basic_gesture_id; | ||
| 126 | u64 used_1; | ||
| 127 | u64 used_2; | ||
| 128 | u64 used_3; | ||
| 129 | u64 used_4; | ||
| 130 | GestureType gesture_type; | ||
| 131 | u16 resolution_width; | ||
| 132 | u16 resolution_height; | ||
| 133 | TouchFingerMap finger_map; | ||
| 134 | }; | ||
| 135 | static_assert(sizeof(TouchAruidData) == 0x80, "TouchAruidData is an invalid size"); | ||
| 136 | |||
| 137 | struct AutoPilotState { | ||
| 138 | u64 count; | ||
| 139 | std::array<TouchState, 16> state; | ||
| 140 | }; | ||
| 141 | static_assert(sizeof(AutoPilotState) == 0x288, "AutoPilotState is an invalid size"); | ||
| 142 | |||
| 90 | } // namespace Service::HID | 143 | } // namespace Service::HID |