summaryrefslogtreecommitdiff
path: root/src/input_common/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common/drivers')
-rw-r--r--src/input_common/drivers/camera.cpp4
-rw-r--r--src/input_common/drivers/camera.h4
-rw-r--r--src/input_common/drivers/gc_adapter.cpp10
-rw-r--r--src/input_common/drivers/gc_adapter.h2
-rw-r--r--src/input_common/drivers/joycon.cpp677
-rw-r--r--src/input_common/drivers/joycon.h111
-rw-r--r--src/input_common/drivers/sdl_driver.cpp77
-rw-r--r--src/input_common/drivers/sdl_driver.h2
-rw-r--r--src/input_common/drivers/tas_input.cpp12
-rw-r--r--src/input_common/drivers/tas_input.h2
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp11
-rw-r--r--src/input_common/drivers/virtual_amiibo.h2
12 files changed, 869 insertions, 45 deletions
diff --git a/src/input_common/drivers/camera.cpp b/src/input_common/drivers/camera.cpp
index fad9177dc..04970f635 100644
--- a/src/input_common/drivers/camera.cpp
+++ b/src/input_common/drivers/camera.cpp
@@ -72,11 +72,11 @@ std::size_t Camera::getImageHeight() const {
72 } 72 }
73} 73}
74 74
75Common::Input::CameraError Camera::SetCameraFormat( 75Common::Input::DriverResult Camera::SetCameraFormat(
76 [[maybe_unused]] const PadIdentifier& identifier_, 76 [[maybe_unused]] const PadIdentifier& identifier_,
77 const Common::Input::CameraFormat camera_format) { 77 const Common::Input::CameraFormat camera_format) {
78 status.format = camera_format; 78 status.format = camera_format;
79 return Common::Input::CameraError::None; 79 return Common::Input::DriverResult::Success;
80} 80}
81 81
82} // namespace InputCommon 82} // namespace InputCommon
diff --git a/src/input_common/drivers/camera.h b/src/input_common/drivers/camera.h
index ead3e0fde..24b27e325 100644
--- a/src/input_common/drivers/camera.h
+++ b/src/input_common/drivers/camera.h
@@ -22,8 +22,8 @@ public:
22 std::size_t getImageWidth() const; 22 std::size_t getImageWidth() const;
23 std::size_t getImageHeight() const; 23 std::size_t getImageHeight() const;
24 24
25 Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_, 25 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier_,
26 Common::Input::CameraFormat camera_format) override; 26 Common::Input::CameraFormat camera_format) override;
27 27
28private: 28private:
29 Common::Input::CameraStatus status{}; 29 Common::Input::CameraStatus status{};
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index 826fa2109..d09ff178b 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/param_package.h" 8#include "common/param_package.h"
9#include "common/polyfill_thread.h"
9#include "common/settings_input.h" 10#include "common/settings_input.h"
10#include "common/thread.h" 11#include "common/thread.h"
11#include "input_common/drivers/gc_adapter.h" 12#include "input_common/drivers/gc_adapter.h"
@@ -217,8 +218,7 @@ void GCAdapter::AdapterScanThread(std::stop_token stop_token) {
217 Common::SetCurrentThreadName("ScanGCAdapter"); 218 Common::SetCurrentThreadName("ScanGCAdapter");
218 usb_adapter_handle = nullptr; 219 usb_adapter_handle = nullptr;
219 pads = {}; 220 pads = {};
220 while (!stop_token.stop_requested() && !Setup()) { 221 while (!Setup() && Common::StoppableTimedWait(stop_token, std::chrono::seconds{2})) {
221 std::this_thread::sleep_for(std::chrono::seconds(2));
222 } 222 }
223} 223}
224 224
@@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) {
324 return true; 324 return true;
325} 325}
326 326
327Common::Input::VibrationError GCAdapter::SetVibration( 327Common::Input::DriverResult GCAdapter::SetVibration(
328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; 329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
330 const auto processed_amplitude = 330 const auto processed_amplitude =
@@ -333,9 +333,9 @@ Common::Input::VibrationError GCAdapter::SetVibration(
333 pads[identifier.port].rumble_amplitude = processed_amplitude; 333 pads[identifier.port].rumble_amplitude = processed_amplitude;
334 334
335 if (!rumble_enabled) { 335 if (!rumble_enabled) {
336 return Common::Input::VibrationError::Disabled; 336 return Common::Input::DriverResult::Disabled;
337 } 337 }
338 return Common::Input::VibrationError::None; 338 return Common::Input::DriverResult::Success;
339} 339}
340 340
341bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { 341bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h
index b5270fd0b..3c2eb376d 100644
--- a/src/input_common/drivers/gc_adapter.h
+++ b/src/input_common/drivers/gc_adapter.h
@@ -25,7 +25,7 @@ public:
25 explicit GCAdapter(std::string input_engine_); 25 explicit GCAdapter(std::string input_engine_);
26 ~GCAdapter() override; 26 ~GCAdapter() override;
27 27
28 Common::Input::VibrationError SetVibration( 28 Common::Input::DriverResult SetVibration(
29 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 29 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
30 30
31 bool IsVibrationEnabled(const PadIdentifier& identifier) override; 31 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
new file mode 100644
index 000000000..4fcfb4510
--- /dev/null
+++ b/src/input_common/drivers/joycon.cpp
@@ -0,0 +1,677 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <fmt/format.h>
5
6#include "common/param_package.h"
7#include "common/polyfill_ranges.h"
8#include "common/polyfill_thread.h"
9#include "common/settings.h"
10#include "common/thread.h"
11#include "input_common/drivers/joycon.h"
12#include "input_common/helpers/joycon_driver.h"
13#include "input_common/helpers/joycon_protocol/joycon_types.h"
14
15namespace InputCommon {
16
17Joycons::Joycons(const std::string& input_engine_) : InputEngine(input_engine_) {
18 // Avoid conflicting with SDL driver
19 if (!Settings::values.enable_joycon_driver) {
20 return;
21 }
22 LOG_INFO(Input, "Joycon driver Initialization started");
23 const int init_res = SDL_hid_init();
24 if (init_res == 0) {
25 Setup();
26 } else {
27 LOG_ERROR(Input, "Hidapi could not be initialized. failed with error = {}", init_res);
28 }
29}
30
31Joycons::~Joycons() {
32 Reset();
33}
34
35void Joycons::Reset() {
36 scan_thread = {};
37 for (const auto& device : left_joycons) {
38 if (!device) {
39 continue;
40 }
41 device->Stop();
42 }
43 for (const auto& device : right_joycons) {
44 if (!device) {
45 continue;
46 }
47 device->Stop();
48 }
49 SDL_hid_exit();
50}
51
52void Joycons::Setup() {
53 u32 port = 0;
54 PreSetController(GetIdentifier(0, Joycon::ControllerType::None));
55 for (auto& device : left_joycons) {
56 PreSetController(GetIdentifier(port, Joycon::ControllerType::Left));
57 device = std::make_shared<Joycon::JoyconDriver>(port++);
58 }
59 port = 0;
60 for (auto& device : right_joycons) {
61 PreSetController(GetIdentifier(port, Joycon::ControllerType::Right));
62 device = std::make_shared<Joycon::JoyconDriver>(port++);
63 }
64
65 scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); });
66}
67
68void Joycons::ScanThread(std::stop_token stop_token) {
69 constexpr u16 nintendo_vendor_id = 0x057e;
70 Common::SetCurrentThreadName("JoyconScanThread");
71
72 do {
73 SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0);
74 SDL_hid_device_info* cur_dev = devs;
75
76 while (cur_dev) {
77 if (IsDeviceNew(cur_dev)) {
78 LOG_DEBUG(Input, "Device Found,type : {:04X} {:04X}", cur_dev->vendor_id,
79 cur_dev->product_id);
80 RegisterNewDevice(cur_dev);
81 }
82 cur_dev = cur_dev->next;
83 }
84
85 SDL_hid_free_enumeration(devs);
86 } while (Common::StoppableTimedWait(stop_token, std::chrono::seconds{5}));
87}
88
89bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {
90 Joycon::ControllerType type{};
91 Joycon::SerialNumber serial_number{};
92
93 const auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
94 if (result != Joycon::DriverResult::Success) {
95 return false;
96 }
97
98 const auto result2 = Joycon::JoyconDriver::GetSerialNumber(device_info, serial_number);
99 if (result2 != Joycon::DriverResult::Success) {
100 return false;
101 }
102
103 auto is_handle_identical = [serial_number](std::shared_ptr<Joycon::JoyconDriver> device) {
104 if (!device) {
105 return false;
106 }
107 if (!device->IsConnected()) {
108 return false;
109 }
110 if (device->GetHandleSerialNumber() != serial_number) {
111 return false;
112 }
113 return true;
114 };
115
116 // Check if device already exist
117 switch (type) {
118 case Joycon::ControllerType::Left:
119 for (const auto& device : left_joycons) {
120 if (is_handle_identical(device)) {
121 return false;
122 }
123 }
124 break;
125 case Joycon::ControllerType::Right:
126 for (const auto& device : right_joycons) {
127 if (is_handle_identical(device)) {
128 return false;
129 }
130 }
131 break;
132 default:
133 return false;
134 }
135
136 return true;
137}
138
139void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
140 Joycon::ControllerType type{};
141 auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
142 auto handle = GetNextFreeHandle(type);
143 if (handle == nullptr) {
144 LOG_WARNING(Input, "No free handles available");
145 return;
146 }
147 if (result == Joycon::DriverResult::Success) {
148 result = handle->RequestDeviceAccess(device_info);
149 }
150 if (result == Joycon::DriverResult::Success) {
151 LOG_WARNING(Input, "Initialize device");
152
153 const std::size_t port = handle->GetDevicePort();
154 const Joycon::JoyconCallbacks callbacks{
155 .on_battery_data = {[this, port, type](Joycon::Battery value) {
156 OnBatteryUpdate(port, type, value);
157 }},
158 .on_color_data = {[this, port, type](Joycon::Color value) {
159 OnColorUpdate(port, type, value);
160 }},
161 .on_button_data = {[this, port, type](int id, bool value) {
162 OnButtonUpdate(port, type, id, value);
163 }},
164 .on_stick_data = {[this, port, type](int id, f32 value) {
165 OnStickUpdate(port, type, id, value);
166 }},
167 .on_motion_data = {[this, port, type](int id, const Joycon::MotionData& value) {
168 OnMotionUpdate(port, type, id, value);
169 }},
170 .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }},
171 .on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) {
172 OnAmiiboUpdate(port, amiibo_data);
173 }},
174 .on_camera_data = {[this, port](const std::vector<u8>& camera_data,
175 Joycon::IrsResolution format) {
176 OnCameraUpdate(port, camera_data, format);
177 }},
178 };
179
180 handle->InitializeDevice();
181 handle->SetCallbacks(callbacks);
182 }
183}
184
185std::shared_ptr<Joycon::JoyconDriver> Joycons::GetNextFreeHandle(
186 Joycon::ControllerType type) const {
187 if (type == Joycon::ControllerType::Left) {
188 const auto unconnected_device =
189 std::ranges::find_if(left_joycons, [](auto& device) { return !device->IsConnected(); });
190 if (unconnected_device != left_joycons.end()) {
191 return *unconnected_device;
192 }
193 }
194 if (type == Joycon::ControllerType::Right) {
195 const auto unconnected_device = std::ranges::find_if(
196 right_joycons, [](auto& device) { return !device->IsConnected(); });
197
198 if (unconnected_device != right_joycons.end()) {
199 return *unconnected_device;
200 }
201 }
202 return nullptr;
203}
204
205bool Joycons::IsVibrationEnabled(const PadIdentifier& identifier) {
206 const auto handle = GetHandle(identifier);
207 if (handle == nullptr) {
208 return false;
209 }
210 return handle->IsVibrationEnabled();
211}
212
213Common::Input::DriverResult Joycons::SetVibration(const PadIdentifier& identifier,
214 const Common::Input::VibrationStatus& vibration) {
215 const Joycon::VibrationValue native_vibration{
216 .low_amplitude = vibration.low_amplitude,
217 .low_frequency = vibration.low_frequency,
218 .high_amplitude = vibration.high_amplitude,
219 .high_frequency = vibration.high_frequency,
220 };
221 auto handle = GetHandle(identifier);
222 if (handle == nullptr) {
223 return Common::Input::DriverResult::InvalidHandle;
224 }
225
226 handle->SetVibration(native_vibration);
227 return Common::Input::DriverResult::Success;
228}
229
230Common::Input::DriverResult Joycons::SetLeds(const PadIdentifier& identifier,
231 const Common::Input::LedStatus& led_status) {
232 auto handle = GetHandle(identifier);
233 if (handle == nullptr) {
234 return Common::Input::DriverResult::InvalidHandle;
235 }
236 int led_config = led_status.led_1 ? 1 : 0;
237 led_config += led_status.led_2 ? 2 : 0;
238 led_config += led_status.led_3 ? 4 : 0;
239 led_config += led_status.led_4 ? 8 : 0;
240
241 return static_cast<Common::Input::DriverResult>(
242 handle->SetLedConfig(static_cast<u8>(led_config)));
243}
244
245Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier,
246 Common::Input::CameraFormat camera_format) {
247 auto handle = GetHandle(identifier);
248 if (handle == nullptr) {
249 return Common::Input::DriverResult::InvalidHandle;
250 }
251 return static_cast<Common::Input::DriverResult>(handle->SetIrsConfig(
252 Joycon::IrsMode::ImageTransfer, static_cast<Joycon::IrsResolution>(camera_format)));
253};
254
255Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const {
256 return Common::Input::NfcState::Success;
257};
258
259Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier_,
260 const std::vector<u8>& data) {
261 return Common::Input::NfcState::NotSupported;
262};
263
264Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
265 const Common::Input::PollingMode polling_mode) {
266 auto handle = GetHandle(identifier);
267 if (handle == nullptr) {
268 LOG_ERROR(Input, "Invalid handle {}", identifier.port);
269 return Common::Input::DriverResult::InvalidHandle;
270 }
271
272 switch (polling_mode) {
273 case Common::Input::PollingMode::Active:
274 return static_cast<Common::Input::DriverResult>(handle->SetActiveMode());
275 case Common::Input::PollingMode::Pasive:
276 return static_cast<Common::Input::DriverResult>(handle->SetPasiveMode());
277 case Common::Input::PollingMode::IR:
278 return static_cast<Common::Input::DriverResult>(handle->SetIrMode());
279 case Common::Input::PollingMode::NFC:
280 return static_cast<Common::Input::DriverResult>(handle->SetNfcMode());
281 case Common::Input::PollingMode::Ring:
282 return static_cast<Common::Input::DriverResult>(handle->SetRingConMode());
283 default:
284 return Common::Input::DriverResult::NotSupported;
285 }
286}
287
288void Joycons::OnBatteryUpdate(std::size_t port, Joycon::ControllerType type,
289 Joycon::Battery value) {
290 const auto identifier = GetIdentifier(port, type);
291 if (value.charging != 0) {
292 SetBattery(identifier, Common::Input::BatteryLevel::Charging);
293 return;
294 }
295
296 Common::Input::BatteryLevel battery{};
297 switch (value.status) {
298 case 0:
299 battery = Common::Input::BatteryLevel::Empty;
300 break;
301 case 1:
302 battery = Common::Input::BatteryLevel::Critical;
303 break;
304 case 2:
305 battery = Common::Input::BatteryLevel::Low;
306 break;
307 case 3:
308 battery = Common::Input::BatteryLevel::Medium;
309 break;
310 case 4:
311 default:
312 battery = Common::Input::BatteryLevel::Full;
313 break;
314 }
315 SetBattery(identifier, battery);
316}
317
318void Joycons::OnColorUpdate(std::size_t port, Joycon::ControllerType type,
319 const Joycon::Color& value) {
320 const auto identifier = GetIdentifier(port, type);
321 Common::Input::BodyColorStatus color{
322 .body = value.body,
323 .buttons = value.buttons,
324 .left_grip = value.left_grip,
325 .right_grip = value.right_grip,
326 };
327 SetColor(identifier, color);
328}
329
330void Joycons::OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value) {
331 const auto identifier = GetIdentifier(port, type);
332 SetButton(identifier, id, value);
333}
334
335void Joycons::OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value) {
336 const auto identifier = GetIdentifier(port, type);
337 SetAxis(identifier, id, value);
338}
339
340void Joycons::OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
341 const Joycon::MotionData& value) {
342 const auto identifier = GetIdentifier(port, type);
343 BasicMotion motion_data{
344 .gyro_x = value.gyro_x,
345 .gyro_y = value.gyro_y,
346 .gyro_z = value.gyro_z,
347 .accel_x = value.accel_x,
348 .accel_y = value.accel_y,
349 .accel_z = value.accel_z,
350 .delta_timestamp = 15000,
351 };
352 SetMotion(identifier, id, motion_data);
353}
354
355void Joycons::OnRingConUpdate(f32 ring_data) {
356 // To simplify ring detection it will always be mapped to an empty identifier for all
357 // controllers
358 constexpr PadIdentifier identifier = {
359 .guid = Common::UUID{},
360 .port = 0,
361 .pad = 0,
362 };
363 SetAxis(identifier, 100, ring_data);
364}
365
366void Joycons::OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data) {
367 const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
368 const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved
369 : Common::Input::NfcState::NewAmiibo;
370 SetNfc(identifier, {nfc_state, amiibo_data});
371}
372
373void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
374 Joycon::IrsResolution format) {
375 const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
376 SetCamera(identifier, {static_cast<Common::Input::CameraFormat>(format), camera_data});
377}
378
379std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifier) const {
380 auto is_handle_active = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
381 if (!device) {
382 return false;
383 }
384 if (!device->IsConnected()) {
385 return false;
386 }
387 if (device->GetDevicePort() == identifier.port) {
388 return true;
389 }
390 return false;
391 };
392 const auto type = static_cast<Joycon::ControllerType>(identifier.pad);
393
394 if (type == Joycon::ControllerType::Left) {
395 const auto matching_device = std::ranges::find_if(
396 left_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
397
398 if (matching_device != left_joycons.end()) {
399 return *matching_device;
400 }
401 }
402
403 if (type == Joycon::ControllerType::Right) {
404 const auto matching_device = std::ranges::find_if(
405 right_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
406
407 if (matching_device != right_joycons.end()) {
408 return *matching_device;
409 }
410 }
411
412 return nullptr;
413}
414
415PadIdentifier Joycons::GetIdentifier(std::size_t port, Joycon::ControllerType type) const {
416 const std::array<u8, 16> guid{0, 0, 0, 0, 0, 0, 0, 0,
417 0, 0, 0, 0, 0, 0, 0, static_cast<u8>(type)};
418 return {
419 .guid = Common::UUID{guid},
420 .port = port,
421 .pad = static_cast<std::size_t>(type),
422 };
423}
424
425Common::ParamPackage Joycons::GetParamPackage(std::size_t port, Joycon::ControllerType type) const {
426 const auto identifier = GetIdentifier(port, type);
427 return {
428 {"engine", GetEngineName()},
429 {"guid", identifier.guid.RawString()},
430 {"port", std::to_string(identifier.port)},
431 {"pad", std::to_string(identifier.pad)},
432 };
433}
434
435std::vector<Common::ParamPackage> Joycons::GetInputDevices() const {
436 std::vector<Common::ParamPackage> devices{};
437
438 auto add_entry = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
439 if (!device) {
440 return;
441 }
442 if (!device->IsConnected()) {
443 return;
444 }
445 auto param = GetParamPackage(device->GetDevicePort(), device->GetHandleDeviceType());
446 std::string name = fmt::format("{} {}", JoyconName(device->GetHandleDeviceType()),
447 device->GetDevicePort() + 1);
448 param.Set("display", std::move(name));
449 devices.emplace_back(param);
450 };
451
452 for (const auto& controller : left_joycons) {
453 add_entry(controller);
454 }
455 for (const auto& controller : right_joycons) {
456 add_entry(controller);
457 }
458
459 // List dual joycon pairs
460 for (std::size_t i = 0; i < MaxSupportedControllers; i++) {
461 if (!left_joycons[i] || !right_joycons[i]) {
462 continue;
463 }
464 if (!left_joycons[i]->IsConnected() || !right_joycons[i]->IsConnected()) {
465 continue;
466 }
467 auto main_param = GetParamPackage(i, left_joycons[i]->GetHandleDeviceType());
468 const auto second_param = GetParamPackage(i, right_joycons[i]->GetHandleDeviceType());
469 const auto type = Joycon::ControllerType::Dual;
470 std::string name = fmt::format("{} {}", JoyconName(type), i + 1);
471
472 main_param.Set("display", std::move(name));
473 main_param.Set("guid2", second_param.Get("guid", ""));
474 main_param.Set("pad", std::to_string(static_cast<size_t>(type)));
475 devices.emplace_back(main_param);
476 }
477
478 return devices;
479}
480
481ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& params) {
482 static constexpr std::array<std::tuple<Settings::NativeButton::Values, Joycon::PadButton, bool>,
483 18>
484 switch_to_joycon_button = {
485 std::tuple{Settings::NativeButton::A, Joycon::PadButton::A, true},
486 {Settings::NativeButton::B, Joycon::PadButton::B, true},
487 {Settings::NativeButton::X, Joycon::PadButton::X, true},
488 {Settings::NativeButton::Y, Joycon::PadButton::Y, true},
489 {Settings::NativeButton::DLeft, Joycon::PadButton::Left, false},
490 {Settings::NativeButton::DUp, Joycon::PadButton::Up, false},
491 {Settings::NativeButton::DRight, Joycon::PadButton::Right, false},
492 {Settings::NativeButton::DDown, Joycon::PadButton::Down, false},
493 {Settings::NativeButton::L, Joycon::PadButton::L, false},
494 {Settings::NativeButton::R, Joycon::PadButton::R, true},
495 {Settings::NativeButton::ZL, Joycon::PadButton::ZL, false},
496 {Settings::NativeButton::ZR, Joycon::PadButton::ZR, true},
497 {Settings::NativeButton::Plus, Joycon::PadButton::Plus, true},
498 {Settings::NativeButton::Minus, Joycon::PadButton::Minus, false},
499 {Settings::NativeButton::Home, Joycon::PadButton::Home, true},
500 {Settings::NativeButton::Screenshot, Joycon::PadButton::Capture, false},
501 {Settings::NativeButton::LStick, Joycon::PadButton::StickL, false},
502 {Settings::NativeButton::RStick, Joycon::PadButton::StickR, true},
503 };
504
505 if (!params.Has("port")) {
506 return {};
507 }
508
509 ButtonMapping mapping{};
510 for (const auto& [switch_button, joycon_button, side] : switch_to_joycon_button) {
511 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
512 auto pad = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
513 if (pad == Joycon::ControllerType::Dual) {
514 pad = side ? Joycon::ControllerType::Right : Joycon::ControllerType::Left;
515 }
516
517 Common::ParamPackage button_params = GetParamPackage(port, pad);
518 button_params.Set("button", static_cast<int>(joycon_button));
519 mapping.insert_or_assign(switch_button, std::move(button_params));
520 }
521
522 // Map SL and SR buttons for left joycons
523 if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Left)) {
524 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
525 Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Left);
526
527 Common::ParamPackage sl_button_params = button_params;
528 Common::ParamPackage sr_button_params = button_params;
529 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL));
530 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR));
531 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
532 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
533 }
534
535 // Map SL and SR buttons for right joycons
536 if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Right)) {
537 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
538 Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Right);
539
540 Common::ParamPackage sl_button_params = button_params;
541 Common::ParamPackage sr_button_params = button_params;
542 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL));
543 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR));
544 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
545 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
546 }
547
548 return mapping;
549}
550
551AnalogMapping Joycons::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
552 if (!params.Has("port")) {
553 return {};
554 }
555
556 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
557 auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
558 auto pad_right = pad_left;
559 if (pad_left == Joycon::ControllerType::Dual) {
560 pad_left = Joycon::ControllerType::Left;
561 pad_right = Joycon::ControllerType::Right;
562 }
563
564 AnalogMapping mapping = {};
565 Common::ParamPackage left_analog_params = GetParamPackage(port, pad_left);
566 left_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::LeftStickX));
567 left_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::LeftStickY));
568 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
569 Common::ParamPackage right_analog_params = GetParamPackage(port, pad_right);
570 right_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::RightStickX));
571 right_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::RightStickY));
572 mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
573 return mapping;
574}
575
576MotionMapping Joycons::GetMotionMappingForDevice(const Common::ParamPackage& params) {
577 if (!params.Has("port")) {
578 return {};
579 }
580
581 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
582 auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
583 auto pad_right = pad_left;
584 if (pad_left == Joycon::ControllerType::Dual) {
585 pad_left = Joycon::ControllerType::Left;
586 pad_right = Joycon::ControllerType::Right;
587 }
588
589 MotionMapping mapping = {};
590 Common::ParamPackage left_motion_params = GetParamPackage(port, pad_left);
591 left_motion_params.Set("motion", 0);
592 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(left_motion_params));
593 Common::ParamPackage right_Motion_params = GetParamPackage(port, pad_right);
594 right_Motion_params.Set("motion", 1);
595 mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(right_Motion_params));
596 return mapping;
597}
598
599Common::Input::ButtonNames Joycons::GetUIButtonName(const Common::ParamPackage& params) const {
600 const auto button = static_cast<Joycon::PadButton>(params.Get("button", 0));
601 switch (button) {
602 case Joycon::PadButton::Left:
603 return Common::Input::ButtonNames::ButtonLeft;
604 case Joycon::PadButton::Right:
605 return Common::Input::ButtonNames::ButtonRight;
606 case Joycon::PadButton::Down:
607 return Common::Input::ButtonNames::ButtonDown;
608 case Joycon::PadButton::Up:
609 return Common::Input::ButtonNames::ButtonUp;
610 case Joycon::PadButton::LeftSL:
611 case Joycon::PadButton::RightSL:
612 return Common::Input::ButtonNames::TriggerSL;
613 case Joycon::PadButton::LeftSR:
614 case Joycon::PadButton::RightSR:
615 return Common::Input::ButtonNames::TriggerSR;
616 case Joycon::PadButton::L:
617 return Common::Input::ButtonNames::TriggerL;
618 case Joycon::PadButton::R:
619 return Common::Input::ButtonNames::TriggerR;
620 case Joycon::PadButton::ZL:
621 return Common::Input::ButtonNames::TriggerZL;
622 case Joycon::PadButton::ZR:
623 return Common::Input::ButtonNames::TriggerZR;
624 case Joycon::PadButton::A:
625 return Common::Input::ButtonNames::ButtonA;
626 case Joycon::PadButton::B:
627 return Common::Input::ButtonNames::ButtonB;
628 case Joycon::PadButton::X:
629 return Common::Input::ButtonNames::ButtonX;
630 case Joycon::PadButton::Y:
631 return Common::Input::ButtonNames::ButtonY;
632 case Joycon::PadButton::Plus:
633 return Common::Input::ButtonNames::ButtonPlus;
634 case Joycon::PadButton::Minus:
635 return Common::Input::ButtonNames::ButtonMinus;
636 case Joycon::PadButton::Home:
637 return Common::Input::ButtonNames::ButtonHome;
638 case Joycon::PadButton::Capture:
639 return Common::Input::ButtonNames::ButtonCapture;
640 case Joycon::PadButton::StickL:
641 return Common::Input::ButtonNames::ButtonStickL;
642 case Joycon::PadButton::StickR:
643 return Common::Input::ButtonNames::ButtonStickR;
644 default:
645 return Common::Input::ButtonNames::Undefined;
646 }
647}
648
649Common::Input::ButtonNames Joycons::GetUIName(const Common::ParamPackage& params) const {
650 if (params.Has("button")) {
651 return GetUIButtonName(params);
652 }
653 if (params.Has("axis")) {
654 return Common::Input::ButtonNames::Value;
655 }
656 if (params.Has("motion")) {
657 return Common::Input::ButtonNames::Engine;
658 }
659
660 return Common::Input::ButtonNames::Invalid;
661}
662
663std::string Joycons::JoyconName(Joycon::ControllerType type) const {
664 switch (type) {
665 case Joycon::ControllerType::Left:
666 return "Left Joycon";
667 case Joycon::ControllerType::Right:
668 return "Right Joycon";
669 case Joycon::ControllerType::Pro:
670 return "Pro Controller";
671 case Joycon::ControllerType::Dual:
672 return "Dual Joycon";
673 default:
674 return "Unknown Switch Controller";
675 }
676}
677} // namespace InputCommon
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
new file mode 100644
index 000000000..2149ab7fd
--- /dev/null
+++ b/src/input_common/drivers/joycon.h
@@ -0,0 +1,111 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <span>
8#include <thread>
9#include <SDL_hidapi.h>
10
11#include "input_common/input_engine.h"
12
13namespace InputCommon::Joycon {
14using SerialNumber = std::array<u8, 15>;
15struct Battery;
16struct Color;
17struct MotionData;
18enum class ControllerType : u8;
19enum class DriverResult;
20enum class IrsResolution;
21class JoyconDriver;
22} // namespace InputCommon::Joycon
23
24namespace InputCommon {
25
26class Joycons final : public InputCommon::InputEngine {
27public:
28 explicit Joycons(const std::string& input_engine_);
29
30 ~Joycons();
31
32 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
33 Common::Input::DriverResult SetVibration(
34 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
35
36 Common::Input::DriverResult SetLeds(const PadIdentifier& identifier,
37 const Common::Input::LedStatus& led_status) override;
38
39 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier,
40 Common::Input::CameraFormat camera_format) override;
41
42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
43 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
44 const std::vector<u8>& data) override;
45
46 Common::Input::DriverResult SetPollingMode(
47 const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override;
48
49 /// Used for automapping features
50 std::vector<Common::ParamPackage> GetInputDevices() const override;
51 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
52 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
53 MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
54 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
55
56private:
57 static constexpr std::size_t MaxSupportedControllers = 8;
58
59 /// For shutting down, clear all data, join all threads, release usb devices
60 void Reset();
61
62 /// Registers controllers, clears all data and starts the scan thread
63 void Setup();
64
65 /// Actively searchs for new devices
66 void ScanThread(std::stop_token stop_token);
67
68 /// Returns true if device is valid and not registered
69 bool IsDeviceNew(SDL_hid_device_info* device_info) const;
70
71 /// Tries to connect to the new device
72 void RegisterNewDevice(SDL_hid_device_info* device_info);
73
74 /// Returns the next free handle
75 std::shared_ptr<Joycon::JoyconDriver> GetNextFreeHandle(Joycon::ControllerType type) const;
76
77 void OnBatteryUpdate(std::size_t port, Joycon::ControllerType type, Joycon::Battery value);
78 void OnColorUpdate(std::size_t port, Joycon::ControllerType type, const Joycon::Color& value);
79 void OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value);
80 void OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value);
81 void OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
82 const Joycon::MotionData& value);
83 void OnRingConUpdate(f32 ring_data);
84 void OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data);
85 void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
86 Joycon::IrsResolution format);
87
88 /// Returns a JoyconHandle corresponding to a PadIdentifier
89 std::shared_ptr<Joycon::JoyconDriver> GetHandle(PadIdentifier identifier) const;
90
91 /// Returns a PadIdentifier corresponding to the port number and joycon type
92 PadIdentifier GetIdentifier(std::size_t port, Joycon::ControllerType type) const;
93
94 /// Returns a ParamPackage corresponding to the port number and joycon type
95 Common::ParamPackage GetParamPackage(std::size_t port, Joycon::ControllerType type) const;
96
97 std::string JoyconName(std::size_t port) const;
98
99 Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
100
101 /// Returns the name of the device in text format
102 std::string JoyconName(Joycon::ControllerType type) const;
103
104 std::jthread scan_thread;
105
106 // Joycon types are split by type to ease supporting dualjoycon configurations
107 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> left_joycons{};
108 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> right_joycons{};
109};
110
111} // namespace InputCommon
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 4818bb744..d975eb815 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -40,25 +40,26 @@ public:
40 } 40 }
41 41
42 void EnableMotion() { 42 void EnableMotion() {
43 if (sdl_controller) { 43 if (!sdl_controller) {
44 SDL_GameController* controller = sdl_controller.get(); 44 return;
45 has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE; 45 }
46 has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE; 46 SDL_GameController* controller = sdl_controller.get();
47 if (has_accel) { 47 if (HasMotion()) {
48 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); 48 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_FALSE);
49 } 49 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_FALSE);
50 if (has_gyro) { 50 }
51 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE); 51 has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE;
52 } 52 has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE;
53 if (has_accel) {
54 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
55 }
56 if (has_gyro) {
57 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE);
53 } 58 }
54 } 59 }
55 60
56 bool HasGyro() const { 61 bool HasMotion() const {
57 return has_gyro; 62 return has_gyro || has_accel;
58 }
59
60 bool HasAccel() const {
61 return has_accel;
62 } 63 }
63 64
64 bool UpdateMotion(SDL_ControllerSensorEvent event) { 65 bool UpdateMotion(SDL_ControllerSensorEvent event) {
@@ -85,6 +86,20 @@ public:
85 if (time_difference == 0) { 86 if (time_difference == 0) {
86 return false; 87 return false;
87 } 88 }
89
90 // Motion data is invalid
91 if (motion.accel_x == 0 && motion.gyro_x == 0 && motion.accel_y == 0 &&
92 motion.gyro_y == 0 && motion.accel_z == 0 && motion.gyro_z == 0) {
93 if (motion_error_count++ < 200) {
94 return false;
95 }
96 // Try restarting the sensor
97 motion_error_count = 0;
98 EnableMotion();
99 return false;
100 }
101
102 motion_error_count = 0;
88 motion.delta_timestamp = time_difference * 1000; 103 motion.delta_timestamp = time_difference * 1000;
89 return true; 104 return true;
90 } 105 }
@@ -250,6 +265,7 @@ private:
250 mutable std::mutex mutex; 265 mutable std::mutex mutex;
251 266
252 u64 last_motion_update{}; 267 u64 last_motion_update{};
268 std::size_t motion_error_count{};
253 bool has_gyro{false}; 269 bool has_gyro{false};
254 bool has_accel{false}; 270 bool has_accel{false};
255 bool has_vibration{false}; 271 bool has_vibration{false};
@@ -318,6 +334,15 @@ void SDLDriver::InitJoystick(int joystick_index) {
318 334
319 const auto guid = GetGUID(sdl_joystick); 335 const auto guid = GetGUID(sdl_joystick);
320 336
337 if (Settings::values.enable_joycon_driver) {
338 if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e &&
339 (guid.uuid[8] == 0x06 || guid.uuid[8] == 0x07)) {
340 LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index);
341 SDL_JoystickClose(sdl_joystick);
342 return;
343 }
344 }
345
321 std::scoped_lock lock{joystick_map_mutex}; 346 std::scoped_lock lock{joystick_map_mutex};
322 if (joystick_map.find(guid) == joystick_map.end()) { 347 if (joystick_map.find(guid) == joystick_map.end()) {
323 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); 348 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
@@ -440,9 +465,13 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
440 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); 465 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
441 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); 466 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
442 467
443 // Use hidapi driver for joycons. This will allow joycons to be detected as a GameController and 468 // Disable hidapi drivers for switch controllers when the custom joycon driver is enabled
444 // not a generic one 469 if (Settings::values.enable_joycon_driver) {
445 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); 470 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0");
471 } else {
472 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
473 }
474 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
446 475
447 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native 476 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
448 // driver on Linux. 477 // driver on Linux.
@@ -532,7 +561,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
532 return devices; 561 return devices;
533} 562}
534 563
535Common::Input::VibrationError SDLDriver::SetVibration( 564Common::Input::DriverResult SDLDriver::SetVibration(
536 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 565 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
537 const auto joystick = 566 const auto joystick =
538 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); 567 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
@@ -566,7 +595,7 @@ Common::Input::VibrationError SDLDriver::SetVibration(
566 .vibration = new_vibration, 595 .vibration = new_vibration,
567 }); 596 });
568 597
569 return Common::Input::VibrationError::None; 598 return Common::Input::DriverResult::Success;
570} 599}
571 600
572bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) { 601bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) {
@@ -942,18 +971,18 @@ MotionMapping SDLDriver::GetMotionMappingForDevice(const Common::ParamPackage& p
942 MotionMapping mapping = {}; 971 MotionMapping mapping = {};
943 joystick->EnableMotion(); 972 joystick->EnableMotion();
944 973
945 if (joystick->HasGyro() || joystick->HasAccel()) { 974 if (joystick->HasMotion()) {
946 mapping.insert_or_assign(Settings::NativeMotion::MotionRight, 975 mapping.insert_or_assign(Settings::NativeMotion::MotionRight,
947 BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); 976 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
948 } 977 }
949 if (params.Has("guid2")) { 978 if (params.Has("guid2")) {
950 joystick2->EnableMotion(); 979 joystick2->EnableMotion();
951 if (joystick2->HasGyro() || joystick2->HasAccel()) { 980 if (joystick2->HasMotion()) {
952 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, 981 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
953 BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID())); 982 BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID()));
954 } 983 }
955 } else { 984 } else {
956 if (joystick->HasGyro() || joystick->HasAccel()) { 985 if (joystick->HasMotion()) {
957 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, 986 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
958 BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); 987 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
959 } 988 }
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index 366bcc496..ffde169b3 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -63,7 +63,7 @@ public:
63 63
64 bool IsStickInverted(const Common::ParamPackage& params) override; 64 bool IsStickInverted(const Common::ParamPackage& params) override;
65 65
66 Common::Input::VibrationError SetVibration( 66 Common::Input::DriverResult SetVibration(
67 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 67 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
68 68
69 bool IsVibrationEnabled(const PadIdentifier& identifier) override; 69 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
diff --git a/src/input_common/drivers/tas_input.cpp b/src/input_common/drivers/tas_input.cpp
index f3ade90da..f3cb14c56 100644
--- a/src/input_common/drivers/tas_input.cpp
+++ b/src/input_common/drivers/tas_input.cpp
@@ -156,10 +156,12 @@ void Tas::RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis) {
156 }; 156 };
157} 157}
158 158
159std::tuple<TasState, size_t, size_t> Tas::GetStatus() const { 159std::tuple<TasState, size_t, std::array<size_t, PLAYER_NUMBER>> Tas::GetStatus() const {
160 TasState state; 160 TasState state;
161 std::array<size_t, PLAYER_NUMBER> lengths{0};
161 if (is_recording) { 162 if (is_recording) {
162 return {TasState::Recording, 0, record_commands.size()}; 163 lengths[0] = record_commands.size();
164 return {TasState::Recording, record_commands.size(), lengths};
163 } 165 }
164 166
165 if (is_running) { 167 if (is_running) {
@@ -168,7 +170,11 @@ std::tuple<TasState, size_t, size_t> Tas::GetStatus() const {
168 state = TasState::Stopped; 170 state = TasState::Stopped;
169 } 171 }
170 172
171 return {state, current_command, script_length}; 173 for (size_t i = 0; i < PLAYER_NUMBER; i++) {
174 lengths[i] = commands[i].size();
175 }
176
177 return {state, current_command, lengths};
172} 178}
173 179
174void Tas::UpdateThread() { 180void Tas::UpdateThread() {
diff --git a/src/input_common/drivers/tas_input.h b/src/input_common/drivers/tas_input.h
index 38a27a230..5be66d142 100644
--- a/src/input_common/drivers/tas_input.h
+++ b/src/input_common/drivers/tas_input.h
@@ -124,7 +124,7 @@ public:
124 * Current playback progress ; 124 * Current playback progress ;
125 * Total length of script file currently loaded or being recorded 125 * Total length of script file currently loaded or being recorded
126 */ 126 */
127 std::tuple<TasState, size_t, size_t> GetStatus() const; 127 std::tuple<TasState, size_t, std::array<size_t, PLAYER_NUMBER>> GetStatus() const;
128 128
129private: 129private:
130 enum class TasAxis : u8; 130 enum class TasAxis : u8;
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
index 63ffaca67..4a0268a4d 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -22,22 +22,23 @@ VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(
22 22
23VirtualAmiibo::~VirtualAmiibo() = default; 23VirtualAmiibo::~VirtualAmiibo() = default;
24 24
25Common::Input::PollingError VirtualAmiibo::SetPollingMode( 25Common::Input::DriverResult VirtualAmiibo::SetPollingMode(
26 [[maybe_unused]] const PadIdentifier& identifier_, 26 [[maybe_unused]] const PadIdentifier& identifier_,
27 const Common::Input::PollingMode polling_mode_) { 27 const Common::Input::PollingMode polling_mode_) {
28 polling_mode = polling_mode_; 28 polling_mode = polling_mode_;
29 29
30 if (polling_mode == Common::Input::PollingMode::NFC) { 30 switch (polling_mode) {
31 case Common::Input::PollingMode::NFC:
31 if (state == State::Initialized) { 32 if (state == State::Initialized) {
32 state = State::WaitingForAmiibo; 33 state = State::WaitingForAmiibo;
33 } 34 }
34 } else { 35 return Common::Input::DriverResult::Success;
36 default:
35 if (state == State::AmiiboIsOpen) { 37 if (state == State::AmiiboIsOpen) {
36 CloseAmiibo(); 38 CloseAmiibo();
37 } 39 }
40 return Common::Input::DriverResult::NotSupported;
38 } 41 }
39
40 return Common::Input::PollingError::None;
41} 42}
42 43
43Common::Input::NfcState VirtualAmiibo::SupportsNfc( 44Common::Input::NfcState VirtualAmiibo::SupportsNfc(
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
index 0f9dad333..13cacfc0a 100644
--- a/src/input_common/drivers/virtual_amiibo.h
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -36,7 +36,7 @@ public:
36 ~VirtualAmiibo() override; 36 ~VirtualAmiibo() override;
37 37
38 // Sets polling mode to a controller 38 // Sets polling mode to a controller
39 Common::Input::PollingError SetPollingMode( 39 Common::Input::DriverResult SetPollingMode(
40 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; 40 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;
41 41
42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; 42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;