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.cpp12
-rw-r--r--src/input_common/drivers/gc_adapter.h2
-rw-r--r--src/input_common/drivers/joycon.cpp844
-rw-r--r--src/input_common/drivers/joycon.h125
-rw-r--r--src/input_common/drivers/keyboard.cpp2
-rw-r--r--src/input_common/drivers/mouse.cpp231
-rw-r--r--src/input_common/drivers/mouse.h43
-rw-r--r--src/input_common/drivers/sdl_driver.cpp129
-rw-r--r--src/input_common/drivers/sdl_driver.h2
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp179
-rw-r--r--src/input_common/drivers/virtual_amiibo.h23
-rw-r--r--src/input_common/drivers/virtual_gamepad.cpp16
-rw-r--r--src/input_common/drivers/virtual_gamepad.h12
15 files changed, 1487 insertions, 141 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..3ad34884d 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/param_package.h" 8#include "common/param_package.h"
9#include "common/polyfill_thread.h"
9#include "common/settings_input.h" 10#include "common/settings_input.h"
10#include "common/thread.h" 11#include "common/thread.h"
11#include "input_common/drivers/gc_adapter.h" 12#include "input_common/drivers/gc_adapter.h"
@@ -217,8 +218,7 @@ void GCAdapter::AdapterScanThread(std::stop_token stop_token) {
217 Common::SetCurrentThreadName("ScanGCAdapter"); 218 Common::SetCurrentThreadName("ScanGCAdapter");
218 usb_adapter_handle = nullptr; 219 usb_adapter_handle = nullptr;
219 pads = {}; 220 pads = {};
220 while (!stop_token.stop_requested() && !Setup()) { 221 while (!Setup() && Common::StoppableTimedWait(stop_token, std::chrono::seconds{2})) {
221 std::this_thread::sleep_for(std::chrono::seconds(2));
222 } 222 }
223} 223}
224 224
@@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) {
324 return true; 324 return true;
325} 325}
326 326
327Common::Input::VibrationError GCAdapter::SetVibration( 327Common::Input::DriverResult GCAdapter::SetVibration(
328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; 329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
330 const auto processed_amplitude = 330 const auto processed_amplitude =
@@ -333,9 +333,9 @@ Common::Input::VibrationError GCAdapter::SetVibration(
333 pads[identifier.port].rumble_amplitude = processed_amplitude; 333 pads[identifier.port].rumble_amplitude = processed_amplitude;
334 334
335 if (!rumble_enabled) { 335 if (!rumble_enabled) {
336 return Common::Input::VibrationError::Disabled; 336 return Common::Input::DriverResult::Disabled;
337 } 337 }
338 return Common::Input::VibrationError::None; 338 return Common::Input::DriverResult::Success;
339} 339}
340 340
341bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { 341bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
@@ -344,7 +344,7 @@ bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identif
344 344
345void GCAdapter::UpdateVibrations() { 345void GCAdapter::UpdateVibrations() {
346 // Use 8 states to keep the switching between on/off fast enough for 346 // Use 8 states to keep the switching between on/off fast enough for
347 // a human to feel different vibration strenght 347 // a human to feel different vibration strength
348 // More states == more rumble strengths == slower update time 348 // More states == more rumble strengths == slower update time
349 constexpr u8 vibration_states = 8; 349 constexpr u8 vibration_states = 8;
350 350
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..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
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
new file mode 100644
index 000000000..4c323d7d6
--- /dev/null
+++ b/src/input_common/drivers/joycon.h
@@ -0,0 +1,125 @@
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;
18struct TagInfo;
19enum class ControllerType : u8;
20enum class DriverResult;
21enum class IrsResolution;
22class JoyconDriver;
23} // namespace InputCommon::Joycon
24
25namespace InputCommon {
26
27class Joycons final : public InputCommon::InputEngine {
28public:
29 explicit Joycons(const std::string& input_engine_);
30
31 ~Joycons();
32
33 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
34 Common::Input::DriverResult SetVibration(
35 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
36
37 Common::Input::DriverResult SetLeds(const PadIdentifier& identifier,
38 const Common::Input::LedStatus& led_status) override;
39
40 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier,
41 Common::Input::CameraFormat camera_format) override;
42
43 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier) const override;
44 Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier) override;
45 Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier) override;
46 Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier,
47 std::vector<u8>& out_data) override;
48 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier,
49 const std::vector<u8>& data) override;
50 Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier,
51 const Common::Input::MifareRequest& request,
52 Common::Input::MifareRequest& out_data) override;
53 Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier,
54 const Common::Input::MifareRequest& request) override;
55
56 Common::Input::DriverResult SetPollingMode(
57 const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override;
58
59 /// Used for automapping features
60 std::vector<Common::ParamPackage> GetInputDevices() const override;
61 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
62 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
63 MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
64 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
65
66private:
67 static constexpr std::size_t MaxSupportedControllers = 8;
68
69 /// For shutting down, clear all data, join all threads, release usb devices
70 void Reset();
71
72 /// Registers controllers, clears all data and starts the scan thread
73 void Setup();
74
75 /// Actively searches for new devices
76 void ScanThread(std::stop_token stop_token);
77
78 /// Returns true if device is valid and not registered
79 bool IsDeviceNew(SDL_hid_device_info* device_info) const;
80
81 /// Tries to connect to the new device
82 void RegisterNewDevice(SDL_hid_device_info* device_info);
83
84 /// Returns the next free handle
85 std::shared_ptr<Joycon::JoyconDriver> GetNextFreeHandle(Joycon::ControllerType type) const;
86
87 void OnBatteryUpdate(std::size_t port, Joycon::ControllerType type, Joycon::Battery value);
88 void OnColorUpdate(std::size_t port, Joycon::ControllerType type, const Joycon::Color& value);
89 void OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value);
90 void OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value);
91 void OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
92 const Joycon::MotionData& value);
93 void OnRingConUpdate(f32 ring_data);
94 void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
95 const Joycon::TagInfo& amiibo_data);
96 void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
97 Joycon::IrsResolution format);
98
99 /// Returns a JoyconHandle corresponding to a PadIdentifier
100 std::shared_ptr<Joycon::JoyconDriver> GetHandle(PadIdentifier identifier) const;
101
102 /// Returns a PadIdentifier corresponding to the port number and joycon type
103 PadIdentifier GetIdentifier(std::size_t port, Joycon::ControllerType type) const;
104
105 /// Returns a ParamPackage corresponding to the port number and joycon type
106 Common::ParamPackage GetParamPackage(std::size_t port, Joycon::ControllerType type) const;
107
108 std::string JoyconName(std::size_t port) const;
109
110 Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
111
112 /// Returns the name of the device in text format
113 std::string JoyconName(Joycon::ControllerType type) const;
114
115 Common::Input::NfcState TranslateDriverResult(Joycon::DriverResult result) const;
116
117 std::jthread scan_thread;
118
119 // Joycon types are split by type to ease supporting dualjoycon configurations
120 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> left_joycons{};
121 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> right_joycons{};
122 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> pro_controller{};
123};
124
125} // namespace InputCommon
diff --git a/src/input_common/drivers/keyboard.cpp b/src/input_common/drivers/keyboard.cpp
index 71e612fbf..2567df9af 100644
--- a/src/input_common/drivers/keyboard.cpp
+++ b/src/input_common/drivers/keyboard.cpp
@@ -24,7 +24,7 @@ constexpr PadIdentifier keyboard_modifier_identifier = {
24}; 24};
25 25
26Keyboard::Keyboard(std::string input_engine_) : InputEngine(std::move(input_engine_)) { 26Keyboard::Keyboard(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
27 // Keyboard is broken into 3 diferent sets: 27 // Keyboard is broken into 3 different sets:
28 // key: Unfiltered intended for controllers. 28 // key: Unfiltered intended for controllers.
29 // keyboard_key: Allows only Settings::NativeKeyboard::Keys intended for keyboard emulation. 29 // keyboard_key: Allows only Settings::NativeKeyboard::Keys intended for keyboard emulation.
30 // keyboard_modifier: Allows only Settings::NativeKeyboard::Modifiers intended for keyboard 30 // keyboard_modifier: Allows only Settings::NativeKeyboard::Modifiers intended for keyboard
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp
index faf9cbdc3..f07cf8a0e 100644
--- a/src/input_common/drivers/mouse.cpp
+++ b/src/input_common/drivers/mouse.cpp
@@ -3,6 +3,7 @@
3 3
4#include <thread> 4#include <thread>
5#include <fmt/format.h> 5#include <fmt/format.h>
6#include <math.h>
6 7
7#include "common/param_package.h" 8#include "common/param_package.h"
8#include "common/settings.h" 9#include "common/settings.h"
@@ -10,135 +11,232 @@
10#include "input_common/drivers/mouse.h" 11#include "input_common/drivers/mouse.h"
11 12
12namespace InputCommon { 13namespace InputCommon {
14constexpr int update_time = 10;
15constexpr float default_stick_sensitivity = 0.0044f;
16constexpr float default_motion_sensitivity = 0.0003f;
17constexpr float maximum_rotation_speed = 2.0f;
13constexpr int mouse_axis_x = 0; 18constexpr int mouse_axis_x = 0;
14constexpr int mouse_axis_y = 1; 19constexpr int mouse_axis_y = 1;
15constexpr int wheel_axis_x = 2; 20constexpr int wheel_axis_x = 2;
16constexpr int wheel_axis_y = 3; 21constexpr int wheel_axis_y = 3;
17constexpr int motion_wheel_y = 4;
18constexpr int touch_axis_x = 10;
19constexpr int touch_axis_y = 11;
20constexpr PadIdentifier identifier = { 22constexpr PadIdentifier identifier = {
21 .guid = Common::UUID{}, 23 .guid = Common::UUID{},
22 .port = 0, 24 .port = 0,
23 .pad = 0, 25 .pad = 0,
24}; 26};
25 27
28constexpr PadIdentifier motion_identifier = {
29 .guid = Common::UUID{},
30 .port = 0,
31 .pad = 1,
32};
33
34constexpr PadIdentifier real_mouse_identifier = {
35 .guid = Common::UUID{},
36 .port = 1,
37 .pad = 0,
38};
39
40constexpr PadIdentifier touch_identifier = {
41 .guid = Common::UUID{},
42 .port = 2,
43 .pad = 0,
44};
45
26Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) { 46Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
27 PreSetController(identifier); 47 PreSetController(identifier);
48 PreSetController(real_mouse_identifier);
49 PreSetController(touch_identifier);
50 PreSetController(motion_identifier);
51
52 // Initialize all mouse axis
28 PreSetAxis(identifier, mouse_axis_x); 53 PreSetAxis(identifier, mouse_axis_x);
29 PreSetAxis(identifier, mouse_axis_y); 54 PreSetAxis(identifier, mouse_axis_y);
30 PreSetAxis(identifier, wheel_axis_x); 55 PreSetAxis(identifier, wheel_axis_x);
31 PreSetAxis(identifier, wheel_axis_y); 56 PreSetAxis(identifier, wheel_axis_y);
32 PreSetAxis(identifier, motion_wheel_y); 57 PreSetAxis(real_mouse_identifier, mouse_axis_x);
33 PreSetAxis(identifier, touch_axis_x); 58 PreSetAxis(real_mouse_identifier, mouse_axis_y);
34 PreSetAxis(identifier, touch_axis_y); 59 PreSetAxis(touch_identifier, mouse_axis_x);
60 PreSetAxis(touch_identifier, mouse_axis_y);
61
62 // Initialize variables
63 mouse_origin = {};
64 last_mouse_position = {};
65 wheel_position = {};
66 last_mouse_change = {};
67 last_motion_change = {};
68
35 update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); }); 69 update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); });
36} 70}
37 71
38void Mouse::UpdateThread(std::stop_token stop_token) { 72void Mouse::UpdateThread(std::stop_token stop_token) {
39 Common::SetCurrentThreadName("Mouse"); 73 Common::SetCurrentThreadName("Mouse");
40 constexpr int update_time = 10; 74
41 while (!stop_token.stop_requested()) { 75 while (!stop_token.stop_requested()) {
42 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { 76 UpdateStickInput();
43 // Slow movement by 4% 77 UpdateMotionInput();
44 last_mouse_change *= 0.96f; 78
45 const float sensitivity =
46 Settings::values.mouse_panning_sensitivity.GetValue() * 0.022f;
47 SetAxis(identifier, mouse_axis_x, last_mouse_change.x * sensitivity);
48 SetAxis(identifier, mouse_axis_y, -last_mouse_change.y * sensitivity);
49 }
50
51 SetAxis(identifier, motion_wheel_y, 0.0f);
52
53 if (mouse_panning_timout++ > 20) {
54 StopPanning();
55 }
56 std::this_thread::sleep_for(std::chrono::milliseconds(update_time)); 79 std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
57 } 80 }
58} 81}
59 82
60void Mouse::MouseMove(int x, int y, f32 touch_x, f32 touch_y, int center_x, int center_y) { 83void Mouse::UpdateStickInput() {
61 // If native mouse is enabled just set the screen coordinates 84 if (!Settings::values.mouse_panning) {
62 if (Settings::values.mouse_enabled) {
63 SetAxis(identifier, mouse_axis_x, touch_x);
64 SetAxis(identifier, mouse_axis_y, touch_y);
65 return; 85 return;
66 } 86 }
67 87
68 SetAxis(identifier, touch_axis_x, touch_x); 88 const float length = last_mouse_change.Length();
69 SetAxis(identifier, touch_axis_y, touch_y);
70 89
71 if (Settings::values.mouse_panning) { 90 // Prevent input from exceeding the max range (1.0f) too much,
72 auto mouse_change = 91 // but allow some room to make it easier to sustain
73 (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>(); 92 if (length > 1.2f) {
74 mouse_panning_timout = 0; 93 last_mouse_change /= length;
94 last_mouse_change *= 1.2f;
95 }
96
97 auto mouse_change = last_mouse_change;
98
99 // Bind the mouse change to [0 <= deadzone_counterweight <= 1,1]
100 if (length < 1.0f) {
101 const float deadzone_h_counterweight =
102 Settings::values.mouse_panning_deadzone_x_counterweight.GetValue();
103 const float deadzone_v_counterweight =
104 Settings::values.mouse_panning_deadzone_y_counterweight.GetValue();
105 mouse_change /= length;
106 mouse_change.x *= length + (1 - length) * deadzone_h_counterweight * 0.01f;
107 mouse_change.y *= length + (1 - length) * deadzone_v_counterweight * 0.01f;
108 }
109
110 SetAxis(identifier, mouse_axis_x, mouse_change.x);
111 SetAxis(identifier, mouse_axis_y, -mouse_change.y);
112
113 // Decay input over time
114 const float clamped_length = std::min(1.0f, length);
115 const float decay_strength = Settings::values.mouse_panning_decay_strength.GetValue();
116 const float decay = 1 - clamped_length * clamped_length * decay_strength * 0.01f;
117 const float min_decay = Settings::values.mouse_panning_min_decay.GetValue();
118 const float clamped_decay = std::min(1 - min_decay / 100.0f, decay);
119 last_mouse_change *= clamped_decay;
120}
75 121
76 const auto move_distance = mouse_change.Length(); 122void Mouse::UpdateMotionInput() {
77 if (move_distance == 0) { 123 // This may need its own sensitivity instead of using the average
78 return; 124 const float sensitivity = (Settings::values.mouse_panning_x_sensitivity.GetValue() +
79 } 125 Settings::values.mouse_panning_y_sensitivity.GetValue()) /
126 2.0f * default_motion_sensitivity;
80 127
81 // Make slow movements at least 3 units on lenght 128 const float rotation_velocity = std::sqrt(last_motion_change.x * last_motion_change.x +
82 if (move_distance < 3.0f) { 129 last_motion_change.y * last_motion_change.y);
83 // Normalize value
84 mouse_change /= move_distance;
85 mouse_change *= 3.0f;
86 }
87 130
88 // Average mouse movements 131 if (rotation_velocity > maximum_rotation_speed / sensitivity) {
89 last_mouse_change = (last_mouse_change * 0.91f) + (mouse_change * 0.09f); 132 const float multiplier = maximum_rotation_speed / rotation_velocity / sensitivity;
133 last_motion_change.x = last_motion_change.x * multiplier;
134 last_motion_change.y = last_motion_change.y * multiplier;
135 }
90 136
91 const auto last_move_distance = last_mouse_change.Length(); 137 const BasicMotion motion_data{
138 .gyro_x = last_motion_change.x * sensitivity,
139 .gyro_y = last_motion_change.y * sensitivity,
140 .gyro_z = last_motion_change.z * sensitivity,
141 .accel_x = 0,
142 .accel_y = 0,
143 .accel_z = 0,
144 .delta_timestamp = update_time * 1000,
145 };
92 146
93 // Make fast movements clamp to 8 units on lenght 147 if (Settings::values.mouse_panning) {
94 if (last_move_distance > 8.0f) { 148 last_motion_change.x = 0;
95 // Normalize value 149 last_motion_change.y = 0;
96 last_mouse_change /= last_move_distance; 150 }
97 last_mouse_change *= 8.0f; 151 last_motion_change.z = 0;
98 }
99 152
100 // Ignore average if it's less than 1 unit and use current movement value 153 SetMotion(motion_identifier, 0, motion_data);
101 if (last_move_distance < 1.0f) { 154}
102 last_mouse_change = mouse_change / mouse_change.Length(); 155
103 } 156void Mouse::Move(int x, int y, int center_x, int center_y) {
157 if (Settings::values.mouse_panning) {
158 const auto mouse_change =
159 (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>();
160 const float x_sensitivity =
161 Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity;
162 const float y_sensitivity =
163 Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity;
164
165 last_motion_change += {-mouse_change.y, -mouse_change.x, 0};
166 last_mouse_change.x += mouse_change.x * x_sensitivity * 0.09f;
167 last_mouse_change.y += mouse_change.y * y_sensitivity * 0.09f;
104 168
105 return; 169 return;
106 } 170 }
107 171
108 if (button_pressed) { 172 if (button_pressed) {
109 const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin; 173 const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin;
110 const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.0012f; 174 const float x_sensitivity = Settings::values.mouse_panning_x_sensitivity.GetValue();
111 SetAxis(identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * sensitivity); 175 const float y_sensitivity = Settings::values.mouse_panning_y_sensitivity.GetValue();
112 SetAxis(identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * sensitivity); 176 SetAxis(identifier, mouse_axis_x,
177 static_cast<float>(mouse_move.x) * x_sensitivity * 0.0012f);
178 SetAxis(identifier, mouse_axis_y,
179 static_cast<float>(-mouse_move.y) * y_sensitivity * 0.0012f);
180
181 last_motion_change = {
182 static_cast<float>(-mouse_move.y) / 50.0f,
183 static_cast<float>(-mouse_move.x) / 50.0f,
184 last_motion_change.z,
185 };
113 } 186 }
114} 187}
115 188
116void Mouse::PressButton(int x, int y, f32 touch_x, f32 touch_y, MouseButton button) { 189void Mouse::MouseMove(f32 touch_x, f32 touch_y) {
117 SetAxis(identifier, touch_axis_x, touch_x); 190 SetAxis(real_mouse_identifier, mouse_axis_x, touch_x);
118 SetAxis(identifier, touch_axis_y, touch_y); 191 SetAxis(real_mouse_identifier, mouse_axis_y, touch_y);
192}
193
194void Mouse::TouchMove(f32 touch_x, f32 touch_y) {
195 SetAxis(touch_identifier, mouse_axis_x, touch_x);
196 SetAxis(touch_identifier, mouse_axis_y, touch_y);
197}
198
199void Mouse::PressButton(int x, int y, MouseButton button) {
119 SetButton(identifier, static_cast<int>(button), true); 200 SetButton(identifier, static_cast<int>(button), true);
201
120 // Set initial analog parameters 202 // Set initial analog parameters
121 mouse_origin = {x, y}; 203 mouse_origin = {x, y};
122 last_mouse_position = {x, y}; 204 last_mouse_position = {x, y};
123 button_pressed = true; 205 button_pressed = true;
124} 206}
125 207
208void Mouse::PressMouseButton(MouseButton button) {
209 SetButton(real_mouse_identifier, static_cast<int>(button), true);
210}
211
212void Mouse::PressTouchButton(f32 touch_x, f32 touch_y, MouseButton button) {
213 SetAxis(touch_identifier, mouse_axis_x, touch_x);
214 SetAxis(touch_identifier, mouse_axis_y, touch_y);
215 SetButton(touch_identifier, static_cast<int>(button), true);
216}
217
126void Mouse::ReleaseButton(MouseButton button) { 218void Mouse::ReleaseButton(MouseButton button) {
127 SetButton(identifier, static_cast<int>(button), false); 219 SetButton(identifier, static_cast<int>(button), false);
220 SetButton(real_mouse_identifier, static_cast<int>(button), false);
221 SetButton(touch_identifier, static_cast<int>(button), false);
128 222
129 if (!Settings::values.mouse_panning && !Settings::values.mouse_enabled) { 223 if (!Settings::values.mouse_panning) {
130 SetAxis(identifier, mouse_axis_x, 0); 224 SetAxis(identifier, mouse_axis_x, 0);
131 SetAxis(identifier, mouse_axis_y, 0); 225 SetAxis(identifier, mouse_axis_y, 0);
132 } 226 }
227
228 last_motion_change.x = 0;
229 last_motion_change.y = 0;
230
133 button_pressed = false; 231 button_pressed = false;
134} 232}
135 233
136void Mouse::MouseWheelChange(int x, int y) { 234void Mouse::MouseWheelChange(int x, int y) {
137 wheel_position.x += x; 235 wheel_position.x += x;
138 wheel_position.y += y; 236 wheel_position.y += y;
237 last_motion_change.z += static_cast<f32>(y) / 100.0f;
139 SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x)); 238 SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x));
140 SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y)); 239 SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y));
141 SetAxis(identifier, motion_wheel_y, static_cast<f32>(y) / 100.0f);
142} 240}
143 241
144void Mouse::ReleaseAllButtons() { 242void Mouse::ReleaseAllButtons() {
@@ -146,10 +244,6 @@ void Mouse::ReleaseAllButtons() {
146 button_pressed = false; 244 button_pressed = false;
147} 245}
148 246
149void Mouse::StopPanning() {
150 last_mouse_change = {};
151}
152
153std::vector<Common::ParamPackage> Mouse::GetInputDevices() const { 247std::vector<Common::ParamPackage> Mouse::GetInputDevices() const {
154 std::vector<Common::ParamPackage> devices; 248 std::vector<Common::ParamPackage> devices;
155 devices.emplace_back(Common::ParamPackage{ 249 devices.emplace_back(Common::ParamPackage{
@@ -207,6 +301,9 @@ Common::Input::ButtonNames Mouse::GetUIName(const Common::ParamPackage& params)
207 if (params.Has("axis_x") && params.Has("axis_y") && params.Has("axis_z")) { 301 if (params.Has("axis_x") && params.Has("axis_y") && params.Has("axis_z")) {
208 return Common::Input::ButtonNames::Engine; 302 return Common::Input::ButtonNames::Engine;
209 } 303 }
304 if (params.Has("motion")) {
305 return Common::Input::ButtonNames::Engine;
306 }
210 307
211 return Common::Input::ButtonNames::Invalid; 308 return Common::Input::ButtonNames::Invalid;
212} 309}
diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h
index 72073cc23..0e8edcce1 100644
--- a/src/input_common/drivers/mouse.h
+++ b/src/input_common/drivers/mouse.h
@@ -37,13 +37,43 @@ public:
37 * @param center_x the x-coordinate of the middle of the screen 37 * @param center_x the x-coordinate of the middle of the screen
38 * @param center_y the y-coordinate of the middle of the screen 38 * @param center_y the y-coordinate of the middle of the screen
39 */ 39 */
40 void MouseMove(int x, int y, f32 touch_x, f32 touch_y, int center_x, int center_y); 40 void Move(int x, int y, int center_x, int center_y);
41 41
42 /** 42 /**
43 * Sets the status of all buttons bound with the key to pressed 43 * Signals that real mouse has moved.
44 * @param key_code the code of the key to press 44 * @param x the absolute position on the touchscreen of the cursor
45 * @param y the absolute position on the touchscreen of the cursor
45 */ 46 */
46 void PressButton(int x, int y, f32 touch_x, f32 touch_y, MouseButton button); 47 void MouseMove(f32 touch_x, f32 touch_y);
48
49 /**
50 * Signals that touch finger has moved.
51 * @param x the absolute position on the touchscreen of the cursor
52 * @param y the absolute position on the touchscreen of the cursor
53 */
54 void TouchMove(f32 touch_x, f32 touch_y);
55
56 /**
57 * Sets the status of a button to pressed
58 * @param x the x-coordinate of the cursor
59 * @param y the y-coordinate of the cursor
60 * @param button the id of the button to press
61 */
62 void PressButton(int x, int y, MouseButton button);
63
64 /**
65 * Sets the status of a mouse button to pressed
66 * @param button the id of the button to press
67 */
68 void PressMouseButton(MouseButton button);
69
70 /**
71 * Sets the status of touch finger to pressed
72 * @param x the absolute position on the touchscreen of the cursor
73 * @param y the absolute position on the touchscreen of the cursor
74 * @param button the id of the button to press
75 */
76 void PressTouchButton(f32 touch_x, f32 touch_y, MouseButton button);
47 77
48 /** 78 /**
49 * Sets the status of all buttons bound with the key to released 79 * Sets the status of all buttons bound with the key to released
@@ -66,16 +96,17 @@ public:
66 96
67private: 97private:
68 void UpdateThread(std::stop_token stop_token); 98 void UpdateThread(std::stop_token stop_token);
69 void StopPanning(); 99 void UpdateStickInput();
100 void UpdateMotionInput();
70 101
71 Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const; 102 Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
72 103
73 Common::Vec2<int> mouse_origin; 104 Common::Vec2<int> mouse_origin;
74 Common::Vec2<int> last_mouse_position; 105 Common::Vec2<int> last_mouse_position;
75 Common::Vec2<float> last_mouse_change; 106 Common::Vec2<float> last_mouse_change;
107 Common::Vec3<float> last_motion_change;
76 Common::Vec2<int> wheel_position; 108 Common::Vec2<int> wheel_position;
77 bool button_pressed; 109 bool button_pressed;
78 int mouse_panning_timout{};
79 std::jthread update_thread; 110 std::jthread update_thread;
80}; 111};
81 112
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 9835d99d2..9f26392b1 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -109,14 +109,37 @@ public:
109 } 109 }
110 110
111 bool RumblePlay(const Common::Input::VibrationStatus vibration) { 111 bool RumblePlay(const Common::Input::VibrationStatus vibration) {
112 constexpr u32 rumble_max_duration_ms = 1000; 112 constexpr u32 rumble_max_duration_ms = 2000;
113 constexpr f32 low_start_sensitivity_limit = 140.0;
114 constexpr f32 low_width_sensitivity_limit = 400.0;
115 constexpr f32 high_start_sensitivity_limit = 200.0;
116 constexpr f32 high_width_sensitivity_limit = 700.0;
117 // Try to provide some feeling of the frequency by reducing the amplitude depending on it.
118 f32 low_frequency_scale = 1.0;
119 if (vibration.low_frequency > low_start_sensitivity_limit) {
120 low_frequency_scale =
121 std::max(1.0f - (vibration.low_frequency - low_start_sensitivity_limit) /
122 low_width_sensitivity_limit,
123 0.3f);
124 }
125 f32 low_amplitude = vibration.low_amplitude * low_frequency_scale;
126
127 f32 high_frequency_scale = 1.0;
128 if (vibration.high_frequency > high_start_sensitivity_limit) {
129 high_frequency_scale =
130 std::max(1.0f - (vibration.high_frequency - high_start_sensitivity_limit) /
131 high_width_sensitivity_limit,
132 0.3f);
133 }
134 f32 high_amplitude = vibration.high_amplitude * high_frequency_scale;
135
113 if (sdl_controller) { 136 if (sdl_controller) {
114 return SDL_GameControllerRumble( 137 return SDL_GameControllerRumble(sdl_controller.get(), static_cast<u16>(low_amplitude),
115 sdl_controller.get(), static_cast<u16>(vibration.low_amplitude), 138 static_cast<u16>(high_amplitude),
116 static_cast<u16>(vibration.high_amplitude), rumble_max_duration_ms) != -1; 139 rumble_max_duration_ms) != -1;
117 } else if (sdl_joystick) { 140 } else if (sdl_joystick) {
118 return SDL_JoystickRumble(sdl_joystick.get(), static_cast<u16>(vibration.low_amplitude), 141 return SDL_JoystickRumble(sdl_joystick.get(), static_cast<u16>(low_amplitude),
119 static_cast<u16>(vibration.high_amplitude), 142 static_cast<u16>(high_amplitude),
120 rumble_max_duration_ms) != -1; 143 rumble_max_duration_ms) != -1;
121 } 144 }
122 145
@@ -127,6 +150,8 @@ public:
127 if (sdl_controller) { 150 if (sdl_controller) {
128 const auto type = SDL_GameControllerGetType(sdl_controller.get()); 151 const auto type = SDL_GameControllerGetType(sdl_controller.get());
129 return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) || 152 return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) ||
153 (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT) ||
154 (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) ||
130 (type == SDL_CONTROLLER_TYPE_PS5); 155 (type == SDL_CONTROLLER_TYPE_PS5);
131 } 156 }
132 return false; 157 return false;
@@ -205,9 +230,8 @@ public:
205 return false; 230 return false;
206 } 231 }
207 232
208 Common::Input::BatteryLevel GetBatteryLevel() { 233 Common::Input::BatteryLevel GetBatteryLevel(SDL_JoystickPowerLevel battery_level) {
209 const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get()); 234 switch (battery_level) {
210 switch (level) {
211 case SDL_JOYSTICK_POWER_EMPTY: 235 case SDL_JOYSTICK_POWER_EMPTY:
212 return Common::Input::BatteryLevel::Empty; 236 return Common::Input::BatteryLevel::Empty;
213 case SDL_JOYSTICK_POWER_LOW: 237 case SDL_JOYSTICK_POWER_LOW:
@@ -334,11 +358,27 @@ void SDLDriver::InitJoystick(int joystick_index) {
334 358
335 const auto guid = GetGUID(sdl_joystick); 359 const auto guid = GetGUID(sdl_joystick);
336 360
361 if (Settings::values.enable_joycon_driver) {
362 if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e &&
363 (guid.uuid[8] == 0x06 || guid.uuid[8] == 0x07)) {
364 LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index);
365 SDL_JoystickClose(sdl_joystick);
366 return;
367 }
368 }
369
370 if (Settings::values.enable_procon_driver) {
371 if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e && guid.uuid[8] == 0x09) {
372 LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index);
373 SDL_JoystickClose(sdl_joystick);
374 return;
375 }
376 }
377
337 std::scoped_lock lock{joystick_map_mutex}; 378 std::scoped_lock lock{joystick_map_mutex};
338 if (joystick_map.find(guid) == joystick_map.end()) { 379 if (joystick_map.find(guid) == joystick_map.end()) {
339 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); 380 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
340 PreSetController(joystick->GetPadIdentifier()); 381 PreSetController(joystick->GetPadIdentifier());
341 SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel());
342 joystick->EnableMotion(); 382 joystick->EnableMotion();
343 joystick_map[guid].emplace_back(std::move(joystick)); 383 joystick_map[guid].emplace_back(std::move(joystick));
344 return; 384 return;
@@ -358,7 +398,6 @@ void SDLDriver::InitJoystick(int joystick_index) {
358 const int port = static_cast<int>(joystick_guid_list.size()); 398 const int port = static_cast<int>(joystick_guid_list.size());
359 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); 399 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
360 PreSetController(joystick->GetPadIdentifier()); 400 PreSetController(joystick->GetPadIdentifier());
361 SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel());
362 joystick->EnableMotion(); 401 joystick->EnableMotion();
363 joystick_guid_list.emplace_back(std::move(joystick)); 402 joystick_guid_list.emplace_back(std::move(joystick));
364} 403}
@@ -398,8 +437,6 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
398 if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { 437 if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
399 const PadIdentifier identifier = joystick->GetPadIdentifier(); 438 const PadIdentifier identifier = joystick->GetPadIdentifier();
400 SetButton(identifier, event.jbutton.button, true); 439 SetButton(identifier, event.jbutton.button, true);
401 // Battery doesn't trigger an event so just update every button press
402 SetBattery(identifier, joystick->GetBatteryLevel());
403 } 440 }
404 break; 441 break;
405 } 442 }
@@ -426,6 +463,13 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
426 } 463 }
427 break; 464 break;
428 } 465 }
466 case SDL_JOYBATTERYUPDATED: {
467 if (auto joystick = GetSDLJoystickBySDLID(event.jbattery.which)) {
468 const PadIdentifier identifier = joystick->GetPadIdentifier();
469 SetBattery(identifier, joystick->GetBatteryLevel(event.jbattery.level));
470 }
471 break;
472 }
429 case SDL_JOYDEVICEREMOVED: 473 case SDL_JOYDEVICEREMOVED:
430 LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which); 474 LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
431 CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which)); 475 CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
@@ -443,6 +487,10 @@ void SDLDriver::CloseJoysticks() {
443} 487}
444 488
445SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_engine_)) { 489SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
490 // Set our application name. Currently passed to DBus by SDL and visible to the user through
491 // their desktop environment.
492 SDL_SetHint(SDL_HINT_APP_NAME, "yuzu");
493
446 if (!Settings::values.enable_raw_input) { 494 if (!Settings::values.enable_raw_input) {
447 // Disable raw input. When enabled this setting causes SDL to die when a web applet opens 495 // Disable raw input. When enabled this setting causes SDL to die when a web applet opens
448 SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0"); 496 SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
@@ -456,9 +504,25 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
456 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); 504 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
457 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); 505 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
458 506
459 // Use hidapi driver for joycons. This will allow joycons to be detected as a GameController and 507 // Disable hidapi drivers for joycon controllers when the custom joycon driver is enabled
460 // not a generic one 508 if (Settings::values.enable_joycon_driver) {
461 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); 509 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0");
510 } else {
511 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
512 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED, "0");
513 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "0");
514 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, "1");
515 }
516
517 // Disable hidapi drivers for pro controllers when the custom joycon driver is enabled
518 if (Settings::values.enable_procon_driver) {
519 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0");
520 } else {
521 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
522 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0");
523 }
524
525 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, "1");
462 526
463 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native 527 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
464 // driver on Linux. 528 // driver on Linux.
@@ -548,7 +612,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
548 return devices; 612 return devices;
549} 613}
550 614
551Common::Input::VibrationError SDLDriver::SetVibration( 615Common::Input::DriverResult SDLDriver::SetVibration(
552 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 616 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
553 const auto joystick = 617 const auto joystick =
554 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); 618 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
@@ -582,14 +646,14 @@ Common::Input::VibrationError SDLDriver::SetVibration(
582 .vibration = new_vibration, 646 .vibration = new_vibration,
583 }); 647 });
584 648
585 return Common::Input::VibrationError::None; 649 return Common::Input::DriverResult::Success;
586} 650}
587 651
588bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) { 652bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) {
589 const auto joystick = 653 const auto joystick =
590 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); 654 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
591 655
592 constexpr Common::Input::VibrationStatus test_vibration{ 656 static constexpr Common::Input::VibrationStatus test_vibration{
593 .low_amplitude = 1, 657 .low_amplitude = 1,
594 .low_frequency = 160.0f, 658 .low_frequency = 160.0f,
595 .high_amplitude = 1, 659 .high_amplitude = 1,
@@ -597,7 +661,7 @@ bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) {
597 .type = Common::Input::VibrationAmplificationType::Exponential, 661 .type = Common::Input::VibrationAmplificationType::Exponential,
598 }; 662 };
599 663
600 constexpr Common::Input::VibrationStatus zero_vibration{ 664 static constexpr Common::Input::VibrationStatus zero_vibration{
601 .low_amplitude = 0, 665 .low_amplitude = 0,
602 .low_frequency = 160.0f, 666 .low_frequency = 160.0f,
603 .high_amplitude = 0, 667 .high_amplitude = 0,
@@ -625,12 +689,27 @@ bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) {
625} 689}
626 690
627void SDLDriver::SendVibrations() { 691void SDLDriver::SendVibrations() {
692 std::vector<VibrationRequest> filtered_vibrations{};
628 while (!vibration_queue.Empty()) { 693 while (!vibration_queue.Empty()) {
629 VibrationRequest request; 694 VibrationRequest request;
630 vibration_queue.Pop(request); 695 vibration_queue.Pop(request);
631 const auto joystick = GetSDLJoystickByGUID(request.identifier.guid.RawString(), 696 const auto joystick = GetSDLJoystickByGUID(request.identifier.guid.RawString(),
632 static_cast<int>(request.identifier.port)); 697 static_cast<int>(request.identifier.port));
633 joystick->RumblePlay(request.vibration); 698 const auto it = std::find_if(filtered_vibrations.begin(), filtered_vibrations.end(),
699 [request](VibrationRequest vibration) {
700 return vibration.identifier == request.identifier;
701 });
702 if (it == filtered_vibrations.end()) {
703 filtered_vibrations.push_back(std::move(request));
704 continue;
705 }
706 *it = request;
707 }
708
709 for (const auto& vibration : filtered_vibrations) {
710 const auto joystick = GetSDLJoystickByGUID(vibration.identifier.guid.RawString(),
711 static_cast<int>(vibration.identifier.port));
712 joystick->RumblePlay(vibration.vibration);
634 } 713 }
635} 714}
636 715
@@ -721,10 +800,12 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p
721 800
722 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController. 801 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
723 // We will add those afterwards 802 // We will add those afterwards
724 // This list also excludes Screenshot since theres not really a mapping for that 803 // This list also excludes Screenshot since there's not really a mapping for that
725 ButtonBindings switch_to_sdl_button; 804 ButtonBindings switch_to_sdl_button;
726 805
727 if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) { 806 if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO ||
807 SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT ||
808 SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) {
728 switch_to_sdl_button = GetNintendoButtonBinding(joystick); 809 switch_to_sdl_button = GetNintendoButtonBinding(joystick);
729 } else { 810 } else {
730 switch_to_sdl_button = GetDefaultButtonBinding(); 811 switch_to_sdl_button = GetDefaultButtonBinding();
@@ -980,7 +1061,7 @@ MotionMapping SDLDriver::GetMotionMappingForDevice(const Common::ParamPackage& p
980 1061
981Common::Input::ButtonNames SDLDriver::GetUIName(const Common::ParamPackage& params) const { 1062Common::Input::ButtonNames SDLDriver::GetUIName(const Common::ParamPackage& params) const {
982 if (params.Has("button")) { 1063 if (params.Has("button")) {
983 // TODO(German77): Find how to substitue the values for real button names 1064 // TODO(German77): Find how to substitute the values for real button names
984 return Common::Input::ButtonNames::Value; 1065 return Common::Input::ButtonNames::Value;
985 } 1066 }
986 if (params.Has("hat")) { 1067 if (params.Has("hat")) {
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..180eb53ef 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -22,28 +22,61 @@ 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 if (state == State::Initialized) { 31 case Common::Input::PollingMode::NFC:
32 state = State::WaitingForAmiibo; 32 state = State::Initialized;
33 } 33 return Common::Input::DriverResult::Success;
34 } else { 34 default:
35 if (state == State::AmiiboIsOpen) { 35 if (state == State::TagNearby) {
36 CloseAmiibo(); 36 CloseAmiibo();
37 } 37 }
38 state = State::Disabled;
39 return Common::Input::DriverResult::NotSupported;
38 } 40 }
39
40 return Common::Input::PollingError::None;
41} 41}
42 42
43Common::Input::NfcState VirtualAmiibo::SupportsNfc( 43Common::Input::NfcState VirtualAmiibo::SupportsNfc(
44 [[maybe_unused]] const PadIdentifier& identifier_) const { 44 [[maybe_unused]] const PadIdentifier& identifier_) const {
45 return Common::Input::NfcState::Success; 45 return Common::Input::NfcState::Success;
46} 46}
47Common::Input::NfcState VirtualAmiibo::StartNfcPolling(const PadIdentifier& identifier_) {
48 if (state != State::Initialized) {
49 return Common::Input::NfcState::WrongDeviceState;
50 }
51 state = State::WaitingForAmiibo;
52 return Common::Input::NfcState::Success;
53}
54
55Common::Input::NfcState VirtualAmiibo::StopNfcPolling(const PadIdentifier& identifier_) {
56 if (state == State::Disabled) {
57 return Common::Input::NfcState::WrongDeviceState;
58 }
59 if (state == State::TagNearby) {
60 CloseAmiibo();
61 }
62 state = State::Initialized;
63 return Common::Input::NfcState::Success;
64}
65
66Common::Input::NfcState VirtualAmiibo::ReadAmiiboData(const PadIdentifier& identifier_,
67 std::vector<u8>& out_data) {
68 if (state != State::TagNearby) {
69 return Common::Input::NfcState::WrongDeviceState;
70 }
71
72 if (status.tag_type != 1U << 1) {
73 return Common::Input::NfcState::InvalidTagType;
74 }
75
76 out_data.resize(nfc_data.size());
77 memcpy(out_data.data(), nfc_data.data(), nfc_data.size());
78 return Common::Input::NfcState::Success;
79}
47 80
48Common::Input::NfcState VirtualAmiibo::WriteNfcData( 81Common::Input::NfcState VirtualAmiibo::WriteNfcData(
49 [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) { 82 [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) {
@@ -56,7 +89,7 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData(
56 } 89 }
57 90
58 if (!nfc_file.Write(data)) { 91 if (!nfc_file.Write(data)) {
59 LOG_ERROR(Service_NFP, "Error writting to file"); 92 LOG_ERROR(Service_NFP, "Error writing to file");
60 return Common::Input::NfcState::WriteFailed; 93 return Common::Input::NfcState::WriteFailed;
61 } 94 }
62 95
@@ -65,6 +98,69 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData(
65 return Common::Input::NfcState::Success; 98 return Common::Input::NfcState::Success;
66} 99}
67 100
101Common::Input::NfcState VirtualAmiibo::ReadMifareData(const PadIdentifier& identifier_,
102 const Common::Input::MifareRequest& request,
103 Common::Input::MifareRequest& out_data) {
104 if (state != State::TagNearby) {
105 return Common::Input::NfcState::WrongDeviceState;
106 }
107
108 if (status.tag_type != 1U << 6) {
109 return Common::Input::NfcState::InvalidTagType;
110 }
111
112 for (std::size_t i = 0; i < request.data.size(); i++) {
113 if (request.data[i].command == 0) {
114 continue;
115 }
116 out_data.data[i].command = request.data[i].command;
117 out_data.data[i].sector = request.data[i].sector;
118
119 const std::size_t sector_index =
120 request.data[i].sector * sizeof(Common::Input::MifareData::data);
121
122 if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) {
123 return Common::Input::NfcState::WriteFailed;
124 }
125
126 // Ignore the sector key as we don't support it
127 memcpy(out_data.data[i].data.data(), nfc_data.data() + sector_index,
128 sizeof(Common::Input::MifareData::data));
129 }
130
131 return Common::Input::NfcState::Success;
132}
133
134Common::Input::NfcState VirtualAmiibo::WriteMifareData(
135 const PadIdentifier& identifier_, const Common::Input::MifareRequest& request) {
136 if (state != State::TagNearby) {
137 return Common::Input::NfcState::WrongDeviceState;
138 }
139
140 if (status.tag_type != 1U << 6) {
141 return Common::Input::NfcState::InvalidTagType;
142 }
143
144 for (std::size_t i = 0; i < request.data.size(); i++) {
145 if (request.data[i].command == 0) {
146 continue;
147 }
148
149 const std::size_t sector_index =
150 request.data[i].sector * sizeof(Common::Input::MifareData::data);
151
152 if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) {
153 return Common::Input::NfcState::WriteFailed;
154 }
155
156 // Ignore the sector key as we don't support it
157 memcpy(nfc_data.data() + sector_index, request.data[i].data.data(),
158 sizeof(Common::Input::MifareData::data));
159 }
160
161 return Common::Input::NfcState::Success;
162}
163
68VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const { 164VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
69 return state; 165 return state;
70} 166}
@@ -72,10 +168,7 @@ VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
72VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { 168VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
73 const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read, 169 const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read,
74 Common::FS::FileType::BinaryFile}; 170 Common::FS::FileType::BinaryFile};
75 171 std::vector<u8> data{};
76 if (state != State::WaitingForAmiibo) {
77 return Info::WrongDeviceState;
78 }
79 172
80 if (!nfc_file.IsOpen()) { 173 if (!nfc_file.IsOpen()) {
81 return Info::UnableToLoad; 174 return Info::UnableToLoad;
@@ -84,14 +177,15 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
84 switch (nfc_file.GetSize()) { 177 switch (nfc_file.GetSize()) {
85 case AmiiboSize: 178 case AmiiboSize:
86 case AmiiboSizeWithoutPassword: 179 case AmiiboSizeWithoutPassword:
87 nfc_data.resize(AmiiboSize); 180 case AmiiboSizeWithSignature:
88 if (nfc_file.Read(nfc_data) < AmiiboSizeWithoutPassword) { 181 data.resize(AmiiboSize);
182 if (nfc_file.Read(data) < AmiiboSizeWithoutPassword) {
89 return Info::NotAnAmiibo; 183 return Info::NotAnAmiibo;
90 } 184 }
91 break; 185 break;
92 case MifareSize: 186 case MifareSize:
93 nfc_data.resize(MifareSize); 187 data.resize(MifareSize);
94 if (nfc_file.Read(nfc_data) < MifareSize) { 188 if (nfc_file.Read(data) < MifareSize) {
95 return Info::NotAnAmiibo; 189 return Info::NotAnAmiibo;
96 } 190 }
97 break; 191 break;
@@ -100,14 +194,44 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
100 } 194 }
101 195
102 file_path = filename; 196 file_path = filename;
103 state = State::AmiiboIsOpen; 197 return LoadAmiibo(data);
104 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); 198}
199
200VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) {
201 if (state != State::WaitingForAmiibo) {
202 return Info::WrongDeviceState;
203 }
204
205 switch (data.size_bytes()) {
206 case AmiiboSize:
207 case AmiiboSizeWithoutPassword:
208 case AmiiboSizeWithSignature:
209 nfc_data.resize(AmiiboSize);
210 status.tag_type = 1U << 1;
211 status.uuid_length = 7;
212 break;
213 case MifareSize:
214 nfc_data.resize(MifareSize);
215 status.tag_type = 1U << 6;
216 status.uuid_length = 4;
217 break;
218 default:
219 return Info::NotAnAmiibo;
220 }
221
222 status.uuid = {};
223 status.protocol = 1;
224 state = State::TagNearby;
225 status.state = Common::Input::NfcState::NewAmiibo,
226 memcpy(nfc_data.data(), data.data(), data.size_bytes());
227 memcpy(status.uuid.data(), nfc_data.data(), status.uuid_length);
228 SetNfc(identifier, status);
105 return Info::Success; 229 return Info::Success;
106} 230}
107 231
108VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { 232VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
109 if (state == State::AmiiboIsOpen) { 233 if (state == State::TagNearby) {
110 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); 234 SetNfc(identifier, status);
111 return Info::Success; 235 return Info::Success;
112 } 236 }
113 237
@@ -115,9 +239,14 @@ VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
115} 239}
116 240
117VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { 241VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
118 state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo 242 if (state != State::TagNearby) {
119 : State::Initialized; 243 return Info::Success;
120 SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}}); 244 }
245
246 state = State::WaitingForAmiibo;
247 status.state = Common::Input::NfcState::AmiiboRemoved;
248 SetNfc(identifier, status);
249 status.tag_type = 0;
121 return Info::Success; 250 return Info::Success;
122} 251}
123 252
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
index 0f9dad333..490f38e05 100644
--- a/src/input_common/drivers/virtual_amiibo.h
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include <span>
7#include <string> 8#include <string>
8#include <vector> 9#include <vector>
9 10
@@ -19,9 +20,10 @@ namespace InputCommon {
19class VirtualAmiibo final : public InputEngine { 20class VirtualAmiibo final : public InputEngine {
20public: 21public:
21 enum class State { 22 enum class State {
23 Disabled,
22 Initialized, 24 Initialized,
23 WaitingForAmiibo, 25 WaitingForAmiibo,
24 AmiiboIsOpen, 26 TagNearby,
25 }; 27 };
26 28
27 enum class Info { 29 enum class Info {
@@ -36,17 +38,26 @@ public:
36 ~VirtualAmiibo() override; 38 ~VirtualAmiibo() override;
37 39
38 // Sets polling mode to a controller 40 // Sets polling mode to a controller
39 Common::Input::PollingError SetPollingMode( 41 Common::Input::DriverResult SetPollingMode(
40 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; 42 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;
41 43
42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; 44 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
43 45 Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier_) override;
46 Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier_) override;
47 Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier_,
48 std::vector<u8>& out_data) override;
44 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, 49 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
45 const std::vector<u8>& data) override; 50 const std::vector<u8>& data) override;
51 Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier_,
52 const Common::Input::MifareRequest& data,
53 Common::Input::MifareRequest& out_data) override;
54 Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier_,
55 const Common::Input::MifareRequest& data) override;
46 56
47 State GetCurrentState() const; 57 State GetCurrentState() const;
48 58
49 Info LoadAmiibo(const std::string& amiibo_file); 59 Info LoadAmiibo(const std::string& amiibo_file);
60 Info LoadAmiibo(std::span<u8> data);
50 Info ReloadAmiibo(); 61 Info ReloadAmiibo();
51 Info CloseAmiibo(); 62 Info CloseAmiibo();
52 63
@@ -55,11 +66,13 @@ public:
55private: 66private:
56 static constexpr std::size_t AmiiboSize = 0x21C; 67 static constexpr std::size_t AmiiboSize = 0x21C;
57 static constexpr std::size_t AmiiboSizeWithoutPassword = AmiiboSize - 0x8; 68 static constexpr std::size_t AmiiboSizeWithoutPassword = AmiiboSize - 0x8;
69 static constexpr std::size_t AmiiboSizeWithSignature = AmiiboSize + 0x20;
58 static constexpr std::size_t MifareSize = 0x400; 70 static constexpr std::size_t MifareSize = 0x400;
59 71
60 std::string file_path{}; 72 std::string file_path{};
61 State state{State::Initialized}; 73 State state{State::Disabled};
62 std::vector<u8> nfc_data; 74 std::vector<u8> nfc_data;
63 Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive}; 75 Common::Input::NfcStatus status;
76 Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Passive};
64}; 77};
65} // namespace InputCommon 78} // namespace InputCommon
diff --git a/src/input_common/drivers/virtual_gamepad.cpp b/src/input_common/drivers/virtual_gamepad.cpp
index 7db945aa6..c15cbbe58 100644
--- a/src/input_common/drivers/virtual_gamepad.cpp
+++ b/src/input_common/drivers/virtual_gamepad.cpp
@@ -39,6 +39,22 @@ void VirtualGamepad::SetStickPosition(std::size_t player_index, VirtualStick axi
39 SetStickPosition(player_index, static_cast<int>(axis_id), x_value, y_value); 39 SetStickPosition(player_index, static_cast<int>(axis_id), x_value, y_value);
40} 40}
41 41
42void VirtualGamepad::SetMotionState(std::size_t player_index, u64 delta_timestamp, float gyro_x,
43 float gyro_y, float gyro_z, float accel_x, float accel_y,
44 float accel_z) {
45 const auto identifier = GetIdentifier(player_index);
46 const BasicMotion motion_data{
47 .gyro_x = gyro_x,
48 .gyro_y = gyro_y,
49 .gyro_z = gyro_z,
50 .accel_x = accel_x,
51 .accel_y = accel_y,
52 .accel_z = accel_z,
53 .delta_timestamp = delta_timestamp,
54 };
55 SetMotion(identifier, 0, motion_data);
56}
57
42void VirtualGamepad::ResetControllers() { 58void VirtualGamepad::ResetControllers() {
43 for (std::size_t i = 0; i < PlayerIndexCount; i++) { 59 for (std::size_t i = 0; i < PlayerIndexCount; i++) {
44 SetStickPosition(i, VirtualStick::Left, 0.0f, 0.0f); 60 SetStickPosition(i, VirtualStick::Left, 0.0f, 0.0f);
diff --git a/src/input_common/drivers/virtual_gamepad.h b/src/input_common/drivers/virtual_gamepad.h
index 3df91cc6f..dfbc45a28 100644
--- a/src/input_common/drivers/virtual_gamepad.h
+++ b/src/input_common/drivers/virtual_gamepad.h
@@ -52,7 +52,7 @@ public:
52 void SetButtonState(std::size_t player_index, VirtualButton button_id, bool value); 52 void SetButtonState(std::size_t player_index, VirtualButton button_id, bool value);
53 53
54 /** 54 /**
55 * Sets the status of all buttons bound with the key to released 55 * Sets the status of a stick to a specific player index
56 * @param player_index the player number that will take this action 56 * @param player_index the player number that will take this action
57 * @param axis_id the id of the axis to move 57 * @param axis_id the id of the axis to move
58 * @param x_value the position of the stick in the x axis 58 * @param x_value the position of the stick in the x axis
@@ -62,6 +62,16 @@ public:
62 void SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value, 62 void SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value,
63 float y_value); 63 float y_value);
64 64
65 /**
66 * Sets the status of the motion sensor to a specific player index
67 * @param player_index the player number that will take this action
68 * @param delta_timestamp time passed since last reading
69 * @param gyro_x,gyro_y,gyro_z the gyro sensor readings
70 * @param accel_x,accel_y,accel_z the acelerometer reading
71 */
72 void SetMotionState(std::size_t player_index, u64 delta_timestamp, float gyro_x, float gyro_y,
73 float gyro_z, float accel_x, float accel_y, float accel_z);
74
65 /// Restores all inputs into the neutral position 75 /// Restores all inputs into the neutral position
66 void ResetControllers(); 76 void ResetControllers();
67 77