summaryrefslogtreecommitdiff
path: root/src/input_common/helpers/joycon_protocol/poller.cpp
diff options
context:
space:
mode:
authorGravatar Narr the Reg2022-12-20 20:27:34 -0600
committerGravatar Narr the Reg2023-01-19 18:05:21 -0600
commitf09a023292e659af46d551b9b134d94d000a57c7 (patch)
treef34ef390cac9f32f7d807614505601635ac62e28 /src/input_common/helpers/joycon_protocol/poller.cpp
parentinput_common: Use calibration from joycon (diff)
downloadyuzu-f09a023292e659af46d551b9b134d94d000a57c7.tar.gz
yuzu-f09a023292e659af46d551b9b134d94d000a57c7.tar.xz
yuzu-f09a023292e659af46d551b9b134d94d000a57c7.zip
input_common: Add support for joycon input reports
Diffstat (limited to 'src/input_common/helpers/joycon_protocol/poller.cpp')
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.cpp315
1 files changed, 315 insertions, 0 deletions
diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp
new file mode 100644
index 000000000..341479c0c
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/poller.cpp
@@ -0,0 +1,315 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/poller.h"
6
7namespace InputCommon::Joycon {
8
9JoyconPoller::JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_,
10 JoyStickCalibration right_stick_calibration_,
11 MotionCalibration motion_calibration_)
12 : device_type{device_type_}, left_stick_calibration{left_stick_calibration_},
13 right_stick_calibration{right_stick_calibration_}, motion_calibration{motion_calibration_} {}
14
15void JoyconPoller::SetCallbacks(const Joycon::JoyconCallbacks& callbacks_) {
16 callbacks = std::move(callbacks_);
17}
18
19void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status) {
20 InputReportActive data{};
21 memcpy(&data, buffer.data(), sizeof(InputReportActive));
22
23 switch (device_type) {
24 case Joycon::ControllerType::Left:
25 UpdateActiveLeftPadInput(data, motion_status);
26 break;
27 case Joycon::ControllerType::Right:
28 UpdateActiveRightPadInput(data, motion_status);
29 break;
30 case Joycon::ControllerType::Pro:
31 UpdateActiveProPadInput(data, motion_status);
32 break;
33 case Joycon::ControllerType::Grip:
34 case Joycon::ControllerType::Dual:
35 case Joycon::ControllerType::None:
36 break;
37 }
38
39 callbacks.on_battery_data(data.battery_status);
40}
41
42void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) {
43 InputReportPassive data{};
44 memcpy(&data, buffer.data(), sizeof(InputReportPassive));
45
46 switch (device_type) {
47 case Joycon::ControllerType::Left:
48 UpdatePasiveLeftPadInput(data);
49 break;
50 case Joycon::ControllerType::Right:
51 UpdatePasiveRightPadInput(data);
52 break;
53 case Joycon::ControllerType::Pro:
54 UpdatePasiveProPadInput(data);
55 break;
56 case Joycon::ControllerType::Grip:
57 case Joycon::ControllerType::Dual:
58 case Joycon::ControllerType::None:
59 break;
60 }
61}
62
63void JoyconPoller::ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status) {
64 // This mode is compatible with the active mode
65 ReadActiveMode(buffer, motion_status);
66}
67
68void JoyconPoller::UpdateColor(const Color& color) {
69 callbacks.on_color_data(color);
70}
71
72void JoyconPoller::UpdateActiveLeftPadInput(const InputReportActive& input,
73 const MotionStatus& motion_status) {
74 static constexpr std::array<Joycon::PadButton, 11> left_buttons{
75 Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right,
76 Joycon::PadButton::Left, Joycon::PadButton::LeftSL, Joycon::PadButton::LeftSR,
77 Joycon::PadButton::L, Joycon::PadButton::ZL, Joycon::PadButton::Minus,
78 Joycon::PadButton::Capture, Joycon::PadButton::StickL,
79 };
80
81 const u32 raw_button =
82 static_cast<u32>(input.button_input[2] | ((input.button_input[1] & 0b00101001) << 16));
83 for (std::size_t i = 0; i < left_buttons.size(); ++i) {
84 const bool button_status = (raw_button & static_cast<u32>(left_buttons[i])) != 0;
85 const int button = static_cast<int>(left_buttons[i]);
86 callbacks.on_button_data(button, button_status);
87 }
88
89 const u16 raw_left_axis_x =
90 static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8));
91 const u16 raw_left_axis_y =
92 static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4));
93 const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x);
94 const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y);
95 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x);
96 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y);
97
98 if (motion_status.is_enabled) {
99 auto left_motion = GetMotionInput(input, motion_status);
100 // Rotate motion axis to the correct direction
101 left_motion.accel_y = -left_motion.accel_y;
102 left_motion.accel_z = -left_motion.accel_z;
103 left_motion.gyro_x = -left_motion.gyro_x;
104 callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), left_motion);
105 }
106}
107
108void JoyconPoller::UpdateActiveRightPadInput(const InputReportActive& input,
109 const MotionStatus& motion_status) {
110 static constexpr std::array<Joycon::PadButton, 11> right_buttons{
111 Joycon::PadButton::Y, Joycon::PadButton::X, Joycon::PadButton::B,
112 Joycon::PadButton::A, Joycon::PadButton::RightSL, Joycon::PadButton::RightSR,
113 Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus,
114 Joycon::PadButton::Home, Joycon::PadButton::StickR,
115 };
116
117 const u32 raw_button =
118 static_cast<u32>((input.button_input[0] << 8) | (input.button_input[1] << 16));
119 for (std::size_t i = 0; i < right_buttons.size(); ++i) {
120 const bool button_status = (raw_button & static_cast<u32>(right_buttons[i])) != 0;
121 const int button = static_cast<int>(right_buttons[i]);
122 callbacks.on_button_data(button, button_status);
123 }
124
125 const u16 raw_right_axis_x =
126 static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8));
127 const u16 raw_right_axis_y =
128 static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4));
129 const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x);
130 const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y);
131 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x);
132 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y);
133
134 if (motion_status.is_enabled) {
135 auto right_motion = GetMotionInput(input, motion_status);
136 // Rotate motion axis to the correct direction
137 right_motion.accel_x = -right_motion.accel_x;
138 right_motion.accel_y = -right_motion.accel_y;
139 right_motion.gyro_z = -right_motion.gyro_z;
140 callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), right_motion);
141 }
142}
143
144void JoyconPoller::UpdateActiveProPadInput(const InputReportActive& input,
145 const MotionStatus& motion_status) {
146 static constexpr std::array<Joycon::PadButton, 18> pro_buttons{
147 Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right,
148 Joycon::PadButton::Left, Joycon::PadButton::L, Joycon::PadButton::ZL,
149 Joycon::PadButton::Minus, Joycon::PadButton::Capture, Joycon::PadButton::Y,
150 Joycon::PadButton::X, Joycon::PadButton::B, Joycon::PadButton::A,
151 Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus,
152 Joycon::PadButton::Home, Joycon::PadButton::StickL, Joycon::PadButton::StickR,
153 };
154
155 const u32 raw_button = static_cast<u32>(input.button_input[2] | (input.button_input[0] << 8) |
156 (input.button_input[1] << 16));
157 for (std::size_t i = 0; i < pro_buttons.size(); ++i) {
158 const bool button_status = (raw_button & static_cast<u32>(pro_buttons[i])) != 0;
159 const int button = static_cast<int>(pro_buttons[i]);
160 callbacks.on_button_data(button, button_status);
161 }
162
163 const u16 raw_left_axis_x =
164 static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8));
165 const u16 raw_left_axis_y =
166 static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4));
167 const u16 raw_right_axis_x =
168 static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8));
169 const u16 raw_right_axis_y =
170 static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4));
171
172 const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x);
173 const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y);
174 const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x);
175 const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y);
176 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x);
177 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y);
178 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x);
179 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y);
180
181 if (motion_status.is_enabled) {
182 auto pro_motion = GetMotionInput(input, motion_status);
183 pro_motion.gyro_x = -pro_motion.gyro_x;
184 pro_motion.accel_y = -pro_motion.accel_y;
185 pro_motion.accel_z = -pro_motion.accel_z;
186 callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), pro_motion);
187 callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), pro_motion);
188 }
189}
190
191void JoyconPoller::UpdatePasiveLeftPadInput(const InputReportPassive& input) {
192 static constexpr std::array<Joycon::PasivePadButton, 11> left_buttons{
193 Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
194 Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
195 Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
196 Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
197 Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Capture,
198 Joycon::PasivePadButton::StickL,
199 };
200
201 for (std::size_t i = 0; i < left_buttons.size(); ++i) {
202 const bool button_status = (input.button_input & static_cast<u32>(left_buttons[i])) != 0;
203 const int button = static_cast<int>(left_buttons[i]);
204 callbacks.on_button_data(button, button_status);
205 }
206}
207
208void JoyconPoller::UpdatePasiveRightPadInput(const InputReportPassive& input) {
209 static constexpr std::array<Joycon::PasivePadButton, 11> right_buttons{
210 Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
211 Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
212 Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
213 Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
214 Joycon::PasivePadButton::Plus, Joycon::PasivePadButton::Home,
215 Joycon::PasivePadButton::StickR,
216 };
217
218 for (std::size_t i = 0; i < right_buttons.size(); ++i) {
219 const bool button_status = (input.button_input & static_cast<u32>(right_buttons[i])) != 0;
220 const int button = static_cast<int>(right_buttons[i]);
221 callbacks.on_button_data(button, button_status);
222 }
223}
224
225void JoyconPoller::UpdatePasiveProPadInput(const InputReportPassive& input) {
226 static constexpr std::array<Joycon::PasivePadButton, 14> pro_buttons{
227 Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
228 Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
229 Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
230 Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
231 Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Plus,
232 Joycon::PasivePadButton::Capture, Joycon::PasivePadButton::Home,
233 Joycon::PasivePadButton::StickL, Joycon::PasivePadButton::StickR,
234 };
235
236 for (std::size_t i = 0; i < pro_buttons.size(); ++i) {
237 const bool button_status = (input.button_input & static_cast<u32>(pro_buttons[i])) != 0;
238 const int button = static_cast<int>(pro_buttons[i]);
239 callbacks.on_button_data(button, button_status);
240 }
241}
242
243f32 JoyconPoller::GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const {
244 const f32 value = static_cast<f32>(raw_value - calibration.center);
245 if (value > 0.0f) {
246 return value / calibration.max;
247 }
248 return value / calibration.min;
249}
250
251f32 JoyconPoller::GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal,
252 AccelerometerSensitivity sensitivity) const {
253 const f32 value = raw * (1.0f / (cal.scale - cal.offset)) * 4;
254 switch (sensitivity) {
255 case Joycon::AccelerometerSensitivity::G2:
256 return value / 4.0f;
257 case Joycon::AccelerometerSensitivity::G4:
258 return value / 2.0f;
259 case Joycon::AccelerometerSensitivity::G8:
260 return value;
261 case Joycon::AccelerometerSensitivity::G16:
262 return value * 2.0f;
263 }
264 return value;
265}
266
267f32 JoyconPoller::GetGyroValue(s16 raw, const MotionSensorCalibration& cal,
268 GyroSensitivity sensitivity) const {
269 const f32 value = (raw - cal.offset) * (936.0f / (cal.scale - cal.offset)) / 360.0f;
270 switch (sensitivity) {
271 case Joycon::GyroSensitivity::DPS250:
272 return value / 8.0f;
273 case Joycon::GyroSensitivity::DPS500:
274 return value / 4.0f;
275 case Joycon::GyroSensitivity::DPS1000:
276 return value / 2.0f;
277 case Joycon::GyroSensitivity::DPS2000:
278 return value;
279 }
280 return value;
281}
282
283s16 JoyconPoller::GetRawIMUValues(std::size_t sensor, size_t axis,
284 const InputReportActive& input) const {
285 return input.motion_input[(sensor * 3) + axis];
286}
287
288MotionData JoyconPoller::GetMotionInput(const InputReportActive& input,
289 const MotionStatus& motion_status) const {
290 MotionData motion{};
291 const auto& accel_cal = motion_calibration.accelerometer;
292 const auto& gyro_cal = motion_calibration.gyro;
293 const s16 raw_accel_x = input.motion_input[1];
294 const s16 raw_accel_y = input.motion_input[0];
295 const s16 raw_accel_z = input.motion_input[2];
296 const s16 raw_gyro_x = input.motion_input[4];
297 const s16 raw_gyro_y = input.motion_input[3];
298 const s16 raw_gyro_z = input.motion_input[5];
299
300 motion.delta_timestamp = motion_status.delta_time;
301 motion.accel_x =
302 GetAccelerometerValue(raw_accel_x, accel_cal[1], motion_status.accelerometer_sensitivity);
303 motion.accel_y =
304 GetAccelerometerValue(raw_accel_y, accel_cal[0], motion_status.accelerometer_sensitivity);
305 motion.accel_z =
306 GetAccelerometerValue(raw_accel_z, accel_cal[2], motion_status.accelerometer_sensitivity);
307 motion.gyro_x = GetGyroValue(raw_gyro_x, gyro_cal[1], motion_status.gyro_sensitivity);
308 motion.gyro_y = GetGyroValue(raw_gyro_y, gyro_cal[0], motion_status.gyro_sensitivity);
309 motion.gyro_z = GetGyroValue(raw_gyro_z, gyro_cal[2], motion_status.gyro_sensitivity);
310
311 // TODO(German77): Return all three samples data
312 return motion;
313}
314
315} // namespace InputCommon::Joycon