gesture.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <cstring>
  5. #include "common/common_types.h"
  6. #include "common/logging/log.h"
  7. #include "common/settings.h"
  8. #include "core/core_timing.h"
  9. #include "core/frontend/emu_window.h"
  10. #include "core/hle/service/hid/controllers/gesture.h"
  11. namespace Service::HID {
  12. constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
  13. constexpr f32 angle_threshold = 0.08f;
  14. constexpr f32 pinch_threshold = 100.0f;
  15. Controller_Gesture::Controller_Gesture(Core::System& system) : ControllerBase(system) {}
  16. Controller_Gesture::~Controller_Gesture() = default;
  17. void Controller_Gesture::OnInit() {
  18. for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
  19. mouse_finger_id[id] = MAX_POINTS;
  20. keyboard_finger_id[id] = MAX_POINTS;
  21. udp_finger_id[id] = MAX_POINTS;
  22. }
  23. }
  24. void Controller_Gesture::OnRelease() {}
  25. void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
  26. std::size_t size) {
  27. shared_memory.header.timestamp = core_timing.GetCPUTicks();
  28. shared_memory.header.total_entry_count = 17;
  29. if (!IsControllerActivated() || !Settings::values.touchscreen.enabled) {
  30. shared_memory.header.entry_count = 0;
  31. shared_memory.header.last_entry_index = 0;
  32. return;
  33. }
  34. shared_memory.header.entry_count = 16;
  35. const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
  36. shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
  37. auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
  38. cur_entry.sampling_number = last_entry.sampling_number + 1;
  39. cur_entry.sampling_number2 = cur_entry.sampling_number;
  40. // TODO(german77): Implement all gesture types
  41. const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus();
  42. const Input::TouchStatus& udp_status = touch_udp_device->GetStatus();
  43. for (std::size_t id = 0; id < mouse_status.size(); ++id) {
  44. mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
  45. udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
  46. }
  47. if (Settings::values.use_touch_from_button) {
  48. const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
  49. for (std::size_t id = 0; id < mouse_status.size(); ++id) {
  50. keyboard_finger_id[id] =
  51. UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
  52. }
  53. }
  54. TouchType type = TouchType::Idle;
  55. Attribute attributes{};
  56. GestureProperties gesture = GetGestureProperties();
  57. if (last_gesture.active_points != gesture.active_points) {
  58. ++last_gesture.detection_count;
  59. }
  60. if (gesture.active_points > 0) {
  61. if (last_gesture.active_points == 0) {
  62. attributes.is_new_touch.Assign(true);
  63. last_gesture.average_distance = gesture.average_distance;
  64. last_gesture.angle = gesture.angle;
  65. }
  66. type = TouchType::Touch;
  67. if (gesture.mid_point.x != last_entry.x || gesture.mid_point.y != last_entry.y) {
  68. type = TouchType::Pan;
  69. }
  70. if (std::abs(gesture.average_distance - last_gesture.average_distance) > pinch_threshold) {
  71. type = TouchType::Pinch;
  72. }
  73. if (std::abs(gesture.angle - last_gesture.angle) > angle_threshold) {
  74. type = TouchType::Rotate;
  75. }
  76. cur_entry.delta_x = gesture.mid_point.x - last_entry.x;
  77. cur_entry.delta_y = gesture.mid_point.y - last_entry.y;
  78. // TODO: Find how velocities are calculated
  79. cur_entry.vel_x = static_cast<float>(cur_entry.delta_x) * 150.1f;
  80. cur_entry.vel_y = static_cast<float>(cur_entry.delta_y) * 150.1f;
  81. // Slowdown the rate of change for less flapping
  82. last_gesture.average_distance =
  83. (last_gesture.average_distance * 0.9f) + (gesture.average_distance * 0.1f);
  84. last_gesture.angle = (last_gesture.angle * 0.9f) + (gesture.angle * 0.1f);
  85. } else {
  86. cur_entry.delta_x = 0;
  87. cur_entry.delta_y = 0;
  88. cur_entry.vel_x = 0;
  89. cur_entry.vel_y = 0;
  90. }
  91. last_gesture.active_points = gesture.active_points;
  92. cur_entry.detection_count = last_gesture.detection_count;
  93. cur_entry.type = type;
  94. cur_entry.attributes = attributes;
  95. cur_entry.x = gesture.mid_point.x;
  96. cur_entry.y = gesture.mid_point.y;
  97. cur_entry.point_count = static_cast<s32>(gesture.active_points);
  98. for (size_t id = 0; id < MAX_POINTS; id++) {
  99. cur_entry.points[id].x = gesture.points[id].x;
  100. cur_entry.points[id].y = gesture.points[id].y;
  101. }
  102. cur_entry.rotation_angle = 0;
  103. cur_entry.scale = 0;
  104. std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
  105. }
  106. void Controller_Gesture::OnLoadInputDevices() {
  107. touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
  108. touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
  109. touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
  110. }
  111. std::optional<std::size_t> Controller_Gesture::GetUnusedFingerID() const {
  112. std::size_t first_free_id = 0;
  113. while (first_free_id < MAX_POINTS) {
  114. if (!fingers[first_free_id].pressed) {
  115. return first_free_id;
  116. } else {
  117. first_free_id++;
  118. }
  119. }
  120. return std::nullopt;
  121. }
  122. std::size_t Controller_Gesture::UpdateTouchInputEvent(
  123. const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
  124. const auto& [x, y, pressed] = touch_input;
  125. if (finger_id > MAX_POINTS) {
  126. LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
  127. return MAX_POINTS;
  128. }
  129. if (pressed) {
  130. if (finger_id == MAX_POINTS) {
  131. const auto first_free_id = GetUnusedFingerID();
  132. if (!first_free_id) {
  133. // Invalid finger id do nothing
  134. return MAX_POINTS;
  135. }
  136. finger_id = first_free_id.value();
  137. fingers[finger_id].pressed = true;
  138. }
  139. fingers[finger_id].x = x;
  140. fingers[finger_id].y = y;
  141. return finger_id;
  142. }
  143. if (finger_id != MAX_POINTS) {
  144. fingers[finger_id].pressed = false;
  145. }
  146. return MAX_POINTS;
  147. }
  148. Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
  149. GestureProperties gesture;
  150. std::array<Finger, MAX_POINTS> active_fingers;
  151. const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
  152. [](const auto& finger) { return finger.pressed; });
  153. gesture.active_points =
  154. static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
  155. for (size_t id = 0; id < gesture.active_points; ++id) {
  156. gesture.points[id].x =
  157. static_cast<int>(active_fingers[id].x * Layout::ScreenUndocked::Width);
  158. gesture.points[id].y =
  159. static_cast<int>(active_fingers[id].y * Layout::ScreenUndocked::Height);
  160. gesture.mid_point.x += static_cast<int>(gesture.points[id].x / gesture.active_points);
  161. gesture.mid_point.y += static_cast<int>(gesture.points[id].y / gesture.active_points);
  162. }
  163. for (size_t id = 0; id < gesture.active_points; ++id) {
  164. const double distance =
  165. std::pow(static_cast<float>(gesture.mid_point.x - gesture.points[id].x), 2) +
  166. std::pow(static_cast<float>(gesture.mid_point.y - gesture.points[id].y), 2);
  167. gesture.average_distance +=
  168. static_cast<float>(distance) / static_cast<float>(gesture.active_points);
  169. }
  170. gesture.angle = std::atan2(static_cast<float>(gesture.mid_point.y - gesture.points[0].y),
  171. static_cast<float>(gesture.mid_point.x - gesture.points[0].x));
  172. return gesture;
  173. }
  174. } // namespace Service::HID