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