summaryrefslogtreecommitdiff
path: root/src/core/hid/emulated_controller.cpp
diff options
context:
space:
mode:
authorGravatar Narr the Reg2024-01-04 20:37:43 -0600
committerGravatar Narr the Reg2024-01-05 11:41:15 -0600
commitee847f8ff0b1b0aec39c1b78c010bc0c08a0a613 (patch)
tree3b95cbb74be05f0ce7a007353f1f9f95e1ed3901 /src/core/hid/emulated_controller.cpp
parentMerge pull request #12437 from ameerj/gl-amd-fixes (diff)
downloadyuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.gz
yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.xz
yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.zip
hid_core: Move hid to it's own subproject
Diffstat (limited to 'src/core/hid/emulated_controller.cpp')
-rw-r--r--src/core/hid/emulated_controller.cpp1972
1 files changed, 0 insertions, 1972 deletions
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
deleted file mode 100644
index a6e681e15..000000000
--- a/src/core/hid/emulated_controller.cpp
+++ /dev/null
@@ -1,1972 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <common/scope_exit.h>
6
7#include "common/polyfill_ranges.h"
8#include "common/thread.h"
9#include "core/hid/emulated_controller.h"
10#include "core/hid/input_converter.h"
11#include "core/hle/service/hid/hid_util.h"
12
13namespace Core::HID {
14constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
15constexpr s32 HID_TRIGGER_MAX = 0x7fff;
16constexpr u32 TURBO_BUTTON_DELAY = 4;
17// Use a common UUID for TAS and Virtual Gamepad
18constexpr Common::UUID TAS_UUID =
19 Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
20constexpr Common::UUID VIRTUAL_UUID =
21 Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
22
23EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {}
24
25EmulatedController::~EmulatedController() = default;
26
27NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) {
28 switch (type) {
29 case Settings::ControllerType::ProController:
30 return NpadStyleIndex::ProController;
31 case Settings::ControllerType::DualJoyconDetached:
32 return NpadStyleIndex::JoyconDual;
33 case Settings::ControllerType::LeftJoycon:
34 return NpadStyleIndex::JoyconLeft;
35 case Settings::ControllerType::RightJoycon:
36 return NpadStyleIndex::JoyconRight;
37 case Settings::ControllerType::Handheld:
38 return NpadStyleIndex::Handheld;
39 case Settings::ControllerType::GameCube:
40 return NpadStyleIndex::GameCube;
41 case Settings::ControllerType::Pokeball:
42 return NpadStyleIndex::Pokeball;
43 case Settings::ControllerType::NES:
44 return NpadStyleIndex::NES;
45 case Settings::ControllerType::SNES:
46 return NpadStyleIndex::SNES;
47 case Settings::ControllerType::N64:
48 return NpadStyleIndex::N64;
49 case Settings::ControllerType::SegaGenesis:
50 return NpadStyleIndex::SegaGenesis;
51 default:
52 return NpadStyleIndex::ProController;
53 }
54}
55
56Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) {
57 switch (type) {
58 case NpadStyleIndex::ProController:
59 return Settings::ControllerType::ProController;
60 case NpadStyleIndex::JoyconDual:
61 return Settings::ControllerType::DualJoyconDetached;
62 case NpadStyleIndex::JoyconLeft:
63 return Settings::ControllerType::LeftJoycon;
64 case NpadStyleIndex::JoyconRight:
65 return Settings::ControllerType::RightJoycon;
66 case NpadStyleIndex::Handheld:
67 return Settings::ControllerType::Handheld;
68 case NpadStyleIndex::GameCube:
69 return Settings::ControllerType::GameCube;
70 case NpadStyleIndex::Pokeball:
71 return Settings::ControllerType::Pokeball;
72 case NpadStyleIndex::NES:
73 return Settings::ControllerType::NES;
74 case NpadStyleIndex::SNES:
75 return Settings::ControllerType::SNES;
76 case NpadStyleIndex::N64:
77 return Settings::ControllerType::N64;
78 case NpadStyleIndex::SegaGenesis:
79 return Settings::ControllerType::SegaGenesis;
80 default:
81 return Settings::ControllerType::ProController;
82 }
83}
84
85void EmulatedController::ReloadFromSettings() {
86 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
87 const auto& player = Settings::values.players.GetValue()[player_index];
88
89 for (std::size_t index = 0; index < player.buttons.size(); ++index) {
90 button_params[index] = Common::ParamPackage(player.buttons[index]);
91 }
92 for (std::size_t index = 0; index < player.analogs.size(); ++index) {
93 stick_params[index] = Common::ParamPackage(player.analogs[index]);
94 }
95 for (std::size_t index = 0; index < player.motions.size(); ++index) {
96 motion_params[index] = Common::ParamPackage(player.motions[index]);
97 }
98
99 controller.color_values = {};
100 ReloadColorsFromSettings();
101
102 ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs);
103
104 // Other or debug controller should always be a pro controller
105 if (npad_id_type != NpadIdType::Other) {
106 SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
107 original_npad_type = npad_type;
108 } else {
109 SetNpadStyleIndex(NpadStyleIndex::ProController);
110 original_npad_type = npad_type;
111 }
112
113 Disconnect();
114 if (player.connected) {
115 Connect();
116 }
117
118 ReloadInput();
119}
120
121void EmulatedController::ReloadColorsFromSettings() {
122 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
123 const auto& player = Settings::values.players.GetValue()[player_index];
124
125 // Avoid updating colors if overridden by physical controller
126 if (controller.color_values[LeftIndex].body != 0 &&
127 controller.color_values[RightIndex].body != 0) {
128 return;
129 }
130
131 controller.colors_state.fullkey = {
132 .body = GetNpadColor(player.body_color_left),
133 .button = GetNpadColor(player.button_color_left),
134 };
135 controller.colors_state.left = {
136 .body = GetNpadColor(player.body_color_left),
137 .button = GetNpadColor(player.button_color_left),
138 };
139 controller.colors_state.right = {
140 .body = GetNpadColor(player.body_color_right),
141 .button = GetNpadColor(player.button_color_right),
142 };
143}
144
145void EmulatedController::LoadDevices() {
146 // TODO(german77): Use more buttons to detect the correct device
147 const auto left_joycon = button_params[Settings::NativeButton::DRight];
148 const auto right_joycon = button_params[Settings::NativeButton::A];
149
150 // Triggers for GC controllers
151 trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL];
152 trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR];
153
154 color_params[LeftIndex] = left_joycon;
155 color_params[RightIndex] = right_joycon;
156 color_params[LeftIndex].Set("color", true);
157 color_params[RightIndex].Set("color", true);
158
159 battery_params[LeftIndex] = left_joycon;
160 battery_params[RightIndex] = right_joycon;
161 battery_params[LeftIndex].Set("battery", true);
162 battery_params[RightIndex].Set("battery", true);
163
164 camera_params[0] = right_joycon;
165 camera_params[0].Set("camera", true);
166 nfc_params[1] = right_joycon;
167 nfc_params[1].Set("nfc", true);
168
169 // Only map virtual devices to the first controller
170 if (npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld) {
171 camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};
172 ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
173 nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
174 }
175
176 output_params[LeftIndex] = left_joycon;
177 output_params[RightIndex] = right_joycon;
178 output_params[2] = camera_params[1];
179 output_params[3] = nfc_params[0];
180 output_params[LeftIndex].Set("output", true);
181 output_params[RightIndex].Set("output", true);
182 output_params[2].Set("output", true);
183 output_params[3].Set("output", true);
184
185 LoadTASParams();
186 LoadVirtualGamepadParams();
187
188 std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice);
189 std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice);
190 std::ranges::transform(motion_params, motion_devices.begin(), Common::Input::CreateInputDevice);
191 std::ranges::transform(trigger_params, trigger_devices.begin(),
192 Common::Input::CreateInputDevice);
193 std::ranges::transform(battery_params, battery_devices.begin(),
194 Common::Input::CreateInputDevice);
195 std::ranges::transform(color_params, color_devices.begin(), Common::Input::CreateInputDevice);
196 std::ranges::transform(camera_params, camera_devices.begin(), Common::Input::CreateInputDevice);
197 std::ranges::transform(ring_params, ring_analog_devices.begin(),
198 Common::Input::CreateInputDevice);
199 std::ranges::transform(nfc_params, nfc_devices.begin(), Common::Input::CreateInputDevice);
200 std::ranges::transform(output_params, output_devices.begin(),
201 Common::Input::CreateOutputDevice);
202
203 // Initialize TAS devices
204 std::ranges::transform(tas_button_params, tas_button_devices.begin(),
205 Common::Input::CreateInputDevice);
206 std::ranges::transform(tas_stick_params, tas_stick_devices.begin(),
207 Common::Input::CreateInputDevice);
208
209 // Initialize virtual gamepad devices
210 std::ranges::transform(virtual_button_params, virtual_button_devices.begin(),
211 Common::Input::CreateInputDevice);
212 std::ranges::transform(virtual_stick_params, virtual_stick_devices.begin(),
213 Common::Input::CreateInputDevice);
214 std::ranges::transform(virtual_motion_params, virtual_motion_devices.begin(),
215 Common::Input::CreateInputDevice);
216}
217
218void EmulatedController::LoadTASParams() {
219 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
220 Common::ParamPackage common_params{};
221 common_params.Set("engine", "tas");
222 common_params.Set("port", static_cast<int>(player_index));
223 for (auto& param : tas_button_params) {
224 param = common_params;
225 }
226 for (auto& param : tas_stick_params) {
227 param = common_params;
228 }
229
230 // TODO(german77): Replace this with an input profile or something better
231 tas_button_params[Settings::NativeButton::A].Set("button", 0);
232 tas_button_params[Settings::NativeButton::B].Set("button", 1);
233 tas_button_params[Settings::NativeButton::X].Set("button", 2);
234 tas_button_params[Settings::NativeButton::Y].Set("button", 3);
235 tas_button_params[Settings::NativeButton::LStick].Set("button", 4);
236 tas_button_params[Settings::NativeButton::RStick].Set("button", 5);
237 tas_button_params[Settings::NativeButton::L].Set("button", 6);
238 tas_button_params[Settings::NativeButton::R].Set("button", 7);
239 tas_button_params[Settings::NativeButton::ZL].Set("button", 8);
240 tas_button_params[Settings::NativeButton::ZR].Set("button", 9);
241 tas_button_params[Settings::NativeButton::Plus].Set("button", 10);
242 tas_button_params[Settings::NativeButton::Minus].Set("button", 11);
243 tas_button_params[Settings::NativeButton::DLeft].Set("button", 12);
244 tas_button_params[Settings::NativeButton::DUp].Set("button", 13);
245 tas_button_params[Settings::NativeButton::DRight].Set("button", 14);
246 tas_button_params[Settings::NativeButton::DDown].Set("button", 15);
247 tas_button_params[Settings::NativeButton::SLLeft].Set("button", 16);
248 tas_button_params[Settings::NativeButton::SRLeft].Set("button", 17);
249 tas_button_params[Settings::NativeButton::Home].Set("button", 18);
250 tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
251 tas_button_params[Settings::NativeButton::SLRight].Set("button", 20);
252 tas_button_params[Settings::NativeButton::SRRight].Set("button", 21);
253
254 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
255 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
256 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2);
257 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);
258
259 // set to optimal stick to avoid sanitizing the stick and tweaking the coordinates
260 // making sure they play back in the game as originally written down in the script file
261 tas_stick_params[Settings::NativeAnalog::LStick].Set("deadzone", 0.0f);
262 tas_stick_params[Settings::NativeAnalog::LStick].Set("range", 1.0f);
263 tas_stick_params[Settings::NativeAnalog::RStick].Set("deadzone", 0.0f);
264 tas_stick_params[Settings::NativeAnalog::RStick].Set("range", 1.0f);
265}
266
267void EmulatedController::LoadVirtualGamepadParams() {
268 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
269 Common::ParamPackage common_params{};
270 common_params.Set("engine", "virtual_gamepad");
271 common_params.Set("port", static_cast<int>(player_index));
272 for (auto& param : virtual_button_params) {
273 param = common_params;
274 }
275 for (auto& param : virtual_stick_params) {
276 param = common_params;
277 }
278 for (auto& param : virtual_stick_params) {
279 param = common_params;
280 }
281 for (auto& param : virtual_motion_params) {
282 param = common_params;
283 }
284
285 // TODO(german77): Replace this with an input profile or something better
286 virtual_button_params[Settings::NativeButton::A].Set("button", 0);
287 virtual_button_params[Settings::NativeButton::B].Set("button", 1);
288 virtual_button_params[Settings::NativeButton::X].Set("button", 2);
289 virtual_button_params[Settings::NativeButton::Y].Set("button", 3);
290 virtual_button_params[Settings::NativeButton::LStick].Set("button", 4);
291 virtual_button_params[Settings::NativeButton::RStick].Set("button", 5);
292 virtual_button_params[Settings::NativeButton::L].Set("button", 6);
293 virtual_button_params[Settings::NativeButton::R].Set("button", 7);
294 virtual_button_params[Settings::NativeButton::ZL].Set("button", 8);
295 virtual_button_params[Settings::NativeButton::ZR].Set("button", 9);
296 virtual_button_params[Settings::NativeButton::Plus].Set("button", 10);
297 virtual_button_params[Settings::NativeButton::Minus].Set("button", 11);
298 virtual_button_params[Settings::NativeButton::DLeft].Set("button", 12);
299 virtual_button_params[Settings::NativeButton::DUp].Set("button", 13);
300 virtual_button_params[Settings::NativeButton::DRight].Set("button", 14);
301 virtual_button_params[Settings::NativeButton::DDown].Set("button", 15);
302 virtual_button_params[Settings::NativeButton::SLLeft].Set("button", 16);
303 virtual_button_params[Settings::NativeButton::SRLeft].Set("button", 17);
304 virtual_button_params[Settings::NativeButton::Home].Set("button", 18);
305 virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
306 virtual_button_params[Settings::NativeButton::SLRight].Set("button", 20);
307 virtual_button_params[Settings::NativeButton::SRRight].Set("button", 21);
308
309 virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
310 virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
311 virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2);
312 virtual_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);
313 virtual_stick_params[Settings::NativeAnalog::LStick].Set("deadzone", 0.0f);
314 virtual_stick_params[Settings::NativeAnalog::LStick].Set("range", 1.0f);
315 virtual_stick_params[Settings::NativeAnalog::RStick].Set("deadzone", 0.0f);
316 virtual_stick_params[Settings::NativeAnalog::RStick].Set("range", 1.0f);
317
318 virtual_motion_params[Settings::NativeMotion::MotionLeft].Set("motion", 0);
319 virtual_motion_params[Settings::NativeMotion::MotionRight].Set("motion", 0);
320}
321
322void EmulatedController::ReloadInput() {
323 // If you load any device here add the equivalent to the UnloadInput() function
324 LoadDevices();
325 for (std::size_t index = 0; index < button_devices.size(); ++index) {
326 if (!button_devices[index]) {
327 continue;
328 }
329 const auto uuid = Common::UUID{button_params[index].Get("guid", "")};
330 button_devices[index]->SetCallback({
331 .on_change =
332 [this, index, uuid](const Common::Input::CallbackStatus& callback) {
333 SetButton(callback, index, uuid);
334 },
335 });
336 button_devices[index]->ForceUpdate();
337 }
338
339 for (std::size_t index = 0; index < stick_devices.size(); ++index) {
340 if (!stick_devices[index]) {
341 continue;
342 }
343 const auto uuid = Common::UUID{stick_params[index].Get("guid", "")};
344 stick_devices[index]->SetCallback({
345 .on_change =
346 [this, index, uuid](const Common::Input::CallbackStatus& callback) {
347 SetStick(callback, index, uuid);
348 },
349 });
350 stick_devices[index]->ForceUpdate();
351 }
352
353 for (std::size_t index = 0; index < trigger_devices.size(); ++index) {
354 if (!trigger_devices[index]) {
355 continue;
356 }
357 const auto uuid = Common::UUID{trigger_params[index].Get("guid", "")};
358 trigger_devices[index]->SetCallback({
359 .on_change =
360 [this, index, uuid](const Common::Input::CallbackStatus& callback) {
361 SetTrigger(callback, index, uuid);
362 },
363 });
364 trigger_devices[index]->ForceUpdate();
365 }
366
367 for (std::size_t index = 0; index < battery_devices.size(); ++index) {
368 if (!battery_devices[index]) {
369 continue;
370 }
371 battery_devices[index]->SetCallback({
372 .on_change =
373 [this, index](const Common::Input::CallbackStatus& callback) {
374 SetBattery(callback, index);
375 },
376 });
377 battery_devices[index]->ForceUpdate();
378 }
379
380 for (std::size_t index = 0; index < color_devices.size(); ++index) {
381 if (!color_devices[index]) {
382 continue;
383 }
384 color_devices[index]->SetCallback({
385 .on_change =
386 [this, index](const Common::Input::CallbackStatus& callback) {
387 SetColors(callback, index);
388 },
389 });
390 color_devices[index]->ForceUpdate();
391 }
392
393 for (std::size_t index = 0; index < motion_devices.size(); ++index) {
394 if (!motion_devices[index]) {
395 continue;
396 }
397 motion_devices[index]->SetCallback({
398 .on_change =
399 [this, index](const Common::Input::CallbackStatus& callback) {
400 SetMotion(callback, index);
401 },
402 });
403
404 // Restore motion state
405 auto& emulated_motion = controller.motion_values[index].emulated;
406 auto& motion = controller.motion_state[index];
407 emulated_motion.ResetRotations();
408 emulated_motion.ResetQuaternion();
409 motion.accel = emulated_motion.GetAcceleration();
410 motion.gyro = emulated_motion.GetGyroscope();
411 motion.rotation = emulated_motion.GetRotations();
412 motion.euler = emulated_motion.GetEulerAngles();
413 motion.orientation = emulated_motion.GetOrientation();
414 motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity);
415 }
416
417 for (std::size_t index = 0; index < camera_devices.size(); ++index) {
418 if (!camera_devices[index]) {
419 continue;
420 }
421 camera_devices[index]->SetCallback({
422 .on_change =
423 [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); },
424 });
425 camera_devices[index]->ForceUpdate();
426 }
427
428 for (std::size_t index = 0; index < ring_analog_devices.size(); ++index) {
429 if (!ring_analog_devices[index]) {
430 continue;
431 }
432 ring_analog_devices[index]->SetCallback({
433 .on_change =
434 [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
435 });
436 ring_analog_devices[index]->ForceUpdate();
437 }
438
439 for (std::size_t index = 0; index < nfc_devices.size(); ++index) {
440 if (!nfc_devices[index]) {
441 continue;
442 }
443 nfc_devices[index]->SetCallback({
444 .on_change =
445 [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); },
446 });
447 nfc_devices[index]->ForceUpdate();
448 }
449
450 // Register TAS devices. No need to force update
451 for (std::size_t index = 0; index < tas_button_devices.size(); ++index) {
452 if (!tas_button_devices[index]) {
453 continue;
454 }
455 tas_button_devices[index]->SetCallback({
456 .on_change =
457 [this, index](const Common::Input::CallbackStatus& callback) {
458 SetButton(callback, index, TAS_UUID);
459 },
460 });
461 }
462
463 for (std::size_t index = 0; index < tas_stick_devices.size(); ++index) {
464 if (!tas_stick_devices[index]) {
465 continue;
466 }
467 tas_stick_devices[index]->SetCallback({
468 .on_change =
469 [this, index](const Common::Input::CallbackStatus& callback) {
470 SetStick(callback, index, TAS_UUID);
471 },
472 });
473 }
474
475 // Register virtual devices. No need to force update
476 for (std::size_t index = 0; index < virtual_button_devices.size(); ++index) {
477 if (!virtual_button_devices[index]) {
478 continue;
479 }
480 virtual_button_devices[index]->SetCallback({
481 .on_change =
482 [this, index](const Common::Input::CallbackStatus& callback) {
483 SetButton(callback, index, VIRTUAL_UUID);
484 },
485 });
486 }
487
488 for (std::size_t index = 0; index < virtual_stick_devices.size(); ++index) {
489 if (!virtual_stick_devices[index]) {
490 continue;
491 }
492 virtual_stick_devices[index]->SetCallback({
493 .on_change =
494 [this, index](const Common::Input::CallbackStatus& callback) {
495 SetStick(callback, index, VIRTUAL_UUID);
496 },
497 });
498 }
499
500 for (std::size_t index = 0; index < virtual_motion_devices.size(); ++index) {
501 if (!virtual_motion_devices[index]) {
502 continue;
503 }
504 virtual_motion_devices[index]->SetCallback({
505 .on_change =
506 [this, index](const Common::Input::CallbackStatus& callback) {
507 SetMotion(callback, index);
508 },
509 });
510 }
511 turbo_button_state = 0;
512 is_initalized = true;
513}
514
515void EmulatedController::UnloadInput() {
516 is_initalized = false;
517 for (auto& button : button_devices) {
518 button.reset();
519 }
520 for (auto& stick : stick_devices) {
521 stick.reset();
522 }
523 for (auto& motion : motion_devices) {
524 motion.reset();
525 }
526 for (auto& trigger : trigger_devices) {
527 trigger.reset();
528 }
529 for (auto& battery : battery_devices) {
530 battery.reset();
531 }
532 for (auto& color : color_devices) {
533 color.reset();
534 }
535 for (auto& output : output_devices) {
536 output.reset();
537 }
538 for (auto& button : tas_button_devices) {
539 button.reset();
540 }
541 for (auto& stick : tas_stick_devices) {
542 stick.reset();
543 }
544 for (auto& button : virtual_button_devices) {
545 button.reset();
546 }
547 for (auto& stick : virtual_stick_devices) {
548 stick.reset();
549 }
550 for (auto& motion : virtual_motion_devices) {
551 motion.reset();
552 }
553 for (auto& camera : camera_devices) {
554 camera.reset();
555 }
556 for (auto& ring : ring_analog_devices) {
557 ring.reset();
558 }
559 for (auto& nfc : nfc_devices) {
560 nfc.reset();
561 }
562}
563
564void EmulatedController::EnableConfiguration() {
565 std::scoped_lock lock{connect_mutex, npad_mutex};
566 is_configuring = true;
567 tmp_is_connected = is_connected;
568 tmp_npad_type = npad_type;
569}
570
571void EmulatedController::DisableConfiguration() {
572 is_configuring = false;
573
574 // Get Joycon colors before turning on the controller
575 for (const auto& color_device : color_devices) {
576 color_device->ForceUpdate();
577 }
578
579 // Apply temporary npad type to the real controller
580 if (tmp_npad_type != npad_type) {
581 if (is_connected) {
582 Disconnect();
583 }
584 SetNpadStyleIndex(tmp_npad_type);
585 original_npad_type = tmp_npad_type;
586 }
587
588 // Apply temporary connected status to the real controller
589 if (tmp_is_connected != is_connected) {
590 if (tmp_is_connected) {
591 Connect();
592 return;
593 }
594 Disconnect();
595 }
596}
597
598void EmulatedController::EnableSystemButtons() {
599 std::scoped_lock lock{mutex};
600 system_buttons_enabled = true;
601}
602
603void EmulatedController::DisableSystemButtons() {
604 std::scoped_lock lock{mutex};
605 system_buttons_enabled = false;
606 controller.home_button_state.raw = 0;
607 controller.capture_button_state.raw = 0;
608}
609
610void EmulatedController::ResetSystemButtons() {
611 std::scoped_lock lock{mutex};
612 controller.home_button_state.home.Assign(false);
613 controller.capture_button_state.capture.Assign(false);
614}
615
616bool EmulatedController::IsConfiguring() const {
617 return is_configuring;
618}
619
620void EmulatedController::SaveCurrentConfig() {
621 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
622 auto& player = Settings::values.players.GetValue()[player_index];
623 player.connected = is_connected;
624 player.controller_type = MapNPadToSettingsType(npad_type);
625 for (std::size_t index = 0; index < player.buttons.size(); ++index) {
626 player.buttons[index] = button_params[index].Serialize();
627 }
628 for (std::size_t index = 0; index < player.analogs.size(); ++index) {
629 player.analogs[index] = stick_params[index].Serialize();
630 }
631 for (std::size_t index = 0; index < player.motions.size(); ++index) {
632 player.motions[index] = motion_params[index].Serialize();
633 }
634 if (npad_id_type == NpadIdType::Player1) {
635 Settings::values.ringcon_analogs = ring_params[0].Serialize();
636 }
637}
638
639void EmulatedController::RestoreConfig() {
640 if (!is_configuring) {
641 return;
642 }
643 ReloadFromSettings();
644}
645
646std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices() const {
647 std::vector<Common::ParamPackage> devices;
648 for (const auto& param : button_params) {
649 if (!param.Has("engine")) {
650 continue;
651 }
652 const auto devices_it = std::find_if(
653 devices.begin(), devices.end(), [&param](const Common::ParamPackage& param_) {
654 return param.Get("engine", "") == param_.Get("engine", "") &&
655 param.Get("guid", "") == param_.Get("guid", "") &&
656 param.Get("port", 0) == param_.Get("port", 0) &&
657 param.Get("pad", 0) == param_.Get("pad", 0);
658 });
659 if (devices_it != devices.end()) {
660 continue;
661 }
662
663 auto& device = devices.emplace_back();
664 device.Set("engine", param.Get("engine", ""));
665 device.Set("guid", param.Get("guid", ""));
666 device.Set("port", param.Get("port", 0));
667 device.Set("pad", param.Get("pad", 0));
668 }
669
670 for (const auto& param : stick_params) {
671 if (!param.Has("engine")) {
672 continue;
673 }
674 if (param.Get("engine", "") == "analog_from_button") {
675 continue;
676 }
677 const auto devices_it = std::find_if(
678 devices.begin(), devices.end(), [&param](const Common::ParamPackage& param_) {
679 return param.Get("engine", "") == param_.Get("engine", "") &&
680 param.Get("guid", "") == param_.Get("guid", "") &&
681 param.Get("port", 0) == param_.Get("port", 0) &&
682 param.Get("pad", 0) == param_.Get("pad", 0);
683 });
684 if (devices_it != devices.end()) {
685 continue;
686 }
687
688 auto& device = devices.emplace_back();
689 device.Set("engine", param.Get("engine", ""));
690 device.Set("guid", param.Get("guid", ""));
691 device.Set("port", param.Get("port", 0));
692 device.Set("pad", param.Get("pad", 0));
693 }
694 return devices;
695}
696
697Common::ParamPackage EmulatedController::GetButtonParam(std::size_t index) const {
698 if (index >= button_params.size()) {
699 return {};
700 }
701 return button_params[index];
702}
703
704Common::ParamPackage EmulatedController::GetStickParam(std::size_t index) const {
705 if (index >= stick_params.size()) {
706 return {};
707 }
708 return stick_params[index];
709}
710
711Common::ParamPackage EmulatedController::GetMotionParam(std::size_t index) const {
712 if (index >= motion_params.size()) {
713 return {};
714 }
715 return motion_params[index];
716}
717
718void EmulatedController::SetButtonParam(std::size_t index, Common::ParamPackage param) {
719 if (index >= button_params.size()) {
720 return;
721 }
722 button_params[index] = std::move(param);
723 ReloadInput();
724}
725
726void EmulatedController::SetStickParam(std::size_t index, Common::ParamPackage param) {
727 if (index >= stick_params.size()) {
728 return;
729 }
730 stick_params[index] = std::move(param);
731 ReloadInput();
732}
733
734void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage param) {
735 if (index >= motion_params.size()) {
736 return;
737 }
738 motion_params[index] = std::move(param);
739 ReloadInput();
740}
741
742void EmulatedController::StartMotionCalibration() {
743 for (ControllerMotionInfo& motion : controller.motion_values) {
744 motion.emulated.Calibrate();
745 }
746}
747
748void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
749 Common::UUID uuid) {
750 if (index >= controller.button_values.size()) {
751 return;
752 }
753 std::unique_lock lock{mutex};
754 bool value_changed = false;
755 const auto new_status = TransformToButton(callback);
756 auto& current_status = controller.button_values[index];
757
758 // Only read button values that have the same uuid or are pressed once
759 if (current_status.uuid != uuid) {
760 if (!new_status.value) {
761 return;
762 }
763 }
764
765 current_status.toggle = new_status.toggle;
766 current_status.turbo = new_status.turbo;
767 current_status.uuid = uuid;
768
769 // Update button status with current
770 if (!current_status.toggle) {
771 current_status.locked = false;
772 if (current_status.value != new_status.value) {
773 current_status.value = new_status.value;
774 value_changed = true;
775 }
776 } else {
777 // Toggle button and lock status
778 if (new_status.value && !current_status.locked) {
779 current_status.locked = true;
780 current_status.value = !current_status.value;
781 value_changed = true;
782 }
783
784 // Unlock button ready for next press
785 if (!new_status.value && current_status.locked) {
786 current_status.locked = false;
787 }
788 }
789
790 if (!value_changed) {
791 return;
792 }
793
794 if (is_configuring) {
795 controller.npad_button_state.raw = NpadButton::None;
796 controller.debug_pad_button_state.raw = 0;
797 controller.home_button_state.raw = 0;
798 controller.capture_button_state.raw = 0;
799 lock.unlock();
800 TriggerOnChange(ControllerTriggerType::Button, false);
801 return;
802 }
803
804 // GC controllers have triggers not buttons
805 if (npad_type == NpadStyleIndex::GameCube) {
806 if (index == Settings::NativeButton::ZR) {
807 return;
808 }
809 if (index == Settings::NativeButton::ZL) {
810 return;
811 }
812 }
813
814 switch (index) {
815 case Settings::NativeButton::A:
816 controller.npad_button_state.a.Assign(current_status.value);
817 controller.debug_pad_button_state.a.Assign(current_status.value);
818 break;
819 case Settings::NativeButton::B:
820 controller.npad_button_state.b.Assign(current_status.value);
821 controller.debug_pad_button_state.b.Assign(current_status.value);
822 break;
823 case Settings::NativeButton::X:
824 controller.npad_button_state.x.Assign(current_status.value);
825 controller.debug_pad_button_state.x.Assign(current_status.value);
826 break;
827 case Settings::NativeButton::Y:
828 controller.npad_button_state.y.Assign(current_status.value);
829 controller.debug_pad_button_state.y.Assign(current_status.value);
830 break;
831 case Settings::NativeButton::LStick:
832 controller.npad_button_state.stick_l.Assign(current_status.value);
833 break;
834 case Settings::NativeButton::RStick:
835 controller.npad_button_state.stick_r.Assign(current_status.value);
836 break;
837 case Settings::NativeButton::L:
838 controller.npad_button_state.l.Assign(current_status.value);
839 controller.debug_pad_button_state.l.Assign(current_status.value);
840 break;
841 case Settings::NativeButton::R:
842 controller.npad_button_state.r.Assign(current_status.value);
843 controller.debug_pad_button_state.r.Assign(current_status.value);
844 break;
845 case Settings::NativeButton::ZL:
846 controller.npad_button_state.zl.Assign(current_status.value);
847 controller.debug_pad_button_state.zl.Assign(current_status.value);
848 break;
849 case Settings::NativeButton::ZR:
850 controller.npad_button_state.zr.Assign(current_status.value);
851 controller.debug_pad_button_state.zr.Assign(current_status.value);
852 break;
853 case Settings::NativeButton::Plus:
854 controller.npad_button_state.plus.Assign(current_status.value);
855 controller.debug_pad_button_state.plus.Assign(current_status.value);
856 break;
857 case Settings::NativeButton::Minus:
858 controller.npad_button_state.minus.Assign(current_status.value);
859 controller.debug_pad_button_state.minus.Assign(current_status.value);
860 break;
861 case Settings::NativeButton::DLeft:
862 controller.npad_button_state.left.Assign(current_status.value);
863 controller.debug_pad_button_state.d_left.Assign(current_status.value);
864 break;
865 case Settings::NativeButton::DUp:
866 controller.npad_button_state.up.Assign(current_status.value);
867 controller.debug_pad_button_state.d_up.Assign(current_status.value);
868 break;
869 case Settings::NativeButton::DRight:
870 controller.npad_button_state.right.Assign(current_status.value);
871 controller.debug_pad_button_state.d_right.Assign(current_status.value);
872 break;
873 case Settings::NativeButton::DDown:
874 controller.npad_button_state.down.Assign(current_status.value);
875 controller.debug_pad_button_state.d_down.Assign(current_status.value);
876 break;
877 case Settings::NativeButton::SLLeft:
878 controller.npad_button_state.left_sl.Assign(current_status.value);
879 break;
880 case Settings::NativeButton::SLRight:
881 controller.npad_button_state.right_sl.Assign(current_status.value);
882 break;
883 case Settings::NativeButton::SRLeft:
884 controller.npad_button_state.left_sr.Assign(current_status.value);
885 break;
886 case Settings::NativeButton::SRRight:
887 controller.npad_button_state.right_sr.Assign(current_status.value);
888 break;
889 case Settings::NativeButton::Home:
890 if (!system_buttons_enabled) {
891 break;
892 }
893 controller.home_button_state.home.Assign(current_status.value);
894 break;
895 case Settings::NativeButton::Screenshot:
896 if (!system_buttons_enabled) {
897 break;
898 }
899 controller.capture_button_state.capture.Assign(current_status.value);
900 break;
901 }
902
903 lock.unlock();
904
905 if (!is_connected) {
906 if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) {
907 Connect();
908 }
909 if (npad_id_type == NpadIdType::Handheld && npad_type == NpadStyleIndex::Handheld) {
910 Connect();
911 }
912 }
913 TriggerOnChange(ControllerTriggerType::Button, true);
914}
915
916void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
917 Common::UUID uuid) {
918 if (index >= controller.stick_values.size()) {
919 return;
920 }
921 auto trigger_guard =
922 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Stick, !is_configuring); });
923 std::scoped_lock lock{mutex};
924 const auto stick_value = TransformToStick(callback);
925
926 // Only read stick values that have the same uuid or are over the threshold to avoid flapping
927 if (controller.stick_values[index].uuid != uuid) {
928 const bool is_tas = uuid == TAS_UUID;
929 if (is_tas && stick_value.x.value == 0 && stick_value.y.value == 0) {
930 trigger_guard.Cancel();
931 return;
932 }
933 if (!is_tas && !stick_value.down && !stick_value.up && !stick_value.left &&
934 !stick_value.right) {
935 trigger_guard.Cancel();
936 return;
937 }
938 }
939
940 controller.stick_values[index] = stick_value;
941 controller.stick_values[index].uuid = uuid;
942
943 if (is_configuring) {
944 controller.analog_stick_state.left = {};
945 controller.analog_stick_state.right = {};
946 return;
947 }
948
949 const AnalogStickState stick{
950 .x = static_cast<s32>(controller.stick_values[index].x.value * HID_JOYSTICK_MAX),
951 .y = static_cast<s32>(controller.stick_values[index].y.value * HID_JOYSTICK_MAX),
952 };
953
954 switch (index) {
955 case Settings::NativeAnalog::LStick:
956 controller.analog_stick_state.left = stick;
957 controller.npad_button_state.stick_l_left.Assign(controller.stick_values[index].left);
958 controller.npad_button_state.stick_l_up.Assign(controller.stick_values[index].up);
959 controller.npad_button_state.stick_l_right.Assign(controller.stick_values[index].right);
960 controller.npad_button_state.stick_l_down.Assign(controller.stick_values[index].down);
961 break;
962 case Settings::NativeAnalog::RStick:
963 controller.analog_stick_state.right = stick;
964 controller.npad_button_state.stick_r_left.Assign(controller.stick_values[index].left);
965 controller.npad_button_state.stick_r_up.Assign(controller.stick_values[index].up);
966 controller.npad_button_state.stick_r_right.Assign(controller.stick_values[index].right);
967 controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down);
968 break;
969 }
970}
971
972void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback,
973 std::size_t index, Common::UUID uuid) {
974 if (index >= controller.trigger_values.size()) {
975 return;
976 }
977 auto trigger_guard =
978 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring); });
979 std::scoped_lock lock{mutex};
980 const auto trigger_value = TransformToTrigger(callback);
981
982 // Only read trigger values that have the same uuid or are pressed once
983 if (controller.trigger_values[index].uuid != uuid) {
984 if (!trigger_value.pressed.value) {
985 return;
986 }
987 }
988
989 controller.trigger_values[index] = trigger_value;
990 controller.trigger_values[index].uuid = uuid;
991
992 if (is_configuring) {
993 controller.gc_trigger_state.left = 0;
994 controller.gc_trigger_state.right = 0;
995 return;
996 }
997
998 // Only GC controllers have analog triggers
999 if (npad_type != NpadStyleIndex::GameCube) {
1000 trigger_guard.Cancel();
1001 return;
1002 }
1003
1004 const auto& trigger = controller.trigger_values[index];
1005
1006 switch (index) {
1007 case Settings::NativeTrigger::LTrigger:
1008 controller.gc_trigger_state.left = static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX);
1009 controller.npad_button_state.zl.Assign(trigger.pressed.value);
1010 break;
1011 case Settings::NativeTrigger::RTrigger:
1012 controller.gc_trigger_state.right =
1013 static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX);
1014 controller.npad_button_state.zr.Assign(trigger.pressed.value);
1015 break;
1016 }
1017}
1018
1019void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback,
1020 std::size_t index) {
1021 if (index >= controller.motion_values.size()) {
1022 return;
1023 }
1024 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Motion, !is_configuring); });
1025 std::scoped_lock lock{mutex};
1026 auto& raw_status = controller.motion_values[index].raw_status;
1027 auto& emulated = controller.motion_values[index].emulated;
1028
1029 raw_status = TransformToMotion(callback);
1030 emulated.SetAcceleration(Common::Vec3f{
1031 raw_status.accel.x.value,
1032 raw_status.accel.y.value,
1033 raw_status.accel.z.value,
1034 });
1035 emulated.SetGyroscope(Common::Vec3f{
1036 raw_status.gyro.x.value,
1037 raw_status.gyro.y.value,
1038 raw_status.gyro.z.value,
1039 });
1040 emulated.SetUserGyroThreshold(raw_status.gyro.x.properties.threshold);
1041 emulated.UpdateRotation(raw_status.delta_timestamp);
1042 emulated.UpdateOrientation(raw_status.delta_timestamp);
1043
1044 auto& motion = controller.motion_state[index];
1045 motion.accel = emulated.GetAcceleration();
1046 motion.gyro = emulated.GetGyroscope();
1047 motion.rotation = emulated.GetRotations();
1048 motion.euler = emulated.GetEulerAngles();
1049 motion.orientation = emulated.GetOrientation();
1050 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
1051}
1052
1053void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback,
1054 std::size_t index) {
1055 if (index >= controller.color_values.size()) {
1056 return;
1057 }
1058 auto trigger_guard =
1059 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Color, !is_configuring); });
1060 std::scoped_lock lock{mutex};
1061 controller.color_values[index] = TransformToColor(callback);
1062
1063 if (is_configuring) {
1064 return;
1065 }
1066
1067 if (controller.color_values[index].body == 0) {
1068 trigger_guard.Cancel();
1069 return;
1070 }
1071
1072 controller.colors_state.fullkey = {
1073 .body = GetNpadColor(controller.color_values[index].body),
1074 .button = GetNpadColor(controller.color_values[index].buttons),
1075 };
1076 if (npad_type == NpadStyleIndex::ProController) {
1077 controller.colors_state.left = {
1078 .body = GetNpadColor(controller.color_values[index].left_grip),
1079 .button = GetNpadColor(controller.color_values[index].buttons),
1080 };
1081 controller.colors_state.right = {
1082 .body = GetNpadColor(controller.color_values[index].right_grip),
1083 .button = GetNpadColor(controller.color_values[index].buttons),
1084 };
1085 } else {
1086 switch (index) {
1087 case LeftIndex:
1088 controller.colors_state.left = {
1089 .body = GetNpadColor(controller.color_values[index].body),
1090 .button = GetNpadColor(controller.color_values[index].buttons),
1091 };
1092 break;
1093 case RightIndex:
1094 controller.colors_state.right = {
1095 .body = GetNpadColor(controller.color_values[index].body),
1096 .button = GetNpadColor(controller.color_values[index].buttons),
1097 };
1098 break;
1099 }
1100 }
1101}
1102
1103void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback,
1104 std::size_t index) {
1105 if (index >= controller.battery_values.size()) {
1106 return;
1107 }
1108 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Battery, !is_configuring); });
1109 std::scoped_lock lock{mutex};
1110 controller.battery_values[index] = TransformToBattery(callback);
1111
1112 if (is_configuring) {
1113 return;
1114 }
1115
1116 bool is_charging = false;
1117 bool is_powered = false;
1118 NpadBatteryLevel battery_level = NpadBatteryLevel::Empty;
1119 switch (controller.battery_values[index]) {
1120 case Common::Input::BatteryLevel::Charging:
1121 is_charging = true;
1122 is_powered = true;
1123 battery_level = NpadBatteryLevel::Full;
1124 break;
1125 case Common::Input::BatteryLevel::Medium:
1126 battery_level = NpadBatteryLevel::High;
1127 break;
1128 case Common::Input::BatteryLevel::Low:
1129 battery_level = NpadBatteryLevel::Low;
1130 break;
1131 case Common::Input::BatteryLevel::Critical:
1132 battery_level = NpadBatteryLevel::Critical;
1133 break;
1134 case Common::Input::BatteryLevel::Empty:
1135 battery_level = NpadBatteryLevel::Empty;
1136 break;
1137 case Common::Input::BatteryLevel::None:
1138 case Common::Input::BatteryLevel::Full:
1139 default:
1140 is_powered = true;
1141 battery_level = NpadBatteryLevel::Full;
1142 break;
1143 }
1144
1145 switch (index) {
1146 case LeftIndex:
1147 controller.battery_state.left = {
1148 .is_powered = is_powered,
1149 .is_charging = is_charging,
1150 .battery_level = battery_level,
1151 };
1152 break;
1153 case RightIndex:
1154 controller.battery_state.right = {
1155 .is_powered = is_powered,
1156 .is_charging = is_charging,
1157 .battery_level = battery_level,
1158 };
1159 break;
1160 case DualIndex:
1161 controller.battery_state.dual = {
1162 .is_powered = is_powered,
1163 .is_charging = is_charging,
1164 .battery_level = battery_level,
1165 };
1166 break;
1167 }
1168}
1169
1170void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
1171 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring); });
1172 std::scoped_lock lock{mutex};
1173 controller.camera_values = TransformToCamera(callback);
1174
1175 if (is_configuring) {
1176 return;
1177 }
1178
1179 controller.camera_state.sample++;
1180 controller.camera_state.format =
1181 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format);
1182 controller.camera_state.data = controller.camera_values.data;
1183}
1184
1185void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
1186 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::RingController, !is_configuring); });
1187 std::scoped_lock lock{mutex};
1188 const auto force_value = TransformToStick(callback);
1189
1190 controller.ring_analog_value = force_value.x;
1191
1192 if (is_configuring) {
1193 return;
1194 }
1195
1196 controller.ring_analog_state.force = force_value.x.value;
1197}
1198
1199void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1200 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring); });
1201 std::scoped_lock lock{mutex};
1202 controller.nfc_values = TransformToNfc(callback);
1203
1204 if (is_configuring) {
1205 return;
1206 }
1207
1208 controller.nfc_state = controller.nfc_values;
1209}
1210
1211bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
1212 if (!is_initalized) {
1213 return false;
1214 }
1215 if (device_index >= output_devices.size()) {
1216 return false;
1217 }
1218 if (!output_devices[device_index]) {
1219 return false;
1220 }
1221 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
1222 const auto& player = Settings::values.players.GetValue()[player_index];
1223 const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f;
1224
1225 if (!player.vibration_enabled) {
1226 return false;
1227 }
1228
1229 // Exponential amplification is too strong at low amplitudes. Switch to a linear
1230 // amplification if strength is set below 0.7f
1231 const Common::Input::VibrationAmplificationType type =
1232 strength > 0.7f ? Common::Input::VibrationAmplificationType::Exponential
1233 : Common::Input::VibrationAmplificationType::Linear;
1234
1235 const Common::Input::VibrationStatus status = {
1236 .low_amplitude = std::min(vibration.low_amplitude * strength, 1.0f),
1237 .low_frequency = vibration.low_frequency,
1238 .high_amplitude = std::min(vibration.high_amplitude * strength, 1.0f),
1239 .high_frequency = vibration.high_frequency,
1240 .type = type,
1241 };
1242 return output_devices[device_index]->SetVibration(status) ==
1243 Common::Input::DriverResult::Success;
1244}
1245
1246bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
1247 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
1248 const auto& player = Settings::values.players.GetValue()[player_index];
1249
1250 if (!is_initalized) {
1251 return false;
1252 }
1253
1254 if (!player.vibration_enabled) {
1255 return false;
1256 }
1257
1258 if (device_index >= output_devices.size()) {
1259 return false;
1260 }
1261
1262 if (!output_devices[device_index]) {
1263 return false;
1264 }
1265
1266 return output_devices[device_index]->IsVibrationEnabled();
1267}
1268
1269Common::Input::DriverResult EmulatedController::SetPollingMode(
1270 EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) {
1271 LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index);
1272
1273 if (!is_initalized) {
1274 return Common::Input::DriverResult::InvalidHandle;
1275 }
1276
1277 auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)];
1278 auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1279 auto& nfc_output_device = output_devices[3];
1280
1281 if (device_index == EmulatedDeviceIndex::LeftIndex) {
1282 controller.left_polling_mode = polling_mode;
1283 return left_output_device->SetPollingMode(polling_mode);
1284 }
1285
1286 if (device_index == EmulatedDeviceIndex::RightIndex) {
1287 controller.right_polling_mode = polling_mode;
1288 const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
1289 const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode);
1290
1291 // Restore previous state
1292 if (mapped_nfc_result != Common::Input::DriverResult::Success) {
1293 right_output_device->SetPollingMode(Common::Input::PollingMode::Active);
1294 }
1295
1296 if (virtual_nfc_result == Common::Input::DriverResult::Success) {
1297 return virtual_nfc_result;
1298 }
1299 return mapped_nfc_result;
1300 }
1301
1302 controller.left_polling_mode = polling_mode;
1303 controller.right_polling_mode = polling_mode;
1304 left_output_device->SetPollingMode(polling_mode);
1305 right_output_device->SetPollingMode(polling_mode);
1306 nfc_output_device->SetPollingMode(polling_mode);
1307 return Common::Input::DriverResult::Success;
1308}
1309
1310Common::Input::PollingMode EmulatedController::GetPollingMode(
1311 EmulatedDeviceIndex device_index) const {
1312 if (device_index == EmulatedDeviceIndex::LeftIndex) {
1313 return controller.left_polling_mode;
1314 }
1315 return controller.right_polling_mode;
1316}
1317
1318bool EmulatedController::SetCameraFormat(
1319 Core::IrSensor::ImageTransferProcessorFormat camera_format) {
1320 LOG_INFO(Service_HID, "Set camera format {}", camera_format);
1321
1322 if (!is_initalized) {
1323 return false;
1324 }
1325
1326 auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1327 auto& camera_output_device = output_devices[2];
1328
1329 if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
1330 camera_format)) == Common::Input::DriverResult::Success) {
1331 return true;
1332 }
1333
1334 // Fallback to Qt camera if native device doesn't have support
1335 return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
1336 camera_format)) == Common::Input::DriverResult::Success;
1337}
1338
1339Common::ParamPackage EmulatedController::GetRingParam() const {
1340 return ring_params[0];
1341}
1342
1343void EmulatedController::SetRingParam(Common::ParamPackage param) {
1344 ring_params[0] = std::move(param);
1345 ReloadInput();
1346}
1347
1348bool EmulatedController::HasNfc() const {
1349
1350 if (!is_initalized) {
1351 return false;
1352 }
1353
1354 const auto& nfc_output_device = output_devices[3];
1355
1356 switch (npad_type) {
1357 case NpadStyleIndex::JoyconRight:
1358 case NpadStyleIndex::JoyconDual:
1359 case NpadStyleIndex::ProController:
1360 case NpadStyleIndex::Handheld:
1361 break;
1362 default:
1363 return false;
1364 }
1365
1366 const bool has_virtual_nfc =
1367 npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld;
1368 const bool is_virtual_nfc_supported =
1369 nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported;
1370
1371 return is_connected && (has_virtual_nfc && is_virtual_nfc_supported);
1372}
1373
1374bool EmulatedController::AddNfcHandle() {
1375 nfc_handles++;
1376 return SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::NFC) ==
1377 Common::Input::DriverResult::Success;
1378}
1379
1380bool EmulatedController::RemoveNfcHandle() {
1381 nfc_handles--;
1382 if (nfc_handles <= 0) {
1383 return SetPollingMode(EmulatedDeviceIndex::RightIndex,
1384 Common::Input::PollingMode::Active) ==
1385 Common::Input::DriverResult::Success;
1386 }
1387 return true;
1388}
1389
1390bool EmulatedController::StartNfcPolling() {
1391 if (!is_initalized) {
1392 return false;
1393 }
1394
1395 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1396 auto& nfc_virtual_output_device = output_devices[3];
1397
1398 const auto device_result = nfc_output_device->StartNfcPolling();
1399 const auto virtual_device_result = nfc_virtual_output_device->StartNfcPolling();
1400
1401 return device_result == Common::Input::NfcState::Success ||
1402 virtual_device_result == Common::Input::NfcState::Success;
1403}
1404
1405bool EmulatedController::StopNfcPolling() {
1406 if (!is_initalized) {
1407 return false;
1408 }
1409
1410 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1411 auto& nfc_virtual_output_device = output_devices[3];
1412
1413 const auto device_result = nfc_output_device->StopNfcPolling();
1414 const auto virtual_device_result = nfc_virtual_output_device->StopNfcPolling();
1415
1416 return device_result == Common::Input::NfcState::Success ||
1417 virtual_device_result == Common::Input::NfcState::Success;
1418}
1419
1420bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
1421 if (!is_initalized) {
1422 return false;
1423 }
1424
1425 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1426 auto& nfc_virtual_output_device = output_devices[3];
1427
1428 if (nfc_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success) {
1429 return true;
1430 }
1431
1432 return nfc_virtual_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success;
1433}
1434
1435bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request,
1436 Common::Input::MifareRequest& out_data) {
1437 if (!is_initalized) {
1438 return false;
1439 }
1440
1441 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1442 auto& nfc_virtual_output_device = output_devices[3];
1443
1444 if (nfc_output_device->ReadMifareData(request, out_data) == Common::Input::NfcState::Success) {
1445 return true;
1446 }
1447
1448 return nfc_virtual_output_device->ReadMifareData(request, out_data) ==
1449 Common::Input::NfcState::Success;
1450}
1451
1452bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) {
1453 if (!is_initalized) {
1454 return false;
1455 }
1456
1457 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1458 auto& nfc_virtual_output_device = output_devices[3];
1459
1460 if (nfc_output_device->WriteMifareData(request) == Common::Input::NfcState::Success) {
1461 return true;
1462 }
1463
1464 return nfc_virtual_output_device->WriteMifareData(request) == Common::Input::NfcState::Success;
1465}
1466
1467bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
1468 if (!is_initalized) {
1469 return false;
1470 }
1471
1472 auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1473 auto& nfc_virtual_output_device = output_devices[3];
1474
1475 if (nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported) {
1476 return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success;
1477 }
1478
1479 return nfc_virtual_output_device->WriteNfcData(data) == Common::Input::NfcState::Success;
1480}
1481
1482void EmulatedController::SetLedPattern() {
1483 if (!is_initalized) {
1484 return;
1485 }
1486
1487 for (auto& device : output_devices) {
1488 if (!device) {
1489 continue;
1490 }
1491
1492 const LedPattern pattern = GetLedPattern();
1493 const Common::Input::LedStatus status = {
1494 .led_1 = pattern.position1 != 0,
1495 .led_2 = pattern.position2 != 0,
1496 .led_3 = pattern.position3 != 0,
1497 .led_4 = pattern.position4 != 0,
1498 };
1499 device->SetLED(status);
1500 }
1501}
1502
1503void EmulatedController::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode mode) {
1504 for (auto& motion : controller.motion_values) {
1505 switch (mode) {
1506 case GyroscopeZeroDriftMode::Loose:
1507 motion_sensitivity = motion.emulated.IsAtRestLoose;
1508 motion.emulated.SetGyroThreshold(motion.emulated.ThresholdLoose);
1509 break;
1510 case GyroscopeZeroDriftMode::Tight:
1511 motion_sensitivity = motion.emulated.IsAtRestThight;
1512 motion.emulated.SetGyroThreshold(motion.emulated.ThresholdThight);
1513 break;
1514 case GyroscopeZeroDriftMode::Standard:
1515 default:
1516 motion_sensitivity = motion.emulated.IsAtRestStandard;
1517 motion.emulated.SetGyroThreshold(motion.emulated.ThresholdStandard);
1518 break;
1519 }
1520 }
1521}
1522
1523void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) {
1524 supported_style_tag = supported_styles;
1525 if (!is_connected) {
1526 return;
1527 }
1528
1529 // Attempt to reconnect with the original type
1530 if (npad_type != original_npad_type) {
1531 Disconnect();
1532 const auto current_npad_type = npad_type;
1533 SetNpadStyleIndex(original_npad_type);
1534 if (IsControllerSupported()) {
1535 Connect();
1536 return;
1537 }
1538 SetNpadStyleIndex(current_npad_type);
1539 Connect();
1540 }
1541
1542 if (IsControllerSupported()) {
1543 return;
1544 }
1545
1546 Disconnect();
1547
1548 // Fallback Fullkey controllers to Pro controllers
1549 if (IsControllerFullkey() && supported_style_tag.fullkey) {
1550 LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
1551 SetNpadStyleIndex(NpadStyleIndex::ProController);
1552 Connect();
1553 return;
1554 }
1555
1556 // Fallback Dual joycon controllers to Pro controllers
1557 if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) {
1558 LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
1559 SetNpadStyleIndex(NpadStyleIndex::ProController);
1560 Connect();
1561 return;
1562 }
1563
1564 // Fallback Pro controllers to Dual joycon
1565 if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) {
1566 LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type);
1567 SetNpadStyleIndex(NpadStyleIndex::JoyconDual);
1568 Connect();
1569 return;
1570 }
1571
1572 LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
1573 npad_type);
1574}
1575
1576bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
1577 std::scoped_lock lock{mutex};
1578 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
1579 switch (type) {
1580 case NpadStyleIndex::ProController:
1581 case NpadStyleIndex::GameCube:
1582 case NpadStyleIndex::NES:
1583 case NpadStyleIndex::SNES:
1584 case NpadStyleIndex::N64:
1585 case NpadStyleIndex::SegaGenesis:
1586 return true;
1587 default:
1588 return false;
1589 }
1590}
1591
1592bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
1593 std::scoped_lock lock{mutex};
1594 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
1595 switch (type) {
1596 case NpadStyleIndex::ProController:
1597 return supported_style_tag.fullkey.As<bool>();
1598 case NpadStyleIndex::Handheld:
1599 return supported_style_tag.handheld.As<bool>();
1600 case NpadStyleIndex::JoyconDual:
1601 return supported_style_tag.joycon_dual.As<bool>();
1602 case NpadStyleIndex::JoyconLeft:
1603 return supported_style_tag.joycon_left.As<bool>();
1604 case NpadStyleIndex::JoyconRight:
1605 return supported_style_tag.joycon_right.As<bool>();
1606 case NpadStyleIndex::GameCube:
1607 return supported_style_tag.gamecube.As<bool>();
1608 case NpadStyleIndex::Pokeball:
1609 return supported_style_tag.palma.As<bool>();
1610 case NpadStyleIndex::NES:
1611 return supported_style_tag.lark.As<bool>();
1612 case NpadStyleIndex::SNES:
1613 return supported_style_tag.lucia.As<bool>();
1614 case NpadStyleIndex::N64:
1615 return supported_style_tag.lagoon.As<bool>();
1616 case NpadStyleIndex::SegaGenesis:
1617 return supported_style_tag.lager.As<bool>();
1618 default:
1619 return false;
1620 }
1621}
1622
1623void EmulatedController::Connect(bool use_temporary_value) {
1624 if (!IsControllerSupported(use_temporary_value)) {
1625 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
1626 LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
1627 return;
1628 }
1629
1630 auto trigger_guard =
1631 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); });
1632 std::scoped_lock lock{connect_mutex, mutex};
1633 if (is_configuring) {
1634 tmp_is_connected = true;
1635 return;
1636 }
1637
1638 if (is_connected) {
1639 trigger_guard.Cancel();
1640 return;
1641 }
1642 is_connected = true;
1643}
1644
1645void EmulatedController::Disconnect() {
1646 auto trigger_guard =
1647 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); });
1648 std::scoped_lock lock{connect_mutex, mutex};
1649 if (is_configuring) {
1650 tmp_is_connected = false;
1651 return;
1652 }
1653
1654 if (!is_connected) {
1655 trigger_guard.Cancel();
1656 return;
1657 }
1658 is_connected = false;
1659}
1660
1661bool EmulatedController::IsConnected(bool get_temporary_value) const {
1662 std::scoped_lock lock{connect_mutex};
1663 if (get_temporary_value && is_configuring) {
1664 return tmp_is_connected;
1665 }
1666 return is_connected;
1667}
1668
1669NpadIdType EmulatedController::GetNpadIdType() const {
1670 std::scoped_lock lock{mutex};
1671 return npad_id_type;
1672}
1673
1674NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
1675 std::scoped_lock lock{npad_mutex};
1676 if (get_temporary_value && is_configuring) {
1677 return tmp_npad_type;
1678 }
1679 return npad_type;
1680}
1681
1682void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
1683 auto trigger_guard =
1684 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); });
1685 std::scoped_lock lock{mutex, npad_mutex};
1686
1687 if (is_configuring) {
1688 if (tmp_npad_type == npad_type_) {
1689 trigger_guard.Cancel();
1690 return;
1691 }
1692 tmp_npad_type = npad_type_;
1693 return;
1694 }
1695
1696 if (npad_type == npad_type_) {
1697 trigger_guard.Cancel();
1698 return;
1699 }
1700 if (is_connected) {
1701 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
1702 Service::HID::NpadIdTypeToIndex(npad_id_type));
1703 }
1704 npad_type = npad_type_;
1705}
1706
1707LedPattern EmulatedController::GetLedPattern() const {
1708 switch (npad_id_type) {
1709 case NpadIdType::Player1:
1710 return LedPattern{1, 0, 0, 0};
1711 case NpadIdType::Player2:
1712 return LedPattern{1, 1, 0, 0};
1713 case NpadIdType::Player3:
1714 return LedPattern{1, 1, 1, 0};
1715 case NpadIdType::Player4:
1716 return LedPattern{1, 1, 1, 1};
1717 case NpadIdType::Player5:
1718 return LedPattern{1, 0, 0, 1};
1719 case NpadIdType::Player6:
1720 return LedPattern{1, 0, 1, 0};
1721 case NpadIdType::Player7:
1722 return LedPattern{1, 0, 1, 1};
1723 case NpadIdType::Player8:
1724 return LedPattern{0, 1, 1, 0};
1725 default:
1726 return LedPattern{0, 0, 0, 0};
1727 }
1728}
1729
1730ButtonValues EmulatedController::GetButtonsValues() const {
1731 std::scoped_lock lock{mutex};
1732 return controller.button_values;
1733}
1734
1735SticksValues EmulatedController::GetSticksValues() const {
1736 std::scoped_lock lock{mutex};
1737 return controller.stick_values;
1738}
1739
1740TriggerValues EmulatedController::GetTriggersValues() const {
1741 std::scoped_lock lock{mutex};
1742 return controller.trigger_values;
1743}
1744
1745ControllerMotionValues EmulatedController::GetMotionValues() const {
1746 std::scoped_lock lock{mutex};
1747 return controller.motion_values;
1748}
1749
1750ColorValues EmulatedController::GetColorsValues() const {
1751 std::scoped_lock lock{mutex};
1752 return controller.color_values;
1753}
1754
1755BatteryValues EmulatedController::GetBatteryValues() const {
1756 std::scoped_lock lock{mutex};
1757 return controller.battery_values;
1758}
1759
1760CameraValues EmulatedController::GetCameraValues() const {
1761 std::scoped_lock lock{mutex};
1762 return controller.camera_values;
1763}
1764
1765RingAnalogValue EmulatedController::GetRingSensorValues() const {
1766 return controller.ring_analog_value;
1767}
1768
1769HomeButtonState EmulatedController::GetHomeButtons() const {
1770 std::scoped_lock lock{mutex};
1771 if (is_configuring) {
1772 return {};
1773 }
1774 return controller.home_button_state;
1775}
1776
1777CaptureButtonState EmulatedController::GetCaptureButtons() const {
1778 std::scoped_lock lock{mutex};
1779 if (is_configuring) {
1780 return {};
1781 }
1782 return controller.capture_button_state;
1783}
1784
1785NpadButtonState EmulatedController::GetNpadButtons() const {
1786 std::scoped_lock lock{mutex};
1787 if (is_configuring) {
1788 return {};
1789 }
1790 return {controller.npad_button_state.raw & GetTurboButtonMask()};
1791}
1792
1793DebugPadButton EmulatedController::GetDebugPadButtons() const {
1794 std::scoped_lock lock{mutex};
1795 if (is_configuring) {
1796 return {};
1797 }
1798 return controller.debug_pad_button_state;
1799}
1800
1801AnalogSticks EmulatedController::GetSticks() const {
1802 std::scoped_lock lock{mutex};
1803
1804 if (is_configuring) {
1805 return {};
1806 }
1807
1808 return controller.analog_stick_state;
1809}
1810
1811NpadGcTriggerState EmulatedController::GetTriggers() const {
1812 std::scoped_lock lock{mutex};
1813 if (is_configuring) {
1814 return {};
1815 }
1816 return controller.gc_trigger_state;
1817}
1818
1819MotionState EmulatedController::GetMotions() const {
1820 std::unique_lock lock{mutex};
1821 return controller.motion_state;
1822}
1823
1824ControllerColors EmulatedController::GetColors() const {
1825 std::scoped_lock lock{mutex};
1826 return controller.colors_state;
1827}
1828
1829BatteryLevelState EmulatedController::GetBattery() const {
1830 std::scoped_lock lock{mutex};
1831 return controller.battery_state;
1832}
1833
1834const CameraState& EmulatedController::GetCamera() const {
1835 std::scoped_lock lock{mutex};
1836 return controller.camera_state;
1837}
1838
1839RingSensorForce EmulatedController::GetRingSensorForce() const {
1840 return controller.ring_analog_state;
1841}
1842
1843const NfcState& EmulatedController::GetNfc() const {
1844 std::scoped_lock lock{mutex};
1845 return controller.nfc_state;
1846}
1847
1848NpadColor EmulatedController::GetNpadColor(u32 color) {
1849 return {
1850 .r = static_cast<u8>((color >> 16) & 0xFF),
1851 .g = static_cast<u8>((color >> 8) & 0xFF),
1852 .b = static_cast<u8>(color & 0xFF),
1853 .a = 0xff,
1854 };
1855}
1856
1857void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
1858 std::scoped_lock lock{callback_mutex};
1859 for (const auto& poller_pair : callback_list) {
1860 const ControllerUpdateCallback& poller = poller_pair.second;
1861 if (!is_npad_service_update && poller.is_npad_service) {
1862 continue;
1863 }
1864 if (poller.on_change) {
1865 poller.on_change(type);
1866 }
1867 }
1868}
1869
1870int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
1871 std::scoped_lock lock{callback_mutex};
1872 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
1873 return last_callback_key++;
1874}
1875
1876void EmulatedController::DeleteCallback(int key) {
1877 std::scoped_lock lock{callback_mutex};
1878 const auto& iterator = callback_list.find(key);
1879 if (iterator == callback_list.end()) {
1880 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
1881 return;
1882 }
1883 callback_list.erase(iterator);
1884}
1885
1886void EmulatedController::StatusUpdate() {
1887 turbo_button_state = (turbo_button_state + 1) % (TURBO_BUTTON_DELAY * 2);
1888
1889 // Some drivers like key motion need constant refreshing
1890 for (std::size_t index = 0; index < motion_devices.size(); ++index) {
1891 const auto& raw_status = controller.motion_values[index].raw_status;
1892 auto& device = motion_devices[index];
1893 if (!raw_status.force_update) {
1894 continue;
1895 }
1896 if (!device) {
1897 continue;
1898 }
1899 device->ForceUpdate();
1900 }
1901}
1902
1903NpadButton EmulatedController::GetTurboButtonMask() const {
1904 // Apply no mask when disabled
1905 if (turbo_button_state < TURBO_BUTTON_DELAY) {
1906 return {NpadButton::All};
1907 }
1908
1909 NpadButtonState button_mask{};
1910 for (std::size_t index = 0; index < controller.button_values.size(); ++index) {
1911 if (!controller.button_values[index].turbo) {
1912 continue;
1913 }
1914
1915 switch (index) {
1916 case Settings::NativeButton::A:
1917 button_mask.a.Assign(1);
1918 break;
1919 case Settings::NativeButton::B:
1920 button_mask.b.Assign(1);
1921 break;
1922 case Settings::NativeButton::X:
1923 button_mask.x.Assign(1);
1924 break;
1925 case Settings::NativeButton::Y:
1926 button_mask.y.Assign(1);
1927 break;
1928 case Settings::NativeButton::L:
1929 button_mask.l.Assign(1);
1930 break;
1931 case Settings::NativeButton::R:
1932 button_mask.r.Assign(1);
1933 break;
1934 case Settings::NativeButton::ZL:
1935 button_mask.zl.Assign(1);
1936 break;
1937 case Settings::NativeButton::ZR:
1938 button_mask.zr.Assign(1);
1939 break;
1940 case Settings::NativeButton::DLeft:
1941 button_mask.left.Assign(1);
1942 break;
1943 case Settings::NativeButton::DUp:
1944 button_mask.up.Assign(1);
1945 break;
1946 case Settings::NativeButton::DRight:
1947 button_mask.right.Assign(1);
1948 break;
1949 case Settings::NativeButton::DDown:
1950 button_mask.down.Assign(1);
1951 break;
1952 case Settings::NativeButton::SLLeft:
1953 button_mask.left_sl.Assign(1);
1954 break;
1955 case Settings::NativeButton::SLRight:
1956 button_mask.right_sl.Assign(1);
1957 break;
1958 case Settings::NativeButton::SRLeft:
1959 button_mask.left_sr.Assign(1);
1960 break;
1961 case Settings::NativeButton::SRRight:
1962 button_mask.right_sr.Assign(1);
1963 break;
1964 default:
1965 break;
1966 }
1967 }
1968
1969 return static_cast<NpadButton>(~button_mask.raw);
1970}
1971
1972} // namespace Core::HID