summaryrefslogtreecommitdiff
path: root/src/input_common/gcadapter
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common/gcadapter')
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp290
-rw-r--r--src/input_common/gcadapter/gc_adapter.h51
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp40
3 files changed, 175 insertions, 206 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
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index 3586c8bda..75bf9fe74 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -10,6 +10,7 @@
10#include <unordered_map> 10#include <unordered_map>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/threadsafe_queue.h" 12#include "common/threadsafe_queue.h"
13#include "input_common/main.h"
13 14
14struct libusb_context; 15struct libusb_context;
15struct libusb_device; 16struct libusb_device;
@@ -47,24 +48,10 @@ enum class PadAxes : u8 {
47}; 48};
48 49
49struct GCPadStatus { 50struct GCPadStatus {
50 u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits 51 u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
51 u8 stick_x{}; // 0 <= stick_x <= 255 52
52 u8 stick_y{}; // 0 <= stick_y <= 255 53 std::array<u8, 6> axis_values{}; // Triggers and sticks, following indices defined in PadAxes
53 u8 substick_x{}; // 0 <= substick_x <= 255 54 static constexpr u8 THRESHOLD = 50; // Threshold for axis press for polling
54 u8 substick_y{}; // 0 <= substick_y <= 255
55 u8 trigger_left{}; // 0 <= trigger_left <= 255
56 u8 trigger_right{}; // 0 <= trigger_right <= 255
57
58 static constexpr u8 MAIN_STICK_CENTER_X = 0x80;
59 static constexpr u8 MAIN_STICK_CENTER_Y = 0x80;
60 static constexpr u8 MAIN_STICK_RADIUS = 0x7f;
61 static constexpr u8 C_STICK_CENTER_X = 0x80;
62 static constexpr u8 C_STICK_CENTER_Y = 0x80;
63 static constexpr u8 C_STICK_RADIUS = 0x7f;
64 static constexpr u8 THRESHOLD = 10;
65
66 // 256/4, at least a quarter press to count as a press. For polling mostly
67 static constexpr u8 TRIGGER_THRESHOLD = 64;
68 55
69 u8 port{}; 56 u8 port{};
70 PadAxes axis{PadAxes::Undefined}; 57 PadAxes axis{PadAxes::Undefined};
@@ -78,11 +65,6 @@ struct GCState {
78 65
79enum class ControllerTypes { None, Wired, Wireless }; 66enum class ControllerTypes { None, Wired, Wireless };
80 67
81enum {
82 NO_ADAPTER_DETECTED = 0,
83 ADAPTER_DETECTED = 1,
84};
85
86class Adapter { 68class Adapter {
87public: 69public:
88 /// Initialize the GC Adapter capture and read sequence 70 /// Initialize the GC Adapter capture and read sequence
@@ -94,8 +76,12 @@ public:
94 void BeginConfiguration(); 76 void BeginConfiguration();
95 void EndConfiguration(); 77 void EndConfiguration();
96 78
79 std::vector<Common::ParamPackage> GetInputDevices() const;
80 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
81 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
82
97 /// Returns true if there is a device connected to port 83 /// Returns true if there is a device connected to port
98 bool DeviceConnected(std::size_t port); 84 bool DeviceConnected(std::size_t port) const;
99 85
100 std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue(); 86 std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue();
101 const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const; 87 const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const;
@@ -111,12 +97,6 @@ private:
111 void PadToState(const GCPadStatus& pad, GCState& state); 97 void PadToState(const GCPadStatus& pad, GCState& state);
112 98
113 void Read(); 99 void Read();
114 void ScanThreadFunc();
115 /// Begin scanning for the GC Adapter.
116 void StartScanThread();
117
118 /// Stop scanning for the adapter
119 void StopScanThread();
120 100
121 /// Resets status of device connected to port 101 /// Resets status of device connected to port
122 void ResetDeviceType(std::size_t port); 102 void ResetDeviceType(std::size_t port);
@@ -133,19 +113,11 @@ private:
133 /// For use in initialization, querying devices to find the adapter 113 /// For use in initialization, querying devices to find the adapter
134 void Setup(); 114 void Setup();
135 115
136 int current_status = NO_ADAPTER_DETECTED;
137 libusb_device_handle* usb_adapter_handle = nullptr; 116 libusb_device_handle* usb_adapter_handle = nullptr;
138 std::array<ControllerTypes, 4> adapter_controllers_status{};
139
140 std::mutex s_mutex;
141 117
142 std::thread adapter_input_thread; 118 std::thread adapter_input_thread;
143 bool adapter_thread_running; 119 bool adapter_thread_running;
144 120
145 std::mutex initialization_mutex;
146 std::thread detect_thread;
147 bool detect_thread_running = false;
148
149 libusb_context* libusb_ctx; 121 libusb_context* libusb_ctx;
150 122
151 u8 input_endpoint = 0; 123 u8 input_endpoint = 0;
@@ -153,10 +125,11 @@ private:
153 125
154 bool configuring = false; 126 bool configuring = false;
155 127
156 std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
157 std::array<GCState, 4> state; 128 std::array<GCState, 4> state;
158 std::array<bool, 4> get_origin; 129 std::array<bool, 4> get_origin;
159 std::array<GCPadStatus, 4> origin_status; 130 std::array<GCPadStatus, 4> origin_status;
131 std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
132 std::array<ControllerTypes, 4> adapter_controllers_status{};
160}; 133};
161 134
162} // namespace GCAdapter 135} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index 96e22d3ad..92e9e8e89 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -15,7 +15,7 @@ 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_, GCAdapter::Adapter* adapter) 18 explicit GCButton(int port_, int 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;
@@ -30,15 +30,16 @@ public:
30private: 30private:
31 const int port; 31 const int port;
32 const int button; 32 const int button;
33 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(int port_, int axis_, float threshold_, bool trigger_if_greater_,
39 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), origin_value(adapter->GetOriginValue(port_, axis_)) {} 41 gcadapter(adapter),
42 origin_value(static_cast<float>(adapter->GetOriginValue(port_, axis_))) {}
42 43
43 bool GetStatus() const override { 44 bool GetStatus() const override {
44 if (gcadapter->DeviceConnected(port)) { 45 if (gcadapter->DeviceConnected(port)) {
@@ -59,7 +60,7 @@ private:
59 const int axis; 60 const int axis;
60 float threshold; 61 float threshold;
61 bool trigger_if_greater; 62 bool trigger_if_greater;
62 GCAdapter::Adapter* gcadapter; 63 const GCAdapter::Adapter* gcadapter;
63 const float origin_value; 64 const float origin_value;
64}; 65};
65 66
@@ -76,8 +77,7 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param
76 77
77 // button is not an axis/stick button 78 // button is not an axis/stick button
78 if (button_id != PAD_STICK_ID) { 79 if (button_id != PAD_STICK_ID) {
79 auto button = std::make_unique<GCButton>(port, button_id, adapter.get()); 80 return std::make_unique<GCButton>(port, button_id, adapter.get());
80 return std::move(button);
81 } 81 }
82 82
83 // For Axis buttons, used by the binary sticks. 83 // For Axis buttons, used by the binary sticks.
@@ -149,19 +149,18 @@ void GCButtonFactory::EndConfiguration() {
149 149
150class GCAnalog final : public Input::AnalogDevice { 150class GCAnalog final : public Input::AnalogDevice {
151public: 151public:
152 GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter) 152 GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_,
153 const GCAdapter::Adapter* adapter, float range_)
153 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter), 154 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter),
154 origin_value_x(adapter->GetOriginValue(port_, axis_x_)), 155 origin_value_x(static_cast<float>(adapter->GetOriginValue(port_, axis_x_))),
155 origin_value_y(adapter->GetOriginValue(port_, axis_y_)) {} 156 origin_value_y(static_cast<float>(adapter->GetOriginValue(port_, axis_y_))),
157 range(range_) {}
156 158
157 float GetAxis(int axis) const { 159 float GetAxis(int axis) const {
158 if (gcadapter->DeviceConnected(port)) { 160 if (gcadapter->DeviceConnected(port)) {
159 std::lock_guard lock{mutex}; 161 std::lock_guard lock{mutex};
160 const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y; 162 const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y;
161 // division is not by a perfect 128 to account for some variance in center location 163 return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / (100.0f * range);
162 // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
163 // [20-230]
164 return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / 95.0f;
165 } 164 }
166 return 0.0f; 165 return 0.0f;
167 } 166 }
@@ -194,7 +193,7 @@ public:
194 193
195 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 194 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
196 const auto [x, y] = GetStatus(); 195 const auto [x, y] = GetStatus();
197 const float directional_deadzone = 0.4f; 196 const float directional_deadzone = 0.5f;
198 switch (direction) { 197 switch (direction) {
199 case Input::AnalogDirection::RIGHT: 198 case Input::AnalogDirection::RIGHT:
200 return x > directional_deadzone; 199 return x > directional_deadzone;
@@ -213,9 +212,10 @@ private:
213 const int axis_x; 212 const int axis_x;
214 const int axis_y; 213 const int axis_y;
215 const float deadzone; 214 const float deadzone;
216 GCAdapter::Adapter* gcadapter; 215 const GCAdapter::Adapter* gcadapter;
217 const float origin_value_x; 216 const float origin_value_x;
218 const float origin_value_y; 217 const float origin_value_y;
218 const float range;
219 mutable std::mutex mutex; 219 mutable std::mutex mutex;
220}; 220};
221 221
@@ -234,9 +234,10 @@ std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::Param
234 const int port = params.Get("port", 0); 234 const int port = params.Get("port", 0);
235 const int axis_x = params.Get("axis_x", 0); 235 const int axis_x = params.Get("axis_x", 0);
236 const int axis_y = params.Get("axis_y", 1); 236 const int axis_y = params.Get("axis_y", 1);
237 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); 237 const float 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);
238 239
239 return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get()); 240 return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range);
240} 241}
241 242
242void GCAnalogFactory::BeginConfiguration() { 243void GCAnalogFactory::BeginConfiguration() {
@@ -264,7 +265,8 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
264 if (analog_x_axis == -1) { 265 if (analog_x_axis == -1) {
265 analog_x_axis = axis; 266 analog_x_axis = axis;
266 controller_number = static_cast<int>(port); 267 controller_number = static_cast<int>(port);
267 } else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) { 268 } else if (analog_y_axis == -1 && analog_x_axis != axis &&
269 controller_number == static_cast<int>(port)) {
268 analog_y_axis = axis; 270 analog_y_axis = axis;
269 } 271 }
270 } 272 }