summaryrefslogtreecommitdiff
path: root/src/core/hid/emulated_controller.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hid/emulated_controller.cpp')
-rw-r--r--src/core/hid/emulated_controller.cpp1139
1 files changed, 1139 insertions, 0 deletions
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
new file mode 100644
index 000000000..93372445b
--- /dev/null
+++ b/src/core/hid/emulated_controller.cpp
@@ -0,0 +1,1139 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include "core/hid/emulated_controller.h"
6#include "core/hid/input_converter.h"
7
8namespace Core::HID {
9constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
10constexpr s32 HID_TRIGGER_MAX = 0x7fff;
11
12EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {}
13
14EmulatedController::~EmulatedController() = default;
15
16NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) {
17 switch (type) {
18 case Settings::ControllerType::ProController:
19 return NpadStyleIndex::ProController;
20 case Settings::ControllerType::DualJoyconDetached:
21 return NpadStyleIndex::JoyconDual;
22 case Settings::ControllerType::LeftJoycon:
23 return NpadStyleIndex::JoyconLeft;
24 case Settings::ControllerType::RightJoycon:
25 return NpadStyleIndex::JoyconRight;
26 case Settings::ControllerType::Handheld:
27 return NpadStyleIndex::Handheld;
28 case Settings::ControllerType::GameCube:
29 return NpadStyleIndex::GameCube;
30 case Settings::ControllerType::Pokeball:
31 return NpadStyleIndex::Pokeball;
32 case Settings::ControllerType::NES:
33 return NpadStyleIndex::NES;
34 case Settings::ControllerType::SNES:
35 return NpadStyleIndex::SNES;
36 case Settings::ControllerType::N64:
37 return NpadStyleIndex::N64;
38 case Settings::ControllerType::SegaGenesis:
39 return NpadStyleIndex::SegaGenesis;
40 default:
41 return NpadStyleIndex::ProController;
42 }
43}
44
45Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) {
46 switch (type) {
47 case NpadStyleIndex::ProController:
48 return Settings::ControllerType::ProController;
49 case NpadStyleIndex::JoyconDual:
50 return Settings::ControllerType::DualJoyconDetached;
51 case NpadStyleIndex::JoyconLeft:
52 return Settings::ControllerType::LeftJoycon;
53 case NpadStyleIndex::JoyconRight:
54 return Settings::ControllerType::RightJoycon;
55 case NpadStyleIndex::Handheld:
56 return Settings::ControllerType::Handheld;
57 case NpadStyleIndex::GameCube:
58 return Settings::ControllerType::GameCube;
59 case NpadStyleIndex::Pokeball:
60 return Settings::ControllerType::Pokeball;
61 case NpadStyleIndex::NES:
62 return Settings::ControllerType::NES;
63 case NpadStyleIndex::SNES:
64 return Settings::ControllerType::SNES;
65 case NpadStyleIndex::N64:
66 return Settings::ControllerType::N64;
67 case NpadStyleIndex::SegaGenesis:
68 return Settings::ControllerType::SegaGenesis;
69 default:
70 return Settings::ControllerType::ProController;
71 }
72}
73
74void EmulatedController::ReloadFromSettings() {
75 const auto player_index = NpadIdTypeToIndex(npad_id_type);
76 const auto& player = Settings::values.players.GetValue()[player_index];
77
78 for (std::size_t index = 0; index < player.buttons.size(); ++index) {
79 button_params[index] = Common::ParamPackage(player.buttons[index]);
80 }
81 for (std::size_t index = 0; index < player.analogs.size(); ++index) {
82 stick_params[index] = Common::ParamPackage(player.analogs[index]);
83 }
84 for (std::size_t index = 0; index < player.motions.size(); ++index) {
85 motion_params[index] = Common::ParamPackage(player.motions[index]);
86 }
87
88 controller.colors_state.left = {
89 .body = player.body_color_left,
90 .button = player.button_color_left,
91 };
92
93 controller.colors_state.right = {
94 .body = player.body_color_right,
95 .button = player.button_color_right,
96 };
97
98 controller.colors_state.fullkey = controller.colors_state.left;
99
100 // Other or debug controller should always be a pro controller
101 if (npad_id_type != NpadIdType::Other) {
102 SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
103 } else {
104 SetNpadStyleIndex(NpadStyleIndex::ProController);
105 }
106
107 if (player.connected) {
108 Connect();
109 } else {
110 Disconnect();
111 }
112
113 ReloadInput();
114}
115
116void EmulatedController::LoadDevices() {
117 // TODO(german77): Use more buttons to detect the correct device
118 const auto left_joycon = button_params[Settings::NativeButton::DRight];
119 const auto right_joycon = button_params[Settings::NativeButton::A];
120
121 // Triggers for GC controllers
122 trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL];
123 trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR];
124
125 battery_params[LeftIndex] = left_joycon;
126 battery_params[RightIndex] = right_joycon;
127 battery_params[LeftIndex].Set("battery", true);
128 battery_params[RightIndex].Set("battery", true);
129
130 output_params[LeftIndex] = left_joycon;
131 output_params[RightIndex] = right_joycon;
132 output_params[LeftIndex].Set("output", true);
133 output_params[RightIndex].Set("output", true);
134
135 LoadTASParams();
136
137 std::transform(button_params.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
138 button_params.begin() + Settings::NativeButton::BUTTON_NS_END,
139 button_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
140 std::transform(stick_params.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
141 stick_params.begin() + Settings::NativeAnalog::STICK_HID_END,
142 stick_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
143 std::transform(motion_params.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
144 motion_params.begin() + Settings::NativeMotion::MOTION_HID_END,
145 motion_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
146 std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(),
147 Common::Input::CreateDevice<Common::Input::InputDevice>);
148 std::transform(battery_params.begin(), battery_params.begin(), battery_devices.end(),
149 Common::Input::CreateDevice<Common::Input::InputDevice>);
150 std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
151 Common::Input::CreateDevice<Common::Input::OutputDevice>);
152
153 // Initialize TAS devices
154 std::transform(tas_button_params.begin(), tas_button_params.end(), tas_button_devices.begin(),
155 Common::Input::CreateDevice<Common::Input::InputDevice>);
156 std::transform(tas_stick_params.begin(), tas_stick_params.end(), tas_stick_devices.begin(),
157 Common::Input::CreateDevice<Common::Input::InputDevice>);
158}
159
160void EmulatedController::LoadTASParams() {
161 const auto player_index = NpadIdTypeToIndex(npad_id_type);
162 Common::ParamPackage common_params{};
163 common_params.Set("engine", "tas");
164 common_params.Set("port", static_cast<int>(player_index));
165 for (auto& param : tas_button_params) {
166 param = common_params;
167 }
168 for (auto& param : tas_stick_params) {
169 param = common_params;
170 }
171
172 // TODO(german77): Replace this with an input profile or something better
173 tas_button_params[Settings::NativeButton::A].Set("button", 0);
174 tas_button_params[Settings::NativeButton::B].Set("button", 1);
175 tas_button_params[Settings::NativeButton::X].Set("button", 2);
176 tas_button_params[Settings::NativeButton::Y].Set("button", 3);
177 tas_button_params[Settings::NativeButton::LStick].Set("button", 4);
178 tas_button_params[Settings::NativeButton::RStick].Set("button", 5);
179 tas_button_params[Settings::NativeButton::L].Set("button", 6);
180 tas_button_params[Settings::NativeButton::R].Set("button", 7);
181 tas_button_params[Settings::NativeButton::ZL].Set("button", 8);
182 tas_button_params[Settings::NativeButton::ZR].Set("button", 9);
183 tas_button_params[Settings::NativeButton::Plus].Set("button", 10);
184 tas_button_params[Settings::NativeButton::Minus].Set("button", 11);
185 tas_button_params[Settings::NativeButton::DLeft].Set("button", 12);
186 tas_button_params[Settings::NativeButton::DUp].Set("button", 13);
187 tas_button_params[Settings::NativeButton::DRight].Set("button", 14);
188 tas_button_params[Settings::NativeButton::DDown].Set("button", 15);
189 tas_button_params[Settings::NativeButton::SL].Set("button", 16);
190 tas_button_params[Settings::NativeButton::SR].Set("button", 17);
191 tas_button_params[Settings::NativeButton::Home].Set("button", 18);
192 tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
193
194 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
195 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
196 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2);
197 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);
198}
199
200void EmulatedController::ReloadInput() {
201 // If you load any device here add the equivalent to the UnloadInput() function
202 LoadDevices();
203 for (std::size_t index = 0; index < button_devices.size(); ++index) {
204 if (!button_devices[index]) {
205 continue;
206 }
207 const auto uuid = Common::UUID{button_params[index].Get("guid", "")};
208 button_devices[index]->SetCallback({
209 .on_change =
210 [this, index, uuid](const Common::Input::CallbackStatus& callback) {
211 SetButton(callback, index, uuid);
212 },
213 });
214 button_devices[index]->ForceUpdate();
215 }
216
217 for (std::size_t index = 0; index < stick_devices.size(); ++index) {
218 if (!stick_devices[index]) {
219 continue;
220 }
221 const auto uuid = Common::UUID{stick_params[index].Get("guid", "")};
222 stick_devices[index]->SetCallback({
223 .on_change =
224 [this, index, uuid](const Common::Input::CallbackStatus& callback) {
225 SetStick(callback, index, uuid);
226 },
227 });
228 stick_devices[index]->ForceUpdate();
229 }
230
231 for (std::size_t index = 0; index < trigger_devices.size(); ++index) {
232 if (!trigger_devices[index]) {
233 continue;
234 }
235 const auto uuid = Common::UUID{trigger_params[index].Get("guid", "")};
236 trigger_devices[index]->SetCallback({
237 .on_change =
238 [this, index, uuid](const Common::Input::CallbackStatus& callback) {
239 SetTrigger(callback, index, uuid);
240 },
241 });
242 trigger_devices[index]->ForceUpdate();
243 }
244
245 for (std::size_t index = 0; index < battery_devices.size(); ++index) {
246 if (!battery_devices[index]) {
247 continue;
248 }
249 battery_devices[index]->SetCallback({
250 .on_change =
251 [this, index](const Common::Input::CallbackStatus& callback) {
252 SetBattery(callback, index);
253 },
254 });
255 battery_devices[index]->ForceUpdate();
256 }
257
258 for (std::size_t index = 0; index < motion_devices.size(); ++index) {
259 if (!motion_devices[index]) {
260 continue;
261 }
262 motion_devices[index]->SetCallback({
263 .on_change =
264 [this, index](const Common::Input::CallbackStatus& callback) {
265 SetMotion(callback, index);
266 },
267 });
268 motion_devices[index]->ForceUpdate();
269 }
270
271 // Use a common UUID for TAS
272 const auto tas_uuid = Common::UUID{0x0, 0x7A5};
273
274 // Register TAS devices. No need to force update
275 for (std::size_t index = 0; index < tas_button_devices.size(); ++index) {
276 if (!tas_button_devices[index]) {
277 continue;
278 }
279 tas_button_devices[index]->SetCallback({
280 .on_change =
281 [this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
282 SetButton(callback, index, tas_uuid);
283 },
284 });
285 }
286
287 for (std::size_t index = 0; index < tas_stick_devices.size(); ++index) {
288 if (!tas_stick_devices[index]) {
289 continue;
290 }
291 tas_stick_devices[index]->SetCallback({
292 .on_change =
293 [this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
294 SetStick(callback, index, tas_uuid);
295 },
296 });
297 }
298}
299
300void EmulatedController::UnloadInput() {
301 for (auto& button : button_devices) {
302 button.reset();
303 }
304 for (auto& stick : stick_devices) {
305 stick.reset();
306 }
307 for (auto& motion : motion_devices) {
308 motion.reset();
309 }
310 for (auto& trigger : trigger_devices) {
311 trigger.reset();
312 }
313 for (auto& battery : battery_devices) {
314 battery.reset();
315 }
316 for (auto& output : output_devices) {
317 output.reset();
318 }
319 for (auto& button : tas_button_devices) {
320 button.reset();
321 }
322 for (auto& stick : tas_stick_devices) {
323 stick.reset();
324 }
325}
326
327void EmulatedController::EnableConfiguration() {
328 is_configuring = true;
329 tmp_is_connected = is_connected;
330 tmp_npad_type = npad_type;
331}
332
333void EmulatedController::DisableConfiguration() {
334 is_configuring = false;
335
336 // Apply temporary npad type to the real controller
337 if (tmp_npad_type != npad_type) {
338 if (is_connected) {
339 Disconnect();
340 }
341 SetNpadStyleIndex(tmp_npad_type);
342 }
343
344 // Apply temporary connected status to the real controller
345 if (tmp_is_connected != is_connected) {
346 if (tmp_is_connected) {
347 Connect();
348 return;
349 }
350 Disconnect();
351 }
352}
353
354bool EmulatedController::IsConfiguring() const {
355 return is_configuring;
356}
357
358void EmulatedController::SaveCurrentConfig() {
359 const auto player_index = NpadIdTypeToIndex(npad_id_type);
360 auto& player = Settings::values.players.GetValue()[player_index];
361 player.connected = is_connected;
362 player.controller_type = MapNPadToSettingsType(npad_type);
363 for (std::size_t index = 0; index < player.buttons.size(); ++index) {
364 player.buttons[index] = button_params[index].Serialize();
365 }
366 for (std::size_t index = 0; index < player.analogs.size(); ++index) {
367 player.analogs[index] = stick_params[index].Serialize();
368 }
369 for (std::size_t index = 0; index < player.motions.size(); ++index) {
370 player.motions[index] = motion_params[index].Serialize();
371 }
372}
373
374void EmulatedController::RestoreConfig() {
375 if (!is_configuring) {
376 return;
377 }
378 ReloadFromSettings();
379}
380
381std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
382 EmulatedDeviceIndex device_index) const {
383 std::vector<Common::ParamPackage> devices;
384 for (const auto& param : button_params) {
385 if (!param.Has("engine")) {
386 continue;
387 }
388 const auto devices_it = std::find_if(
389 devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
390 return param.Get("engine", "") == param_.Get("engine", "") &&
391 param.Get("guid", "") == param_.Get("guid", "") &&
392 param.Get("port", 0) == param_.Get("port", 0);
393 });
394 if (devices_it != devices.end()) {
395 continue;
396 }
397 Common::ParamPackage device{};
398 device.Set("engine", param.Get("engine", ""));
399 device.Set("guid", param.Get("guid", ""));
400 device.Set("port", param.Get("port", 0));
401 devices.push_back(device);
402 }
403
404 for (const auto& param : stick_params) {
405 if (!param.Has("engine")) {
406 continue;
407 }
408 if (param.Get("engine", "") == "analog_from_button") {
409 continue;
410 }
411 const auto devices_it = std::find_if(
412 devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
413 return param.Get("engine", "") == param_.Get("engine", "") &&
414 param.Get("guid", "") == param_.Get("guid", "") &&
415 param.Get("port", 0) == param_.Get("port", 0);
416 });
417 if (devices_it != devices.end()) {
418 continue;
419 }
420 Common::ParamPackage device{};
421 device.Set("engine", param.Get("engine", ""));
422 device.Set("guid", param.Get("guid", ""));
423 device.Set("port", param.Get("port", 0));
424 devices.push_back(device);
425 }
426 return devices;
427}
428
429Common::ParamPackage EmulatedController::GetButtonParam(std::size_t index) const {
430 if (index >= button_params.size()) {
431 return {};
432 }
433 return button_params[index];
434}
435
436Common::ParamPackage EmulatedController::GetStickParam(std::size_t index) const {
437 if (index >= stick_params.size()) {
438 return {};
439 }
440 return stick_params[index];
441}
442
443Common::ParamPackage EmulatedController::GetMotionParam(std::size_t index) const {
444 if (index >= motion_params.size()) {
445 return {};
446 }
447 return motion_params[index];
448}
449
450void EmulatedController::SetButtonParam(std::size_t index, Common::ParamPackage param) {
451 if (index >= button_params.size()) {
452 return;
453 }
454 button_params[index] = std::move(param);
455 ReloadInput();
456}
457
458void EmulatedController::SetStickParam(std::size_t index, Common::ParamPackage param) {
459 if (index >= stick_params.size()) {
460 return;
461 }
462 stick_params[index] = std::move(param);
463 ReloadInput();
464}
465
466void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage param) {
467 if (index >= motion_params.size()) {
468 return;
469 }
470 motion_params[index] = std::move(param);
471 ReloadInput();
472}
473
474void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
475 Common::UUID uuid) {
476 if (index >= controller.button_values.size()) {
477 return;
478 }
479 {
480 std::lock_guard lock{mutex};
481 bool value_changed = false;
482 const auto new_status = TransformToButton(callback);
483 auto& current_status = controller.button_values[index];
484
485 // Only read button values that have the same uuid or are pressed once
486 if (current_status.uuid != uuid) {
487 if (!new_status.value) {
488 return;
489 }
490 }
491
492 current_status.toggle = new_status.toggle;
493 current_status.uuid = uuid;
494
495 // Update button status with current
496 if (!current_status.toggle) {
497 current_status.locked = false;
498 if (current_status.value != new_status.value) {
499 current_status.value = new_status.value;
500 value_changed = true;
501 }
502 } else {
503 // Toggle button and lock status
504 if (new_status.value && !current_status.locked) {
505 current_status.locked = true;
506 current_status.value = !current_status.value;
507 value_changed = true;
508 }
509
510 // Unlock button ready for next press
511 if (!new_status.value && current_status.locked) {
512 current_status.locked = false;
513 }
514 }
515
516 if (!value_changed) {
517 return;
518 }
519
520 if (is_configuring) {
521 controller.npad_button_state.raw = NpadButton::None;
522 controller.debug_pad_button_state.raw = 0;
523 TriggerOnChange(ControllerTriggerType::Button, false);
524 return;
525 }
526
527 switch (index) {
528 case Settings::NativeButton::A:
529 controller.npad_button_state.a.Assign(current_status.value);
530 controller.debug_pad_button_state.a.Assign(current_status.value);
531 break;
532 case Settings::NativeButton::B:
533 controller.npad_button_state.b.Assign(current_status.value);
534 controller.debug_pad_button_state.b.Assign(current_status.value);
535 break;
536 case Settings::NativeButton::X:
537 controller.npad_button_state.x.Assign(current_status.value);
538 controller.debug_pad_button_state.x.Assign(current_status.value);
539 break;
540 case Settings::NativeButton::Y:
541 controller.npad_button_state.y.Assign(current_status.value);
542 controller.debug_pad_button_state.y.Assign(current_status.value);
543 break;
544 case Settings::NativeButton::LStick:
545 controller.npad_button_state.stick_l.Assign(current_status.value);
546 break;
547 case Settings::NativeButton::RStick:
548 controller.npad_button_state.stick_r.Assign(current_status.value);
549 break;
550 case Settings::NativeButton::L:
551 controller.npad_button_state.l.Assign(current_status.value);
552 controller.debug_pad_button_state.l.Assign(current_status.value);
553 break;
554 case Settings::NativeButton::R:
555 controller.npad_button_state.r.Assign(current_status.value);
556 controller.debug_pad_button_state.r.Assign(current_status.value);
557 break;
558 case Settings::NativeButton::ZL:
559 controller.npad_button_state.zl.Assign(current_status.value);
560 controller.debug_pad_button_state.zl.Assign(current_status.value);
561 break;
562 case Settings::NativeButton::ZR:
563 controller.npad_button_state.zr.Assign(current_status.value);
564 controller.debug_pad_button_state.zr.Assign(current_status.value);
565 break;
566 case Settings::NativeButton::Plus:
567 controller.npad_button_state.plus.Assign(current_status.value);
568 controller.debug_pad_button_state.plus.Assign(current_status.value);
569 break;
570 case Settings::NativeButton::Minus:
571 controller.npad_button_state.minus.Assign(current_status.value);
572 controller.debug_pad_button_state.minus.Assign(current_status.value);
573 break;
574 case Settings::NativeButton::DLeft:
575 controller.npad_button_state.left.Assign(current_status.value);
576 controller.debug_pad_button_state.d_left.Assign(current_status.value);
577 break;
578 case Settings::NativeButton::DUp:
579 controller.npad_button_state.up.Assign(current_status.value);
580 controller.debug_pad_button_state.d_up.Assign(current_status.value);
581 break;
582 case Settings::NativeButton::DRight:
583 controller.npad_button_state.right.Assign(current_status.value);
584 controller.debug_pad_button_state.d_right.Assign(current_status.value);
585 break;
586 case Settings::NativeButton::DDown:
587 controller.npad_button_state.down.Assign(current_status.value);
588 controller.debug_pad_button_state.d_down.Assign(current_status.value);
589 break;
590 case Settings::NativeButton::SL:
591 controller.npad_button_state.left_sl.Assign(current_status.value);
592 controller.npad_button_state.right_sl.Assign(current_status.value);
593 break;
594 case Settings::NativeButton::SR:
595 controller.npad_button_state.left_sr.Assign(current_status.value);
596 controller.npad_button_state.right_sr.Assign(current_status.value);
597 break;
598 case Settings::NativeButton::Home:
599 case Settings::NativeButton::Screenshot:
600 break;
601 }
602 }
603 if (!is_connected) {
604 if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) {
605 Connect();
606 }
607 if (npad_id_type == NpadIdType::Handheld && npad_type == NpadStyleIndex::Handheld) {
608 Connect();
609 }
610 }
611 TriggerOnChange(ControllerTriggerType::Button, true);
612}
613
614void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
615 Common::UUID uuid) {
616 if (index >= controller.stick_values.size()) {
617 return;
618 }
619 std::lock_guard lock{mutex};
620 const auto stick_value = TransformToStick(callback);
621
622 // Only read stick values that have the same uuid or are over the threshold to avoid flapping
623 if (controller.stick_values[index].uuid != uuid) {
624 if (!stick_value.down && !stick_value.up && !stick_value.left && !stick_value.right) {
625 return;
626 }
627 }
628
629 controller.stick_values[index] = stick_value;
630 controller.stick_values[index].uuid = uuid;
631
632 if (is_configuring) {
633 controller.analog_stick_state.left = {};
634 controller.analog_stick_state.right = {};
635 TriggerOnChange(ControllerTriggerType::Stick, false);
636 return;
637 }
638
639 const AnalogStickState stick{
640 .x = static_cast<s32>(controller.stick_values[index].x.value * HID_JOYSTICK_MAX),
641 .y = static_cast<s32>(controller.stick_values[index].y.value * HID_JOYSTICK_MAX),
642 };
643
644 switch (index) {
645 case Settings::NativeAnalog::LStick:
646 controller.analog_stick_state.left = stick;
647 controller.npad_button_state.stick_l_left.Assign(controller.stick_values[index].left);
648 controller.npad_button_state.stick_l_up.Assign(controller.stick_values[index].up);
649 controller.npad_button_state.stick_l_right.Assign(controller.stick_values[index].right);
650 controller.npad_button_state.stick_l_down.Assign(controller.stick_values[index].down);
651 break;
652 case Settings::NativeAnalog::RStick:
653 controller.analog_stick_state.right = stick;
654 controller.npad_button_state.stick_r_left.Assign(controller.stick_values[index].left);
655 controller.npad_button_state.stick_r_up.Assign(controller.stick_values[index].up);
656 controller.npad_button_state.stick_r_right.Assign(controller.stick_values[index].right);
657 controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down);
658 break;
659 }
660
661 TriggerOnChange(ControllerTriggerType::Stick, true);
662}
663
664void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback,
665 std::size_t index, Common::UUID uuid) {
666 if (index >= controller.trigger_values.size()) {
667 return;
668 }
669 std::lock_guard lock{mutex};
670 const auto trigger_value = TransformToTrigger(callback);
671
672 // Only read trigger values that have the same uuid or are pressed once
673 if (controller.trigger_values[index].uuid != uuid) {
674 if (!trigger_value.pressed.value) {
675 return;
676 }
677 }
678
679 controller.trigger_values[index] = trigger_value;
680 controller.trigger_values[index].uuid = uuid;
681
682 if (is_configuring) {
683 controller.gc_trigger_state.left = 0;
684 controller.gc_trigger_state.right = 0;
685 TriggerOnChange(ControllerTriggerType::Trigger, false);
686 return;
687 }
688
689 const auto& trigger = controller.trigger_values[index];
690
691 switch (index) {
692 case Settings::NativeTrigger::LTrigger:
693 controller.gc_trigger_state.left = static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX);
694 controller.npad_button_state.zl.Assign(trigger.pressed.value);
695 break;
696 case Settings::NativeTrigger::RTrigger:
697 controller.gc_trigger_state.right =
698 static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX);
699 controller.npad_button_state.zr.Assign(trigger.pressed.value);
700 break;
701 }
702
703 TriggerOnChange(ControllerTriggerType::Trigger, true);
704}
705
706void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback,
707 std::size_t index) {
708 if (index >= controller.motion_values.size()) {
709 return;
710 }
711 std::lock_guard lock{mutex};
712 auto& raw_status = controller.motion_values[index].raw_status;
713 auto& emulated = controller.motion_values[index].emulated;
714
715 raw_status = TransformToMotion(callback);
716 emulated.SetAcceleration(Common::Vec3f{
717 raw_status.accel.x.value,
718 raw_status.accel.y.value,
719 raw_status.accel.z.value,
720 });
721 emulated.SetGyroscope(Common::Vec3f{
722 raw_status.gyro.x.value,
723 raw_status.gyro.y.value,
724 raw_status.gyro.z.value,
725 });
726 emulated.UpdateRotation(raw_status.delta_timestamp);
727 emulated.UpdateOrientation(raw_status.delta_timestamp);
728 force_update_motion = raw_status.force_update;
729
730 if (is_configuring) {
731 TriggerOnChange(ControllerTriggerType::Motion, false);
732 return;
733 }
734
735 auto& motion = controller.motion_state[index];
736 motion.accel = emulated.GetAcceleration();
737 motion.gyro = emulated.GetGyroscope();
738 motion.rotation = emulated.GetRotations();
739 motion.orientation = emulated.GetOrientation();
740 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
741
742 TriggerOnChange(ControllerTriggerType::Motion, true);
743}
744
745void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback,
746 std::size_t index) {
747 if (index >= controller.battery_values.size()) {
748 return;
749 }
750 std::lock_guard lock{mutex};
751 controller.battery_values[index] = TransformToBattery(callback);
752
753 if (is_configuring) {
754 TriggerOnChange(ControllerTriggerType::Battery, false);
755 return;
756 }
757
758 bool is_charging = false;
759 bool is_powered = false;
760 NpadBatteryLevel battery_level = 0;
761 switch (controller.battery_values[index]) {
762 case Common::Input::BatteryLevel::Charging:
763 is_charging = true;
764 is_powered = true;
765 battery_level = 6;
766 break;
767 case Common::Input::BatteryLevel::Medium:
768 battery_level = 6;
769 break;
770 case Common::Input::BatteryLevel::Low:
771 battery_level = 4;
772 break;
773 case Common::Input::BatteryLevel::Critical:
774 battery_level = 2;
775 break;
776 case Common::Input::BatteryLevel::Empty:
777 battery_level = 0;
778 break;
779 case Common::Input::BatteryLevel::None:
780 case Common::Input::BatteryLevel::Full:
781 default:
782 is_powered = true;
783 battery_level = 8;
784 break;
785 }
786
787 switch (index) {
788 case LeftIndex:
789 controller.battery_state.left = {
790 .is_powered = is_powered,
791 .is_charging = is_charging,
792 .battery_level = battery_level,
793 };
794 break;
795 case RightIndex:
796 controller.battery_state.right = {
797 .is_powered = is_powered,
798 .is_charging = is_charging,
799 .battery_level = battery_level,
800 };
801 break;
802 case DualIndex:
803 controller.battery_state.dual = {
804 .is_powered = is_powered,
805 .is_charging = is_charging,
806 .battery_level = battery_level,
807 };
808 break;
809 }
810 TriggerOnChange(ControllerTriggerType::Battery, true);
811}
812
813bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
814 if (device_index >= output_devices.size()) {
815 return false;
816 }
817 if (!output_devices[device_index]) {
818 return false;
819 }
820 const auto player_index = NpadIdTypeToIndex(npad_id_type);
821 const auto& player = Settings::values.players.GetValue()[player_index];
822 const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f;
823
824 if (!player.vibration_enabled) {
825 return false;
826 }
827
828 // Exponential amplification is too strong at low amplitudes. Switch to a linear
829 // amplification if strength is set below 0.7f
830 const Common::Input::VibrationAmplificationType type =
831 strength > 0.7f ? Common::Input::VibrationAmplificationType::Exponential
832 : Common::Input::VibrationAmplificationType::Linear;
833
834 const Common::Input::VibrationStatus status = {
835 .low_amplitude = std::min(vibration.low_amplitude * strength, 1.0f),
836 .low_frequency = vibration.low_frequency,
837 .high_amplitude = std::min(vibration.high_amplitude * strength, 1.0f),
838 .high_frequency = vibration.high_frequency,
839 .type = type,
840 };
841 return output_devices[device_index]->SetVibration(status) ==
842 Common::Input::VibrationError::None;
843}
844
845bool EmulatedController::TestVibration(std::size_t device_index) {
846 if (device_index >= output_devices.size()) {
847 return false;
848 }
849 if (!output_devices[device_index]) {
850 return false;
851 }
852
853 // Send a slight vibration to test for rumble support
854 constexpr Common::Input::VibrationStatus status = {
855 .low_amplitude = 0.001f,
856 .low_frequency = 160.0f,
857 .high_amplitude = 0.001f,
858 .high_frequency = 320.0f,
859 .type = Common::Input::VibrationAmplificationType::Linear,
860 };
861 return output_devices[device_index]->SetVibration(status) ==
862 Common::Input::VibrationError::None;
863}
864
865void EmulatedController::SetLedPattern() {
866 for (auto& device : output_devices) {
867 if (!device) {
868 continue;
869 }
870
871 const LedPattern pattern = GetLedPattern();
872 const Common::Input::LedStatus status = {
873 .led_1 = pattern.position1 != 0,
874 .led_2 = pattern.position2 != 0,
875 .led_3 = pattern.position3 != 0,
876 .led_4 = pattern.position4 != 0,
877 };
878 device->SetLED(status);
879 }
880}
881
882void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) {
883 supported_style_tag = supported_styles;
884 if (!is_connected) {
885 return;
886 }
887 if (!IsControllerSupported()) {
888 LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
889 npad_type);
890 Disconnect();
891 }
892}
893
894bool EmulatedController::IsControllerSupported() const {
895 switch (npad_type) {
896 case NpadStyleIndex::ProController:
897 return supported_style_tag.fullkey;
898 case NpadStyleIndex::Handheld:
899 return supported_style_tag.handheld;
900 case NpadStyleIndex::JoyconDual:
901 return supported_style_tag.joycon_dual;
902 case NpadStyleIndex::JoyconLeft:
903 return supported_style_tag.joycon_left;
904 case NpadStyleIndex::JoyconRight:
905 return supported_style_tag.joycon_right;
906 case NpadStyleIndex::GameCube:
907 return supported_style_tag.gamecube;
908 case NpadStyleIndex::Pokeball:
909 return supported_style_tag.palma;
910 case NpadStyleIndex::NES:
911 return supported_style_tag.lark;
912 case NpadStyleIndex::SNES:
913 return supported_style_tag.lucia;
914 case NpadStyleIndex::N64:
915 return supported_style_tag.lagoon;
916 case NpadStyleIndex::SegaGenesis:
917 return supported_style_tag.lager;
918 default:
919 return false;
920 }
921}
922
923void EmulatedController::Connect() {
924 if (!IsControllerSupported()) {
925 LOG_ERROR(Service_HID, "Controller type {} is not supported", npad_type);
926 return;
927 }
928 {
929 std::lock_guard lock{mutex};
930 if (is_configuring) {
931 tmp_is_connected = true;
932 TriggerOnChange(ControllerTriggerType::Connected, false);
933 return;
934 }
935
936 if (is_connected) {
937 return;
938 }
939 is_connected = true;
940 }
941 TriggerOnChange(ControllerTriggerType::Connected, true);
942}
943
944void EmulatedController::Disconnect() {
945 {
946 std::lock_guard lock{mutex};
947 if (is_configuring) {
948 tmp_is_connected = false;
949 TriggerOnChange(ControllerTriggerType::Disconnected, false);
950 return;
951 }
952
953 if (!is_connected) {
954 return;
955 }
956 is_connected = false;
957 }
958 TriggerOnChange(ControllerTriggerType::Disconnected, true);
959}
960
961bool EmulatedController::IsConnected(bool get_temporary_value) const {
962 if (get_temporary_value && is_configuring) {
963 return tmp_is_connected;
964 }
965 return is_connected;
966}
967
968bool EmulatedController::IsVibrationEnabled() const {
969 const auto player_index = NpadIdTypeToIndex(npad_id_type);
970 const auto& player = Settings::values.players.GetValue()[player_index];
971 return player.vibration_enabled;
972}
973
974NpadIdType EmulatedController::GetNpadIdType() const {
975 return npad_id_type;
976}
977
978NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
979 if (get_temporary_value && is_configuring) {
980 return tmp_npad_type;
981 }
982 return npad_type;
983}
984
985void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
986 {
987 std::lock_guard lock{mutex};
988
989 if (is_configuring) {
990 if (tmp_npad_type == npad_type_) {
991 return;
992 }
993 tmp_npad_type = npad_type_;
994 TriggerOnChange(ControllerTriggerType::Type, false);
995 return;
996 }
997
998 if (npad_type == npad_type_) {
999 return;
1000 }
1001 if (is_connected) {
1002 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
1003 NpadIdTypeToIndex(npad_id_type));
1004 }
1005 npad_type = npad_type_;
1006 }
1007 TriggerOnChange(ControllerTriggerType::Type, true);
1008}
1009
1010LedPattern EmulatedController::GetLedPattern() const {
1011 switch (npad_id_type) {
1012 case NpadIdType::Player1:
1013 return LedPattern{1, 0, 0, 0};
1014 case NpadIdType::Player2:
1015 return LedPattern{1, 1, 0, 0};
1016 case NpadIdType::Player3:
1017 return LedPattern{1, 1, 1, 0};
1018 case NpadIdType::Player4:
1019 return LedPattern{1, 1, 1, 1};
1020 case NpadIdType::Player5:
1021 return LedPattern{1, 0, 0, 1};
1022 case NpadIdType::Player6:
1023 return LedPattern{1, 0, 1, 0};
1024 case NpadIdType::Player7:
1025 return LedPattern{1, 0, 1, 1};
1026 case NpadIdType::Player8:
1027 return LedPattern{0, 1, 1, 0};
1028 default:
1029 return LedPattern{0, 0, 0, 0};
1030 }
1031}
1032
1033ButtonValues EmulatedController::GetButtonsValues() const {
1034 return controller.button_values;
1035}
1036
1037SticksValues EmulatedController::GetSticksValues() const {
1038 return controller.stick_values;
1039}
1040
1041TriggerValues EmulatedController::GetTriggersValues() const {
1042 return controller.trigger_values;
1043}
1044
1045ControllerMotionValues EmulatedController::GetMotionValues() const {
1046 return controller.motion_values;
1047}
1048
1049ColorValues EmulatedController::GetColorsValues() const {
1050 return controller.color_values;
1051}
1052
1053BatteryValues EmulatedController::GetBatteryValues() const {
1054 return controller.battery_values;
1055}
1056
1057NpadButtonState EmulatedController::GetNpadButtons() const {
1058 if (is_configuring) {
1059 return {};
1060 }
1061 return controller.npad_button_state;
1062}
1063
1064DebugPadButton EmulatedController::GetDebugPadButtons() const {
1065 if (is_configuring) {
1066 return {};
1067 }
1068 return controller.debug_pad_button_state;
1069}
1070
1071AnalogSticks EmulatedController::GetSticks() const {
1072 if (is_configuring) {
1073 return {};
1074 }
1075 // Some drivers like stick from buttons need constant refreshing
1076 for (auto& device : stick_devices) {
1077 if (!device) {
1078 continue;
1079 }
1080 device->SoftUpdate();
1081 }
1082 return controller.analog_stick_state;
1083}
1084
1085NpadGcTriggerState EmulatedController::GetTriggers() const {
1086 if (is_configuring) {
1087 return {};
1088 }
1089 return controller.gc_trigger_state;
1090}
1091
1092MotionState EmulatedController::GetMotions() const {
1093 if (force_update_motion) {
1094 for (auto& device : motion_devices) {
1095 if (!device) {
1096 continue;
1097 }
1098 device->ForceUpdate();
1099 }
1100 }
1101 return controller.motion_state;
1102}
1103
1104ControllerColors EmulatedController::GetColors() const {
1105 return controller.colors_state;
1106}
1107
1108BatteryLevelState EmulatedController::GetBattery() const {
1109 return controller.battery_state;
1110}
1111
1112void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
1113 for (const auto& poller_pair : callback_list) {
1114 const ControllerUpdateCallback& poller = poller_pair.second;
1115 if (!is_npad_service_update && poller.is_npad_service) {
1116 continue;
1117 }
1118 if (poller.on_change) {
1119 poller.on_change(type);
1120 }
1121 }
1122}
1123
1124int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
1125 std::lock_guard lock{mutex};
1126 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
1127 return last_callback_key++;
1128}
1129
1130void EmulatedController::DeleteCallback(int key) {
1131 std::lock_guard lock{mutex};
1132 const auto& iterator = callback_list.find(key);
1133 if (iterator == callback_list.end()) {
1134 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
1135 return;
1136 }
1137 callback_list.erase(iterator);
1138}
1139} // namespace Core::HID