gesture.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  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. Controller_Gesture::Controller_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. Controller_Gesture::~Controller_Gesture() = default;
  30. void Controller_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 Controller_Gesture::OnRelease() {}
  36. void Controller_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 Controller_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 Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
  65. f32 time_difference) {
  66. const auto& last_entry = GetLastGestureEntry();
  67. if (force_update) {
  68. force_update = false;
  69. return true;
  70. }
  71. // Update if coordinates change
  72. for (size_t id = 0; id < MAX_POINTS; id++) {
  73. if (gesture.points[id] != last_gesture.points[id]) {
  74. return true;
  75. }
  76. }
  77. // Update on press and hold event after 0.5 seconds
  78. if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 &&
  79. time_difference > press_delay) {
  80. return enable_press_and_tap;
  81. }
  82. return false;
  83. }
  84. void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
  85. f32 time_difference) {
  86. GestureType type = GestureType::Idle;
  87. GestureAttribute attributes{};
  88. const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state;
  89. // Reset next state to default
  90. next_state.sampling_number = last_entry.sampling_number + 1;
  91. next_state.delta = {};
  92. next_state.vel_x = 0;
  93. next_state.vel_y = 0;
  94. next_state.direction = GestureDirection::None;
  95. next_state.rotation_angle = 0;
  96. next_state.scale = 0;
  97. if (gesture.active_points > 0) {
  98. if (last_gesture.active_points == 0) {
  99. NewGesture(gesture, type, attributes);
  100. } else {
  101. UpdateExistingGesture(gesture, type, time_difference);
  102. }
  103. } else {
  104. EndGesture(gesture, last_gesture, type, attributes, time_difference);
  105. }
  106. // Apply attributes
  107. next_state.detection_count = gesture.detection_count;
  108. next_state.type = type;
  109. next_state.attributes = attributes;
  110. next_state.pos = gesture.mid_point;
  111. next_state.point_count = static_cast<s32>(gesture.active_points);
  112. next_state.points = gesture.points;
  113. last_gesture = gesture;
  114. shared_memory->gesture_lifo.WriteNextEntry(next_state);
  115. }
  116. void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
  117. GestureAttribute& attributes) {
  118. const auto& last_entry = GetLastGestureEntry();
  119. gesture.detection_count++;
  120. type = GestureType::Touch;
  121. // New touch after cancel is not considered new
  122. if (last_entry.type != GestureType::Cancel) {
  123. attributes.is_new_touch.Assign(1);
  124. enable_press_and_tap = true;
  125. }
  126. }
  127. void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
  128. f32 time_difference) {
  129. const auto& last_entry = GetLastGestureEntry();
  130. // Promote to pan type if touch moved
  131. for (size_t id = 0; id < MAX_POINTS; id++) {
  132. if (gesture.points[id] != last_gesture.points[id]) {
  133. type = GestureType::Pan;
  134. break;
  135. }
  136. }
  137. // Number of fingers changed cancel the last event and clear data
  138. if (gesture.active_points != last_gesture.active_points) {
  139. type = GestureType::Cancel;
  140. enable_press_and_tap = false;
  141. gesture.active_points = 0;
  142. gesture.mid_point = {};
  143. gesture.points.fill({});
  144. return;
  145. }
  146. // Calculate extra parameters of panning
  147. if (type == GestureType::Pan) {
  148. UpdatePanEvent(gesture, last_gesture, type, time_difference);
  149. return;
  150. }
  151. // Promote to press type
  152. if (last_entry.type == GestureType::Touch) {
  153. type = GestureType::Press;
  154. }
  155. }
  156. void Controller_Gesture::EndGesture(GestureProperties& gesture,
  157. GestureProperties& last_gesture_props, GestureType& type,
  158. GestureAttribute& attributes, f32 time_difference) {
  159. const auto& last_entry = GetLastGestureEntry();
  160. if (last_gesture_props.active_points != 0) {
  161. switch (last_entry.type) {
  162. case GestureType::Touch:
  163. if (enable_press_and_tap) {
  164. SetTapEvent(gesture, last_gesture_props, type, attributes);
  165. return;
  166. }
  167. type = GestureType::Cancel;
  168. force_update = true;
  169. break;
  170. case GestureType::Press:
  171. case GestureType::Tap:
  172. case GestureType::Swipe:
  173. case GestureType::Pinch:
  174. case GestureType::Rotate:
  175. type = GestureType::Complete;
  176. force_update = true;
  177. break;
  178. case GestureType::Pan:
  179. EndPanEvent(gesture, last_gesture_props, type, time_difference);
  180. break;
  181. default:
  182. break;
  183. }
  184. return;
  185. }
  186. if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) {
  187. gesture.detection_count++;
  188. }
  189. }
  190. void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
  191. GestureProperties& last_gesture_props, GestureType& type,
  192. GestureAttribute& attributes) {
  193. type = GestureType::Tap;
  194. gesture = last_gesture_props;
  195. force_update = true;
  196. f32 tap_time_difference =
  197. static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000);
  198. last_tap_timestamp = last_update_timestamp;
  199. if (tap_time_difference < double_tap_delay) {
  200. attributes.is_double_tap.Assign(1);
  201. }
  202. }
  203. void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
  204. GestureProperties& last_gesture_props, GestureType& type,
  205. f32 time_difference) {
  206. const auto& last_entry = GetLastGestureEntry();
  207. next_state.delta = gesture.mid_point - last_entry.pos;
  208. next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
  209. next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
  210. last_pan_time_difference = time_difference;
  211. // Promote to pinch type
  212. if (std::abs(gesture.average_distance - last_gesture_props.average_distance) >
  213. pinch_threshold) {
  214. type = GestureType::Pinch;
  215. next_state.scale = gesture.average_distance / last_gesture_props.average_distance;
  216. }
  217. const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) /
  218. (1 + (gesture.angle * last_gesture_props.angle)));
  219. // Promote to rotate type
  220. if (std::abs(angle_between_two_lines) > angle_threshold) {
  221. type = GestureType::Rotate;
  222. next_state.scale = 0;
  223. next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
  224. }
  225. }
  226. void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
  227. GestureProperties& last_gesture_props, GestureType& type,
  228. f32 time_difference) {
  229. const auto& last_entry = GetLastGestureEntry();
  230. next_state.vel_x =
  231. static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
  232. next_state.vel_y =
  233. static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference);
  234. const f32 curr_vel =
  235. std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
  236. // Set swipe event with parameters
  237. if (curr_vel > swipe_threshold) {
  238. SetSwipeEvent(gesture, last_gesture_props, type);
  239. return;
  240. }
  241. // End panning without swipe
  242. type = GestureType::Complete;
  243. next_state.vel_x = 0;
  244. next_state.vel_y = 0;
  245. force_update = true;
  246. }
  247. void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
  248. GestureProperties& last_gesture_props, GestureType& type) {
  249. const auto& last_entry = GetLastGestureEntry();
  250. type = GestureType::Swipe;
  251. gesture = last_gesture_props;
  252. force_update = true;
  253. next_state.delta = last_entry.delta;
  254. if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
  255. if (next_state.delta.x > 0) {
  256. next_state.direction = GestureDirection::Right;
  257. return;
  258. }
  259. next_state.direction = GestureDirection::Left;
  260. return;
  261. }
  262. if (next_state.delta.y > 0) {
  263. next_state.direction = GestureDirection::Down;
  264. return;
  265. }
  266. next_state.direction = GestureDirection::Up;
  267. }
  268. const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const {
  269. return shared_memory->gesture_lifo.ReadCurrentEntry().state;
  270. }
  271. Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
  272. GestureProperties gesture;
  273. std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
  274. const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
  275. [](const auto& finger) { return finger.pressed; });
  276. gesture.active_points =
  277. static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
  278. for (size_t id = 0; id < gesture.active_points; ++id) {
  279. const auto& [active_x, active_y] = active_fingers[id].position;
  280. gesture.points[id] = {
  281. .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width),
  282. .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height),
  283. };
  284. // Hack: There is no touch in docked but games still allow it
  285. if (Settings::IsDockedMode()) {
  286. gesture.points[id] = {
  287. .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width),
  288. .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height),
  289. };
  290. }
  291. gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points);
  292. gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points);
  293. }
  294. for (size_t id = 0; id < gesture.active_points; ++id) {
  295. const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) +
  296. Square(gesture.mid_point.y - gesture.points[id].y));
  297. gesture.average_distance += distance / static_cast<f32>(gesture.active_points);
  298. }
  299. gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y),
  300. static_cast<f32>(gesture.mid_point.x - gesture.points[0].x));
  301. gesture.detection_count = last_gesture.detection_count;
  302. return gesture;
  303. }
  304. } // namespace Service::HID