analog_from_button.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // Copyright 2017 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <atomic>
  5. #include <chrono>
  6. #include <cmath>
  7. #include <thread>
  8. #include "common/math_util.h"
  9. #include "core/settings.h"
  10. #include "input_common/analog_from_button.h"
  11. namespace InputCommon {
  12. class Analog final : public Input::AnalogDevice {
  13. public:
  14. using Button = std::unique_ptr<Input::ButtonDevice>;
  15. Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_,
  16. float modifier_scale_, float modifier_angle_)
  17. : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
  18. right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
  19. modifier_angle(modifier_angle_) {
  20. update_thread_running.store(true);
  21. update_thread = std::thread(&Analog::UpdateStatus, this);
  22. }
  23. ~Analog() override {
  24. if (update_thread_running.load()) {
  25. update_thread_running.store(false);
  26. if (update_thread.joinable()) {
  27. update_thread.join();
  28. }
  29. }
  30. }
  31. void MoveToDirection(bool enable, float to_angle) {
  32. if (!enable) {
  33. return;
  34. }
  35. constexpr float TAU = Common::PI * 2.0f;
  36. // Use wider angle to ease the transition.
  37. constexpr float aperture = TAU * 0.15f;
  38. const float top_limit = to_angle + aperture;
  39. const float bottom_limit = to_angle - aperture;
  40. if ((angle > to_angle && angle <= top_limit) ||
  41. (angle + TAU > to_angle && angle + TAU <= top_limit)) {
  42. angle -= modifier_angle;
  43. if (angle < 0) {
  44. angle += TAU;
  45. }
  46. } else if ((angle >= bottom_limit && angle < to_angle) ||
  47. (angle - TAU >= bottom_limit && angle - TAU < to_angle)) {
  48. angle += modifier_angle;
  49. if (angle >= TAU) {
  50. angle -= TAU;
  51. }
  52. } else {
  53. angle = to_angle;
  54. }
  55. }
  56. void UpdateStatus() {
  57. while (update_thread_running.load()) {
  58. const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
  59. bool r = right->GetStatus();
  60. bool l = left->GetStatus();
  61. bool u = up->GetStatus();
  62. bool d = down->GetStatus();
  63. // Eliminate contradictory movements
  64. if (r && l) {
  65. r = false;
  66. l = false;
  67. }
  68. if (u && d) {
  69. u = false;
  70. d = false;
  71. }
  72. // Move to the right
  73. MoveToDirection(r && !u && !d, 0.0f);
  74. // Move to the upper right
  75. MoveToDirection(r && u && !d, Common::PI * 0.25f);
  76. // Move up
  77. MoveToDirection(u && !l && !r, Common::PI * 0.5f);
  78. // Move to the upper left
  79. MoveToDirection(l && u && !d, Common::PI * 0.75f);
  80. // Move to the left
  81. MoveToDirection(l && !u && !d, Common::PI);
  82. // Move to the bottom left
  83. MoveToDirection(l && !u && d, Common::PI * 1.25f);
  84. // Move down
  85. MoveToDirection(d && !l && !r, Common::PI * 1.5f);
  86. // Move to the bottom right
  87. MoveToDirection(r && !u && d, Common::PI * 1.75f);
  88. // Move if a key is pressed
  89. if (r || l || u || d) {
  90. amplitude = coef;
  91. } else {
  92. amplitude = 0;
  93. }
  94. // Delay the update rate to 100hz
  95. std::this_thread::sleep_for(std::chrono::milliseconds(10));
  96. }
  97. }
  98. std::tuple<float, float> GetStatus() const override {
  99. if (Settings::values.emulate_analog_keyboard) {
  100. return std::make_tuple(std::cos(angle) * amplitude, std::sin(angle) * amplitude);
  101. }
  102. constexpr float SQRT_HALF = 0.707106781f;
  103. int x = 0, y = 0;
  104. if (right->GetStatus()) {
  105. ++x;
  106. }
  107. if (left->GetStatus()) {
  108. --x;
  109. }
  110. if (up->GetStatus()) {
  111. ++y;
  112. }
  113. if (down->GetStatus()) {
  114. --y;
  115. }
  116. const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
  117. return std::make_tuple(static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF),
  118. static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF));
  119. }
  120. Input::AnalogProperties GetAnalogProperties() const override {
  121. return {modifier_scale, 1.0f, 0.5f};
  122. }
  123. bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
  124. switch (direction) {
  125. case Input::AnalogDirection::RIGHT:
  126. return right->GetStatus();
  127. case Input::AnalogDirection::LEFT:
  128. return left->GetStatus();
  129. case Input::AnalogDirection::UP:
  130. return up->GetStatus();
  131. case Input::AnalogDirection::DOWN:
  132. return down->GetStatus();
  133. }
  134. return false;
  135. }
  136. private:
  137. Button up;
  138. Button down;
  139. Button left;
  140. Button right;
  141. Button modifier;
  142. float modifier_scale;
  143. float modifier_angle;
  144. float angle{};
  145. float amplitude{};
  146. std::thread update_thread;
  147. std::atomic<bool> update_thread_running{};
  148. };
  149. std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) {
  150. const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
  151. auto up = Input::CreateDevice<Input::ButtonDevice>(params.Get("up", null_engine));
  152. auto down = Input::CreateDevice<Input::ButtonDevice>(params.Get("down", null_engine));
  153. auto left = Input::CreateDevice<Input::ButtonDevice>(params.Get("left", null_engine));
  154. auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine));
  155. auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine));
  156. auto modifier_scale = params.Get("modifier_scale", 0.5f);
  157. auto modifier_angle = params.Get("modifier_angle", 0.035f);
  158. return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left),
  159. std::move(right), std::move(modifier), modifier_scale,
  160. modifier_angle);
  161. }
  162. } // namespace InputCommon