gesture.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "common/logging/log.h"
  4. #include "common/math_util.h"
  5. #include "common/settings.h"
  6. #include "core/core_timing.h"
  7. #include "core/frontend/emu_window.h"
  8. #include "core/hid/hid_core.h"
  9. #include "core/hle/service/hid/controllers/gesture.h"
  10. namespace Service::HID {
  11. constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
  12. // HW is around 700, value is set to 400 to make it easier to trigger with mouse
  13. constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s
  14. constexpr f32 angle_threshold = 0.015f; // Threshold in radians
  15. constexpr f32 pinch_threshold = 0.5f; // Threshold in pixels
  16. constexpr f32 press_delay = 0.5f; // Time in seconds
  17. constexpr f32 double_tap_delay = 0.35f; // Time in seconds
  18. constexpr f32 Square(s32 num) {
  19. return static_cast<f32>(num * num);
  20. }
  21. Gesture::Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
  22. : ControllerBase(hid_core_) {
  23. static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size,
  24. "GestureSharedMemory is bigger than the shared memory");
  25. shared_memory = std::construct_at(
  26. reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
  27. console = hid_core.GetEmulatedConsole();
  28. }
  29. Gesture::~Gesture() = default;
  30. void Gesture::OnInit() {
  31. shared_memory->gesture_lifo.buffer_count = 0;
  32. shared_memory->gesture_lifo.buffer_tail = 0;
  33. force_update = true;
  34. }
  35. void Gesture::OnRelease() {}
  36. void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
  37. if (!IsControllerActivated()) {
  38. shared_memory->gesture_lifo.buffer_count = 0;
  39. shared_memory->gesture_lifo.buffer_tail = 0;
  40. return;
  41. }
  42. ReadTouchInput();
  43. GestureProperties gesture = GetGestureProperties();
  44. f32 time_difference =
  45. static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) /
  46. (1000 * 1000 * 1000);
  47. // Only update if necessary
  48. if (!ShouldUpdateGesture(gesture, time_difference)) {
  49. return;
  50. }
  51. last_update_timestamp = shared_memory->gesture_lifo.timestamp;
  52. UpdateGestureSharedMemory(gesture, time_difference);
  53. }
  54. void Gesture::ReadTouchInput() {
  55. if (!Settings::values.touchscreen.enabled) {
  56. fingers = {};
  57. return;
  58. }
  59. const auto touch_status = console->GetTouch();
  60. for (std::size_t id = 0; id < fingers.size(); ++id) {
  61. fingers[id] = touch_status[id];
  62. }
  63. }
  64. bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) {
  65. const auto& last_entry = GetLastGestureEntry();
  66. if (force_update) {
  67. force_update = false;
  68. return true;
  69. }
  70. // Update if coordinates change
  71. for (size_t id = 0; id < MAX_POINTS; id++) {
  72. if (gesture.points[id] != last_gesture.points[id]) {
  73. return true;
  74. }
  75. }
  76. // Update on press and hold event after 0.5 seconds
  77. if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 &&
  78. time_difference > press_delay) {
  79. return enable_press_and_tap;
  80. }
  81. return false;
  82. }
  83. void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) {
  84. GestureType type = GestureType::Idle;
  85. GestureAttribute attributes{};
  86. const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state;
  87. // Reset next state to default
  88. next_state.sampling_number = last_entry.sampling_number + 1;
  89. next_state.delta = {};
  90. next_state.vel_x = 0;
  91. next_state.vel_y = 0;
  92. next_state.direction = GestureDirection::None;
  93. next_state.rotation_angle = 0;
  94. next_state.scale = 0;
  95. if (gesture.active_points > 0) {
  96. if (last_gesture.active_points == 0) {
  97. NewGesture(gesture, type, attributes);
  98. } else {
  99. UpdateExistingGesture(gesture, type, time_difference);
  100. }
  101. } else {
  102. EndGesture(gesture, last_gesture, type, attributes, time_difference);
  103. }
  104. // Apply attributes
  105. next_state.detection_count = gesture.detection_count;
  106. next_state.type = type;
  107. next_state.attributes = attributes;
  108. next_state.pos = gesture.mid_point;
  109. next_state.point_count = static_cast<s32>(gesture.active_points);
  110. next_state.points = gesture.points;
  111. last_gesture = gesture;
  112. shared_memory->gesture_lifo.WriteNextEntry(next_state);
  113. }
  114. void Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
  115. GestureAttribute& attributes) {
  116. const auto& last_entry = GetLastGestureEntry();
  117. gesture.detection_count++;
  118. type = GestureType::Touch;
  119. // New touch after cancel is not considered new
  120. if (last_entry.type != GestureType::Cancel) {
  121. attributes.is_new_touch.Assign(1);
  122. enable_press_and_tap = true;
  123. }
  124. }
  125. void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
  126. f32 time_difference) {
  127. const auto& last_entry = GetLastGestureEntry();
  128. // Promote to pan type if touch moved
  129. for (size_t id = 0; id < MAX_POINTS; id++) {
  130. if (gesture.points[id] != last_gesture.points[id]) {
  131. type = GestureType::Pan;
  132. break;
  133. }
  134. }
  135. // Number of fingers changed cancel the last event and clear data
  136. if (gesture.active_points != last_gesture.active_points) {
  137. type = GestureType::Cancel;
  138. enable_press_and_tap = false;
  139. gesture.active_points = 0;
  140. gesture.mid_point = {};
  141. gesture.points.fill({});
  142. return;
  143. }
  144. // Calculate extra parameters of panning
  145. if (type == GestureType::Pan) {
  146. UpdatePanEvent(gesture, last_gesture, type, time_difference);
  147. return;
  148. }
  149. // Promote to press type
  150. if (last_entry.type == GestureType::Touch) {
  151. type = GestureType::Press;
  152. }
  153. }
  154. void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
  155. GestureType& type, GestureAttribute& attributes, f32 time_difference) {
  156. const auto& last_entry = GetLastGestureEntry();
  157. if (last_gesture_props.active_points != 0) {
  158. switch (last_entry.type) {
  159. case GestureType::Touch:
  160. if (enable_press_and_tap) {
  161. SetTapEvent(gesture, last_gesture_props, type, attributes);
  162. return;
  163. }
  164. type = GestureType::Cancel;
  165. force_update = true;
  166. break;
  167. case GestureType::Press:
  168. case GestureType::Tap:
  169. case GestureType::Swipe:
  170. case GestureType::Pinch:
  171. case GestureType::Rotate:
  172. type = GestureType::Complete;
  173. force_update = true;
  174. break;
  175. case GestureType::Pan:
  176. EndPanEvent(gesture, last_gesture_props, type, time_difference);
  177. break;
  178. default:
  179. break;
  180. }
  181. return;
  182. }
  183. if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) {
  184. gesture.detection_count++;
  185. }
  186. }
  187. void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
  188. GestureType& type, GestureAttribute& attributes) {
  189. type = GestureType::Tap;
  190. gesture = last_gesture_props;
  191. force_update = true;
  192. f32 tap_time_difference =
  193. static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000);
  194. last_tap_timestamp = last_update_timestamp;
  195. if (tap_time_difference < double_tap_delay) {
  196. attributes.is_double_tap.Assign(1);
  197. }
  198. }
  199. void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
  200. GestureType& type, f32 time_difference) {
  201. const auto& last_entry = GetLastGestureEntry();
  202. next_state.delta = gesture.mid_point - last_entry.pos;
  203. next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
  204. next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
  205. last_pan_time_difference = time_difference;
  206. // Promote to pinch type
  207. if (std::abs(gesture.average_distance - last_gesture_props.average_distance) >
  208. pinch_threshold) {
  209. type = GestureType::Pinch;
  210. next_state.scale = gesture.average_distance / last_gesture_props.average_distance;
  211. }
  212. const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) /
  213. (1 + (gesture.angle * last_gesture_props.angle)));
  214. // Promote to rotate type
  215. if (std::abs(angle_between_two_lines) > angle_threshold) {
  216. type = GestureType::Rotate;
  217. next_state.scale = 0;
  218. next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
  219. }
  220. }
  221. void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
  222. GestureType& type, f32 time_difference) {
  223. const auto& last_entry = GetLastGestureEntry();
  224. next_state.vel_x =
  225. static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
  226. next_state.vel_y =
  227. static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference);
  228. const f32 curr_vel =
  229. std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
  230. // Set swipe event with parameters
  231. if (curr_vel > swipe_threshold) {
  232. SetSwipeEvent(gesture, last_gesture_props, type);
  233. return;
  234. }
  235. // End panning without swipe
  236. type = GestureType::Complete;
  237. next_state.vel_x = 0;
  238. next_state.vel_y = 0;
  239. force_update = true;
  240. }
  241. void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
  242. GestureType& type) {
  243. const auto& last_entry = GetLastGestureEntry();
  244. type = GestureType::Swipe;
  245. gesture = last_gesture_props;
  246. force_update = true;
  247. next_state.delta = last_entry.delta;
  248. if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
  249. if (next_state.delta.x > 0) {
  250. next_state.direction = GestureDirection::Right;
  251. return;
  252. }
  253. next_state.direction = GestureDirection::Left;
  254. return;
  255. }
  256. if (next_state.delta.y > 0) {
  257. next_state.direction = GestureDirection::Down;
  258. return;
  259. }
  260. next_state.direction = GestureDirection::Up;
  261. }
  262. const Gesture::GestureState& Gesture::GetLastGestureEntry() const {
  263. return shared_memory->gesture_lifo.ReadCurrentEntry().state;
  264. }
  265. Gesture::GestureProperties Gesture::GetGestureProperties() {
  266. GestureProperties gesture;
  267. std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
  268. const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
  269. [](const auto& finger) { return finger.pressed; });
  270. gesture.active_points =
  271. static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
  272. for (size_t id = 0; id < gesture.active_points; ++id) {
  273. const auto& [active_x, active_y] = active_fingers[id].position;
  274. gesture.points[id] = {
  275. .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width),
  276. .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height),
  277. };
  278. // Hack: There is no touch in docked but games still allow it
  279. if (Settings::IsDockedMode()) {
  280. gesture.points[id] = {
  281. .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width),
  282. .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height),
  283. };
  284. }
  285. gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points);
  286. gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points);
  287. }
  288. for (size_t id = 0; id < gesture.active_points; ++id) {
  289. const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) +
  290. Square(gesture.mid_point.y - gesture.points[id].y));
  291. gesture.average_distance += distance / static_cast<f32>(gesture.active_points);
  292. }
  293. gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y),
  294. static_cast<f32>(gesture.mid_point.x - gesture.points[0].x));
  295. gesture.detection_count = last_gesture.detection_count;
  296. return gesture;
  297. }
  298. } // namespace Service::HID