input_converter.cpp 13 KB

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