summaryrefslogtreecommitdiff
path: root/src/input_common/gcadapter/gc_adapter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common/gcadapter/gc_adapter.cpp')
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp290
1 files changed, 142 insertions, 148 deletions
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index 898a278a9..89c148aba 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -4,9 +4,20 @@
4 4
5#include <chrono> 5#include <chrono>
6#include <thread> 6#include <thread>
7
8#ifdef _MSC_VER
9#pragma warning(push)
10#pragma warning(disable : 4200) // nonstandard extension used : zero-sized array in struct/union
11#endif
7#include <libusb.h> 12#include <libusb.h>
13#ifdef _MSC_VER
14#pragma warning(pop)
15#endif
16
8#include "common/logging/log.h" 17#include "common/logging/log.h"
18#include "common/param_package.h"
9#include "input_common/gcadapter/gc_adapter.h" 19#include "input_common/gcadapter/gc_adapter.h"
20#include "input_common/settings.h"
10 21
11namespace GCAdapter { 22namespace GCAdapter {
12 23
@@ -24,12 +35,9 @@ Adapter::Adapter() {
24 } 35 }
25 LOG_INFO(Input, "GC Adapter Initialization started"); 36 LOG_INFO(Input, "GC Adapter Initialization started");
26 37
27 current_status = NO_ADAPTER_DETECTED;
28 get_origin.fill(true);
29
30 const int init_res = libusb_init(&libusb_ctx); 38 const int init_res = libusb_init(&libusb_ctx);
31 if (init_res == LIBUSB_SUCCESS) { 39 if (init_res == LIBUSB_SUCCESS) {
32 StartScanThread(); 40 Setup();
33 } else { 41 } else {
34 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res); 42 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
35 } 43 }
@@ -37,9 +45,9 @@ Adapter::Adapter() {
37 45
38GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) { 46GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) {
39 GCPadStatus pad = {}; 47 GCPadStatus pad = {};
48 const std::size_t offset = 1 + (9 * port);
40 49
41 ControllerTypes type = ControllerTypes(adapter_payload[1 + (9 * port)] >> 4); 50 adapter_controllers_status[port] = static_cast<ControllerTypes>(adapter_payload[offset] >> 4);
42 adapter_controllers_status[port] = type;
43 51
44 static constexpr std::array<PadButton, 8> b1_buttons{ 52 static constexpr std::array<PadButton, 8> b1_buttons{
45 PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X, 53 PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X,
@@ -54,14 +62,19 @@ GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& ad
54 PadButton::PAD_TRIGGER_L, 62 PadButton::PAD_TRIGGER_L,
55 }; 63 };
56 64
65 static constexpr std::array<PadAxes, 6> axes{
66 PadAxes::StickX, PadAxes::StickY, PadAxes::SubstickX,
67 PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight,
68 };
69
57 if (adapter_controllers_status[port] == ControllerTypes::None && !get_origin[port]) { 70 if (adapter_controllers_status[port] == ControllerTypes::None && !get_origin[port]) {
58 // Controller may have been disconnected, recalibrate if reconnected. 71 // Controller may have been disconnected, recalibrate if reconnected.
59 get_origin[port] = true; 72 get_origin[port] = true;
60 } 73 }
61 74
62 if (adapter_controllers_status[port] != ControllerTypes::None) { 75 if (adapter_controllers_status[port] != ControllerTypes::None) {
63 const u8 b1 = adapter_payload[1 + (9 * port) + 1]; 76 const u8 b1 = adapter_payload[offset + 1];
64 const u8 b2 = adapter_payload[1 + (9 * port) + 2]; 77 const u8 b2 = adapter_payload[offset + 2];
65 78
66 for (std::size_t i = 0; i < b1_buttons.size(); ++i) { 79 for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
67 if ((b1 & (1U << i)) != 0) { 80 if ((b1 & (1U << i)) != 0) {
@@ -74,21 +87,13 @@ GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& ad
74 pad.button |= static_cast<u16>(b2_buttons[j]); 87 pad.button |= static_cast<u16>(b2_buttons[j]);
75 } 88 }
76 } 89 }
77 90 for (PadAxes axis : axes) {
78 pad.stick_x = adapter_payload[1 + (9 * port) + 3]; 91 const std::size_t index = static_cast<std::size_t>(axis);
79 pad.stick_y = adapter_payload[1 + (9 * port) + 4]; 92 pad.axis_values[index] = adapter_payload[offset + 3 + index];
80 pad.substick_x = adapter_payload[1 + (9 * port) + 5]; 93 }
81 pad.substick_y = adapter_payload[1 + (9 * port) + 6];
82 pad.trigger_left = adapter_payload[1 + (9 * port) + 7];
83 pad.trigger_right = adapter_payload[1 + (9 * port) + 8];
84 94
85 if (get_origin[port]) { 95 if (get_origin[port]) {
86 origin_status[port].stick_x = pad.stick_x; 96 origin_status[port].axis_values = pad.axis_values;
87 origin_status[port].stick_y = pad.stick_y;
88 origin_status[port].substick_x = pad.substick_x;
89 origin_status[port].substick_y = pad.substick_y;
90 origin_status[port].trigger_left = pad.trigger_left;
91 origin_status[port].trigger_right = pad.trigger_right;
92 get_origin[port] = false; 97 get_origin[port] = false;
93 } 98 }
94 } 99 }
@@ -101,82 +106,47 @@ void Adapter::PadToState(const GCPadStatus& pad, GCState& state) {
101 state.buttons.insert_or_assign(button_value, pad.button & button_value); 106 state.buttons.insert_or_assign(button_value, pad.button & button_value);
102 } 107 }
103 108
104 state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickX), pad.stick_x); 109 for (size_t i = 0; i < pad.axis_values.size(); ++i) {
105 state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickY), pad.stick_y); 110 state.axes.insert_or_assign(static_cast<u8>(i), pad.axis_values[i]);
106 state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickX), pad.substick_x); 111 }
107 state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickY), pad.substick_y);
108 state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerLeft), pad.trigger_left);
109 state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerRight), pad.trigger_right);
110} 112}
111 113
112void Adapter::Read() { 114void Adapter::Read() {
113 LOG_DEBUG(Input, "GC Adapter Read() thread started"); 115 LOG_DEBUG(Input, "GC Adapter Read() thread started");
114 116
115 int payload_size_in, payload_size_copy; 117 int payload_size;
116 std::array<u8, 37> adapter_payload; 118 std::array<u8, 37> adapter_payload;
117 std::array<u8, 37> adapter_payload_copy;
118 std::array<GCPadStatus, 4> pads; 119 std::array<GCPadStatus, 4> pads;
119 120
120 while (adapter_thread_running) { 121 while (adapter_thread_running) {
121 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(), 122 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
122 sizeof(adapter_payload), &payload_size_in, 16); 123 sizeof(adapter_payload), &payload_size, 16);
123 payload_size_copy = 0;
124 // this mutex might be redundant?
125 {
126 std::lock_guard<std::mutex> lk(s_mutex);
127 std::copy(std::begin(adapter_payload), std::end(adapter_payload),
128 std::begin(adapter_payload_copy));
129 payload_size_copy = payload_size_in;
130 }
131 124
132 if (payload_size_copy != sizeof(adapter_payload_copy) || 125 if (payload_size != sizeof(adapter_payload) || adapter_payload[0] != LIBUSB_DT_HID) {
133 adapter_payload_copy[0] != LIBUSB_DT_HID) { 126 LOG_ERROR(Input,
134 LOG_ERROR(Input, "error reading payload (size: {}, type: {:02x})", payload_size_copy, 127 "Error reading payload (size: {}, type: {:02x}) Is the adapter connected?",
135 adapter_payload_copy[0]); 128 payload_size, adapter_payload[0]);
136 adapter_thread_running = false; // error reading from adapter, stop reading. 129 adapter_thread_running = false; // error reading from adapter, stop reading.
137 break; 130 break;
138 } 131 }
139 for (std::size_t port = 0; port < pads.size(); ++port) { 132 for (std::size_t port = 0; port < pads.size(); ++port) {
140 pads[port] = GetPadStatus(port, adapter_payload_copy); 133 pads[port] = GetPadStatus(port, adapter_payload);
141 if (DeviceConnected(port) && configuring) { 134 if (DeviceConnected(port) && configuring) {
142 if (pads[port].button != 0) { 135 if (pads[port].button != 0) {
143 pad_queue[port].Push(pads[port]); 136 pad_queue[port].Push(pads[port]);
144 } 137 }
145 138
146 // Accounting for a threshold here because of some controller variance 139 // Accounting for a threshold here to ensure an intentional press
147 if (pads[port].stick_x > origin_status[port].stick_x + pads[port].THRESHOLD || 140 for (size_t i = 0; i < pads[port].axis_values.size(); ++i) {
148 pads[port].stick_x < origin_status[port].stick_x - pads[port].THRESHOLD) { 141 const u8 value = pads[port].axis_values[i];
149 pads[port].axis = GCAdapter::PadAxes::StickX; 142 const u8 origin = origin_status[port].axis_values[i];
150 pads[port].axis_value = pads[port].stick_x; 143
151 pad_queue[port].Push(pads[port]); 144 if (value > origin + pads[port].THRESHOLD ||
152 } 145 value < origin - pads[port].THRESHOLD) {
153 if (pads[port].stick_y > origin_status[port].stick_y + pads[port].THRESHOLD || 146 pads[port].axis = static_cast<PadAxes>(i);
154 pads[port].stick_y < origin_status[port].stick_y - pads[port].THRESHOLD) { 147 pads[port].axis_value = pads[port].axis_values[i];
155 pads[port].axis = GCAdapter::PadAxes::StickY; 148 pad_queue[port].Push(pads[port]);
156 pads[port].axis_value = pads[port].stick_y; 149 }
157 pad_queue[port].Push(pads[port]);
158 }
159 if (pads[port].substick_x > origin_status[port].substick_x + pads[port].THRESHOLD ||
160 pads[port].substick_x < origin_status[port].substick_x - pads[port].THRESHOLD) {
161 pads[port].axis = GCAdapter::PadAxes::SubstickX;
162 pads[port].axis_value = pads[port].substick_x;
163 pad_queue[port].Push(pads[port]);
164 }
165 if (pads[port].substick_y > origin_status[port].substick_y + pads[port].THRESHOLD ||
166 pads[port].substick_y < origin_status[port].substick_y - pads[port].THRESHOLD) {
167 pads[port].axis = GCAdapter::PadAxes::SubstickY;
168 pads[port].axis_value = pads[port].substick_y;
169 pad_queue[port].Push(pads[port]);
170 }
171 if (pads[port].trigger_left > pads[port].TRIGGER_THRESHOLD) {
172 pads[port].axis = GCAdapter::PadAxes::TriggerLeft;
173 pads[port].axis_value = pads[port].trigger_left;
174 pad_queue[port].Push(pads[port]);
175 }
176 if (pads[port].trigger_right > pads[port].TRIGGER_THRESHOLD) {
177 pads[port].axis = GCAdapter::PadAxes::TriggerRight;
178 pads[port].axis_value = pads[port].trigger_right;
179 pad_queue[port].Push(pads[port]);
180 } 150 }
181 } 151 }
182 PadToState(pads[port], state[port]); 152 PadToState(pads[port], state[port]);
@@ -185,42 +155,11 @@ void Adapter::Read() {
185 } 155 }
186} 156}
187 157
188void Adapter::ScanThreadFunc() {
189 LOG_INFO(Input, "GC Adapter scanning thread started");
190
191 while (detect_thread_running) {
192 if (usb_adapter_handle == nullptr) {
193 std::lock_guard<std::mutex> lk(initialization_mutex);
194 Setup();
195 }
196 std::this_thread::sleep_for(std::chrono::milliseconds(500));
197 }
198}
199
200void Adapter::StartScanThread() {
201 if (detect_thread_running) {
202 return;
203 }
204 if (!libusb_ctx) {
205 return;
206 }
207
208 detect_thread_running = true;
209 detect_thread = std::thread(&Adapter::ScanThreadFunc, this);
210}
211
212void Adapter::StopScanThread() {
213 detect_thread_running = false;
214 detect_thread.join();
215}
216
217void Adapter::Setup() { 158void Adapter::Setup() {
218 // Reset the error status in case the adapter gets unplugged 159 // Initialize all controllers as unplugged
219 if (current_status < 0) {
220 current_status = NO_ADAPTER_DETECTED;
221 }
222
223 adapter_controllers_status.fill(ControllerTypes::None); 160 adapter_controllers_status.fill(ControllerTypes::None);
161 // Initialize all ports to store axis origin values
162 get_origin.fill(true);
224 163
225 // pointer to list of connected usb devices 164 // pointer to list of connected usb devices
226 libusb_device** devices{}; 165 libusb_device** devices{};
@@ -229,8 +168,6 @@ void Adapter::Setup() {
229 const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices); 168 const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices);
230 if (device_count < 0) { 169 if (device_count < 0) {
231 LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count); 170 LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count);
232 detect_thread_running = false; // Stop the loop constantly checking for gc adapter
233 // TODO: For hotplug+gc adapter checkbox implementation, revert this.
234 return; 171 return;
235 } 172 }
236 173
@@ -244,9 +181,6 @@ void Adapter::Setup() {
244 } 181 }
245 libusb_free_device_list(devices, 1); 182 libusb_free_device_list(devices, 1);
246 } 183 }
247 // Break out of the ScanThreadFunc() loop that is constantly looking for the device
248 // Assumes user has GC adapter plugged in before launch to use the adapter
249 detect_thread_running = false;
250} 184}
251 185
252bool Adapter::CheckDeviceAccess(libusb_device* device) { 186bool Adapter::CheckDeviceAccess(libusb_device* device) {
@@ -331,32 +265,23 @@ void Adapter::GetGCEndpoint(libusb_device* device) {
331 sizeof(clear_payload), nullptr, 16); 265 sizeof(clear_payload), nullptr, 16);
332 266
333 adapter_thread_running = true; 267 adapter_thread_running = true;
334 current_status = ADAPTER_DETECTED; 268 adapter_input_thread = std::thread(&Adapter::Read, this);
335 adapter_input_thread = std::thread([=] { Read(); }); // Read input
336} 269}
337 270
338Adapter::~Adapter() { 271Adapter::~Adapter() {
339 StopScanThread();
340 Reset(); 272 Reset();
341} 273}
342 274
343void Adapter::Reset() { 275void Adapter::Reset() {
344 std::unique_lock<std::mutex> lock(initialization_mutex, std::defer_lock);
345 if (!lock.try_lock()) {
346 return;
347 }
348 if (current_status != ADAPTER_DETECTED) {
349 return;
350 }
351
352 if (adapter_thread_running) { 276 if (adapter_thread_running) {
353 adapter_thread_running = false; 277 adapter_thread_running = false;
354 } 278 }
355 adapter_input_thread.join(); 279 if (adapter_input_thread.joinable()) {
280 adapter_input_thread.join();
281 }
356 282
357 adapter_controllers_status.fill(ControllerTypes::None); 283 adapter_controllers_status.fill(ControllerTypes::None);
358 get_origin.fill(true); 284 get_origin.fill(true);
359 current_status = NO_ADAPTER_DETECTED;
360 285
361 if (usb_adapter_handle) { 286 if (usb_adapter_handle) {
362 libusb_release_interface(usb_adapter_handle, 1); 287 libusb_release_interface(usb_adapter_handle, 1);
@@ -369,7 +294,93 @@ void Adapter::Reset() {
369 } 294 }
370} 295}
371 296
372bool Adapter::DeviceConnected(std::size_t port) { 297std::vector<Common::ParamPackage> Adapter::GetInputDevices() const {
298 std::vector<Common::ParamPackage> devices;
299 for (std::size_t port = 0; port < state.size(); ++port) {
300 if (!DeviceConnected(port)) {
301 continue;
302 }
303 std::string name = fmt::format("Gamecube Controller {}", port);
304 devices.emplace_back(Common::ParamPackage{
305 {"class", "gcpad"},
306 {"display", std::move(name)},
307 {"port", std::to_string(port)},
308 });
309 }
310 return devices;
311}
312
313InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
314 const Common::ParamPackage& params) const {
315 // This list is missing ZL/ZR since those are not considered buttons.
316 // We will add those afterwards
317 // This list also excludes any button that can't be really mapped
318 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12>
319 switch_to_gcadapter_button = {
320 std::pair{Settings::NativeButton::A, PadButton::PAD_BUTTON_A},
321 {Settings::NativeButton::B, PadButton::PAD_BUTTON_B},
322 {Settings::NativeButton::X, PadButton::PAD_BUTTON_X},
323 {Settings::NativeButton::Y, PadButton::PAD_BUTTON_Y},
324 {Settings::NativeButton::Plus, PadButton::PAD_BUTTON_START},
325 {Settings::NativeButton::DLeft, PadButton::PAD_BUTTON_LEFT},
326 {Settings::NativeButton::DUp, PadButton::PAD_BUTTON_UP},
327 {Settings::NativeButton::DRight, PadButton::PAD_BUTTON_RIGHT},
328 {Settings::NativeButton::DDown, PadButton::PAD_BUTTON_DOWN},
329 {Settings::NativeButton::SL, PadButton::PAD_TRIGGER_L},
330 {Settings::NativeButton::SR, PadButton::PAD_TRIGGER_R},
331 {Settings::NativeButton::R, PadButton::PAD_TRIGGER_Z},
332 };
333 if (!params.Has("port")) {
334 return {};
335 }
336
337 InputCommon::ButtonMapping mapping{};
338 for (const auto& [switch_button, gcadapter_button] : switch_to_gcadapter_button) {
339 Common::ParamPackage button_params({{"engine", "gcpad"}});
340 button_params.Set("port", params.Get("port", 0));
341 button_params.Set("button", static_cast<int>(gcadapter_button));
342 mapping.insert_or_assign(switch_button, std::move(button_params));
343 }
344
345 // Add the missing bindings for ZL/ZR
346 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadAxes>, 2>
347 switch_to_gcadapter_axis = {
348 std::pair{Settings::NativeButton::ZL, PadAxes::TriggerLeft},
349 {Settings::NativeButton::ZR, PadAxes::TriggerRight},
350 };
351 for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) {
352 Common::ParamPackage button_params({{"engine", "gcpad"}});
353 button_params.Set("port", params.Get("port", 0));
354 button_params.Set("button", static_cast<int>(PadButton::PAD_STICK));
355 button_params.Set("axis", static_cast<int>(gcadapter_axis));
356 mapping.insert_or_assign(switch_button, std::move(button_params));
357 }
358 return mapping;
359}
360
361InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice(
362 const Common::ParamPackage& params) const {
363 if (!params.Has("port")) {
364 return {};
365 }
366
367 InputCommon::AnalogMapping mapping = {};
368 Common::ParamPackage left_analog_params;
369 left_analog_params.Set("engine", "gcpad");
370 left_analog_params.Set("port", params.Get("port", 0));
371 left_analog_params.Set("axis_x", static_cast<int>(PadAxes::StickX));
372 left_analog_params.Set("axis_y", static_cast<int>(PadAxes::StickY));
373 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
374 Common::ParamPackage right_analog_params;
375 right_analog_params.Set("engine", "gcpad");
376 right_analog_params.Set("port", params.Get("port", 0));
377 right_analog_params.Set("axis_x", static_cast<int>(PadAxes::SubstickX));
378 right_analog_params.Set("axis_y", static_cast<int>(PadAxes::SubstickY));
379 mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
380 return mapping;
381}
382
383bool Adapter::DeviceConnected(std::size_t port) const {
373 return adapter_controllers_status[port] != ControllerTypes::None; 384 return adapter_controllers_status[port] != ControllerTypes::None;
374} 385}
375 386
@@ -409,24 +420,7 @@ const std::array<GCState, 4>& Adapter::GetPadState() const {
409} 420}
410 421
411int Adapter::GetOriginValue(int port, int axis) const { 422int Adapter::GetOriginValue(int port, int axis) const {
412 const auto& status = origin_status[port]; 423 return origin_status[port].axis_values[axis];
413
414 switch (static_cast<PadAxes>(axis)) {
415 case PadAxes::StickX:
416 return status.stick_x;
417 case PadAxes::StickY:
418 return status.stick_y;
419 case PadAxes::SubstickX:
420 return status.substick_x;
421 case PadAxes::SubstickY:
422 return status.substick_y;
423 case PadAxes::TriggerLeft:
424 return status.trigger_left;
425 case PadAxes::TriggerRight:
426 return status.trigger_right;
427 default:
428 return 0;
429 }
430} 424}
431 425
432} // namespace GCAdapter 426} // namespace GCAdapter