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