summaryrefslogtreecommitdiff
path: root/src/input_common/drivers/joycon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common/drivers/joycon.cpp')
-rw-r--r--src/input_common/drivers/joycon.cpp844
1 files changed, 844 insertions, 0 deletions
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
new file mode 100644
index 000000000..52494e0d9
--- /dev/null
+++ b/src/input_common/drivers/joycon.cpp
@@ -0,0 +1,844 @@
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 && !Settings::values.enable_procon_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 for (const auto& device : pro_controller) {
50 if (!device) {
51 continue;
52 }
53 device->Stop();
54 }
55 SDL_hid_exit();
56}
57
58void Joycons::Setup() {
59 u32 port = 0;
60 PreSetController(GetIdentifier(0, Joycon::ControllerType::None));
61 for (auto& device : left_joycons) {
62 PreSetController(GetIdentifier(port, Joycon::ControllerType::Left));
63 device = std::make_shared<Joycon::JoyconDriver>(port++);
64 }
65 port = 0;
66 for (auto& device : right_joycons) {
67 PreSetController(GetIdentifier(port, Joycon::ControllerType::Right));
68 device = std::make_shared<Joycon::JoyconDriver>(port++);
69 }
70 port = 0;
71 for (auto& device : pro_controller) {
72 PreSetController(GetIdentifier(port, Joycon::ControllerType::Pro));
73 device = std::make_shared<Joycon::JoyconDriver>(port++);
74 }
75
76 scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); });
77}
78
79void Joycons::ScanThread(std::stop_token stop_token) {
80 constexpr u16 nintendo_vendor_id = 0x057e;
81 Common::SetCurrentThreadName("JoyconScanThread");
82
83 do {
84 SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0);
85 SDL_hid_device_info* cur_dev = devs;
86
87 while (cur_dev) {
88 if (IsDeviceNew(cur_dev)) {
89 LOG_DEBUG(Input, "Device Found,type : {:04X} {:04X}", cur_dev->vendor_id,
90 cur_dev->product_id);
91 RegisterNewDevice(cur_dev);
92 }
93 cur_dev = cur_dev->next;
94 }
95
96 SDL_hid_free_enumeration(devs);
97 } while (Common::StoppableTimedWait(stop_token, std::chrono::seconds{5}));
98}
99
100bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {
101 Joycon::ControllerType type{};
102 Joycon::SerialNumber serial_number{};
103
104 const auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
105 if (result != Joycon::DriverResult::Success) {
106 return false;
107 }
108
109 const auto result2 = Joycon::JoyconDriver::GetSerialNumber(device_info, serial_number);
110 if (result2 != Joycon::DriverResult::Success) {
111 return false;
112 }
113
114 auto is_handle_identical = [serial_number](std::shared_ptr<Joycon::JoyconDriver> device) {
115 if (!device) {
116 return false;
117 }
118 if (!device->IsConnected()) {
119 return false;
120 }
121 if (device->GetHandleSerialNumber() != serial_number) {
122 return false;
123 }
124 return true;
125 };
126
127 // Check if device already exist
128 switch (type) {
129 case Joycon::ControllerType::Left:
130 if (!Settings::values.enable_joycon_driver) {
131 return false;
132 }
133 for (const auto& device : left_joycons) {
134 if (is_handle_identical(device)) {
135 return false;
136 }
137 }
138 break;
139 case Joycon::ControllerType::Right:
140 if (!Settings::values.enable_joycon_driver) {
141 return false;
142 }
143 for (const auto& device : right_joycons) {
144 if (is_handle_identical(device)) {
145 return false;
146 }
147 }
148 break;
149 case Joycon::ControllerType::Pro:
150 if (!Settings::values.enable_procon_driver) {
151 return false;
152 }
153 for (const auto& device : pro_controller) {
154 if (is_handle_identical(device)) {
155 return false;
156 }
157 }
158 break;
159 default:
160 return false;
161 }
162
163 return true;
164}
165
166void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
167 Joycon::ControllerType type{};
168 auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
169 auto handle = GetNextFreeHandle(type);
170 if (handle == nullptr) {
171 LOG_WARNING(Input, "No free handles available");
172 return;
173 }
174 if (result == Joycon::DriverResult::Success) {
175 result = handle->RequestDeviceAccess(device_info);
176 }
177 if (result == Joycon::DriverResult::Success) {
178 LOG_WARNING(Input, "Initialize device");
179
180 const std::size_t port = handle->GetDevicePort();
181 const Joycon::JoyconCallbacks callbacks{
182 .on_battery_data = {[this, port, type](Joycon::Battery value) {
183 OnBatteryUpdate(port, type, value);
184 }},
185 .on_color_data = {[this, port, type](Joycon::Color value) {
186 OnColorUpdate(port, type, value);
187 }},
188 .on_button_data = {[this, port, type](int id, bool value) {
189 OnButtonUpdate(port, type, id, value);
190 }},
191 .on_stick_data = {[this, port, type](int id, f32 value) {
192 OnStickUpdate(port, type, id, value);
193 }},
194 .on_motion_data = {[this, port, type](int id, const Joycon::MotionData& value) {
195 OnMotionUpdate(port, type, id, value);
196 }},
197 .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }},
198 .on_amiibo_data = {[this, port, type](const Joycon::TagInfo& tag_info) {
199 OnAmiiboUpdate(port, type, tag_info);
200 }},
201 .on_camera_data = {[this, port](const std::vector<u8>& camera_data,
202 Joycon::IrsResolution format) {
203 OnCameraUpdate(port, camera_data, format);
204 }},
205 };
206
207 handle->InitializeDevice();
208 handle->SetCallbacks(callbacks);
209 }
210}
211
212std::shared_ptr<Joycon::JoyconDriver> Joycons::GetNextFreeHandle(
213 Joycon::ControllerType type) const {
214 if (type == Joycon::ControllerType::Left) {
215 const auto unconnected_device =
216 std::ranges::find_if(left_joycons, [](auto& device) { return !device->IsConnected(); });
217 if (unconnected_device != left_joycons.end()) {
218 return *unconnected_device;
219 }
220 }
221 if (type == Joycon::ControllerType::Right) {
222 const auto unconnected_device = std::ranges::find_if(
223 right_joycons, [](auto& device) { return !device->IsConnected(); });
224
225 if (unconnected_device != right_joycons.end()) {
226 return *unconnected_device;
227 }
228 }
229 if (type == Joycon::ControllerType::Pro) {
230 const auto unconnected_device = std::ranges::find_if(
231 pro_controller, [](auto& device) { return !device->IsConnected(); });
232
233 if (unconnected_device != pro_controller.end()) {
234 return *unconnected_device;
235 }
236 }
237 return nullptr;
238}
239
240bool Joycons::IsVibrationEnabled(const PadIdentifier& identifier) {
241 const auto handle = GetHandle(identifier);
242 if (handle == nullptr) {
243 return false;
244 }
245 return handle->IsVibrationEnabled();
246}
247
248Common::Input::DriverResult Joycons::SetVibration(const PadIdentifier& identifier,
249 const Common::Input::VibrationStatus& vibration) {
250 const Joycon::VibrationValue native_vibration{
251 .low_amplitude = vibration.low_amplitude,
252 .low_frequency = vibration.low_frequency,
253 .high_amplitude = vibration.high_amplitude,
254 .high_frequency = vibration.high_frequency,
255 };
256 auto handle = GetHandle(identifier);
257 if (handle == nullptr) {
258 return Common::Input::DriverResult::InvalidHandle;
259 }
260
261 handle->SetVibration(native_vibration);
262 return Common::Input::DriverResult::Success;
263}
264
265Common::Input::DriverResult Joycons::SetLeds(const PadIdentifier& identifier,
266 const Common::Input::LedStatus& led_status) {
267 auto handle = GetHandle(identifier);
268 if (handle == nullptr) {
269 return Common::Input::DriverResult::InvalidHandle;
270 }
271 int led_config = led_status.led_1 ? 1 : 0;
272 led_config += led_status.led_2 ? 2 : 0;
273 led_config += led_status.led_3 ? 4 : 0;
274 led_config += led_status.led_4 ? 8 : 0;
275
276 return static_cast<Common::Input::DriverResult>(
277 handle->SetLedConfig(static_cast<u8>(led_config)));
278}
279
280Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier,
281 Common::Input::CameraFormat camera_format) {
282 auto handle = GetHandle(identifier);
283 if (handle == nullptr) {
284 return Common::Input::DriverResult::InvalidHandle;
285 }
286 return static_cast<Common::Input::DriverResult>(handle->SetIrsConfig(
287 Joycon::IrsMode::ImageTransfer, static_cast<Joycon::IrsResolution>(camera_format)));
288};
289
290Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const {
291 return Common::Input::NfcState::Success;
292};
293
294Common::Input::NfcState Joycons::StartNfcPolling(const PadIdentifier& identifier) {
295 auto handle = GetHandle(identifier);
296 if (handle == nullptr) {
297 return Common::Input::NfcState::Unknown;
298 }
299 return TranslateDriverResult(handle->StartNfcPolling());
300};
301
302Common::Input::NfcState Joycons::StopNfcPolling(const PadIdentifier& identifier) {
303 auto handle = GetHandle(identifier);
304 if (handle == nullptr) {
305 return Common::Input::NfcState::Unknown;
306 }
307 return TranslateDriverResult(handle->StopNfcPolling());
308};
309
310Common::Input::NfcState Joycons::ReadAmiiboData(const PadIdentifier& identifier,
311 std::vector<u8>& out_data) {
312 auto handle = GetHandle(identifier);
313 if (handle == nullptr) {
314 return Common::Input::NfcState::Unknown;
315 }
316 return TranslateDriverResult(handle->ReadAmiiboData(out_data));
317}
318
319Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier,
320 const std::vector<u8>& data) {
321 auto handle = GetHandle(identifier);
322 if (handle == nullptr) {
323 return Common::Input::NfcState::Unknown;
324 }
325 return TranslateDriverResult(handle->WriteNfcData(data));
326};
327
328Common::Input::NfcState Joycons::ReadMifareData(const PadIdentifier& identifier,
329 const Common::Input::MifareRequest& request,
330 Common::Input::MifareRequest& data) {
331 auto handle = GetHandle(identifier);
332 if (handle == nullptr) {
333 return Common::Input::NfcState::Unknown;
334 }
335
336 const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command);
337 std::vector<Joycon::MifareReadChunk> read_request{};
338 for (const auto& request_data : request.data) {
339 if (request_data.command == 0) {
340 continue;
341 }
342 Joycon::MifareReadChunk chunk = {
343 .command = command,
344 .sector_key = {},
345 .sector = request_data.sector,
346 };
347 memcpy(chunk.sector_key.data(), request_data.key.data(),
348 sizeof(Joycon::MifareReadChunk::sector_key));
349 read_request.emplace_back(chunk);
350 }
351
352 std::vector<Joycon::MifareReadData> read_data(read_request.size());
353 const auto result = handle->ReadMifareData(read_request, read_data);
354 if (result == Joycon::DriverResult::Success) {
355 for (std::size_t i = 0; i < read_request.size(); i++) {
356 data.data[i] = {
357 .command = static_cast<u8>(command),
358 .sector = read_data[i].sector,
359 .key = {},
360 .data = read_data[i].data,
361 };
362 }
363 }
364 return TranslateDriverResult(result);
365};
366
367Common::Input::NfcState Joycons::WriteMifareData(const PadIdentifier& identifier,
368 const Common::Input::MifareRequest& request) {
369 auto handle = GetHandle(identifier);
370 if (handle == nullptr) {
371 return Common::Input::NfcState::Unknown;
372 }
373
374 const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command);
375 std::vector<Joycon::MifareWriteChunk> write_request{};
376 for (const auto& request_data : request.data) {
377 if (request_data.command == 0) {
378 continue;
379 }
380 Joycon::MifareWriteChunk chunk = {
381 .command = command,
382 .sector_key = {},
383 .sector = request_data.sector,
384 .data = {},
385 };
386 memcpy(chunk.sector_key.data(), request_data.key.data(),
387 sizeof(Joycon::MifareReadChunk::sector_key));
388 memcpy(chunk.data.data(), request_data.data.data(), sizeof(Joycon::MifareWriteChunk::data));
389 write_request.emplace_back(chunk);
390 }
391
392 return TranslateDriverResult(handle->WriteMifareData(write_request));
393};
394
395Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
396 const Common::Input::PollingMode polling_mode) {
397 auto handle = GetHandle(identifier);
398 if (handle == nullptr) {
399 LOG_ERROR(Input, "Invalid handle {}", identifier.port);
400 return Common::Input::DriverResult::InvalidHandle;
401 }
402
403 switch (polling_mode) {
404 case Common::Input::PollingMode::Active:
405 return static_cast<Common::Input::DriverResult>(handle->SetActiveMode());
406 case Common::Input::PollingMode::Passive:
407 return static_cast<Common::Input::DriverResult>(handle->SetPassiveMode());
408 case Common::Input::PollingMode::IR:
409 return static_cast<Common::Input::DriverResult>(handle->SetIrMode());
410 case Common::Input::PollingMode::NFC:
411 return static_cast<Common::Input::DriverResult>(handle->SetNfcMode());
412 case Common::Input::PollingMode::Ring:
413 return static_cast<Common::Input::DriverResult>(handle->SetRingConMode());
414 default:
415 return Common::Input::DriverResult::NotSupported;
416 }
417}
418
419void Joycons::OnBatteryUpdate(std::size_t port, Joycon::ControllerType type,
420 Joycon::Battery value) {
421 const auto identifier = GetIdentifier(port, type);
422 if (value.charging != 0) {
423 SetBattery(identifier, Common::Input::BatteryLevel::Charging);
424 return;
425 }
426
427 Common::Input::BatteryLevel battery{};
428 switch (value.status) {
429 case 0:
430 battery = Common::Input::BatteryLevel::Empty;
431 break;
432 case 1:
433 battery = Common::Input::BatteryLevel::Critical;
434 break;
435 case 2:
436 battery = Common::Input::BatteryLevel::Low;
437 break;
438 case 3:
439 battery = Common::Input::BatteryLevel::Medium;
440 break;
441 case 4:
442 default:
443 battery = Common::Input::BatteryLevel::Full;
444 break;
445 }
446 SetBattery(identifier, battery);
447}
448
449void Joycons::OnColorUpdate(std::size_t port, Joycon::ControllerType type,
450 const Joycon::Color& value) {
451 const auto identifier = GetIdentifier(port, type);
452 Common::Input::BodyColorStatus color{
453 .body = value.body,
454 .buttons = value.buttons,
455 .left_grip = value.left_grip,
456 .right_grip = value.right_grip,
457 };
458 SetColor(identifier, color);
459}
460
461void Joycons::OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value) {
462 const auto identifier = GetIdentifier(port, type);
463 SetButton(identifier, id, value);
464}
465
466void Joycons::OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value) {
467 const auto identifier = GetIdentifier(port, type);
468 SetAxis(identifier, id, value);
469}
470
471void Joycons::OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
472 const Joycon::MotionData& value) {
473 const auto identifier = GetIdentifier(port, type);
474 BasicMotion motion_data{
475 .gyro_x = value.gyro_x,
476 .gyro_y = value.gyro_y,
477 .gyro_z = value.gyro_z,
478 .accel_x = value.accel_x,
479 .accel_y = value.accel_y,
480 .accel_z = value.accel_z,
481 .delta_timestamp = 15000,
482 };
483 SetMotion(identifier, id, motion_data);
484}
485
486void Joycons::OnRingConUpdate(f32 ring_data) {
487 // To simplify ring detection it will always be mapped to an empty identifier for all
488 // controllers
489 static constexpr PadIdentifier identifier = {
490 .guid = Common::UUID{},
491 .port = 0,
492 .pad = 0,
493 };
494 SetAxis(identifier, 100, ring_data);
495}
496
497void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
498 const Joycon::TagInfo& tag_info) {
499 const auto identifier = GetIdentifier(port, type);
500 const auto nfc_state = tag_info.uuid_length == 0 ? Common::Input::NfcState::AmiiboRemoved
501 : Common::Input::NfcState::NewAmiibo;
502
503 const Common::Input::NfcStatus nfc_status{
504 .state = nfc_state,
505 .uuid_length = tag_info.uuid_length,
506 .protocol = tag_info.protocol,
507 .tag_type = tag_info.tag_type,
508 .uuid = tag_info.uuid,
509 };
510
511 SetNfc(identifier, nfc_status);
512}
513
514void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
515 Joycon::IrsResolution format) {
516 const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
517 SetCamera(identifier, {static_cast<Common::Input::CameraFormat>(format), camera_data});
518}
519
520std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifier) const {
521 auto is_handle_active = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
522 if (!device) {
523 return false;
524 }
525 if (!device->IsConnected()) {
526 return false;
527 }
528 if (device->GetDevicePort() == identifier.port) {
529 return true;
530 }
531 return false;
532 };
533 const auto type = static_cast<Joycon::ControllerType>(identifier.pad);
534
535 if (type == Joycon::ControllerType::Left) {
536 const auto matching_device = std::ranges::find_if(
537 left_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
538
539 if (matching_device != left_joycons.end()) {
540 return *matching_device;
541 }
542 }
543
544 if (type == Joycon::ControllerType::Right) {
545 const auto matching_device = std::ranges::find_if(
546 right_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
547
548 if (matching_device != right_joycons.end()) {
549 return *matching_device;
550 }
551 }
552
553 if (type == Joycon::ControllerType::Pro) {
554 const auto matching_device = std::ranges::find_if(
555 pro_controller, [is_handle_active](auto& device) { return is_handle_active(device); });
556
557 if (matching_device != pro_controller.end()) {
558 return *matching_device;
559 }
560 }
561
562 return nullptr;
563}
564
565PadIdentifier Joycons::GetIdentifier(std::size_t port, Joycon::ControllerType type) const {
566 const std::array<u8, 16> guid{0, 0, 0, 0, 0, 0, 0, 0,
567 0, 0, 0, 0, 0, 0, 0, static_cast<u8>(type)};
568 return {
569 .guid = Common::UUID{guid},
570 .port = port,
571 .pad = static_cast<std::size_t>(type),
572 };
573}
574
575Common::ParamPackage Joycons::GetParamPackage(std::size_t port, Joycon::ControllerType type) const {
576 const auto identifier = GetIdentifier(port, type);
577 return {
578 {"engine", GetEngineName()},
579 {"guid", identifier.guid.RawString()},
580 {"port", std::to_string(identifier.port)},
581 {"pad", std::to_string(identifier.pad)},
582 };
583}
584
585std::vector<Common::ParamPackage> Joycons::GetInputDevices() const {
586 std::vector<Common::ParamPackage> devices{};
587
588 auto add_entry = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
589 if (!device) {
590 return;
591 }
592 if (!device->IsConnected()) {
593 return;
594 }
595 auto param = GetParamPackage(device->GetDevicePort(), device->GetHandleDeviceType());
596 std::string name = fmt::format("{} {}", JoyconName(device->GetHandleDeviceType()),
597 device->GetDevicePort() + 1);
598 param.Set("display", std::move(name));
599 devices.emplace_back(param);
600 };
601
602 for (const auto& controller : left_joycons) {
603 add_entry(controller);
604 }
605 for (const auto& controller : right_joycons) {
606 add_entry(controller);
607 }
608 for (const auto& controller : pro_controller) {
609 add_entry(controller);
610 }
611
612 // List dual joycon pairs
613 for (std::size_t i = 0; i < MaxSupportedControllers; i++) {
614 if (!left_joycons[i] || !right_joycons[i]) {
615 continue;
616 }
617 if (!left_joycons[i]->IsConnected() || !right_joycons[i]->IsConnected()) {
618 continue;
619 }
620 auto main_param = GetParamPackage(i, left_joycons[i]->GetHandleDeviceType());
621 const auto second_param = GetParamPackage(i, right_joycons[i]->GetHandleDeviceType());
622 const auto type = Joycon::ControllerType::Dual;
623 std::string name = fmt::format("{} {}", JoyconName(type), i + 1);
624
625 main_param.Set("display", std::move(name));
626 main_param.Set("guid2", second_param.Get("guid", ""));
627 main_param.Set("pad", std::to_string(static_cast<size_t>(type)));
628 devices.emplace_back(main_param);
629 }
630
631 return devices;
632}
633
634ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& params) {
635 static constexpr std::array<std::tuple<Settings::NativeButton::Values, Joycon::PadButton, bool>,
636 18>
637 switch_to_joycon_button = {
638 std::tuple{Settings::NativeButton::A, Joycon::PadButton::A, true},
639 {Settings::NativeButton::B, Joycon::PadButton::B, true},
640 {Settings::NativeButton::X, Joycon::PadButton::X, true},
641 {Settings::NativeButton::Y, Joycon::PadButton::Y, true},
642 {Settings::NativeButton::DLeft, Joycon::PadButton::Left, false},
643 {Settings::NativeButton::DUp, Joycon::PadButton::Up, false},
644 {Settings::NativeButton::DRight, Joycon::PadButton::Right, false},
645 {Settings::NativeButton::DDown, Joycon::PadButton::Down, false},
646 {Settings::NativeButton::L, Joycon::PadButton::L, false},
647 {Settings::NativeButton::R, Joycon::PadButton::R, true},
648 {Settings::NativeButton::ZL, Joycon::PadButton::ZL, false},
649 {Settings::NativeButton::ZR, Joycon::PadButton::ZR, true},
650 {Settings::NativeButton::Plus, Joycon::PadButton::Plus, true},
651 {Settings::NativeButton::Minus, Joycon::PadButton::Minus, false},
652 {Settings::NativeButton::Home, Joycon::PadButton::Home, true},
653 {Settings::NativeButton::Screenshot, Joycon::PadButton::Capture, false},
654 {Settings::NativeButton::LStick, Joycon::PadButton::StickL, false},
655 {Settings::NativeButton::RStick, Joycon::PadButton::StickR, true},
656 };
657
658 if (!params.Has("port")) {
659 return {};
660 }
661
662 ButtonMapping mapping{};
663 for (const auto& [switch_button, joycon_button, side] : switch_to_joycon_button) {
664 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
665 auto pad = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
666 if (pad == Joycon::ControllerType::Dual) {
667 pad = side ? Joycon::ControllerType::Right : Joycon::ControllerType::Left;
668 }
669
670 Common::ParamPackage button_params = GetParamPackage(port, pad);
671 button_params.Set("button", static_cast<int>(joycon_button));
672 mapping.insert_or_assign(switch_button, std::move(button_params));
673 }
674
675 // Map SL and SR buttons for left joycons
676 if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Left)) {
677 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
678 Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Left);
679
680 Common::ParamPackage sl_button_params = button_params;
681 Common::ParamPackage sr_button_params = button_params;
682 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL));
683 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR));
684 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
685 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
686 }
687
688 // Map SL and SR buttons for right joycons
689 if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Right)) {
690 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
691 Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Right);
692
693 Common::ParamPackage sl_button_params = button_params;
694 Common::ParamPackage sr_button_params = button_params;
695 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL));
696 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR));
697 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
698 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
699 }
700
701 return mapping;
702}
703
704AnalogMapping Joycons::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
705 if (!params.Has("port")) {
706 return {};
707 }
708
709 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
710 auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
711 auto pad_right = pad_left;
712 if (pad_left == Joycon::ControllerType::Dual) {
713 pad_left = Joycon::ControllerType::Left;
714 pad_right = Joycon::ControllerType::Right;
715 }
716
717 AnalogMapping mapping = {};
718 Common::ParamPackage left_analog_params = GetParamPackage(port, pad_left);
719 left_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::LeftStickX));
720 left_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::LeftStickY));
721 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
722 Common::ParamPackage right_analog_params = GetParamPackage(port, pad_right);
723 right_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::RightStickX));
724 right_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::RightStickY));
725 mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
726 return mapping;
727}
728
729MotionMapping Joycons::GetMotionMappingForDevice(const Common::ParamPackage& params) {
730 if (!params.Has("port")) {
731 return {};
732 }
733
734 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
735 auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
736 auto pad_right = pad_left;
737 if (pad_left == Joycon::ControllerType::Dual) {
738 pad_left = Joycon::ControllerType::Left;
739 pad_right = Joycon::ControllerType::Right;
740 }
741
742 MotionMapping mapping = {};
743 Common::ParamPackage left_motion_params = GetParamPackage(port, pad_left);
744 left_motion_params.Set("motion", 0);
745 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(left_motion_params));
746 Common::ParamPackage right_Motion_params = GetParamPackage(port, pad_right);
747 right_Motion_params.Set("motion", 1);
748 mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(right_Motion_params));
749 return mapping;
750}
751
752Common::Input::ButtonNames Joycons::GetUIButtonName(const Common::ParamPackage& params) const {
753 const auto button = static_cast<Joycon::PadButton>(params.Get("button", 0));
754 switch (button) {
755 case Joycon::PadButton::Left:
756 return Common::Input::ButtonNames::ButtonLeft;
757 case Joycon::PadButton::Right:
758 return Common::Input::ButtonNames::ButtonRight;
759 case Joycon::PadButton::Down:
760 return Common::Input::ButtonNames::ButtonDown;
761 case Joycon::PadButton::Up:
762 return Common::Input::ButtonNames::ButtonUp;
763 case Joycon::PadButton::LeftSL:
764 case Joycon::PadButton::RightSL:
765 return Common::Input::ButtonNames::TriggerSL;
766 case Joycon::PadButton::LeftSR:
767 case Joycon::PadButton::RightSR:
768 return Common::Input::ButtonNames::TriggerSR;
769 case Joycon::PadButton::L:
770 return Common::Input::ButtonNames::TriggerL;
771 case Joycon::PadButton::R:
772 return Common::Input::ButtonNames::TriggerR;
773 case Joycon::PadButton::ZL:
774 return Common::Input::ButtonNames::TriggerZL;
775 case Joycon::PadButton::ZR:
776 return Common::Input::ButtonNames::TriggerZR;
777 case Joycon::PadButton::A:
778 return Common::Input::ButtonNames::ButtonA;
779 case Joycon::PadButton::B:
780 return Common::Input::ButtonNames::ButtonB;
781 case Joycon::PadButton::X:
782 return Common::Input::ButtonNames::ButtonX;
783 case Joycon::PadButton::Y:
784 return Common::Input::ButtonNames::ButtonY;
785 case Joycon::PadButton::Plus:
786 return Common::Input::ButtonNames::ButtonPlus;
787 case Joycon::PadButton::Minus:
788 return Common::Input::ButtonNames::ButtonMinus;
789 case Joycon::PadButton::Home:
790 return Common::Input::ButtonNames::ButtonHome;
791 case Joycon::PadButton::Capture:
792 return Common::Input::ButtonNames::ButtonCapture;
793 case Joycon::PadButton::StickL:
794 return Common::Input::ButtonNames::ButtonStickL;
795 case Joycon::PadButton::StickR:
796 return Common::Input::ButtonNames::ButtonStickR;
797 default:
798 return Common::Input::ButtonNames::Undefined;
799 }
800}
801
802Common::Input::ButtonNames Joycons::GetUIName(const Common::ParamPackage& params) const {
803 if (params.Has("button")) {
804 return GetUIButtonName(params);
805 }
806 if (params.Has("axis")) {
807 return Common::Input::ButtonNames::Value;
808 }
809 if (params.Has("motion")) {
810 return Common::Input::ButtonNames::Engine;
811 }
812
813 return Common::Input::ButtonNames::Invalid;
814}
815
816std::string Joycons::JoyconName(Joycon::ControllerType type) const {
817 switch (type) {
818 case Joycon::ControllerType::Left:
819 return "Left Joycon";
820 case Joycon::ControllerType::Right:
821 return "Right Joycon";
822 case Joycon::ControllerType::Pro:
823 return "Pro Controller";
824 case Joycon::ControllerType::Dual:
825 return "Dual Joycon";
826 default:
827 return "Unknown Switch Controller";
828 }
829}
830
831Common::Input::NfcState Joycons::TranslateDriverResult(Joycon::DriverResult result) const {
832 switch (result) {
833 case Joycon::DriverResult::Success:
834 return Common::Input::NfcState::Success;
835 case Joycon::DriverResult::Disabled:
836 return Common::Input::NfcState::WrongDeviceState;
837 case Joycon::DriverResult::NotSupported:
838 return Common::Input::NfcState::NotSupported;
839 default:
840 return Common::Input::NfcState::Unknown;
841 }
842}
843
844} // namespace InputCommon