input_converter.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <random>
  4. #include "common/input.h"
  5. #include "core/hid/input_converter.h"
  6. namespace Core::HID {
  7. Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback) {
  8. Common::Input::BatteryStatus battery{Common::Input::BatteryStatus::None};
  9. switch (callback.type) {
  10. case Common::Input::InputType::Analog:
  11. case Common::Input::InputType::Trigger: {
  12. const auto value = TransformToTrigger(callback).analog.value;
  13. battery = Common::Input::BatteryLevel::Empty;
  14. if (value > 0.2f) {
  15. battery = Common::Input::BatteryLevel::Critical;
  16. }
  17. if (value > 0.4f) {
  18. battery = Common::Input::BatteryLevel::Low;
  19. }
  20. if (value > 0.6f) {
  21. battery = Common::Input::BatteryLevel::Medium;
  22. }
  23. if (value > 0.8f) {
  24. battery = Common::Input::BatteryLevel::Full;
  25. }
  26. if (value >= 0.95f) {
  27. battery = Common::Input::BatteryLevel::Charging;
  28. }
  29. break;
  30. }
  31. case Common::Input::InputType::Button:
  32. battery = callback.button_status.value ? Common::Input::BatteryLevel::Charging
  33. : Common::Input::BatteryLevel::Critical;
  34. break;
  35. case Common::Input::InputType::Battery:
  36. battery = callback.battery_status;
  37. break;
  38. default:
  39. LOG_ERROR(Input, "Conversion from type {} to battery not implemented", callback.type);
  40. break;
  41. }
  42. return battery;
  43. }
  44. Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback) {
  45. Common::Input::ButtonStatus status{};
  46. switch (callback.type) {
  47. case Common::Input::InputType::Analog:
  48. case Common::Input::InputType::Trigger:
  49. status.value = TransformToTrigger(callback).pressed.value;
  50. break;
  51. case Common::Input::InputType::Button:
  52. status = callback.button_status;
  53. break;
  54. default:
  55. LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type);
  56. break;
  57. }
  58. if (status.inverted) {
  59. status.value = !status.value;
  60. }
  61. return status;
  62. }
  63. Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback) {
  64. Common::Input::MotionStatus status{};
  65. switch (callback.type) {
  66. case Common::Input::InputType::Button: {
  67. Common::Input::AnalogProperties properties{
  68. .deadzone = 0.0f,
  69. .range = 1.0f,
  70. .offset = 0.0f,
  71. };
  72. status.delta_timestamp = 5000;
  73. status.force_update = true;
  74. status.accel.x = {
  75. .value = 0.0f,
  76. .raw_value = 0.0f,
  77. .properties = properties,
  78. };
  79. status.accel.y = {
  80. .value = 0.0f,
  81. .raw_value = 0.0f,
  82. .properties = properties,
  83. };
  84. status.accel.z = {
  85. .value = 0.0f,
  86. .raw_value = -1.0f,
  87. .properties = properties,
  88. };
  89. status.gyro.x = {
  90. .value = 0.0f,
  91. .raw_value = 0.0f,
  92. .properties = properties,
  93. };
  94. status.gyro.y = {
  95. .value = 0.0f,
  96. .raw_value = 0.0f,
  97. .properties = properties,
  98. };
  99. status.gyro.z = {
  100. .value = 0.0f,
  101. .raw_value = 0.0f,
  102. .properties = properties,
  103. };
  104. if (TransformToButton(callback).value) {
  105. std::random_device device;
  106. std::mt19937 gen(device());
  107. std::uniform_int_distribution<s16> distribution(-5000, 5000);
  108. status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
  109. status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
  110. status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
  111. status.gyro.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
  112. status.gyro.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
  113. status.gyro.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
  114. }
  115. break;
  116. }
  117. case Common::Input::InputType::Motion:
  118. status = callback.motion_status;
  119. break;
  120. default:
  121. LOG_ERROR(Input, "Conversion from type {} to motion not implemented", callback.type);
  122. break;
  123. }
  124. SanitizeAnalog(status.accel.x, false);
  125. SanitizeAnalog(status.accel.y, false);
  126. SanitizeAnalog(status.accel.z, false);
  127. SanitizeAnalog(status.gyro.x, false);
  128. SanitizeAnalog(status.gyro.y, false);
  129. SanitizeAnalog(status.gyro.z, false);
  130. return status;
  131. }
  132. Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback) {
  133. Common::Input::StickStatus status{};
  134. switch (callback.type) {
  135. case Common::Input::InputType::Stick:
  136. status = callback.stick_status;
  137. break;
  138. default:
  139. LOG_ERROR(Input, "Conversion from type {} to stick not implemented", callback.type);
  140. break;
  141. }
  142. SanitizeStick(status.x, status.y, true);
  143. const auto& properties_x = status.x.properties;
  144. const auto& properties_y = status.y.properties;
  145. const float x = status.x.value;
  146. const float y = status.y.value;
  147. // Set directional buttons
  148. status.right = x > properties_x.threshold;
  149. status.left = x < -properties_x.threshold;
  150. status.up = y > properties_y.threshold;
  151. status.down = y < -properties_y.threshold;
  152. return status;
  153. }
  154. Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback) {
  155. Common::Input::TouchStatus status{};
  156. switch (callback.type) {
  157. case Common::Input::InputType::Touch:
  158. status = callback.touch_status;
  159. break;
  160. case Common::Input::InputType::Stick:
  161. status.x = callback.stick_status.x;
  162. status.y = callback.stick_status.y;
  163. break;
  164. default:
  165. LOG_ERROR(Input, "Conversion from type {} to touch not implemented", callback.type);
  166. break;
  167. }
  168. SanitizeAnalog(status.x, true);
  169. SanitizeAnalog(status.y, true);
  170. float& x = status.x.value;
  171. float& y = status.y.value;
  172. // Adjust if value is inverted
  173. x = status.x.properties.inverted ? 1.0f + x : x;
  174. y = status.y.properties.inverted ? 1.0f + y : y;
  175. // clamp value
  176. x = std::clamp(x, 0.0f, 1.0f);
  177. y = std::clamp(y, 0.0f, 1.0f);
  178. if (status.pressed.inverted) {
  179. status.pressed.value = !status.pressed.value;
  180. }
  181. return status;
  182. }
  183. Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback) {
  184. Common::Input::TriggerStatus status{};
  185. float& raw_value = status.analog.raw_value;
  186. bool calculate_button_value = true;
  187. switch (callback.type) {
  188. case Common::Input::InputType::Analog:
  189. status.analog.properties = callback.analog_status.properties;
  190. raw_value = callback.analog_status.raw_value;
  191. break;
  192. case Common::Input::InputType::Button:
  193. status.analog.properties.range = 1.0f;
  194. status.analog.properties.inverted = callback.button_status.inverted;
  195. raw_value = callback.button_status.value ? 1.0f : 0.0f;
  196. break;
  197. case Common::Input::InputType::Trigger:
  198. status = callback.trigger_status;
  199. calculate_button_value = false;
  200. break;
  201. default:
  202. LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type);
  203. break;
  204. }
  205. SanitizeAnalog(status.analog, true);
  206. const auto& properties = status.analog.properties;
  207. float& value = status.analog.value;
  208. // Set button status
  209. if (calculate_button_value) {
  210. status.pressed.value = value > properties.threshold;
  211. }
  212. // Adjust if value is inverted
  213. value = properties.inverted ? 1.0f + value : value;
  214. // clamp value
  215. value = std::clamp(value, 0.0f, 1.0f);
  216. return status;
  217. }
  218. Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback) {
  219. Common::Input::AnalogStatus status{};
  220. switch (callback.type) {
  221. case Common::Input::InputType::Analog:
  222. status.properties = callback.analog_status.properties;
  223. status.raw_value = callback.analog_status.raw_value;
  224. break;
  225. default:
  226. LOG_ERROR(Input, "Conversion from type {} to analog not implemented", callback.type);
  227. break;
  228. }
  229. SanitizeAnalog(status, false);
  230. // Adjust if value is inverted
  231. status.value = status.properties.inverted ? -status.value : status.value;
  232. return status;
  233. }
  234. void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
  235. const auto& properties = analog.properties;
  236. float& raw_value = analog.raw_value;
  237. float& value = analog.value;
  238. if (!std::isnormal(raw_value)) {
  239. raw_value = 0;
  240. }
  241. // Apply center offset
  242. raw_value -= properties.offset;
  243. // Set initial values to be formated
  244. value = raw_value;
  245. // Calculate vector size
  246. const float r = std::abs(value);
  247. // Return zero if value is smaller than the deadzone
  248. if (r <= properties.deadzone || properties.deadzone == 1.0f) {
  249. analog.value = 0;
  250. return;
  251. }
  252. // Adjust range of value
  253. const float deadzone_factor =
  254. 1.0f / r * (r - properties.deadzone) / (1.0f - properties.deadzone);
  255. value = value * deadzone_factor / properties.range;
  256. // Invert direction if needed
  257. if (properties.inverted) {
  258. value = -value;
  259. }
  260. // Clamp value
  261. if (clamp_value) {
  262. value = std::clamp(value, -1.0f, 1.0f);
  263. }
  264. }
  265. void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
  266. bool clamp_value) {
  267. const auto& properties_x = analog_x.properties;
  268. const auto& properties_y = analog_y.properties;
  269. float& raw_x = analog_x.raw_value;
  270. float& raw_y = analog_y.raw_value;
  271. float& x = analog_x.value;
  272. float& y = analog_y.value;
  273. if (!std::isnormal(raw_x)) {
  274. raw_x = 0;
  275. }
  276. if (!std::isnormal(raw_y)) {
  277. raw_y = 0;
  278. }
  279. // Apply center offset
  280. raw_x += properties_x.offset;
  281. raw_y += properties_y.offset;
  282. // Apply X scale correction from offset
  283. if (std::abs(properties_x.offset) < 0.75f) {
  284. if (raw_x > 0) {
  285. raw_x /= 1 + properties_x.offset;
  286. } else {
  287. raw_x /= 1 - properties_x.offset;
  288. }
  289. }
  290. // Apply Y scale correction from offset
  291. if (std::abs(properties_y.offset) < 0.75f) {
  292. if (raw_y > 0) {
  293. raw_y /= 1 + properties_y.offset;
  294. } else {
  295. raw_y /= 1 - properties_y.offset;
  296. }
  297. }
  298. // Invert direction if needed
  299. raw_x = properties_x.inverted ? -raw_x : raw_x;
  300. raw_y = properties_y.inverted ? -raw_y : raw_y;
  301. // Set initial values to be formated
  302. x = raw_x;
  303. y = raw_y;
  304. // Calculate vector size
  305. float r = x * x + y * y;
  306. r = std::sqrt(r);
  307. // TODO(German77): Use deadzone and range of both axis
  308. // Return zero if values are smaller than the deadzone
  309. if (r <= properties_x.deadzone || properties_x.deadzone >= 1.0f) {
  310. x = 0;
  311. y = 0;
  312. return;
  313. }
  314. // Adjust range of joystick
  315. const float deadzone_factor =
  316. 1.0f / r * (r - properties_x.deadzone) / (1.0f - properties_x.deadzone);
  317. x = x * deadzone_factor / properties_x.range;
  318. y = y * deadzone_factor / properties_x.range;
  319. r = r * deadzone_factor / properties_x.range;
  320. // Normalize joystick
  321. if (clamp_value && r > 1.0f) {
  322. x /= r;
  323. y /= r;
  324. }
  325. }
  326. } // namespace Core::HID