summaryrefslogtreecommitdiff
path: root/src/input_common
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/input_common/CMakeLists.txt41
-rwxr-xr-xsrc/input_common/analog_from_button.cpp134
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp457
-rw-r--r--src/input_common/gcadapter/gc_adapter.h146
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp226
-rw-r--r--src/input_common/gcadapter/gc_poller.h11
-rw-r--r--src/input_common/keyboard.cpp5
-rw-r--r--src/input_common/main.cpp98
-rw-r--r--src/input_common/main.h41
-rw-r--r--src/input_common/motion_emu.cpp178
-rw-r--r--src/input_common/motion_emu.h46
-rw-r--r--src/input_common/motion_from_button.cpp34
-rw-r--r--src/input_common/motion_from_button.h25
-rw-r--r--src/input_common/motion_input.cpp144
-rw-r--r--src/input_common/motion_input.h32
-rw-r--r--src/input_common/mouse/mouse_input.cpp129
-rw-r--r--src/input_common/mouse/mouse_input.h98
-rw-r--r--src/input_common/mouse/mouse_poller.cpp274
-rw-r--r--src/input_common/mouse/mouse_poller.h109
-rw-r--r--src/input_common/sdl/sdl.h2
-rw-r--r--src/input_common/sdl/sdl_impl.cpp558
-rw-r--r--src/input_common/sdl/sdl_impl.h4
-rw-r--r--src/input_common/settings.cpp21
-rw-r--r--src/input_common/settings.h37
-rw-r--r--src/input_common/touch_from_button.cpp11
-rw-r--r--src/input_common/udp/client.cpp234
-rw-r--r--src/input_common/udp/client.h52
-rw-r--r--src/input_common/udp/protocol.h11
-rw-r--r--src/input_common/udp/udp.cpp84
29 files changed, 2314 insertions, 928 deletions
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 09361e37e..38ab31898 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -5,8 +5,8 @@ add_library(input_common STATIC
5 keyboard.h 5 keyboard.h
6 main.cpp 6 main.cpp
7 main.h 7 main.h
8 motion_emu.cpp 8 motion_from_button.cpp
9 motion_emu.h 9 motion_from_button.h
10 motion_input.cpp 10 motion_input.cpp
11 motion_input.h 11 motion_input.h
12 settings.cpp 12 settings.cpp
@@ -17,6 +17,10 @@ add_library(input_common STATIC
17 gcadapter/gc_adapter.h 17 gcadapter/gc_adapter.h
18 gcadapter/gc_poller.cpp 18 gcadapter/gc_poller.cpp
19 gcadapter/gc_poller.h 19 gcadapter/gc_poller.h
20 mouse/mouse_input.cpp
21 mouse/mouse_input.h
22 mouse/mouse_poller.cpp
23 mouse/mouse_poller.h
20 sdl/sdl.cpp 24 sdl/sdl.cpp
21 sdl/sdl.h 25 sdl/sdl.h
22 udp/client.cpp 26 udp/client.cpp
@@ -27,6 +31,39 @@ add_library(input_common STATIC
27 udp/udp.h 31 udp/udp.h
28) 32)
29 33
34if (MSVC)
35 target_compile_options(input_common PRIVATE
36 /W4
37 /WX
38
39 # 'expression' : signed/unsigned mismatch
40 /we4018
41 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point)
42 /we4244
43 # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch
44 /we4245
45 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
46 /we4254
47 # 'var' : conversion from 'size_t' to 'type', possible loss of data
48 /we4267
49 # 'context' : truncation from 'type1' to 'type2'
50 /we4305
51 )
52else()
53 target_compile_options(input_common PRIVATE
54 -Werror
55 -Werror=conversion
56 -Werror=ignored-qualifiers
57 -Werror=implicit-fallthrough
58 -Werror=reorder
59 -Werror=shadow
60 -Werror=sign-compare
61 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
62 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
63 -Werror=unused-variable
64 )
65endif()
66
30if(SDL2_FOUND) 67if(SDL2_FOUND)
31 target_sources(input_common PRIVATE 68 target_sources(input_common PRIVATE
32 sdl/sdl_impl.cpp 69 sdl/sdl_impl.cpp
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp
index 6cabdaa3c..40b516f85 100755
--- a/src/input_common/analog_from_button.cpp
+++ b/src/input_common/analog_from_button.cpp
@@ -2,6 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono>
6#include <cmath>
7#include <thread>
8#include "common/math_util.h"
9#include "core/settings.h"
5#include "input_common/analog_from_button.h" 10#include "input_common/analog_from_button.h"
6 11
7namespace InputCommon { 12namespace InputCommon {
@@ -11,27 +16,123 @@ public:
11 using Button = std::unique_ptr<Input::ButtonDevice>; 16 using Button = std::unique_ptr<Input::ButtonDevice>;
12 17
13 Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_, 18 Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_,
14 float modifier_scale_) 19 float modifier_scale_, float modifier_angle_)
15 : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)), 20 : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
16 right(std::move(right_)), modifier(std::move(modifier_)), 21 right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
17 modifier_scale(modifier_scale_) {} 22 modifier_angle(modifier_angle_) {
23 update_thread = std::thread(&Analog::UpdateStatus, this);
24 }
25
26 ~Analog() override {
27 update_thread_running = false;
28 if (update_thread.joinable()) {
29 update_thread.join();
30 }
31 }
32
33 void MoveToDirection(bool enable, float to_angle) {
34 if (!enable) {
35 return;
36 }
37 constexpr float TAU = Common::PI * 2.0f;
38 // Use wider angle to ease the transition.
39 constexpr float aperture = TAU * 0.15f;
40 const float top_limit = to_angle + aperture;
41 const float bottom_limit = to_angle - aperture;
42
43 if ((angle > to_angle && angle <= top_limit) ||
44 (angle + TAU > to_angle && angle + TAU <= top_limit)) {
45 angle -= modifier_angle;
46 if (angle < 0) {
47 angle += TAU;
48 }
49 } else if ((angle >= bottom_limit && angle < to_angle) ||
50 (angle - TAU >= bottom_limit && angle - TAU < to_angle)) {
51 angle += modifier_angle;
52 if (angle >= TAU) {
53 angle -= TAU;
54 }
55 } else {
56 angle = to_angle;
57 }
58 }
59
60 void UpdateStatus() {
61 while (update_thread_running) {
62 const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
63
64 bool r = right->GetStatus();
65 bool l = left->GetStatus();
66 bool u = up->GetStatus();
67 bool d = down->GetStatus();
68
69 // Eliminate contradictory movements
70 if (r && l) {
71 r = false;
72 l = false;
73 }
74 if (u && d) {
75 u = false;
76 d = false;
77 }
78
79 // Move to the right
80 MoveToDirection(r && !u && !d, 0.0f);
81
82 // Move to the upper right
83 MoveToDirection(r && u && !d, Common::PI * 0.25f);
84
85 // Move up
86 MoveToDirection(u && !l && !r, Common::PI * 0.5f);
87
88 // Move to the upper left
89 MoveToDirection(l && u && !d, Common::PI * 0.75f);
90
91 // Move to the left
92 MoveToDirection(l && !u && !d, Common::PI);
93
94 // Move to the bottom left
95 MoveToDirection(l && !u && d, Common::PI * 1.25f);
96
97 // Move down
98 MoveToDirection(d && !l && !r, Common::PI * 1.5f);
99
100 // Move to the bottom right
101 MoveToDirection(r && !u && d, Common::PI * 1.75f);
102
103 // Move if a key is pressed
104 if (r || l || u || d) {
105 amplitude = coef;
106 } else {
107 amplitude = 0;
108 }
109
110 // Delay the update rate to 100hz
111 std::this_thread::sleep_for(std::chrono::milliseconds(10));
112 }
113 }
18 114
19 std::tuple<float, float> GetStatus() const override { 115 std::tuple<float, float> GetStatus() const override {
116 if (Settings::values.emulate_analog_keyboard) {
117 return std::make_tuple(std::cos(angle) * amplitude, std::sin(angle) * amplitude);
118 }
20 constexpr float SQRT_HALF = 0.707106781f; 119 constexpr float SQRT_HALF = 0.707106781f;
21 int x = 0, y = 0; 120 int x = 0, y = 0;
22 121 if (right->GetStatus()) {
23 if (right->GetStatus())
24 ++x; 122 ++x;
25 if (left->GetStatus()) 123 }
124 if (left->GetStatus()) {
26 --x; 125 --x;
27 if (up->GetStatus()) 126 }
127 if (up->GetStatus()) {
28 ++y; 128 ++y;
29 if (down->GetStatus()) 129 }
130 if (down->GetStatus()) {
30 --y; 131 --y;
31 132 }
32 float coef = modifier->GetStatus() ? modifier_scale : 1.0f; 133 const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
33 return std::make_tuple(x * coef * (y == 0 ? 1.0f : SQRT_HALF), 134 return std::make_tuple(static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF),
34 y * coef * (x == 0 ? 1.0f : SQRT_HALF)); 135 static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF));
35 } 136 }
36 137
37 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 138 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
@@ -55,6 +156,11 @@ private:
55 Button right; 156 Button right;
56 Button modifier; 157 Button modifier;
57 float modifier_scale; 158 float modifier_scale;
159 float modifier_angle;
160 float angle{};
161 float amplitude{};
162 std::thread update_thread;
163 bool update_thread_running{true};
58}; 164};
59 165
60std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) { 166std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) {
@@ -65,8 +171,10 @@ std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::Para
65 auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine)); 171 auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine));
66 auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine)); 172 auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine));
67 auto modifier_scale = params.Get("modifier_scale", 0.5f); 173 auto modifier_scale = params.Get("modifier_scale", 0.5f);
174 auto modifier_angle = params.Get("modifier_angle", 0.035f);
68 return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left), 175 return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left),
69 std::move(right), std::move(modifier), modifier_scale); 176 std::move(right), std::move(modifier), modifier_scale,
177 modifier_angle);
70} 178}
71 179
72} // namespace InputCommon 180} // namespace InputCommon
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index 89c148aba..d80195c82 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -21,14 +21,6 @@
21 21
22namespace GCAdapter { 22namespace GCAdapter {
23 23
24/// Used to loop through and assign button in poller
25constexpr std::array<PadButton, 12> PadButtonArray{
26 PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, PadButton::PAD_BUTTON_DOWN,
27 PadButton::PAD_BUTTON_UP, PadButton::PAD_TRIGGER_Z, PadButton::PAD_TRIGGER_R,
28 PadButton::PAD_TRIGGER_L, PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B,
29 PadButton::PAD_BUTTON_X, PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_START,
30};
31
32Adapter::Adapter() { 24Adapter::Adapter() {
33 if (usb_adapter_handle != nullptr) { 25 if (usb_adapter_handle != nullptr) {
34 return; 26 return;
@@ -37,179 +29,261 @@ Adapter::Adapter() {
37 29
38 const int init_res = libusb_init(&libusb_ctx); 30 const int init_res = libusb_init(&libusb_ctx);
39 if (init_res == LIBUSB_SUCCESS) { 31 if (init_res == LIBUSB_SUCCESS) {
40 Setup(); 32 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this);
41 } else { 33 } else {
42 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res); 34 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
43 } 35 }
44} 36}
45 37
46GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) { 38Adapter::~Adapter() {
47 GCPadStatus pad = {}; 39 Reset();
48 const std::size_t offset = 1 + (9 * port); 40}
49 41
50 adapter_controllers_status[port] = static_cast<ControllerTypes>(adapter_payload[offset] >> 4); 42void Adapter::AdapterInputThread() {
43 LOG_DEBUG(Input, "GC Adapter input thread started");
44 s32 payload_size{};
45 AdapterPayload adapter_payload{};
51 46
52 static constexpr std::array<PadButton, 8> b1_buttons{ 47 if (adapter_scan_thread.joinable()) {
53 PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X, 48 adapter_scan_thread.join();
54 PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, 49 }
55 PadButton::PAD_BUTTON_DOWN, PadButton::PAD_BUTTON_UP,
56 };
57 50
58 static constexpr std::array<PadButton, 4> b2_buttons{ 51 while (adapter_input_thread_running) {
59 PadButton::PAD_BUTTON_START, 52 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
60 PadButton::PAD_TRIGGER_Z, 53 static_cast<s32>(adapter_payload.size()), &payload_size, 16);
61 PadButton::PAD_TRIGGER_R, 54 if (IsPayloadCorrect(adapter_payload, payload_size)) {
62 PadButton::PAD_TRIGGER_L, 55 UpdateControllers(adapter_payload);
63 }; 56 UpdateVibrations();
57 }
58 std::this_thread::yield();
59 }
64 60
65 static constexpr std::array<PadAxes, 6> axes{ 61 if (restart_scan_thread) {
66 PadAxes::StickX, PadAxes::StickY, PadAxes::SubstickX, 62 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this);
67 PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight, 63 restart_scan_thread = false;
68 }; 64 }
65}
69 66
70 if (adapter_controllers_status[port] == ControllerTypes::None && !get_origin[port]) { 67bool Adapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size) {
71 // Controller may have been disconnected, recalibrate if reconnected. 68 if (payload_size != static_cast<s32>(adapter_payload.size()) ||
72 get_origin[port] = true; 69 adapter_payload[0] != LIBUSB_DT_HID) {
70 LOG_DEBUG(Input, "Error reading payload (size: {}, type: {:02x})", payload_size,
71 adapter_payload[0]);
72 if (input_error_counter++ > 20) {
73 LOG_ERROR(Input, "GC adapter timeout, Is the adapter connected?");
74 adapter_input_thread_running = false;
75 restart_scan_thread = true;
76 }
77 return false;
73 } 78 }
74 79
75 if (adapter_controllers_status[port] != ControllerTypes::None) { 80 input_error_counter = 0;
76 const u8 b1 = adapter_payload[offset + 1]; 81 return true;
77 const u8 b2 = adapter_payload[offset + 2]; 82}
78 83
79 for (std::size_t i = 0; i < b1_buttons.size(); ++i) { 84void Adapter::UpdateControllers(const AdapterPayload& adapter_payload) {
80 if ((b1 & (1U << i)) != 0) { 85 for (std::size_t port = 0; port < pads.size(); ++port) {
81 pad.button |= static_cast<u16>(b1_buttons[i]); 86 const std::size_t offset = 1 + (9 * port);
87 const auto type = static_cast<ControllerTypes>(adapter_payload[offset] >> 4);
88 UpdatePadType(port, type);
89 if (DeviceConnected(port)) {
90 const u8 b1 = adapter_payload[offset + 1];
91 const u8 b2 = adapter_payload[offset + 2];
92 UpdateStateButtons(port, b1, b2);
93 UpdateStateAxes(port, adapter_payload);
94 if (configuring) {
95 UpdateYuzuSettings(port);
82 } 96 }
83 } 97 }
98 }
99}
84 100
85 for (std::size_t j = 0; j < b2_buttons.size(); ++j) { 101void Adapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) {
86 if ((b2 & (1U << j)) != 0) { 102 if (pads[port].type == pad_type) {
87 pad.button |= static_cast<u16>(b2_buttons[j]); 103 return;
88 } 104 }
89 } 105 // Device changed reset device and set new type
90 for (PadAxes axis : axes) { 106 ResetDevice(port);
91 const std::size_t index = static_cast<std::size_t>(axis); 107 pads[port].type = pad_type;
92 pad.axis_values[index] = adapter_payload[offset + 3 + index]; 108}
109
110void Adapter::UpdateStateButtons(std::size_t port, u8 b1, u8 b2) {
111 if (port >= pads.size()) {
112 return;
113 }
114
115 static constexpr std::array<PadButton, 8> b1_buttons{
116 PadButton::ButtonA, PadButton::ButtonB, PadButton::ButtonX, PadButton::ButtonY,
117 PadButton::ButtonLeft, PadButton::ButtonRight, PadButton::ButtonDown, PadButton::ButtonUp,
118 };
119
120 static constexpr std::array<PadButton, 4> b2_buttons{
121 PadButton::ButtonStart,
122 PadButton::TriggerZ,
123 PadButton::TriggerR,
124 PadButton::TriggerL,
125 };
126 pads[port].buttons = 0;
127 for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
128 if ((b1 & (1U << i)) != 0) {
129 pads[port].buttons =
130 static_cast<u16>(pads[port].buttons | static_cast<u16>(b1_buttons[i]));
131 pads[port].last_button = b1_buttons[i];
93 } 132 }
133 }
94 134
95 if (get_origin[port]) { 135 for (std::size_t j = 0; j < b2_buttons.size(); ++j) {
96 origin_status[port].axis_values = pad.axis_values; 136 if ((b2 & (1U << j)) != 0) {
97 get_origin[port] = false; 137 pads[port].buttons =
138 static_cast<u16>(pads[port].buttons | static_cast<u16>(b2_buttons[j]));
139 pads[port].last_button = b2_buttons[j];
98 } 140 }
99 } 141 }
100 return pad;
101} 142}
102 143
103void Adapter::PadToState(const GCPadStatus& pad, GCState& state) { 144void Adapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload) {
104 for (const auto& button : PadButtonArray) { 145 if (port >= pads.size()) {
105 const u16 button_value = static_cast<u16>(button); 146 return;
106 state.buttons.insert_or_assign(button_value, pad.button & button_value);
107 } 147 }
108 148
109 for (size_t i = 0; i < pad.axis_values.size(); ++i) { 149 const std::size_t offset = 1 + (9 * port);
110 state.axes.insert_or_assign(static_cast<u8>(i), pad.axis_values[i]); 150 static constexpr std::array<PadAxes, 6> axes{
151 PadAxes::StickX, PadAxes::StickY, PadAxes::SubstickX,
152 PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight,
153 };
154
155 for (const PadAxes axis : axes) {
156 const auto index = static_cast<std::size_t>(axis);
157 const u8 axis_value = adapter_payload[offset + 3 + index];
158 if (pads[port].axis_origin[index] == 255) {
159 pads[port].axis_origin[index] = axis_value;
160 }
161 pads[port].axis_values[index] =
162 static_cast<s16>(axis_value - pads[port].axis_origin[index]);
111 } 163 }
112} 164}
113 165
114void Adapter::Read() { 166void Adapter::UpdateYuzuSettings(std::size_t port) {
115 LOG_DEBUG(Input, "GC Adapter Read() thread started"); 167 if (port >= pads.size()) {
168 return;
169 }
116 170
117 int payload_size; 171 constexpr u8 axis_threshold = 50;
118 std::array<u8, 37> adapter_payload; 172 GCPadStatus pad_status = {.port = port};
119 std::array<GCPadStatus, 4> pads;
120 173
121 while (adapter_thread_running) { 174 if (pads[port].buttons != 0) {
122 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(), 175 pad_status.button = pads[port].last_button;
123 sizeof(adapter_payload), &payload_size, 16); 176 pad_queue.Push(pad_status);
124 177 }
125 if (payload_size != sizeof(adapter_payload) || adapter_payload[0] != LIBUSB_DT_HID) {
126 LOG_ERROR(Input,
127 "Error reading payload (size: {}, type: {:02x}) Is the adapter connected?",
128 payload_size, adapter_payload[0]);
129 adapter_thread_running = false; // error reading from adapter, stop reading.
130 break;
131 }
132 for (std::size_t port = 0; port < pads.size(); ++port) {
133 pads[port] = GetPadStatus(port, adapter_payload);
134 if (DeviceConnected(port) && configuring) {
135 if (pads[port].button != 0) {
136 pad_queue[port].Push(pads[port]);
137 }
138 178
139 // Accounting for a threshold here to ensure an intentional press 179 // Accounting for a threshold here to ensure an intentional press
140 for (size_t i = 0; i < pads[port].axis_values.size(); ++i) { 180 for (std::size_t i = 0; i < pads[port].axis_values.size(); ++i) {
141 const u8 value = pads[port].axis_values[i]; 181 const s16 value = pads[port].axis_values[i];
142 const u8 origin = origin_status[port].axis_values[i]; 182
143 183 if (value > axis_threshold || value < -axis_threshold) {
144 if (value > origin + pads[port].THRESHOLD || 184 pad_status.axis = static_cast<PadAxes>(i);
145 value < origin - pads[port].THRESHOLD) { 185 pad_status.axis_value = value;
146 pads[port].axis = static_cast<PadAxes>(i); 186 pad_status.axis_threshold = axis_threshold;
147 pads[port].axis_value = pads[port].axis_values[i]; 187 pad_queue.Push(pad_status);
148 pad_queue[port].Push(pads[port]);
149 }
150 }
151 }
152 PadToState(pads[port], state[port]);
153 } 188 }
154 std::this_thread::yield();
155 } 189 }
156} 190}
157 191
158void Adapter::Setup() { 192void Adapter::UpdateVibrations() {
159 // Initialize all controllers as unplugged 193 // Use 8 states to keep the switching between on/off fast enough for
160 adapter_controllers_status.fill(ControllerTypes::None); 194 // a human to not notice the difference between switching from on/off
161 // Initialize all ports to store axis origin values 195 // More states = more rumble strengths = slower update time
162 get_origin.fill(true); 196 constexpr u8 vibration_states = 8;
163 197
164 // pointer to list of connected usb devices 198 vibration_counter = (vibration_counter + 1) % vibration_states;
165 libusb_device** devices{}; 199
166 200 for (GCController& pad : pads) {
167 // populate the list of devices, get the count 201 const bool vibrate = pad.rumble_amplitude > vibration_counter;
168 const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices); 202 vibration_changed |= vibrate != pad.enable_vibration;
169 if (device_count < 0) { 203 pad.enable_vibration = vibrate;
170 LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count);
171 return;
172 } 204 }
205 SendVibrations();
206}
173 207
174 if (devices != nullptr) { 208void Adapter::SendVibrations() {
175 for (std::size_t index = 0; index < static_cast<std::size_t>(device_count); ++index) { 209 if (!rumble_enabled || !vibration_changed) {
176 if (CheckDeviceAccess(devices[index])) { 210 return;
177 // GC Adapter found and accessible, registering it 211 }
178 GetGCEndpoint(devices[index]); 212 s32 size{};
179 break; 213 constexpr u8 rumble_command = 0x11;
180 } 214 const u8 p1 = pads[0].enable_vibration;
215 const u8 p2 = pads[1].enable_vibration;
216 const u8 p3 = pads[2].enable_vibration;
217 const u8 p4 = pads[3].enable_vibration;
218 std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4};
219 const int err = libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, payload.data(),
220 static_cast<s32>(payload.size()), &size, 16);
221 if (err) {
222 LOG_DEBUG(Input, "Adapter libusb write failed: {}", libusb_error_name(err));
223 if (output_error_counter++ > 5) {
224 LOG_ERROR(Input, "GC adapter output timeout, Rumble disabled");
225 rumble_enabled = false;
181 } 226 }
182 libusb_free_device_list(devices, 1); 227 return;
183 } 228 }
229 output_error_counter = 0;
230 vibration_changed = false;
184} 231}
185 232
186bool Adapter::CheckDeviceAccess(libusb_device* device) { 233bool Adapter::RumblePlay(std::size_t port, u8 amplitude) {
187 libusb_device_descriptor desc; 234 pads[port].rumble_amplitude = amplitude;
188 const int get_descriptor_error = libusb_get_device_descriptor(device, &desc); 235
189 if (get_descriptor_error) { 236 return rumble_enabled;
190 // could not acquire the descriptor, no point in trying to use it. 237}
191 LOG_ERROR(Input, "libusb_get_device_descriptor failed with error: {}", 238
192 get_descriptor_error); 239void Adapter::AdapterScanThread() {
193 return false; 240 adapter_scan_thread_running = true;
241 adapter_input_thread_running = false;
242 if (adapter_input_thread.joinable()) {
243 adapter_input_thread.join();
194 } 244 }
245 ClearLibusbHandle();
246 ResetDevices();
247 while (adapter_scan_thread_running && !adapter_input_thread_running) {
248 Setup();
249 std::this_thread::sleep_for(std::chrono::seconds(1));
250 }
251}
195 252
196 if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) { 253void Adapter::Setup() {
197 // This isn't the device we are looking for. 254 usb_adapter_handle = libusb_open_device_with_vid_pid(libusb_ctx, 0x057e, 0x0337);
198 return false; 255
256 if (usb_adapter_handle == NULL) {
257 return;
258 }
259 if (!CheckDeviceAccess()) {
260 ClearLibusbHandle();
261 return;
199 } 262 }
200 const int open_error = libusb_open(device, &usb_adapter_handle);
201 263
202 if (open_error == LIBUSB_ERROR_ACCESS) { 264 libusb_device* device = libusb_get_device(usb_adapter_handle);
203 LOG_ERROR(Input, "Yuzu can not gain access to this device: ID {:04X}:{:04X}.", 265
204 desc.idVendor, desc.idProduct); 266 LOG_INFO(Input, "GC adapter is now connected");
205 return false; 267 // GC Adapter found and accessible, registering it
268 if (GetGCEndpoint(device)) {
269 adapter_scan_thread_running = false;
270 adapter_input_thread_running = true;
271 rumble_enabled = true;
272 input_error_counter = 0;
273 output_error_counter = 0;
274 adapter_input_thread = std::thread(&Adapter::AdapterInputThread, this);
206 } 275 }
207 if (open_error) { 276}
208 LOG_ERROR(Input, "libusb_open failed to open device with error = {}", open_error); 277
209 return false; 278bool Adapter::CheckDeviceAccess() {
279 // This fixes payload problems from offbrand GCAdapters
280 const s32 control_transfer_error =
281 libusb_control_transfer(usb_adapter_handle, 0x21, 11, 0x0001, 0, nullptr, 0, 1000);
282 if (control_transfer_error < 0) {
283 LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error);
210 } 284 }
211 285
212 int kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0); 286 s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0);
213 if (kernel_driver_error == 1) { 287 if (kernel_driver_error == 1) {
214 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0); 288 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0);
215 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { 289 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
@@ -235,13 +309,13 @@ bool Adapter::CheckDeviceAccess(libusb_device* device) {
235 return true; 309 return true;
236} 310}
237 311
238void Adapter::GetGCEndpoint(libusb_device* device) { 312bool Adapter::GetGCEndpoint(libusb_device* device) {
239 libusb_config_descriptor* config = nullptr; 313 libusb_config_descriptor* config = nullptr;
240 const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config); 314 const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
241 if (config_descriptor_return != LIBUSB_SUCCESS) { 315 if (config_descriptor_return != LIBUSB_SUCCESS) {
242 LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}", 316 LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}",
243 config_descriptor_return); 317 config_descriptor_return);
244 return; 318 return false;
245 } 319 }
246 320
247 for (u8 ic = 0; ic < config->bNumInterfaces; ic++) { 321 for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
@@ -250,7 +324,7 @@ void Adapter::GetGCEndpoint(libusb_device* device) {
250 const libusb_interface_descriptor* interface = &interfaceContainer->altsetting[i]; 324 const libusb_interface_descriptor* interface = &interfaceContainer->altsetting[i];
251 for (u8 e = 0; e < interface->bNumEndpoints; e++) { 325 for (u8 e = 0; e < interface->bNumEndpoints; e++) {
252 const libusb_endpoint_descriptor* endpoint = &interface->endpoint[e]; 326 const libusb_endpoint_descriptor* endpoint = &interface->endpoint[e];
253 if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) { 327 if ((endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) != 0) {
254 input_endpoint = endpoint->bEndpointAddress; 328 input_endpoint = endpoint->bEndpointAddress;
255 } else { 329 } else {
256 output_endpoint = endpoint->bEndpointAddress; 330 output_endpoint = endpoint->bEndpointAddress;
@@ -263,31 +337,51 @@ void Adapter::GetGCEndpoint(libusb_device* device) {
263 unsigned char clear_payload = 0x13; 337 unsigned char clear_payload = 0x13;
264 libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload, 338 libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload,
265 sizeof(clear_payload), nullptr, 16); 339 sizeof(clear_payload), nullptr, 16);
266 340 return true;
267 adapter_thread_running = true;
268 adapter_input_thread = std::thread(&Adapter::Read, this);
269} 341}
270 342
271Adapter::~Adapter() { 343void Adapter::JoinThreads() {
272 Reset(); 344 restart_scan_thread = false;
273} 345 adapter_input_thread_running = false;
346 adapter_scan_thread_running = false;
274 347
275void Adapter::Reset() { 348 if (adapter_scan_thread.joinable()) {
276 if (adapter_thread_running) { 349 adapter_scan_thread.join();
277 adapter_thread_running = false;
278 } 350 }
351
279 if (adapter_input_thread.joinable()) { 352 if (adapter_input_thread.joinable()) {
280 adapter_input_thread.join(); 353 adapter_input_thread.join();
281 } 354 }
355}
282 356
283 adapter_controllers_status.fill(ControllerTypes::None); 357void Adapter::ClearLibusbHandle() {
284 get_origin.fill(true);
285
286 if (usb_adapter_handle) { 358 if (usb_adapter_handle) {
287 libusb_release_interface(usb_adapter_handle, 1); 359 libusb_release_interface(usb_adapter_handle, 1);
288 libusb_close(usb_adapter_handle); 360 libusb_close(usb_adapter_handle);
289 usb_adapter_handle = nullptr; 361 usb_adapter_handle = nullptr;
290 } 362 }
363}
364
365void Adapter::ResetDevices() {
366 for (std::size_t i = 0; i < pads.size(); ++i) {
367 ResetDevice(i);
368 }
369}
370
371void Adapter::ResetDevice(std::size_t port) {
372 pads[port].type = ControllerTypes::None;
373 pads[port].enable_vibration = false;
374 pads[port].rumble_amplitude = 0;
375 pads[port].buttons = 0;
376 pads[port].last_button = PadButton::Undefined;
377 pads[port].axis_values.fill(0);
378 pads[port].axis_origin.fill(255);
379}
380
381void Adapter::Reset() {
382 JoinThreads();
383 ClearLibusbHandle();
384 ResetDevices();
291 385
292 if (libusb_ctx) { 386 if (libusb_ctx) {
293 libusb_exit(libusb_ctx); 387 libusb_exit(libusb_ctx);
@@ -296,11 +390,11 @@ void Adapter::Reset() {
296 390
297std::vector<Common::ParamPackage> Adapter::GetInputDevices() const { 391std::vector<Common::ParamPackage> Adapter::GetInputDevices() const {
298 std::vector<Common::ParamPackage> devices; 392 std::vector<Common::ParamPackage> devices;
299 for (std::size_t port = 0; port < state.size(); ++port) { 393 for (std::size_t port = 0; port < pads.size(); ++port) {
300 if (!DeviceConnected(port)) { 394 if (!DeviceConnected(port)) {
301 continue; 395 continue;
302 } 396 }
303 std::string name = fmt::format("Gamecube Controller {}", port); 397 std::string name = fmt::format("Gamecube Controller {}", port + 1);
304 devices.emplace_back(Common::ParamPackage{ 398 devices.emplace_back(Common::ParamPackage{
305 {"class", "gcpad"}, 399 {"class", "gcpad"},
306 {"display", std::move(name)}, 400 {"display", std::move(name)},
@@ -317,18 +411,18 @@ InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
317 // This list also excludes any button that can't be really mapped 411 // This list also excludes any button that can't be really mapped
318 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12> 412 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12>
319 switch_to_gcadapter_button = { 413 switch_to_gcadapter_button = {
320 std::pair{Settings::NativeButton::A, PadButton::PAD_BUTTON_A}, 414 std::pair{Settings::NativeButton::A, PadButton::ButtonA},
321 {Settings::NativeButton::B, PadButton::PAD_BUTTON_B}, 415 {Settings::NativeButton::B, PadButton::ButtonB},
322 {Settings::NativeButton::X, PadButton::PAD_BUTTON_X}, 416 {Settings::NativeButton::X, PadButton::ButtonX},
323 {Settings::NativeButton::Y, PadButton::PAD_BUTTON_Y}, 417 {Settings::NativeButton::Y, PadButton::ButtonY},
324 {Settings::NativeButton::Plus, PadButton::PAD_BUTTON_START}, 418 {Settings::NativeButton::Plus, PadButton::ButtonStart},
325 {Settings::NativeButton::DLeft, PadButton::PAD_BUTTON_LEFT}, 419 {Settings::NativeButton::DLeft, PadButton::ButtonLeft},
326 {Settings::NativeButton::DUp, PadButton::PAD_BUTTON_UP}, 420 {Settings::NativeButton::DUp, PadButton::ButtonUp},
327 {Settings::NativeButton::DRight, PadButton::PAD_BUTTON_RIGHT}, 421 {Settings::NativeButton::DRight, PadButton::ButtonRight},
328 {Settings::NativeButton::DDown, PadButton::PAD_BUTTON_DOWN}, 422 {Settings::NativeButton::DDown, PadButton::ButtonDown},
329 {Settings::NativeButton::SL, PadButton::PAD_TRIGGER_L}, 423 {Settings::NativeButton::SL, PadButton::TriggerL},
330 {Settings::NativeButton::SR, PadButton::PAD_TRIGGER_R}, 424 {Settings::NativeButton::SR, PadButton::TriggerR},
331 {Settings::NativeButton::R, PadButton::PAD_TRIGGER_Z}, 425 {Settings::NativeButton::R, PadButton::TriggerZ},
332 }; 426 };
333 if (!params.Has("port")) { 427 if (!params.Has("port")) {
334 return {}; 428 return {};
@@ -351,8 +445,10 @@ InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
351 for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) { 445 for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) {
352 Common::ParamPackage button_params({{"engine", "gcpad"}}); 446 Common::ParamPackage button_params({{"engine", "gcpad"}});
353 button_params.Set("port", params.Get("port", 0)); 447 button_params.Set("port", params.Get("port", 0));
354 button_params.Set("button", static_cast<int>(PadButton::PAD_STICK)); 448 button_params.Set("button", static_cast<s32>(PadButton::Stick));
355 button_params.Set("axis", static_cast<int>(gcadapter_axis)); 449 button_params.Set("axis", static_cast<s32>(gcadapter_axis));
450 button_params.Set("threshold", 0.5f);
451 button_params.Set("direction", "+");
356 mapping.insert_or_assign(switch_button, std::move(button_params)); 452 mapping.insert_or_assign(switch_button, std::move(button_params));
357 } 453 }
358 return mapping; 454 return mapping;
@@ -381,46 +477,33 @@ InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice(
381} 477}
382 478
383bool Adapter::DeviceConnected(std::size_t port) const { 479bool Adapter::DeviceConnected(std::size_t port) const {
384 return adapter_controllers_status[port] != ControllerTypes::None; 480 return pads[port].type != ControllerTypes::None;
385}
386
387void Adapter::ResetDeviceType(std::size_t port) {
388 adapter_controllers_status[port] = ControllerTypes::None;
389} 481}
390 482
391void Adapter::BeginConfiguration() { 483void Adapter::BeginConfiguration() {
392 get_origin.fill(true); 484 pad_queue.Clear();
393 for (auto& pq : pad_queue) {
394 pq.Clear();
395 }
396 configuring = true; 485 configuring = true;
397} 486}
398 487
399void Adapter::EndConfiguration() { 488void Adapter::EndConfiguration() {
400 for (auto& pq : pad_queue) { 489 pad_queue.Clear();
401 pq.Clear();
402 }
403 configuring = false; 490 configuring = false;
404} 491}
405 492
406std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() { 493Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() {
407 return pad_queue; 494 return pad_queue;
408} 495}
409 496
410const std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() const { 497const Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() const {
411 return pad_queue; 498 return pad_queue;
412} 499}
413 500
414std::array<GCState, 4>& Adapter::GetPadState() { 501GCController& Adapter::GetPadState(std::size_t port) {
415 return state; 502 return pads.at(port);
416}
417
418const std::array<GCState, 4>& Adapter::GetPadState() const {
419 return state;
420} 503}
421 504
422int Adapter::GetOriginValue(int port, int axis) const { 505const GCController& Adapter::GetPadState(std::size_t port) const {
423 return origin_status[port].axis_values[axis]; 506 return pads.at(port);
424} 507}
425 508
426} // namespace GCAdapter 509} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index 75bf9fe74..7a6c545bd 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -19,24 +19,23 @@ struct libusb_device_handle;
19namespace GCAdapter { 19namespace GCAdapter {
20 20
21enum class PadButton { 21enum class PadButton {
22 PAD_BUTTON_LEFT = 0x0001, 22 Undefined = 0x0000,
23 PAD_BUTTON_RIGHT = 0x0002, 23 ButtonLeft = 0x0001,
24 PAD_BUTTON_DOWN = 0x0004, 24 ButtonRight = 0x0002,
25 PAD_BUTTON_UP = 0x0008, 25 ButtonDown = 0x0004,
26 PAD_TRIGGER_Z = 0x0010, 26 ButtonUp = 0x0008,
27 PAD_TRIGGER_R = 0x0020, 27 TriggerZ = 0x0010,
28 PAD_TRIGGER_L = 0x0040, 28 TriggerR = 0x0020,
29 PAD_BUTTON_A = 0x0100, 29 TriggerL = 0x0040,
30 PAD_BUTTON_B = 0x0200, 30 ButtonA = 0x0100,
31 PAD_BUTTON_X = 0x0400, 31 ButtonB = 0x0200,
32 PAD_BUTTON_Y = 0x0800, 32 ButtonX = 0x0400,
33 PAD_BUTTON_START = 0x1000, 33 ButtonY = 0x0800,
34 ButtonStart = 0x1000,
34 // Below is for compatibility with "AxisButton" type 35 // Below is for compatibility with "AxisButton" type
35 PAD_STICK = 0x2000, 36 Stick = 0x2000,
36}; 37};
37 38
38extern const std::array<PadButton, 12> PadButtonArray;
39
40enum class PadAxes : u8 { 39enum class PadAxes : u8 {
41 StickX, 40 StickX,
42 StickY, 41 StickY,
@@ -47,89 +46,122 @@ enum class PadAxes : u8 {
47 Undefined, 46 Undefined,
48}; 47};
49 48
49enum class ControllerTypes {
50 None,
51 Wired,
52 Wireless,
53};
54
50struct GCPadStatus { 55struct GCPadStatus {
51 u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits 56 std::size_t port{};
52 57
53 std::array<u8, 6> axis_values{}; // Triggers and sticks, following indices defined in PadAxes 58 PadButton button{PadButton::Undefined}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
54 static constexpr u8 THRESHOLD = 50; // Threshold for axis press for polling
55 59
56 u8 port{};
57 PadAxes axis{PadAxes::Undefined}; 60 PadAxes axis{PadAxes::Undefined};
58 u8 axis_value{255}; 61 s16 axis_value{};
62 u8 axis_threshold{50};
59}; 63};
60 64
61struct GCState { 65struct GCController {
62 std::unordered_map<int, bool> buttons; 66 ControllerTypes type{};
63 std::unordered_map<int, u16> axes; 67 bool enable_vibration{};
68 u8 rumble_amplitude{};
69 u16 buttons{};
70 PadButton last_button{};
71 std::array<s16, 6> axis_values{};
72 std::array<u8, 6> axis_origin{};
64}; 73};
65 74
66enum class ControllerTypes { None, Wired, Wireless };
67
68class Adapter { 75class Adapter {
69public: 76public:
70 /// Initialize the GC Adapter capture and read sequence
71 Adapter(); 77 Adapter();
72
73 /// Close the adapter read thread and release the adapter
74 ~Adapter(); 78 ~Adapter();
79
80 /// Request a vibration for a controller
81 bool RumblePlay(std::size_t port, u8 amplitude);
82
75 /// Used for polling 83 /// Used for polling
76 void BeginConfiguration(); 84 void BeginConfiguration();
77 void EndConfiguration(); 85 void EndConfiguration();
78 86
87 Common::SPSCQueue<GCPadStatus>& GetPadQueue();
88 const Common::SPSCQueue<GCPadStatus>& GetPadQueue() const;
89
90 GCController& GetPadState(std::size_t port);
91 const GCController& GetPadState(std::size_t port) const;
92
93 /// Returns true if there is a device connected to port
94 bool DeviceConnected(std::size_t port) const;
95
96 /// Used for automapping features
79 std::vector<Common::ParamPackage> GetInputDevices() const; 97 std::vector<Common::ParamPackage> GetInputDevices() const;
80 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; 98 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
81 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const; 99 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
82 100
83 /// Returns true if there is a device connected to port 101private:
84 bool DeviceConnected(std::size_t port) const; 102 using AdapterPayload = std::array<u8, 37>;
85 103
86 std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue(); 104 void UpdatePadType(std::size_t port, ControllerTypes pad_type);
87 const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const; 105 void UpdateControllers(const AdapterPayload& adapter_payload);
106 void UpdateYuzuSettings(std::size_t port);
107 void UpdateStateButtons(std::size_t port, u8 b1, u8 b2);
108 void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);
109 void UpdateVibrations();
88 110
89 std::array<GCState, 4>& GetPadState(); 111 void AdapterInputThread();
90 const std::array<GCState, 4>& GetPadState() const;
91 112
92 int GetOriginValue(int port, int axis) const; 113 void AdapterScanThread();
93 114
94private: 115 bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);
95 GCPadStatus GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload); 116
117 // Updates vibration state of all controllers
118 void SendVibrations();
96 119
97 void PadToState(const GCPadStatus& pad, GCState& state); 120 /// For use in initialization, querying devices to find the adapter
121 void Setup();
98 122
99 void Read(); 123 /// Resets status of all GC controller devices to a disconnected state
124 void ResetDevices();
100 125
101 /// Resets status of device connected to port 126 /// Resets status of device connected to a disconnected state
102 void ResetDeviceType(std::size_t port); 127 void ResetDevice(std::size_t port);
103 128
104 /// Returns true if we successfully gain access to GC Adapter 129 /// Returns true if we successfully gain access to GC Adapter
105 bool CheckDeviceAccess(libusb_device* device); 130 bool CheckDeviceAccess();
106 131
107 /// Captures GC Adapter endpoint address, 132 /// Captures GC Adapter endpoint address
108 void GetGCEndpoint(libusb_device* device); 133 /// Returns true if the endpoint was set correctly
134 bool GetGCEndpoint(libusb_device* device);
109 135
110 /// For shutting down, clear all data, join all threads, release usb 136 /// For shutting down, clear all data, join all threads, release usb
111 void Reset(); 137 void Reset();
112 138
113 /// For use in initialization, querying devices to find the adapter 139 // Join all threads
114 void Setup(); 140 void JoinThreads();
141
142 // Release usb handles
143 void ClearLibusbHandle();
115 144
116 libusb_device_handle* usb_adapter_handle = nullptr; 145 libusb_device_handle* usb_adapter_handle = nullptr;
146 std::array<GCController, 4> pads;
147 Common::SPSCQueue<GCPadStatus> pad_queue;
117 148
118 std::thread adapter_input_thread; 149 std::thread adapter_input_thread;
119 bool adapter_thread_running; 150 std::thread adapter_scan_thread;
151 bool adapter_input_thread_running;
152 bool adapter_scan_thread_running;
153 bool restart_scan_thread;
120 154
121 libusb_context* libusb_ctx; 155 libusb_context* libusb_ctx;
122 156
123 u8 input_endpoint = 0; 157 u8 input_endpoint{0};
124 u8 output_endpoint = 0; 158 u8 output_endpoint{0};
125 159 u8 input_error_counter{0};
126 bool configuring = false; 160 u8 output_error_counter{0};
161 int vibration_counter{0};
127 162
128 std::array<GCState, 4> state; 163 bool configuring{false};
129 std::array<bool, 4> get_origin; 164 bool rumble_enabled{true};
130 std::array<GCPadStatus, 4> origin_status; 165 bool vibration_changed{true};
131 std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
132 std::array<ControllerTypes, 4> adapter_controllers_status{};
133}; 166};
134
135} // namespace GCAdapter 167} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index 92e9e8e89..9670bdeb2 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -15,36 +15,35 @@ namespace InputCommon {
15 15
16class GCButton final : public Input::ButtonDevice { 16class GCButton final : public Input::ButtonDevice {
17public: 17public:
18 explicit GCButton(int port_, int button_, const GCAdapter::Adapter* adapter) 18 explicit GCButton(u32 port_, s32 button_, const GCAdapter::Adapter* adapter)
19 : port(port_), button(button_), gcadapter(adapter) {} 19 : port(port_), button(button_), gcadapter(adapter) {}
20 20
21 ~GCButton() override; 21 ~GCButton() override;
22 22
23 bool GetStatus() const override { 23 bool GetStatus() const override {
24 if (gcadapter->DeviceConnected(port)) { 24 if (gcadapter->DeviceConnected(port)) {
25 return gcadapter->GetPadState()[port].buttons.at(button); 25 return (gcadapter->GetPadState(port).buttons & button) != 0;
26 } 26 }
27 return false; 27 return false;
28 } 28 }
29 29
30private: 30private:
31 const int port; 31 const u32 port;
32 const int button; 32 const s32 button;
33 const GCAdapter::Adapter* gcadapter; 33 const GCAdapter::Adapter* gcadapter;
34}; 34};
35 35
36class GCAxisButton final : public Input::ButtonDevice { 36class GCAxisButton final : public Input::ButtonDevice {
37public: 37public:
38 explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_, 38 explicit GCAxisButton(u32 port_, u32 axis_, float threshold_, bool trigger_if_greater_,
39 const GCAdapter::Adapter* adapter) 39 const GCAdapter::Adapter* adapter)
40 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_), 40 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
41 gcadapter(adapter), 41 gcadapter(adapter) {}
42 origin_value(static_cast<float>(adapter->GetOriginValue(port_, axis_))) {}
43 42
44 bool GetStatus() const override { 43 bool GetStatus() const override {
45 if (gcadapter->DeviceConnected(port)) { 44 if (gcadapter->DeviceConnected(port)) {
46 const float current_axis_value = gcadapter->GetPadState()[port].axes.at(axis); 45 const float current_axis_value = gcadapter->GetPadState(port).axis_values.at(axis);
47 const float axis_value = (current_axis_value - origin_value) / 128.0f; 46 const float axis_value = current_axis_value / 128.0f;
48 if (trigger_if_greater) { 47 if (trigger_if_greater) {
49 // TODO: Might be worthwile to set a slider for the trigger threshold. It is 48 // TODO: Might be worthwile to set a slider for the trigger threshold. It is
50 // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick 49 // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
@@ -56,12 +55,11 @@ public:
56 } 55 }
57 56
58private: 57private:
59 const int port; 58 const u32 port;
60 const int axis; 59 const u32 axis;
61 float threshold; 60 float threshold;
62 bool trigger_if_greater; 61 bool trigger_if_greater;
63 const GCAdapter::Adapter* gcadapter; 62 const GCAdapter::Adapter* gcadapter;
64 const float origin_value;
65}; 63};
66 64
67GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) 65GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
@@ -70,10 +68,10 @@ GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
70GCButton::~GCButton() = default; 68GCButton::~GCButton() = default;
71 69
72std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::ParamPackage& params) { 70std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::ParamPackage& params) {
73 const int button_id = params.Get("button", 0); 71 const auto button_id = params.Get("button", 0);
74 const int port = params.Get("port", 0); 72 const auto port = static_cast<u32>(params.Get("port", 0));
75 73
76 constexpr int PAD_STICK_ID = static_cast<u16>(GCAdapter::PadButton::PAD_STICK); 74 constexpr s32 PAD_STICK_ID = static_cast<s32>(GCAdapter::PadButton::Stick);
77 75
78 // button is not an axis/stick button 76 // button is not an axis/stick button
79 if (button_id != PAD_STICK_ID) { 77 if (button_id != PAD_STICK_ID) {
@@ -98,7 +96,6 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param
98 adapter.get()); 96 adapter.get());
99 } 97 }
100 98
101 UNREACHABLE();
102 return nullptr; 99 return nullptr;
103} 100}
104 101
@@ -106,32 +103,25 @@ Common::ParamPackage GCButtonFactory::GetNextInput() const {
106 Common::ParamPackage params; 103 Common::ParamPackage params;
107 GCAdapter::GCPadStatus pad; 104 GCAdapter::GCPadStatus pad;
108 auto& queue = adapter->GetPadQueue(); 105 auto& queue = adapter->GetPadQueue();
109 for (std::size_t port = 0; port < queue.size(); ++port) { 106 while (queue.Pop(pad)) {
110 while (queue[port].Pop(pad)) { 107 // This while loop will break on the earliest detected button
111 // This while loop will break on the earliest detected button 108 params.Set("engine", "gcpad");
112 params.Set("engine", "gcpad"); 109 params.Set("port", static_cast<s32>(pad.port));
113 params.Set("port", static_cast<int>(port)); 110 if (pad.button != GCAdapter::PadButton::Undefined) {
114 for (const auto& button : GCAdapter::PadButtonArray) { 111 params.Set("button", static_cast<u16>(pad.button));
115 const u16 button_value = static_cast<u16>(button); 112 }
116 if (pad.button & button_value) {
117 params.Set("button", button_value);
118 break;
119 }
120 }
121 113
122 // For Axis button implementation 114 // For Axis button implementation
123 if (pad.axis != GCAdapter::PadAxes::Undefined) { 115 if (pad.axis != GCAdapter::PadAxes::Undefined) {
124 params.Set("axis", static_cast<u8>(pad.axis)); 116 params.Set("axis", static_cast<u8>(pad.axis));
125 params.Set("button", static_cast<u16>(GCAdapter::PadButton::PAD_STICK)); 117 params.Set("button", static_cast<u16>(GCAdapter::PadButton::Stick));
126 if (pad.axis_value > 128) { 118 params.Set("threshold", "0.25");
127 params.Set("direction", "+"); 119 if (pad.axis_value > 0) {
128 params.Set("threshold", "0.25"); 120 params.Set("direction", "+");
129 } else { 121 } else {
130 params.Set("direction", "-"); 122 params.Set("direction", "-");
131 params.Set("threshold", "-0.25");
132 }
133 break;
134 } 123 }
124 break;
135 } 125 }
136 } 126 }
137 return params; 127 return params;
@@ -149,26 +139,30 @@ void GCButtonFactory::EndConfiguration() {
149 139
150class GCAnalog final : public Input::AnalogDevice { 140class GCAnalog final : public Input::AnalogDevice {
151public: 141public:
152 GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, 142 explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, bool invert_x_, bool invert_y_,
153 const GCAdapter::Adapter* adapter, float range_) 143 float deadzone_, float range_, const GCAdapter::Adapter* adapter)
154 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter), 144 : port(port_), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), invert_y(invert_y_),
155 origin_value_x(static_cast<float>(adapter->GetOriginValue(port_, axis_x_))), 145 deadzone(deadzone_), range(range_), gcadapter(adapter) {}
156 origin_value_y(static_cast<float>(adapter->GetOriginValue(port_, axis_y_))), 146
157 range(range_) {} 147 float GetAxis(u32 axis) const {
158
159 float GetAxis(int axis) const {
160 if (gcadapter->DeviceConnected(port)) { 148 if (gcadapter->DeviceConnected(port)) {
161 std::lock_guard lock{mutex}; 149 std::lock_guard lock{mutex};
162 const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y; 150 const auto axis_value =
163 return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / (100.0f * range); 151 static_cast<float>(gcadapter->GetPadState(port).axis_values.at(axis));
152 return (axis_value) / (100.0f * range);
164 } 153 }
165 return 0.0f; 154 return 0.0f;
166 } 155 }
167 156
168 std::pair<float, float> GetAnalog(int axis_x, int axis_y) const { 157 std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
169 float x = GetAxis(axis_x); 158 float x = GetAxis(analog_axis_x);
170 float y = GetAxis(axis_y); 159 float y = GetAxis(analog_axis_y);
171 160 if (invert_x) {
161 x = -x;
162 }
163 if (invert_y) {
164 y = -y;
165 }
172 // Make sure the coordinates are in the unit circle, 166 // Make sure the coordinates are in the unit circle,
173 // otherwise normalize it. 167 // otherwise normalize it.
174 float r = x * x + y * y; 168 float r = x * x + y * y;
@@ -208,14 +202,14 @@ public:
208 } 202 }
209 203
210private: 204private:
211 const int port; 205 const u32 port;
212 const int axis_x; 206 const u32 axis_x;
213 const int axis_y; 207 const u32 axis_y;
208 const bool invert_x;
209 const bool invert_y;
214 const float deadzone; 210 const float deadzone;
215 const GCAdapter::Adapter* gcadapter;
216 const float origin_value_x;
217 const float origin_value_y;
218 const float range; 211 const float range;
212 const GCAdapter::Adapter* gcadapter;
219 mutable std::mutex mutex; 213 mutable std::mutex mutex;
220}; 214};
221 215
@@ -231,13 +225,18 @@ GCAnalogFactory::GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
231 * - "axis_y": the index of the axis to be bind as y-axis 225 * - "axis_y": the index of the axis to be bind as y-axis
232 */ 226 */
233std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::ParamPackage& params) { 227std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::ParamPackage& params) {
234 const int port = params.Get("port", 0); 228 const auto port = static_cast<u32>(params.Get("port", 0));
235 const int axis_x = params.Get("axis_x", 0); 229 const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
236 const int axis_y = params.Get("axis_y", 1); 230 const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
237 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); 231 const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
238 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); 232 const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
239 233 const std::string invert_x_value = params.Get("invert_x", "+");
240 return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range); 234 const std::string invert_y_value = params.Get("invert_y", "+");
235 const bool invert_x = invert_x_value == "-";
236 const bool invert_y = invert_y_value == "-";
237
238 return std::make_unique<GCAnalog>(port, axis_x, axis_y, invert_x, invert_y, deadzone, range,
239 adapter.get());
241} 240}
242 241
243void GCAnalogFactory::BeginConfiguration() { 242void GCAnalogFactory::BeginConfiguration() {
@@ -252,31 +251,51 @@ void GCAnalogFactory::EndConfiguration() {
252 251
253Common::ParamPackage GCAnalogFactory::GetNextInput() { 252Common::ParamPackage GCAnalogFactory::GetNextInput() {
254 GCAdapter::GCPadStatus pad; 253 GCAdapter::GCPadStatus pad;
254 Common::ParamPackage params;
255 auto& queue = adapter->GetPadQueue(); 255 auto& queue = adapter->GetPadQueue();
256 for (std::size_t port = 0; port < queue.size(); ++port) { 256 while (queue.Pop(pad)) {
257 while (queue[port].Pop(pad)) { 257 if (pad.button != GCAdapter::PadButton::Undefined) {
258 if (pad.axis == GCAdapter::PadAxes::Undefined || 258 params.Set("engine", "gcpad");
259 std::abs((pad.axis_value - 128.0f) / 128.0f) < 0.1) { 259 params.Set("port", static_cast<s32>(pad.port));
260 continue; 260 params.Set("button", static_cast<u16>(pad.button));
261 } 261 return params;
262 // An analog device needs two axes, so we need to store the axis for later and wait for 262 }
263 // a second input event. The axes also must be from the same joystick. 263 if (pad.axis == GCAdapter::PadAxes::Undefined ||
264 const u8 axis = static_cast<u8>(pad.axis); 264 std::abs(static_cast<float>(pad.axis_value) / 128.0f) < 0.1f) {
265 if (analog_x_axis == -1) { 265 continue;
266 analog_x_axis = axis; 266 }
267 controller_number = static_cast<int>(port); 267 // An analog device needs two axes, so we need to store the axis for later and wait for
268 } else if (analog_y_axis == -1 && analog_x_axis != axis && 268 // a second input event. The axes also must be from the same joystick.
269 controller_number == static_cast<int>(port)) { 269 const u8 axis = static_cast<u8>(pad.axis);
270 analog_y_axis = axis; 270 if (axis == 0 || axis == 1) {
271 } 271 analog_x_axis = 0;
272 analog_y_axis = 1;
273 controller_number = static_cast<s32>(pad.port);
274 break;
275 }
276 if (axis == 2 || axis == 3) {
277 analog_x_axis = 2;
278 analog_y_axis = 3;
279 controller_number = static_cast<s32>(pad.port);
280 break;
281 }
282
283 if (analog_x_axis == -1) {
284 analog_x_axis = axis;
285 controller_number = static_cast<s32>(pad.port);
286 } else if (analog_y_axis == -1 && analog_x_axis != axis &&
287 controller_number == static_cast<s32>(pad.port)) {
288 analog_y_axis = axis;
289 break;
272 } 290 }
273 } 291 }
274 Common::ParamPackage params;
275 if (analog_x_axis != -1 && analog_y_axis != -1) { 292 if (analog_x_axis != -1 && analog_y_axis != -1) {
276 params.Set("engine", "gcpad"); 293 params.Set("engine", "gcpad");
277 params.Set("port", controller_number); 294 params.Set("port", controller_number);
278 params.Set("axis_x", analog_x_axis); 295 params.Set("axis_x", analog_x_axis);
279 params.Set("axis_y", analog_y_axis); 296 params.Set("axis_y", analog_y_axis);
297 params.Set("invert_x", "+");
298 params.Set("invert_y", "+");
280 analog_x_axis = -1; 299 analog_x_axis = -1;
281 analog_y_axis = -1; 300 analog_y_axis = -1;
282 controller_number = -1; 301 controller_number = -1;
@@ -285,4 +304,43 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
285 return params; 304 return params;
286} 305}
287 306
307class GCVibration final : public Input::VibrationDevice {
308public:
309 explicit GCVibration(u32 port_, GCAdapter::Adapter* adapter)
310 : port(port_), gcadapter(adapter) {}
311
312 u8 GetStatus() const override {
313 return gcadapter->RumblePlay(port, 0);
314 }
315
316 bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
317 [[maybe_unused]] f32 freq_high) const override {
318 const auto mean_amplitude = (amp_low + amp_high) * 0.5f;
319 const auto processed_amplitude =
320 static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8);
321
322 return gcadapter->RumblePlay(port, processed_amplitude);
323 }
324
325private:
326 const u32 port;
327 GCAdapter::Adapter* gcadapter;
328};
329
330/// An vibration device factory that creates vibration devices from GC Adapter
331GCVibrationFactory::GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
332 : adapter(std::move(adapter_)) {}
333
334/**
335 * Creates a vibration device from a joystick
336 * @param params contains parameters for creating the device:
337 * - "port": the nth gcpad on the adapter
338 */
339std::unique_ptr<Input::VibrationDevice> GCVibrationFactory::Create(
340 const Common::ParamPackage& params) {
341 const auto port = static_cast<u32>(params.Get("port", 0));
342
343 return std::make_unique<GCVibration>(port, adapter.get());
344}
345
288} // namespace InputCommon 346} // namespace InputCommon
diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h
index 0527f328f..d1271e3ea 100644
--- a/src/input_common/gcadapter/gc_poller.h
+++ b/src/input_common/gcadapter/gc_poller.h
@@ -64,4 +64,15 @@ private:
64 bool polling = false; 64 bool polling = false;
65}; 65};
66 66
67/// A vibration device factory creates vibration devices from GC Adapter
68class GCVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
69public:
70 explicit GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
71
72 std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override;
73
74private:
75 std::shared_ptr<GCAdapter::Adapter> adapter;
76};
77
67} // namespace InputCommon 78} // namespace InputCommon
diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp
index afb8e6612..24a6f7a33 100644
--- a/src/input_common/keyboard.cpp
+++ b/src/input_common/keyboard.cpp
@@ -49,8 +49,9 @@ public:
49 void ChangeKeyStatus(int key_code, bool pressed) { 49 void ChangeKeyStatus(int key_code, bool pressed) {
50 std::lock_guard guard{mutex}; 50 std::lock_guard guard{mutex};
51 for (const KeyButtonPair& pair : list) { 51 for (const KeyButtonPair& pair : list) {
52 if (pair.key_code == key_code) 52 if (pair.key_code == key_code) {
53 pair.key_button->status.store(pressed); 53 pair.key_button->status.store(pressed);
54 }
54 } 55 }
55 } 56 }
56 57
@@ -73,7 +74,7 @@ KeyButton::~KeyButton() {
73} 74}
74 75
75std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) { 76std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) {
76 int key_code = params.Get("code", 0); 77 const int key_code = params.Get("code", 0);
77 std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list); 78 std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list);
78 key_button_list->AddKeyButton(key_code, button.get()); 79 key_button_list->AddKeyButton(key_code, button.get());
79 return button; 80 return button;
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 8da829132..7c4e7dd3b 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -10,7 +10,9 @@
10#include "input_common/gcadapter/gc_poller.h" 10#include "input_common/gcadapter/gc_poller.h"
11#include "input_common/keyboard.h" 11#include "input_common/keyboard.h"
12#include "input_common/main.h" 12#include "input_common/main.h"
13#include "input_common/motion_emu.h" 13#include "input_common/motion_from_button.h"
14#include "input_common/mouse/mouse_input.h"
15#include "input_common/mouse/mouse_poller.h"
14#include "input_common/touch_from_button.h" 16#include "input_common/touch_from_button.h"
15#include "input_common/udp/client.h" 17#include "input_common/udp/client.h"
16#include "input_common/udp/udp.h" 18#include "input_common/udp/udp.h"
@@ -27,13 +29,15 @@ struct InputSubsystem::Impl {
27 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons); 29 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
28 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter); 30 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
29 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog); 31 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
32 gcvibration = std::make_shared<GCVibrationFactory>(gcadapter);
33 Input::RegisterFactory<Input::VibrationDevice>("gcpad", gcvibration);
30 34
31 keyboard = std::make_shared<Keyboard>(); 35 keyboard = std::make_shared<Keyboard>();
32 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); 36 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
33 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button", 37 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
34 std::make_shared<AnalogFromButton>()); 38 std::make_shared<AnalogFromButton>());
35 motion_emu = std::make_shared<MotionEmu>(); 39 Input::RegisterFactory<Input::MotionDevice>("keyboard",
36 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu); 40 std::make_shared<MotionFromButton>());
37 Input::RegisterFactory<Input::TouchDevice>("touch_from_button", 41 Input::RegisterFactory<Input::TouchDevice>("touch_from_button",
38 std::make_shared<TouchFromButtonFactory>()); 42 std::make_shared<TouchFromButtonFactory>());
39 43
@@ -46,35 +50,56 @@ struct InputSubsystem::Impl {
46 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion); 50 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion);
47 udptouch = std::make_shared<UDPTouchFactory>(udp); 51 udptouch = std::make_shared<UDPTouchFactory>(udp);
48 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch); 52 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch);
53
54 mouse = std::make_shared<MouseInput::Mouse>();
55 mousebuttons = std::make_shared<MouseButtonFactory>(mouse);
56 Input::RegisterFactory<Input::ButtonDevice>("mouse", mousebuttons);
57 mouseanalog = std::make_shared<MouseAnalogFactory>(mouse);
58 Input::RegisterFactory<Input::AnalogDevice>("mouse", mouseanalog);
59 mousemotion = std::make_shared<MouseMotionFactory>(mouse);
60 Input::RegisterFactory<Input::MotionDevice>("mouse", mousemotion);
61 mousetouch = std::make_shared<MouseTouchFactory>(mouse);
62 Input::RegisterFactory<Input::TouchDevice>("mouse", mousetouch);
49 } 63 }
50 64
51 void Shutdown() { 65 void Shutdown() {
52 Input::UnregisterFactory<Input::ButtonDevice>("keyboard"); 66 Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
67 Input::UnregisterFactory<Input::MotionDevice>("keyboard");
53 keyboard.reset(); 68 keyboard.reset();
54 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button"); 69 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
55 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
56 motion_emu.reset();
57 Input::UnregisterFactory<Input::TouchDevice>("touch_from_button"); 70 Input::UnregisterFactory<Input::TouchDevice>("touch_from_button");
58#ifdef HAVE_SDL2 71#ifdef HAVE_SDL2
59 sdl.reset(); 72 sdl.reset();
60#endif 73#endif
61 Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); 74 Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
62 Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); 75 Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
76 Input::UnregisterFactory<Input::VibrationDevice>("gcpad");
63 77
64 gcbuttons.reset(); 78 gcbuttons.reset();
65 gcanalog.reset(); 79 gcanalog.reset();
80 gcvibration.reset();
66 81
67 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); 82 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
68 Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); 83 Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
69 84
70 udpmotion.reset(); 85 udpmotion.reset();
71 udptouch.reset(); 86 udptouch.reset();
87
88 Input::UnregisterFactory<Input::ButtonDevice>("mouse");
89 Input::UnregisterFactory<Input::AnalogDevice>("mouse");
90 Input::UnregisterFactory<Input::MotionDevice>("mouse");
91 Input::UnregisterFactory<Input::TouchDevice>("mouse");
92
93 mousebuttons.reset();
94 mouseanalog.reset();
95 mousemotion.reset();
96 mousetouch.reset();
72 } 97 }
73 98
74 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const { 99 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
75 std::vector<Common::ParamPackage> devices = { 100 std::vector<Common::ParamPackage> devices = {
76 Common::ParamPackage{{"display", "Any"}, {"class", "any"}}, 101 Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
77 Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "key"}}, 102 Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}},
78 }; 103 };
79#ifdef HAVE_SDL2 104#ifdef HAVE_SDL2
80 auto sdl_devices = sdl->GetInputDevices(); 105 auto sdl_devices = sdl->GetInputDevices();
@@ -92,10 +117,6 @@ struct InputSubsystem::Impl {
92 if (!params.Has("class") || params.Get("class", "") == "any") { 117 if (!params.Has("class") || params.Get("class", "") == "any") {
93 return {}; 118 return {};
94 } 119 }
95 if (params.Get("class", "") == "key") {
96 // TODO consider returning the SDL key codes for the default keybindings
97 return {};
98 }
99 if (params.Get("class", "") == "gcpad") { 120 if (params.Get("class", "") == "gcpad") {
100 return gcadapter->GetAnalogMappingForDevice(params); 121 return gcadapter->GetAnalogMappingForDevice(params);
101 } 122 }
@@ -112,10 +133,6 @@ struct InputSubsystem::Impl {
112 if (!params.Has("class") || params.Get("class", "") == "any") { 133 if (!params.Has("class") || params.Get("class", "") == "any") {
113 return {}; 134 return {};
114 } 135 }
115 if (params.Get("class", "") == "key") {
116 // TODO consider returning the SDL key codes for the default keybindings
117 return {};
118 }
119 if (params.Get("class", "") == "gcpad") { 136 if (params.Get("class", "") == "gcpad") {
120 return gcadapter->GetButtonMappingForDevice(params); 137 return gcadapter->GetButtonMappingForDevice(params);
121 } 138 }
@@ -140,16 +157,21 @@ struct InputSubsystem::Impl {
140 } 157 }
141 158
142 std::shared_ptr<Keyboard> keyboard; 159 std::shared_ptr<Keyboard> keyboard;
143 std::shared_ptr<MotionEmu> motion_emu;
144#ifdef HAVE_SDL2 160#ifdef HAVE_SDL2
145 std::unique_ptr<SDL::State> sdl; 161 std::unique_ptr<SDL::State> sdl;
146#endif 162#endif
147 std::shared_ptr<GCButtonFactory> gcbuttons; 163 std::shared_ptr<GCButtonFactory> gcbuttons;
148 std::shared_ptr<GCAnalogFactory> gcanalog; 164 std::shared_ptr<GCAnalogFactory> gcanalog;
165 std::shared_ptr<GCVibrationFactory> gcvibration;
149 std::shared_ptr<UDPMotionFactory> udpmotion; 166 std::shared_ptr<UDPMotionFactory> udpmotion;
150 std::shared_ptr<UDPTouchFactory> udptouch; 167 std::shared_ptr<UDPTouchFactory> udptouch;
168 std::shared_ptr<MouseButtonFactory> mousebuttons;
169 std::shared_ptr<MouseAnalogFactory> mouseanalog;
170 std::shared_ptr<MouseMotionFactory> mousemotion;
171 std::shared_ptr<MouseTouchFactory> mousetouch;
151 std::shared_ptr<CemuhookUDP::Client> udp; 172 std::shared_ptr<CemuhookUDP::Client> udp;
152 std::shared_ptr<GCAdapter::Adapter> gcadapter; 173 std::shared_ptr<GCAdapter::Adapter> gcadapter;
174 std::shared_ptr<MouseInput::Mouse> mouse;
153}; 175};
154 176
155InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {} 177InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
@@ -172,12 +194,12 @@ const Keyboard* InputSubsystem::GetKeyboard() const {
172 return impl->keyboard.get(); 194 return impl->keyboard.get();
173} 195}
174 196
175MotionEmu* InputSubsystem::GetMotionEmu() { 197MouseInput::Mouse* InputSubsystem::GetMouse() {
176 return impl->motion_emu.get(); 198 return impl->mouse.get();
177} 199}
178 200
179const MotionEmu* InputSubsystem::GetMotionEmu() const { 201const MouseInput::Mouse* InputSubsystem::GetMouse() const {
180 return impl->motion_emu.get(); 202 return impl->mouse.get();
181} 203}
182 204
183std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const { 205std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
@@ -192,6 +214,10 @@ ButtonMapping InputSubsystem::GetButtonMappingForDevice(const Common::ParamPacka
192 return impl->GetButtonMappingForDevice(device); 214 return impl->GetButtonMappingForDevice(device);
193} 215}
194 216
217MotionMapping InputSubsystem::GetMotionMappingForDevice(const Common::ParamPackage& device) const {
218 return impl->GetMotionMappingForDevice(device);
219}
220
195GCAnalogFactory* InputSubsystem::GetGCAnalogs() { 221GCAnalogFactory* InputSubsystem::GetGCAnalogs() {
196 return impl->gcanalog.get(); 222 return impl->gcanalog.get();
197} 223}
@@ -224,11 +250,43 @@ const UDPTouchFactory* InputSubsystem::GetUDPTouch() const {
224 return impl->udptouch.get(); 250 return impl->udptouch.get();
225} 251}
226 252
253MouseButtonFactory* InputSubsystem::GetMouseButtons() {
254 return impl->mousebuttons.get();
255}
256
257const MouseButtonFactory* InputSubsystem::GetMouseButtons() const {
258 return impl->mousebuttons.get();
259}
260
261MouseAnalogFactory* InputSubsystem::GetMouseAnalogs() {
262 return impl->mouseanalog.get();
263}
264
265const MouseAnalogFactory* InputSubsystem::GetMouseAnalogs() const {
266 return impl->mouseanalog.get();
267}
268
269MouseMotionFactory* InputSubsystem::GetMouseMotions() {
270 return impl->mousemotion.get();
271}
272
273const MouseMotionFactory* InputSubsystem::GetMouseMotions() const {
274 return impl->mousemotion.get();
275}
276
277MouseTouchFactory* InputSubsystem::GetMouseTouch() {
278 return impl->mousetouch.get();
279}
280
281const MouseTouchFactory* InputSubsystem::GetMouseTouch() const {
282 return impl->mousetouch.get();
283}
284
227void InputSubsystem::ReloadInputDevices() { 285void InputSubsystem::ReloadInputDevices() {
228 if (!impl->udp) { 286 if (!impl->udp) {
229 return; 287 return;
230 } 288 }
231 impl->udp->ReloadUDPClient(); 289 impl->udp->ReloadSockets();
232} 290}
233 291
234std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers( 292std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers(
diff --git a/src/input_common/main.h b/src/input_common/main.h
index dded3f1ef..5d6f26385 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -25,6 +25,10 @@ namespace Settings::NativeMotion {
25enum Values : int; 25enum Values : int;
26} 26}
27 27
28namespace MouseInput {
29class Mouse;
30}
31
28namespace InputCommon { 32namespace InputCommon {
29namespace Polling { 33namespace Polling {
30 34
@@ -56,8 +60,11 @@ class GCAnalogFactory;
56class GCButtonFactory; 60class GCButtonFactory;
57class UDPMotionFactory; 61class UDPMotionFactory;
58class UDPTouchFactory; 62class UDPTouchFactory;
63class MouseButtonFactory;
64class MouseAnalogFactory;
65class MouseMotionFactory;
66class MouseTouchFactory;
59class Keyboard; 67class Keyboard;
60class MotionEmu;
61 68
62/** 69/**
63 * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default 70 * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
@@ -90,11 +97,11 @@ public:
90 /// Retrieves the underlying keyboard device. 97 /// Retrieves the underlying keyboard device.
91 [[nodiscard]] const Keyboard* GetKeyboard() const; 98 [[nodiscard]] const Keyboard* GetKeyboard() const;
92 99
93 /// Retrieves the underlying motion emulation factory. 100 /// Retrieves the underlying mouse device.
94 [[nodiscard]] MotionEmu* GetMotionEmu(); 101 [[nodiscard]] MouseInput::Mouse* GetMouse();
95 102
96 /// Retrieves the underlying motion emulation factory. 103 /// Retrieves the underlying mouse device.
97 [[nodiscard]] const MotionEmu* GetMotionEmu() const; 104 [[nodiscard]] const MouseInput::Mouse* GetMouse() const;
98 105
99 /** 106 /**
100 * Returns all available input devices that this Factory can create a new device with. 107 * Returns all available input devices that this Factory can create a new device with.
@@ -137,6 +144,30 @@ public:
137 /// Retrieves the underlying udp touch handler. 144 /// Retrieves the underlying udp touch handler.
138 [[nodiscard]] const UDPTouchFactory* GetUDPTouch() const; 145 [[nodiscard]] const UDPTouchFactory* GetUDPTouch() const;
139 146
147 /// Retrieves the underlying GameCube button handler.
148 [[nodiscard]] MouseButtonFactory* GetMouseButtons();
149
150 /// Retrieves the underlying GameCube button handler.
151 [[nodiscard]] const MouseButtonFactory* GetMouseButtons() const;
152
153 /// Retrieves the underlying udp touch handler.
154 [[nodiscard]] MouseAnalogFactory* GetMouseAnalogs();
155
156 /// Retrieves the underlying udp touch handler.
157 [[nodiscard]] const MouseAnalogFactory* GetMouseAnalogs() const;
158
159 /// Retrieves the underlying udp motion handler.
160 [[nodiscard]] MouseMotionFactory* GetMouseMotions();
161
162 /// Retrieves the underlying udp motion handler.
163 [[nodiscard]] const MouseMotionFactory* GetMouseMotions() const;
164
165 /// Retrieves the underlying udp touch handler.
166 [[nodiscard]] MouseTouchFactory* GetMouseTouch();
167
168 /// Retrieves the underlying udp touch handler.
169 [[nodiscard]] const MouseTouchFactory* GetMouseTouch() const;
170
140 /// Reloads the input devices 171 /// Reloads the input devices
141 void ReloadInputDevices(); 172 void ReloadInputDevices();
142 173
diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp
deleted file mode 100644
index 69fd3c1d2..000000000
--- a/src/input_common/motion_emu.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
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 <algorithm>
6#include <chrono>
7#include <mutex>
8#include <thread>
9#include <tuple>
10#include "common/math_util.h"
11#include "common/quaternion.h"
12#include "common/thread.h"
13#include "common/vector_math.h"
14#include "input_common/motion_emu.h"
15
16namespace InputCommon {
17
18// Implementation class of the motion emulation device
19class MotionEmuDevice {
20public:
21 MotionEmuDevice(int update_millisecond, float sensitivity)
22 : update_millisecond(update_millisecond),
23 update_duration(std::chrono::duration_cast<std::chrono::steady_clock::duration>(
24 std::chrono::milliseconds(update_millisecond))),
25 sensitivity(sensitivity), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {}
26
27 ~MotionEmuDevice() {
28 if (motion_emu_thread.joinable()) {
29 shutdown_event.Set();
30 motion_emu_thread.join();
31 }
32 }
33
34 void BeginTilt(int x, int y) {
35 mouse_origin = Common::MakeVec(x, y);
36 is_tilting = true;
37 }
38
39 void Tilt(int x, int y) {
40 auto mouse_move = Common::MakeVec(x, y) - mouse_origin;
41 if (is_tilting) {
42 std::lock_guard guard{tilt_mutex};
43 if (mouse_move.x == 0 && mouse_move.y == 0) {
44 tilt_angle = 0;
45 } else {
46 tilt_direction = mouse_move.Cast<float>();
47 tilt_angle =
48 std::clamp(tilt_direction.Normalize() * sensitivity, 0.0f, Common::PI * 0.5f);
49 }
50 }
51 }
52
53 void EndTilt() {
54 std::lock_guard guard{tilt_mutex};
55 tilt_angle = 0;
56 is_tilting = false;
57 }
58
59 Input::MotionStatus GetStatus() {
60 std::lock_guard guard{status_mutex};
61 return status;
62 }
63
64private:
65 const int update_millisecond;
66 const std::chrono::steady_clock::duration update_duration;
67 const float sensitivity;
68
69 Common::Vec2<int> mouse_origin;
70
71 std::mutex tilt_mutex;
72 Common::Vec2<float> tilt_direction;
73 float tilt_angle = 0;
74
75 bool is_tilting = false;
76
77 Common::Event shutdown_event;
78
79 Input::MotionStatus status;
80 std::mutex status_mutex;
81
82 // Note: always keep the thread declaration at the end so that other objects are initialized
83 // before this!
84 std::thread motion_emu_thread;
85
86 void MotionEmuThread() {
87 auto update_time = std::chrono::steady_clock::now();
88 Common::Quaternion<float> q = Common::MakeQuaternion(Common::Vec3<float>(), 0);
89 Common::Quaternion<float> old_q;
90
91 while (!shutdown_event.WaitUntil(update_time)) {
92 update_time += update_duration;
93 old_q = q;
94
95 {
96 std::lock_guard guard{tilt_mutex};
97
98 // Find the quaternion describing current 3DS tilting
99 q = Common::MakeQuaternion(
100 Common::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x), tilt_angle);
101 }
102
103 auto inv_q = q.Inverse();
104
105 // Set the gravity vector in world space
106 auto gravity = Common::MakeVec(0.0f, -1.0f, 0.0f);
107
108 // Find the angular rate vector in world space
109 auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
110 angular_rate *= 1000 / update_millisecond / Common::PI * 180;
111
112 // Transform the two vectors from world space to 3DS space
113 gravity = QuaternionRotate(inv_q, gravity);
114 angular_rate = QuaternionRotate(inv_q, angular_rate);
115
116 // TODO: Calculate the correct rotation vector and orientation matrix
117 const auto matrix4x4 = q.ToMatrix();
118 const auto rotation = Common::MakeVec(0.0f, 0.0f, 0.0f);
119 const std::array orientation{
120 Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
121 Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
122 Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10]),
123 };
124
125 // Update the sensor state
126 {
127 std::lock_guard guard{status_mutex};
128 status = std::make_tuple(gravity, angular_rate, rotation, orientation);
129 }
130 }
131 }
132};
133
134// Interface wrapper held by input receiver as a unique_ptr. It holds the implementation class as
135// a shared_ptr, which is also observed by the factory class as a weak_ptr. In this way the factory
136// can forward all the inputs to the implementation only when it is valid.
137class MotionEmuDeviceWrapper : public Input::MotionDevice {
138public:
139 MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) {
140 device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity);
141 }
142
143 Input::MotionStatus GetStatus() const override {
144 return device->GetStatus();
145 }
146
147 std::shared_ptr<MotionEmuDevice> device;
148};
149
150std::unique_ptr<Input::MotionDevice> MotionEmu::Create(const Common::ParamPackage& params) {
151 int update_period = params.Get("update_period", 100);
152 float sensitivity = params.Get("sensitivity", 0.01f);
153 auto device_wrapper = std::make_unique<MotionEmuDeviceWrapper>(update_period, sensitivity);
154 // Previously created device is disconnected here. Having two motion devices for 3DS is not
155 // expected.
156 current_device = device_wrapper->device;
157 return device_wrapper;
158}
159
160void MotionEmu::BeginTilt(int x, int y) {
161 if (auto ptr = current_device.lock()) {
162 ptr->BeginTilt(x, y);
163 }
164}
165
166void MotionEmu::Tilt(int x, int y) {
167 if (auto ptr = current_device.lock()) {
168 ptr->Tilt(x, y);
169 }
170}
171
172void MotionEmu::EndTilt() {
173 if (auto ptr = current_device.lock()) {
174 ptr->EndTilt();
175 }
176}
177
178} // namespace InputCommon
diff --git a/src/input_common/motion_emu.h b/src/input_common/motion_emu.h
deleted file mode 100644
index 7a7e22467..000000000
--- a/src/input_common/motion_emu.h
+++ /dev/null
@@ -1,46 +0,0 @@
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 "core/frontend/input.h"
8
9namespace InputCommon {
10
11class MotionEmuDevice;
12
13class MotionEmu : public Input::Factory<Input::MotionDevice> {
14public:
15 /**
16 * Creates a motion device emulated from mouse input
17 * @param params contains parameters for creating the device:
18 * - "update_period": update period in milliseconds
19 * - "sensitivity": the coefficient converting mouse movement to tilting angle
20 */
21 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
22
23 /**
24 * Signals that a motion sensor tilt has begun.
25 * @param x the x-coordinate of the cursor
26 * @param y the y-coordinate of the cursor
27 */
28 void BeginTilt(int x, int y);
29
30 /**
31 * Signals that a motion sensor tilt is occurring.
32 * @param x the x-coordinate of the cursor
33 * @param y the y-coordinate of the cursor
34 */
35 void Tilt(int x, int y);
36
37 /**
38 * Signals that a motion sensor tilt has ended.
39 */
40 void EndTilt();
41
42private:
43 std::weak_ptr<MotionEmuDevice> current_device;
44};
45
46} // namespace InputCommon
diff --git a/src/input_common/motion_from_button.cpp b/src/input_common/motion_from_button.cpp
new file mode 100644
index 000000000..29045a673
--- /dev/null
+++ b/src/input_common/motion_from_button.cpp
@@ -0,0 +1,34 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "input_common/motion_from_button.h"
6#include "input_common/motion_input.h"
7
8namespace InputCommon {
9
10class MotionKey final : public Input::MotionDevice {
11public:
12 using Button = std::unique_ptr<Input::ButtonDevice>;
13
14 explicit MotionKey(Button key_) : key(std::move(key_)) {}
15
16 Input::MotionStatus GetStatus() const override {
17
18 if (key->GetStatus()) {
19 return motion.GetRandomMotion(2, 6);
20 }
21 return motion.GetRandomMotion(0, 0);
22 }
23
24private:
25 Button key;
26 InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f};
27};
28
29std::unique_ptr<Input::MotionDevice> MotionFromButton::Create(const Common::ParamPackage& params) {
30 auto key = Input::CreateDevice<Input::ButtonDevice>(params.Serialize());
31 return std::make_unique<MotionKey>(std::move(key));
32}
33
34} // namespace InputCommon
diff --git a/src/input_common/motion_from_button.h b/src/input_common/motion_from_button.h
new file mode 100644
index 000000000..a959046fb
--- /dev/null
+++ b/src/input_common/motion_from_button.h
@@ -0,0 +1,25 @@
1// Copyright 2020 yuzu 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 "core/frontend/input.h"
8
9namespace InputCommon {
10
11/**
12 * An motion device factory that takes a keyboard button and uses it as a random
13 * motion device.
14 */
15class MotionFromButton final : public Input::Factory<Input::MotionDevice> {
16public:
17 /**
18 * Creates an motion device from button devices
19 * @param params contains parameters for creating the device:
20 * - "key": a serialized ParamPackage for creating a button device
21 */
22 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
23};
24
25} // namespace InputCommon
diff --git a/src/input_common/motion_input.cpp b/src/input_common/motion_input.cpp
index 22a849866..6a65f175e 100644
--- a/src/input_common/motion_input.cpp
+++ b/src/input_common/motion_input.cpp
@@ -2,13 +2,13 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included 3// Refer to the license.txt file included
4 4
5#include <random>
5#include "common/math_util.h" 6#include "common/math_util.h"
6#include "input_common/motion_input.h" 7#include "input_common/motion_input.h"
7 8
8namespace InputCommon { 9namespace InputCommon {
9 10
10MotionInput::MotionInput(f32 new_kp, f32 new_ki, f32 new_kd) 11MotionInput::MotionInput(f32 new_kp, f32 new_ki, f32 new_kd) : kp(new_kp), ki(new_ki), kd(new_kd) {}
11 : kp(new_kp), ki(new_ki), kd(new_kd), quat{{0, 0, -1}, 0} {}
12 12
13void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) { 13void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
14 accel = acceleration; 14 accel = acceleration;
@@ -16,8 +16,16 @@ void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
16 16
17void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) { 17void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
18 gyro = gyroscope - gyro_drift; 18 gyro = gyroscope - gyro_drift;
19
20 // Auto adjust drift to minimize drift
21 if (!IsMoving(0.1f)) {
22 gyro_drift = (gyro_drift * 0.9999f) + (gyroscope * 0.0001f);
23 }
24
19 if (gyro.Length2() < gyro_threshold) { 25 if (gyro.Length2() < gyro_threshold) {
20 gyro = {}; 26 gyro = {};
27 } else {
28 only_accelerometer = false;
21 } 29 }
22} 30}
23 31
@@ -50,7 +58,7 @@ bool MotionInput::IsCalibrated(f32 sensitivity) const {
50} 58}
51 59
52void MotionInput::UpdateRotation(u64 elapsed_time) { 60void MotionInput::UpdateRotation(u64 elapsed_time) {
53 const f32 sample_period = elapsed_time / 1000000.0f; 61 const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
54 if (sample_period > 0.1f) { 62 if (sample_period > 0.1f) {
55 return; 63 return;
56 } 64 }
@@ -66,9 +74,9 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
66 f32 q2 = quat.xyz[0]; 74 f32 q2 = quat.xyz[0];
67 f32 q3 = quat.xyz[1]; 75 f32 q3 = quat.xyz[1];
68 f32 q4 = quat.xyz[2]; 76 f32 q4 = quat.xyz[2];
69 const f32 sample_period = elapsed_time / 1000000.0f; 77 const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
70 78
71 // ignore invalid elapsed time 79 // Ignore invalid elapsed time
72 if (sample_period > 0.1f) { 80 if (sample_period > 0.1f) {
73 return; 81 return;
74 } 82 }
@@ -80,6 +88,13 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
80 rad_gyro.y = -swap; 88 rad_gyro.y = -swap;
81 rad_gyro.z = -rad_gyro.z; 89 rad_gyro.z = -rad_gyro.z;
82 90
91 // Clear gyro values if there is no gyro present
92 if (only_accelerometer) {
93 rad_gyro.x = 0;
94 rad_gyro.y = 0;
95 rad_gyro.z = 0;
96 }
97
83 // Ignore drift correction if acceleration is not reliable 98 // Ignore drift correction if acceleration is not reliable
84 if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) { 99 if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) {
85 const f32 ax = -normal_accel.x; 100 const f32 ax = -normal_accel.x;
@@ -92,8 +107,11 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
92 const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; 107 const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
93 108
94 // Error is cross product between estimated direction and measured direction of gravity 109 // Error is cross product between estimated direction and measured direction of gravity
95 const Common::Vec3f new_real_error = {az * vx - ax * vz, ay * vz - az * vy, 110 const Common::Vec3f new_real_error = {
96 ax * vy - ay * vx}; 111 az * vx - ax * vz,
112 ay * vz - az * vy,
113 ax * vy - ay * vx,
114 };
97 115
98 derivative_error = new_real_error - real_error; 116 derivative_error = new_real_error - real_error;
99 real_error = new_real_error; 117 real_error = new_real_error;
@@ -106,9 +124,22 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
106 } 124 }
107 125
108 // Apply feedback terms 126 // Apply feedback terms
109 rad_gyro += kp * real_error; 127 if (!only_accelerometer) {
110 rad_gyro += ki * integral_error; 128 rad_gyro += kp * real_error;
111 rad_gyro += kd * derivative_error; 129 rad_gyro += ki * integral_error;
130 rad_gyro += kd * derivative_error;
131 } else {
132 // Give more weight to accelerometer values to compensate for the lack of gyro
133 rad_gyro += 35.0f * kp * real_error;
134 rad_gyro += 10.0f * ki * integral_error;
135 rad_gyro += 10.0f * kd * derivative_error;
136
137 // Emulate gyro values for games that need them
138 gyro.x = -rad_gyro.y;
139 gyro.y = rad_gyro.x;
140 gyro.z = -rad_gyro.z;
141 UpdateRotation(elapsed_time);
142 }
112 } 143 }
113 144
114 const f32 gx = rad_gyro.y; 145 const f32 gx = rad_gyro.y;
@@ -159,18 +190,49 @@ Common::Vec3f MotionInput::GetRotations() const {
159 return rotations; 190 return rotations;
160} 191}
161 192
193Input::MotionStatus MotionInput::GetMotion() const {
194 const Common::Vec3f gyroscope = GetGyroscope();
195 const Common::Vec3f accelerometer = GetAcceleration();
196 const Common::Vec3f rotation = GetRotations();
197 const std::array<Common::Vec3f, 3> orientation = GetOrientation();
198 return {accelerometer, gyroscope, rotation, orientation};
199}
200
201Input::MotionStatus MotionInput::GetRandomMotion(int accel_magnitude, int gyro_magnitude) const {
202 std::random_device device;
203 std::mt19937 gen(device());
204 std::uniform_int_distribution<s16> distribution(-1000, 1000);
205 const Common::Vec3f gyroscope{
206 static_cast<f32>(distribution(gen)) * 0.001f,
207 static_cast<f32>(distribution(gen)) * 0.001f,
208 static_cast<f32>(distribution(gen)) * 0.001f,
209 };
210 const Common::Vec3f accelerometer{
211 static_cast<f32>(distribution(gen)) * 0.001f,
212 static_cast<f32>(distribution(gen)) * 0.001f,
213 static_cast<f32>(distribution(gen)) * 0.001f,
214 };
215 constexpr Common::Vec3f rotation;
216 constexpr std::array orientation{
217 Common::Vec3f{1.0f, 0.0f, 0.0f},
218 Common::Vec3f{0.0f, 1.0f, 0.0f},
219 Common::Vec3f{0.0f, 0.0f, 1.0f},
220 };
221 return {accelerometer * accel_magnitude, gyroscope * gyro_magnitude, rotation, orientation};
222}
223
162void MotionInput::ResetOrientation() { 224void MotionInput::ResetOrientation() {
163 if (!reset_enabled) { 225 if (!reset_enabled || only_accelerometer) {
164 return; 226 return;
165 } 227 }
166 if (!IsMoving(0.5f) && accel.z <= -0.9f) { 228 if (!IsMoving(0.5f) && accel.z <= -0.9f) {
167 ++reset_counter; 229 ++reset_counter;
168 if (reset_counter > 900) { 230 if (reset_counter > 900) {
169 // TODO: calculate quaternion from gravity vector
170 quat.w = 0; 231 quat.w = 0;
171 quat.xyz[0] = 0; 232 quat.xyz[0] = 0;
172 quat.xyz[1] = 0; 233 quat.xyz[1] = 0;
173 quat.xyz[2] = -1; 234 quat.xyz[2] = -1;
235 SetOrientationFromAccelerometer();
174 integral_error = {}; 236 integral_error = {};
175 reset_counter = 0; 237 reset_counter = 0;
176 } 238 }
@@ -178,4 +240,62 @@ void MotionInput::ResetOrientation() {
178 reset_counter = 0; 240 reset_counter = 0;
179 } 241 }
180} 242}
243
244void MotionInput::SetOrientationFromAccelerometer() {
245 int iterations = 0;
246 const f32 sample_period = 0.015f;
247
248 const auto normal_accel = accel.Normalized();
249
250 while (!IsCalibrated(0.01f) && ++iterations < 100) {
251 // Short name local variable for readability
252 f32 q1 = quat.w;
253 f32 q2 = quat.xyz[0];
254 f32 q3 = quat.xyz[1];
255 f32 q4 = quat.xyz[2];
256
257 Common::Vec3f rad_gyro;
258 const f32 ax = -normal_accel.x;
259 const f32 ay = normal_accel.y;
260 const f32 az = -normal_accel.z;
261
262 // Estimated direction of gravity
263 const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
264 const f32 vy = 2.0f * (q1 * q2 + q3 * q4);
265 const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
266
267 // Error is cross product between estimated direction and measured direction of gravity
268 const Common::Vec3f new_real_error = {
269 az * vx - ax * vz,
270 ay * vz - az * vy,
271 ax * vy - ay * vx,
272 };
273
274 derivative_error = new_real_error - real_error;
275 real_error = new_real_error;
276
277 rad_gyro += 10.0f * kp * real_error;
278 rad_gyro += 5.0f * ki * integral_error;
279 rad_gyro += 10.0f * kd * derivative_error;
280
281 const f32 gx = rad_gyro.y;
282 const f32 gy = rad_gyro.x;
283 const f32 gz = rad_gyro.z;
284
285 // Integrate rate of change of quaternion
286 const f32 pa = q2;
287 const f32 pb = q3;
288 const f32 pc = q4;
289 q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period);
290 q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period);
291 q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
292 q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
293
294 quat.w = q1;
295 quat.xyz[0] = q2;
296 quat.xyz[1] = q3;
297 quat.xyz[2] = q4;
298 quat = quat.Normalized();
299 }
300}
181} // namespace InputCommon 301} // namespace InputCommon
diff --git a/src/input_common/motion_input.h b/src/input_common/motion_input.h
index 54b4439d9..efe74cf19 100644
--- a/src/input_common/motion_input.h
+++ b/src/input_common/motion_input.h
@@ -7,12 +7,13 @@
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/quaternion.h" 8#include "common/quaternion.h"
9#include "common/vector_math.h" 9#include "common/vector_math.h"
10#include "core/frontend/input.h"
10 11
11namespace InputCommon { 12namespace InputCommon {
12 13
13class MotionInput { 14class MotionInput {
14public: 15public:
15 MotionInput(f32 new_kp, f32 new_ki, f32 new_kd); 16 explicit MotionInput(f32 new_kp, f32 new_ki, f32 new_kd);
16 17
17 MotionInput(const MotionInput&) = default; 18 MotionInput(const MotionInput&) = default;
18 MotionInput& operator=(const MotionInput&) = default; 19 MotionInput& operator=(const MotionInput&) = default;
@@ -21,7 +22,7 @@ public:
21 MotionInput& operator=(MotionInput&&) = default; 22 MotionInput& operator=(MotionInput&&) = default;
22 23
23 void SetAcceleration(const Common::Vec3f& acceleration); 24 void SetAcceleration(const Common::Vec3f& acceleration);
24 void SetGyroscope(const Common::Vec3f& acceleration); 25 void SetGyroscope(const Common::Vec3f& gyroscope);
25 void SetQuaternion(const Common::Quaternion<f32>& quaternion); 26 void SetQuaternion(const Common::Quaternion<f32>& quaternion);
26 void SetGyroDrift(const Common::Vec3f& drift); 27 void SetGyroDrift(const Common::Vec3f& drift);
27 void SetGyroThreshold(f32 threshold); 28 void SetGyroThreshold(f32 threshold);
@@ -32,29 +33,33 @@ public:
32 void UpdateRotation(u64 elapsed_time); 33 void UpdateRotation(u64 elapsed_time);
33 void UpdateOrientation(u64 elapsed_time); 34 void UpdateOrientation(u64 elapsed_time);
34 35
35 std::array<Common::Vec3f, 3> GetOrientation() const; 36 [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
36 Common::Vec3f GetAcceleration() const; 37 [[nodiscard]] Common::Vec3f GetAcceleration() const;
37 Common::Vec3f GetGyroscope() const; 38 [[nodiscard]] Common::Vec3f GetGyroscope() const;
38 Common::Vec3f GetRotations() const; 39 [[nodiscard]] Common::Vec3f GetRotations() const;
39 Common::Quaternion<f32> GetQuaternion() const; 40 [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
41 [[nodiscard]] Input::MotionStatus GetMotion() const;
42 [[nodiscard]] Input::MotionStatus GetRandomMotion(int accel_magnitude,
43 int gyro_magnitude) const;
40 44
41 bool IsMoving(f32 sensitivity) const; 45 [[nodiscard]] bool IsMoving(f32 sensitivity) const;
42 bool IsCalibrated(f32 sensitivity) const; 46 [[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
43 47
44private: 48private:
45 void ResetOrientation(); 49 void ResetOrientation();
50 void SetOrientationFromAccelerometer();
46 51
47 // PID constants 52 // PID constants
48 const f32 kp; 53 f32 kp;
49 const f32 ki; 54 f32 ki;
50 const f32 kd; 55 f32 kd;
51 56
52 // PID errors 57 // PID errors
53 Common::Vec3f real_error; 58 Common::Vec3f real_error;
54 Common::Vec3f integral_error; 59 Common::Vec3f integral_error;
55 Common::Vec3f derivative_error; 60 Common::Vec3f derivative_error;
56 61
57 Common::Quaternion<f32> quat; 62 Common::Quaternion<f32> quat{{0.0f, 0.0f, -1.0f}, 0.0f};
58 Common::Vec3f rotations; 63 Common::Vec3f rotations;
59 Common::Vec3f accel; 64 Common::Vec3f accel;
60 Common::Vec3f gyro; 65 Common::Vec3f gyro;
@@ -63,6 +68,7 @@ private:
63 f32 gyro_threshold = 0.0f; 68 f32 gyro_threshold = 0.0f;
64 u32 reset_counter = 0; 69 u32 reset_counter = 0;
65 bool reset_enabled = true; 70 bool reset_enabled = true;
71 bool only_accelerometer = true;
66}; 72};
67 73
68} // namespace InputCommon 74} // namespace InputCommon
diff --git a/src/input_common/mouse/mouse_input.cpp b/src/input_common/mouse/mouse_input.cpp
new file mode 100644
index 000000000..10786a541
--- /dev/null
+++ b/src/input_common/mouse/mouse_input.cpp
@@ -0,0 +1,129 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include "input_common/mouse/mouse_input.h"
6
7namespace MouseInput {
8
9Mouse::Mouse() {
10 update_thread = std::thread(&Mouse::UpdateThread, this);
11}
12
13Mouse::~Mouse() {
14 update_thread_running = false;
15 if (update_thread.joinable()) {
16 update_thread.join();
17 }
18}
19
20void Mouse::UpdateThread() {
21 constexpr int update_time = 10;
22 while (update_thread_running) {
23 for (MouseInfo& info : mouse_info) {
24 const Common::Vec3f angular_direction{
25 -info.tilt_direction.y,
26 0.0f,
27 -info.tilt_direction.x,
28 };
29
30 info.motion.SetGyroscope(angular_direction * info.tilt_speed);
31 info.motion.UpdateRotation(update_time * 1000);
32 info.motion.UpdateOrientation(update_time * 1000);
33 info.tilt_speed = 0;
34 info.data.motion = info.motion.GetMotion();
35 }
36 if (configuring) {
37 UpdateYuzuSettings();
38 }
39 std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
40 }
41}
42
43void Mouse::UpdateYuzuSettings() {
44 if (buttons == 0) {
45 return;
46 }
47
48 mouse_queue.Push(MouseStatus{
49 .button = last_button,
50 });
51}
52
53void Mouse::PressButton(int x, int y, int button_) {
54 const auto button_index = static_cast<std::size_t>(button_);
55 if (button_index >= mouse_info.size()) {
56 return;
57 }
58
59 const auto button = 1U << button_index;
60 buttons |= static_cast<u16>(button);
61 last_button = static_cast<MouseButton>(button_index);
62
63 mouse_info[button_index].mouse_origin = Common::MakeVec(x, y);
64 mouse_info[button_index].last_mouse_position = Common::MakeVec(x, y);
65 mouse_info[button_index].data.pressed = true;
66}
67
68void Mouse::MouseMove(int x, int y) {
69 for (MouseInfo& info : mouse_info) {
70 if (info.data.pressed) {
71 const auto mouse_move = Common::MakeVec(x, y) - info.mouse_origin;
72 const auto mouse_change = Common::MakeVec(x, y) - info.last_mouse_position;
73 info.last_mouse_position = Common::MakeVec(x, y);
74 info.data.axis = {mouse_move.x, -mouse_move.y};
75
76 if (mouse_change.x == 0 && mouse_change.y == 0) {
77 info.tilt_speed = 0;
78 } else {
79 info.tilt_direction = mouse_change.Cast<float>();
80 info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity;
81 }
82 }
83 }
84}
85
86void Mouse::ReleaseButton(int button_) {
87 const auto button_index = static_cast<std::size_t>(button_);
88 if (button_index >= mouse_info.size()) {
89 return;
90 }
91
92 const auto button = 1U << button_index;
93 buttons &= static_cast<u16>(0xFF - button);
94
95 mouse_info[button_index].tilt_speed = 0;
96 mouse_info[button_index].data.pressed = false;
97 mouse_info[button_index].data.axis = {0, 0};
98}
99
100void Mouse::BeginConfiguration() {
101 buttons = 0;
102 last_button = MouseButton::Undefined;
103 mouse_queue.Clear();
104 configuring = true;
105}
106
107void Mouse::EndConfiguration() {
108 buttons = 0;
109 last_button = MouseButton::Undefined;
110 mouse_queue.Clear();
111 configuring = false;
112}
113
114Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() {
115 return mouse_queue;
116}
117
118const Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() const {
119 return mouse_queue;
120}
121
122MouseData& Mouse::GetMouseState(std::size_t button) {
123 return mouse_info[button].data;
124}
125
126const MouseData& Mouse::GetMouseState(std::size_t button) const {
127 return mouse_info[button].data;
128}
129} // namespace MouseInput
diff --git a/src/input_common/mouse/mouse_input.h b/src/input_common/mouse/mouse_input.h
new file mode 100644
index 000000000..58803c1bf
--- /dev/null
+++ b/src/input_common/mouse/mouse_input.h
@@ -0,0 +1,98 @@
1// Copyright 2020 yuzu 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 <mutex>
9#include <thread>
10
11#include "common/common_types.h"
12#include "common/threadsafe_queue.h"
13#include "common/vector_math.h"
14#include "core/frontend/input.h"
15#include "input_common/motion_input.h"
16
17namespace MouseInput {
18
19enum class MouseButton {
20 Left,
21 Wheel,
22 Right,
23 Forward,
24 Backward,
25 Undefined,
26};
27
28struct MouseStatus {
29 MouseButton button{MouseButton::Undefined};
30};
31
32struct MouseData {
33 bool pressed{};
34 std::array<int, 2> axis{};
35 Input::MotionStatus motion{};
36 Input::TouchStatus touch{};
37};
38
39class Mouse {
40public:
41 Mouse();
42 ~Mouse();
43
44 /// Used for polling
45 void BeginConfiguration();
46 void EndConfiguration();
47
48 /**
49 * Signals that a button is pressed.
50 * @param x the x-coordinate of the cursor
51 * @param y the y-coordinate of the cursor
52 * @param button_ the button pressed
53 */
54 void PressButton(int x, int y, int button_);
55
56 /**
57 * Signals that mouse has moved.
58 * @param x the x-coordinate of the cursor
59 * @param y the y-coordinate of the cursor
60 */
61 void MouseMove(int x, int y);
62
63 /**
64 * Signals that a motion sensor tilt has ended.
65 */
66 void ReleaseButton(int button_);
67
68 [[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue();
69 [[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const;
70
71 [[nodiscard]] MouseData& GetMouseState(std::size_t button);
72 [[nodiscard]] const MouseData& GetMouseState(std::size_t button) const;
73
74private:
75 void UpdateThread();
76 void UpdateYuzuSettings();
77
78 struct MouseInfo {
79 InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f};
80 Common::Vec2<int> mouse_origin;
81 Common::Vec2<int> last_mouse_position;
82 bool is_tilting = false;
83 float sensitivity{0.120f};
84
85 float tilt_speed = 0;
86 Common::Vec2<float> tilt_direction;
87 MouseData data;
88 };
89
90 u16 buttons{};
91 std::thread update_thread;
92 MouseButton last_button{MouseButton::Undefined};
93 std::array<MouseInfo, 5> mouse_info;
94 Common::SPSCQueue<MouseStatus> mouse_queue;
95 bool configuring{false};
96 bool update_thread_running{true};
97};
98} // namespace MouseInput
diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp
new file mode 100644
index 000000000..508eb0c7d
--- /dev/null
+++ b/src/input_common/mouse/mouse_poller.cpp
@@ -0,0 +1,274 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <mutex>
6#include <utility>
7
8#include "common/threadsafe_queue.h"
9#include "input_common/mouse/mouse_input.h"
10#include "input_common/mouse/mouse_poller.h"
11
12namespace InputCommon {
13
14class MouseButton final : public Input::ButtonDevice {
15public:
16 explicit MouseButton(u32 button_, const MouseInput::Mouse* mouse_input_)
17 : button(button_), mouse_input(mouse_input_) {}
18
19 bool GetStatus() const override {
20 return mouse_input->GetMouseState(button).pressed;
21 }
22
23private:
24 const u32 button;
25 const MouseInput::Mouse* mouse_input;
26};
27
28MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
29 : mouse_input(std::move(mouse_input_)) {}
30
31std::unique_ptr<Input::ButtonDevice> MouseButtonFactory::Create(
32 const Common::ParamPackage& params) {
33 const auto button_id = params.Get("button", 0);
34
35 return std::make_unique<MouseButton>(button_id, mouse_input.get());
36}
37
38Common::ParamPackage MouseButtonFactory::GetNextInput() const {
39 MouseInput::MouseStatus pad;
40 Common::ParamPackage params;
41 auto& queue = mouse_input->GetMouseQueue();
42 while (queue.Pop(pad)) {
43 // This while loop will break on the earliest detected button
44 if (pad.button != MouseInput::MouseButton::Undefined) {
45 params.Set("engine", "mouse");
46 params.Set("button", static_cast<u16>(pad.button));
47 return params;
48 }
49 }
50 return params;
51}
52
53void MouseButtonFactory::BeginConfiguration() {
54 polling = true;
55 mouse_input->BeginConfiguration();
56}
57
58void MouseButtonFactory::EndConfiguration() {
59 polling = false;
60 mouse_input->EndConfiguration();
61}
62
63class MouseAnalog final : public Input::AnalogDevice {
64public:
65 explicit MouseAnalog(u32 port_, u32 axis_x_, u32 axis_y_, bool invert_x_, bool invert_y_,
66 float deadzone_, float range_, const MouseInput::Mouse* mouse_input_)
67 : button(port_), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), invert_y(invert_y_),
68 deadzone(deadzone_), range(range_), mouse_input(mouse_input_) {}
69
70 float GetAxis(u32 axis) const {
71 std::lock_guard lock{mutex};
72 const auto axis_value =
73 static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis));
74 return axis_value / (100.0f * range);
75 }
76
77 std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
78 float x = GetAxis(analog_axis_x);
79 float y = GetAxis(analog_axis_y);
80 if (invert_x) {
81 x = -x;
82 }
83 if (invert_y) {
84 y = -y;
85 }
86
87 // Make sure the coordinates are in the unit circle,
88 // otherwise normalize it.
89 float r = x * x + y * y;
90 if (r > 1.0f) {
91 r = std::sqrt(r);
92 x /= r;
93 y /= r;
94 }
95
96 return {x, y};
97 }
98
99 std::tuple<float, float> GetStatus() const override {
100 const auto [x, y] = GetAnalog(axis_x, axis_y);
101 const float r = std::sqrt((x * x) + (y * y));
102 if (r > deadzone) {
103 return {x / r * (r - deadzone) / (1 - deadzone),
104 y / r * (r - deadzone) / (1 - deadzone)};
105 }
106 return {0.0f, 0.0f};
107 }
108
109private:
110 const u32 button;
111 const u32 axis_x;
112 const u32 axis_y;
113 const bool invert_x;
114 const bool invert_y;
115 const float deadzone;
116 const float range;
117 const MouseInput::Mouse* mouse_input;
118 mutable std::mutex mutex;
119};
120
121/// An analog device factory that creates analog devices from GC Adapter
122MouseAnalogFactory::MouseAnalogFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
123 : mouse_input(std::move(mouse_input_)) {}
124
125/**
126 * Creates analog device from joystick axes
127 * @param params contains parameters for creating the device:
128 * - "port": the nth gcpad on the adapter
129 * - "axis_x": the index of the axis to be bind as x-axis
130 * - "axis_y": the index of the axis to be bind as y-axis
131 */
132std::unique_ptr<Input::AnalogDevice> MouseAnalogFactory::Create(
133 const Common::ParamPackage& params) {
134 const auto port = static_cast<u32>(params.Get("port", 0));
135 const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
136 const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
137 const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
138 const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
139 const std::string invert_x_value = params.Get("invert_x", "+");
140 const std::string invert_y_value = params.Get("invert_y", "+");
141 const bool invert_x = invert_x_value == "-";
142 const bool invert_y = invert_y_value == "-";
143
144 return std::make_unique<MouseAnalog>(port, axis_x, axis_y, invert_x, invert_y, deadzone, range,
145 mouse_input.get());
146}
147
148void MouseAnalogFactory::BeginConfiguration() {
149 polling = true;
150 mouse_input->BeginConfiguration();
151}
152
153void MouseAnalogFactory::EndConfiguration() {
154 polling = false;
155 mouse_input->EndConfiguration();
156}
157
158Common::ParamPackage MouseAnalogFactory::GetNextInput() const {
159 MouseInput::MouseStatus pad;
160 Common::ParamPackage params;
161 auto& queue = mouse_input->GetMouseQueue();
162 while (queue.Pop(pad)) {
163 // This while loop will break on the earliest detected button
164 if (pad.button != MouseInput::MouseButton::Undefined) {
165 params.Set("engine", "mouse");
166 params.Set("port", static_cast<u16>(pad.button));
167 params.Set("axis_x", 0);
168 params.Set("axis_y", 1);
169 params.Set("invert_x", "+");
170 params.Set("invert_y", "+");
171 return params;
172 }
173 }
174 return params;
175}
176
177class MouseMotion final : public Input::MotionDevice {
178public:
179 explicit MouseMotion(u32 button_, const MouseInput::Mouse* mouse_input_)
180 : button(button_), mouse_input(mouse_input_) {}
181
182 Input::MotionStatus GetStatus() const override {
183 return mouse_input->GetMouseState(button).motion;
184 }
185
186private:
187 const u32 button;
188 const MouseInput::Mouse* mouse_input;
189};
190
191MouseMotionFactory::MouseMotionFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
192 : mouse_input(std::move(mouse_input_)) {}
193
194std::unique_ptr<Input::MotionDevice> MouseMotionFactory::Create(
195 const Common::ParamPackage& params) {
196 const auto button_id = params.Get("button", 0);
197
198 return std::make_unique<MouseMotion>(button_id, mouse_input.get());
199}
200
201Common::ParamPackage MouseMotionFactory::GetNextInput() const {
202 MouseInput::MouseStatus pad;
203 Common::ParamPackage params;
204 auto& queue = mouse_input->GetMouseQueue();
205 while (queue.Pop(pad)) {
206 // This while loop will break on the earliest detected button
207 if (pad.button != MouseInput::MouseButton::Undefined) {
208 params.Set("engine", "mouse");
209 params.Set("button", static_cast<u16>(pad.button));
210 return params;
211 }
212 }
213 return params;
214}
215
216void MouseMotionFactory::BeginConfiguration() {
217 polling = true;
218 mouse_input->BeginConfiguration();
219}
220
221void MouseMotionFactory::EndConfiguration() {
222 polling = false;
223 mouse_input->EndConfiguration();
224}
225
226class MouseTouch final : public Input::TouchDevice {
227public:
228 explicit MouseTouch(u32 button_, const MouseInput::Mouse* mouse_input_)
229 : button(button_), mouse_input(mouse_input_) {}
230
231 Input::TouchStatus GetStatus() const override {
232 return mouse_input->GetMouseState(button).touch;
233 }
234
235private:
236 const u32 button;
237 const MouseInput::Mouse* mouse_input;
238};
239
240MouseTouchFactory::MouseTouchFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
241 : mouse_input(std::move(mouse_input_)) {}
242
243std::unique_ptr<Input::TouchDevice> MouseTouchFactory::Create(const Common::ParamPackage& params) {
244 const auto button_id = params.Get("button", 0);
245
246 return std::make_unique<MouseTouch>(button_id, mouse_input.get());
247}
248
249Common::ParamPackage MouseTouchFactory::GetNextInput() const {
250 MouseInput::MouseStatus pad;
251 Common::ParamPackage params;
252 auto& queue = mouse_input->GetMouseQueue();
253 while (queue.Pop(pad)) {
254 // This while loop will break on the earliest detected button
255 if (pad.button != MouseInput::MouseButton::Undefined) {
256 params.Set("engine", "mouse");
257 params.Set("button", static_cast<u16>(pad.button));
258 return params;
259 }
260 }
261 return params;
262}
263
264void MouseTouchFactory::BeginConfiguration() {
265 polling = true;
266 mouse_input->BeginConfiguration();
267}
268
269void MouseTouchFactory::EndConfiguration() {
270 polling = false;
271 mouse_input->EndConfiguration();
272}
273
274} // namespace InputCommon
diff --git a/src/input_common/mouse/mouse_poller.h b/src/input_common/mouse/mouse_poller.h
new file mode 100644
index 000000000..cf331293b
--- /dev/null
+++ b/src/input_common/mouse/mouse_poller.h
@@ -0,0 +1,109 @@
1// Copyright 2020 yuzu 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 <memory>
8#include "core/frontend/input.h"
9#include "input_common/mouse/mouse_input.h"
10
11namespace InputCommon {
12
13/**
14 * A button device factory representing a mouse. It receives mouse events and forward them
15 * to all button devices it created.
16 */
17class MouseButtonFactory final : public Input::Factory<Input::ButtonDevice> {
18public:
19 explicit MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
20
21 /**
22 * Creates a button device from a button press
23 * @param params contains parameters for creating the device:
24 * - "code": the code of the key to bind with the button
25 */
26 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
27
28 Common::ParamPackage GetNextInput() const;
29
30 /// For device input configuration/polling
31 void BeginConfiguration();
32 void EndConfiguration();
33
34 bool IsPolling() const {
35 return polling;
36 }
37
38private:
39 std::shared_ptr<MouseInput::Mouse> mouse_input;
40 bool polling = false;
41};
42
43/// An analog device factory that creates analog devices from mouse
44class MouseAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
45public:
46 explicit MouseAnalogFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
47
48 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
49
50 Common::ParamPackage GetNextInput() const;
51
52 /// For device input configuration/polling
53 void BeginConfiguration();
54 void EndConfiguration();
55
56 bool IsPolling() const {
57 return polling;
58 }
59
60private:
61 std::shared_ptr<MouseInput::Mouse> mouse_input;
62 bool polling = false;
63};
64
65/// A motion device factory that creates motion devices from mouse
66class MouseMotionFactory final : public Input::Factory<Input::MotionDevice> {
67public:
68 explicit MouseMotionFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
69
70 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
71
72 Common::ParamPackage GetNextInput() const;
73
74 /// For device input configuration/polling
75 void BeginConfiguration();
76 void EndConfiguration();
77
78 bool IsPolling() const {
79 return polling;
80 }
81
82private:
83 std::shared_ptr<MouseInput::Mouse> mouse_input;
84 bool polling = false;
85};
86
87/// An touch device factory that creates touch devices from mouse
88class MouseTouchFactory final : public Input::Factory<Input::TouchDevice> {
89public:
90 explicit MouseTouchFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
91
92 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override;
93
94 Common::ParamPackage GetNextInput() const;
95
96 /// For device input configuration/polling
97 void BeginConfiguration();
98 void EndConfiguration();
99
100 bool IsPolling() const {
101 return polling;
102 }
103
104private:
105 std::shared_ptr<MouseInput::Mouse> mouse_input;
106 bool polling = false;
107};
108
109} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index f3554be9a..42bbf14d4 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -23,7 +23,7 @@ public:
23 /// Unregisters SDL device factories and shut them down. 23 /// Unregisters SDL device factories and shut them down.
24 virtual ~State() = default; 24 virtual ~State() = default;
25 25
26 virtual Pollers GetPollers(Polling::DeviceType type) { 26 virtual Pollers GetPollers(Polling::DeviceType) {
27 return {}; 27 return {};
28 } 28 }
29 29
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index a9e676f4b..d32eb732a 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -5,6 +5,7 @@
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <array>
7#include <atomic> 7#include <atomic>
8#include <chrono>
8#include <cmath> 9#include <cmath>
9#include <functional> 10#include <functional>
10#include <mutex> 11#include <mutex>
@@ -21,6 +22,7 @@
21#include "common/param_package.h" 22#include "common/param_package.h"
22#include "common/threadsafe_queue.h" 23#include "common/threadsafe_queue.h"
23#include "core/frontend/input.h" 24#include "core/frontend/input.h"
25#include "input_common/motion_input.h"
24#include "input_common/sdl/sdl_impl.h" 26#include "input_common/sdl/sdl_impl.h"
25#include "input_common/settings.h" 27#include "input_common/settings.h"
26 28
@@ -54,9 +56,9 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
54class SDLJoystick { 56class SDLJoystick {
55public: 57public:
56 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, 58 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
57 SDL_GameController* gamecontroller) 59 SDL_GameController* game_controller)
58 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose}, 60 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
59 sdl_controller{gamecontroller, &SDL_GameControllerClose} {} 61 sdl_controller{game_controller, &SDL_GameControllerClose} {}
60 62
61 void SetButton(int button, bool value) { 63 void SetButton(int button, bool value) {
62 std::lock_guard lock{mutex}; 64 std::lock_guard lock{mutex};
@@ -75,7 +77,17 @@ public:
75 77
76 float GetAxis(int axis, float range) const { 78 float GetAxis(int axis, float range) const {
77 std::lock_guard lock{mutex}; 79 std::lock_guard lock{mutex};
78 return state.axes.at(axis) / (32767.0f * range); 80 return static_cast<float>(state.axes.at(axis)) / (32767.0f * range);
81 }
82
83 bool RumblePlay(u16 amp_low, u16 amp_high) {
84 if (sdl_controller) {
85 return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0;
86 } else if (sdl_joystick) {
87 return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0;
88 }
89
90 return false;
79 } 91 }
80 92
81 std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const { 93 std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const {
@@ -95,6 +107,10 @@ public:
95 return std::make_tuple(x, y); 107 return std::make_tuple(x, y);
96 } 108 }
97 109
110 const MotionInput& GetMotion() const {
111 return motion;
112 }
113
98 void SetHat(int hat, Uint8 direction) { 114 void SetHat(int hat, Uint8 direction) {
99 std::lock_guard lock{mutex}; 115 std::lock_guard lock{mutex};
100 state.hats.insert_or_assign(hat, direction); 116 state.hats.insert_or_assign(hat, direction);
@@ -122,15 +138,15 @@ public:
122 return sdl_joystick.get(); 138 return sdl_joystick.get();
123 } 139 }
124 140
125 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
126 sdl_controller.reset(controller);
127 sdl_joystick.reset(joystick);
128 }
129
130 SDL_GameController* GetSDLGameController() const { 141 SDL_GameController* GetSDLGameController() const {
131 return sdl_controller.get(); 142 return sdl_controller.get();
132 } 143 }
133 144
145 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
146 sdl_joystick.reset(joystick);
147 sdl_controller.reset(controller);
148 }
149
134private: 150private:
135 struct State { 151 struct State {
136 std::unordered_map<int, bool> buttons; 152 std::unordered_map<int, bool> buttons;
@@ -142,74 +158,66 @@ private:
142 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; 158 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
143 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller; 159 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
144 mutable std::mutex mutex; 160 mutable std::mutex mutex;
161
162 // Motion is initialized without PID values as motion input is not aviable for SDL2
163 MotionInput motion{0.0f, 0.0f, 0.0f};
145}; 164};
146 165
147std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) { 166std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
148 std::lock_guard lock{joystick_map_mutex}; 167 std::lock_guard lock{joystick_map_mutex};
149 const auto it = joystick_map.find(guid); 168 const auto it = joystick_map.find(guid);
169
150 if (it != joystick_map.end()) { 170 if (it != joystick_map.end()) {
151 while (it->second.size() <= static_cast<std::size_t>(port)) { 171 while (it->second.size() <= static_cast<std::size_t>(port)) {
152 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), 172 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
153 nullptr, nullptr); 173 nullptr, nullptr);
154 it->second.emplace_back(std::move(joystick)); 174 it->second.emplace_back(std::move(joystick));
155 } 175 }
156 return it->second[port]; 176
177 return it->second[static_cast<std::size_t>(port)];
157 } 178 }
179
158 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr); 180 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
181
159 return joystick_map[guid].emplace_back(std::move(joystick)); 182 return joystick_map[guid].emplace_back(std::move(joystick));
160} 183}
161 184
162std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { 185std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
163 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); 186 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
164 auto sdl_controller = SDL_GameControllerFromInstanceID(sdl_id);
165 const std::string guid = GetGUID(sdl_joystick); 187 const std::string guid = GetGUID(sdl_joystick);
166 188
167 std::lock_guard lock{joystick_map_mutex}; 189 std::lock_guard lock{joystick_map_mutex};
168 const auto map_it = joystick_map.find(guid); 190 const auto map_it = joystick_map.find(guid);
169 if (map_it != joystick_map.end()) { 191
170 const auto vec_it = 192 if (map_it == joystick_map.end()) {
171 std::find_if(map_it->second.begin(), map_it->second.end(), 193 return nullptr;
172 [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) { 194 }
173 return sdl_joystick == joystick->GetSDLJoystick(); 195
174 }); 196 const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
175 if (vec_it != map_it->second.end()) { 197 [&sdl_joystick](const auto& joystick) {
176 // This is the common case: There is already an existing SDL_Joystick maped to a 198 return joystick->GetSDLJoystick() == sdl_joystick;
177 // SDLJoystick. return the SDLJoystick 199 });
178 return *vec_it; 200
179 } 201 if (vec_it == map_it->second.end()) {
180 202 return nullptr;
181 // Search for a SDLJoystick without a mapped SDL_Joystick... 203 }
182 const auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(), 204
183 [](const std::shared_ptr<SDLJoystick>& joystick) { 205 return *vec_it;
184 return !joystick->GetSDLJoystick();
185 });
186 if (nullptr_it != map_it->second.end()) {
187 // ... and map it
188 (*nullptr_it)->SetSDLJoystick(sdl_joystick, sdl_controller);
189 return *nullptr_it;
190 }
191
192 // There is no SDLJoystick without a mapped SDL_Joystick
193 // Create a new SDLJoystick
194 const int port = static_cast<int>(map_it->second.size());
195 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_controller);
196 return map_it->second.emplace_back(std::move(joystick));
197 }
198
199 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_controller);
200 return joystick_map[guid].emplace_back(std::move(joystick));
201} 206}
202 207
203void SDLState::InitJoystick(int joystick_index) { 208void SDLState::InitJoystick(int joystick_index) {
204 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); 209 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
205 SDL_GameController* sdl_gamecontroller = nullptr; 210 SDL_GameController* sdl_gamecontroller = nullptr;
211
206 if (SDL_IsGameController(joystick_index)) { 212 if (SDL_IsGameController(joystick_index)) {
207 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index); 213 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
208 } 214 }
215
209 if (!sdl_joystick) { 216 if (!sdl_joystick) {
210 LOG_ERROR(Input, "failed to open joystick {}", joystick_index); 217 LOG_ERROR(Input, "Failed to open joystick {}", joystick_index);
211 return; 218 return;
212 } 219 }
220
213 const std::string guid = GetGUID(sdl_joystick); 221 const std::string guid = GetGUID(sdl_joystick);
214 222
215 std::lock_guard lock{joystick_map_mutex}; 223 std::lock_guard lock{joystick_map_mutex};
@@ -218,14 +226,17 @@ void SDLState::InitJoystick(int joystick_index) {
218 joystick_map[guid].emplace_back(std::move(joystick)); 226 joystick_map[guid].emplace_back(std::move(joystick));
219 return; 227 return;
220 } 228 }
229
221 auto& joystick_guid_list = joystick_map[guid]; 230 auto& joystick_guid_list = joystick_map[guid];
222 const auto it = std::find_if( 231 const auto joystick_it =
223 joystick_guid_list.begin(), joystick_guid_list.end(), 232 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
224 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); }); 233 [](const auto& joystick) { return !joystick->GetSDLJoystick(); });
225 if (it != joystick_guid_list.end()) { 234
226 (*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller); 235 if (joystick_it != joystick_guid_list.end()) {
236 (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
227 return; 237 return;
228 } 238 }
239
229 const int port = static_cast<int>(joystick_guid_list.size()); 240 const int port = static_cast<int>(joystick_guid_list.size());
230 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); 241 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
231 joystick_guid_list.emplace_back(std::move(joystick)); 242 joystick_guid_list.emplace_back(std::move(joystick));
@@ -234,22 +245,15 @@ void SDLState::InitJoystick(int joystick_index) {
234void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) { 245void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
235 const std::string guid = GetGUID(sdl_joystick); 246 const std::string guid = GetGUID(sdl_joystick);
236 247
237 std::shared_ptr<SDLJoystick> joystick; 248 std::lock_guard lock{joystick_map_mutex};
238 { 249 // This call to guid is safe since the joystick is guaranteed to be in the map
239 std::lock_guard lock{joystick_map_mutex}; 250 const auto& joystick_guid_list = joystick_map[guid];
240 // This call to guid is safe since the joystick is guaranteed to be in the map 251 const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
241 const auto& joystick_guid_list = joystick_map[guid]; 252 [&sdl_joystick](const auto& joystick) {
242 const auto joystick_it = 253 return joystick->GetSDLJoystick() == sdl_joystick;
243 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), 254 });
244 [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) { 255
245 return joystick->GetSDLJoystick() == sdl_joystick; 256 (*joystick_it)->SetSDLJoystick(nullptr, nullptr);
246 });
247 joystick = *joystick_it;
248 }
249
250 // Destruct SDL_Joystick outside the lock guard because SDL can internally call the
251 // event callback which locks the mutex again.
252 joystick->SetSDLJoystick(nullptr, nullptr);
253} 257}
254 258
255void SDLState::HandleGameControllerEvent(const SDL_Event& event) { 259void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
@@ -347,14 +351,21 @@ private:
347 351
348class SDLAnalog final : public Input::AnalogDevice { 352class SDLAnalog final : public Input::AnalogDevice {
349public: 353public:
350 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_, 354 explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_,
351 float range_) 355 bool invert_x_, bool invert_y_, float deadzone_, float range_)
352 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), 356 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_),
353 range(range_) {} 357 invert_y(invert_y_), deadzone(deadzone_), range(range_) {}
354 358
355 std::tuple<float, float> GetStatus() const override { 359 std::tuple<float, float> GetStatus() const override {
356 const auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range); 360 auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range);
357 const float r = std::sqrt((x * x) + (y * y)); 361 const float r = std::sqrt((x * x) + (y * y));
362 if (invert_x) {
363 x = -x;
364 }
365 if (invert_y) {
366 y = -y;
367 }
368
358 if (r > deadzone) { 369 if (r > deadzone) {
359 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone), 370 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
360 y / r * (r - deadzone) / (1 - deadzone)); 371 y / r * (r - deadzone) / (1 - deadzone));
@@ -382,10 +393,100 @@ private:
382 std::shared_ptr<SDLJoystick> joystick; 393 std::shared_ptr<SDLJoystick> joystick;
383 const int axis_x; 394 const int axis_x;
384 const int axis_y; 395 const int axis_y;
396 const bool invert_x;
397 const bool invert_y;
385 const float deadzone; 398 const float deadzone;
386 const float range; 399 const float range;
387}; 400};
388 401
402class SDLVibration final : public Input::VibrationDevice {
403public:
404 explicit SDLVibration(std::shared_ptr<SDLJoystick> joystick_)
405 : joystick(std::move(joystick_)) {}
406
407 u8 GetStatus() const override {
408 joystick->RumblePlay(1, 1);
409 return joystick->RumblePlay(0, 0);
410 }
411
412 bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
413 [[maybe_unused]] f32 freq_high) const override {
414 const auto process_amplitude = [](f32 amplitude) {
415 return static_cast<u16>((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF);
416 };
417
418 const auto processed_amp_low = process_amplitude(amp_low);
419 const auto processed_amp_high = process_amplitude(amp_high);
420
421 return joystick->RumblePlay(processed_amp_low, processed_amp_high);
422 }
423
424private:
425 std::shared_ptr<SDLJoystick> joystick;
426};
427
428class SDLDirectionMotion final : public Input::MotionDevice {
429public:
430 explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
431 : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
432
433 Input::MotionStatus GetStatus() const override {
434 if (joystick->GetHatDirection(hat, direction)) {
435 return joystick->GetMotion().GetRandomMotion(2, 6);
436 }
437 return joystick->GetMotion().GetRandomMotion(0, 0);
438 }
439
440private:
441 std::shared_ptr<SDLJoystick> joystick;
442 int hat;
443 Uint8 direction;
444};
445
446class SDLAxisMotion final : public Input::MotionDevice {
447public:
448 explicit SDLAxisMotion(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
449 bool trigger_if_greater_)
450 : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
451 trigger_if_greater(trigger_if_greater_) {}
452
453 Input::MotionStatus GetStatus() const override {
454 const float axis_value = joystick->GetAxis(axis, 1.0f);
455 bool trigger = axis_value < threshold;
456 if (trigger_if_greater) {
457 trigger = axis_value > threshold;
458 }
459
460 if (trigger) {
461 return joystick->GetMotion().GetRandomMotion(2, 6);
462 }
463 return joystick->GetMotion().GetRandomMotion(0, 0);
464 }
465
466private:
467 std::shared_ptr<SDLJoystick> joystick;
468 int axis;
469 float threshold;
470 bool trigger_if_greater;
471};
472
473class SDLButtonMotion final : public Input::MotionDevice {
474public:
475 explicit SDLButtonMotion(std::shared_ptr<SDLJoystick> joystick_, int button_)
476 : joystick(std::move(joystick_)), button(button_) {}
477
478 Input::MotionStatus GetStatus() const override {
479 if (joystick->GetButton(button)) {
480 return joystick->GetMotion().GetRandomMotion(2, 6);
481 }
482 return joystick->GetMotion().GetRandomMotion(0, 0);
483 }
484
485private:
486 std::shared_ptr<SDLJoystick> joystick;
487 int button;
488};
489
389/// A button device factory that creates button devices from SDL joystick 490/// A button device factory that creates button devices from SDL joystick
390class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> { 491class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
391public: 492public:
@@ -466,7 +567,7 @@ class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
466public: 567public:
467 explicit SDLAnalogFactory(SDLState& state_) : state(state_) {} 568 explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
468 /** 569 /**
469 * Creates analog device from joystick axes 570 * Creates an analog device from joystick axes
470 * @param params contains parameters for creating the device: 571 * @param params contains parameters for creating the device:
471 * - "guid": the guid of the joystick to bind 572 * - "guid": the guid of the joystick to bind
472 * - "port": the nth joystick of the same type 573 * - "port": the nth joystick of the same type
@@ -480,12 +581,101 @@ public:
480 const int axis_y = params.Get("axis_y", 1); 581 const int axis_y = params.Get("axis_y", 1);
481 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); 582 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
482 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); 583 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
584 const std::string invert_x_value = params.Get("invert_x", "+");
585 const std::string invert_y_value = params.Get("invert_y", "+");
586 const bool invert_x = invert_x_value == "-";
587 const bool invert_y = invert_y_value == "-";
483 auto joystick = state.GetSDLJoystickByGUID(guid, port); 588 auto joystick = state.GetSDLJoystickByGUID(guid, port);
484 589
485 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash 590 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
486 joystick->SetAxis(axis_x, 0); 591 joystick->SetAxis(axis_x, 0);
487 joystick->SetAxis(axis_y, 0); 592 joystick->SetAxis(axis_y, 0);
488 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone, range); 593 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, invert_x, invert_y, deadzone,
594 range);
595 }
596
597private:
598 SDLState& state;
599};
600
601/// An vibration device factory that creates vibration devices from SDL joystick
602class SDLVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
603public:
604 explicit SDLVibrationFactory(SDLState& state_) : state(state_) {}
605 /**
606 * Creates a vibration device from a joystick
607 * @param params contains parameters for creating the device:
608 * - "guid": the guid of the joystick to bind
609 * - "port": the nth joystick of the same type
610 */
611 std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override {
612 const std::string guid = params.Get("guid", "0");
613 const int port = params.Get("port", 0);
614 return std::make_unique<SDLVibration>(state.GetSDLJoystickByGUID(guid, port));
615 }
616
617private:
618 SDLState& state;
619};
620
621/// A motion device factory that creates motion devices from SDL joystick
622class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> {
623public:
624 explicit SDLMotionFactory(SDLState& state_) : state(state_) {}
625 /**
626 * Creates motion device from joystick axes
627 * @param params contains parameters for creating the device:
628 * - "guid": the guid of the joystick to bind
629 * - "port": the nth joystick of the same type
630 */
631 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override {
632 const std::string guid = params.Get("guid", "0");
633 const int port = params.Get("port", 0);
634
635 auto joystick = state.GetSDLJoystickByGUID(guid, port);
636
637 if (params.Has("hat")) {
638 const int hat = params.Get("hat", 0);
639 const std::string direction_name = params.Get("direction", "");
640 Uint8 direction;
641 if (direction_name == "up") {
642 direction = SDL_HAT_UP;
643 } else if (direction_name == "down") {
644 direction = SDL_HAT_DOWN;
645 } else if (direction_name == "left") {
646 direction = SDL_HAT_LEFT;
647 } else if (direction_name == "right") {
648 direction = SDL_HAT_RIGHT;
649 } else {
650 direction = 0;
651 }
652 // This is necessary so accessing GetHat with hat won't crash
653 joystick->SetHat(hat, SDL_HAT_CENTERED);
654 return std::make_unique<SDLDirectionMotion>(joystick, hat, direction);
655 }
656
657 if (params.Has("axis")) {
658 const int axis = params.Get("axis", 0);
659 const float threshold = params.Get("threshold", 0.5f);
660 const std::string direction_name = params.Get("direction", "");
661 bool trigger_if_greater;
662 if (direction_name == "+") {
663 trigger_if_greater = true;
664 } else if (direction_name == "-") {
665 trigger_if_greater = false;
666 } else {
667 trigger_if_greater = true;
668 LOG_ERROR(Input, "Unknown direction {}", direction_name);
669 }
670 // This is necessary so accessing GetAxis with axis won't crash
671 joystick->SetAxis(axis, 0);
672 return std::make_unique<SDLAxisMotion>(joystick, axis, threshold, trigger_if_greater);
673 }
674
675 const int button = params.Get("button", 0);
676 // This is necessary so accessing GetButton with button won't crash
677 joystick->SetButton(button, false);
678 return std::make_unique<SDLButtonMotion>(joystick, button);
489 } 679 }
490 680
491private: 681private:
@@ -494,18 +684,22 @@ private:
494 684
495SDLState::SDLState() { 685SDLState::SDLState() {
496 using namespace Input; 686 using namespace Input;
497 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
498 button_factory = std::make_shared<SDLButtonFactory>(*this); 687 button_factory = std::make_shared<SDLButtonFactory>(*this);
499 RegisterFactory<AnalogDevice>("sdl", analog_factory); 688 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
689 vibration_factory = std::make_shared<SDLVibrationFactory>(*this);
690 motion_factory = std::make_shared<SDLMotionFactory>(*this);
500 RegisterFactory<ButtonDevice>("sdl", button_factory); 691 RegisterFactory<ButtonDevice>("sdl", button_factory);
692 RegisterFactory<AnalogDevice>("sdl", analog_factory);
693 RegisterFactory<VibrationDevice>("sdl", vibration_factory);
694 RegisterFactory<MotionDevice>("sdl", motion_factory);
501 695
502 // If the frontend is going to manage the event loop, then we dont start one here 696 // If the frontend is going to manage the event loop, then we don't start one here
503 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK); 697 start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0;
504 if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) { 698 if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) {
505 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); 699 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
506 return; 700 return;
507 } 701 }
508 has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); 702 has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0;
509 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) { 703 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
510 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError()); 704 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
511 } 705 }
@@ -518,7 +712,7 @@ SDLState::SDLState() {
518 using namespace std::chrono_literals; 712 using namespace std::chrono_literals;
519 while (initialized) { 713 while (initialized) {
520 SDL_PumpEvents(); 714 SDL_PumpEvents();
521 std::this_thread::sleep_for(5ms); 715 std::this_thread::sleep_for(1ms);
522 } 716 }
523 }); 717 });
524 } 718 }
@@ -533,6 +727,8 @@ SDLState::~SDLState() {
533 using namespace Input; 727 using namespace Input;
534 UnregisterFactory<ButtonDevice>("sdl"); 728 UnregisterFactory<ButtonDevice>("sdl");
535 UnregisterFactory<AnalogDevice>("sdl"); 729 UnregisterFactory<AnalogDevice>("sdl");
730 UnregisterFactory<VibrationDevice>("sdl");
731 UnregisterFactory<MotionDevice>("sdl");
536 732
537 CloseJoysticks(); 733 CloseJoysticks();
538 SDL_DelEventWatch(&SDLEventWatcher, this); 734 SDL_DelEventWatch(&SDLEventWatcher, this);
@@ -549,8 +745,7 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
549 std::vector<Common::ParamPackage> devices; 745 std::vector<Common::ParamPackage> devices;
550 for (const auto& [key, value] : joystick_map) { 746 for (const auto& [key, value] : joystick_map) {
551 for (const auto& joystick : value) { 747 for (const auto& joystick : value) {
552 auto joy = joystick->GetSDLJoystick(); 748 if (auto* const controller = joystick->GetSDLGameController()) {
553 if (auto controller = joystick->GetSDLGameController()) {
554 std::string name = 749 std::string name =
555 fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort()); 750 fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort());
556 devices.emplace_back(Common::ParamPackage{ 751 devices.emplace_back(Common::ParamPackage{
@@ -559,7 +754,7 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
559 {"guid", joystick->GetGUID()}, 754 {"guid", joystick->GetGUID()},
560 {"port", std::to_string(joystick->GetPort())}, 755 {"port", std::to_string(joystick->GetPort())},
561 }); 756 });
562 } else if (joy) { 757 } else if (auto* const joy = joystick->GetSDLJoystick()) {
563 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort()); 758 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
564 devices.emplace_back(Common::ParamPackage{ 759 devices.emplace_back(Common::ParamPackage{
565 {"class", "sdl"}, 760 {"class", "sdl"},
@@ -574,7 +769,7 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
574} 769}
575 770
576namespace { 771namespace {
577Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis, 772Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
578 float value = 0.1f) { 773 float value = 0.1f) {
579 Common::ParamPackage params({{"engine", "sdl"}}); 774 Common::ParamPackage params({{"engine", "sdl"}});
580 params.Set("port", port); 775 params.Set("port", port);
@@ -590,7 +785,7 @@ Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid
590 return params; 785 return params;
591} 786}
592 787
593Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) { 788Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, s32 button) {
594 Common::ParamPackage params({{"engine", "sdl"}}); 789 Common::ParamPackage params({{"engine", "sdl"}});
595 params.Set("port", port); 790 params.Set("port", port);
596 params.Set("guid", std::move(guid)); 791 params.Set("guid", std::move(guid));
@@ -598,7 +793,7 @@ Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid
598 return params; 793 return params;
599} 794}
600 795
601Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u8 hat, u8 value) { 796Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat, s32 value) {
602 Common::ParamPackage params({{"engine", "sdl"}}); 797 Common::ParamPackage params({{"engine", "sdl"}});
603 798
604 params.Set("port", port); 799 params.Set("port", port);
@@ -626,19 +821,56 @@ Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u
626Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { 821Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
627 switch (event.type) { 822 switch (event.type) {
628 case SDL_JOYAXISMOTION: { 823 case SDL_JOYAXISMOTION: {
629 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 824 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
630 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), 825 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
631 event.jaxis.axis, event.jaxis.value); 826 static_cast<s32>(event.jaxis.axis),
827 event.jaxis.value);
828 }
829 break;
830 }
831 case SDL_JOYBUTTONUP: {
832 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
833 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
834 static_cast<s32>(event.jbutton.button));
835 }
836 break;
837 }
838 case SDL_JOYHATMOTION: {
839 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
840 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
841 static_cast<s32>(event.jhat.hat),
842 static_cast<s32>(event.jhat.value));
843 }
844 break;
845 }
846 }
847 return {};
848}
849
850Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Event& event) {
851 switch (event.type) {
852 case SDL_JOYAXISMOTION: {
853 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
854 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
855 static_cast<s32>(event.jaxis.axis),
856 event.jaxis.value);
857 }
858 break;
632 } 859 }
633 case SDL_JOYBUTTONUP: { 860 case SDL_JOYBUTTONUP: {
634 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); 861 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
635 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), 862 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
636 event.jbutton.button); 863 static_cast<s32>(event.jbutton.button));
864 }
865 break;
637 } 866 }
638 case SDL_JOYHATMOTION: { 867 case SDL_JOYHATMOTION: {
639 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); 868 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
640 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(), 869 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
641 event.jhat.hat, event.jhat.value); 870 static_cast<s32>(event.jhat.hat),
871 static_cast<s32>(event.jhat.value));
872 }
873 break;
642 } 874 }
643 } 875 }
644 return {}; 876 return {};
@@ -647,6 +879,8 @@ Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Eve
647Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid, 879Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
648 const SDL_GameControllerButtonBind& binding) { 880 const SDL_GameControllerButtonBind& binding) {
649 switch (binding.bindType) { 881 switch (binding.bindType) {
882 case SDL_CONTROLLER_BINDTYPE_NONE:
883 break;
650 case SDL_CONTROLLER_BINDTYPE_AXIS: 884 case SDL_CONTROLLER_BINDTYPE_AXIS:
651 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis); 885 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
652 case SDL_CONTROLLER_BINDTYPE_BUTTON: 886 case SDL_CONTROLLER_BINDTYPE_BUTTON:
@@ -666,6 +900,8 @@ Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& gui
666 params.Set("guid", guid); 900 params.Set("guid", guid);
667 params.Set("axis_x", axis_x); 901 params.Set("axis_x", axis_x);
668 params.Set("axis_y", axis_y); 902 params.Set("axis_y", axis_y);
903 params.Set("invert_x", "+");
904 params.Set("invert_y", "+");
669 return params; 905 return params;
670} 906}
671} // Anonymous namespace 907} // Anonymous namespace
@@ -767,7 +1003,7 @@ class SDLPoller : public InputCommon::Polling::DevicePoller {
767public: 1003public:
768 explicit SDLPoller(SDLState& state_) : state(state_) {} 1004 explicit SDLPoller(SDLState& state_) : state(state_) {}
769 1005
770 void Start(const std::string& device_id) override { 1006 void Start([[maybe_unused]] const std::string& device_id) override {
771 state.event_queue.Clear(); 1007 state.event_queue.Clear();
772 state.polling = true; 1008 state.polling = true;
773 } 1009 }
@@ -794,6 +1030,78 @@ public:
794 } 1030 }
795 return {}; 1031 return {};
796 } 1032 }
1033 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(SDL_Event& event) {
1034 switch (event.type) {
1035 case SDL_JOYAXISMOTION:
1036 if (!axis_memory.count(event.jaxis.which) ||
1037 !axis_memory[event.jaxis.which].count(event.jaxis.axis)) {
1038 axis_memory[event.jaxis.which][event.jaxis.axis] = event.jaxis.value;
1039 axis_event_count[event.jaxis.which][event.jaxis.axis] = 1;
1040 break;
1041 } else {
1042 axis_event_count[event.jaxis.which][event.jaxis.axis]++;
1043 // The joystick and axis exist in our map if we take this branch, so no checks
1044 // needed
1045 if (std::abs(
1046 (event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]) /
1047 32767.0) < 0.5) {
1048 break;
1049 } else {
1050 if (axis_event_count[event.jaxis.which][event.jaxis.axis] == 2 &&
1051 IsAxisAtPole(event.jaxis.value) &&
1052 IsAxisAtPole(axis_memory[event.jaxis.which][event.jaxis.axis])) {
1053 // If we have exactly two events and both are near a pole, this is
1054 // likely a digital input masquerading as an analog axis; Instead of
1055 // trying to look at the direction the axis travelled, assume the first
1056 // event was press and the second was release; This should handle most
1057 // digital axes while deferring to the direction of travel for analog
1058 // axes
1059 event.jaxis.value = static_cast<Sint16>(
1060 std::copysign(32767, axis_memory[event.jaxis.which][event.jaxis.axis]));
1061 } else {
1062 // There are more than two events, so this is likely a true analog axis,
1063 // check the direction it travelled
1064 event.jaxis.value = static_cast<Sint16>(std::copysign(
1065 32767,
1066 event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]));
1067 }
1068 axis_memory.clear();
1069 axis_event_count.clear();
1070 }
1071 }
1072 [[fallthrough]];
1073 case SDL_JOYBUTTONUP:
1074 case SDL_JOYHATMOTION:
1075 return {SDLEventToButtonParamPackage(state, event)};
1076 }
1077 return std::nullopt;
1078 }
1079
1080private:
1081 // Determine whether an axis value is close to an extreme or center
1082 // Some controllers have a digital D-Pad as a pair of analog sticks, with 3 possible values per
1083 // axis, which is why the center must be considered a pole
1084 bool IsAxisAtPole(int16_t value) const {
1085 return std::abs(value) >= 32767 || std::abs(value) < 327;
1086 }
1087 std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, int16_t>> axis_memory;
1088 std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, uint32_t>> axis_event_count;
1089};
1090
1091class SDLMotionPoller final : public SDLPoller {
1092public:
1093 explicit SDLMotionPoller(SDLState& state_) : SDLPoller(state_) {}
1094
1095 Common::ParamPackage GetNextInput() override {
1096 SDL_Event event;
1097 while (state.event_queue.Pop(event)) {
1098 const auto package = FromEvent(event);
1099 if (package) {
1100 return *package;
1101 }
1102 }
1103 return {};
1104 }
797 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const { 1105 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const {
798 switch (event.type) { 1106 switch (event.type) {
799 case SDL_JOYAXISMOTION: 1107 case SDL_JOYAXISMOTION:
@@ -803,7 +1111,7 @@ public:
803 [[fallthrough]]; 1111 [[fallthrough]];
804 case SDL_JOYBUTTONUP: 1112 case SDL_JOYBUTTONUP:
805 case SDL_JOYHATMOTION: 1113 case SDL_JOYHATMOTION:
806 return {SDLEventToButtonParamPackage(state, event)}; 1114 return {SDLEventToMotionParamPackage(state, event)};
807 } 1115 }
808 return std::nullopt; 1116 return std::nullopt;
809 } 1117 }
@@ -821,7 +1129,6 @@ public:
821 1129
822 void Start(const std::string& device_id) override { 1130 void Start(const std::string& device_id) override {
823 SDLPoller::Start(device_id); 1131 SDLPoller::Start(device_id);
824 // Load the game controller
825 // Reset stored axes 1132 // Reset stored axes
826 analog_x_axis = -1; 1133 analog_x_axis = -1;
827 analog_y_axis = -1; 1134 analog_y_axis = -1;
@@ -834,52 +1141,34 @@ public:
834 if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) { 1141 if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
835 continue; 1142 continue;
836 } 1143 }
837 // Simplify controller config by testing if game controller support is enabled.
838 if (event.type == SDL_JOYAXISMOTION) { 1144 if (event.type == SDL_JOYAXISMOTION) {
839 const auto axis = event.jaxis.axis; 1145 const auto axis = event.jaxis.axis;
840 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 1146 // In order to return a complete analog param, we need inputs for both axes.
841 const auto controller = joystick->GetSDLGameController(); 1147 // First we take the x-axis (horizontal) input, then the y-axis (vertical) input.
842 if (controller) { 1148 if (analog_x_axis == -1) {
843 const auto axis_left_x = 1149 analog_x_axis = axis;
844 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) 1150 } else if (analog_y_axis == -1 && analog_x_axis != axis) {
845 .value.axis; 1151 analog_y_axis = axis;
846 const auto axis_left_y = 1152 }
847 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) 1153 } else {
848 .value.axis; 1154 // If the press wasn't accepted as a joy axis, check for a button press
849 const auto axis_right_x = 1155 auto button_press = button_poller.FromEvent(event);
850 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) 1156 if (button_press) {
851 .value.axis; 1157 return *button_press;
852 const auto axis_right_y =
853 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY)
854 .value.axis;
855
856 if (axis == axis_left_x || axis == axis_left_y) {
857 analog_x_axis = axis_left_x;
858 analog_y_axis = axis_left_y;
859 break;
860 } else if (axis == axis_right_x || axis == axis_right_y) {
861 analog_x_axis = axis_right_x;
862 analog_y_axis = axis_right_y;
863 break;
864 }
865 } 1158 }
866 }
867
868 // If the press wasn't accepted as a joy axis, check for a button press
869 auto button_press = button_poller.FromEvent(event);
870 if (button_press) {
871 return *button_press;
872 } 1159 }
873 } 1160 }
874 1161
875 if (analog_x_axis != -1 && analog_y_axis != -1) { 1162 if (analog_x_axis != -1 && analog_y_axis != -1) {
876 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 1163 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
877 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1164 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
878 analog_x_axis, analog_y_axis); 1165 analog_x_axis, analog_y_axis);
879 analog_x_axis = -1; 1166 analog_x_axis = -1;
880 analog_y_axis = -1; 1167 analog_y_axis = -1;
881 return params; 1168 return params;
1169 }
882 } 1170 }
1171
883 return {}; 1172 return {};
884 } 1173 }
885 1174
@@ -900,6 +1189,9 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
900 case InputCommon::Polling::DeviceType::Button: 1189 case InputCommon::Polling::DeviceType::Button:
901 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); 1190 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
902 break; 1191 break;
1192 case InputCommon::Polling::DeviceType::Motion:
1193 pollers.emplace_back(std::make_unique<Polling::SDLMotionPoller>(*this));
1194 break;
903 } 1195 }
904 1196
905 return pollers; 1197 return pollers;
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
index bd19ba61d..08044b00d 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/sdl/sdl_impl.h
@@ -21,6 +21,8 @@ namespace InputCommon::SDL {
21 21
22class SDLAnalogFactory; 22class SDLAnalogFactory;
23class SDLButtonFactory; 23class SDLButtonFactory;
24class SDLMotionFactory;
25class SDLVibrationFactory;
24class SDLJoystick; 26class SDLJoystick;
25 27
26class SDLState : public State { 28class SDLState : public State {
@@ -71,6 +73,8 @@ private:
71 73
72 std::shared_ptr<SDLButtonFactory> button_factory; 74 std::shared_ptr<SDLButtonFactory> button_factory;
73 std::shared_ptr<SDLAnalogFactory> analog_factory; 75 std::shared_ptr<SDLAnalogFactory> analog_factory;
76 std::shared_ptr<SDLVibrationFactory> vibration_factory;
77 std::shared_ptr<SDLMotionFactory> motion_factory;
74 78
75 bool start_thread = false; 79 bool start_thread = false;
76 std::atomic<bool> initialized = false; 80 std::atomic<bool> initialized = false;
diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp
index b66c05856..557e7a9a0 100644
--- a/src/input_common/settings.cpp
+++ b/src/input_common/settings.cpp
@@ -14,13 +14,6 @@ const std::array<const char*, NumButtons> mapping = {{
14}}; 14}};
15} 15}
16 16
17namespace NativeMotion {
18const std::array<const char*, NumMotions> mapping = {{
19 "motionleft",
20 "motionright",
21}};
22}
23
24namespace NativeAnalog { 17namespace NativeAnalog {
25const std::array<const char*, NumAnalogs> mapping = {{ 18const std::array<const char*, NumAnalogs> mapping = {{
26 "lstick", 19 "lstick",
@@ -28,6 +21,20 @@ const std::array<const char*, NumAnalogs> mapping = {{
28}}; 21}};
29} 22}
30 23
24namespace NativeVibration {
25const std::array<const char*, NumVibrations> mapping = {{
26 "left_vibration_device",
27 "right_vibration_device",
28}};
29}
30
31namespace NativeMotion {
32const std::array<const char*, NumMotions> mapping = {{
33 "motionleft",
34 "motionright",
35}};
36}
37
31namespace NativeMouseButton { 38namespace NativeMouseButton {
32const std::array<const char*, NumMouseButtons> mapping = {{ 39const std::array<const char*, NumMouseButtons> mapping = {{
33 "left", 40 "left",
diff --git a/src/input_common/settings.h b/src/input_common/settings.h
index ab0b95cf1..75486554b 100644
--- a/src/input_common/settings.h
+++ b/src/input_common/settings.h
@@ -66,17 +66,32 @@ constexpr int NUM_STICKS_HID = NumAnalogs;
66extern const std::array<const char*, NumAnalogs> mapping; 66extern const std::array<const char*, NumAnalogs> mapping;
67} // namespace NativeAnalog 67} // namespace NativeAnalog
68 68
69namespace NativeVibration {
70enum Values : int {
71 LeftVibrationDevice,
72 RightVibrationDevice,
73
74 NumVibrations,
75};
76
77constexpr int VIBRATION_HID_BEGIN = LeftVibrationDevice;
78constexpr int VIBRATION_HID_END = NumVibrations;
79constexpr int NUM_VIBRATIONS_HID = NumVibrations;
80
81extern const std::array<const char*, NumVibrations> mapping;
82}; // namespace NativeVibration
83
69namespace NativeMotion { 84namespace NativeMotion {
70enum Values : int { 85enum Values : int {
71 MOTIONLEFT, 86 MotionLeft,
72 MOTIONRIGHT, 87 MotionRight,
73 88
74 NumMotions, 89 NumMotions,
75}; 90};
76 91
77constexpr int MOTION_HID_BEGIN = MOTIONLEFT; 92constexpr int MOTION_HID_BEGIN = MotionLeft;
78constexpr int MOTION_HID_END = NumMotions; 93constexpr int MOTION_HID_END = NumMotions;
79constexpr int NUM_MOTION_HID = NumMotions; 94constexpr int NUM_MOTIONS_HID = NumMotions;
80 95
81extern const std::array<const char*, NumMotions> mapping; 96extern const std::array<const char*, NumMotions> mapping;
82} // namespace NativeMotion 97} // namespace NativeMotion
@@ -305,9 +320,11 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
305 320
306} // namespace NativeKeyboard 321} // namespace NativeKeyboard
307 322
308using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
309using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; 323using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
310using MotionRaw = std::array<std::string, NativeMotion::NumMotions>; 324using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
325using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
326using VibrationsRaw = std::array<std::string, NativeVibration::NumVibrations>;
327
311using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>; 328using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
312using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>; 329using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
313using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>; 330using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
@@ -330,9 +347,11 @@ struct PlayerInput {
330 ControllerType controller_type; 347 ControllerType controller_type;
331 ButtonsRaw buttons; 348 ButtonsRaw buttons;
332 AnalogsRaw analogs; 349 AnalogsRaw analogs;
333 MotionRaw motions; 350 VibrationsRaw vibrations;
334 std::string lstick_mod; 351 MotionsRaw motions;
335 std::string rstick_mod; 352
353 bool vibration_enabled;
354 int vibration_strength;
336 355
337 u32 body_color_left; 356 u32 body_color_left;
338 u32 body_color_right; 357 u32 body_color_right;
diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp
index 98da0ef1a..a07124a86 100644
--- a/src/input_common/touch_from_button.cpp
+++ b/src/input_common/touch_from_button.cpp
@@ -11,9 +11,11 @@ namespace InputCommon {
11class TouchFromButtonDevice final : public Input::TouchDevice { 11class TouchFromButtonDevice final : public Input::TouchDevice {
12public: 12public:
13 TouchFromButtonDevice() { 13 TouchFromButtonDevice() {
14 for (const auto& config_entry : 14 const auto button_index =
15 Settings::values.touch_from_button_maps[Settings::values.touch_from_button_map_index] 15 static_cast<std::size_t>(Settings::values.touch_from_button_map_index);
16 .buttons) { 16 const auto& buttons = Settings::values.touch_from_button_maps[button_index].buttons;
17
18 for (const auto& config_entry : buttons) {
17 const Common::ParamPackage package{config_entry}; 19 const Common::ParamPackage package{config_entry};
18 map.emplace_back( 20 map.emplace_back(
19 Input::CreateDevice<Input::ButtonDevice>(config_entry), 21 Input::CreateDevice<Input::ButtonDevice>(config_entry),
@@ -42,8 +44,7 @@ private:
42 std::vector<std::tuple<std::unique_ptr<Input::ButtonDevice>, int, int>> map; 44 std::vector<std::tuple<std::unique_ptr<Input::ButtonDevice>, int, int>> map;
43}; 45};
44 46
45std::unique_ptr<Input::TouchDevice> TouchFromButtonFactory::Create( 47std::unique_ptr<Input::TouchDevice> TouchFromButtonFactory::Create(const Common::ParamPackage&) {
46 const Common::ParamPackage& params) {
47 return std::make_unique<TouchFromButtonDevice>(); 48 return std::make_unique<TouchFromButtonDevice>();
48} 49}
49 50
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index 2b6a68d4b..412d57896 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -26,11 +26,11 @@ class Socket {
26public: 26public:
27 using clock = std::chrono::system_clock; 27 using clock = std::chrono::system_clock;
28 28
29 explicit Socket(const std::string& host, u16 port, u8 pad_index, u32 client_id, 29 explicit Socket(const std::string& host, u16 port, std::size_t pad_index_, u32 client_id_,
30 SocketCallback callback) 30 SocketCallback callback_)
31 : callback(std::move(callback)), timer(io_service), 31 : callback(std::move(callback_)), timer(io_service),
32 socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id), 32 socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id_),
33 pad_index(pad_index) { 33 pad_index(pad_index_) {
34 boost::system::error_code ec{}; 34 boost::system::error_code ec{};
35 auto ipv4 = boost::asio::ip::make_address_v4(host, ec); 35 auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
36 if (ec.value() != boost::system::errc::success) { 36 if (ec.value() != boost::system::errc::success) {
@@ -63,7 +63,7 @@ public:
63 } 63 }
64 64
65private: 65private:
66 void HandleReceive(const boost::system::error_code& error, std::size_t bytes_transferred) { 66 void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) {
67 if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) { 67 if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) {
68 switch (*type) { 68 switch (*type) {
69 case Type::Version: { 69 case Type::Version: {
@@ -90,16 +90,20 @@ private:
90 StartReceive(); 90 StartReceive();
91 } 91 }
92 92
93 void HandleSend(const boost::system::error_code& error) { 93 void HandleSend(const boost::system::error_code&) {
94 boost::system::error_code _ignored{}; 94 boost::system::error_code _ignored{};
95 // Send a request for getting port info for the pad 95 // Send a request for getting port info for the pad
96 Request::PortInfo port_info{1, {pad_index, 0, 0, 0}}; 96 const Request::PortInfo port_info{1, {static_cast<u8>(pad_index), 0, 0, 0}};
97 const auto port_message = Request::Create(port_info, client_id); 97 const auto port_message = Request::Create(port_info, client_id);
98 std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE); 98 std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE);
99 socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored); 99 socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored);
100 100
101 // Send a request for getting pad data for the pad 101 // Send a request for getting pad data for the pad
102 Request::PadData pad_data{Request::PadData::Flags::Id, pad_index, EMPTY_MAC_ADDRESS}; 102 const Request::PadData pad_data{
103 Request::PadData::Flags::Id,
104 static_cast<u8>(pad_index),
105 EMPTY_MAC_ADDRESS,
106 };
103 const auto pad_message = Request::Create(pad_data, client_id); 107 const auto pad_message = Request::Create(pad_data, client_id);
104 std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE); 108 std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE);
105 socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored); 109 socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored);
@@ -112,7 +116,7 @@ private:
112 udp::socket socket; 116 udp::socket socket;
113 117
114 u32 client_id{}; 118 u32 client_id{};
115 u8 pad_index{}; 119 std::size_t pad_index{};
116 120
117 static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>); 121 static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>);
118 static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>); 122 static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>);
@@ -132,15 +136,7 @@ static void SocketLoop(Socket* socket) {
132 136
133Client::Client() { 137Client::Client() {
134 LOG_INFO(Input, "Udp Initialization started"); 138 LOG_INFO(Input, "Udp Initialization started");
135 for (std::size_t client = 0; client < clients.size(); client++) { 139 ReloadSockets();
136 u8 pad = client % 4;
137 StartCommunication(client, Settings::values.udp_input_address,
138 Settings::values.udp_input_port, pad, 24872);
139 // Set motion parameters
140 // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
141 // Real HW values are unknown, 0.0001 is an approximate to Standard
142 clients[client].motion.SetGyroThreshold(0.0001f);
143 }
144} 140}
145 141
146Client::~Client() { 142Client::~Client() {
@@ -163,39 +159,77 @@ std::vector<Common::ParamPackage> Client::GetInputDevices() const {
163 return devices; 159 return devices;
164} 160}
165 161
166bool Client::DeviceConnected(std::size_t pad) const { 162bool Client::DeviceConnected(std::size_t client) const {
167 // Use last timestamp to detect if the socket has stopped sending data 163 // Use last timestamp to detect if the socket has stopped sending data
168 const auto now = std::chrono::system_clock::now(); 164 const auto now = std::chrono::steady_clock::now();
169 u64 time_difference = 165 const auto time_difference =
170 std::chrono::duration_cast<std::chrono::milliseconds>(now - clients[pad].last_motion_update) 166 static_cast<u64>(std::chrono::duration_cast<std::chrono::milliseconds>(
171 .count(); 167 now - clients[client].last_motion_update)
172 return time_difference < 1000 && clients[pad].active == 1; 168 .count());
169 return time_difference < 1000 && clients[client].active == 1;
173} 170}
174 171
175void Client::ReloadUDPClient() { 172void Client::ReloadSockets() {
176 for (std::size_t client = 0; client < clients.size(); client++) { 173 Reset();
177 ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, client); 174
175 std::stringstream servers_ss(Settings::values.udp_input_servers);
176 std::string server_token;
177 std::size_t client = 0;
178 while (std::getline(servers_ss, server_token, ',')) {
179 if (client == max_udp_clients) {
180 break;
181 }
182 std::stringstream server_ss(server_token);
183 std::string token;
184 std::getline(server_ss, token, ':');
185 std::string udp_input_address = token;
186 std::getline(server_ss, token, ':');
187 char* temp;
188 const u16 udp_input_port = static_cast<u16>(std::strtol(token.c_str(), &temp, 0));
189 if (*temp != '\0') {
190 LOG_ERROR(Input, "Port number is not valid {}", token);
191 continue;
192 }
193
194 for (std::size_t pad = 0; pad < 4; ++pad) {
195 const std::size_t client_number =
196 GetClientNumber(udp_input_address, udp_input_port, pad);
197 if (client_number != max_udp_clients) {
198 LOG_ERROR(Input, "Duplicated UDP servers found");
199 continue;
200 }
201 StartCommunication(client++, udp_input_address, udp_input_port, pad, 24872);
202 }
178 } 203 }
179} 204}
180void Client::ReloadSocket(const std::string& host, u16 port, u8 pad_index, u32 client_id) { 205
181 // client number must be determined from host / port and pad index 206std::size_t Client::GetClientNumber(std::string_view host, u16 port, std::size_t pad) const {
182 std::size_t client = pad_index; 207 for (std::size_t client = 0; client < clients.size(); client++) {
183 clients[client].socket->Stop(); 208 if (clients[client].active == -1) {
184 clients[client].thread.join(); 209 continue;
185 StartCommunication(client, host, port, pad_index, client_id); 210 }
211 if (clients[client].host == host && clients[client].port == port &&
212 clients[client].pad_index == pad) {
213 return client;
214 }
215 }
216 return max_udp_clients;
186} 217}
187 218
188void Client::OnVersion(Response::Version data) { 219void Client::OnVersion([[maybe_unused]] Response::Version data) {
189 LOG_TRACE(Input, "Version packet received: {}", data.version); 220 LOG_TRACE(Input, "Version packet received: {}", data.version);
190} 221}
191 222
192void Client::OnPortInfo(Response::PortInfo data) { 223void Client::OnPortInfo([[maybe_unused]] Response::PortInfo data) {
193 LOG_TRACE(Input, "PortInfo packet received: {}", data.model); 224 LOG_TRACE(Input, "PortInfo packet received: {}", data.model);
194} 225}
195 226
196void Client::OnPadData(Response::PadData data) { 227void Client::OnPadData(Response::PadData data, std::size_t client) {
197 // client number must be determined from host / port and pad index 228 // Accept packets only for the correct pad
198 std::size_t client = data.info.id; 229 if (static_cast<u8>(clients[client].pad_index) != data.info.id) {
230 return;
231 }
232
199 LOG_TRACE(Input, "PadData packet received"); 233 LOG_TRACE(Input, "PadData packet received");
200 if (data.packet_counter == clients[client].packet_sequence) { 234 if (data.packet_counter == clients[client].packet_sequence) {
201 LOG_WARNING( 235 LOG_WARNING(
@@ -204,14 +238,15 @@ void Client::OnPadData(Response::PadData data) {
204 clients[client].packet_sequence, data.packet_counter); 238 clients[client].packet_sequence, data.packet_counter);
205 return; 239 return;
206 } 240 }
207 clients[client].active = data.info.is_pad_active; 241 clients[client].active = static_cast<s8>(data.info.is_pad_active);
208 clients[client].packet_sequence = data.packet_counter; 242 clients[client].packet_sequence = data.packet_counter;
209 const auto now = std::chrono::system_clock::now(); 243 const auto now = std::chrono::steady_clock::now();
210 u64 time_difference = std::chrono::duration_cast<std::chrono::microseconds>( 244 const auto time_difference =
211 now - clients[client].last_motion_update) 245 static_cast<u64>(std::chrono::duration_cast<std::chrono::microseconds>(
212 .count(); 246 now - clients[client].last_motion_update)
247 .count());
213 clients[client].last_motion_update = now; 248 clients[client].last_motion_update = now;
214 Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw}; 249 const Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw};
215 clients[client].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y}); 250 clients[client].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y});
216 // Gyroscope values are not it the correct scale from better joy. 251 // Gyroscope values are not it the correct scale from better joy.
217 // Dividing by 312 allows us to make one full turn = 1 turn 252 // Dividing by 312 allows us to make one full turn = 1 turn
@@ -219,14 +254,10 @@ void Client::OnPadData(Response::PadData data) {
219 clients[client].motion.SetGyroscope(raw_gyroscope / 312.0f); 254 clients[client].motion.SetGyroscope(raw_gyroscope / 312.0f);
220 clients[client].motion.UpdateRotation(time_difference); 255 clients[client].motion.UpdateRotation(time_difference);
221 clients[client].motion.UpdateOrientation(time_difference); 256 clients[client].motion.UpdateOrientation(time_difference);
222 Common::Vec3f gyroscope = clients[client].motion.GetGyroscope();
223 Common::Vec3f accelerometer = clients[client].motion.GetAcceleration();
224 Common::Vec3f rotation = clients[client].motion.GetRotations();
225 std::array<Common::Vec3f, 3> orientation = clients[client].motion.GetOrientation();
226 257
227 { 258 {
228 std::lock_guard guard(clients[client].status.update_mutex); 259 std::lock_guard guard(clients[client].status.update_mutex);
229 clients[client].status.motion_status = {accelerometer, gyroscope, rotation, orientation}; 260 clients[client].status.motion_status = clients[client].motion.GetMotion();
230 261
231 // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates 262 // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates
232 // between a simple "tap" and a hard press that causes the touch screen to click. 263 // between a simple "tap" and a hard press that causes the touch screen to click.
@@ -241,98 +272,129 @@ void Client::OnPadData(Response::PadData data) {
241 const u16 min_y = clients[client].status.touch_calibration->min_y; 272 const u16 min_y = clients[client].status.touch_calibration->min_y;
242 const u16 max_y = clients[client].status.touch_calibration->max_y; 273 const u16 max_y = clients[client].status.touch_calibration->max_y;
243 274
244 x = (std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - min_x) / 275 x = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) -
276 min_x) /
245 static_cast<float>(max_x - min_x); 277 static_cast<float>(max_x - min_x);
246 y = (std::clamp(static_cast<u16>(data.touch_1.y), min_y, max_y) - min_y) / 278 y = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.y), min_y, max_y) -
279 min_y) /
247 static_cast<float>(max_y - min_y); 280 static_cast<float>(max_y - min_y);
248 } 281 }
249 282
250 clients[client].status.touch_status = {x, y, is_active}; 283 clients[client].status.touch_status = {x, y, is_active};
251 284
252 if (configuring) { 285 if (configuring) {
286 const Common::Vec3f gyroscope = clients[client].motion.GetGyroscope();
287 const Common::Vec3f accelerometer = clients[client].motion.GetAcceleration();
253 UpdateYuzuSettings(client, accelerometer, gyroscope, is_active); 288 UpdateYuzuSettings(client, accelerometer, gyroscope, is_active);
254 } 289 }
255 } 290 }
256} 291}
257 292
258void Client::StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index, 293void Client::StartCommunication(std::size_t client, const std::string& host, u16 port,
259 u32 client_id) { 294 std::size_t pad_index, u32 client_id) {
260 SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, 295 SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
261 [this](Response::PortInfo info) { OnPortInfo(info); }, 296 [this](Response::PortInfo info) { OnPortInfo(info); },
262 [this](Response::PadData data) { OnPadData(data); }}; 297 [this, client](Response::PadData data) { OnPadData(data, client); }};
263 LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); 298 LOG_INFO(Input, "Starting communication with UDP input server on {}:{}:{}", host, port,
299 pad_index);
300 clients[client].host = host;
301 clients[client].port = port;
302 clients[client].pad_index = pad_index;
303 clients[client].active = 0;
264 clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback); 304 clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback);
265 clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()}; 305 clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
306 // Set motion parameters
307 // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
308 // Real HW values are unknown, 0.0001 is an approximate to Standard
309 clients[client].motion.SetGyroThreshold(0.0001f);
266} 310}
267 311
268void Client::Reset() { 312void Client::Reset() {
269 for (std::size_t client = 0; client < clients.size(); client++) { 313 for (auto& client : clients) {
270 clients[client].socket->Stop(); 314 if (client.thread.joinable()) {
271 clients[client].thread.join(); 315 client.active = -1;
316 client.socket->Stop();
317 client.thread.join();
318 }
272 } 319 }
273} 320}
274 321
275void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, 322void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
276 const Common::Vec3<float>& gyro, bool touch) { 323 const Common::Vec3<float>& gyro, bool touch) {
277 UDPPadStatus pad; 324 if (gyro.Length() > 0.2f) {
325 LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {}), touch={}",
326 client, gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2], touch);
327 }
328 UDPPadStatus pad{
329 .host = clients[client].host,
330 .port = clients[client].port,
331 .pad_index = clients[client].pad_index,
332 };
278 if (touch) { 333 if (touch) {
279 pad.touch = PadTouch::Click; 334 pad.touch = PadTouch::Click;
280 pad_queue[client].Push(pad); 335 pad_queue.Push(pad);
281 } 336 }
282 for (size_t i = 0; i < 3; ++i) { 337 for (size_t i = 0; i < 3; ++i) {
283 if (gyro[i] > 6.0f || gyro[i] < -6.0f) { 338 if (gyro[i] > 5.0f || gyro[i] < -5.0f) {
284 pad.motion = static_cast<PadMotion>(i); 339 pad.motion = static_cast<PadMotion>(i);
285 pad.motion_value = gyro[i]; 340 pad.motion_value = gyro[i];
286 pad_queue[client].Push(pad); 341 pad_queue.Push(pad);
287 } 342 }
288 if (acc[i] > 2.0f || acc[i] < -2.0f) { 343 if (acc[i] > 1.75f || acc[i] < -1.75f) {
289 pad.motion = static_cast<PadMotion>(i + 3); 344 pad.motion = static_cast<PadMotion>(i + 3);
290 pad.motion_value = acc[i]; 345 pad.motion_value = acc[i];
291 pad_queue[client].Push(pad); 346 pad_queue.Push(pad);
292 } 347 }
293 } 348 }
294} 349}
295 350
296void Client::BeginConfiguration() { 351void Client::BeginConfiguration() {
297 for (auto& pq : pad_queue) { 352 pad_queue.Clear();
298 pq.Clear();
299 }
300 configuring = true; 353 configuring = true;
301} 354}
302 355
303void Client::EndConfiguration() { 356void Client::EndConfiguration() {
304 for (auto& pq : pad_queue) { 357 pad_queue.Clear();
305 pq.Clear();
306 }
307 configuring = false; 358 configuring = false;
308} 359}
309 360
310DeviceStatus& Client::GetPadState(std::size_t pad) { 361DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) {
311 return clients[pad].status; 362 const std::size_t client_number = GetClientNumber(host, port, pad);
363 if (client_number == max_udp_clients) {
364 return clients[0].status;
365 }
366 return clients[client_number].status;
312} 367}
313 368
314const DeviceStatus& Client::GetPadState(std::size_t pad) const { 369const DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) const {
315 return clients[pad].status; 370 const std::size_t client_number = GetClientNumber(host, port, pad);
371 if (client_number == max_udp_clients) {
372 return clients[0].status;
373 }
374 return clients[client_number].status;
316} 375}
317 376
318std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() { 377Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() {
319 return pad_queue; 378 return pad_queue;
320} 379}
321 380
322const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() const { 381const Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() const {
323 return pad_queue; 382 return pad_queue;
324} 383}
325 384
326void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, 385void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
327 std::function<void()> success_callback, 386 const std::function<void()>& success_callback,
328 std::function<void()> failure_callback) { 387 const std::function<void()>& failure_callback) {
329 std::thread([=] { 388 std::thread([=] {
330 Common::Event success_event; 389 Common::Event success_event;
331 SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, 390 SocketCallback callback{
332 [&](Response::PadData data) { success_event.Set(); }}; 391 .version = [](Response::Version) {},
392 .port_info = [](Response::PortInfo) {},
393 .pad_data = [&](Response::PadData) { success_event.Set(); },
394 };
333 Socket socket{host, port, pad_index, client_id, std::move(callback)}; 395 Socket socket{host, port, pad_index, client_id, std::move(callback)};
334 std::thread worker_thread{SocketLoop, &socket}; 396 std::thread worker_thread{SocketLoop, &socket};
335 bool result = success_event.WaitFor(std::chrono::seconds(8)); 397 const bool result = success_event.WaitFor(std::chrono::seconds(5));
336 socket.Stop(); 398 socket.Stop();
337 worker_thread.join(); 399 worker_thread.join();
338 if (result) { 400 if (result) {
@@ -344,7 +406,7 @@ void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 clie
344} 406}
345 407
346CalibrationConfigurationJob::CalibrationConfigurationJob( 408CalibrationConfigurationJob::CalibrationConfigurationJob(
347 const std::string& host, u16 port, u8 pad_index, u32 client_id, 409 const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
348 std::function<void(Status)> status_callback, 410 std::function<void(Status)> status_callback,
349 std::function<void(u16, u16, u16, u16)> data_callback) { 411 std::function<void(u16, u16, u16, u16)> data_callback) {
350 412
@@ -357,14 +419,14 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
357 u16 max_y{}; 419 u16 max_y{};
358 420
359 Status current_status{Status::Initialized}; 421 Status current_status{Status::Initialized};
360 SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, 422 SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {},
361 [&](Response::PadData data) { 423 [&](Response::PadData data) {
362 if (current_status == Status::Initialized) { 424 if (current_status == Status::Initialized) {
363 // Receiving data means the communication is ready now 425 // Receiving data means the communication is ready now
364 current_status = Status::Ready; 426 current_status = Status::Ready;
365 status_callback(current_status); 427 status_callback(current_status);
366 } 428 }
367 if (!data.touch_1.is_active) { 429 if (data.touch_1.is_active == 0) {
368 return; 430 return;
369 } 431 }
370 LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x, 432 LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x,
diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h
index 523dc6a7a..00c8b09f5 100644
--- a/src/input_common/udp/client.h
+++ b/src/input_common/udp/client.h
@@ -21,8 +21,7 @@
21 21
22namespace InputCommon::CemuhookUDP { 22namespace InputCommon::CemuhookUDP {
23 23
24constexpr u16 DEFAULT_PORT = 26760; 24constexpr char DEFAULT_SRV[] = "127.0.0.1:26760";
25constexpr char DEFAULT_ADDR[] = "127.0.0.1";
26 25
27class Socket; 26class Socket;
28 27
@@ -48,6 +47,9 @@ enum class PadTouch {
48}; 47};
49 48
50struct UDPPadStatus { 49struct UDPPadStatus {
50 std::string host{"127.0.0.1"};
51 u16 port{26760};
52 std::size_t pad_index{};
51 PadTouch touch{PadTouch::Undefined}; 53 PadTouch touch{PadTouch::Undefined};
52 PadMotion motion{PadMotion::Undefined}; 54 PadMotion motion{PadMotion::Undefined};
53 f32 motion_value{0.0f}; 55 f32 motion_value{0.0f};
@@ -82,46 +84,52 @@ public:
82 84
83 std::vector<Common::ParamPackage> GetInputDevices() const; 85 std::vector<Common::ParamPackage> GetInputDevices() const;
84 86
85 bool DeviceConnected(std::size_t pad) const; 87 bool DeviceConnected(std::size_t client) const;
86 void ReloadUDPClient(); 88 void ReloadSockets();
87 void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, u8 pad_index = 0,
88 u32 client_id = 24872);
89 89
90 std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue(); 90 Common::SPSCQueue<UDPPadStatus>& GetPadQueue();
91 const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue() const; 91 const Common::SPSCQueue<UDPPadStatus>& GetPadQueue() const;
92 92
93 DeviceStatus& GetPadState(std::size_t pad); 93 DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad);
94 const DeviceStatus& GetPadState(std::size_t pad) const; 94 const DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad) const;
95 95
96private: 96private:
97 struct ClientData { 97 struct ClientData {
98 std::string host{"127.0.0.1"};
99 u16 port{26760};
100 std::size_t pad_index{};
98 std::unique_ptr<Socket> socket; 101 std::unique_ptr<Socket> socket;
99 DeviceStatus status; 102 DeviceStatus status;
100 std::thread thread; 103 std::thread thread;
101 u64 packet_sequence = 0; 104 u64 packet_sequence{};
102 u8 active; 105 s8 active{-1};
103 106
104 // Realtime values 107 // Realtime values
105 // motion is initalized with PID values for drift correction on joycons 108 // motion is initalized with PID values for drift correction on joycons
106 InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f}; 109 InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f};
107 std::chrono::time_point<std::chrono::system_clock> last_motion_update; 110 std::chrono::time_point<std::chrono::steady_clock> last_motion_update;
108 }; 111 };
109 112
110 // For shutting down, clear all data, join all threads, release usb 113 // For shutting down, clear all data, join all threads, release usb
111 void Reset(); 114 void Reset();
112 115
116 // Translates configuration to client number
117 std::size_t GetClientNumber(std::string_view host, u16 port, std::size_t pad) const;
118
113 void OnVersion(Response::Version); 119 void OnVersion(Response::Version);
114 void OnPortInfo(Response::PortInfo); 120 void OnPortInfo(Response::PortInfo);
115 void OnPadData(Response::PadData); 121 void OnPadData(Response::PadData, std::size_t client);
116 void StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index, 122 void StartCommunication(std::size_t client, const std::string& host, u16 port,
117 u32 client_id); 123 std::size_t pad_index, u32 client_id);
118 void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, 124 void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
119 const Common::Vec3<float>& gyro, bool touch); 125 const Common::Vec3<float>& gyro, bool touch);
120 126
121 bool configuring = false; 127 bool configuring = false;
122 128
123 std::array<ClientData, 4> clients; 129 // Allocate clients for 8 udp servers
124 std::array<Common::SPSCQueue<UDPPadStatus>, 4> pad_queue; 130 const std::size_t max_udp_clients = 32;
131 std::array<ClientData, 4 * 8> clients;
132 Common::SPSCQueue<UDPPadStatus> pad_queue;
125}; 133};
126 134
127/// An async job allowing configuration of the touchpad calibration. 135/// An async job allowing configuration of the touchpad calibration.
@@ -139,7 +147,7 @@ public:
139 * @param status_callback Callback for job status updates 147 * @param status_callback Callback for job status updates
140 * @param data_callback Called when calibration data is ready 148 * @param data_callback Called when calibration data is ready
141 */ 149 */
142 explicit CalibrationConfigurationJob(const std::string& host, u16 port, u8 pad_index, 150 explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::size_t pad_index,
143 u32 client_id, std::function<void(Status)> status_callback, 151 u32 client_id, std::function<void(Status)> status_callback,
144 std::function<void(u16, u16, u16, u16)> data_callback); 152 std::function<void(u16, u16, u16, u16)> data_callback);
145 ~CalibrationConfigurationJob(); 153 ~CalibrationConfigurationJob();
@@ -149,8 +157,8 @@ private:
149 Common::Event complete_event; 157 Common::Event complete_event;
150}; 158};
151 159
152void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, 160void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
153 std::function<void()> success_callback, 161 const std::function<void()>& success_callback,
154 std::function<void()> failure_callback); 162 const std::function<void()>& failure_callback);
155 163
156} // namespace InputCommon::CemuhookUDP 164} // namespace InputCommon::CemuhookUDP
diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h
index 3ba4d1fc8..fc1aea4b9 100644
--- a/src/input_common/udp/protocol.h
+++ b/src/input_common/udp/protocol.h
@@ -7,7 +7,16 @@
7#include <array> 7#include <array>
8#include <optional> 8#include <optional>
9#include <type_traits> 9#include <type_traits>
10
11#ifdef _MSC_VER
12#pragma warning(push)
13#pragma warning(disable : 4701)
14#endif
10#include <boost/crc.hpp> 15#include <boost/crc.hpp>
16#ifdef _MSC_VER
17#pragma warning(pop)
18#endif
19
11#include "common/bit_field.h" 20#include "common/bit_field.h"
12#include "common/swap.h" 21#include "common/swap.h"
13 22
@@ -93,7 +102,7 @@ static_assert(std::is_trivially_copyable_v<PadData>,
93 102
94/** 103/**
95 * Creates a message with the proper header data that can be sent to the server. 104 * Creates a message with the proper header data that can be sent to the server.
96 * @param T data Request body to send 105 * @param data Request body to send
97 * @param client_id ID of the udp client (usually not checked on the server) 106 * @param client_id ID of the udp client (usually not checked on the server)
98 */ 107 */
99template <typename T> 108template <typename T>
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
index eba077a36..c5da27a38 100644
--- a/src/input_common/udp/udp.cpp
+++ b/src/input_common/udp/udp.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <atomic>
6#include <list>
7#include <mutex> 5#include <mutex>
8#include <utility> 6#include <utility>
9#include "common/assert.h" 7#include "common/assert.h"
@@ -15,36 +13,36 @@ namespace InputCommon {
15 13
16class UDPMotion final : public Input::MotionDevice { 14class UDPMotion final : public Input::MotionDevice {
17public: 15public:
18 UDPMotion(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_) 16 explicit UDPMotion(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_)
19 : ip(ip_), port(port_), pad(pad_), client(client_) {} 17 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
20 18
21 Input::MotionStatus GetStatus() const override { 19 Input::MotionStatus GetStatus() const override {
22 return client->GetPadState(pad).motion_status; 20 return client->GetPadState(ip, port, pad).motion_status;
23 } 21 }
24 22
25private: 23private:
26 const std::string ip; 24 const std::string ip;
27 const int port; 25 const u16 port;
28 const int pad; 26 const u16 pad;
29 CemuhookUDP::Client* client; 27 CemuhookUDP::Client* client;
30 mutable std::mutex mutex; 28 mutable std::mutex mutex;
31}; 29};
32 30
33/// A motion device factory that creates motion devices from JC Adapter 31/// A motion device factory that creates motion devices from a UDP client
34UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_) 32UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_)
35 : client(std::move(client_)) {} 33 : client(std::move(client_)) {}
36 34
37/** 35/**
38 * Creates motion device 36 * Creates motion device
39 * @param params contains parameters for creating the device: 37 * @param params contains parameters for creating the device:
40 * - "port": the nth jcpad on the adapter 38 * - "port": the UDP port number
41 */ 39 */
42std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) { 40std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) {
43 const std::string ip = params.Get("ip", "127.0.0.1"); 41 auto ip = params.Get("ip", "127.0.0.1");
44 const int port = params.Get("port", 26760); 42 const auto port = static_cast<u16>(params.Get("port", 26760));
45 const int pad = params.Get("pad_index", 0); 43 const auto pad = static_cast<u16>(params.Get("pad_index", 0));
46 44
47 return std::make_unique<UDPMotion>(ip, port, pad, client.get()); 45 return std::make_unique<UDPMotion>(std::move(ip), port, pad, client.get());
48} 46}
49 47
50void UDPMotionFactory::BeginConfiguration() { 48void UDPMotionFactory::BeginConfiguration() {
@@ -61,54 +59,52 @@ Common::ParamPackage UDPMotionFactory::GetNextInput() {
61 Common::ParamPackage params; 59 Common::ParamPackage params;
62 CemuhookUDP::UDPPadStatus pad; 60 CemuhookUDP::UDPPadStatus pad;
63 auto& queue = client->GetPadQueue(); 61 auto& queue = client->GetPadQueue();
64 for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) { 62 while (queue.Pop(pad)) {
65 while (queue[pad_number].Pop(pad)) { 63 if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) {
66 if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) { 64 continue;
67 continue;
68 }
69 params.Set("engine", "cemuhookudp");
70 params.Set("ip", "127.0.0.1");
71 params.Set("port", 26760);
72 params.Set("pad_index", static_cast<int>(pad_number));
73 params.Set("motion", static_cast<u16>(pad.motion));
74 return params;
75 } 65 }
66 params.Set("engine", "cemuhookudp");
67 params.Set("ip", pad.host);
68 params.Set("port", static_cast<u16>(pad.port));
69 params.Set("pad_index", static_cast<u16>(pad.pad_index));
70 params.Set("motion", static_cast<u16>(pad.motion));
71 return params;
76 } 72 }
77 return params; 73 return params;
78} 74}
79 75
80class UDPTouch final : public Input::TouchDevice { 76class UDPTouch final : public Input::TouchDevice {
81public: 77public:
82 UDPTouch(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_) 78 explicit UDPTouch(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_)
83 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} 79 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
84 80
85 std::tuple<float, float, bool> GetStatus() const override { 81 std::tuple<float, float, bool> GetStatus() const override {
86 return client->GetPadState(pad).touch_status; 82 return client->GetPadState(ip, port, pad).touch_status;
87 } 83 }
88 84
89private: 85private:
90 const std::string ip; 86 const std::string ip;
91 const int port; 87 const u16 port;
92 const int pad; 88 const u16 pad;
93 CemuhookUDP::Client* client; 89 CemuhookUDP::Client* client;
94 mutable std::mutex mutex; 90 mutable std::mutex mutex;
95}; 91};
96 92
97/// A motion device factory that creates motion devices from JC Adapter 93/// A motion device factory that creates motion devices from a UDP client
98UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_) 94UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_)
99 : client(std::move(client_)) {} 95 : client(std::move(client_)) {}
100 96
101/** 97/**
102 * Creates motion device 98 * Creates motion device
103 * @param params contains parameters for creating the device: 99 * @param params contains parameters for creating the device:
104 * - "port": the nth jcpad on the adapter 100 * - "port": the UDP port number
105 */ 101 */
106std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) { 102std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) {
107 const std::string ip = params.Get("ip", "127.0.0.1"); 103 auto ip = params.Get("ip", "127.0.0.1");
108 const int port = params.Get("port", 26760); 104 const auto port = static_cast<u16>(params.Get("port", 26760));
109 const int pad = params.Get("pad_index", 0); 105 const auto pad = static_cast<u16>(params.Get("pad_index", 0));
110 106
111 return std::make_unique<UDPTouch>(ip, port, pad, client.get()); 107 return std::make_unique<UDPTouch>(std::move(ip), port, pad, client.get());
112} 108}
113 109
114void UDPTouchFactory::BeginConfiguration() { 110void UDPTouchFactory::BeginConfiguration() {
@@ -125,18 +121,16 @@ Common::ParamPackage UDPTouchFactory::GetNextInput() {
125 Common::ParamPackage params; 121 Common::ParamPackage params;
126 CemuhookUDP::UDPPadStatus pad; 122 CemuhookUDP::UDPPadStatus pad;
127 auto& queue = client->GetPadQueue(); 123 auto& queue = client->GetPadQueue();
128 for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) { 124 while (queue.Pop(pad)) {
129 while (queue[pad_number].Pop(pad)) { 125 if (pad.touch == CemuhookUDP::PadTouch::Undefined) {
130 if (pad.touch == CemuhookUDP::PadTouch::Undefined) { 126 continue;
131 continue;
132 }
133 params.Set("engine", "cemuhookudp");
134 params.Set("ip", "127.0.0.1");
135 params.Set("port", 26760);
136 params.Set("pad_index", static_cast<int>(pad_number));
137 params.Set("touch", static_cast<u16>(pad.touch));
138 return params;
139 } 127 }
128 params.Set("engine", "cemuhookudp");
129 params.Set("ip", pad.host);
130 params.Set("port", static_cast<u16>(pad.port));
131 params.Set("pad_index", static_cast<u16>(pad.pad_index));
132 params.Set("touch", static_cast<u16>(pad.touch));
133 return params;
140 } 134 }
141 return params; 135 return params;
142} 136}