emulated_console.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "common/settings.h"
  4. #include "core/hid/emulated_console.h"
  5. #include "core/hid/input_converter.h"
  6. namespace Core::HID {
  7. EmulatedConsole::EmulatedConsole() = default;
  8. EmulatedConsole::~EmulatedConsole() = default;
  9. void EmulatedConsole::ReloadFromSettings() {
  10. // Using first motion device from player 1. No need to assign any unique config at the moment
  11. const auto& player = Settings::values.players.GetValue()[0];
  12. motion_params = Common::ParamPackage(player.motions[0]);
  13. ReloadInput();
  14. }
  15. void EmulatedConsole::SetTouchParams() {
  16. std::size_t index = 0;
  17. // We can't use mouse as touch if native mouse is enabled
  18. if (!Settings::values.mouse_enabled) {
  19. touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
  20. }
  21. touch_params[index++] =
  22. Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
  23. touch_params[index++] =
  24. Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
  25. for (int i = 0; i < static_cast<int>(MaxActiveTouchInputs); i++) {
  26. Common::ParamPackage touchscreen_param{};
  27. touchscreen_param.Set("engine", "touch");
  28. touchscreen_param.Set("axis_x", i * 2);
  29. touchscreen_param.Set("axis_y", (i * 2) + 1);
  30. touchscreen_param.Set("button", i);
  31. touch_params[index++] = std::move(touchscreen_param);
  32. }
  33. if (Settings::values.touch_from_button_maps.empty()) {
  34. LOG_WARNING(Input, "touch_from_button_maps is unset by frontend config");
  35. return;
  36. }
  37. const auto button_index =
  38. static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
  39. const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons;
  40. // Map the rest of the fingers from touch from button configuration
  41. for (const auto& config_entry : touch_buttons) {
  42. if (index >= MaxTouchDevices) {
  43. continue;
  44. }
  45. Common::ParamPackage params{config_entry};
  46. Common::ParamPackage touch_button_params;
  47. const int x = params.Get("x", 0);
  48. const int y = params.Get("y", 0);
  49. params.Erase("x");
  50. params.Erase("y");
  51. touch_button_params.Set("engine", "touch_from_button");
  52. touch_button_params.Set("button", params.Serialize());
  53. touch_button_params.Set("x", x);
  54. touch_button_params.Set("y", y);
  55. touch_params[index] = std::move(touch_button_params);
  56. index++;
  57. }
  58. }
  59. void EmulatedConsole::ReloadInput() {
  60. // If you load any device here add the equivalent to the UnloadInput() function
  61. SetTouchParams();
  62. motion_devices = Common::Input::CreateInputDevice(motion_params);
  63. if (motion_devices) {
  64. motion_devices->SetCallback({
  65. .on_change =
  66. [this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); },
  67. });
  68. }
  69. // Unique index for identifying touch device source
  70. std::size_t index = 0;
  71. for (auto& touch_device : touch_devices) {
  72. touch_device = Common::Input::CreateInputDevice(touch_params[index]);
  73. if (!touch_device) {
  74. continue;
  75. }
  76. touch_device->SetCallback({
  77. .on_change =
  78. [this, index](const Common::Input::CallbackStatus& callback) {
  79. SetTouch(callback, index);
  80. },
  81. });
  82. index++;
  83. }
  84. }
  85. void EmulatedConsole::UnloadInput() {
  86. motion_devices.reset();
  87. for (auto& touch : touch_devices) {
  88. touch.reset();
  89. }
  90. }
  91. void EmulatedConsole::EnableConfiguration() {
  92. is_configuring = true;
  93. SaveCurrentConfig();
  94. }
  95. void EmulatedConsole::DisableConfiguration() {
  96. is_configuring = false;
  97. }
  98. bool EmulatedConsole::IsConfiguring() const {
  99. return is_configuring;
  100. }
  101. void EmulatedConsole::SaveCurrentConfig() {
  102. if (!is_configuring) {
  103. return;
  104. }
  105. }
  106. void EmulatedConsole::RestoreConfig() {
  107. if (!is_configuring) {
  108. return;
  109. }
  110. ReloadFromSettings();
  111. }
  112. Common::ParamPackage EmulatedConsole::GetMotionParam() const {
  113. return motion_params;
  114. }
  115. void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
  116. motion_params = std::move(param);
  117. ReloadInput();
  118. }
  119. void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
  120. std::unique_lock lock{mutex};
  121. auto& raw_status = console.motion_values.raw_status;
  122. auto& emulated = console.motion_values.emulated;
  123. raw_status = TransformToMotion(callback);
  124. emulated.SetAcceleration(Common::Vec3f{
  125. raw_status.accel.x.value,
  126. raw_status.accel.y.value,
  127. raw_status.accel.z.value,
  128. });
  129. emulated.SetGyroscope(Common::Vec3f{
  130. raw_status.gyro.x.value,
  131. raw_status.gyro.y.value,
  132. raw_status.gyro.z.value,
  133. });
  134. emulated.UpdateRotation(raw_status.delta_timestamp);
  135. emulated.UpdateOrientation(raw_status.delta_timestamp);
  136. if (is_configuring) {
  137. lock.unlock();
  138. TriggerOnChange(ConsoleTriggerType::Motion);
  139. return;
  140. }
  141. auto& motion = console.motion_state;
  142. motion.accel = emulated.GetAcceleration();
  143. motion.gyro = emulated.GetGyroscope();
  144. motion.rotation = emulated.GetRotations();
  145. motion.orientation = emulated.GetOrientation();
  146. motion.quaternion = emulated.GetQuaternion();
  147. motion.gyro_bias = emulated.GetGyroBias();
  148. motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
  149. // Find what is this value
  150. motion.verticalization_error = 0.0f;
  151. lock.unlock();
  152. TriggerOnChange(ConsoleTriggerType::Motion);
  153. }
  154. void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
  155. if (index >= MaxTouchDevices) {
  156. return;
  157. }
  158. std::unique_lock lock{mutex};
  159. const auto touch_input = TransformToTouch(callback);
  160. auto touch_index = GetIndexFromFingerId(index);
  161. bool is_new_input = false;
  162. if (!touch_index.has_value() && touch_input.pressed.value) {
  163. touch_index = GetNextFreeIndex();
  164. is_new_input = true;
  165. }
  166. // No free entries or invalid state. Ignore input
  167. if (!touch_index.has_value()) {
  168. return;
  169. }
  170. auto& touch_value = console.touch_values[touch_index.value()];
  171. if (is_new_input) {
  172. touch_value.pressed.value = true;
  173. touch_value.id = static_cast<int>(index);
  174. }
  175. touch_value.x = touch_input.x;
  176. touch_value.y = touch_input.y;
  177. if (!touch_input.pressed.value) {
  178. touch_value.pressed.value = false;
  179. }
  180. if (is_configuring) {
  181. lock.unlock();
  182. TriggerOnChange(ConsoleTriggerType::Touch);
  183. return;
  184. }
  185. // Touch outside allowed range. Ignore input
  186. if (touch_index.value() >= MaxActiveTouchInputs) {
  187. return;
  188. }
  189. console.touch_state[touch_index.value()] = {
  190. .position = {touch_value.x.value, touch_value.y.value},
  191. .id = static_cast<u32>(touch_index.value()),
  192. .pressed = touch_input.pressed.value,
  193. };
  194. lock.unlock();
  195. TriggerOnChange(ConsoleTriggerType::Touch);
  196. }
  197. ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
  198. std::scoped_lock lock{mutex};
  199. return console.motion_values;
  200. }
  201. TouchValues EmulatedConsole::GetTouchValues() const {
  202. std::scoped_lock lock{mutex};
  203. return console.touch_values;
  204. }
  205. ConsoleMotion EmulatedConsole::GetMotion() const {
  206. std::scoped_lock lock{mutex};
  207. return console.motion_state;
  208. }
  209. TouchFingerState EmulatedConsole::GetTouch() const {
  210. std::scoped_lock lock{mutex};
  211. return console.touch_state;
  212. }
  213. std::optional<std::size_t> EmulatedConsole::GetIndexFromFingerId(std::size_t finger_id) const {
  214. for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
  215. const auto& finger = console.touch_values[index];
  216. if (!finger.pressed.value) {
  217. continue;
  218. }
  219. if (finger.id == static_cast<int>(finger_id)) {
  220. return index;
  221. }
  222. }
  223. return std::nullopt;
  224. }
  225. std::optional<std::size_t> EmulatedConsole::GetNextFreeIndex() const {
  226. for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
  227. if (!console.touch_values[index].pressed.value) {
  228. return index;
  229. }
  230. }
  231. return std::nullopt;
  232. }
  233. void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
  234. std::scoped_lock lock{callback_mutex};
  235. for (const auto& poller_pair : callback_list) {
  236. const ConsoleUpdateCallback& poller = poller_pair.second;
  237. if (poller.on_change) {
  238. poller.on_change(type);
  239. }
  240. }
  241. }
  242. int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
  243. std::scoped_lock lock{callback_mutex};
  244. callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
  245. return last_callback_key++;
  246. }
  247. void EmulatedConsole::DeleteCallback(int key) {
  248. std::scoped_lock lock{callback_mutex};
  249. const auto& iterator = callback_list.find(key);
  250. if (iterator == callback_list.end()) {
  251. LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
  252. return;
  253. }
  254. callback_list.erase(iterator);
  255. }
  256. } // namespace Core::HID