summaryrefslogtreecommitdiff
path: root/src/hid_core/resources
diff options
context:
space:
mode:
authorGravatar Narr the Reg2024-01-16 12:17:18 -0600
committerGravatar german772024-01-28 18:27:25 -0600
commit575183d6dcd8da9b10ee41e47be4b7d4f8631783 (patch)
treed2898bdefae5be2fb68e7df97465422c0fae3991 /src/hid_core/resources
parentMerge pull request #12555 from flodavid/fix-gamemode-setting (diff)
downloadyuzu-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.h8
-rw-r--r--src/hid_core/resources/npad/npad.cpp3
-rw-r--r--src/hid_core/resources/npad/npad.h4
-rw-r--r--src/hid_core/resources/touch_screen/gesture.cpp367
-rw-r--r--src/hid_core/resources/touch_screen/gesture.h87
-rw-r--r--src/hid_core/resources/touch_screen/gesture_handler.cpp260
-rw-r--r--src/hid_core/resources/touch_screen/gesture_handler.h55
-rw-r--r--src/hid_core/resources/touch_screen/gesture_types.h77
-rw-r--r--src/hid_core/resources/touch_screen/touch_screen.cpp209
-rw-r--r--src/hid_core/resources/touch_screen/touch_screen.h71
-rw-r--r--src/hid_core/resources/touch_screen/touch_screen_driver.cpp114
-rw-r--r--src/hid_core/resources/touch_screen/touch_screen_driver.h47
-rw-r--r--src/hid_core/resources/touch_screen/touch_screen_resource.cpp579
-rw-r--r--src/hid_core/resources/touch_screen/touch_screen_resource.h126
-rw-r--r--src/hid_core/resources/touch_screen/touch_types.h61
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
14namespace Core { 14namespace Core {
15class System; 15class System;
16} 16} // namespace Core
17 17
18namespace Kernel { 18namespace Kernel {
19class KEvent;
19class KSharedMemory; 20class KSharedMemory;
20} 21} // namespace Kernel
21 22
22namespace Service::HID { 23namespace Service::HID {
23struct SharedMemoryFormat; 24struct 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
79class AppletResource { 81class 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) {
1081void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource, 1081void 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
13namespace Service::HID { 7namespace Service::HID {
14// HW is around 700, value is set to 400 to make it easier to trigger with mouse
15constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s
16constexpr f32 angle_threshold = 0.015f; // Threshold in radians
17constexpr f32 pinch_threshold = 0.5f; // Threshold in pixels
18constexpr f32 press_delay = 0.5f; // Time in seconds
19constexpr f32 double_tap_delay = 0.35f; // Time in seconds
20 8
21constexpr f32 Square(s32 num) { 9Gesture::Gesture(std::shared_ptr<TouchResource> resource) : touch_resource{resource} {}
22 return static_cast<f32>(num * num);
23}
24 10
25Gesture::Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) {
26 console = hid_core.GetEmulatedConsole();
27}
28Gesture::~Gesture() = default; 11Gesture::~Gesture() = default;
29 12
30void Gesture::OnInit() { 13Result 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
45void Gesture::OnRelease() {}
46
47void 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
80void 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
92bool 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
115void 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
152void 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
166void 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
200void 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
235void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 31Result 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
248void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 36Result 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
274void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 48Result 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
297void 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
321const GestureState& Gesture::GetLastGestureEntry() const {
322 return shared_memory->gesture_lifo.ReadCurrentEntry().state;
323}
324
325GestureProperties 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
12namespace Core::HID {
13class EmulatedConsole;
14}
15 10
16namespace Service::HID { 11namespace Service::HID {
17struct GestureSharedMemoryFormat; 12class TouchResource;
18 13
19class Gesture final : public ControllerBase { 14/// Handles gesture request from HID interfaces
15class Gesture {
20public: 16public:
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
33private: 27private:
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
7namespace Service::HID {
8
9constexpr f32 Square(s32 num) {
10 return static_cast<f32>(num * num);
11}
12
13GestureHandler::GestureHandler() {}
14
15GestureHandler::~GestureHandler() {}
16
17void 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
50bool 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
72void 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
108void 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
119void 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
150void 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
184void 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
196void 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
218void 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
239void 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
10namespace Service::HID {
11
12class GestureHandler {
13public:
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
22private:
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
11namespace Service::HID {
12static constexpr size_t MAX_FINGERS = 16;
13static constexpr size_t MAX_POINTS = 4;
14
15// This is nn::hid::GestureType
16enum 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
30enum class GestureDirection : u32 {
31 None,
32 Left,
33 Up,
34 Right,
35 Down,
36};
37
38// This is nn::hid::GestureAttribute
39struct 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};
47static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
48
49// This is nn::hid::GestureState
50struct 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};
65static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
66
67struct 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
15namespace Service::HID { 8namespace Service::HID {
16 9
17TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_) 10TouchScreen::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
23TouchScreen::~TouchScreen() = default; 12TouchScreen::~TouchScreen() = default;
24 13
25void TouchScreen::OnInit() {} 14Result TouchScreen::Activate() {
15 std::scoped_lock lock{mutex};
26 16
27void TouchScreen::OnRelease() {} 17 // TODO: Result result = CreateThread();
28 18 Result result = ResultSuccess;
29void 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()) { 31Result 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(); 36Result 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
48Result TouchScreen::IsActive(bool& out_is_active) const {
49 out_is_active = touch_resource->IsTouchActive();
50 return ResultSuccess;
51}
52
53Result TouchScreen::SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state) {
54 std::scoped_lock lock{mutex};
55 return touch_resource->SetTouchScreenAutoPilotState(auto_pilot_state);
56}
57
58Result TouchScreen::UnsetTouchScreenAutoPilotState() {
59 std::scoped_lock lock{mutex};
60 return touch_resource->UnsetTouchScreenAutoPilotState();
61}
62
63Result TouchScreen::RequestNextTouchInput() {
64 std::scoped_lock lock{mutex};
65 return touch_resource->RequestNextTouchInput();
66}
67
68Result TouchScreen::RequestNextDummyInput() {
69 std::scoped_lock lock{mutex};
70 return touch_resource->RequestNextDummyInput();
71}
72
73Result TouchScreen::ProcessTouchScreenAutoTune() {
74 std::scoped_lock lock{mutex};
75 return touch_resource->ProcessTouchScreenAutoTune();
76}
77
78Result 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
85Result TouchScreen::SetTouchScreenResolution(u32 width, u32 height, u64 aruid) {
86 std::scoped_lock lock{mutex};
87 return touch_resource->SetTouchScreenResolution(width, height, aruid);
88}
89
90Result 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
96Result 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
102Result TouchScreen::SetTouchScreenDefaultConfiguration(
103 const Core::HID::TouchScreenConfigurationForNx& mode) {
104 std::scoped_lock lock{mutex};
105 return touch_resource->SetTouchScreenDefaultConfiguration(mode);
106}
107
108Result 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
127void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) { 114void 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
12namespace Core::HID { 11namespace Core::HID {
13class EmulatedConsole; 12struct TouchScreenConfigurationForNx;
14} // namespace Core::HID 13}
14
15namespace Core::Timing {
16struct EventType;
17}
15 18
16namespace Service::HID { 19namespace Service::HID {
17struct TouchScreenSharedMemoryFormat; 20class TouchResource;
21struct AutoPilotState;
18 22
19class TouchScreen final : public ControllerBase { 23/// Handles touch request from HID interfaces
24class TouchScreen {
20public: 25public:
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
35private: 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
58private:
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
10namespace Service::HID {
11
12TouchDriver::TouchDriver(Core::HID::HIDCore& hid_core) {
13 console = hid_core.GetEmulatedConsole();
14}
15
16TouchDriver::~TouchDriver() = default;
17
18Result TouchDriver::StartTouchSensor() {
19 is_running = true;
20 return ResultSuccess;
21}
22
23Result TouchDriver::StopTouchSensor() {
24 is_running = false;
25 return ResultSuccess;
26}
27
28bool TouchDriver::IsRunning() const {
29 return is_running;
30}
31
32void TouchDriver::ProcessTouchScreenAutoTune() const {
33 // TODO
34}
35
36Result TouchDriver::WaitForDummyInput() {
37 touch_status = {};
38 return ResultSuccess;
39}
40
41Result 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
102void TouchDriver::GetNextTouchState(TouchScreenState& out_state) const {
103 out_state = touch_status;
104}
105
106void TouchDriver::SetTouchMode(Core::HID::TouchScreenModeForNx mode) {
107 touch_mode = mode;
108}
109
110Core::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
12namespace Core::HID {
13class HIDCore;
14} // namespace Core::HID
15
16namespace Service::HID {
17
18/// This handles all request to Ftm3bd56(TouchPanel) hardware
19class TouchDriver {
20public:
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
38private:
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
16namespace Service::HID {
17constexpr auto GestureUpdatePeriod = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
18
19TouchResource::TouchResource(Core::System& system_) : system{system_} {
20 m_set_sys = system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys");
21}
22
23TouchResource::~TouchResource() {
24 Finalize();
25};
26
27Result 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
59Result 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
93Result 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
120Result 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
157Result 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
172Result 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
187bool TouchResource::IsTouchActive() const {
188 return touch_ref_counter != 0;
189}
190
191bool TouchResource::IsGestureActive() const {
192 return gesture_ref_counter != 0;
193}
194
195void TouchResource::SetTouchDriver(std::shared_ptr<TouchDriver> driver) {
196 touch_driver = driver;
197}
198
199void TouchResource::SetAppletResource(std::shared_ptr<AppletResource> shared,
200 std::recursive_mutex* mutex) {
201 applet_resource = shared;
202 shared_mutex = mutex;
203}
204
205void TouchResource::SetInputEvent(Kernel::KEvent* event, std::mutex* mutex) {
206 input_event = event;
207 input_mutex = mutex;
208}
209
210void TouchResource::SetHandheldConfig(std::shared_ptr<HandheldConfig> config) {
211 handheld_config = config;
212}
213
214void TouchResource::SetTimerEvent(std::shared_ptr<Core::Timing::EventType> event) {
215 timer_event = event;
216}
217
218Result 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
240Result 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
250Result 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
266Result 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
282Result TouchResource::ProcessTouchScreenAutoTune() {
283 touch_driver->ProcessTouchScreenAutoTune();
284 return ResultSuccess;
285}
286
287void 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
299Result 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
319Result 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
339Result 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
359Result TouchResource::SetTouchScreenDefaultConfiguration(
360 const Core::HID::TouchScreenConfigurationForNx& touch_configuration) {
361 default_touch_screen_mode = touch_configuration.mode;
362 return ResultSuccess;
363}
364
365Result TouchResource::GetTouchScreenDefaultConfiguration(
366 Core::HID::TouchScreenConfigurationForNx& out_touch_configuration) const {
367 out_touch_configuration.mode = default_touch_screen_mode;
368 return ResultSuccess;
369}
370
371Result 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
385void 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
423void 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
509void 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
565void 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
16namespace Core {
17class System;
18}
19
20namespace Core::Timing {
21struct EventType;
22}
23
24namespace Kernel {
25class KEvent;
26} // namespace Kernel
27
28namespace Service::Set {
29class ISystemSettingsServer;
30}
31
32namespace Service::HID {
33class AppletResource;
34class TouchSharedMemoryManager;
35class TouchDriver;
36struct HandheldConfig;
37
38class TouchResource {
39public:
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
83private:
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
15namespace Service::HID { 15namespace Service::HID {
16static constexpr std::size_t MAX_FINGERS = 16; 16constexpr std::size_t MaxFingers = 16;
17static constexpr size_t MAX_POINTS = 4; 17constexpr std::size_t MaxPoints = 4;
18constexpr u32 TouchSensorWidth = 1280;
19constexpr u32 TouchSensorHeight = 720;
20constexpr s32 MaxRotationAngle = 270;
21constexpr u32 MaxTouchDiameter = 30;
22constexpr u32 TouchBorders = 15;
23
24// HW is around 700, value is set to 400 to make it easier to trigger with mouse
25constexpr f32 SwipeThreshold = 400.0f; // Threshold in pixels/s
26constexpr f32 AngleThreshold = 0.015f; // Threshold in radians
27constexpr f32 PinchThreshold = 0.5f; // Threshold in pixels
28constexpr f32 PressDelay = 0.5f; // Time in seconds
29constexpr f32 DoubleTapDelay = 0.35f; // Time in seconds
18 30
19// This is nn::hid::GestureType 31// This is nn::hid::GestureType
20enum class GestureType : u32 { 32enum 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 {
69static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); 82static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
70 83
71struct GestureProperties { 84struct 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
95struct 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};
104static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
105
81// This is nn::hid::TouchScreenState 106// This is nn::hid::TouchScreenState
82struct TouchScreenState { 107struct 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};
88static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size"); 113static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
89 114
115struct TouchFingerMap {
116 s32 finger_count{};
117 Core::HID::TouchScreenModeForNx touch_mode;
118 INSERT_PADDING_BYTES(3);
119 std::array<u32, MaxFingers> finger_ids{};
120};
121static_assert(sizeof(TouchFingerMap) == 0x48, "TouchFingerMap is an invalid size");
122
123struct 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};
135static_assert(sizeof(TouchAruidData) == 0x80, "TouchAruidData is an invalid size");
136
137struct AutoPilotState {
138 u64 count;
139 std::array<TouchState, 16> state;
140};
141static_assert(sizeof(AutoPilotState) == 0x288, "AutoPilotState is an invalid size");
142
90} // namespace Service::HID 143} // namespace Service::HID