input_converter.cpp 10 KB

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