gesture.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "common/logging/log.h"
  5. #include "common/math_util.h"
  6. #include "common/settings.h"
  7. #include "core/core_timing.h"
  8. #include "core/frontend/emu_window.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::System& system_) : ControllerBase(system_) {}
  22. Controller_Gesture::~Controller_Gesture() = default;
  23. void Controller_Gesture::OnInit() {
  24. for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
  25. mouse_finger_id[id] = MAX_POINTS;
  26. keyboard_finger_id[id] = MAX_POINTS;
  27. udp_finger_id[id] = MAX_POINTS;
  28. }
  29. shared_memory.header.entry_count = 0;
  30. force_update = true;
  31. }
  32. void Controller_Gesture::OnRelease() {}
  33. void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
  34. std::size_t size) {
  35. shared_memory.header.timestamp = core_timing.GetCPUTicks();
  36. shared_memory.header.total_entry_count = 17;
  37. if (!IsControllerActivated()) {
  38. shared_memory.header.entry_count = 0;
  39. shared_memory.header.last_entry_index = 0;
  40. return;
  41. }
  42. ReadTouchInput();
  43. GestureProperties gesture = GetGestureProperties();
  44. f32 time_difference = static_cast<f32>(shared_memory.header.timestamp - last_update_timestamp) /
  45. (1000 * 1000 * 1000);
  46. // Only update if necesary
  47. if (!ShouldUpdateGesture(gesture, time_difference)) {
  48. return;
  49. }
  50. last_update_timestamp = shared_memory.header.timestamp;
  51. UpdateGestureSharedMemory(data, size, gesture, time_difference);
  52. }
  53. void Controller_Gesture::ReadTouchInput() {
  54. const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus();
  55. const Input::TouchStatus& udp_status = touch_udp_device->GetStatus();
  56. for (std::size_t id = 0; id < mouse_status.size(); ++id) {
  57. mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
  58. udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
  59. }
  60. if (Settings::values.use_touch_from_button) {
  61. const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
  62. for (std::size_t id = 0; id < mouse_status.size(); ++id) {
  63. keyboard_finger_id[id] =
  64. UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
  65. }
  66. }
  67. }
  68. bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
  69. f32 time_difference) {
  70. const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
  71. if (force_update) {
  72. force_update = false;
  73. return true;
  74. }
  75. // Update if coordinates change
  76. for (size_t id = 0; id < MAX_POINTS; id++) {
  77. if (gesture.points[id] != last_gesture.points[id]) {
  78. return true;
  79. }
  80. }
  81. // Update on press and hold event after 0.5 seconds
  82. if (last_entry.type == TouchType::Touch && last_entry.point_count == 1 &&
  83. time_difference > press_delay) {
  84. return enable_press_and_tap;
  85. }
  86. return false;
  87. }
  88. void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
  89. GestureProperties& gesture,
  90. f32 time_difference) {
  91. TouchType type = TouchType::Idle;
  92. Attribute attributes{};
  93. const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
  94. shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
  95. auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
  96. if (shared_memory.header.entry_count < 16) {
  97. shared_memory.header.entry_count++;
  98. }
  99. cur_entry.sampling_number = last_entry.sampling_number + 1;
  100. cur_entry.sampling_number2 = cur_entry.sampling_number;
  101. // Reset values to default
  102. cur_entry.delta_x = 0;
  103. cur_entry.delta_y = 0;
  104. cur_entry.vel_x = 0;
  105. cur_entry.vel_y = 0;
  106. cur_entry.direction = Direction::None;
  107. cur_entry.rotation_angle = 0;
  108. cur_entry.scale = 0;
  109. if (gesture.active_points > 0) {
  110. if (last_gesture.active_points == 0) {
  111. NewGesture(gesture, type, attributes);
  112. } else {
  113. UpdateExistingGesture(gesture, type, time_difference);
  114. }
  115. } else {
  116. EndGesture(gesture, last_gesture, type, attributes, time_difference);
  117. }
  118. // Apply attributes
  119. cur_entry.detection_count = gesture.detection_count;
  120. cur_entry.type = type;
  121. cur_entry.attributes = attributes;
  122. cur_entry.pos = gesture.mid_point;
  123. cur_entry.point_count = static_cast<s32>(gesture.active_points);
  124. for (size_t id = 0; id < MAX_POINTS; id++) {
  125. cur_entry.points[id].x = gesture.points[id].x;
  126. cur_entry.points[id].y = gesture.points[id].y;
  127. }
  128. last_gesture = gesture;
  129. std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
  130. }
  131. void Controller_Gesture::NewGesture(GestureProperties& gesture, TouchType& type,
  132. Attribute& attributes) {
  133. const auto& last_entry =
  134. shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
  135. gesture.detection_count++;
  136. type = TouchType::Touch;
  137. // New touch after cancel is not considered new
  138. if (last_entry.type != TouchType::Cancel) {
  139. attributes.is_new_touch.Assign(1);
  140. enable_press_and_tap = true;
  141. }
  142. }
  143. void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, TouchType& type,
  144. f32 time_difference) {
  145. const auto& last_entry =
  146. shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
  147. // Promote to pan type if touch moved
  148. for (size_t id = 0; id < MAX_POINTS; id++) {
  149. if (gesture.points[id] != last_gesture.points[id]) {
  150. type = TouchType::Pan;
  151. break;
  152. }
  153. }
  154. // Number of fingers changed cancel the last event and clear data
  155. if (gesture.active_points != last_gesture.active_points) {
  156. type = TouchType::Cancel;
  157. enable_press_and_tap = false;
  158. gesture.active_points = 0;
  159. gesture.mid_point = {};
  160. gesture.points.fill({});
  161. return;
  162. }
  163. // Calculate extra parameters of panning
  164. if (type == TouchType::Pan) {
  165. UpdatePanEvent(gesture, last_gesture, type, time_difference);
  166. return;
  167. }
  168. // Promote to press type
  169. if (last_entry.type == TouchType::Touch) {
  170. type = TouchType::Press;
  171. }
  172. }
  173. void Controller_Gesture::EndGesture(GestureProperties& gesture,
  174. GestureProperties& last_gesture_props, TouchType& type,
  175. Attribute& attributes, f32 time_difference) {
  176. const auto& last_entry =
  177. shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
  178. if (last_gesture_props.active_points != 0) {
  179. switch (last_entry.type) {
  180. case TouchType::Touch:
  181. if (enable_press_and_tap) {
  182. SetTapEvent(gesture, last_gesture_props, type, attributes);
  183. return;
  184. }
  185. type = TouchType::Cancel;
  186. force_update = true;
  187. break;
  188. case TouchType::Press:
  189. case TouchType::Tap:
  190. case TouchType::Swipe:
  191. case TouchType::Pinch:
  192. case TouchType::Rotate:
  193. type = TouchType::Complete;
  194. force_update = true;
  195. break;
  196. case TouchType::Pan:
  197. EndPanEvent(gesture, last_gesture_props, type, time_difference);
  198. break;
  199. default:
  200. break;
  201. }
  202. return;
  203. }
  204. if (last_entry.type == TouchType::Complete || last_entry.type == TouchType::Cancel) {
  205. gesture.detection_count++;
  206. }
  207. }
  208. void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
  209. GestureProperties& last_gesture_props, TouchType& type,
  210. Attribute& attributes) {
  211. type = TouchType::Tap;
  212. gesture = last_gesture_props;
  213. force_update = true;
  214. f32 tap_time_difference =
  215. static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000);
  216. last_tap_timestamp = last_update_timestamp;
  217. if (tap_time_difference < double_tap_delay) {
  218. attributes.is_double_tap.Assign(1);
  219. }
  220. }
  221. void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
  222. GestureProperties& last_gesture_props, TouchType& type,
  223. f32 time_difference) {
  224. auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
  225. const auto& last_entry =
  226. shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
  227. cur_entry.delta_x = gesture.mid_point.x - last_entry.pos.x;
  228. cur_entry.delta_y = gesture.mid_point.y - last_entry.pos.y;
  229. cur_entry.vel_x = static_cast<f32>(cur_entry.delta_x) / time_difference;
  230. cur_entry.vel_y = static_cast<f32>(cur_entry.delta_y) / time_difference;
  231. last_pan_time_difference = time_difference;
  232. // Promote to pinch type
  233. if (std::abs(gesture.average_distance - last_gesture_props.average_distance) >
  234. pinch_threshold) {
  235. type = TouchType::Pinch;
  236. cur_entry.scale = gesture.average_distance / last_gesture_props.average_distance;
  237. }
  238. const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) /
  239. (1 + (gesture.angle * last_gesture_props.angle)));
  240. // Promote to rotate type
  241. if (std::abs(angle_between_two_lines) > angle_threshold) {
  242. type = TouchType::Rotate;
  243. cur_entry.scale = 0;
  244. cur_entry.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
  245. }
  246. }
  247. void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
  248. GestureProperties& last_gesture_props, TouchType& type,
  249. f32 time_difference) {
  250. auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
  251. const auto& last_entry =
  252. shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
  253. cur_entry.vel_x =
  254. static_cast<f32>(last_entry.delta_x) / (last_pan_time_difference + time_difference);
  255. cur_entry.vel_y =
  256. static_cast<f32>(last_entry.delta_y) / (last_pan_time_difference + time_difference);
  257. const f32 curr_vel =
  258. std::sqrt((cur_entry.vel_x * cur_entry.vel_x) + (cur_entry.vel_y * cur_entry.vel_y));
  259. // Set swipe event with parameters
  260. if (curr_vel > swipe_threshold) {
  261. SetSwipeEvent(gesture, last_gesture_props, type);
  262. return;
  263. }
  264. // End panning without swipe
  265. type = TouchType::Complete;
  266. cur_entry.vel_x = 0;
  267. cur_entry.vel_y = 0;
  268. force_update = true;
  269. }
  270. void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
  271. GestureProperties& last_gesture_props, TouchType& type) {
  272. auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
  273. const auto& last_entry =
  274. shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
  275. type = TouchType::Swipe;
  276. gesture = last_gesture_props;
  277. force_update = true;
  278. cur_entry.delta_x = last_entry.delta_x;
  279. cur_entry.delta_y = last_entry.delta_y;
  280. if (std::abs(cur_entry.delta_x) > std::abs(cur_entry.delta_y)) {
  281. if (cur_entry.delta_x > 0) {
  282. cur_entry.direction = Direction::Right;
  283. return;
  284. }
  285. cur_entry.direction = Direction::Left;
  286. return;
  287. }
  288. if (cur_entry.delta_y > 0) {
  289. cur_entry.direction = Direction::Down;
  290. return;
  291. }
  292. cur_entry.direction = Direction::Up;
  293. }
  294. void Controller_Gesture::OnLoadInputDevices() {
  295. touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
  296. touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
  297. touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
  298. }
  299. std::optional<std::size_t> Controller_Gesture::GetUnusedFingerID() const {
  300. // Dont assign any touch input to a point if disabled
  301. if (!Settings::values.touchscreen.enabled) {
  302. return std::nullopt;
  303. }
  304. std::size_t first_free_id = 0;
  305. while (first_free_id < MAX_POINTS) {
  306. if (!fingers[first_free_id].pressed) {
  307. return first_free_id;
  308. } else {
  309. first_free_id++;
  310. }
  311. }
  312. return std::nullopt;
  313. }
  314. std::size_t Controller_Gesture::UpdateTouchInputEvent(
  315. const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
  316. const auto& [x, y, pressed] = touch_input;
  317. if (finger_id > MAX_POINTS) {
  318. LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
  319. return MAX_POINTS;
  320. }
  321. if (pressed) {
  322. if (finger_id == MAX_POINTS) {
  323. const auto first_free_id = GetUnusedFingerID();
  324. if (!first_free_id) {
  325. // Invalid finger id do nothing
  326. return MAX_POINTS;
  327. }
  328. finger_id = first_free_id.value();
  329. fingers[finger_id].pressed = true;
  330. }
  331. fingers[finger_id].x = x;
  332. fingers[finger_id].y = y;
  333. return finger_id;
  334. }
  335. if (finger_id != MAX_POINTS) {
  336. fingers[finger_id].pressed = false;
  337. }
  338. return MAX_POINTS;
  339. }
  340. Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
  341. GestureProperties gesture;
  342. std::array<Finger, MAX_POINTS> active_fingers;
  343. const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
  344. [](const auto& finger) { return finger.pressed; });
  345. gesture.active_points =
  346. static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
  347. for (size_t id = 0; id < gesture.active_points; ++id) {
  348. gesture.points[id].x =
  349. static_cast<s32>(active_fingers[id].x * Layout::ScreenUndocked::Width);
  350. gesture.points[id].y =
  351. static_cast<s32>(active_fingers[id].y * Layout::ScreenUndocked::Height);
  352. // Hack: There is no touch in docked but games still allow it
  353. if (Settings::values.use_docked_mode.GetValue()) {
  354. gesture.points[id].x =
  355. static_cast<s32>(active_fingers[id].x * Layout::ScreenDocked::Width);
  356. gesture.points[id].y =
  357. static_cast<s32>(active_fingers[id].y * Layout::ScreenDocked::Height);
  358. }
  359. gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points);
  360. gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points);
  361. }
  362. for (size_t id = 0; id < gesture.active_points; ++id) {
  363. const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) +
  364. Square(gesture.mid_point.y - gesture.points[id].y));
  365. gesture.average_distance += distance / static_cast<f32>(gesture.active_points);
  366. }
  367. gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y),
  368. static_cast<f32>(gesture.mid_point.x - gesture.points[0].x));
  369. gesture.detection_count = last_gesture.detection_count;
  370. return gesture;
  371. }
  372. } // namespace Service::HID