summaryrefslogtreecommitdiff
path: root/src/input_common/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common/helpers')
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp317
-rw-r--r--src/input_common/helpers/stick_from_buttons.h30
-rw-r--r--src/input_common/helpers/touch_from_buttons.cpp84
-rw-r--r--src/input_common/helpers/touch_from_buttons.h22
-rw-r--r--src/input_common/helpers/udp_protocol.cpp78
-rw-r--r--src/input_common/helpers/udp_protocol.h290
6 files changed, 821 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..e23394f5f
--- /dev/null
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -0,0 +1,317 @@
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 Common::Input::InputDevice {
14public:
15 using Button = std::unique_ptr<Common::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 up->SetCallback({
23 .on_change =
24 [this](const Common::Input::CallbackStatus& callback_) {
25 UpdateUpButtonStatus(callback_);
26 },
27 });
28 down->SetCallback({
29 .on_change =
30 [this](const Common::Input::CallbackStatus& callback_) {
31 UpdateDownButtonStatus(callback_);
32 },
33 });
34 left->SetCallback({
35 .on_change =
36 [this](const Common::Input::CallbackStatus& callback_) {
37 UpdateLeftButtonStatus(callback_);
38 },
39 });
40 right->SetCallback({
41 .on_change =
42 [this](const Common::Input::CallbackStatus& callback_) {
43 UpdateRightButtonStatus(callback_);
44 },
45 });
46 modifier->SetCallback({
47 .on_change =
48 [this](const Common::Input::CallbackStatus& callback_) {
49 UpdateModButtonStatus(callback_);
50 },
51 });
52 last_x_axis_value = 0.0f;
53 last_y_axis_value = 0.0f;
54 }
55
56 bool IsAngleGreater(float old_angle, float new_angle) const {
57 constexpr float TAU = Common::PI * 2.0f;
58 // Use wider angle to ease the transition.
59 constexpr float aperture = TAU * 0.15f;
60 const float top_limit = new_angle + aperture;
61 return (old_angle > new_angle && old_angle <= top_limit) ||
62 (old_angle + TAU > new_angle && old_angle + TAU <= top_limit);
63 }
64
65 bool IsAngleSmaller(float old_angle, float new_angle) const {
66 constexpr float TAU = Common::PI * 2.0f;
67 // Use wider angle to ease the transition.
68 constexpr float aperture = TAU * 0.15f;
69 const float bottom_limit = new_angle - aperture;
70 return (old_angle >= bottom_limit && old_angle < new_angle) ||
71 (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle);
72 }
73
74 float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const {
75 constexpr float TAU = Common::PI * 2.0f;
76 float new_angle = angle;
77
78 auto time_difference = static_cast<float>(
79 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
80 time_difference /= 1000.0f * 1000.0f;
81 if (time_difference > 0.5f) {
82 time_difference = 0.5f;
83 }
84
85 if (IsAngleGreater(new_angle, goal_angle)) {
86 new_angle -= modifier_angle * time_difference;
87 if (new_angle < 0) {
88 new_angle += TAU;
89 }
90 if (!IsAngleGreater(new_angle, goal_angle)) {
91 return goal_angle;
92 }
93 } else if (IsAngleSmaller(new_angle, goal_angle)) {
94 new_angle += modifier_angle * time_difference;
95 if (new_angle >= TAU) {
96 new_angle -= TAU;
97 }
98 if (!IsAngleSmaller(new_angle, goal_angle)) {
99 return goal_angle;
100 }
101 } else {
102 return goal_angle;
103 }
104 return new_angle;
105 }
106
107 void SetGoalAngle(bool r, bool l, bool u, bool d) {
108 // Move to the right
109 if (r && !u && !d) {
110 goal_angle = 0.0f;
111 }
112
113 // Move to the upper right
114 if (r && u && !d) {
115 goal_angle = Common::PI * 0.25f;
116 }
117
118 // Move up
119 if (u && !l && !r) {
120 goal_angle = Common::PI * 0.5f;
121 }
122
123 // Move to the upper left
124 if (l && u && !d) {
125 goal_angle = Common::PI * 0.75f;
126 }
127
128 // Move to the left
129 if (l && !u && !d) {
130 goal_angle = Common::PI;
131 }
132
133 // Move to the bottom left
134 if (l && !u && d) {
135 goal_angle = Common::PI * 1.25f;
136 }
137
138 // Move down
139 if (d && !l && !r) {
140 goal_angle = Common::PI * 1.5f;
141 }
142
143 // Move to the bottom right
144 if (r && !u && d) {
145 goal_angle = Common::PI * 1.75f;
146 }
147 }
148
149 void UpdateUpButtonStatus(const Common::Input::CallbackStatus& button_callback) {
150 up_status = button_callback.button_status.value;
151 UpdateStatus();
152 }
153
154 void UpdateDownButtonStatus(const Common::Input::CallbackStatus& button_callback) {
155 down_status = button_callback.button_status.value;
156 UpdateStatus();
157 }
158
159 void UpdateLeftButtonStatus(const Common::Input::CallbackStatus& button_callback) {
160 left_status = button_callback.button_status.value;
161 UpdateStatus();
162 }
163
164 void UpdateRightButtonStatus(const Common::Input::CallbackStatus& button_callback) {
165 right_status = button_callback.button_status.value;
166 UpdateStatus();
167 }
168
169 void UpdateModButtonStatus(const Common::Input::CallbackStatus& button_callback) {
170 modifier_status = button_callback.button_status.value;
171 UpdateStatus();
172 }
173
174 void UpdateStatus() {
175 const float coef = modifier_status ? modifier_scale : 1.0f;
176
177 bool r = right_status;
178 bool l = left_status;
179 bool u = up_status;
180 bool d = down_status;
181
182 // Eliminate contradictory movements
183 if (r && l) {
184 r = false;
185 l = false;
186 }
187 if (u && d) {
188 u = false;
189 d = false;
190 }
191
192 // Move if a key is pressed
193 if (r || l || u || d) {
194 amplitude = coef;
195 } else {
196 amplitude = 0;
197 }
198
199 const auto now = std::chrono::steady_clock::now();
200 const auto time_difference = static_cast<u64>(
201 std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count());
202
203 if (time_difference < 10) {
204 // Disable analog mode if inputs are too fast
205 SetGoalAngle(r, l, u, d);
206 angle = goal_angle;
207 } else {
208 angle = GetAngle(now);
209 SetGoalAngle(r, l, u, d);
210 }
211
212 last_update = now;
213 Common::Input::CallbackStatus status{
214 .type = Common::Input::InputType::Stick,
215 .stick_status = GetStatus(),
216 };
217 last_x_axis_value = status.stick_status.x.raw_value;
218 last_y_axis_value = status.stick_status.y.raw_value;
219 TriggerOnChange(status);
220 }
221
222 void ForceUpdate() override {
223 up->ForceUpdate();
224 down->ForceUpdate();
225 left->ForceUpdate();
226 right->ForceUpdate();
227 modifier->ForceUpdate();
228 }
229
230 void SoftUpdate() override {
231 Common::Input::CallbackStatus status{
232 .type = Common::Input::InputType::Stick,
233 .stick_status = GetStatus(),
234 };
235 if (last_x_axis_value == status.stick_status.x.raw_value &&
236 last_y_axis_value == status.stick_status.y.raw_value) {
237 return;
238 }
239 last_x_axis_value = status.stick_status.x.raw_value;
240 last_y_axis_value = status.stick_status.y.raw_value;
241 TriggerOnChange(status);
242 }
243
244 Common::Input::StickStatus GetStatus() const {
245 Common::Input::StickStatus status{};
246 status.x.properties = properties;
247 status.y.properties = properties;
248 if (Settings::values.emulate_analog_keyboard) {
249 const auto now = std::chrono::steady_clock::now();
250 float angle_ = GetAngle(now);
251 status.x.raw_value = std::cos(angle_) * amplitude;
252 status.y.raw_value = std::sin(angle_) * amplitude;
253 return status;
254 }
255 constexpr float SQRT_HALF = 0.707106781f;
256 int x = 0, y = 0;
257 if (right_status) {
258 ++x;
259 }
260 if (left_status) {
261 --x;
262 }
263 if (up_status) {
264 ++y;
265 }
266 if (down_status) {
267 --y;
268 }
269 const float coef = modifier_status ? modifier_scale : 1.0f;
270 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
271 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
272 return status;
273 }
274
275private:
276 Button up;
277 Button down;
278 Button left;
279 Button right;
280 Button modifier;
281 float modifier_scale{};
282 float modifier_angle{};
283 float angle{};
284 float goal_angle{};
285 float amplitude{};
286 bool up_status{};
287 bool down_status{};
288 bool left_status{};
289 bool right_status{};
290 bool modifier_status{};
291 float last_x_axis_value{};
292 float last_y_axis_value{};
293 const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
294 std::chrono::time_point<std::chrono::steady_clock> last_update;
295};
296
297std::unique_ptr<Common::Input::InputDevice> StickFromButton::Create(
298 const Common::ParamPackage& params) {
299 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
300 auto up = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
301 params.Get("up", null_engine));
302 auto down = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
303 params.Get("down", null_engine));
304 auto left = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
305 params.Get("left", null_engine));
306 auto right = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
307 params.Get("right", null_engine));
308 auto modifier = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
309 params.Get("modifier", null_engine));
310 auto modifier_scale = params.Get("modifier_scale", 0.5f);
311 auto modifier_angle = params.Get("modifier_angle", 5.5f);
312 return std::make_unique<Stick>(std::move(up), std::move(down), std::move(left),
313 std::move(right), std::move(modifier), modifier_scale,
314 modifier_angle);
315}
316
317} // namespace InputCommon
diff --git a/src/input_common/helpers/stick_from_buttons.h b/src/input_common/helpers/stick_from_buttons.h
new file mode 100644
index 000000000..437ace4f7
--- /dev/null
+++ b/src/input_common/helpers/stick_from_buttons.h
@@ -0,0 +1,30 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/input.h"
8
9namespace InputCommon {
10
11/**
12 * An analog device factory that takes direction button devices and combines them into a analog
13 * device.
14 */
15class StickFromButton final : public Common::Input::Factory<Common::Input::InputDevice> {
16public:
17 /**
18 * Creates an analog device from direction button devices
19 * @param params contains parameters for creating the device:
20 * - "up": a serialized ParamPackage for creating a button device for up direction
21 * - "down": a serialized ParamPackage for creating a button device for down direction
22 * - "left": a serialized ParamPackage for creating a button device for left direction
23 * - "right": a serialized ParamPackage for creating a button device for right direction
24 * - "modifier": a serialized ParamPackage for creating a button device as the modifier
25 * - "modifier_scale": a float for the multiplier the modifier gives to the position
26 */
27 std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
28};
29
30} // namespace InputCommon
diff --git a/src/input_common/helpers/touch_from_buttons.cpp b/src/input_common/helpers/touch_from_buttons.cpp
new file mode 100644
index 000000000..ece1e3b32
--- /dev/null
+++ b/src/input_common/helpers/touch_from_buttons.cpp
@@ -0,0 +1,84 @@
1// Copyright 2020 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include "common/settings.h"
7#include "core/frontend/framebuffer_layout.h"
8#include "input_common/helpers/touch_from_buttons.h"
9
10namespace InputCommon {
11
12class TouchFromButtonDevice final : public Common::Input::InputDevice {
13public:
14 using Button = std::unique_ptr<Common::Input::InputDevice>;
15 TouchFromButtonDevice(Button button_, int touch_id_, float x_, float y_)
16 : button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) {
17 last_button_value = false;
18 button->SetCallback({
19 .on_change =
20 [this](const Common::Input::CallbackStatus& callback_) {
21 UpdateButtonStatus(callback_);
22 },
23 });
24 button->ForceUpdate();
25 }
26
27 void ForceUpdate() override {
28 button->ForceUpdate();
29 }
30
31 Common::Input::TouchStatus GetStatus(bool pressed) const {
32 const Common::Input::ButtonStatus button_status{
33 .value = pressed,
34 };
35 Common::Input::TouchStatus status{
36 .pressed = button_status,
37 .x = {},
38 .y = {},
39 .id = touch_id,
40 };
41 status.x.properties = properties;
42 status.y.properties = properties;
43
44 if (!pressed) {
45 return status;
46 }
47
48 status.x.raw_value = x;
49 status.y.raw_value = y;
50 return status;
51 }
52
53 void UpdateButtonStatus(const Common::Input::CallbackStatus& button_callback) {
54 const Common::Input::CallbackStatus status{
55 .type = Common::Input::InputType::Touch,
56 .touch_status = GetStatus(button_callback.button_status.value),
57 };
58 if (last_button_value != button_callback.button_status.value) {
59 last_button_value = button_callback.button_status.value;
60 TriggerOnChange(status);
61 }
62 }
63
64private:
65 Button button;
66 bool last_button_value;
67 const int touch_id;
68 const float x;
69 const float y;
70 const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
71};
72
73std::unique_ptr<Common::Input::InputDevice> TouchFromButton::Create(
74 const Common::ParamPackage& params) {
75 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
76 auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
77 params.Get("button", null_engine));
78 const auto touch_id = params.Get("touch_id", 0);
79 const float x = params.Get("x", 0.0f) / 1280.0f;
80 const float y = params.Get("y", 0.0f) / 720.0f;
81 return std::make_unique<TouchFromButtonDevice>(std::move(button), touch_id, x, y);
82}
83
84} // namespace InputCommon
diff --git a/src/input_common/helpers/touch_from_buttons.h b/src/input_common/helpers/touch_from_buttons.h
new file mode 100644
index 000000000..628f18215
--- /dev/null
+++ b/src/input_common/helpers/touch_from_buttons.h
@@ -0,0 +1,22 @@
1// Copyright 2020 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/input.h"
8
9namespace InputCommon {
10
11/**
12 * A touch device factory that takes a list of button devices and combines them into a touch device.
13 */
14class TouchFromButton final : public Common::Input::Factory<Common::Input::InputDevice> {
15public:
16 /**
17 * Creates a touch device from a list of button devices
18 */
19 std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
20};
21
22} // namespace InputCommon
diff --git a/src/input_common/helpers/udp_protocol.cpp b/src/input_common/helpers/udp_protocol.cpp
new file mode 100644
index 000000000..cdeab7e11
--- /dev/null
+++ b/src/input_common/helpers/udp_protocol.cpp
@@ -0,0 +1,78 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstddef>
6#include <cstring>
7#include "common/logging/log.h"
8#include "input_common/helpers/udp_protocol.h"
9
10namespace InputCommon::CemuhookUDP {
11
12static constexpr std::size_t GetSizeOfResponseType(Type t) {
13 switch (t) {
14 case Type::Version:
15 return sizeof(Response::Version);
16 case Type::PortInfo:
17 return sizeof(Response::PortInfo);
18 case Type::PadData:
19 return sizeof(Response::PadData);
20 }
21 return 0;
22}
23
24namespace Response {
25
26/**
27 * Returns Type if the packet is valid, else none
28 *
29 * Note: Modifies the buffer to zero out the crc (since thats the easiest way to check without
30 * copying the buffer)
31 */
32std::optional<Type> Validate(u8* data, std::size_t size) {
33 if (size < sizeof(Header)) {
34 return std::nullopt;
35 }
36 Header header{};
37 std::memcpy(&header, data, sizeof(Header));
38 if (header.magic != SERVER_MAGIC) {
39 LOG_ERROR(Input, "UDP Packet has an unexpected magic value");
40 return std::nullopt;
41 }
42 if (header.protocol_version != PROTOCOL_VERSION) {
43 LOG_ERROR(Input, "UDP Packet protocol mismatch");
44 return std::nullopt;
45 }
46 if (header.type < Type::Version || header.type > Type::PadData) {
47 LOG_ERROR(Input, "UDP Packet is an unknown type");
48 return std::nullopt;
49 }
50
51 // Packet size must equal sizeof(Header) + sizeof(Data)
52 // and also verify that the packet info mentions the correct size. Since the spec includes the
53 // type of the packet as part of the data, we need to include it in size calculations here
54 // ie: payload_length == sizeof(T) + sizeof(Type)
55 const std::size_t data_len = GetSizeOfResponseType(header.type);
56 if (header.payload_length != data_len + sizeof(Type) || size < data_len + sizeof(Header)) {
57 LOG_ERROR(
58 Input,
59 "UDP Packet payload length doesn't match. Received: {} PayloadLength: {} Expected: {}",
60 size, header.payload_length, data_len + sizeof(Type));
61 return std::nullopt;
62 }
63
64 const u32 crc32 = header.crc;
65 boost::crc_32_type result;
66 // zero out the crc in the buffer and then run the crc against it
67 std::memset(&data[offsetof(Header, crc)], 0, sizeof(u32_le));
68
69 result.process_bytes(data, data_len + sizeof(Header));
70 if (crc32 != result.checksum()) {
71 LOG_ERROR(Input, "UDP Packet CRC check failed. Offset: {}", offsetof(Header, crc));
72 return std::nullopt;
73 }
74 return header.type;
75}
76} // namespace Response
77
78} // namespace InputCommon::CemuhookUDP
diff --git a/src/input_common/helpers/udp_protocol.h b/src/input_common/helpers/udp_protocol.h
new file mode 100644
index 000000000..bcba12c58
--- /dev/null
+++ b/src/input_common/helpers/udp_protocol.h
@@ -0,0 +1,290 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <optional>
9#include <type_traits>
10
11#include <boost/crc.hpp>
12
13#include "common/bit_field.h"
14#include "common/swap.h"
15
16namespace InputCommon::CemuhookUDP {
17
18constexpr std::size_t MAX_PACKET_SIZE = 100;
19constexpr u16 PROTOCOL_VERSION = 1001;
20constexpr u32 CLIENT_MAGIC = 0x43555344; // DSUC (but flipped for LE)
21constexpr u32 SERVER_MAGIC = 0x53555344; // DSUS (but flipped for LE)
22
23enum class Type : u32 {
24 Version = 0x00100000,
25 PortInfo = 0x00100001,
26 PadData = 0x00100002,
27};
28
29struct Header {
30 u32_le magic{};
31 u16_le protocol_version{};
32 u16_le payload_length{};
33 u32_le crc{};
34 u32_le id{};
35 ///> In the protocol, the type of the packet is not part of the header, but its convenient to
36 ///> include in the header so the callee doesn't have to duplicate the type twice when building
37 ///> the data
38 Type type{};
39};
40static_assert(sizeof(Header) == 20, "UDP Message Header struct has wrong size");
41static_assert(std::is_trivially_copyable_v<Header>, "UDP Message Header is not trivially copyable");
42
43using MacAddress = std::array<u8, 6>;
44constexpr MacAddress EMPTY_MAC_ADDRESS = {0, 0, 0, 0, 0, 0};
45
46#pragma pack(push, 1)
47template <typename T>
48struct Message {
49 Header header{};
50 T data;
51};
52#pragma pack(pop)
53
54template <typename T>
55constexpr Type GetMessageType();
56
57namespace Request {
58
59enum RegisterFlags : u8 {
60 AllPads,
61 PadID,
62 PadMACAdddress,
63};
64
65struct Version {};
66/**
67 * Requests the server to send information about what controllers are plugged into the ports
68 * In citra's case, we only have one controller, so for simplicity's sake, we can just send a
69 * request explicitly for the first controller port and leave it at that. In the future it would be
70 * nice to make this configurable
71 */
72constexpr u32 MAX_PORTS = 4;
73struct PortInfo {
74 u32_le pad_count{}; ///> Number of ports to request data for
75 std::array<u8, MAX_PORTS> port;
76};
77static_assert(std::is_trivially_copyable_v<PortInfo>,
78 "UDP Request PortInfo is not trivially copyable");
79
80/**
81 * Request the latest pad information from the server. If the server hasn't received this message
82 * from the client in a reasonable time frame, the server will stop sending updates. The default
83 * timeout seems to be 5 seconds.
84 */
85struct PadData {
86 /// Determines which method will be used as a look up for the controller
87 RegisterFlags flags{};
88 /// Index of the port of the controller to retrieve data about
89 u8 port_id{};
90 /// Mac address of the controller to retrieve data about
91 MacAddress mac;
92};
93static_assert(sizeof(PadData) == 8, "UDP Request PadData struct has wrong size");
94static_assert(std::is_trivially_copyable_v<PadData>,
95 "UDP Request PadData is not trivially copyable");
96
97/**
98 * Creates a message with the proper header data that can be sent to the server.
99 * @param data Request body to send
100 * @param client_id ID of the udp client (usually not checked on the server)
101 */
102template <typename T>
103Message<T> Create(const T data, const u32 client_id = 0) {
104 boost::crc_32_type crc;
105 Header header{
106 CLIENT_MAGIC, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, client_id, GetMessageType<T>(),
107 };
108 Message<T> message{header, data};
109 crc.process_bytes(&message, sizeof(Message<T>));
110 message.header.crc = crc.checksum();
111 return message;
112}
113} // namespace Request
114
115namespace Response {
116
117enum class ConnectionType : u8 {
118 None,
119 Usb,
120 Bluetooth,
121};
122
123enum class State : u8 {
124 Disconnected,
125 Reserved,
126 Connected,
127};
128
129enum class Model : u8 {
130 None,
131 PartialGyro,
132 FullGyro,
133 Generic,
134};
135
136enum class Battery : u8 {
137 None = 0x00,
138 Dying = 0x01,
139 Low = 0x02,
140 Medium = 0x03,
141 High = 0x04,
142 Full = 0x05,
143 Charging = 0xEE,
144 Charged = 0xEF,
145};
146
147struct Version {
148 u16_le version{};
149};
150static_assert(sizeof(Version) == 2, "UDP Response Version struct has wrong size");
151static_assert(std::is_trivially_copyable_v<Version>,
152 "UDP Response Version is not trivially copyable");
153
154struct PortInfo {
155 u8 id{};
156 State state{};
157 Model model{};
158 ConnectionType connection_type{};
159 MacAddress mac;
160 Battery battery{};
161 u8 is_pad_active{};
162};
163static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size");
164static_assert(std::is_trivially_copyable_v<PortInfo>,
165 "UDP Response PortInfo is not trivially copyable");
166
167struct TouchPad {
168 u8 is_active{};
169 u8 id{};
170 u16_le x{};
171 u16_le y{};
172};
173static_assert(sizeof(TouchPad) == 6, "UDP Response TouchPad struct has wrong size ");
174
175#pragma pack(push, 1)
176struct PadData {
177 PortInfo info{};
178 u32_le packet_counter{};
179
180 u16_le digital_button{};
181 // The following union isn't trivially copyable but we don't use this input anyway.
182 // union DigitalButton {
183 // u16_le button;
184 // BitField<0, 1, u16> button_1; // Share
185 // BitField<1, 1, u16> button_2; // L3
186 // BitField<2, 1, u16> button_3; // R3
187 // BitField<3, 1, u16> button_4; // Options
188 // BitField<4, 1, u16> button_5; // Up
189 // BitField<5, 1, u16> button_6; // Right
190 // BitField<6, 1, u16> button_7; // Down
191 // BitField<7, 1, u16> button_8; // Left
192 // BitField<8, 1, u16> button_9; // L2
193 // BitField<9, 1, u16> button_10; // R2
194 // BitField<10, 1, u16> button_11; // L1
195 // BitField<11, 1, u16> button_12; // R1
196 // BitField<12, 1, u16> button_13; // Triangle
197 // BitField<13, 1, u16> button_14; // Circle
198 // BitField<14, 1, u16> button_15; // Cross
199 // BitField<15, 1, u16> button_16; // Square
200 // } digital_button;
201
202 u8 home;
203 /// If the device supports a "click" on the touchpad, this will change to 1 when a click happens
204 u8 touch_hard_press{};
205 u8 left_stick_x{};
206 u8 left_stick_y{};
207 u8 right_stick_x{};
208 u8 right_stick_y{};
209
210 struct AnalogButton {
211 u8 button_dpad_left_analog{};
212 u8 button_dpad_down_analog{};
213 u8 button_dpad_right_analog{};
214 u8 button_dpad_up_analog{};
215 u8 button_square_analog{};
216 u8 button_cross_analog{};
217 u8 button_circle_analog{};
218 u8 button_triangle_analog{};
219 u8 button_r1_analog{};
220 u8 button_l1_analog{};
221 u8 trigger_r2{};
222 u8 trigger_l2{};
223 } analog_button;
224
225 std::array<TouchPad, 2> touch;
226
227 u64_le motion_timestamp;
228
229 struct Accelerometer {
230 float x{};
231 float y{};
232 float z{};
233 } accel;
234
235 struct Gyroscope {
236 float pitch{};
237 float yaw{};
238 float roll{};
239 } gyro;
240};
241#pragma pack(pop)
242
243static_assert(sizeof(PadData) == 80, "UDP Response PadData struct has wrong size ");
244static_assert(std::is_trivially_copyable_v<PadData>,
245 "UDP Response PadData is not trivially copyable");
246
247static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE,
248 "UDP MAX_PACKET_SIZE is no longer larger than Message<PadData>");
249
250static_assert(sizeof(PadData::AnalogButton) == 12,
251 "UDP Response AnalogButton struct has wrong size ");
252static_assert(sizeof(PadData::Accelerometer) == 12,
253 "UDP Response Accelerometer struct has wrong size ");
254static_assert(sizeof(PadData::Gyroscope) == 12, "UDP Response Gyroscope struct has wrong size ");
255
256/**
257 * Create a Response Message from the data
258 * @param data array of bytes sent from the server
259 * @return boost::none if it failed to parse or Type if it succeeded. The client can then safely
260 * copy the data into the appropriate struct for that Type
261 */
262std::optional<Type> Validate(u8* data, std::size_t size);
263
264} // namespace Response
265
266template <>
267constexpr Type GetMessageType<Request::Version>() {
268 return Type::Version;
269}
270template <>
271constexpr Type GetMessageType<Request::PortInfo>() {
272 return Type::PortInfo;
273}
274template <>
275constexpr Type GetMessageType<Request::PadData>() {
276 return Type::PadData;
277}
278template <>
279constexpr Type GetMessageType<Response::Version>() {
280 return Type::Version;
281}
282template <>
283constexpr Type GetMessageType<Response::PortInfo>() {
284 return Type::PortInfo;
285}
286template <>
287constexpr Type GetMessageType<Response::PadData>() {
288 return Type::PadData;
289}
290} // namespace InputCommon::CemuhookUDP