summaryrefslogtreecommitdiff
path: root/src/input_common/helpers/stick_from_buttons.cpp
diff options
context:
space:
mode:
authorGravatar german772021-09-20 16:57:55 -0500
committerGravatar Narr the Reg2021-11-24 20:30:22 -0600
commit4c6f2c2547e1d97f12ebe708fac693a6183bbc45 (patch)
tree0abcd35f56088bbf732a92838995a465bae0f0ee /src/input_common/helpers/stick_from_buttons.cpp
parentinput_common: Create input poller and mapping (diff)
downloadyuzu-4c6f2c2547e1d97f12ebe708fac693a6183bbc45.tar.gz
yuzu-4c6f2c2547e1d97f12ebe708fac693a6183bbc45.tar.xz
yuzu-4c6f2c2547e1d97f12ebe708fac693a6183bbc45.zip
input_common: Move touch and analog from button. Move udp protocol
Diffstat (limited to 'src/input_common/helpers/stick_from_buttons.cpp')
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp270
1 files changed, 270 insertions, 0 deletions
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp
new file mode 100644
index 000000000..38f150746
--- /dev/null
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -0,0 +1,270 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <cmath>
7#include "common/math_util.h"
8#include "common/settings.h"
9#include "input_common/helpers/stick_from_buttons.h"
10
11namespace InputCommon {
12
13class Stick final : public Input::InputDevice {
14public:
15 using Button = std::unique_ptr<Input::InputDevice>;
16
17 Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_,
18 float modifier_scale_, float modifier_angle_)
19 : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
20 right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
21 modifier_angle(modifier_angle_) {
22 Input::InputCallback button_up_callback{
23 [this](Input::CallbackStatus callback_) { UpdateUpButtonStatus(callback_); }};
24 Input::InputCallback button_down_callback{
25 [this](Input::CallbackStatus callback_) { UpdateDownButtonStatus(callback_); }};
26 Input::InputCallback button_left_callback{
27 [this](Input::CallbackStatus callback_) { UpdateLeftButtonStatus(callback_); }};
28 Input::InputCallback button_right_callback{
29 [this](Input::CallbackStatus callback_) { UpdateRightButtonStatus(callback_); }};
30 Input::InputCallback button_modifier_callback{
31 [this](Input::CallbackStatus callback_) { UpdateModButtonStatus(callback_); }};
32 up->SetCallback(button_up_callback);
33 down->SetCallback(button_down_callback);
34 left->SetCallback(button_left_callback);
35 right->SetCallback(button_right_callback);
36 modifier->SetCallback(button_modifier_callback);
37 }
38
39 bool IsAngleGreater(float old_angle, float new_angle) const {
40 constexpr float TAU = Common::PI * 2.0f;
41 // Use wider angle to ease the transition.
42 constexpr float aperture = TAU * 0.15f;
43 const float top_limit = new_angle + aperture;
44 return (old_angle > new_angle && old_angle <= top_limit) ||
45 (old_angle + TAU > new_angle && old_angle + TAU <= top_limit);
46 }
47
48 bool IsAngleSmaller(float old_angle, float new_angle) const {
49 constexpr float TAU = Common::PI * 2.0f;
50 // Use wider angle to ease the transition.
51 constexpr float aperture = TAU * 0.15f;
52 const float bottom_limit = new_angle - aperture;
53 return (old_angle >= bottom_limit && old_angle < new_angle) ||
54 (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle);
55 }
56
57 float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const {
58 constexpr float TAU = Common::PI * 2.0f;
59 float new_angle = angle;
60
61 auto time_difference = static_cast<float>(
62 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
63 time_difference /= 1000.0f * 1000.0f;
64 if (time_difference > 0.5f) {
65 time_difference = 0.5f;
66 }
67
68 if (IsAngleGreater(new_angle, goal_angle)) {
69 new_angle -= modifier_angle * time_difference;
70 if (new_angle < 0) {
71 new_angle += TAU;
72 }
73 if (!IsAngleGreater(new_angle, goal_angle)) {
74 return goal_angle;
75 }
76 } else if (IsAngleSmaller(new_angle, goal_angle)) {
77 new_angle += modifier_angle * time_difference;
78 if (new_angle >= TAU) {
79 new_angle -= TAU;
80 }
81 if (!IsAngleSmaller(new_angle, goal_angle)) {
82 return goal_angle;
83 }
84 } else {
85 return goal_angle;
86 }
87 return new_angle;
88 }
89
90 void SetGoalAngle(bool r, bool l, bool u, bool d) {
91 // Move to the right
92 if (r && !u && !d) {
93 goal_angle = 0.0f;
94 }
95
96 // Move to the upper right
97 if (r && u && !d) {
98 goal_angle = Common::PI * 0.25f;
99 }
100
101 // Move up
102 if (u && !l && !r) {
103 goal_angle = Common::PI * 0.5f;
104 }
105
106 // Move to the upper left
107 if (l && u && !d) {
108 goal_angle = Common::PI * 0.75f;
109 }
110
111 // Move to the left
112 if (l && !u && !d) {
113 goal_angle = Common::PI;
114 }
115
116 // Move to the bottom left
117 if (l && !u && d) {
118 goal_angle = Common::PI * 1.25f;
119 }
120
121 // Move down
122 if (d && !l && !r) {
123 goal_angle = Common::PI * 1.5f;
124 }
125
126 // Move to the bottom right
127 if (r && !u && d) {
128 goal_angle = Common::PI * 1.75f;
129 }
130 }
131
132 void UpdateUpButtonStatus(Input::CallbackStatus button_callback) {
133 up_status = button_callback.button_status.value;
134 UpdateStatus();
135 }
136
137 void UpdateDownButtonStatus(Input::CallbackStatus button_callback) {
138 down_status = button_callback.button_status.value;
139 UpdateStatus();
140 }
141
142 void UpdateLeftButtonStatus(Input::CallbackStatus button_callback) {
143 left_status = button_callback.button_status.value;
144 UpdateStatus();
145 }
146
147 void UpdateRightButtonStatus(Input::CallbackStatus button_callback) {
148 right_status = button_callback.button_status.value;
149 UpdateStatus();
150 }
151
152 void UpdateModButtonStatus(Input::CallbackStatus button_callback) {
153 modifier_status = button_callback.button_status.value;
154 UpdateStatus();
155 }
156
157 void UpdateStatus() {
158 const float coef = modifier_status ? modifier_scale : 1.0f;
159
160 bool r = right_status;
161 bool l = left_status;
162 bool u = up_status;
163 bool d = down_status;
164
165 // Eliminate contradictory movements
166 if (r && l) {
167 r = false;
168 l = false;
169 }
170 if (u && d) {
171 u = false;
172 d = false;
173 }
174
175 // Move if a key is pressed
176 if (r || l || u || d) {
177 amplitude = coef;
178 } else {
179 amplitude = 0;
180 }
181
182 const auto now = std::chrono::steady_clock::now();
183 const auto time_difference = static_cast<u64>(
184 std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count());
185
186 if (time_difference < 10) {
187 // Disable analog mode if inputs are too fast
188 SetGoalAngle(r, l, u, d);
189 angle = goal_angle;
190 } else {
191 angle = GetAngle(now);
192 SetGoalAngle(r, l, u, d);
193 }
194
195 last_update = now;
196 Input::CallbackStatus status{
197 .type = Input::InputType::Stick,
198 .stick_status = GetStatus(),
199 };
200 TriggerOnChange(status);
201 }
202
203 Input::StickStatus GetStatus() const {
204 Input::StickStatus status{};
205 status.x.properties = properties;
206 status.y.properties = properties;
207 if (Settings::values.emulate_analog_keyboard) {
208 const auto now = std::chrono::steady_clock::now();
209 float angle_ = GetAngle(now);
210 status.x.raw_value = std::cos(angle_) * amplitude;
211 status.y.raw_value = std::sin(angle_) * amplitude;
212 return status;
213 }
214 constexpr float SQRT_HALF = 0.707106781f;
215 int x = 0, y = 0;
216 if (right_status) {
217 ++x;
218 }
219 if (left_status) {
220 --x;
221 }
222 if (up_status) {
223 ++y;
224 }
225 if (down_status) {
226 --y;
227 }
228 const float coef = modifier_status ? modifier_scale : 1.0f;
229 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
230 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
231 return status;
232 }
233
234private:
235 Button up;
236 Button down;
237 Button left;
238 Button right;
239 Button modifier;
240 float modifier_scale;
241 float modifier_angle;
242 float angle{};
243 float goal_angle{};
244 float amplitude{};
245 bool up_status;
246 bool down_status;
247 bool left_status;
248 bool right_status;
249 bool modifier_status;
250 const Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
251 std::chrono::time_point<std::chrono::steady_clock> last_update;
252};
253
254std::unique_ptr<Input::InputDevice> StickFromButton::Create(const Common::ParamPackage& params) {
255 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
256 auto up = Input::CreateDeviceFromString<Input::InputDevice>(params.Get("up", null_engine));
257 auto down = Input::CreateDeviceFromString<Input::InputDevice>(params.Get("down", null_engine));
258 auto left = Input::CreateDeviceFromString<Input::InputDevice>(params.Get("left", null_engine));
259 auto right =
260 Input::CreateDeviceFromString<Input::InputDevice>(params.Get("right", null_engine));
261 auto modifier =
262 Input::CreateDeviceFromString<Input::InputDevice>(params.Get("modifier", null_engine));
263 auto modifier_scale = params.Get("modifier_scale", 0.5f);
264 auto modifier_angle = params.Get("modifier_angle", 5.5f);
265 return std::make_unique<Stick>(std::move(up), std::move(down), std::move(left),
266 std::move(right), std::move(modifier), modifier_scale,
267 modifier_angle);
268}
269
270} // namespace InputCommon