diff options
| -rw-r--r-- | src/core/frontend/input.h | 15 | ||||
| -rwxr-xr-x | src/input_common/analog_from_button.cpp | 195 | ||||
| -rw-r--r-- | src/input_common/keyboard.cpp | 1 |
3 files changed, 138 insertions, 73 deletions
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 0c5d2b3b0..7a047803e 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h | |||
| @@ -27,6 +27,10 @@ struct AnalogProperties { | |||
| 27 | float range; | 27 | float range; |
| 28 | float threshold; | 28 | float threshold; |
| 29 | }; | 29 | }; |
| 30 | template <typename StatusType> | ||
| 31 | struct InputCallback { | ||
| 32 | std::function<void(StatusType)> on_change; | ||
| 33 | }; | ||
| 30 | 34 | ||
| 31 | /// An abstract class template for an input device (a button, an analog input, etc.). | 35 | /// An abstract class template for an input device (a button, an analog input, etc.). |
| 32 | template <typename StatusType> | 36 | template <typename StatusType> |
| @@ -50,6 +54,17 @@ public: | |||
| 50 | [[maybe_unused]] f32 freq_high) const { | 54 | [[maybe_unused]] f32 freq_high) const { |
| 51 | return {}; | 55 | return {}; |
| 52 | } | 56 | } |
| 57 | void SetCallback(InputCallback<StatusType> callback_) { | ||
| 58 | callback = std::move(callback_); | ||
| 59 | } | ||
| 60 | void TriggerOnChange() { | ||
| 61 | if (callback.on_change) { | ||
| 62 | callback.on_change(GetStatus()); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | private: | ||
| 67 | InputCallback<StatusType> callback; | ||
| 53 | }; | 68 | }; |
| 54 | 69 | ||
| 55 | /// An abstract class template for a factory that can create input devices. | 70 | /// An abstract class template for a factory that can create input devices. |
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp index f8ec179d0..100138d11 100755 --- a/src/input_common/analog_from_button.cpp +++ b/src/input_common/analog_from_button.cpp | |||
| @@ -21,104 +21,153 @@ public: | |||
| 21 | : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)), | 21 | : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)), |
| 22 | right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_), | 22 | right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_), |
| 23 | modifier_angle(modifier_angle_) { | 23 | modifier_angle(modifier_angle_) { |
| 24 | update_thread_running.store(true); | 24 | Input::InputCallback<bool> callbacks{ |
| 25 | update_thread = std::thread(&Analog::UpdateStatus, this); | 25 | [this]([[maybe_unused]] bool status) { UpdateStatus(); }}; |
| 26 | up->SetCallback(callbacks); | ||
| 27 | down->SetCallback(callbacks); | ||
| 28 | left->SetCallback(callbacks); | ||
| 29 | right->SetCallback(callbacks); | ||
| 26 | } | 30 | } |
| 27 | 31 | ||
| 28 | ~Analog() override { | 32 | bool IsAngleGreater(float old_angle, float new_angle) const { |
| 29 | if (update_thread_running.load()) { | 33 | constexpr float TAU = Common::PI * 2.0f; |
| 30 | update_thread_running.store(false); | 34 | // Use wider angle to ease the transition. |
| 31 | if (update_thread.joinable()) { | 35 | constexpr float aperture = TAU * 0.15f; |
| 32 | update_thread.join(); | 36 | const float top_limit = new_angle + aperture; |
| 33 | } | 37 | return (old_angle > new_angle && old_angle <= top_limit) || |
| 34 | } | 38 | (old_angle + TAU > new_angle && old_angle + TAU <= top_limit); |
| 35 | } | 39 | } |
| 36 | 40 | ||
| 37 | void MoveToDirection(bool enable, float to_angle) { | 41 | bool IsAngleSmaller(float old_angle, float new_angle) const { |
| 38 | if (!enable) { | ||
| 39 | return; | ||
| 40 | } | ||
| 41 | constexpr float TAU = Common::PI * 2.0f; | 42 | constexpr float TAU = Common::PI * 2.0f; |
| 42 | // Use wider angle to ease the transition. | 43 | // Use wider angle to ease the transition. |
| 43 | constexpr float aperture = TAU * 0.15f; | 44 | constexpr float aperture = TAU * 0.15f; |
| 44 | const float top_limit = to_angle + aperture; | 45 | const float bottom_limit = new_angle - aperture; |
| 45 | const float bottom_limit = to_angle - aperture; | 46 | return (old_angle >= bottom_limit && old_angle < new_angle) || |
| 46 | 47 | (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle); | |
| 47 | if ((angle > to_angle && angle <= top_limit) || | 48 | } |
| 48 | (angle + TAU > to_angle && angle + TAU <= top_limit)) { | 49 | |
| 49 | angle -= modifier_angle; | 50 | float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const { |
| 50 | if (angle < 0) { | 51 | constexpr float TAU = Common::PI * 2.0f; |
| 51 | angle += TAU; | 52 | float new_angle = angle; |
| 53 | |||
| 54 | auto time_difference = static_cast<float>( | ||
| 55 | std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count()); | ||
| 56 | time_difference /= 1000.0f * 1000.0f; | ||
| 57 | if (time_difference > 0.5f) { | ||
| 58 | time_difference = 0.5f; | ||
| 59 | } | ||
| 60 | |||
| 61 | if (IsAngleGreater(new_angle, goal_angle)) { | ||
| 62 | new_angle -= modifier_angle * time_difference; | ||
| 63 | if (new_angle < 0) { | ||
| 64 | new_angle += TAU; | ||
| 65 | } | ||
| 66 | if (!IsAngleGreater(new_angle, goal_angle)) { | ||
| 67 | return goal_angle; | ||
| 52 | } | 68 | } |
| 53 | } else if ((angle >= bottom_limit && angle < to_angle) || | 69 | } else if (IsAngleSmaller(new_angle, goal_angle)) { |
| 54 | (angle - TAU >= bottom_limit && angle - TAU < to_angle)) { | 70 | new_angle += modifier_angle * time_difference; |
| 55 | angle += modifier_angle; | 71 | if (new_angle >= TAU) { |
| 56 | if (angle >= TAU) { | 72 | new_angle -= TAU; |
| 57 | angle -= TAU; | 73 | } |
| 74 | if (!IsAngleSmaller(new_angle, goal_angle)) { | ||
| 75 | return goal_angle; | ||
| 58 | } | 76 | } |
| 59 | } else { | 77 | } else { |
| 60 | angle = to_angle; | 78 | return goal_angle; |
| 61 | } | 79 | } |
| 80 | return new_angle; | ||
| 62 | } | 81 | } |
| 63 | 82 | ||
| 64 | void UpdateStatus() { | 83 | void SetGoalAngle(bool r, bool l, bool u, bool d) { |
| 65 | while (update_thread_running.load()) { | 84 | // Move to the right |
| 66 | const float coef = modifier->GetStatus() ? modifier_scale : 1.0f; | 85 | if (r && !u && !d) { |
| 67 | 86 | goal_angle = 0.0f; | |
| 68 | bool r = right->GetStatus(); | 87 | } |
| 69 | bool l = left->GetStatus(); | 88 | |
| 70 | bool u = up->GetStatus(); | 89 | // Move to the upper right |
| 71 | bool d = down->GetStatus(); | 90 | if (r && u && !d) { |
| 72 | 91 | goal_angle = Common::PI * 0.25f; | |
| 73 | // Eliminate contradictory movements | 92 | } |
| 74 | if (r && l) { | ||
| 75 | r = false; | ||
| 76 | l = false; | ||
| 77 | } | ||
| 78 | if (u && d) { | ||
| 79 | u = false; | ||
| 80 | d = false; | ||
| 81 | } | ||
| 82 | 93 | ||
| 83 | // Move to the right | 94 | // Move up |
| 84 | MoveToDirection(r && !u && !d, 0.0f); | 95 | if (u && !l && !r) { |
| 96 | goal_angle = Common::PI * 0.5f; | ||
| 97 | } | ||
| 85 | 98 | ||
| 86 | // Move to the upper right | 99 | // Move to the upper left |
| 87 | MoveToDirection(r && u && !d, Common::PI * 0.25f); | 100 | if (l && u && !d) { |
| 101 | goal_angle = Common::PI * 0.75f; | ||
| 102 | } | ||
| 88 | 103 | ||
| 89 | // Move up | 104 | // Move to the left |
| 90 | MoveToDirection(u && !l && !r, Common::PI * 0.5f); | 105 | if (l && !u && !d) { |
| 106 | goal_angle = Common::PI; | ||
| 107 | } | ||
| 91 | 108 | ||
| 92 | // Move to the upper left | 109 | // Move to the bottom left |
| 93 | MoveToDirection(l && u && !d, Common::PI * 0.75f); | 110 | if (l && !u && d) { |
| 111 | goal_angle = Common::PI * 1.25f; | ||
| 112 | } | ||
| 94 | 113 | ||
| 95 | // Move to the left | 114 | // Move down |
| 96 | MoveToDirection(l && !u && !d, Common::PI); | 115 | if (d && !l && !r) { |
| 116 | goal_angle = Common::PI * 1.5f; | ||
| 117 | } | ||
| 97 | 118 | ||
| 98 | // Move to the bottom left | 119 | // Move to the bottom right |
| 99 | MoveToDirection(l && !u && d, Common::PI * 1.25f); | 120 | if (r && !u && d) { |
| 121 | goal_angle = Common::PI * 1.75f; | ||
| 122 | } | ||
| 123 | } | ||
| 100 | 124 | ||
| 101 | // Move down | 125 | void UpdateStatus() { |
| 102 | MoveToDirection(d && !l && !r, Common::PI * 1.5f); | 126 | const float coef = modifier->GetStatus() ? modifier_scale : 1.0f; |
| 103 | 127 | ||
| 104 | // Move to the bottom right | 128 | bool r = right->GetStatus(); |
| 105 | MoveToDirection(r && !u && d, Common::PI * 1.75f); | 129 | bool l = left->GetStatus(); |
| 130 | bool u = up->GetStatus(); | ||
| 131 | bool d = down->GetStatus(); | ||
| 106 | 132 | ||
| 107 | // Move if a key is pressed | 133 | // Eliminate contradictory movements |
| 108 | if (r || l || u || d) { | 134 | if (r && l) { |
| 109 | amplitude = coef; | 135 | r = false; |
| 110 | } else { | 136 | l = false; |
| 111 | amplitude = 0; | 137 | } |
| 112 | } | 138 | if (u && d) { |
| 139 | u = false; | ||
| 140 | d = false; | ||
| 141 | } | ||
| 113 | 142 | ||
| 114 | // Delay the update rate to 100hz | 143 | // Move if a key is pressed |
| 115 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); | 144 | if (r || l || u || d) { |
| 145 | amplitude = coef; | ||
| 146 | } else { | ||
| 147 | amplitude = 0; | ||
| 116 | } | 148 | } |
| 149 | |||
| 150 | const auto now = std::chrono::steady_clock::now(); | ||
| 151 | const auto time_difference = static_cast<u64>( | ||
| 152 | std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count()); | ||
| 153 | |||
| 154 | if (time_difference < 10) { | ||
| 155 | // Disable analog mode if inputs are too fast | ||
| 156 | SetGoalAngle(r, l, u, d); | ||
| 157 | angle = goal_angle; | ||
| 158 | } else { | ||
| 159 | angle = GetAngle(now); | ||
| 160 | SetGoalAngle(r, l, u, d); | ||
| 161 | } | ||
| 162 | |||
| 163 | last_update = now; | ||
| 117 | } | 164 | } |
| 118 | 165 | ||
| 119 | std::tuple<float, float> GetStatus() const override { | 166 | std::tuple<float, float> GetStatus() const override { |
| 120 | if (Settings::values.emulate_analog_keyboard) { | 167 | if (Settings::values.emulate_analog_keyboard) { |
| 121 | return std::make_tuple(std::cos(angle) * amplitude, std::sin(angle) * amplitude); | 168 | const auto now = std::chrono::steady_clock::now(); |
| 169 | float angle_ = GetAngle(now); | ||
| 170 | return std::make_tuple(std::cos(angle_) * amplitude, std::sin(angle_) * amplitude); | ||
| 122 | } | 171 | } |
| 123 | constexpr float SQRT_HALF = 0.707106781f; | 172 | constexpr float SQRT_HALF = 0.707106781f; |
| 124 | int x = 0, y = 0; | 173 | int x = 0, y = 0; |
| @@ -166,9 +215,9 @@ private: | |||
| 166 | float modifier_scale; | 215 | float modifier_scale; |
| 167 | float modifier_angle; | 216 | float modifier_angle; |
| 168 | float angle{}; | 217 | float angle{}; |
| 218 | float goal_angle{}; | ||
| 169 | float amplitude{}; | 219 | float amplitude{}; |
| 170 | std::thread update_thread; | 220 | std::chrono::time_point<std::chrono::steady_clock> last_update; |
| 171 | std::atomic<bool> update_thread_running{}; | ||
| 172 | }; | 221 | }; |
| 173 | 222 | ||
| 174 | std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) { | 223 | std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) { |
| @@ -179,7 +228,7 @@ std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::Para | |||
| 179 | auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine)); | 228 | auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine)); |
| 180 | auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine)); | 229 | auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine)); |
| 181 | auto modifier_scale = params.Get("modifier_scale", 0.5f); | 230 | auto modifier_scale = params.Get("modifier_scale", 0.5f); |
| 182 | auto modifier_angle = params.Get("modifier_angle", 0.035f); | 231 | auto modifier_angle = params.Get("modifier_angle", 5.5f); |
| 183 | return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left), | 232 | return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left), |
| 184 | std::move(right), std::move(modifier), modifier_scale, | 233 | std::move(right), std::move(modifier), modifier_scale, |
| 185 | modifier_angle); | 234 | modifier_angle); |
diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp index c467ff4c5..8261e76fd 100644 --- a/src/input_common/keyboard.cpp +++ b/src/input_common/keyboard.cpp | |||
| @@ -75,6 +75,7 @@ public: | |||
| 75 | } else { | 75 | } else { |
| 76 | pair.key_button->UnlockButton(); | 76 | pair.key_button->UnlockButton(); |
| 77 | } | 77 | } |
| 78 | pair.key_button->TriggerOnChange(); | ||
| 78 | } | 79 | } |
| 79 | } | 80 | } |
| 80 | } | 81 | } |