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