summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp274
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h80
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp136
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h71
4 files changed, 191 insertions, 370 deletions
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 764abb5b6..2f98cc54b 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -5,8 +5,10 @@
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "common/math_util.h" 6#include "common/math_util.h"
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core.h"
8#include "core/core_timing.h" 9#include "core/core_timing.h"
9#include "core/frontend/emu_window.h" 10#include "core/frontend/emu_window.h"
11#include "core/hid/hid_core.h"
10#include "core/hle/service/hid/controllers/gesture.h" 12#include "core/hle/service/hid/controllers/gesture.h"
11 13
12namespace Service::HID { 14namespace Service::HID {
@@ -23,16 +25,15 @@ constexpr f32 Square(s32 num) {
23 return static_cast<f32>(num * num); 25 return static_cast<f32>(num * num);
24} 26}
25 27
26Controller_Gesture::Controller_Gesture(Core::System& system_) : ControllerBase(system_) {} 28Controller_Gesture::Controller_Gesture(Core::System& system_) : ControllerBase(system_) {
29 console = system.HIDCore().GetEmulatedConsole();
30}
31
27Controller_Gesture::~Controller_Gesture() = default; 32Controller_Gesture::~Controller_Gesture() = default;
28 33
29void Controller_Gesture::OnInit() { 34void Controller_Gesture::OnInit() {
30 for (std::size_t id = 0; id < MAX_FINGERS; ++id) { 35 gesture_lifo.entry_count = 0;
31 mouse_finger_id[id] = MAX_POINTS; 36 gesture_lifo.last_entry_index = 0;
32 keyboard_finger_id[id] = MAX_POINTS;
33 udp_finger_id[id] = MAX_POINTS;
34 }
35 shared_memory.header.entry_count = 0;
36 force_update = true; 37 force_update = true;
37} 38}
38 39
@@ -40,50 +41,43 @@ void Controller_Gesture::OnRelease() {}
40 41
41void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 42void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
42 std::size_t size) { 43 std::size_t size) {
43 shared_memory.header.timestamp = core_timing.GetCPUTicks(); 44 // TODO FIND WTF IS WRONG HERE!!!!!!!!
44 shared_memory.header.total_entry_count = 17; 45 return;
45
46 if (!IsControllerActivated()) { 46 if (!IsControllerActivated()) {
47 shared_memory.header.entry_count = 0; 47 gesture_lifo.entry_count = 0;
48 shared_memory.header.last_entry_index = 0; 48 gesture_lifo.last_entry_index = 0;
49 std::memcpy(data, &gesture_lifo, sizeof(gesture_lifo));
49 return; 50 return;
50 } 51 }
51 52
52 ReadTouchInput(); 53 ReadTouchInput();
53 54
54 GestureProperties gesture = GetGestureProperties(); 55 GestureProperties gesture = GetGestureProperties();
55 f32 time_difference = static_cast<f32>(shared_memory.header.timestamp - last_update_timestamp) / 56 f32 time_difference =
56 (1000 * 1000 * 1000); 57 static_cast<f32>(gesture_lifo.timestamp - last_update_timestamp) / (1000 * 1000 * 1000);
57 58
58 // Only update if necesary 59 // Only update if necesary
59 if (!ShouldUpdateGesture(gesture, time_difference)) { 60 if (!ShouldUpdateGesture(gesture, time_difference)) {
60 return; 61 return;
61 } 62 }
62 63
63 last_update_timestamp = shared_memory.header.timestamp; 64 last_update_timestamp = gesture_lifo.timestamp;
64 UpdateGestureSharedMemory(data, size, gesture, time_difference); 65 UpdateGestureSharedMemory(data, size, gesture, time_difference);
65} 66}
66 67
67void Controller_Gesture::ReadTouchInput() { 68void Controller_Gesture::ReadTouchInput() {
68 const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus(); 69 const auto touch_status = console->GetTouch();
69 const Input::TouchStatus& udp_status = touch_udp_device->GetStatus(); 70 for (std::size_t id = 0; id < fingers.size(); ++id) {
70 for (std::size_t id = 0; id < mouse_status.size(); ++id) { 71 const Core::HID::TouchFinger& status = touch_status[id];
71 mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]); 72 Finger& finger = fingers[id];
72 udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]); 73 finger.pos = status.position;
73 } 74 finger.pressed = status.pressed;
74
75 if (Settings::values.use_touch_from_button) {
76 const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
77 for (std::size_t id = 0; id < mouse_status.size(); ++id) {
78 keyboard_finger_id[id] =
79 UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
80 }
81 } 75 }
82} 76}
83 77
84bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, 78bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
85 f32 time_difference) { 79 f32 time_difference) {
86 const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; 80 const auto& last_entry = gesture_lifo.ReadCurrentEntry().state;
87 if (force_update) { 81 if (force_update) {
88 force_update = false; 82 force_update = false;
89 return true; 83 return true;
@@ -97,7 +91,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
97 } 91 }
98 92
99 // Update on press and hold event after 0.5 seconds 93 // Update on press and hold event after 0.5 seconds
100 if (last_entry.type == TouchType::Touch && last_entry.point_count == 1 && 94 if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 &&
101 time_difference > press_delay) { 95 time_difference > press_delay) {
102 return enable_press_and_tap; 96 return enable_press_and_tap;
103 } 97 }
@@ -108,27 +102,19 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
108void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, 102void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
109 GestureProperties& gesture, 103 GestureProperties& gesture,
110 f32 time_difference) { 104 f32 time_difference) {
111 TouchType type = TouchType::Idle; 105 GestureType type = GestureType::Idle;
112 Attribute attributes{}; 106 GestureAttribute attributes{};
113
114 const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
115 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
116 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
117 107
118 if (shared_memory.header.entry_count < 16) { 108 const auto& last_entry = gesture_lifo.ReadCurrentEntry().state;
119 shared_memory.header.entry_count++;
120 }
121
122 cur_entry.sampling_number = last_entry.sampling_number + 1;
123 cur_entry.sampling_number2 = cur_entry.sampling_number;
124 109
125 // Reset values to default 110 // Reset next state to default
126 cur_entry.delta = {}; 111 next_state.sampling_number = last_entry.sampling_number + 1;
127 cur_entry.vel_x = 0; 112 next_state.delta = {};
128 cur_entry.vel_y = 0; 113 next_state.vel_x = 0;
129 cur_entry.direction = Direction::None; 114 next_state.vel_y = 0;
130 cur_entry.rotation_angle = 0; 115 next_state.direction = GestureDirection::None;
131 cur_entry.scale = 0; 116 next_state.rotation_angle = 0;
117 next_state.scale = 0;
132 118
133 if (gesture.active_points > 0) { 119 if (gesture.active_points > 0) {
134 if (last_gesture.active_points == 0) { 120 if (last_gesture.active_points == 0) {
@@ -141,46 +127,47 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
141 } 127 }
142 128
143 // Apply attributes 129 // Apply attributes
144 cur_entry.detection_count = gesture.detection_count; 130 next_state.detection_count = gesture.detection_count;
145 cur_entry.type = type; 131 next_state.type = type;
146 cur_entry.attributes = attributes; 132 next_state.attributes = attributes;
147 cur_entry.pos = gesture.mid_point; 133 next_state.pos = gesture.mid_point;
148 cur_entry.point_count = static_cast<s32>(gesture.active_points); 134 next_state.point_count = static_cast<s32>(gesture.active_points);
149 cur_entry.points = gesture.points; 135 next_state.points = gesture.points;
150 last_gesture = gesture; 136 last_gesture = gesture;
151 137
152 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 138 gesture_lifo.WriteNextEntry(next_state);
139 std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo));
153} 140}
154 141
155void Controller_Gesture::NewGesture(GestureProperties& gesture, TouchType& type, 142void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
156 Attribute& attributes) { 143 GestureAttribute& attributes) {
157 const auto& last_entry = GetLastGestureEntry(); 144 const auto& last_entry = gesture_lifo.ReadCurrentEntry().state;
158 145
159 gesture.detection_count++; 146 gesture.detection_count++;
160 type = TouchType::Touch; 147 type = GestureType::Touch;
161 148
162 // New touch after cancel is not considered new 149 // New touch after cancel is not considered new
163 if (last_entry.type != TouchType::Cancel) { 150 if (last_entry.type != GestureType::Cancel) {
164 attributes.is_new_touch.Assign(1); 151 attributes.is_new_touch.Assign(1);
165 enable_press_and_tap = true; 152 enable_press_and_tap = true;
166 } 153 }
167} 154}
168 155
169void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, TouchType& type, 156void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
170 f32 time_difference) { 157 f32 time_difference) {
171 const auto& last_entry = GetLastGestureEntry(); 158 const auto& last_entry = gesture_lifo.ReadCurrentEntry().state;
172 159
173 // Promote to pan type if touch moved 160 // Promote to pan type if touch moved
174 for (size_t id = 0; id < MAX_POINTS; id++) { 161 for (size_t id = 0; id < MAX_POINTS; id++) {
175 if (gesture.points[id] != last_gesture.points[id]) { 162 if (gesture.points[id] != last_gesture.points[id]) {
176 type = TouchType::Pan; 163 type = GestureType::Pan;
177 break; 164 break;
178 } 165 }
179 } 166 }
180 167
181 // Number of fingers changed cancel the last event and clear data 168 // Number of fingers changed cancel the last event and clear data
182 if (gesture.active_points != last_gesture.active_points) { 169 if (gesture.active_points != last_gesture.active_points) {
183 type = TouchType::Cancel; 170 type = GestureType::Cancel;
184 enable_press_and_tap = false; 171 enable_press_and_tap = false;
185 gesture.active_points = 0; 172 gesture.active_points = 0;
186 gesture.mid_point = {}; 173 gesture.mid_point = {};
@@ -189,41 +176,41 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Touch
189 } 176 }
190 177
191 // Calculate extra parameters of panning 178 // Calculate extra parameters of panning
192 if (type == TouchType::Pan) { 179 if (type == GestureType::Pan) {
193 UpdatePanEvent(gesture, last_gesture, type, time_difference); 180 UpdatePanEvent(gesture, last_gesture, type, time_difference);
194 return; 181 return;
195 } 182 }
196 183
197 // Promote to press type 184 // Promote to press type
198 if (last_entry.type == TouchType::Touch) { 185 if (last_entry.type == GestureType::Touch) {
199 type = TouchType::Press; 186 type = GestureType::Press;
200 } 187 }
201} 188}
202 189
203void Controller_Gesture::EndGesture(GestureProperties& gesture, 190void Controller_Gesture::EndGesture(GestureProperties& gesture,
204 GestureProperties& last_gesture_props, TouchType& type, 191 GestureProperties& last_gesture_props, GestureType& type,
205 Attribute& attributes, f32 time_difference) { 192 GestureAttribute& attributes, f32 time_difference) {
206 const auto& last_entry = GetLastGestureEntry(); 193 const auto& last_entry = gesture_lifo.ReadCurrentEntry().state;
207 194
208 if (last_gesture_props.active_points != 0) { 195 if (last_gesture_props.active_points != 0) {
209 switch (last_entry.type) { 196 switch (last_entry.type) {
210 case TouchType::Touch: 197 case GestureType::Touch:
211 if (enable_press_and_tap) { 198 if (enable_press_and_tap) {
212 SetTapEvent(gesture, last_gesture_props, type, attributes); 199 SetTapEvent(gesture, last_gesture_props, type, attributes);
213 return; 200 return;
214 } 201 }
215 type = TouchType::Cancel; 202 type = GestureType::Cancel;
216 force_update = true; 203 force_update = true;
217 break; 204 break;
218 case TouchType::Press: 205 case GestureType::Press:
219 case TouchType::Tap: 206 case GestureType::Tap:
220 case TouchType::Swipe: 207 case GestureType::Swipe:
221 case TouchType::Pinch: 208 case GestureType::Pinch:
222 case TouchType::Rotate: 209 case GestureType::Rotate:
223 type = TouchType::Complete; 210 type = GestureType::Complete;
224 force_update = true; 211 force_update = true;
225 break; 212 break;
226 case TouchType::Pan: 213 case GestureType::Pan:
227 EndPanEvent(gesture, last_gesture_props, type, time_difference); 214 EndPanEvent(gesture, last_gesture_props, type, time_difference);
228 break; 215 break;
229 default: 216 default:
@@ -231,15 +218,15 @@ void Controller_Gesture::EndGesture(GestureProperties& gesture,
231 } 218 }
232 return; 219 return;
233 } 220 }
234 if (last_entry.type == TouchType::Complete || last_entry.type == TouchType::Cancel) { 221 if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) {
235 gesture.detection_count++; 222 gesture.detection_count++;
236 } 223 }
237} 224}
238 225
239void Controller_Gesture::SetTapEvent(GestureProperties& gesture, 226void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
240 GestureProperties& last_gesture_props, TouchType& type, 227 GestureProperties& last_gesture_props, GestureType& type,
241 Attribute& attributes) { 228 GestureAttribute& attributes) {
242 type = TouchType::Tap; 229 type = GestureType::Tap;
243 gesture = last_gesture_props; 230 gesture = last_gesture_props;
244 force_update = true; 231 force_update = true;
245 f32 tap_time_difference = 232 f32 tap_time_difference =
@@ -251,44 +238,42 @@ void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
251} 238}
252 239
253void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture, 240void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
254 GestureProperties& last_gesture_props, TouchType& type, 241 GestureProperties& last_gesture_props, GestureType& type,
255 f32 time_difference) { 242 f32 time_difference) {
256 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; 243 const auto& last_entry = gesture_lifo.ReadCurrentEntry().state;
257 const auto& last_entry = GetLastGestureEntry();
258 244
259 cur_entry.delta = gesture.mid_point - last_entry.pos; 245 next_state.delta = gesture.mid_point - last_entry.pos;
260 cur_entry.vel_x = static_cast<f32>(cur_entry.delta.x) / time_difference; 246 next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
261 cur_entry.vel_y = static_cast<f32>(cur_entry.delta.y) / time_difference; 247 next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
262 last_pan_time_difference = time_difference; 248 last_pan_time_difference = time_difference;
263 249
264 // Promote to pinch type 250 // Promote to pinch type
265 if (std::abs(gesture.average_distance - last_gesture_props.average_distance) > 251 if (std::abs(gesture.average_distance - last_gesture_props.average_distance) >
266 pinch_threshold) { 252 pinch_threshold) {
267 type = TouchType::Pinch; 253 type = GestureType::Pinch;
268 cur_entry.scale = gesture.average_distance / last_gesture_props.average_distance; 254 next_state.scale = gesture.average_distance / last_gesture_props.average_distance;
269 } 255 }
270 256
271 const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) / 257 const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) /
272 (1 + (gesture.angle * last_gesture_props.angle))); 258 (1 + (gesture.angle * last_gesture_props.angle)));
273 // Promote to rotate type 259 // Promote to rotate type
274 if (std::abs(angle_between_two_lines) > angle_threshold) { 260 if (std::abs(angle_between_two_lines) > angle_threshold) {
275 type = TouchType::Rotate; 261 type = GestureType::Rotate;
276 cur_entry.scale = 0; 262 next_state.scale = 0;
277 cur_entry.rotation_angle = angle_between_two_lines * 180.0f / Common::PI; 263 next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
278 } 264 }
279} 265}
280 266
281void Controller_Gesture::EndPanEvent(GestureProperties& gesture, 267void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
282 GestureProperties& last_gesture_props, TouchType& type, 268 GestureProperties& last_gesture_props, GestureType& type,
283 f32 time_difference) { 269 f32 time_difference) {
284 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; 270 const auto& last_entry = gesture_lifo.ReadCurrentEntry().state;
285 const auto& last_entry = GetLastGestureEntry(); 271 next_state.vel_x =
286 cur_entry.vel_x =
287 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); 272 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
288 cur_entry.vel_y = 273 next_state.vel_y =
289 static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference); 274 static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference);
290 const f32 curr_vel = 275 const f32 curr_vel =
291 std::sqrt((cur_entry.vel_x * cur_entry.vel_x) + (cur_entry.vel_y * cur_entry.vel_y)); 276 std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
292 277
293 // Set swipe event with parameters 278 // Set swipe event with parameters
294 if (curr_vel > swipe_threshold) { 279 if (curr_vel > swipe_threshold) {
@@ -297,93 +282,34 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
297 } 282 }
298 283
299 // End panning without swipe 284 // End panning without swipe
300 type = TouchType::Complete; 285 type = GestureType::Complete;
301 cur_entry.vel_x = 0; 286 next_state.vel_x = 0;
302 cur_entry.vel_y = 0; 287 next_state.vel_y = 0;
303 force_update = true; 288 force_update = true;
304} 289}
305 290
306void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, 291void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
307 GestureProperties& last_gesture_props, TouchType& type) { 292 GestureProperties& last_gesture_props, GestureType& type) {
308 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; 293 const auto& last_entry = gesture_lifo.ReadCurrentEntry().state;
309 const auto& last_entry = GetLastGestureEntry();
310 294
311 type = TouchType::Swipe; 295 type = GestureType::Swipe;
312 gesture = last_gesture_props; 296 gesture = last_gesture_props;
313 force_update = true; 297 force_update = true;
314 cur_entry.delta = last_entry.delta; 298 next_state.delta = last_entry.delta;
315 299
316 if (std::abs(cur_entry.delta.x) > std::abs(cur_entry.delta.y)) { 300 if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
317 if (cur_entry.delta.x > 0) { 301 if (next_state.delta.x > 0) {
318 cur_entry.direction = Direction::Right; 302 next_state.direction = GestureDirection::Right;
319 return; 303 return;
320 } 304 }
321 cur_entry.direction = Direction::Left; 305 next_state.direction = GestureDirection::Left;
322 return; 306 return;
323 } 307 }
324 if (cur_entry.delta.y > 0) { 308 if (next_state.delta.y > 0) {
325 cur_entry.direction = Direction::Down; 309 next_state.direction = GestureDirection::Down;
326 return; 310 return;
327 } 311 }
328 cur_entry.direction = Direction::Up; 312 next_state.direction = GestureDirection::Up;
329}
330
331void Controller_Gesture::OnLoadInputDevices() {
332 touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
333 touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
334 touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
335}
336
337std::optional<std::size_t> Controller_Gesture::GetUnusedFingerID() const {
338 // Dont assign any touch input to a point if disabled
339 if (!Settings::values.touchscreen.enabled) {
340 return std::nullopt;
341 }
342 std::size_t first_free_id = 0;
343 while (first_free_id < MAX_POINTS) {
344 if (!fingers[first_free_id].pressed) {
345 return first_free_id;
346 } else {
347 first_free_id++;
348 }
349 }
350 return std::nullopt;
351}
352
353Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() {
354 return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
355}
356
357const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const {
358 return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
359}
360
361std::size_t Controller_Gesture::UpdateTouchInputEvent(
362 const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
363 const auto& [x, y, pressed] = touch_input;
364 if (finger_id > MAX_POINTS) {
365 LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
366 return MAX_POINTS;
367 }
368 if (pressed) {
369 if (finger_id == MAX_POINTS) {
370 const auto first_free_id = GetUnusedFingerID();
371 if (!first_free_id) {
372 // Invalid finger id do nothing
373 return MAX_POINTS;
374 }
375 finger_id = first_free_id.value();
376 fingers[finger_id].pressed = true;
377 }
378 fingers[finger_id].pos = {x, y};
379 return finger_id;
380 }
381
382 if (finger_id != MAX_POINTS) {
383 fingers[finger_id].pressed = false;
384 }
385
386 return MAX_POINTS;
387} 313}
388 314
389Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() { 315Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 7e7ae6625..8e6f315a4 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -8,8 +8,12 @@
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/point.h" 10#include "common/point.h"
11#include "core/frontend/input.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 11#include "core/hle/service/hid/controllers/controller_base.h"
12#include "core/hle/service/hid/ring_lifo.h"
13
14namespace Core::HID {
15class EmulatedController;
16} // namespace Core::HID
13 17
14namespace Service::HID { 18namespace Service::HID {
15class Controller_Gesture final : public ControllerBase { 19class Controller_Gesture final : public ControllerBase {
@@ -26,14 +30,12 @@ public:
26 // When the controller is requesting an update for the shared memory 30 // When the controller is requesting an update for the shared memory
27 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; 31 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
28 32
29 // Called when input devices should be loaded
30 void OnLoadInputDevices() override;
31
32private: 33private:
33 static constexpr size_t MAX_FINGERS = 16; 34 static constexpr size_t MAX_FINGERS = 16;
34 static constexpr size_t MAX_POINTS = 4; 35 static constexpr size_t MAX_POINTS = 4;
35 36
36 enum class TouchType : u32 { 37 // This is nn::hid::GestureType
38 enum class GestureType : u32 {
37 Idle, // Nothing touching the screen 39 Idle, // Nothing touching the screen
38 Complete, // Set at the end of a touch event 40 Complete, // Set at the end of a touch event
39 Cancel, // Set when the number of fingers change 41 Cancel, // Set when the number of fingers change
@@ -46,7 +48,8 @@ private:
46 Rotate, // All points rotating from the midpoint 48 Rotate, // All points rotating from the midpoint
47 }; 49 };
48 50
49 enum class Direction : u32 { 51 // This is nn::hid::GestureDirection
52 enum class GestureDirection : u32 {
50 None, 53 None,
51 Left, 54 Left,
52 Up, 55 Up,
@@ -54,7 +57,8 @@ private:
54 Down, 57 Down,
55 }; 58 };
56 59
57 struct Attribute { 60 // This is nn::hid::GestureAttribute
61 struct GestureAttribute {
58 union { 62 union {
59 u32_le raw{}; 63 u32_le raw{};
60 64
@@ -62,31 +66,25 @@ private:
62 BitField<8, 1, u32> is_double_tap; 66 BitField<8, 1, u32> is_double_tap;
63 }; 67 };
64 }; 68 };
65 static_assert(sizeof(Attribute) == 4, "Attribute is an invalid size"); 69 static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
66 70
71 // This is nn::hid::GestureState
67 struct GestureState { 72 struct GestureState {
68 s64_le sampling_number; 73 s64_le sampling_number;
69 s64_le sampling_number2;
70 s64_le detection_count; 74 s64_le detection_count;
71 TouchType type; 75 GestureType type;
72 Direction direction; 76 GestureDirection direction;
73 Common::Point<s32_le> pos; 77 Common::Point<s32_le> pos;
74 Common::Point<s32_le> delta; 78 Common::Point<s32_le> delta;
75 f32 vel_x; 79 f32 vel_x;
76 f32 vel_y; 80 f32 vel_y;
77 Attribute attributes; 81 GestureAttribute attributes;
78 f32 scale; 82 f32 scale;
79 f32 rotation_angle; 83 f32 rotation_angle;
80 s32_le point_count; 84 s32_le point_count;
81 std::array<Common::Point<s32_le>, 4> points; 85 std::array<Common::Point<s32_le>, 4> points;
82 }; 86 };
83 static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size"); 87 static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
84
85 struct SharedMemory {
86 CommonHeader header;
87 std::array<GestureState, 17> gesture_states;
88 };
89 static_assert(sizeof(SharedMemory) == 0x708, "SharedMemory is an invalid size");
90 88
91 struct Finger { 89 struct Finger {
92 Common::Point<f32> pos{}; 90 Common::Point<f32> pos{};
@@ -114,58 +112,42 @@ private:
114 f32 time_difference); 112 f32 time_difference);
115 113
116 // Initializes new gesture 114 // Initializes new gesture
117 void NewGesture(GestureProperties& gesture, TouchType& type, Attribute& attributes); 115 void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
118 116
119 // Updates existing gesture state 117 // Updates existing gesture state
120 void UpdateExistingGesture(GestureProperties& gesture, TouchType& type, f32 time_difference); 118 void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference);
121 119
122 // Terminates exiting gesture 120 // Terminates exiting gesture
123 void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, 121 void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
124 TouchType& type, Attribute& attributes, f32 time_difference); 122 GestureType& type, GestureAttribute& attributes, f32 time_difference);
125 123
126 // Set current event to a tap event 124 // Set current event to a tap event
127 void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 125 void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
128 TouchType& type, Attribute& attributes); 126 GestureType& type, GestureAttribute& attributes);
129 127
130 // Calculates and set the extra parameters related to a pan event 128 // Calculates and set the extra parameters related to a pan event
131 void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 129 void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
132 TouchType& type, f32 time_difference); 130 GestureType& type, f32 time_difference);
133 131
134 // Terminates the pan event 132 // Terminates the pan event
135 void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 133 void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
136 TouchType& type, f32 time_difference); 134 GestureType& type, f32 time_difference);
137 135
138 // Set current event to a swipe event 136 // Set current event to a swipe event
139 void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 137 void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
140 TouchType& type); 138 GestureType& type);
141
142 // Returns an unused finger id, if there is no fingers available std::nullopt is returned.
143 [[nodiscard]] std::optional<size_t> GetUnusedFingerID() const;
144
145 // Retrieves the last gesture entry, as indicated by shared memory indices.
146 [[nodiscard]] GestureState& GetLastGestureEntry();
147 [[nodiscard]] const GestureState& GetLastGestureEntry() const;
148
149 /**
150 * If the touch is new it tries to assign a new finger id, if there is no fingers available no
151 * changes will be made. Updates the coordinates if the finger id it's already set. If the touch
152 * ends delays the output by one frame to set the end_touch flag before finally freeing the
153 * finger id
154 */
155 size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
156 size_t finger_id);
157 139
158 // Returns the average distance, angle and middle point of the active fingers 140 // Returns the average distance, angle and middle point of the active fingers
159 GestureProperties GetGestureProperties(); 141 GestureProperties GetGestureProperties();
160 142
161 SharedMemory shared_memory{}; 143 // This is nn::hid::detail::GestureLifo
162 std::unique_ptr<Input::TouchDevice> touch_mouse_device; 144 Lifo<GestureState> gesture_lifo{};
163 std::unique_ptr<Input::TouchDevice> touch_udp_device; 145 static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
164 std::unique_ptr<Input::TouchDevice> touch_btn_device; 146 GestureState next_state{};
165 std::array<size_t, MAX_FINGERS> mouse_finger_id{}; 147
166 std::array<size_t, MAX_FINGERS> keyboard_finger_id{};
167 std::array<size_t, MAX_FINGERS> udp_finger_id{};
168 std::array<Finger, MAX_POINTS> fingers{}; 148 std::array<Finger, MAX_POINTS> fingers{};
149 Core::HID::EmulatedConsole* console;
150
169 GestureProperties last_gesture{}; 151 GestureProperties last_gesture{};
170 s64_le last_update_timestamp{}; 152 s64_le last_update_timestamp{};
171 s64_le last_tap_timestamp{}; 153 s64_le last_tap_timestamp{};
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 6ef17acc5..e0a44d06b 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -7,72 +7,79 @@
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/settings.h" 9#include "common/settings.h"
10#include "core/core.h"
10#include "core/core_timing.h" 11#include "core/core_timing.h"
11#include "core/frontend/emu_window.h" 12#include "core/frontend/emu_window.h"
12#include "core/frontend/input.h"
13#include "core/hle/service/hid/controllers/touchscreen.h" 13#include "core/hle/service/hid/controllers/touchscreen.h"
14 14
15namespace Service::HID { 15namespace Service::HID {
16constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; 16constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
17 17
18Controller_Touchscreen::Controller_Touchscreen(Core::System& system_) : ControllerBase{system_} {} 18Controller_Touchscreen::Controller_Touchscreen(Core::System& system_) : ControllerBase{system_} {
19 console = system.HIDCore().GetEmulatedConsole();
20}
21
19Controller_Touchscreen::~Controller_Touchscreen() = default; 22Controller_Touchscreen::~Controller_Touchscreen() = default;
20 23
21void Controller_Touchscreen::OnInit() { 24void Controller_Touchscreen::OnInit() {}
22 for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
23 mouse_finger_id[id] = MAX_FINGERS;
24 keyboard_finger_id[id] = MAX_FINGERS;
25 udp_finger_id[id] = MAX_FINGERS;
26 }
27}
28 25
29void Controller_Touchscreen::OnRelease() {} 26void Controller_Touchscreen::OnRelease() {}
30 27
31void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 28void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
32 std::size_t size) { 29 std::size_t size) {
33 shared_memory.header.timestamp = core_timing.GetCPUTicks(); 30 touch_screen_lifo.timestamp = core_timing.GetCPUTicks();
34 shared_memory.header.total_entry_count = 17;
35 31
36 if (!IsControllerActivated()) { 32 if (!IsControllerActivated()) {
37 shared_memory.header.entry_count = 0; 33 touch_screen_lifo.entry_count = 0;
38 shared_memory.header.last_entry_index = 0; 34 touch_screen_lifo.last_entry_index = 0;
35 std::memcpy(data, &touch_screen_lifo, sizeof(touch_screen_lifo));
39 return; 36 return;
40 } 37 }
41 shared_memory.header.entry_count = 16;
42 38
43 const auto& last_entry = 39 const auto touch_status = console->GetTouch();
44 shared_memory.shared_memory_entries[shared_memory.header.last_entry_index]; 40 for (std::size_t id = 0; id < MAX_FINGERS; id++) {
45 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; 41 const auto& current_touch = touch_status[id];
46 auto& cur_entry = shared_memory.shared_memory_entries[shared_memory.header.last_entry_index]; 42 auto& finger = fingers[id];
43 finger.position = current_touch.position;
44 finger.id = current_touch.id;
47 45
48 cur_entry.sampling_number = last_entry.sampling_number + 1; 46 if (finger.attribute.start_touch) {
49 cur_entry.sampling_number2 = cur_entry.sampling_number; 47 finger.attribute.raw = 0;
48 continue;
49 }
50 50
51 const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus(); 51 if (finger.attribute.end_touch) {
52 const Input::TouchStatus& udp_status = touch_udp_device->GetStatus(); 52 finger.attribute.raw = 0;
53 for (std::size_t id = 0; id < mouse_status.size(); ++id) { 53 finger.pressed = false;
54 mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]); 54 continue;
55 udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]); 55 }
56 } 56
57 if (!finger.pressed && current_touch.pressed) {
58 finger.attribute.start_touch.Assign(1);
59 finger.pressed = true;
60 continue;
61 }
57 62
58 if (Settings::values.use_touch_from_button) { 63 if (finger.pressed && !current_touch.pressed) {
59 const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus(); 64 finger.attribute.raw = 0;
60 for (std::size_t id = 0; id < mouse_status.size(); ++id) { 65 finger.attribute.end_touch.Assign(1);
61 keyboard_finger_id[id] =
62 UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
63 } 66 }
64 } 67 }
65 68
66 std::array<Finger, 16> active_fingers; 69 std::array<Finger, MAX_FINGERS> active_fingers;
67 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), 70 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
68 [](const auto& finger) { return finger.pressed; }); 71 [](const auto& finger) { return finger.pressed; });
69 const auto active_fingers_count = 72 const auto active_fingers_count =
70 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); 73 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
71 74
72 const u64 tick = core_timing.GetCPUTicks(); 75 const u64 tick = core_timing.GetCPUTicks();
73 cur_entry.entry_count = static_cast<s32_le>(active_fingers_count); 76 const auto& last_entry = touch_screen_lifo.ReadCurrentEntry().state;
77
78 next_state.sampling_number = last_entry.sampling_number + 1;
79 next_state.entry_count = static_cast<s32_le>(active_fingers_count);
80
74 for (std::size_t id = 0; id < MAX_FINGERS; ++id) { 81 for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
75 auto& touch_entry = cur_entry.states[id]; 82 auto& touch_entry = next_state.states[id];
76 if (id < active_fingers_count) { 83 if (id < active_fingers_count) {
77 const auto& [active_x, active_y] = active_fingers[id].position; 84 const auto& [active_x, active_y] = active_fingers[id].position;
78 touch_entry.position = { 85 touch_entry.position = {
@@ -97,66 +104,9 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
97 touch_entry.finger = 0; 104 touch_entry.finger = 0;
98 } 105 }
99 } 106 }
100 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
101}
102
103void Controller_Touchscreen::OnLoadInputDevices() {
104 touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
105 touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
106 touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
107}
108
109std::optional<std::size_t> Controller_Touchscreen::GetUnusedFingerID() const {
110 // Dont assign any touch input to a finger if disabled
111 if (!Settings::values.touchscreen.enabled) {
112 return std::nullopt;
113 }
114 std::size_t first_free_id = 0;
115 while (first_free_id < MAX_FINGERS) {
116 if (!fingers[first_free_id].pressed) {
117 return first_free_id;
118 } else {
119 first_free_id++;
120 }
121 }
122 return std::nullopt;
123}
124
125std::size_t Controller_Touchscreen::UpdateTouchInputEvent(
126 const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
127 const auto& [x, y, pressed] = touch_input;
128 if (finger_id > MAX_FINGERS) {
129 LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
130 return MAX_FINGERS;
131 }
132 if (pressed) {
133 Attributes attribute{};
134 if (finger_id == MAX_FINGERS) {
135 const auto first_free_id = GetUnusedFingerID();
136 if (!first_free_id) {
137 // Invalid finger id do nothing
138 return MAX_FINGERS;
139 }
140 finger_id = first_free_id.value();
141 fingers[finger_id].pressed = true;
142 fingers[finger_id].id = static_cast<u32_le>(finger_id);
143 attribute.start_touch.Assign(1);
144 }
145 fingers[finger_id].position = {x, y};
146 fingers[finger_id].attribute = attribute;
147 return finger_id;
148 }
149
150 if (finger_id != MAX_FINGERS) {
151 if (!fingers[finger_id].attribute.end_touch) {
152 fingers[finger_id].attribute.end_touch.Assign(1);
153 fingers[finger_id].attribute.start_touch.Assign(0);
154 return finger_id;
155 }
156 fingers[finger_id].pressed = false;
157 }
158 107
159 return MAX_FINGERS; 108 touch_screen_lifo.WriteNextEntry(next_state);
109 std::memcpy(data + SHARED_MEMORY_OFFSET, &touch_screen_lifo, sizeof(touch_screen_lifo));
160} 110}
161 111
162} // namespace Service::HID 112} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 8e9b40c0a..bcf79237d 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -9,18 +9,22 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/point.h" 10#include "common/point.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/frontend/input.h" 12#include "core/hid/hid_core.h"
13#include "core/hid/hid_types.h"
13#include "core/hle/service/hid/controllers/controller_base.h" 14#include "core/hle/service/hid/controllers/controller_base.h"
15#include "core/hle/service/hid/ring_lifo.h"
14 16
15namespace Service::HID { 17namespace Service::HID {
16class Controller_Touchscreen final : public ControllerBase { 18class Controller_Touchscreen final : public ControllerBase {
17public: 19public:
20 // This is nn::hid::TouchScreenModeForNx
18 enum class TouchScreenModeForNx : u8 { 21 enum class TouchScreenModeForNx : u8 {
19 UseSystemSetting, 22 UseSystemSetting,
20 Finger, 23 Finger,
21 Heat2, 24 Heat2,
22 }; 25 };
23 26
27 // This is nn::hid::TouchScreenConfigurationForNx
24 struct TouchScreenConfigurationForNx { 28 struct TouchScreenConfigurationForNx {
25 TouchScreenModeForNx mode; 29 TouchScreenModeForNx mode;
26 INSERT_PADDING_BYTES_NOINIT(0x7); 30 INSERT_PADDING_BYTES_NOINIT(0x7);
@@ -41,73 +45,32 @@ public:
41 // When the controller is requesting an update for the shared memory 45 // When the controller is requesting an update for the shared memory
42 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 46 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
43 47
44 // Called when input devices should be loaded
45 void OnLoadInputDevices() override;
46
47private: 48private:
48 static constexpr std::size_t MAX_FINGERS = 16; 49 static constexpr std::size_t MAX_FINGERS = 16;
49 50
50 // Returns an unused finger id, if there is no fingers available std::nullopt will be returned 51 // This is nn::hid::TouchScreenState
51 std::optional<std::size_t> GetUnusedFingerID() const; 52 struct TouchScreenState {
52
53 // If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no
54 // changes will be made. Updates the coordinates if the finger id it's already set. If the touch
55 // ends delays the output by one frame to set the end_touch flag before finally freeing the
56 // finger id
57 std::size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
58 std::size_t finger_id);
59
60 struct Attributes {
61 union {
62 u32 raw{};
63 BitField<0, 1, u32> start_touch;
64 BitField<1, 1, u32> end_touch;
65 };
66 };
67 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
68
69 struct TouchState {
70 u64_le delta_time;
71 Attributes attribute;
72 u32_le finger;
73 Common::Point<u32_le> position;
74 u32_le diameter_x;
75 u32_le diameter_y;
76 u32_le rotation_angle;
77 };
78 static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
79
80 struct TouchScreenEntry {
81 s64_le sampling_number; 53 s64_le sampling_number;
82 s64_le sampling_number2;
83 s32_le entry_count; 54 s32_le entry_count;
84 std::array<TouchState, MAX_FINGERS> states; 55 INSERT_PADDING_BYTES(4); // Reserved
56 std::array<Core::HID::TouchState, MAX_FINGERS> states;
85 }; 57 };
86 static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size"); 58 static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
87
88 struct TouchScreenSharedMemory {
89 CommonHeader header;
90 std::array<TouchScreenEntry, 17> shared_memory_entries{};
91 INSERT_PADDING_BYTES(0x3c8);
92 };
93 static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
94 "TouchScreenSharedMemory is an invalid size");
95 59
96 struct Finger { 60 struct Finger {
97 u64_le last_touch{}; 61 u64_le last_touch{};
98 Common::Point<float> position; 62 Common::Point<float> position;
99 u32_le id{}; 63 u32_le id{};
100 bool pressed{}; 64 bool pressed{};
101 Attributes attribute; 65 Core::HID::TouchAttribute attribute;
102 }; 66 };
103 67
104 TouchScreenSharedMemory shared_memory{}; 68 // This is nn::hid::detail::TouchScreenLifo
105 std::unique_ptr<Input::TouchDevice> touch_mouse_device; 69 Lifo<TouchScreenState> touch_screen_lifo{};
106 std::unique_ptr<Input::TouchDevice> touch_udp_device; 70 static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
107 std::unique_ptr<Input::TouchDevice> touch_btn_device; 71 TouchScreenState next_state{};
108 std::array<std::size_t, MAX_FINGERS> mouse_finger_id; 72
109 std::array<std::size_t, MAX_FINGERS> keyboard_finger_id;
110 std::array<std::size_t, MAX_FINGERS> udp_finger_id;
111 std::array<Finger, MAX_FINGERS> fingers; 73 std::array<Finger, MAX_FINGERS> fingers;
74 Core::HID::EmulatedConsole* console;
112}; 75};
113} // namespace Service::HID 76} // namespace Service::HID