summaryrefslogtreecommitdiff
path: root/src/input_common/helpers
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
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')
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp270
-rw-r--r--src/input_common/helpers/stick_from_buttons.h31
-rw-r--r--src/input_common/helpers/touch_from_buttons.cpp70
-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.h259
6 files changed, 730 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
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..1d6e24c98
--- /dev/null
+++ b/src/input_common/helpers/stick_from_buttons.h
@@ -0,0 +1,31 @@
1
2// Copyright 2017 Citra Emulator Project
3// Licensed under GPLv2 or any later version
4// Refer to the license.txt file included.
5
6#pragma once
7
8#include "common/input.h"
9
10namespace InputCommon {
11
12/**
13 * An analog device factory that takes direction button devices and combines them into a analog
14 * device.
15 */
16class StickFromButton final : public Input::Factory<Input::InputDevice> {
17public:
18 /**
19 * Creates an analog device from direction button devices
20 * @param params contains parameters for creating the device:
21 * - "up": a serialized ParamPackage for creating a button device for up direction
22 * - "down": a serialized ParamPackage for creating a button device for down direction
23 * - "left": a serialized ParamPackage for creating a button device for left direction
24 * - "right": a serialized ParamPackage for creating a button device for right direction
25 * - "modifier": a serialized ParamPackage for creating a button device as the modifier
26 * - "modifier_scale": a float for the multiplier the modifier gives to the position
27 */
28 std::unique_ptr<Input::InputDevice> Create(const Common::ParamPackage& params) override;
29};
30
31} // 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..2abfaf841
--- /dev/null
+++ b/src/input_common/helpers/touch_from_buttons.cpp
@@ -0,0 +1,70 @@
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 Input::InputDevice {
13public:
14 using Button = std::unique_ptr<Input::InputDevice>;
15 TouchFromButtonDevice(Button button_, u32 touch_id_, float x_, float y_)
16 : button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) {
17 Input::InputCallback button_up_callback{
18 [this](Input::CallbackStatus callback_) { UpdateButtonStatus(callback_); }};
19 button->SetCallback(button_up_callback);
20 }
21
22 Input::TouchStatus GetStatus(bool pressed) const {
23 const Input::ButtonStatus button_status{
24 .value = pressed,
25 };
26 Input::TouchStatus status{
27 .pressed = button_status,
28 .x = {},
29 .y = {},
30 .id = touch_id,
31 };
32 status.x.properties = properties;
33 status.y.properties = properties;
34
35 if (!pressed) {
36 return status;
37 }
38
39 status.x.raw_value = x;
40 status.y.raw_value = y;
41 return status;
42 }
43
44 void UpdateButtonStatus(Input::CallbackStatus button_callback) {
45 const Input::CallbackStatus status{
46 .type = Input::InputType::Touch,
47 .touch_status = GetStatus(button_callback.button_status.value),
48 };
49 TriggerOnChange(status);
50 }
51
52private:
53 Button button;
54 const u32 touch_id;
55 const float x;
56 const float y;
57 const Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
58};
59
60std::unique_ptr<Input::InputDevice> TouchFromButton::Create(const Common::ParamPackage& params) {
61 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
62 auto button =
63 Input::CreateDeviceFromString<Input::InputDevice>(params.Get("button", null_engine));
64 const auto touch_id = params.Get("touch_id", 0);
65 const float x = params.Get("x", 0.0f) / 1280.0f;
66 const float y = params.Get("y", 0.0f) / 720.0f;
67 return std::make_unique<TouchFromButtonDevice>(std::move(button), touch_id, x, y);
68}
69
70} // 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..21b353728
--- /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 Input::Factory<Input::InputDevice> {
15public:
16 /**
17 * Creates a touch device from a list of button devices
18 */
19 std::unique_ptr<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..1bdc9209e
--- /dev/null
+++ b/src/input_common/helpers/udp_protocol.h
@@ -0,0 +1,259 @@
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
59struct Version {};
60/**
61 * Requests the server to send information about what controllers are plugged into the ports
62 * In citra's case, we only have one controller, so for simplicity's sake, we can just send a
63 * request explicitly for the first controller port and leave it at that. In the future it would be
64 * nice to make this configurable
65 */
66constexpr u32 MAX_PORTS = 4;
67struct PortInfo {
68 u32_le pad_count{}; ///> Number of ports to request data for
69 std::array<u8, MAX_PORTS> port;
70};
71static_assert(std::is_trivially_copyable_v<PortInfo>,
72 "UDP Request PortInfo is not trivially copyable");
73
74/**
75 * Request the latest pad information from the server. If the server hasn't received this message
76 * from the client in a reasonable time frame, the server will stop sending updates. The default
77 * timeout seems to be 5 seconds.
78 */
79struct PadData {
80 enum class Flags : u8 {
81 AllPorts,
82 Id,
83 Mac,
84 };
85 /// Determines which method will be used as a look up for the controller
86 Flags flags{};
87 /// Index of the port of the controller to retrieve data about
88 u8 port_id{};
89 /// Mac address of the controller to retrieve data about
90 MacAddress mac;
91};
92static_assert(sizeof(PadData) == 8, "UDP Request PadData struct has wrong size");
93static_assert(std::is_trivially_copyable_v<PadData>,
94 "UDP Request PadData is not trivially copyable");
95
96/**
97 * Creates a message with the proper header data that can be sent to the server.
98 * @param data Request body to send
99 * @param client_id ID of the udp client (usually not checked on the server)
100 */
101template <typename T>
102Message<T> Create(const T data, const u32 client_id = 0) {
103 boost::crc_32_type crc;
104 Header header{
105 CLIENT_MAGIC, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, client_id, GetMessageType<T>(),
106 };
107 Message<T> message{header, data};
108 crc.process_bytes(&message, sizeof(Message<T>));
109 message.header.crc = crc.checksum();
110 return message;
111}
112} // namespace Request
113
114namespace Response {
115
116struct Version {
117 u16_le version{};
118};
119static_assert(sizeof(Version) == 2, "UDP Response Version struct has wrong size");
120static_assert(std::is_trivially_copyable_v<Version>,
121 "UDP Response Version is not trivially copyable");
122
123struct PortInfo {
124 u8 id{};
125 u8 state{};
126 u8 model{};
127 u8 connection_type{};
128 MacAddress mac;
129 u8 battery{};
130 u8 is_pad_active{};
131};
132static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size");
133static_assert(std::is_trivially_copyable_v<PortInfo>,
134 "UDP Response PortInfo is not trivially copyable");
135
136struct TouchPad {
137 u8 is_active{};
138 u8 id{};
139 u16_le x{};
140 u16_le y{};
141};
142static_assert(sizeof(TouchPad) == 6, "UDP Response TouchPad struct has wrong size ");
143
144#pragma pack(push, 1)
145struct PadData {
146 PortInfo info{};
147 u32_le packet_counter{};
148
149 u16_le digital_button{};
150 // The following union isn't trivially copyable but we don't use this input anyway.
151 // union DigitalButton {
152 // u16_le button;
153 // BitField<0, 1, u16> button_1; // Share
154 // BitField<1, 1, u16> button_2; // L3
155 // BitField<2, 1, u16> button_3; // R3
156 // BitField<3, 1, u16> button_4; // Options
157 // BitField<4, 1, u16> button_5; // Up
158 // BitField<5, 1, u16> button_6; // Right
159 // BitField<6, 1, u16> button_7; // Down
160 // BitField<7, 1, u16> button_8; // Left
161 // BitField<8, 1, u16> button_9; // L2
162 // BitField<9, 1, u16> button_10; // R2
163 // BitField<10, 1, u16> button_11; // L1
164 // BitField<11, 1, u16> button_12; // R1
165 // BitField<12, 1, u16> button_13; // Triangle
166 // BitField<13, 1, u16> button_14; // Circle
167 // BitField<14, 1, u16> button_15; // Cross
168 // BitField<15, 1, u16> button_16; // Square
169 // } digital_button;
170
171 u8 home;
172 /// If the device supports a "click" on the touchpad, this will change to 1 when a click happens
173 u8 touch_hard_press{};
174 u8 left_stick_x{};
175 u8 left_stick_y{};
176 u8 right_stick_x{};
177 u8 right_stick_y{};
178
179 struct AnalogButton {
180 u8 button_8{};
181 u8 button_7{};
182 u8 button_6{};
183 u8 button_5{};
184 u8 button_12{};
185 u8 button_11{};
186 u8 button_10{};
187 u8 button_9{};
188 u8 button_16{};
189 u8 button_15{};
190 u8 button_14{};
191 u8 button_13{};
192 } analog_button;
193
194 std::array<TouchPad, 2> touch;
195
196 u64_le motion_timestamp;
197
198 struct Accelerometer {
199 float x{};
200 float y{};
201 float z{};
202 } accel;
203
204 struct Gyroscope {
205 float pitch{};
206 float yaw{};
207 float roll{};
208 } gyro;
209};
210#pragma pack(pop)
211
212static_assert(sizeof(PadData) == 80, "UDP Response PadData struct has wrong size ");
213static_assert(std::is_trivially_copyable_v<PadData>,
214 "UDP Response PadData is not trivially copyable");
215
216static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE,
217 "UDP MAX_PACKET_SIZE is no longer larger than Message<PadData>");
218
219static_assert(sizeof(PadData::AnalogButton) == 12,
220 "UDP Response AnalogButton struct has wrong size ");
221static_assert(sizeof(PadData::Accelerometer) == 12,
222 "UDP Response Accelerometer struct has wrong size ");
223static_assert(sizeof(PadData::Gyroscope) == 12, "UDP Response Gyroscope struct has wrong size ");
224
225/**
226 * Create a Response Message from the data
227 * @param data array of bytes sent from the server
228 * @return boost::none if it failed to parse or Type if it succeeded. The client can then safely
229 * copy the data into the appropriate struct for that Type
230 */
231std::optional<Type> Validate(u8* data, std::size_t size);
232
233} // namespace Response
234
235template <>
236constexpr Type GetMessageType<Request::Version>() {
237 return Type::Version;
238}
239template <>
240constexpr Type GetMessageType<Request::PortInfo>() {
241 return Type::PortInfo;
242}
243template <>
244constexpr Type GetMessageType<Request::PadData>() {
245 return Type::PadData;
246}
247template <>
248constexpr Type GetMessageType<Response::Version>() {
249 return Type::Version;
250}
251template <>
252constexpr Type GetMessageType<Response::PortInfo>() {
253 return Type::PortInfo;
254}
255template <>
256constexpr Type GetMessageType<Response::PadData>() {
257 return Type::PadData;
258}
259} // namespace InputCommon::CemuhookUDP