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.cpp6
-rw-r--r--src/input_common/drivers/gc_adapter.h2
-rw-r--r--src/input_common/drivers/joycon.cpp678
-rw-r--r--src/input_common/drivers/joycon.h111
-rw-r--r--src/input_common/drivers/sdl_driver.cpp23
-rw-r--r--src/input_common/drivers/sdl_driver.h2
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp11
-rw-r--r--src/input_common/drivers/virtual_amiibo.h2
10 files changed, 823 insertions, 20 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..ecb3e9dc2 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -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..40cda400d
--- /dev/null
+++ b/src/input_common/drivers/joycon.cpp
@@ -0,0 +1,678 @@
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/settings.h"
9#include "common/thread.h"
10#include "input_common/drivers/joycon.h"
11#include "input_common/helpers/joycon_driver.h"
12#include "input_common/helpers/joycon_protocol/joycon_types.h"
13
14namespace InputCommon {
15
16Joycons::Joycons(const std::string& input_engine_) : InputEngine(input_engine_) {
17 // Avoid conflicting with SDL driver
18 if (!Settings::values.enable_joycon_driver) {
19 return;
20 }
21 LOG_INFO(Input, "Joycon driver Initialization started");
22 const int init_res = SDL_hid_init();
23 if (init_res == 0) {
24 Setup();
25 } else {
26 LOG_ERROR(Input, "Hidapi could not be initialized. failed with error = {}", init_res);
27 }
28}
29
30Joycons::~Joycons() {
31 Reset();
32}
33
34void Joycons::Reset() {
35 scan_thread = {};
36 for (const auto& device : left_joycons) {
37 if (!device) {
38 continue;
39 }
40 device->Stop();
41 }
42 for (const auto& device : right_joycons) {
43 if (!device) {
44 continue;
45 }
46 device->Stop();
47 }
48 SDL_hid_exit();
49}
50
51void Joycons::Setup() {
52 u32 port = 0;
53 PreSetController(GetIdentifier(0, Joycon::ControllerType::None));
54 for (auto& device : left_joycons) {
55 PreSetController(GetIdentifier(port, Joycon::ControllerType::Left));
56 device = std::make_shared<Joycon::JoyconDriver>(port++);
57 }
58 port = 0;
59 for (auto& device : right_joycons) {
60 PreSetController(GetIdentifier(port, Joycon::ControllerType::Right));
61 device = std::make_shared<Joycon::JoyconDriver>(port++);
62 }
63
64 scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); });
65}
66
67void Joycons::ScanThread(std::stop_token stop_token) {
68 constexpr u16 nintendo_vendor_id = 0x057e;
69 Common::SetCurrentThreadName("JoyconScanThread");
70 while (!stop_token.stop_requested()) {
71 SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0);
72 SDL_hid_device_info* cur_dev = devs;
73
74 while (cur_dev) {
75 if (IsDeviceNew(cur_dev)) {
76 LOG_DEBUG(Input, "Device Found,type : {:04X} {:04X}", cur_dev->vendor_id,
77 cur_dev->product_id);
78 RegisterNewDevice(cur_dev);
79 }
80 cur_dev = cur_dev->next;
81 }
82
83 SDL_hid_free_enumeration(devs);
84 std::this_thread::sleep_for(std::chrono::seconds(5));
85 }
86}
87
88bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {
89 Joycon::ControllerType type{};
90 Joycon::SerialNumber serial_number{};
91
92 const auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
93 if (result != Joycon::DriverResult::Success) {
94 return false;
95 }
96
97 const auto result2 = Joycon::JoyconDriver::GetSerialNumber(device_info, serial_number);
98 if (result2 != Joycon::DriverResult::Success) {
99 return false;
100 }
101
102 auto is_handle_identical = [serial_number](std::shared_ptr<Joycon::JoyconDriver> device) {
103 if (!device) {
104 return false;
105 }
106 if (!device->IsConnected()) {
107 return false;
108 }
109 if (device->GetHandleSerialNumber() != serial_number) {
110 return false;
111 }
112 return true;
113 };
114
115 // Check if device already exist
116 switch (type) {
117 case Joycon::ControllerType::Left:
118 for (const auto& device : left_joycons) {
119 if (is_handle_identical(device)) {
120 return false;
121 }
122 }
123 break;
124 case Joycon::ControllerType::Right:
125 for (const auto& device : right_joycons) {
126 if (is_handle_identical(device)) {
127 return false;
128 }
129 }
130 break;
131 default:
132 return false;
133 }
134
135 return true;
136}
137
138void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
139 Joycon::ControllerType type{};
140 auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
141 auto handle = GetNextFreeHandle(type);
142 if (handle == nullptr) {
143 LOG_WARNING(Input, "No free handles available");
144 return;
145 }
146 if (result == Joycon::DriverResult::Success) {
147 result = handle->RequestDeviceAccess(device_info);
148 }
149 if (result == Joycon::DriverResult::Success) {
150 LOG_WARNING(Input, "Initialize device");
151
152 const std::size_t port = handle->GetDevicePort();
153 const Joycon::JoyconCallbacks callbacks{
154 .on_battery_data = {[this, port, type](Joycon::Battery value) {
155 OnBatteryUpdate(port, type, value);
156 }},
157 .on_color_data = {[this, port, type](Joycon::Color value) {
158 OnColorUpdate(port, type, value);
159 }},
160 .on_button_data = {[this, port, type](int id, bool value) {
161 OnButtonUpdate(port, type, id, value);
162 }},
163 .on_stick_data = {[this, port, type](int id, f32 value) {
164 OnStickUpdate(port, type, id, value);
165 }},
166 .on_motion_data = {[this, port, type](int id, const Joycon::MotionData& value) {
167 OnMotionUpdate(port, type, id, value);
168 }},
169 .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }},
170 .on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) {
171 OnAmiiboUpdate(port, amiibo_data);
172 }},
173 .on_camera_data = {[this, port](const std::vector<u8>& camera_data,
174 Joycon::IrsResolution format) {
175 OnCameraUpdate(port, camera_data, format);
176 }},
177 };
178
179 handle->InitializeDevice();
180 handle->SetCallbacks(callbacks);
181 }
182}
183
184std::shared_ptr<Joycon::JoyconDriver> Joycons::GetNextFreeHandle(
185 Joycon::ControllerType type) const {
186 if (type == Joycon::ControllerType::Left) {
187 const auto unconnected_device =
188 std::ranges::find_if(left_joycons, [](auto& device) { return !device->IsConnected(); });
189 if (unconnected_device != left_joycons.end()) {
190 return *unconnected_device;
191 }
192 }
193 if (type == Joycon::ControllerType::Right) {
194 const auto unconnected_device = std::ranges::find_if(
195 right_joycons, [](auto& device) { return !device->IsConnected(); });
196
197 if (unconnected_device != right_joycons.end()) {
198 return *unconnected_device;
199 }
200 }
201 return nullptr;
202}
203
204bool Joycons::IsVibrationEnabled(const PadIdentifier& identifier) {
205 const auto handle = GetHandle(identifier);
206 if (handle == nullptr) {
207 return false;
208 }
209 return handle->IsVibrationEnabled();
210}
211
212Common::Input::DriverResult Joycons::SetVibration(const PadIdentifier& identifier,
213 const Common::Input::VibrationStatus& vibration) {
214 const Joycon::VibrationValue native_vibration{
215 .low_amplitude = vibration.low_amplitude,
216 .low_frequency = vibration.low_frequency,
217 .high_amplitude = vibration.high_amplitude,
218 .high_frequency = vibration.high_frequency,
219 };
220 auto handle = GetHandle(identifier);
221 if (handle == nullptr) {
222 return Common::Input::DriverResult::InvalidHandle;
223 }
224
225 handle->SetVibration(native_vibration);
226 return Common::Input::DriverResult::Success;
227}
228
229Common::Input::DriverResult Joycons::SetLeds(const PadIdentifier& identifier,
230 const Common::Input::LedStatus& led_status) {
231 auto handle = GetHandle(identifier);
232 if (handle == nullptr) {
233 return Common::Input::DriverResult::InvalidHandle;
234 }
235 int led_config = led_status.led_1 ? 1 : 0;
236 led_config += led_status.led_2 ? 2 : 0;
237 led_config += led_status.led_3 ? 4 : 0;
238 led_config += led_status.led_4 ? 8 : 0;
239
240 return static_cast<Common::Input::DriverResult>(
241 handle->SetLedConfig(static_cast<u8>(led_config)));
242}
243
244Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier,
245 Common::Input::CameraFormat camera_format) {
246 auto handle = GetHandle(identifier);
247 if (handle == nullptr) {
248 return Common::Input::DriverResult::InvalidHandle;
249 }
250 return static_cast<Common::Input::DriverResult>(handle->SetIrsConfig(
251 Joycon::IrsMode::ImageTransfer, static_cast<Joycon::IrsResolution>(camera_format)));
252};
253
254Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const {
255 return Common::Input::NfcState::Success;
256};
257
258Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier_,
259 const std::vector<u8>& data) {
260 return Common::Input::NfcState::NotSupported;
261};
262
263Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
264 const Common::Input::PollingMode polling_mode) {
265 auto handle = GetHandle(identifier);
266 if (handle == nullptr) {
267 LOG_ERROR(Input, "Invalid handle {}", identifier.port);
268 return Common::Input::DriverResult::InvalidHandle;
269 }
270
271 switch (polling_mode) {
272 case Common::Input::PollingMode::Active:
273 return static_cast<Common::Input::DriverResult>(handle->SetActiveMode());
274 case Common::Input::PollingMode::Pasive:
275 return static_cast<Common::Input::DriverResult>(handle->SetPasiveMode());
276 case Common::Input::PollingMode::IR:
277 return static_cast<Common::Input::DriverResult>(handle->SetIrMode());
278 case Common::Input::PollingMode::NFC:
279 return static_cast<Common::Input::DriverResult>(handle->SetNfcMode());
280 case Common::Input::PollingMode::Ring:
281 return static_cast<Common::Input::DriverResult>(handle->SetRingConMode());
282 default:
283 return Common::Input::DriverResult::NotSupported;
284 }
285}
286
287void Joycons::OnBatteryUpdate(std::size_t port, Joycon::ControllerType type,
288 Joycon::Battery value) {
289 const auto identifier = GetIdentifier(port, type);
290 if (value.charging != 0) {
291 SetBattery(identifier, Common::Input::BatteryLevel::Charging);
292 return;
293 }
294
295 Common::Input::BatteryLevel battery{};
296 switch (value.status) {
297 case 0:
298 battery = Common::Input::BatteryLevel::Empty;
299 break;
300 case 1:
301 battery = Common::Input::BatteryLevel::Critical;
302 break;
303 case 2:
304 battery = Common::Input::BatteryLevel::Low;
305 break;
306 case 3:
307 battery = Common::Input::BatteryLevel::Medium;
308 break;
309 case 4:
310 default:
311 battery = Common::Input::BatteryLevel::Full;
312 break;
313 }
314 SetBattery(identifier, battery);
315}
316
317void Joycons::OnColorUpdate(std::size_t port, Joycon::ControllerType type,
318 const Joycon::Color& value) {
319 const auto identifier = GetIdentifier(port, type);
320 Common::Input::BodyColorStatus color{
321 .body = value.body,
322 .buttons = value.buttons,
323 .left_grip = value.left_grip,
324 .right_grip = value.right_grip,
325 };
326 SetColor(identifier, color);
327}
328
329void Joycons::OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value) {
330 const auto identifier = GetIdentifier(port, type);
331 SetButton(identifier, id, value);
332}
333
334void Joycons::OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value) {
335 const auto identifier = GetIdentifier(port, type);
336 SetAxis(identifier, id, value);
337}
338
339void Joycons::OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
340 const Joycon::MotionData& value) {
341 const auto identifier = GetIdentifier(port, type);
342 BasicMotion motion_data{
343 .gyro_x = value.gyro_x,
344 .gyro_y = value.gyro_y,
345 .gyro_z = value.gyro_z,
346 .accel_x = value.accel_x,
347 .accel_y = value.accel_y,
348 .accel_z = value.accel_z,
349 .delta_timestamp = 15000,
350 };
351 SetMotion(identifier, id, motion_data);
352}
353
354void Joycons::OnRingConUpdate(f32 ring_data) {
355 // To simplify ring detection it will always be mapped to an empty identifier for all
356 // controllers
357 constexpr PadIdentifier identifier = {
358 .guid = Common::UUID{},
359 .port = 0,
360 .pad = 0,
361 };
362 SetAxis(identifier, 100, ring_data);
363}
364
365void Joycons::OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data) {
366 const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
367 const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved
368 : Common::Input::NfcState::NewAmiibo;
369 SetNfc(identifier, {nfc_state, amiibo_data});
370}
371
372void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
373 Joycon::IrsResolution format) {
374 const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
375 SetCamera(identifier, {static_cast<Common::Input::CameraFormat>(format), camera_data});
376}
377
378std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifier) const {
379 auto is_handle_active = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
380 if (!device) {
381 return false;
382 }
383 if (!device->IsConnected()) {
384 return false;
385 }
386 if (device->GetDevicePort() == identifier.port) {
387 return true;
388 }
389 return false;
390 };
391 const auto type = static_cast<Joycon::ControllerType>(identifier.pad);
392
393 if (type == Joycon::ControllerType::Left) {
394 const auto matching_device = std::ranges::find_if(
395 left_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
396
397 if (matching_device != left_joycons.end()) {
398 return *matching_device;
399 }
400 }
401
402 if (type == Joycon::ControllerType::Right) {
403 const auto matching_device = std::ranges::find_if(
404 right_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
405
406 if (matching_device != right_joycons.end()) {
407 return *matching_device;
408 }
409 }
410
411 return nullptr;
412}
413
414PadIdentifier Joycons::GetIdentifier(std::size_t port, Joycon::ControllerType type) const {
415 const std::array<u8, 16> guid{0, 0, 0, 0, 0, 0, 0, 0,
416 0, 0, 0, 0, 0, 0, 0, static_cast<u8>(type)};
417 return {
418 .guid = Common::UUID{guid},
419 .port = port,
420 .pad = static_cast<std::size_t>(type),
421 };
422}
423
424Common::ParamPackage Joycons::GetParamPackage(std::size_t port, Joycon::ControllerType type) const {
425 const auto identifier = GetIdentifier(port, type);
426 return {
427 {"engine", GetEngineName()},
428 {"guid", identifier.guid.RawString()},
429 {"port", std::to_string(identifier.port)},
430 {"pad", std::to_string(identifier.pad)},
431 };
432}
433
434std::vector<Common::ParamPackage> Joycons::GetInputDevices() const {
435 std::vector<Common::ParamPackage> devices{};
436
437 auto add_entry = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
438 if (!device) {
439 return;
440 }
441 if (!device->IsConnected()) {
442 return;
443 }
444 auto param = GetParamPackage(device->GetDevicePort(), device->GetHandleDeviceType());
445 std::string name = fmt::format("{} {}", JoyconName(device->GetHandleDeviceType()),
446 device->GetDevicePort() + 1);
447 param.Set("display", std::move(name));
448 devices.emplace_back(param);
449 };
450
451 for (const auto& controller : left_joycons) {
452 add_entry(controller);
453 }
454 for (const auto& controller : right_joycons) {
455 add_entry(controller);
456 }
457
458 // List dual joycon pairs
459 for (std::size_t i = 0; i < MaxSupportedControllers; i++) {
460 if (!left_joycons[i] || !right_joycons[i]) {
461 continue;
462 }
463 if (!left_joycons[i]->IsConnected() || !right_joycons[i]->IsConnected()) {
464 continue;
465 }
466 auto main_param = GetParamPackage(i, left_joycons[i]->GetHandleDeviceType());
467 const auto second_param = GetParamPackage(i, right_joycons[i]->GetHandleDeviceType());
468 const auto type = Joycon::ControllerType::Dual;
469 std::string name = fmt::format("{} {}", JoyconName(type), i + 1);
470
471 main_param.Set("display", std::move(name));
472 main_param.Set("guid2", second_param.Get("guid", ""));
473 main_param.Set("pad", std::to_string(static_cast<size_t>(type)));
474 devices.emplace_back(main_param);
475 }
476
477 return devices;
478}
479
480ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& params) {
481 static constexpr std::array<std::tuple<Settings::NativeButton::Values, Joycon::PadButton, bool>,
482 18>
483 switch_to_joycon_button = {
484 std::tuple{Settings::NativeButton::A, Joycon::PadButton::A, true},
485 {Settings::NativeButton::B, Joycon::PadButton::B, true},
486 {Settings::NativeButton::X, Joycon::PadButton::X, true},
487 {Settings::NativeButton::Y, Joycon::PadButton::Y, true},
488 {Settings::NativeButton::DLeft, Joycon::PadButton::Left, false},
489 {Settings::NativeButton::DUp, Joycon::PadButton::Up, false},
490 {Settings::NativeButton::DRight, Joycon::PadButton::Right, false},
491 {Settings::NativeButton::DDown, Joycon::PadButton::Down, false},
492 {Settings::NativeButton::L, Joycon::PadButton::L, false},
493 {Settings::NativeButton::R, Joycon::PadButton::R, true},
494 {Settings::NativeButton::ZL, Joycon::PadButton::ZL, false},
495 {Settings::NativeButton::ZR, Joycon::PadButton::ZR, true},
496 {Settings::NativeButton::Plus, Joycon::PadButton::Plus, true},
497 {Settings::NativeButton::Minus, Joycon::PadButton::Minus, false},
498 {Settings::NativeButton::Home, Joycon::PadButton::Home, true},
499 {Settings::NativeButton::Screenshot, Joycon::PadButton::Capture, false},
500 {Settings::NativeButton::LStick, Joycon::PadButton::StickL, false},
501 {Settings::NativeButton::RStick, Joycon::PadButton::StickR, true},
502 };
503
504 if (!params.Has("port")) {
505 return {};
506 }
507
508 ButtonMapping mapping{};
509 for (const auto& [switch_button, joycon_button, side] : switch_to_joycon_button) {
510 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
511 auto pad = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
512 if (pad == Joycon::ControllerType::Dual) {
513 pad = side ? Joycon::ControllerType::Right : Joycon::ControllerType::Left;
514 }
515
516 Common::ParamPackage button_params = GetParamPackage(port, pad);
517 button_params.Set("button", static_cast<int>(joycon_button));
518 mapping.insert_or_assign(switch_button, std::move(button_params));
519 }
520
521 // Map SL and SR buttons for left joycons
522 if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Left)) {
523 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
524 Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Left);
525
526 Common::ParamPackage sl_button_params = button_params;
527 Common::ParamPackage sr_button_params = button_params;
528 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL));
529 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR));
530 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
531 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
532 }
533
534 // Map SL and SR buttons for right joycons
535 if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Right)) {
536 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
537 Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Right);
538
539 Common::ParamPackage sl_button_params = button_params;
540 Common::ParamPackage sr_button_params = button_params;
541 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL));
542 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR));
543 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
544 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
545 }
546
547 return mapping;
548}
549
550AnalogMapping Joycons::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
551 if (!params.Has("port")) {
552 return {};
553 }
554
555 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
556 auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
557 auto pad_right = pad_left;
558 if (pad_left == Joycon::ControllerType::Dual) {
559 pad_left = Joycon::ControllerType::Left;
560 pad_right = Joycon::ControllerType::Right;
561 }
562
563 AnalogMapping mapping = {};
564 Common::ParamPackage left_analog_params = GetParamPackage(port, pad_left);
565 left_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::LeftStickX));
566 left_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::LeftStickY));
567 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
568 Common::ParamPackage right_analog_params = GetParamPackage(port, pad_right);
569 right_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::RightStickX));
570 right_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::RightStickY));
571 mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
572 return mapping;
573}
574
575MotionMapping Joycons::GetMotionMappingForDevice(const Common::ParamPackage& params) {
576 if (!params.Has("port")) {
577 return {};
578 }
579
580 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
581 auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
582 auto pad_right = pad_left;
583 if (pad_left == Joycon::ControllerType::Dual) {
584 pad_left = Joycon::ControllerType::Left;
585 pad_right = Joycon::ControllerType::Right;
586 }
587
588 MotionMapping mapping = {};
589 Common::ParamPackage left_motion_params = GetParamPackage(port, pad_left);
590 left_motion_params.Set("motion", 0);
591 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(left_motion_params));
592 Common::ParamPackage right_Motion_params = GetParamPackage(port, pad_right);
593 right_Motion_params.Set("motion", 1);
594 mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(right_Motion_params));
595 return mapping;
596}
597
598Common::Input::ButtonNames Joycons::GetUIButtonName(const Common::ParamPackage& params) const {
599 const auto button = static_cast<Joycon::PadButton>(params.Get("button", 0));
600 switch (button) {
601 case Joycon::PadButton::Left:
602 return Common::Input::ButtonNames::ButtonLeft;
603 case Joycon::PadButton::Right:
604 return Common::Input::ButtonNames::ButtonRight;
605 case Joycon::PadButton::Down:
606 return Common::Input::ButtonNames::ButtonDown;
607 case Joycon::PadButton::Up:
608 return Common::Input::ButtonNames::ButtonUp;
609 case Joycon::PadButton::LeftSL:
610 case Joycon::PadButton::RightSL:
611 return Common::Input::ButtonNames::TriggerSL;
612 case Joycon::PadButton::LeftSR:
613 case Joycon::PadButton::RightSR:
614 return Common::Input::ButtonNames::TriggerSR;
615 case Joycon::PadButton::L:
616 return Common::Input::ButtonNames::TriggerL;
617 case Joycon::PadButton::R:
618 return Common::Input::ButtonNames::TriggerR;
619 case Joycon::PadButton::ZL:
620 return Common::Input::ButtonNames::TriggerZL;
621 case Joycon::PadButton::ZR:
622 return Common::Input::ButtonNames::TriggerZR;
623 case Joycon::PadButton::A:
624 return Common::Input::ButtonNames::ButtonA;
625 case Joycon::PadButton::B:
626 return Common::Input::ButtonNames::ButtonB;
627 case Joycon::PadButton::X:
628 return Common::Input::ButtonNames::ButtonX;
629 case Joycon::PadButton::Y:
630 return Common::Input::ButtonNames::ButtonY;
631 case Joycon::PadButton::Plus:
632 return Common::Input::ButtonNames::ButtonPlus;
633 case Joycon::PadButton::Minus:
634 return Common::Input::ButtonNames::ButtonMinus;
635 case Joycon::PadButton::Home:
636 return Common::Input::ButtonNames::ButtonHome;
637 case Joycon::PadButton::Capture:
638 return Common::Input::ButtonNames::ButtonCapture;
639 case Joycon::PadButton::StickL:
640 return Common::Input::ButtonNames::ButtonStickL;
641 case Joycon::PadButton::StickR:
642 return Common::Input::ButtonNames::ButtonStickR;
643 default:
644 return Common::Input::ButtonNames::Undefined;
645 }
646}
647
648Common::Input::ButtonNames Joycons::GetUIName(const Common::ParamPackage& params) const {
649 if (params.Has("button")) {
650 return GetUIButtonName(params);
651 }
652 if (params.Has("axis")) {
653 return Common::Input::ButtonNames::Value;
654 }
655 if (params.Has("motion")) {
656 return Common::Input::ButtonNames::Engine;
657 }
658
659 return Common::Input::ButtonNames::Invalid;
660}
661
662std::string Joycons::JoyconName(Joycon::ControllerType type) const {
663 switch (type) {
664 case Joycon::ControllerType::Left:
665 return "Left Joycon";
666 case Joycon::ControllerType::Right:
667 return "Right Joycon";
668 case Joycon::ControllerType::Pro:
669 return "Pro Controller";
670 case Joycon::ControllerType::Grip:
671 return "Grip Controller";
672 case Joycon::ControllerType::Dual:
673 return "Dual Joycon";
674 default:
675 return "Unknown Joycon";
676 }
677}
678} // namespace InputCommon
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
new file mode 100644
index 000000000..316d383d8
--- /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;
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 9835d99d2..d975eb815 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -334,6 +334,15 @@ void SDLDriver::InitJoystick(int joystick_index) {
334 334
335 const auto guid = GetGUID(sdl_joystick); 335 const auto guid = GetGUID(sdl_joystick);
336 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
337 std::scoped_lock lock{joystick_map_mutex}; 346 std::scoped_lock lock{joystick_map_mutex};
338 if (joystick_map.find(guid) == joystick_map.end()) { 347 if (joystick_map.find(guid) == joystick_map.end()) {
339 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);
@@ -456,9 +465,13 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
456 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); 465 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
457 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); 466 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
458 467
459 // 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
460 // not a generic one 469 if (Settings::values.enable_joycon_driver) {
461 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");
462 475
463 // 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
464 // driver on Linux. 477 // driver on Linux.
@@ -548,7 +561,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
548 return devices; 561 return devices;
549} 562}
550 563
551Common::Input::VibrationError SDLDriver::SetVibration( 564Common::Input::DriverResult SDLDriver::SetVibration(
552 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 565 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
553 const auto joystick = 566 const auto joystick =
554 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); 567 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
@@ -582,7 +595,7 @@ Common::Input::VibrationError SDLDriver::SetVibration(
582 .vibration = new_vibration, 595 .vibration = new_vibration,
583 }); 596 });
584 597
585 return Common::Input::VibrationError::None; 598 return Common::Input::DriverResult::Success;
586} 599}
587 600
588bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) { 601bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) {
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/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;