summaryrefslogtreecommitdiff
path: root/src/core/hid
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hid')
-rw-r--r--src/core/hid/emulated_console.cpp324
-rw-r--r--src/core/hid/emulated_console.h192
-rw-r--r--src/core/hid/emulated_controller.cpp1972
-rw-r--r--src/core/hid/emulated_controller.h619
-rw-r--r--src/core/hid/emulated_devices.cpp483
-rw-r--r--src/core/hid/emulated_devices.h212
-rw-r--r--src/core/hid/hid_core.cpp222
-rw-r--r--src/core/hid/hid_core.h89
-rw-r--r--src/core/hid/hid_types.h736
-rw-r--r--src/core/hid/input_converter.cpp436
-rw-r--r--src/core/hid/input_converter.h119
-rw-r--r--src/core/hid/input_interpreter.cpp64
-rw-r--r--src/core/hid/input_interpreter.h111
-rw-r--r--src/core/hid/irs_types.h301
-rw-r--r--src/core/hid/motion_input.cpp357
-rw-r--r--src/core/hid/motion_input.h119
16 files changed, 0 insertions, 6356 deletions
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
deleted file mode 100644
index b4afd930e..000000000
--- a/src/core/hid/emulated_console.cpp
+++ /dev/null
@@ -1,324 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "core/hid/emulated_console.h"
6#include "core/hid/input_converter.h"
7
8namespace Core::HID {
9EmulatedConsole::EmulatedConsole() = default;
10
11EmulatedConsole::~EmulatedConsole() = default;
12
13void EmulatedConsole::ReloadFromSettings() {
14 // Using first motion device from player 1. No need to assign any unique config at the moment
15 const auto& player = Settings::values.players.GetValue()[0];
16 motion_params[0] = Common::ParamPackage(player.motions[0]);
17
18 ReloadInput();
19}
20
21void EmulatedConsole::SetTouchParams() {
22 std::size_t index = 0;
23
24 // We can't use mouse as touch if native mouse is enabled
25 if (!Settings::values.mouse_enabled) {
26 touch_params[index++] =
27 Common::ParamPackage{"engine:mouse,axis_x:0,axis_y:1,button:0,port:2"};
28 }
29
30 touch_params[index++] =
31 Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
32 touch_params[index++] =
33 Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
34
35 for (int i = 0; i < static_cast<int>(MaxActiveTouchInputs); i++) {
36 Common::ParamPackage touchscreen_param{};
37 touchscreen_param.Set("engine", "touch");
38 touchscreen_param.Set("axis_x", i * 2);
39 touchscreen_param.Set("axis_y", (i * 2) + 1);
40 touchscreen_param.Set("button", i);
41 touch_params[index++] = std::move(touchscreen_param);
42 }
43
44 if (Settings::values.touch_from_button_maps.empty()) {
45 LOG_WARNING(Input, "touch_from_button_maps is unset by frontend config");
46 return;
47 }
48
49 const auto button_index =
50 static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
51 const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons;
52
53 // Map the rest of the fingers from touch from button configuration
54 for (const auto& config_entry : touch_buttons) {
55 if (index >= MaxTouchDevices) {
56 continue;
57 }
58 Common::ParamPackage params{config_entry};
59 Common::ParamPackage touch_button_params;
60 const int x = params.Get("x", 0);
61 const int y = params.Get("y", 0);
62 params.Erase("x");
63 params.Erase("y");
64 touch_button_params.Set("engine", "touch_from_button");
65 touch_button_params.Set("button", params.Serialize());
66 touch_button_params.Set("x", x);
67 touch_button_params.Set("y", y);
68 touch_params[index] = std::move(touch_button_params);
69 index++;
70 }
71}
72
73void EmulatedConsole::ReloadInput() {
74 // If you load any device here add the equivalent to the UnloadInput() function
75 SetTouchParams();
76
77 motion_params[1] = Common::ParamPackage{"engine:virtual_gamepad,port:8,motion:0"};
78
79 for (std::size_t index = 0; index < motion_devices.size(); ++index) {
80 motion_devices[index] = Common::Input::CreateInputDevice(motion_params[index]);
81 if (!motion_devices[index]) {
82 continue;
83 }
84 motion_devices[index]->SetCallback({
85 .on_change =
86 [this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); },
87 });
88 }
89
90 // Restore motion state
91 auto& emulated_motion = console.motion_values.emulated;
92 auto& motion = console.motion_state;
93 emulated_motion.ResetRotations();
94 emulated_motion.ResetQuaternion();
95 motion.accel = emulated_motion.GetAcceleration();
96 motion.gyro = emulated_motion.GetGyroscope();
97 motion.rotation = emulated_motion.GetRotations();
98 motion.orientation = emulated_motion.GetOrientation();
99 motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity);
100
101 // Unique index for identifying touch device source
102 std::size_t index = 0;
103 for (auto& touch_device : touch_devices) {
104 touch_device = Common::Input::CreateInputDevice(touch_params[index]);
105 if (!touch_device) {
106 continue;
107 }
108 touch_device->SetCallback({
109 .on_change =
110 [this, index](const Common::Input::CallbackStatus& callback) {
111 SetTouch(callback, index);
112 },
113 });
114 index++;
115 }
116}
117
118void EmulatedConsole::UnloadInput() {
119 for (auto& motion : motion_devices) {
120 motion.reset();
121 }
122 for (auto& touch : touch_devices) {
123 touch.reset();
124 }
125}
126
127void EmulatedConsole::EnableConfiguration() {
128 is_configuring = true;
129 SaveCurrentConfig();
130}
131
132void EmulatedConsole::DisableConfiguration() {
133 is_configuring = false;
134}
135
136bool EmulatedConsole::IsConfiguring() const {
137 return is_configuring;
138}
139
140void EmulatedConsole::SaveCurrentConfig() {
141 if (!is_configuring) {
142 return;
143 }
144}
145
146void EmulatedConsole::RestoreConfig() {
147 if (!is_configuring) {
148 return;
149 }
150 ReloadFromSettings();
151}
152
153Common::ParamPackage EmulatedConsole::GetMotionParam() const {
154 return motion_params[0];
155}
156
157void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
158 motion_params[0] = std::move(param);
159 ReloadInput();
160}
161
162void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
163 std::unique_lock lock{mutex};
164 auto& raw_status = console.motion_values.raw_status;
165 auto& emulated = console.motion_values.emulated;
166
167 raw_status = TransformToMotion(callback);
168 emulated.SetAcceleration(Common::Vec3f{
169 raw_status.accel.x.value,
170 raw_status.accel.y.value,
171 raw_status.accel.z.value,
172 });
173 emulated.SetGyroscope(Common::Vec3f{
174 raw_status.gyro.x.value,
175 raw_status.gyro.y.value,
176 raw_status.gyro.z.value,
177 });
178 emulated.UpdateRotation(raw_status.delta_timestamp);
179 emulated.UpdateOrientation(raw_status.delta_timestamp);
180
181 if (is_configuring) {
182 lock.unlock();
183 TriggerOnChange(ConsoleTriggerType::Motion);
184 return;
185 }
186
187 auto& motion = console.motion_state;
188 motion.accel = emulated.GetAcceleration();
189 motion.gyro = emulated.GetGyroscope();
190 motion.rotation = emulated.GetRotations();
191 motion.orientation = emulated.GetOrientation();
192 motion.quaternion = emulated.GetQuaternion();
193 motion.gyro_bias = emulated.GetGyroBias();
194 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
195 // Find what is this value
196 motion.verticalization_error = 0.0f;
197
198 lock.unlock();
199 TriggerOnChange(ConsoleTriggerType::Motion);
200}
201
202void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
203 if (index >= MaxTouchDevices) {
204 return;
205 }
206 std::unique_lock lock{mutex};
207
208 const auto touch_input = TransformToTouch(callback);
209 auto touch_index = GetIndexFromFingerId(index);
210 bool is_new_input = false;
211
212 if (!touch_index.has_value() && touch_input.pressed.value) {
213 touch_index = GetNextFreeIndex();
214 is_new_input = true;
215 }
216
217 // No free entries or invalid state. Ignore input
218 if (!touch_index.has_value()) {
219 return;
220 }
221
222 auto& touch_value = console.touch_values[touch_index.value()];
223
224 if (is_new_input) {
225 touch_value.pressed.value = true;
226 touch_value.id = static_cast<int>(index);
227 }
228
229 touch_value.x = touch_input.x;
230 touch_value.y = touch_input.y;
231
232 if (!touch_input.pressed.value) {
233 touch_value.pressed.value = false;
234 }
235
236 if (is_configuring) {
237 lock.unlock();
238 TriggerOnChange(ConsoleTriggerType::Touch);
239 return;
240 }
241
242 // Touch outside allowed range. Ignore input
243 if (touch_index.value() >= MaxActiveTouchInputs) {
244 return;
245 }
246
247 console.touch_state[touch_index.value()] = {
248 .position = {touch_value.x.value, touch_value.y.value},
249 .id = static_cast<u32>(touch_index.value()),
250 .pressed = touch_input.pressed.value,
251 };
252
253 lock.unlock();
254 TriggerOnChange(ConsoleTriggerType::Touch);
255}
256
257ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
258 std::scoped_lock lock{mutex};
259 return console.motion_values;
260}
261
262TouchValues EmulatedConsole::GetTouchValues() const {
263 std::scoped_lock lock{mutex};
264 return console.touch_values;
265}
266
267ConsoleMotion EmulatedConsole::GetMotion() const {
268 std::scoped_lock lock{mutex};
269 return console.motion_state;
270}
271
272TouchFingerState EmulatedConsole::GetTouch() const {
273 std::scoped_lock lock{mutex};
274 return console.touch_state;
275}
276
277std::optional<std::size_t> EmulatedConsole::GetIndexFromFingerId(std::size_t finger_id) const {
278 for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
279 const auto& finger = console.touch_values[index];
280 if (!finger.pressed.value) {
281 continue;
282 }
283 if (finger.id == static_cast<int>(finger_id)) {
284 return index;
285 }
286 }
287 return std::nullopt;
288}
289
290std::optional<std::size_t> EmulatedConsole::GetNextFreeIndex() const {
291 for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
292 if (!console.touch_values[index].pressed.value) {
293 return index;
294 }
295 }
296 return std::nullopt;
297}
298
299void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
300 std::scoped_lock lock{callback_mutex};
301 for (const auto& poller_pair : callback_list) {
302 const ConsoleUpdateCallback& poller = poller_pair.second;
303 if (poller.on_change) {
304 poller.on_change(type);
305 }
306 }
307}
308
309int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
310 std::scoped_lock lock{callback_mutex};
311 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
312 return last_callback_key++;
313}
314
315void EmulatedConsole::DeleteCallback(int key) {
316 std::scoped_lock lock{callback_mutex};
317 const auto& iterator = callback_list.find(key);
318 if (iterator == callback_list.end()) {
319 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
320 return;
321 }
322 callback_list.erase(iterator);
323}
324} // namespace Core::HID
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h
deleted file mode 100644
index fae15a556..000000000
--- a/src/core/hid/emulated_console.h
+++ /dev/null
@@ -1,192 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <functional>
8#include <memory>
9#include <mutex>
10#include <optional>
11#include <unordered_map>
12
13#include "common/common_funcs.h"
14#include "common/common_types.h"
15#include "common/input.h"
16#include "common/param_package.h"
17#include "common/point.h"
18#include "common/quaternion.h"
19#include "common/vector_math.h"
20#include "core/hid/hid_types.h"
21#include "core/hid/motion_input.h"
22
23namespace Core::HID {
24static constexpr std::size_t MaxTouchDevices = 32;
25static constexpr std::size_t MaxActiveTouchInputs = 16;
26
27struct ConsoleMotionInfo {
28 Common::Input::MotionStatus raw_status{};
29 MotionInput emulated{};
30};
31
32using ConsoleMotionDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 2>;
33using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, MaxTouchDevices>;
34
35using ConsoleMotionParams = std::array<Common::ParamPackage, 2>;
36using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
37
38using ConsoleMotionValues = ConsoleMotionInfo;
39using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
40
41// Contains all motion related data that is used on the services
42struct ConsoleMotion {
43 Common::Vec3f accel{};
44 Common::Vec3f gyro{};
45 Common::Vec3f rotation{};
46 std::array<Common::Vec3f, 3> orientation{};
47 Common::Quaternion<f32> quaternion{};
48 Common::Vec3f gyro_bias{};
49 f32 verticalization_error{};
50 bool is_at_rest{};
51};
52
53using TouchFingerState = std::array<TouchFinger, MaxActiveTouchInputs>;
54
55struct ConsoleStatus {
56 // Data from input_common
57 ConsoleMotionValues motion_values{};
58 TouchValues touch_values{};
59
60 // Data for HID services
61 ConsoleMotion motion_state{};
62 TouchFingerState touch_state{};
63};
64
65enum class ConsoleTriggerType {
66 Motion,
67 Touch,
68 All,
69};
70
71struct ConsoleUpdateCallback {
72 std::function<void(ConsoleTriggerType)> on_change;
73};
74
75class EmulatedConsole {
76public:
77 /**
78 * Contains all input data within the emulated switch console tablet such as touch and motion
79 */
80 explicit EmulatedConsole();
81 ~EmulatedConsole();
82
83 YUZU_NON_COPYABLE(EmulatedConsole);
84 YUZU_NON_MOVEABLE(EmulatedConsole);
85
86 /// Removes all callbacks created from input devices
87 void UnloadInput();
88
89 /**
90 * Sets the emulated console into configuring mode
91 * This prevents the modification of the HID state of the emulated console by input commands
92 */
93 void EnableConfiguration();
94
95 /// Returns the emulated console into normal mode, allowing the modification of the HID state
96 void DisableConfiguration();
97
98 /// Returns true if the emulated console is in configuring mode
99 bool IsConfiguring() const;
100
101 /// Reload all input devices
102 void ReloadInput();
103
104 /// Overrides current mapped devices with the stored configuration and reloads all input devices
105 void ReloadFromSettings();
106
107 /// Saves the current mapped configuration
108 void SaveCurrentConfig();
109
110 /// Reverts any mapped changes made that weren't saved
111 void RestoreConfig();
112
113 // Returns the current mapped motion device
114 Common::ParamPackage GetMotionParam() const;
115
116 /**
117 * Updates the current mapped motion device
118 * @param param ParamPackage with controller data to be mapped
119 */
120 void SetMotionParam(Common::ParamPackage param);
121
122 /// Returns the latest status of motion input from the console with parameters
123 ConsoleMotionValues GetMotionValues() const;
124
125 /// Returns the latest status of touch input from the console with parameters
126 TouchValues GetTouchValues() const;
127
128 /// Returns the latest status of motion input from the console
129 ConsoleMotion GetMotion() const;
130
131 /// Returns the latest status of touch input from the console
132 TouchFingerState GetTouch() const;
133
134 /**
135 * Adds a callback to the list of events
136 * @param update_callback A ConsoleUpdateCallback that will be triggered
137 * @return an unique key corresponding to the callback index in the list
138 */
139 int SetCallback(ConsoleUpdateCallback update_callback);
140
141 /**
142 * Removes a callback from the list stopping any future events to this object
143 * @param key Key corresponding to the callback index in the list
144 */
145 void DeleteCallback(int key);
146
147private:
148 /// Creates and stores the touch params
149 void SetTouchParams();
150
151 /**
152 * Updates the motion status of the console
153 * @param callback A CallbackStatus containing gyro and accelerometer data
154 */
155 void SetMotion(const Common::Input::CallbackStatus& callback);
156
157 /**
158 * Updates the touch status of the console
159 * @param callback A CallbackStatus containing the touch position
160 * @param index Finger ID to be updated
161 */
162 void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index);
163
164 std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
165
166 std::optional<std::size_t> GetNextFreeIndex() const;
167
168 /**
169 * Triggers a callback that something has changed on the console status
170 * @param type Input type of the event to trigger
171 */
172 void TriggerOnChange(ConsoleTriggerType type);
173
174 bool is_configuring{false};
175 f32 motion_sensitivity{0.01f};
176
177 ConsoleMotionParams motion_params;
178 TouchParams touch_params;
179
180 ConsoleMotionDevices motion_devices;
181 TouchDevices touch_devices;
182
183 mutable std::mutex mutex;
184 mutable std::mutex callback_mutex;
185 std::unordered_map<int, ConsoleUpdateCallback> callback_list;
186 int last_callback_key = 0;
187
188 // Stores the current status of all console input
189 ConsoleStatus console;
190};
191
192} // namespace Core::HID
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
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
deleted file mode 100644
index d6e20ab66..000000000
--- a/src/core/hid/emulated_controller.h
+++ /dev/null
@@ -1,619 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <functional>
8#include <memory>
9#include <mutex>
10#include <unordered_map>
11#include <vector>
12
13#include "common/common_types.h"
14#include "common/input.h"
15#include "common/param_package.h"
16#include "common/settings.h"
17#include "common/vector_math.h"
18#include "core/hid/hid_types.h"
19#include "core/hid/irs_types.h"
20#include "core/hid/motion_input.h"
21
22namespace Core::HID {
23const std::size_t max_emulated_controllers = 2;
24const std::size_t output_devices_size = 4;
25struct ControllerMotionInfo {
26 Common::Input::MotionStatus raw_status{};
27 MotionInput emulated{};
28};
29
30using ButtonDevices =
31 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeButton::NumButtons>;
32using StickDevices =
33 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeAnalog::NumAnalogs>;
34using ControllerMotionDevices =
35 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>;
36using TriggerDevices =
37 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
38using ColorDevices =
39 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
40using BatteryDevices =
41 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
42using CameraDevices =
43 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
44using RingAnalogDevices =
45 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
46using NfcDevices =
47 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
48using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>;
49
50using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
51using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
52using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
53using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
54using ColorParams = std::array<Common::ParamPackage, max_emulated_controllers>;
55using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
56using CameraParams = std::array<Common::ParamPackage, max_emulated_controllers>;
57using RingAnalogParams = std::array<Common::ParamPackage, max_emulated_controllers>;
58using NfcParams = std::array<Common::ParamPackage, max_emulated_controllers>;
59using OutputParams = std::array<Common::ParamPackage, output_devices_size>;
60
61using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
62using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
63using TriggerValues =
64 std::array<Common::Input::TriggerStatus, Settings::NativeTrigger::NumTriggers>;
65using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
66using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
67using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
68using CameraValues = Common::Input::CameraStatus;
69using RingAnalogValue = Common::Input::AnalogStatus;
70using NfcValues = Common::Input::NfcStatus;
71using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
72
73struct AnalogSticks {
74 AnalogStickState left{};
75 AnalogStickState right{};
76};
77
78struct ControllerColors {
79 NpadControllerColor fullkey{};
80 NpadControllerColor left{};
81 NpadControllerColor right{};
82};
83
84struct BatteryLevelState {
85 NpadPowerInfo dual{};
86 NpadPowerInfo left{};
87 NpadPowerInfo right{};
88};
89
90struct CameraState {
91 Core::IrSensor::ImageTransferProcessorFormat format{};
92 std::vector<u8> data{};
93 std::size_t sample{};
94};
95
96struct RingSensorForce {
97 f32 force;
98};
99
100using NfcState = Common::Input::NfcStatus;
101
102struct ControllerMotion {
103 Common::Vec3f accel{};
104 Common::Vec3f gyro{};
105 Common::Vec3f rotation{};
106 Common::Vec3f euler{};
107 std::array<Common::Vec3f, 3> orientation{};
108 bool is_at_rest{};
109};
110
111enum EmulatedDeviceIndex : u8 {
112 LeftIndex,
113 RightIndex,
114 DualIndex,
115 AllDevices,
116};
117
118using MotionState = std::array<ControllerMotion, 2>;
119
120struct ControllerStatus {
121 // Data from input_common
122 ButtonValues button_values{};
123 SticksValues stick_values{};
124 ControllerMotionValues motion_values{};
125 TriggerValues trigger_values{};
126 ColorValues color_values{};
127 BatteryValues battery_values{};
128 VibrationValues vibration_values{};
129 CameraValues camera_values{};
130 RingAnalogValue ring_analog_value{};
131 NfcValues nfc_values{};
132
133 // Data for HID services
134 HomeButtonState home_button_state{};
135 CaptureButtonState capture_button_state{};
136 NpadButtonState npad_button_state{};
137 DebugPadButton debug_pad_button_state{};
138 AnalogSticks analog_stick_state{};
139 MotionState motion_state{};
140 NpadGcTriggerState gc_trigger_state{};
141 ControllerColors colors_state{};
142 BatteryLevelState battery_state{};
143 CameraState camera_state{};
144 RingSensorForce ring_analog_state{};
145 NfcState nfc_state{};
146 Common::Input::PollingMode left_polling_mode{};
147 Common::Input::PollingMode right_polling_mode{};
148};
149
150enum class ControllerTriggerType {
151 Button,
152 Stick,
153 Trigger,
154 Motion,
155 Color,
156 Battery,
157 Vibration,
158 IrSensor,
159 RingController,
160 Nfc,
161 Connected,
162 Disconnected,
163 Type,
164 All,
165};
166
167struct ControllerUpdateCallback {
168 std::function<void(ControllerTriggerType)> on_change;
169 bool is_npad_service;
170};
171
172class EmulatedController {
173public:
174 /**
175 * Contains all input data (buttons, joysticks, vibration, and motion) within this controller.
176 * @param npad_id_type npad id type for this specific controller
177 */
178 explicit EmulatedController(NpadIdType npad_id_type_);
179 ~EmulatedController();
180
181 YUZU_NON_COPYABLE(EmulatedController);
182 YUZU_NON_MOVEABLE(EmulatedController);
183
184 /// Converts the controller type from settings to npad type
185 static NpadStyleIndex MapSettingsTypeToNPad(Settings::ControllerType type);
186
187 /// Converts npad type to the equivalent of controller type from settings
188 static Settings::ControllerType MapNPadToSettingsType(NpadStyleIndex type);
189
190 /// Gets the NpadIdType for this controller
191 NpadIdType GetNpadIdType() const;
192
193 /// Sets the NpadStyleIndex for this controller
194 void SetNpadStyleIndex(NpadStyleIndex npad_type_);
195
196 /**
197 * Gets the NpadStyleIndex for this controller
198 * @param get_temporary_value If true tmp_npad_type will be returned
199 * @return NpadStyleIndex set on the controller
200 */
201 NpadStyleIndex GetNpadStyleIndex(bool get_temporary_value = false) const;
202
203 /**
204 * Sets the supported controller types. Disconnects the controller if current type is not
205 * supported
206 * @param supported_styles bitflag with supported types
207 */
208 void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
209
210 /**
211 * Sets the connected status to true
212 * @param use_temporary_value If true tmp_npad_type will be used
213 */
214 void Connect(bool use_temporary_value = false);
215
216 /// Sets the connected status to false
217 void Disconnect();
218
219 /**
220 * Is the emulated connected
221 * @param get_temporary_value If true tmp_is_connected will be returned
222 * @return true if the controller has the connected status
223 */
224 bool IsConnected(bool get_temporary_value = false) const;
225
226 /// Removes all callbacks created from input devices
227 void UnloadInput();
228
229 /**
230 * Sets the emulated controller into configuring mode
231 * This prevents the modification of the HID state of the emulated controller by input commands
232 */
233 void EnableConfiguration();
234
235 /// Returns the emulated controller into normal mode, allowing the modification of the HID state
236 void DisableConfiguration();
237
238 /// Enables Home and Screenshot buttons
239 void EnableSystemButtons();
240
241 /// Disables Home and Screenshot buttons
242 void DisableSystemButtons();
243
244 /// Sets Home and Screenshot buttons to false
245 void ResetSystemButtons();
246
247 /// Returns true if the emulated controller is in configuring mode
248 bool IsConfiguring() const;
249
250 /// Reload all input devices
251 void ReloadInput();
252
253 /// Overrides current mapped devices with the stored configuration and reloads all input devices
254 void ReloadFromSettings();
255
256 /// Updates current colors with the ones stored in the configuration
257 void ReloadColorsFromSettings();
258
259 /// Saves the current mapped configuration
260 void SaveCurrentConfig();
261
262 /// Reverts any mapped changes made that weren't saved
263 void RestoreConfig();
264
265 /// Returns a vector of mapped devices from the mapped button and stick parameters
266 std::vector<Common::ParamPackage> GetMappedDevices() const;
267
268 // Returns the current mapped button device
269 Common::ParamPackage GetButtonParam(std::size_t index) const;
270
271 // Returns the current mapped stick device
272 Common::ParamPackage GetStickParam(std::size_t index) const;
273
274 // Returns the current mapped motion device
275 Common::ParamPackage GetMotionParam(std::size_t index) const;
276
277 /**
278 * Updates the current mapped button device
279 * @param param ParamPackage with controller data to be mapped
280 */
281 void SetButtonParam(std::size_t index, Common::ParamPackage param);
282
283 /**
284 * Updates the current mapped stick device
285 * @param param ParamPackage with controller data to be mapped
286 */
287 void SetStickParam(std::size_t index, Common::ParamPackage param);
288
289 /**
290 * Updates the current mapped motion device
291 * @param param ParamPackage with controller data to be mapped
292 */
293 void SetMotionParam(std::size_t index, Common::ParamPackage param);
294
295 /// Auto calibrates the current motion devices
296 void StartMotionCalibration();
297
298 /// Returns the latest button status from the controller with parameters
299 ButtonValues GetButtonsValues() const;
300
301 /// Returns the latest analog stick status from the controller with parameters
302 SticksValues GetSticksValues() const;
303
304 /// Returns the latest trigger status from the controller with parameters
305 TriggerValues GetTriggersValues() const;
306
307 /// Returns the latest motion status from the controller with parameters
308 ControllerMotionValues GetMotionValues() const;
309
310 /// Returns the latest color status from the controller with parameters
311 ColorValues GetColorsValues() const;
312
313 /// Returns the latest battery status from the controller with parameters
314 BatteryValues GetBatteryValues() const;
315
316 /// Returns the latest camera status from the controller with parameters
317 CameraValues GetCameraValues() const;
318
319 /// Returns the latest status of analog input from the ring sensor with parameters
320 RingAnalogValue GetRingSensorValues() const;
321
322 /// Returns the latest status of button input for the hid::HomeButton service
323 HomeButtonState GetHomeButtons() const;
324
325 /// Returns the latest status of button input for the hid::CaptureButton service
326 CaptureButtonState GetCaptureButtons() const;
327
328 /// Returns the latest status of button input for the hid::Npad service
329 NpadButtonState GetNpadButtons() const;
330
331 /// Returns the latest status of button input for the debug pad service
332 DebugPadButton GetDebugPadButtons() const;
333
334 /// Returns the latest status of stick input from the mouse
335 AnalogSticks GetSticks() const;
336
337 /// Returns the latest status of trigger input from the mouse
338 NpadGcTriggerState GetTriggers() const;
339
340 /// Returns the latest status of motion input from the mouse
341 MotionState GetMotions() const;
342
343 /// Returns the latest color value from the controller
344 ControllerColors GetColors() const;
345
346 /// Returns the latest battery status from the controller
347 BatteryLevelState GetBattery() const;
348
349 /// Returns the latest camera status from the controller
350 const CameraState& GetCamera() const;
351
352 /// Returns the latest ringcon force sensor value
353 RingSensorForce GetRingSensorForce() const;
354
355 /// Returns the latest ntag status from the controller
356 const NfcState& GetNfc() const;
357
358 /**
359 * Sends a specific vibration to the output device
360 * @return true if vibration had no errors
361 */
362 bool SetVibration(std::size_t device_index, VibrationValue vibration);
363
364 /**
365 * Sends a small vibration to the output device
366 * @return true if SetVibration was successful
367 */
368 bool IsVibrationEnabled(std::size_t device_index);
369
370 /**
371 * Sets the desired data to be polled from a controller
372 * @param device_index index of the controller to set the polling mode
373 * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
374 * @return driver result from this command
375 */
376 Common::Input::DriverResult SetPollingMode(EmulatedDeviceIndex device_index,
377 Common::Input::PollingMode polling_mode);
378 /**
379 * Get the current polling mode from a controller
380 * @param device_index index of the controller to set the polling mode
381 * @return current polling mode
382 */
383 Common::Input::PollingMode GetPollingMode(EmulatedDeviceIndex device_index) const;
384
385 /**
386 * Sets the desired camera format to be polled from a controller
387 * @param camera_format size of each frame
388 * @return true if SetCameraFormat was successful
389 */
390 bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
391
392 // Returns the current mapped ring device
393 Common::ParamPackage GetRingParam() const;
394
395 /**
396 * Updates the current mapped ring device
397 * @param param ParamPackage with ring sensor data to be mapped
398 */
399 void SetRingParam(Common::ParamPackage param);
400
401 /// Returns true if the device has nfc support
402 bool HasNfc() const;
403
404 /// Sets the joycon in nfc mode and increments the handle count
405 bool AddNfcHandle();
406
407 /// Decrements the handle count if zero sets the joycon in active mode
408 bool RemoveNfcHandle();
409
410 /// Start searching for nfc tags
411 bool StartNfcPolling();
412
413 /// Stop searching for nfc tags
414 bool StopNfcPolling();
415
416 /// Returns true if the nfc tag was readable
417 bool ReadAmiiboData(std::vector<u8>& data);
418
419 /// Returns true if the nfc tag was written
420 bool WriteNfc(const std::vector<u8>& data);
421
422 /// Returns true if the nfc tag was readable
423 bool ReadMifareData(const Common::Input::MifareRequest& request,
424 Common::Input::MifareRequest& out_data);
425
426 /// Returns true if the nfc tag was written
427 bool WriteMifareData(const Common::Input::MifareRequest& request);
428
429 /// Returns the led pattern corresponding to this emulated controller
430 LedPattern GetLedPattern() const;
431
432 /// Asks the output device to change the player led pattern
433 void SetLedPattern();
434
435 /// Changes sensitivity of the motion sensor
436 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode mode);
437
438 /**
439 * Adds a callback to the list of events
440 * @param update_callback A ConsoleUpdateCallback that will be triggered
441 * @return an unique key corresponding to the callback index in the list
442 */
443 int SetCallback(ControllerUpdateCallback update_callback);
444
445 /**
446 * Removes a callback from the list stopping any future events to this object
447 * @param key Key corresponding to the callback index in the list
448 */
449 void DeleteCallback(int key);
450
451 /// Swaps the state of the turbo buttons and updates motion input
452 void StatusUpdate();
453
454private:
455 /// creates input devices from params
456 void LoadDevices();
457
458 /// Set the params for TAS devices
459 void LoadTASParams();
460
461 /// Set the params for virtual pad devices
462 void LoadVirtualGamepadParams();
463
464 /**
465 * @param use_temporary_value If true tmp_npad_type will be used
466 * @return true if the controller style is fullkey
467 */
468 bool IsControllerFullkey(bool use_temporary_value = false) const;
469
470 /**
471 * Checks the current controller type against the supported_style_tag
472 * @param use_temporary_value If true tmp_npad_type will be used
473 * @return true if the controller is supported
474 */
475 bool IsControllerSupported(bool use_temporary_value = false) const;
476
477 /**
478 * Updates the button status of the controller
479 * @param callback A CallbackStatus containing the button status
480 * @param index Button ID of the to be updated
481 */
482 void SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
483 Common::UUID uuid);
484
485 /**
486 * Updates the analog stick status of the controller
487 * @param callback A CallbackStatus containing the analog stick status
488 * @param index stick ID of the to be updated
489 */
490 void SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
491 Common::UUID uuid);
492
493 /**
494 * Updates the trigger status of the controller
495 * @param callback A CallbackStatus containing the trigger status
496 * @param index trigger ID of the to be updated
497 */
498 void SetTrigger(const Common::Input::CallbackStatus& callback, std::size_t index,
499 Common::UUID uuid);
500
501 /**
502 * Updates the motion status of the controller
503 * @param callback A CallbackStatus containing gyro and accelerometer data
504 * @param index motion ID of the to be updated
505 */
506 void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index);
507
508 /**
509 * Updates the color status of the controller
510 * @param callback A CallbackStatus containing the color status
511 * @param index color ID of the to be updated
512 */
513 void SetColors(const Common::Input::CallbackStatus& callback, std::size_t index);
514
515 /**
516 * Updates the battery status of the controller
517 * @param callback A CallbackStatus containing the battery status
518 * @param index battery ID of the to be updated
519 */
520 void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
521
522 /**
523 * Updates the camera status of the controller
524 * @param callback A CallbackStatus containing the camera status
525 */
526 void SetCamera(const Common::Input::CallbackStatus& callback);
527
528 /**
529 * Updates the ring analog sensor status of the ring controller
530 * @param callback A CallbackStatus containing the force status
531 */
532 void SetRingAnalog(const Common::Input::CallbackStatus& callback);
533
534 /**
535 * Updates the nfc status of the controller
536 * @param callback A CallbackStatus containing the nfc status
537 */
538 void SetNfc(const Common::Input::CallbackStatus& callback);
539
540 /**
541 * Converts a color format from bgra to rgba
542 * @param color in bgra format
543 * @return NpadColor in rgba format
544 */
545 NpadColor GetNpadColor(u32 color);
546
547 /**
548 * Triggers a callback that something has changed on the controller status
549 * @param type Input type of the event to trigger
550 * @param is_service_update indicates if this event should only be sent to HID services
551 */
552 void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
553
554 NpadButton GetTurboButtonMask() const;
555
556 const NpadIdType npad_id_type;
557 NpadStyleIndex npad_type{NpadStyleIndex::None};
558 NpadStyleIndex original_npad_type{NpadStyleIndex::None};
559 NpadStyleTag supported_style_tag{NpadStyleSet::All};
560 bool is_connected{false};
561 bool is_configuring{false};
562 bool is_initalized{false};
563 bool system_buttons_enabled{true};
564 f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
565 u32 turbo_button_state{0};
566 std::size_t nfc_handles{0};
567
568 // Temporary values to avoid doing changes while the controller is in configuring mode
569 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
570 bool tmp_is_connected{false};
571
572 ButtonParams button_params;
573 StickParams stick_params;
574 ControllerMotionParams motion_params;
575 TriggerParams trigger_params;
576 BatteryParams battery_params;
577 ColorParams color_params;
578 CameraParams camera_params;
579 RingAnalogParams ring_params;
580 NfcParams nfc_params;
581 OutputParams output_params;
582
583 ButtonDevices button_devices;
584 StickDevices stick_devices;
585 ControllerMotionDevices motion_devices;
586 TriggerDevices trigger_devices;
587 BatteryDevices battery_devices;
588 ColorDevices color_devices;
589 CameraDevices camera_devices;
590 RingAnalogDevices ring_analog_devices;
591 NfcDevices nfc_devices;
592 OutputDevices output_devices;
593
594 // TAS related variables
595 ButtonParams tas_button_params;
596 StickParams tas_stick_params;
597 ButtonDevices tas_button_devices;
598 StickDevices tas_stick_devices;
599
600 // Virtual gamepad related variables
601 ButtonParams virtual_button_params;
602 StickParams virtual_stick_params;
603 ControllerMotionParams virtual_motion_params;
604 ButtonDevices virtual_button_devices;
605 StickDevices virtual_stick_devices;
606 ControllerMotionDevices virtual_motion_devices;
607
608 mutable std::mutex mutex;
609 mutable std::mutex callback_mutex;
610 mutable std::mutex npad_mutex;
611 mutable std::mutex connect_mutex;
612 std::unordered_map<int, ControllerUpdateCallback> callback_list;
613 int last_callback_key = 0;
614
615 // Stores the current status of all controller input
616 ControllerStatus controller;
617};
618
619} // namespace Core::HID
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
deleted file mode 100644
index 8e165dded..000000000
--- a/src/core/hid/emulated_devices.cpp
+++ /dev/null
@@ -1,483 +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 <fmt/format.h>
6
7#include "core/hid/emulated_devices.h"
8#include "core/hid/input_converter.h"
9
10namespace Core::HID {
11
12EmulatedDevices::EmulatedDevices() = default;
13
14EmulatedDevices::~EmulatedDevices() = default;
15
16void EmulatedDevices::ReloadFromSettings() {
17 ReloadInput();
18}
19
20void EmulatedDevices::ReloadInput() {
21 // If you load any device here add the equivalent to the UnloadInput() function
22
23 // Native Mouse is mapped on port 1, pad 0
24 const Common::ParamPackage mouse_params{"engine:mouse,port:1,pad:0"};
25
26 // Keyboard keys is mapped on port 1, pad 0 for normal keys, pad 1 for moddifier keys
27 const Common::ParamPackage keyboard_params{"engine:keyboard,port:1"};
28
29 std::size_t key_index = 0;
30 for (auto& mouse_device : mouse_button_devices) {
31 Common::ParamPackage mouse_button_params = mouse_params;
32 mouse_button_params.Set("button", static_cast<int>(key_index));
33 mouse_device = Common::Input::CreateInputDevice(mouse_button_params);
34 key_index++;
35 }
36
37 Common::ParamPackage mouse_position_params = mouse_params;
38 mouse_position_params.Set("axis_x", 0);
39 mouse_position_params.Set("axis_y", 1);
40 mouse_position_params.Set("deadzone", 0.0f);
41 mouse_position_params.Set("range", 1.0f);
42 mouse_position_params.Set("threshold", 0.0f);
43 mouse_stick_device = Common::Input::CreateInputDevice(mouse_position_params);
44
45 // First two axis are reserved for mouse position
46 key_index = 2;
47 for (auto& mouse_device : mouse_wheel_devices) {
48 Common::ParamPackage mouse_wheel_params = mouse_params;
49 mouse_wheel_params.Set("axis", static_cast<int>(key_index));
50 mouse_device = Common::Input::CreateInputDevice(mouse_wheel_params);
51 key_index++;
52 }
53
54 key_index = 0;
55 for (auto& keyboard_device : keyboard_devices) {
56 Common::ParamPackage keyboard_key_params = keyboard_params;
57 keyboard_key_params.Set("button", static_cast<int>(key_index));
58 keyboard_key_params.Set("pad", 0);
59 keyboard_device = Common::Input::CreateInputDevice(keyboard_key_params);
60 key_index++;
61 }
62
63 key_index = 0;
64 for (auto& keyboard_device : keyboard_modifier_devices) {
65 Common::ParamPackage keyboard_moddifier_params = keyboard_params;
66 keyboard_moddifier_params.Set("button", static_cast<int>(key_index));
67 keyboard_moddifier_params.Set("pad", 1);
68 keyboard_device = Common::Input::CreateInputDevice(keyboard_moddifier_params);
69 key_index++;
70 }
71
72 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
73 if (!mouse_button_devices[index]) {
74 continue;
75 }
76 mouse_button_devices[index]->SetCallback({
77 .on_change =
78 [this, index](const Common::Input::CallbackStatus& callback) {
79 SetMouseButton(callback, index);
80 },
81 });
82 }
83
84 for (std::size_t index = 0; index < mouse_wheel_devices.size(); ++index) {
85 if (!mouse_wheel_devices[index]) {
86 continue;
87 }
88 mouse_wheel_devices[index]->SetCallback({
89 .on_change =
90 [this, index](const Common::Input::CallbackStatus& callback) {
91 SetMouseWheel(callback, index);
92 },
93 });
94 }
95
96 if (mouse_stick_device) {
97 mouse_stick_device->SetCallback({
98 .on_change =
99 [this](const Common::Input::CallbackStatus& callback) {
100 SetMousePosition(callback);
101 },
102 });
103 }
104
105 for (std::size_t index = 0; index < keyboard_devices.size(); ++index) {
106 if (!keyboard_devices[index]) {
107 continue;
108 }
109 keyboard_devices[index]->SetCallback({
110 .on_change =
111 [this, index](const Common::Input::CallbackStatus& callback) {
112 SetKeyboardButton(callback, index);
113 },
114 });
115 }
116
117 for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) {
118 if (!keyboard_modifier_devices[index]) {
119 continue;
120 }
121 keyboard_modifier_devices[index]->SetCallback({
122 .on_change =
123 [this, index](const Common::Input::CallbackStatus& callback) {
124 SetKeyboardModifier(callback, index);
125 },
126 });
127 }
128}
129
130void EmulatedDevices::UnloadInput() {
131 for (auto& button : mouse_button_devices) {
132 button.reset();
133 }
134 for (auto& analog : mouse_wheel_devices) {
135 analog.reset();
136 }
137 mouse_stick_device.reset();
138 for (auto& button : keyboard_devices) {
139 button.reset();
140 }
141 for (auto& button : keyboard_modifier_devices) {
142 button.reset();
143 }
144}
145
146void EmulatedDevices::EnableConfiguration() {
147 is_configuring = true;
148 SaveCurrentConfig();
149}
150
151void EmulatedDevices::DisableConfiguration() {
152 is_configuring = false;
153}
154
155bool EmulatedDevices::IsConfiguring() const {
156 return is_configuring;
157}
158
159void EmulatedDevices::SaveCurrentConfig() {
160 if (!is_configuring) {
161 return;
162 }
163}
164
165void EmulatedDevices::RestoreConfig() {
166 if (!is_configuring) {
167 return;
168 }
169 ReloadFromSettings();
170}
171
172void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
173 std::size_t index) {
174 if (index >= device_status.keyboard_values.size()) {
175 return;
176 }
177 std::unique_lock lock{mutex};
178 bool value_changed = false;
179 const auto new_status = TransformToButton(callback);
180 auto& current_status = device_status.keyboard_values[index];
181 current_status.toggle = new_status.toggle;
182
183 // Update button status with current status
184 if (!current_status.toggle) {
185 current_status.locked = false;
186 if (current_status.value != new_status.value) {
187 current_status.value = new_status.value;
188 value_changed = true;
189 }
190 } else {
191 // Toggle button and lock status
192 if (new_status.value && !current_status.locked) {
193 current_status.locked = true;
194 current_status.value = !current_status.value;
195 value_changed = true;
196 }
197
198 // Unlock button, ready for next press
199 if (!new_status.value && current_status.locked) {
200 current_status.locked = false;
201 }
202 }
203
204 if (!value_changed) {
205 return;
206 }
207
208 if (is_configuring) {
209 lock.unlock();
210 TriggerOnChange(DeviceTriggerType::Keyboard);
211 return;
212 }
213
214 // Index should be converted from NativeKeyboard to KeyboardKeyIndex
215 UpdateKey(index, current_status.value);
216
217 lock.unlock();
218 TriggerOnChange(DeviceTriggerType::Keyboard);
219}
220
221void EmulatedDevices::UpdateKey(std::size_t key_index, bool status) {
222 constexpr std::size_t KEYS_PER_BYTE = 8;
223 auto& entry = device_status.keyboard_state.key[key_index / KEYS_PER_BYTE];
224 const u8 mask = static_cast<u8>(1 << (key_index % KEYS_PER_BYTE));
225 if (status) {
226 entry = entry | mask;
227 } else {
228 entry = static_cast<u8>(entry & ~mask);
229 }
230}
231
232void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& callback,
233 std::size_t index) {
234 if (index >= device_status.keyboard_moddifier_values.size()) {
235 return;
236 }
237 std::unique_lock lock{mutex};
238 bool value_changed = false;
239 const auto new_status = TransformToButton(callback);
240 auto& current_status = device_status.keyboard_moddifier_values[index];
241 current_status.toggle = new_status.toggle;
242
243 // Update button status with current
244 if (!current_status.toggle) {
245 current_status.locked = false;
246 if (current_status.value != new_status.value) {
247 current_status.value = new_status.value;
248 value_changed = true;
249 }
250 } else {
251 // Toggle button and lock status
252 if (new_status.value && !current_status.locked) {
253 current_status.locked = true;
254 current_status.value = !current_status.value;
255 value_changed = true;
256 }
257
258 // Unlock button ready for next press
259 if (!new_status.value && current_status.locked) {
260 current_status.locked = false;
261 }
262 }
263
264 if (!value_changed) {
265 return;
266 }
267
268 if (is_configuring) {
269 lock.unlock();
270 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
271 return;
272 }
273
274 switch (index) {
275 case Settings::NativeKeyboard::LeftControl:
276 case Settings::NativeKeyboard::RightControl:
277 device_status.keyboard_moddifier_state.control.Assign(current_status.value);
278 break;
279 case Settings::NativeKeyboard::LeftShift:
280 case Settings::NativeKeyboard::RightShift:
281 device_status.keyboard_moddifier_state.shift.Assign(current_status.value);
282 break;
283 case Settings::NativeKeyboard::LeftAlt:
284 device_status.keyboard_moddifier_state.left_alt.Assign(current_status.value);
285 break;
286 case Settings::NativeKeyboard::RightAlt:
287 device_status.keyboard_moddifier_state.right_alt.Assign(current_status.value);
288 break;
289 case Settings::NativeKeyboard::CapsLock:
290 device_status.keyboard_moddifier_state.caps_lock.Assign(current_status.value);
291 break;
292 case Settings::NativeKeyboard::ScrollLock:
293 device_status.keyboard_moddifier_state.scroll_lock.Assign(current_status.value);
294 break;
295 case Settings::NativeKeyboard::NumLock:
296 device_status.keyboard_moddifier_state.num_lock.Assign(current_status.value);
297 break;
298 }
299
300 lock.unlock();
301 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
302}
303
304void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callback,
305 std::size_t index) {
306 if (index >= device_status.mouse_button_values.size()) {
307 return;
308 }
309 std::unique_lock lock{mutex};
310 bool value_changed = false;
311 const auto new_status = TransformToButton(callback);
312 auto& current_status = device_status.mouse_button_values[index];
313 current_status.toggle = new_status.toggle;
314
315 // Update button status with current
316 if (!current_status.toggle) {
317 current_status.locked = false;
318 if (current_status.value != new_status.value) {
319 current_status.value = new_status.value;
320 value_changed = true;
321 }
322 } else {
323 // Toggle button and lock status
324 if (new_status.value && !current_status.locked) {
325 current_status.locked = true;
326 current_status.value = !current_status.value;
327 value_changed = true;
328 }
329
330 // Unlock button ready for next press
331 if (!new_status.value && current_status.locked) {
332 current_status.locked = false;
333 }
334 }
335
336 if (!value_changed) {
337 return;
338 }
339
340 if (is_configuring) {
341 lock.unlock();
342 TriggerOnChange(DeviceTriggerType::Mouse);
343 return;
344 }
345
346 switch (index) {
347 case Settings::NativeMouseButton::Left:
348 device_status.mouse_button_state.left.Assign(current_status.value);
349 break;
350 case Settings::NativeMouseButton::Right:
351 device_status.mouse_button_state.right.Assign(current_status.value);
352 break;
353 case Settings::NativeMouseButton::Middle:
354 device_status.mouse_button_state.middle.Assign(current_status.value);
355 break;
356 case Settings::NativeMouseButton::Forward:
357 device_status.mouse_button_state.forward.Assign(current_status.value);
358 break;
359 case Settings::NativeMouseButton::Back:
360 device_status.mouse_button_state.back.Assign(current_status.value);
361 break;
362 }
363
364 lock.unlock();
365 TriggerOnChange(DeviceTriggerType::Mouse);
366}
367
368void EmulatedDevices::SetMouseWheel(const Common::Input::CallbackStatus& callback,
369 std::size_t index) {
370 if (index >= device_status.mouse_wheel_values.size()) {
371 return;
372 }
373 std::unique_lock lock{mutex};
374 const auto analog_value = TransformToAnalog(callback);
375
376 device_status.mouse_wheel_values[index] = analog_value;
377
378 if (is_configuring) {
379 device_status.mouse_wheel_state = {};
380 lock.unlock();
381 TriggerOnChange(DeviceTriggerType::Mouse);
382 return;
383 }
384
385 switch (index) {
386 case Settings::NativeMouseWheel::X:
387 device_status.mouse_wheel_state.x = static_cast<s32>(analog_value.value);
388 break;
389 case Settings::NativeMouseWheel::Y:
390 device_status.mouse_wheel_state.y = static_cast<s32>(analog_value.value);
391 break;
392 }
393
394 lock.unlock();
395 TriggerOnChange(DeviceTriggerType::Mouse);
396}
397
398void EmulatedDevices::SetMousePosition(const Common::Input::CallbackStatus& callback) {
399 std::unique_lock lock{mutex};
400 const auto touch_value = TransformToTouch(callback);
401
402 device_status.mouse_stick_value = touch_value;
403
404 if (is_configuring) {
405 device_status.mouse_position_state = {};
406 lock.unlock();
407 TriggerOnChange(DeviceTriggerType::Mouse);
408 return;
409 }
410
411 device_status.mouse_position_state.x = touch_value.x.value;
412 device_status.mouse_position_state.y = touch_value.y.value;
413
414 lock.unlock();
415 TriggerOnChange(DeviceTriggerType::Mouse);
416}
417
418KeyboardValues EmulatedDevices::GetKeyboardValues() const {
419 std::scoped_lock lock{mutex};
420 return device_status.keyboard_values;
421}
422
423KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
424 std::scoped_lock lock{mutex};
425 return device_status.keyboard_moddifier_values;
426}
427
428MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
429 std::scoped_lock lock{mutex};
430 return device_status.mouse_button_values;
431}
432
433KeyboardKey EmulatedDevices::GetKeyboard() const {
434 std::scoped_lock lock{mutex};
435 return device_status.keyboard_state;
436}
437
438KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
439 std::scoped_lock lock{mutex};
440 return device_status.keyboard_moddifier_state;
441}
442
443MouseButton EmulatedDevices::GetMouseButtons() const {
444 std::scoped_lock lock{mutex};
445 return device_status.mouse_button_state;
446}
447
448MousePosition EmulatedDevices::GetMousePosition() const {
449 std::scoped_lock lock{mutex};
450 return device_status.mouse_position_state;
451}
452
453AnalogStickState EmulatedDevices::GetMouseWheel() const {
454 std::scoped_lock lock{mutex};
455 return device_status.mouse_wheel_state;
456}
457
458void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
459 std::scoped_lock lock{callback_mutex};
460 for (const auto& poller_pair : callback_list) {
461 const InterfaceUpdateCallback& poller = poller_pair.second;
462 if (poller.on_change) {
463 poller.on_change(type);
464 }
465 }
466}
467
468int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
469 std::scoped_lock lock{callback_mutex};
470 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
471 return last_callback_key++;
472}
473
474void EmulatedDevices::DeleteCallback(int key) {
475 std::scoped_lock lock{callback_mutex};
476 const auto& iterator = callback_list.find(key);
477 if (iterator == callback_list.end()) {
478 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
479 return;
480 }
481 callback_list.erase(iterator);
482}
483} // namespace Core::HID
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
deleted file mode 100644
index 5eab693e4..000000000
--- a/src/core/hid/emulated_devices.h
+++ /dev/null
@@ -1,212 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <functional>
8#include <memory>
9#include <mutex>
10#include <unordered_map>
11#include <vector>
12
13#include "common/common_types.h"
14#include "common/input.h"
15#include "common/param_package.h"
16#include "common/settings.h"
17#include "core/hid/hid_types.h"
18
19namespace Core::HID {
20using KeyboardDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
21 Settings::NativeKeyboard::NumKeyboardKeys>;
22using KeyboardModifierDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
23 Settings::NativeKeyboard::NumKeyboardMods>;
24using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
25 Settings::NativeMouseButton::NumMouseButtons>;
26using MouseWheelDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
27 Settings::NativeMouseWheel::NumMouseWheels>;
28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
29
30using MouseButtonParams =
31 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
32
33using KeyboardValues =
34 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
35using KeyboardModifierValues =
36 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardMods>;
37using MouseButtonValues =
38 std::array<Common::Input::ButtonStatus, Settings::NativeMouseButton::NumMouseButtons>;
39using MouseWheelValues =
40 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
41using MouseStickValue = Common::Input::TouchStatus;
42
43struct MousePosition {
44 f32 x;
45 f32 y;
46};
47
48struct DeviceStatus {
49 // Data from input_common
50 KeyboardValues keyboard_values{};
51 KeyboardModifierValues keyboard_moddifier_values{};
52 MouseButtonValues mouse_button_values{};
53 MouseWheelValues mouse_wheel_values{};
54 MouseStickValue mouse_stick_value{};
55
56 // Data for HID services
57 KeyboardKey keyboard_state{};
58 KeyboardModifier keyboard_moddifier_state{};
59 MouseButton mouse_button_state{};
60 MousePosition mouse_position_state{};
61 AnalogStickState mouse_wheel_state{};
62};
63
64enum class DeviceTriggerType {
65 Keyboard,
66 KeyboardModdifier,
67 Mouse,
68 RingController,
69};
70
71struct InterfaceUpdateCallback {
72 std::function<void(DeviceTriggerType)> on_change;
73};
74
75class EmulatedDevices {
76public:
77 /**
78 * Contains all input data related to external devices that aren't necessarily a controller
79 * This includes devices such as the keyboard or mouse
80 */
81 explicit EmulatedDevices();
82 ~EmulatedDevices();
83
84 YUZU_NON_COPYABLE(EmulatedDevices);
85 YUZU_NON_MOVEABLE(EmulatedDevices);
86
87 /// Removes all callbacks created from input devices
88 void UnloadInput();
89
90 /**
91 * Sets the emulated devices into configuring mode
92 * This prevents the modification of the HID state of the emulated devices by input commands
93 */
94 void EnableConfiguration();
95
96 /// Returns the emulated devices into normal mode, allowing the modification of the HID state
97 void DisableConfiguration();
98
99 /// Returns true if the emulated device is in configuring mode
100 bool IsConfiguring() const;
101
102 /// Reload all input devices
103 void ReloadInput();
104
105 /// Overrides current mapped devices with the stored configuration and reloads all input devices
106 void ReloadFromSettings();
107
108 /// Saves the current mapped configuration
109 void SaveCurrentConfig();
110
111 /// Reverts any mapped changes made that weren't saved
112 void RestoreConfig();
113
114 /// Returns the latest status of button input from the keyboard with parameters
115 KeyboardValues GetKeyboardValues() const;
116
117 /// Returns the latest status of button input from the keyboard modifiers with parameters
118 KeyboardModifierValues GetKeyboardModdifierValues() const;
119
120 /// Returns the latest status of button input from the mouse with parameters
121 MouseButtonValues GetMouseButtonsValues() const;
122
123 /// Returns the latest status of button input from the keyboard
124 KeyboardKey GetKeyboard() const;
125
126 /// Returns the latest status of button input from the keyboard modifiers
127 KeyboardModifier GetKeyboardModifier() const;
128
129 /// Returns the latest status of button input from the mouse
130 MouseButton GetMouseButtons() const;
131
132 /// Returns the latest mouse coordinates
133 MousePosition GetMousePosition() const;
134
135 /// Returns the latest mouse wheel change
136 AnalogStickState GetMouseWheel() const;
137
138 /**
139 * Adds a callback to the list of events
140 * @param update_callback InterfaceUpdateCallback that will be triggered
141 * @return an unique key corresponding to the callback index in the list
142 */
143 int SetCallback(InterfaceUpdateCallback update_callback);
144
145 /**
146 * Removes a callback from the list stopping any future events to this object
147 * @param key Key corresponding to the callback index in the list
148 */
149 void DeleteCallback(int key);
150
151private:
152 /// Helps assigning a value to keyboard_state
153 void UpdateKey(std::size_t key_index, bool status);
154
155 /**
156 * Updates the touch status of the keyboard device
157 * @param callback A CallbackStatus containing the key status
158 * @param index key ID to be updated
159 */
160 void SetKeyboardButton(const Common::Input::CallbackStatus& callback, std::size_t index);
161
162 /**
163 * Updates the keyboard status of the keyboard device
164 * @param callback A CallbackStatus containing the modifier key status
165 * @param index modifier key ID to be updated
166 */
167 void SetKeyboardModifier(const Common::Input::CallbackStatus& callback, std::size_t index);
168
169 /**
170 * Updates the mouse button status of the mouse device
171 * @param callback A CallbackStatus containing the button status
172 * @param index Button ID to be updated
173 */
174 void SetMouseButton(const Common::Input::CallbackStatus& callback, std::size_t index);
175
176 /**
177 * Updates the mouse wheel status of the mouse device
178 * @param callback A CallbackStatus containing the wheel status
179 * @param index wheel ID to be updated
180 */
181 void SetMouseWheel(const Common::Input::CallbackStatus& callback, std::size_t index);
182
183 /**
184 * Updates the mouse position status of the mouse device
185 * @param callback A CallbackStatus containing the position status
186 */
187 void SetMousePosition(const Common::Input::CallbackStatus& callback);
188
189 /**
190 * Triggers a callback that something has changed on the device status
191 * @param type Input type of the event to trigger
192 */
193 void TriggerOnChange(DeviceTriggerType type);
194
195 bool is_configuring{false};
196
197 KeyboardDevices keyboard_devices;
198 KeyboardModifierDevices keyboard_modifier_devices;
199 MouseButtonDevices mouse_button_devices;
200 MouseWheelDevices mouse_wheel_devices;
201 MouseStickDevice mouse_stick_device;
202
203 mutable std::mutex mutex;
204 mutable std::mutex callback_mutex;
205 std::unordered_map<int, InterfaceUpdateCallback> callback_list;
206 int last_callback_key = 0;
207
208 // Stores the current status of all external device input
209 DeviceStatus device_status;
210};
211
212} // namespace Core::HID
diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp
deleted file mode 100644
index 2cf25a870..000000000
--- a/src/core/hid/hid_core.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "core/hid/emulated_console.h"
6#include "core/hid/emulated_controller.h"
7#include "core/hid/emulated_devices.h"
8#include "core/hid/hid_core.h"
9#include "core/hle/service/hid/hid_util.h"
10
11namespace Core::HID {
12
13HIDCore::HIDCore()
14 : player_1{std::make_unique<EmulatedController>(NpadIdType::Player1)},
15 player_2{std::make_unique<EmulatedController>(NpadIdType::Player2)},
16 player_3{std::make_unique<EmulatedController>(NpadIdType::Player3)},
17 player_4{std::make_unique<EmulatedController>(NpadIdType::Player4)},
18 player_5{std::make_unique<EmulatedController>(NpadIdType::Player5)},
19 player_6{std::make_unique<EmulatedController>(NpadIdType::Player6)},
20 player_7{std::make_unique<EmulatedController>(NpadIdType::Player7)},
21 player_8{std::make_unique<EmulatedController>(NpadIdType::Player8)},
22 other{std::make_unique<EmulatedController>(NpadIdType::Other)},
23 handheld{std::make_unique<EmulatedController>(NpadIdType::Handheld)},
24 console{std::make_unique<EmulatedConsole>()}, devices{std::make_unique<EmulatedDevices>()} {}
25
26HIDCore::~HIDCore() = default;
27
28EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) {
29 switch (npad_id_type) {
30 case NpadIdType::Player1:
31 return player_1.get();
32 case NpadIdType::Player2:
33 return player_2.get();
34 case NpadIdType::Player3:
35 return player_3.get();
36 case NpadIdType::Player4:
37 return player_4.get();
38 case NpadIdType::Player5:
39 return player_5.get();
40 case NpadIdType::Player6:
41 return player_6.get();
42 case NpadIdType::Player7:
43 return player_7.get();
44 case NpadIdType::Player8:
45 return player_8.get();
46 case NpadIdType::Other:
47 return other.get();
48 case NpadIdType::Handheld:
49 return handheld.get();
50 case NpadIdType::Invalid:
51 default:
52 ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
53 return nullptr;
54 }
55}
56
57const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) const {
58 switch (npad_id_type) {
59 case NpadIdType::Player1:
60 return player_1.get();
61 case NpadIdType::Player2:
62 return player_2.get();
63 case NpadIdType::Player3:
64 return player_3.get();
65 case NpadIdType::Player4:
66 return player_4.get();
67 case NpadIdType::Player5:
68 return player_5.get();
69 case NpadIdType::Player6:
70 return player_6.get();
71 case NpadIdType::Player7:
72 return player_7.get();
73 case NpadIdType::Player8:
74 return player_8.get();
75 case NpadIdType::Other:
76 return other.get();
77 case NpadIdType::Handheld:
78 return handheld.get();
79 case NpadIdType::Invalid:
80 default:
81 ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
82 return nullptr;
83 }
84}
85EmulatedConsole* HIDCore::GetEmulatedConsole() {
86 return console.get();
87}
88
89const EmulatedConsole* HIDCore::GetEmulatedConsole() const {
90 return console.get();
91}
92
93EmulatedDevices* HIDCore::GetEmulatedDevices() {
94 return devices.get();
95}
96
97const EmulatedDevices* HIDCore::GetEmulatedDevices() const {
98 return devices.get();
99}
100
101EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) {
102 return GetEmulatedController(Service::HID::IndexToNpadIdType(index));
103}
104
105const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const {
106 return GetEmulatedController(Service::HID::IndexToNpadIdType(index));
107}
108
109void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
110 supported_style_tag.raw = style_tag.raw;
111 player_1->SetSupportedNpadStyleTag(supported_style_tag);
112 player_2->SetSupportedNpadStyleTag(supported_style_tag);
113 player_3->SetSupportedNpadStyleTag(supported_style_tag);
114 player_4->SetSupportedNpadStyleTag(supported_style_tag);
115 player_5->SetSupportedNpadStyleTag(supported_style_tag);
116 player_6->SetSupportedNpadStyleTag(supported_style_tag);
117 player_7->SetSupportedNpadStyleTag(supported_style_tag);
118 player_8->SetSupportedNpadStyleTag(supported_style_tag);
119 other->SetSupportedNpadStyleTag(supported_style_tag);
120 handheld->SetSupportedNpadStyleTag(supported_style_tag);
121}
122
123NpadStyleTag HIDCore::GetSupportedStyleTag() const {
124 return supported_style_tag;
125}
126
127s8 HIDCore::GetPlayerCount() const {
128 s8 active_players = 0;
129 for (std::size_t player_index = 0; player_index < available_controllers - 2; ++player_index) {
130 const auto* const controller = GetEmulatedControllerByIndex(player_index);
131 if (controller->IsConnected()) {
132 active_players++;
133 }
134 }
135 return active_players;
136}
137
138NpadIdType HIDCore::GetFirstNpadId() const {
139 for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
140 const auto* const controller = GetEmulatedControllerByIndex(player_index);
141 if (controller->IsConnected()) {
142 return controller->GetNpadIdType();
143 }
144 }
145 return NpadIdType::Player1;
146}
147
148NpadIdType HIDCore::GetFirstDisconnectedNpadId() const {
149 for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
150 const auto* const controller = GetEmulatedControllerByIndex(player_index);
151 if (!controller->IsConnected()) {
152 return controller->GetNpadIdType();
153 }
154 }
155 return NpadIdType::Player1;
156}
157
158void HIDCore::SetLastActiveController(NpadIdType npad_id) {
159 last_active_controller = npad_id;
160}
161
162NpadIdType HIDCore::GetLastActiveController() const {
163 return last_active_controller;
164}
165
166void HIDCore::EnableAllControllerConfiguration() {
167 player_1->EnableConfiguration();
168 player_2->EnableConfiguration();
169 player_3->EnableConfiguration();
170 player_4->EnableConfiguration();
171 player_5->EnableConfiguration();
172 player_6->EnableConfiguration();
173 player_7->EnableConfiguration();
174 player_8->EnableConfiguration();
175 other->EnableConfiguration();
176 handheld->EnableConfiguration();
177}
178
179void HIDCore::DisableAllControllerConfiguration() {
180 player_1->DisableConfiguration();
181 player_2->DisableConfiguration();
182 player_3->DisableConfiguration();
183 player_4->DisableConfiguration();
184 player_5->DisableConfiguration();
185 player_6->DisableConfiguration();
186 player_7->DisableConfiguration();
187 player_8->DisableConfiguration();
188 other->DisableConfiguration();
189 handheld->DisableConfiguration();
190}
191
192void HIDCore::ReloadInputDevices() {
193 player_1->ReloadFromSettings();
194 player_2->ReloadFromSettings();
195 player_3->ReloadFromSettings();
196 player_4->ReloadFromSettings();
197 player_5->ReloadFromSettings();
198 player_6->ReloadFromSettings();
199 player_7->ReloadFromSettings();
200 player_8->ReloadFromSettings();
201 other->ReloadFromSettings();
202 handheld->ReloadFromSettings();
203 console->ReloadFromSettings();
204 devices->ReloadFromSettings();
205}
206
207void HIDCore::UnloadInputDevices() {
208 player_1->UnloadInput();
209 player_2->UnloadInput();
210 player_3->UnloadInput();
211 player_4->UnloadInput();
212 player_5->UnloadInput();
213 player_6->UnloadInput();
214 player_7->UnloadInput();
215 player_8->UnloadInput();
216 other->UnloadInput();
217 handheld->UnloadInput();
218 console->UnloadInput();
219 devices->UnloadInput();
220}
221
222} // namespace Core::HID
diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h
deleted file mode 100644
index 80abab18b..000000000
--- a/src/core/hid/hid_core.h
+++ /dev/null
@@ -1,89 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "common/common_funcs.h"
9#include "core/hid/hid_types.h"
10
11namespace Core::HID {
12class EmulatedConsole;
13class EmulatedController;
14class EmulatedDevices;
15} // namespace Core::HID
16
17namespace Core::HID {
18
19class HIDCore {
20public:
21 explicit HIDCore();
22 ~HIDCore();
23
24 YUZU_NON_COPYABLE(HIDCore);
25 YUZU_NON_MOVEABLE(HIDCore);
26
27 EmulatedController* GetEmulatedController(NpadIdType npad_id_type);
28 const EmulatedController* GetEmulatedController(NpadIdType npad_id_type) const;
29
30 EmulatedController* GetEmulatedControllerByIndex(std::size_t index);
31 const EmulatedController* GetEmulatedControllerByIndex(std::size_t index) const;
32
33 EmulatedConsole* GetEmulatedConsole();
34 const EmulatedConsole* GetEmulatedConsole() const;
35
36 EmulatedDevices* GetEmulatedDevices();
37 const EmulatedDevices* GetEmulatedDevices() const;
38
39 void SetSupportedStyleTag(NpadStyleTag style_tag);
40 NpadStyleTag GetSupportedStyleTag() const;
41
42 /// Counts the connected players from P1-P8
43 s8 GetPlayerCount() const;
44
45 /// Returns the first connected npad id
46 NpadIdType GetFirstNpadId() const;
47
48 /// Returns the first disconnected npad id
49 NpadIdType GetFirstDisconnectedNpadId() const;
50
51 /// Sets the npad id of the last active controller
52 void SetLastActiveController(NpadIdType npad_id);
53
54 /// Returns the npad id of the last controller that pushed a button
55 NpadIdType GetLastActiveController() const;
56
57 /// Sets all emulated controllers into configuring mode.
58 void EnableAllControllerConfiguration();
59
60 /// Sets all emulated controllers into normal mode.
61 void DisableAllControllerConfiguration();
62
63 /// Reloads all input devices from settings
64 void ReloadInputDevices();
65
66 /// Removes all callbacks from input common
67 void UnloadInputDevices();
68
69 /// Number of emulated controllers
70 static constexpr std::size_t available_controllers{10};
71
72private:
73 std::unique_ptr<EmulatedController> player_1;
74 std::unique_ptr<EmulatedController> player_2;
75 std::unique_ptr<EmulatedController> player_3;
76 std::unique_ptr<EmulatedController> player_4;
77 std::unique_ptr<EmulatedController> player_5;
78 std::unique_ptr<EmulatedController> player_6;
79 std::unique_ptr<EmulatedController> player_7;
80 std::unique_ptr<EmulatedController> player_8;
81 std::unique_ptr<EmulatedController> other;
82 std::unique_ptr<EmulatedController> handheld;
83 std::unique_ptr<EmulatedConsole> console;
84 std::unique_ptr<EmulatedDevices> devices;
85 NpadStyleTag supported_style_tag{NpadStyleSet::All};
86 NpadIdType last_active_controller{NpadIdType::Handheld};
87};
88
89} // namespace Core::HID
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
deleted file mode 100644
index a81ed6af0..000000000
--- a/src/core/hid/hid_types.h
+++ /dev/null
@@ -1,736 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/bit_field.h"
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9#include "common/point.h"
10#include "common/uuid.h"
11#include "common/vector_math.h"
12
13namespace Core::HID {
14
15enum class DeviceIndex : u8 {
16 Left = 0,
17 Right = 1,
18 None = 2,
19 MaxDeviceIndex = 3,
20};
21
22// This is nn::hid::NpadButton
23enum class NpadButton : u64 {
24 None = 0,
25 A = 1U << 0,
26 B = 1U << 1,
27 X = 1U << 2,
28 Y = 1U << 3,
29 StickL = 1U << 4,
30 StickR = 1U << 5,
31 L = 1U << 6,
32 R = 1U << 7,
33 ZL = 1U << 8,
34 ZR = 1U << 9,
35 Plus = 1U << 10,
36 Minus = 1U << 11,
37
38 Left = 1U << 12,
39 Up = 1U << 13,
40 Right = 1U << 14,
41 Down = 1U << 15,
42
43 StickLLeft = 1U << 16,
44 StickLUp = 1U << 17,
45 StickLRight = 1U << 18,
46 StickLDown = 1U << 19,
47
48 StickRLeft = 1U << 20,
49 StickRUp = 1U << 21,
50 StickRRight = 1U << 22,
51 StickRDown = 1U << 23,
52
53 LeftSL = 1U << 24,
54 LeftSR = 1U << 25,
55
56 RightSL = 1U << 26,
57 RightSR = 1U << 27,
58
59 Palma = 1U << 28,
60 Verification = 1U << 29,
61 HandheldLeftB = 1U << 30,
62 LagonCLeft = 1U << 31,
63 LagonCUp = 1ULL << 32,
64 LagonCRight = 1ULL << 33,
65 LagonCDown = 1ULL << 34,
66
67 All = 0xFFFFFFFFFFFFFFFFULL,
68};
69DECLARE_ENUM_FLAG_OPERATORS(NpadButton);
70
71enum class KeyboardKeyIndex : u32 {
72 A = 4,
73 B = 5,
74 C = 6,
75 D = 7,
76 E = 8,
77 F = 9,
78 G = 10,
79 H = 11,
80 I = 12,
81 J = 13,
82 K = 14,
83 L = 15,
84 M = 16,
85 N = 17,
86 O = 18,
87 P = 19,
88 Q = 20,
89 R = 21,
90 S = 22,
91 T = 23,
92 U = 24,
93 V = 25,
94 W = 26,
95 X = 27,
96 Y = 28,
97 Z = 29,
98 D1 = 30,
99 D2 = 31,
100 D3 = 32,
101 D4 = 33,
102 D5 = 34,
103 D6 = 35,
104 D7 = 36,
105 D8 = 37,
106 D9 = 38,
107 D0 = 39,
108 Return = 40,
109 Escape = 41,
110 Backspace = 42,
111 Tab = 43,
112 Space = 44,
113 Minus = 45,
114 Plus = 46,
115 OpenBracket = 47,
116 CloseBracket = 48,
117 Pipe = 49,
118 Tilde = 50,
119 Semicolon = 51,
120 Quote = 52,
121 Backquote = 53,
122 Comma = 54,
123 Period = 55,
124 Slash = 56,
125 CapsLock = 57,
126 F1 = 58,
127 F2 = 59,
128 F3 = 60,
129 F4 = 61,
130 F5 = 62,
131 F6 = 63,
132 F7 = 64,
133 F8 = 65,
134 F9 = 66,
135 F10 = 67,
136 F11 = 68,
137 F12 = 69,
138 PrintScreen = 70,
139 ScrollLock = 71,
140 Pause = 72,
141 Insert = 73,
142 Home = 74,
143 PageUp = 75,
144 Delete = 76,
145 End = 77,
146 PageDown = 78,
147 RightArrow = 79,
148 LeftArrow = 80,
149 DownArrow = 81,
150 UpArrow = 82,
151 NumLock = 83,
152 NumPadDivide = 84,
153 NumPadMultiply = 85,
154 NumPadSubtract = 86,
155 NumPadAdd = 87,
156 NumPadEnter = 88,
157 NumPad1 = 89,
158 NumPad2 = 90,
159 NumPad3 = 91,
160 NumPad4 = 92,
161 NumPad5 = 93,
162 NumPad6 = 94,
163 NumPad7 = 95,
164 NumPad8 = 96,
165 NumPad9 = 97,
166 NumPad0 = 98,
167 NumPadDot = 99,
168 Backslash = 100,
169 Application = 101,
170 Power = 102,
171 NumPadEquals = 103,
172 F13 = 104,
173 F14 = 105,
174 F15 = 106,
175 F16 = 107,
176 F17 = 108,
177 F18 = 109,
178 F19 = 110,
179 F20 = 111,
180 F21 = 112,
181 F22 = 113,
182 F23 = 114,
183 F24 = 115,
184 NumPadComma = 133,
185 Ro = 135,
186 KatakanaHiragana = 136,
187 Yen = 137,
188 Henkan = 138,
189 Muhenkan = 139,
190 NumPadCommaPc98 = 140,
191 HangulEnglish = 144,
192 Hanja = 145,
193 Katakana = 146,
194 Hiragana = 147,
195 ZenkakuHankaku = 148,
196 LeftControl = 224,
197 LeftShift = 225,
198 LeftAlt = 226,
199 LeftGui = 227,
200 RightControl = 228,
201 RightShift = 229,
202 RightAlt = 230,
203 RightGui = 231,
204};
205
206// This is nn::hid::NpadIdType
207enum class NpadIdType : u32 {
208 Player1 = 0x0,
209 Player2 = 0x1,
210 Player3 = 0x2,
211 Player4 = 0x3,
212 Player5 = 0x4,
213 Player6 = 0x5,
214 Player7 = 0x6,
215 Player8 = 0x7,
216 Other = 0x10,
217 Handheld = 0x20,
218
219 Invalid = 0xFFFFFFFF,
220};
221
222enum class NpadInterfaceType : u8 {
223 Bluetooth = 1,
224 Rail = 2,
225 Usb = 3,
226 Embedded = 4,
227};
228
229// This is nn::hid::NpadStyleIndex
230enum class NpadStyleIndex : u8 {
231 None = 0,
232 ProController = 3,
233 Handheld = 4,
234 HandheldNES = 4,
235 JoyconDual = 5,
236 JoyconLeft = 6,
237 JoyconRight = 7,
238 GameCube = 8,
239 Pokeball = 9,
240 NES = 10,
241 SNES = 12,
242 N64 = 13,
243 SegaGenesis = 14,
244 SystemExt = 32,
245 System = 33,
246 MaxNpadType = 34,
247};
248
249// This is nn::hid::NpadStyleSet
250enum class NpadStyleSet : u32 {
251 None = 0,
252 Fullkey = 1U << 0,
253 Handheld = 1U << 1,
254 JoyDual = 1U << 2,
255 JoyLeft = 1U << 3,
256 JoyRight = 1U << 4,
257 Gc = 1U << 5,
258 Palma = 1U << 6,
259 Lark = 1U << 7,
260 HandheldLark = 1U << 8,
261 Lucia = 1U << 9,
262 Lagoon = 1U << 10,
263 Lager = 1U << 11,
264 SystemExt = 1U << 29,
265 System = 1U << 30,
266
267 All = 0xFFFFFFFFU,
268};
269static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
270DECLARE_ENUM_FLAG_OPERATORS(NpadStyleSet)
271
272// This is nn::hid::VibrationDevicePosition
273enum class VibrationDevicePosition : u32 {
274 None = 0,
275 Left = 1,
276 Right = 2,
277};
278
279// This is nn::hid::VibrationDeviceType
280enum class VibrationDeviceType : u32 {
281 Unknown = 0,
282 LinearResonantActuator = 1,
283 GcErm = 2,
284 N64 = 3,
285};
286
287// This is nn::hid::VibrationGcErmCommand
288enum class VibrationGcErmCommand : u64 {
289 Stop = 0,
290 Start = 1,
291 StopHard = 2,
292};
293
294// This is nn::hid::GyroscopeZeroDriftMode
295enum class GyroscopeZeroDriftMode : u32 {
296 Loose = 0,
297 Standard = 1,
298 Tight = 2,
299};
300
301// This is nn::settings::system::TouchScreenMode
302enum class TouchScreenMode : u32 {
303 Stylus = 0,
304 Standard = 1,
305};
306
307// This is nn::hid::TouchScreenModeForNx
308enum class TouchScreenModeForNx : u8 {
309 UseSystemSetting,
310 Finger,
311 Heat2,
312};
313
314// This is nn::hid::system::NpadBatteryLevel
315enum class NpadBatteryLevel : u32 {
316 Empty,
317 Critical,
318 Low,
319 High,
320 Full,
321};
322
323// This is nn::hid::NpadStyleTag
324struct NpadStyleTag {
325 union {
326 NpadStyleSet raw{};
327
328 BitField<0, 1, u32> fullkey;
329 BitField<1, 1, u32> handheld;
330 BitField<2, 1, u32> joycon_dual;
331 BitField<3, 1, u32> joycon_left;
332 BitField<4, 1, u32> joycon_right;
333 BitField<5, 1, u32> gamecube;
334 BitField<6, 1, u32> palma;
335 BitField<7, 1, u32> lark;
336 BitField<8, 1, u32> handheld_lark;
337 BitField<9, 1, u32> lucia;
338 BitField<10, 1, u32> lagoon;
339 BitField<11, 1, u32> lager;
340 BitField<29, 1, u32> system_ext;
341 BitField<30, 1, u32> system;
342 };
343};
344static_assert(sizeof(NpadStyleTag) == 4, "NpadStyleTag is an invalid size");
345
346// This is nn::hid::TouchAttribute
347struct TouchAttribute {
348 union {
349 u32 raw{};
350 BitField<0, 1, u32> start_touch;
351 BitField<1, 1, u32> end_touch;
352 };
353};
354static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size");
355
356// This is nn::hid::TouchState
357struct TouchState {
358 u64 delta_time{};
359 TouchAttribute attribute{};
360 u32 finger{};
361 Common::Point<u32> position{};
362 u32 diameter_x{};
363 u32 diameter_y{};
364 u32 rotation_angle{};
365};
366static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
367
368struct TouchFinger {
369 u64 last_touch{};
370 Common::Point<float> position{};
371 u32 id{};
372 TouchAttribute attribute{};
373 bool pressed{};
374};
375
376// This is nn::hid::TouchScreenConfigurationForNx
377struct TouchScreenConfigurationForNx {
378 TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
379 INSERT_PADDING_BYTES(0xF);
380};
381static_assert(sizeof(TouchScreenConfigurationForNx) == 0x10,
382 "TouchScreenConfigurationForNx is an invalid size");
383
384struct NpadColor {
385 u8 r{};
386 u8 g{};
387 u8 b{};
388 u8 a{};
389};
390static_assert(sizeof(NpadColor) == 4, "NpadColor is an invalid size");
391
392// This is nn::hid::NpadControllerColor
393struct NpadControllerColor {
394 NpadColor body{};
395 NpadColor button{};
396};
397static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size");
398
399// This is nn::hid::AnalogStickState
400struct AnalogStickState {
401 s32 x{};
402 s32 y{};
403};
404static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size");
405
406// This is nn::hid::server::NpadGcTriggerState
407struct NpadGcTriggerState {
408 s64 sampling_number{};
409 s32 left{};
410 s32 right{};
411};
412static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
413
414// This is nn::hid::system::NpadPowerInfo
415struct NpadPowerInfo {
416 bool is_powered{};
417 bool is_charging{};
418 INSERT_PADDING_BYTES(0x6);
419 NpadBatteryLevel battery_level{NpadBatteryLevel::Full};
420};
421static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");
422
423struct LedPattern {
424 explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
425 position1.Assign(light1);
426 position2.Assign(light2);
427 position3.Assign(light3);
428 position4.Assign(light4);
429 }
430 union {
431 u64 raw{};
432 BitField<0, 1, u64> position1;
433 BitField<1, 1, u64> position2;
434 BitField<2, 1, u64> position3;
435 BitField<3, 1, u64> position4;
436 };
437};
438
439struct HomeButtonState {
440 union {
441 u64 raw{};
442
443 // Buttons
444 BitField<0, 1, u64> home;
445 };
446};
447static_assert(sizeof(HomeButtonState) == 0x8, "HomeButtonState has incorrect size.");
448
449struct CaptureButtonState {
450 union {
451 u64 raw{};
452
453 // Buttons
454 BitField<0, 1, u64> capture;
455 };
456};
457static_assert(sizeof(CaptureButtonState) == 0x8, "CaptureButtonState has incorrect size.");
458
459struct NpadButtonState {
460 union {
461 NpadButton raw{};
462
463 // Buttons
464 BitField<0, 1, u64> a;
465 BitField<1, 1, u64> b;
466 BitField<2, 1, u64> x;
467 BitField<3, 1, u64> y;
468 BitField<4, 1, u64> stick_l;
469 BitField<5, 1, u64> stick_r;
470 BitField<6, 1, u64> l;
471 BitField<7, 1, u64> r;
472 BitField<8, 1, u64> zl;
473 BitField<9, 1, u64> zr;
474 BitField<10, 1, u64> plus;
475 BitField<11, 1, u64> minus;
476
477 // D-Pad
478 BitField<12, 1, u64> left;
479 BitField<13, 1, u64> up;
480 BitField<14, 1, u64> right;
481 BitField<15, 1, u64> down;
482
483 // Left JoyStick
484 BitField<16, 1, u64> stick_l_left;
485 BitField<17, 1, u64> stick_l_up;
486 BitField<18, 1, u64> stick_l_right;
487 BitField<19, 1, u64> stick_l_down;
488
489 // Right JoyStick
490 BitField<20, 1, u64> stick_r_left;
491 BitField<21, 1, u64> stick_r_up;
492 BitField<22, 1, u64> stick_r_right;
493 BitField<23, 1, u64> stick_r_down;
494
495 BitField<24, 1, u64> left_sl;
496 BitField<25, 1, u64> left_sr;
497
498 BitField<26, 1, u64> right_sl;
499 BitField<27, 1, u64> right_sr;
500
501 BitField<28, 1, u64> palma;
502 BitField<29, 1, u64> verification;
503 BitField<30, 1, u64> handheld_left_b;
504 BitField<31, 1, u64> lagon_c_left;
505 BitField<32, 1, u64> lagon_c_up;
506 BitField<33, 1, u64> lagon_c_right;
507 BitField<34, 1, u64> lagon_c_down;
508 };
509};
510static_assert(sizeof(NpadButtonState) == 0x8, "NpadButtonState has incorrect size.");
511
512// This is nn::hid::DebugPadButton
513struct DebugPadButton {
514 union {
515 u32 raw{};
516 BitField<0, 1, u32> a;
517 BitField<1, 1, u32> b;
518 BitField<2, 1, u32> x;
519 BitField<3, 1, u32> y;
520 BitField<4, 1, u32> l;
521 BitField<5, 1, u32> r;
522 BitField<6, 1, u32> zl;
523 BitField<7, 1, u32> zr;
524 BitField<8, 1, u32> plus;
525 BitField<9, 1, u32> minus;
526 BitField<10, 1, u32> d_left;
527 BitField<11, 1, u32> d_up;
528 BitField<12, 1, u32> d_right;
529 BitField<13, 1, u32> d_down;
530 };
531};
532static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size");
533
534// This is nn::hid::ConsoleSixAxisSensorHandle
535struct ConsoleSixAxisSensorHandle {
536 u8 unknown_1{};
537 u8 unknown_2{};
538 INSERT_PADDING_BYTES_NOINIT(2);
539};
540static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4,
541 "ConsoleSixAxisSensorHandle is an invalid size");
542
543// This is nn::hid::SixAxisSensorHandle
544struct SixAxisSensorHandle {
545 NpadStyleIndex npad_type{NpadStyleIndex::None};
546 u8 npad_id{};
547 DeviceIndex device_index{DeviceIndex::None};
548 INSERT_PADDING_BYTES_NOINIT(1);
549};
550static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size");
551
552// These parameters seem related to how much gyro/accelerometer is used
553struct SixAxisSensorFusionParameters {
554 f32 parameter1{0.03f}; // Range 0.0 to 1.0, default 0.03
555 f32 parameter2{0.4f}; // Default 0.4
556};
557static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
558 "SixAxisSensorFusionParameters is an invalid size");
559
560// This is nn::hid::server::SixAxisSensorProperties
561struct SixAxisSensorProperties {
562 union {
563 u8 raw{};
564 BitField<0, 1, u8> is_newly_assigned;
565 BitField<1, 1, u8> is_firmware_update_available;
566 };
567};
568static_assert(sizeof(SixAxisSensorProperties) == 1, "SixAxisSensorProperties is an invalid size");
569
570// This is nn::hid::SixAxisSensorCalibrationParameter
571struct SixAxisSensorCalibrationParameter {
572 std::array<u8, 0x744> unknown_data{};
573};
574static_assert(sizeof(SixAxisSensorCalibrationParameter) == 0x744,
575 "SixAxisSensorCalibrationParameter is an invalid size");
576
577// This is nn::hid::SixAxisSensorIcInformation
578struct SixAxisSensorIcInformation {
579 f32 angular_rate{2000.0f}; // dps
580 std::array<f32, 6> unknown_gyro_data1{
581 -10.0f, -10.0f, -10.0f, 10.0f, 10.0f, 10.0f,
582 }; // dps
583 std::array<f32, 9> unknown_gyro_data2{
584 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
585 };
586 std::array<f32, 9> unknown_gyro_data3{
587 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
588 };
589 f32 acceleration_range{8.0f}; // g force
590 std::array<f32, 6> unknown_accel_data1{
591 -0.0612f, -0.0612f, -0.0612f, 0.0612f, 0.0612f, 0.0612f,
592 }; // g force
593 std::array<f32, 9> unknown_accel_data2{
594 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
595 };
596 std::array<f32, 9> unknown_accel_data3{
597 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
598 };
599};
600static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8,
601 "SixAxisSensorIcInformation is an invalid size");
602
603// This is nn::hid::SixAxisSensorAttribute
604struct SixAxisSensorAttribute {
605 union {
606 u32 raw{};
607 BitField<0, 1, u32> is_connected;
608 BitField<1, 1, u32> is_interpolated;
609 };
610};
611static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
612
613// This is nn::hid::SixAxisSensorState
614struct SixAxisSensorState {
615 s64 delta_time{};
616 s64 sampling_number{};
617 Common::Vec3f accel{};
618 Common::Vec3f gyro{};
619 Common::Vec3f rotation{};
620 std::array<Common::Vec3f, 3> orientation{};
621 SixAxisSensorAttribute attribute{};
622 INSERT_PADDING_BYTES(4); // Reserved
623};
624static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
625
626// This is nn::hid::VibrationDeviceHandle
627struct VibrationDeviceHandle {
628 NpadStyleIndex npad_type{NpadStyleIndex::None};
629 u8 npad_id{};
630 DeviceIndex device_index{DeviceIndex::None};
631 INSERT_PADDING_BYTES_NOINIT(1);
632};
633static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size");
634
635// This is nn::hid::VibrationValue
636struct VibrationValue {
637 f32 low_amplitude{};
638 f32 low_frequency{};
639 f32 high_amplitude{};
640 f32 high_frequency{};
641};
642static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
643
644constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
645 .low_amplitude = 0.0f,
646 .low_frequency = 160.0f,
647 .high_amplitude = 0.0f,
648 .high_frequency = 320.0f,
649};
650
651// This is nn::hid::VibrationDeviceInfo
652struct VibrationDeviceInfo {
653 VibrationDeviceType type{};
654 VibrationDevicePosition position{};
655};
656static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
657
658// This is nn::hid::KeyboardModifier
659struct KeyboardModifier {
660 union {
661 u32 raw{};
662 BitField<0, 1, u32> control;
663 BitField<1, 1, u32> shift;
664 BitField<2, 1, u32> left_alt;
665 BitField<3, 1, u32> right_alt;
666 BitField<4, 1, u32> gui;
667 BitField<8, 1, u32> caps_lock;
668 BitField<9, 1, u32> scroll_lock;
669 BitField<10, 1, u32> num_lock;
670 BitField<11, 1, u32> katakana;
671 BitField<12, 1, u32> hiragana;
672 };
673};
674
675static_assert(sizeof(KeyboardModifier) == 0x4, "KeyboardModifier is an invalid size");
676
677// This is nn::hid::KeyboardAttribute
678struct KeyboardAttribute {
679 union {
680 u32 raw{};
681 BitField<0, 1, u32> is_connected;
682 };
683};
684static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid size");
685
686// This is nn::hid::KeyboardKey
687struct KeyboardKey {
688 // This should be a 256 bit flag
689 std::array<u8, 32> key{};
690};
691static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size");
692
693// This is nn::hid::MouseButton
694struct MouseButton {
695 union {
696 u32_le raw{};
697 BitField<0, 1, u32> left;
698 BitField<1, 1, u32> right;
699 BitField<2, 1, u32> middle;
700 BitField<3, 1, u32> forward;
701 BitField<4, 1, u32> back;
702 };
703};
704static_assert(sizeof(MouseButton) == 0x4, "MouseButton is an invalid size");
705
706// This is nn::hid::MouseAttribute
707struct MouseAttribute {
708 union {
709 u32 raw{};
710 BitField<0, 1, u32> transferable;
711 BitField<1, 1, u32> is_connected;
712 };
713};
714static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size");
715
716// This is nn::hid::detail::MouseState
717struct MouseState {
718 s64 sampling_number{};
719 s32 x{};
720 s32 y{};
721 s32 delta_x{};
722 s32 delta_y{};
723 // Axis Order in HW is switched for the wheel
724 s32 delta_wheel_y{};
725 s32 delta_wheel_x{};
726 MouseButton button{};
727 MouseAttribute attribute{};
728};
729static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size");
730
731struct UniquePadId {
732 u64 id;
733};
734static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size");
735
736} // namespace Core::HID
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
deleted file mode 100644
index a05716fd8..000000000
--- a/src/core/hid/input_converter.cpp
+++ /dev/null
@@ -1,436 +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 <random>
6
7#include "common/input.h"
8#include "core/hid/input_converter.h"
9
10namespace Core::HID {
11
12Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback) {
13 Common::Input::BatteryStatus battery{Common::Input::BatteryStatus::None};
14 switch (callback.type) {
15 case Common::Input::InputType::Analog:
16 case Common::Input::InputType::Trigger: {
17 const auto value = TransformToTrigger(callback).analog.value;
18 battery = Common::Input::BatteryLevel::Empty;
19 if (value > 0.2f) {
20 battery = Common::Input::BatteryLevel::Critical;
21 }
22 if (value > 0.4f) {
23 battery = Common::Input::BatteryLevel::Low;
24 }
25 if (value > 0.6f) {
26 battery = Common::Input::BatteryLevel::Medium;
27 }
28 if (value > 0.8f) {
29 battery = Common::Input::BatteryLevel::Full;
30 }
31 if (value >= 0.95f) {
32 battery = Common::Input::BatteryLevel::Charging;
33 }
34 break;
35 }
36 case Common::Input::InputType::Button:
37 battery = callback.button_status.value ? Common::Input::BatteryLevel::Charging
38 : Common::Input::BatteryLevel::Critical;
39 break;
40 case Common::Input::InputType::Battery:
41 battery = callback.battery_status;
42 break;
43 default:
44 LOG_ERROR(Input, "Conversion from type {} to battery not implemented", callback.type);
45 break;
46 }
47
48 return battery;
49}
50
51Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback) {
52 Common::Input::ButtonStatus status{};
53 switch (callback.type) {
54 case Common::Input::InputType::Analog:
55 status.value = TransformToTrigger(callback).pressed.value;
56 status.toggle = callback.analog_status.properties.toggle;
57 status.inverted = callback.analog_status.properties.inverted_button;
58 break;
59 case Common::Input::InputType::Trigger:
60 status.value = TransformToTrigger(callback).pressed.value;
61 break;
62 case Common::Input::InputType::Button:
63 status = callback.button_status;
64 break;
65 case Common::Input::InputType::Motion:
66 status.value = std::abs(callback.motion_status.gyro.x.raw_value) > 1.0f;
67 break;
68 default:
69 LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type);
70 break;
71 }
72
73 if (status.inverted) {
74 status.value = !status.value;
75 }
76
77 return status;
78}
79
80Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback) {
81 Common::Input::MotionStatus status{};
82 switch (callback.type) {
83 case Common::Input::InputType::Button: {
84 Common::Input::AnalogProperties properties{
85 .deadzone = 0.0f,
86 .range = 1.0f,
87 .offset = 0.0f,
88 };
89 status.delta_timestamp = 1000;
90 status.force_update = true;
91 status.accel.x = {
92 .value = 0.0f,
93 .raw_value = 0.0f,
94 .properties = properties,
95 };
96 status.accel.y = {
97 .value = 0.0f,
98 .raw_value = 0.0f,
99 .properties = properties,
100 };
101 status.accel.z = {
102 .value = 0.0f,
103 .raw_value = -1.0f,
104 .properties = properties,
105 };
106 status.gyro.x = {
107 .value = 0.0f,
108 .raw_value = 0.0f,
109 .properties = properties,
110 };
111 status.gyro.y = {
112 .value = 0.0f,
113 .raw_value = 0.0f,
114 .properties = properties,
115 };
116 status.gyro.z = {
117 .value = 0.0f,
118 .raw_value = 0.0f,
119 .properties = properties,
120 };
121 if (TransformToButton(callback).value) {
122 std::random_device device;
123 std::mt19937 gen(device());
124 std::uniform_int_distribution<s16> distribution(-5000, 5000);
125 status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
126 status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
127 status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
128 status.gyro.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
129 status.gyro.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
130 status.gyro.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
131 }
132 break;
133 }
134 case Common::Input::InputType::Motion:
135 status = callback.motion_status;
136 break;
137 default:
138 LOG_ERROR(Input, "Conversion from type {} to motion not implemented", callback.type);
139 break;
140 }
141 SanitizeAnalog(status.accel.x, false);
142 SanitizeAnalog(status.accel.y, false);
143 SanitizeAnalog(status.accel.z, false);
144 SanitizeAnalog(status.gyro.x, false);
145 SanitizeAnalog(status.gyro.y, false);
146 SanitizeAnalog(status.gyro.z, false);
147
148 return status;
149}
150
151Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback) {
152 Common::Input::StickStatus status{};
153
154 switch (callback.type) {
155 case Common::Input::InputType::Stick:
156 status = callback.stick_status;
157 break;
158 default:
159 LOG_ERROR(Input, "Conversion from type {} to stick not implemented", callback.type);
160 break;
161 }
162
163 SanitizeStick(status.x, status.y, true);
164 const auto& properties_x = status.x.properties;
165 const auto& properties_y = status.y.properties;
166 const float x = status.x.value;
167 const float y = status.y.value;
168
169 // Set directional buttons
170 status.right = x > properties_x.threshold;
171 status.left = x < -properties_x.threshold;
172 status.up = y > properties_y.threshold;
173 status.down = y < -properties_y.threshold;
174
175 return status;
176}
177
178Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback) {
179 Common::Input::TouchStatus status{};
180
181 switch (callback.type) {
182 case Common::Input::InputType::Touch:
183 status = callback.touch_status;
184 break;
185 case Common::Input::InputType::Stick:
186 status.x = callback.stick_status.x;
187 status.y = callback.stick_status.y;
188 break;
189 default:
190 LOG_ERROR(Input, "Conversion from type {} to touch not implemented", callback.type);
191 break;
192 }
193
194 SanitizeAnalog(status.x, true);
195 SanitizeAnalog(status.y, true);
196 float& x = status.x.value;
197 float& y = status.y.value;
198
199 // Adjust if value is inverted
200 x = status.x.properties.inverted ? 1.0f + x : x;
201 y = status.y.properties.inverted ? 1.0f + y : y;
202
203 // clamp value
204 x = std::clamp(x, 0.0f, 1.0f);
205 y = std::clamp(y, 0.0f, 1.0f);
206
207 if (status.pressed.inverted) {
208 status.pressed.value = !status.pressed.value;
209 }
210
211 return status;
212}
213
214Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback) {
215 Common::Input::TriggerStatus status{};
216 float& raw_value = status.analog.raw_value;
217 bool calculate_button_value = true;
218
219 switch (callback.type) {
220 case Common::Input::InputType::Analog:
221 status.analog.properties = callback.analog_status.properties;
222 raw_value = callback.analog_status.raw_value;
223 break;
224 case Common::Input::InputType::Button:
225 status.analog.properties.range = 1.0f;
226 status.analog.properties.inverted = callback.button_status.inverted;
227 raw_value = callback.button_status.value ? 1.0f : 0.0f;
228 break;
229 case Common::Input::InputType::Trigger:
230 status = callback.trigger_status;
231 calculate_button_value = false;
232 break;
233 case Common::Input::InputType::Motion:
234 status.analog.properties.range = 1.0f;
235 raw_value = callback.motion_status.accel.x.raw_value;
236 break;
237 default:
238 LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type);
239 break;
240 }
241
242 SanitizeAnalog(status.analog, true);
243 const auto& properties = status.analog.properties;
244 float& value = status.analog.value;
245
246 // Set button status
247 if (calculate_button_value) {
248 status.pressed.value = value > properties.threshold;
249 }
250
251 // Adjust if value is inverted
252 value = properties.inverted ? 1.0f + value : value;
253
254 // clamp value
255 value = std::clamp(value, 0.0f, 1.0f);
256
257 return status;
258}
259
260Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback) {
261 Common::Input::AnalogStatus status{};
262
263 switch (callback.type) {
264 case Common::Input::InputType::Analog:
265 status.properties = callback.analog_status.properties;
266 status.raw_value = callback.analog_status.raw_value;
267 break;
268 default:
269 LOG_ERROR(Input, "Conversion from type {} to analog not implemented", callback.type);
270 break;
271 }
272
273 SanitizeAnalog(status, false);
274
275 // Adjust if value is inverted
276 status.value = status.properties.inverted ? -status.value : status.value;
277
278 return status;
279}
280
281Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback) {
282 Common::Input::CameraStatus camera{};
283 switch (callback.type) {
284 case Common::Input::InputType::IrSensor:
285 camera = {
286 .format = callback.camera_status,
287 .data = callback.raw_data,
288 };
289 break;
290 default:
291 LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type);
292 break;
293 }
294
295 return camera;
296}
297
298Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback) {
299 Common::Input::NfcStatus nfc{};
300 switch (callback.type) {
301 case Common::Input::InputType::Nfc:
302 return callback.nfc_status;
303 default:
304 LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type);
305 break;
306 }
307
308 return nfc;
309}
310
311Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback) {
312 switch (callback.type) {
313 case Common::Input::InputType::Color:
314 return callback.color_status;
315 break;
316 default:
317 LOG_ERROR(Input, "Conversion from type {} to color not implemented", callback.type);
318 return {};
319 break;
320 }
321}
322
323void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
324 const auto& properties = analog.properties;
325 float& raw_value = analog.raw_value;
326 float& value = analog.value;
327
328 if (!std::isnormal(raw_value)) {
329 raw_value = 0;
330 }
331
332 // Apply center offset
333 raw_value -= properties.offset;
334
335 // Set initial values to be formatted
336 value = raw_value;
337
338 // Calculate vector size
339 const float r = std::abs(value);
340
341 // Return zero if value is smaller than the deadzone
342 if (r <= properties.deadzone || properties.deadzone == 1.0f) {
343 analog.value = 0;
344 return;
345 }
346
347 // Adjust range of value
348 const float deadzone_factor =
349 1.0f / r * (r - properties.deadzone) / (1.0f - properties.deadzone);
350 value = value * deadzone_factor / properties.range;
351
352 // Invert direction if needed
353 if (properties.inverted) {
354 value = -value;
355 }
356
357 // Clamp value
358 if (clamp_value) {
359 value = std::clamp(value, -1.0f, 1.0f);
360 }
361}
362
363void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
364 bool clamp_value) {
365 const auto& properties_x = analog_x.properties;
366 const auto& properties_y = analog_y.properties;
367 float& raw_x = analog_x.raw_value;
368 float& raw_y = analog_y.raw_value;
369 float& x = analog_x.value;
370 float& y = analog_y.value;
371
372 if (!std::isnormal(raw_x)) {
373 raw_x = 0;
374 }
375 if (!std::isnormal(raw_y)) {
376 raw_y = 0;
377 }
378
379 // Apply center offset
380 raw_x += properties_x.offset;
381 raw_y += properties_y.offset;
382
383 // Apply X scale correction from offset
384 if (std::abs(properties_x.offset) < 0.75f) {
385 if (raw_x > 0) {
386 raw_x /= 1 + properties_x.offset;
387 } else {
388 raw_x /= 1 - properties_x.offset;
389 }
390 }
391
392 // Apply Y scale correction from offset
393 if (std::abs(properties_y.offset) < 0.75f) {
394 if (raw_y > 0) {
395 raw_y /= 1 + properties_y.offset;
396 } else {
397 raw_y /= 1 - properties_y.offset;
398 }
399 }
400
401 // Invert direction if needed
402 raw_x = properties_x.inverted ? -raw_x : raw_x;
403 raw_y = properties_y.inverted ? -raw_y : raw_y;
404
405 // Set initial values to be formatted
406 x = raw_x;
407 y = raw_y;
408
409 // Calculate vector size
410 float r = x * x + y * y;
411 r = std::sqrt(r);
412
413 // TODO(German77): Use deadzone and range of both axis
414
415 // Return zero if values are smaller than the deadzone
416 if (r <= properties_x.deadzone || properties_x.deadzone >= 1.0f) {
417 x = 0;
418 y = 0;
419 return;
420 }
421
422 // Adjust range of joystick
423 const float deadzone_factor =
424 1.0f / r * (r - properties_x.deadzone) / (1.0f - properties_x.deadzone);
425 x = x * deadzone_factor / properties_x.range;
426 y = y * deadzone_factor / properties_x.range;
427 r = r * deadzone_factor / properties_x.range;
428
429 // Normalize joystick
430 if (clamp_value && r > 1.0f) {
431 x /= r;
432 y /= r;
433 }
434}
435
436} // namespace Core::HID
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h
deleted file mode 100644
index c51c03e57..000000000
--- a/src/core/hid/input_converter.h
+++ /dev/null
@@ -1,119 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6namespace Common::Input {
7struct CallbackStatus;
8enum class BatteryLevel : u32;
9using BatteryStatus = BatteryLevel;
10struct AnalogStatus;
11struct ButtonStatus;
12struct MotionStatus;
13struct StickStatus;
14struct TouchStatus;
15struct TriggerStatus;
16}; // namespace Common::Input
17
18namespace Core::HID {
19
20/**
21 * Converts raw input data into a valid battery status.
22 *
23 * @param callback Supported callbacks: Analog, Battery, Trigger.
24 * @return A valid BatteryStatus object.
25 */
26Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback);
27
28/**
29 * Converts raw input data into a valid button status. Applies invert properties to the output.
30 *
31 * @param callback Supported callbacks: Analog, Button, Trigger.
32 * @return A valid TouchStatus object.
33 */
34Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback);
35
36/**
37 * Converts raw input data into a valid motion status.
38 *
39 * @param callback Supported callbacks: Motion.
40 * @return A valid TouchStatus object.
41 */
42Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback);
43
44/**
45 * Converts raw input data into a valid stick status. Applies offset, deadzone, range and invert
46 * properties to the output.
47 *
48 * @param callback Supported callbacks: Stick.
49 * @return A valid StickStatus object.
50 */
51Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback);
52
53/**
54 * Converts raw input data into a valid touch status.
55 *
56 * @param callback Supported callbacks: Touch.
57 * @return A valid TouchStatus object.
58 */
59Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback);
60
61/**
62 * Converts raw input data into a valid trigger status. Applies offset, deadzone, range and
63 * invert properties to the output. Button status uses the threshold property if necessary.
64 *
65 * @param callback Supported callbacks: Analog, Button, Trigger.
66 * @return A valid TriggerStatus object.
67 */
68Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback);
69
70/**
71 * Converts raw input data into a valid analog status. Applies offset, deadzone, range and
72 * invert properties to the output.
73 *
74 * @param callback Supported callbacks: Analog.
75 * @return A valid AnalogStatus object.
76 */
77Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
78
79/**
80 * Converts raw input data into a valid camera status.
81 *
82 * @param callback Supported callbacks: Camera.
83 * @return A valid CameraObject object.
84 */
85Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback);
86
87/**
88 * Converts raw input data into a valid nfc status.
89 *
90 * @param callback Supported callbacks: Nfc.
91 * @return A valid data tag vector.
92 */
93Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback);
94
95/**
96 * Converts raw input data into a valid color status.
97 *
98 * @param callback Supported callbacks: Color.
99 * @return A valid Color object.
100 */
101Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback);
102
103/**
104 * Converts raw analog data into a valid analog value
105 * @param analog An analog object containing raw data and properties
106 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
107 */
108void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value);
109
110/**
111 * Converts raw stick data into a valid stick value
112 * @param analog_x raw analog data and properties for the x-axis
113 * @param analog_y raw analog data and properties for the y-axis
114 * @param clamp_value bool that determines if the value needs to be clamped into the unit circle.
115 */
116void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
117 bool clamp_value);
118
119} // namespace Core::HID
diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp
deleted file mode 100644
index 072f38a68..000000000
--- a/src/core/hid/input_interpreter.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hid/hid_types.h"
6#include "core/hid/input_interpreter.h"
7#include "core/hle/service/hid/controllers/npad.h"
8#include "core/hle/service/hid/hid_server.h"
9#include "core/hle/service/hid/resource_manager.h"
10#include "core/hle/service/sm/sm.h"
11
12InputInterpreter::InputInterpreter(Core::System& system)
13 : npad{system.ServiceManager()
14 .GetService<Service::HID::IHidServer>("hid")
15 ->GetResourceManager()
16 ->GetNpad()} {
17 ResetButtonStates();
18}
19
20InputInterpreter::~InputInterpreter() = default;
21
22void InputInterpreter::PollInput() {
23 if (npad == nullptr) {
24 return;
25 }
26 const auto button_state = npad->GetAndResetPressState();
27
28 previous_index = current_index;
29 current_index = (current_index + 1) % button_states.size();
30
31 button_states[current_index] = button_state;
32}
33
34void InputInterpreter::ResetButtonStates() {
35 previous_index = 0;
36 current_index = 0;
37
38 button_states[0] = Core::HID::NpadButton::All;
39
40 for (std::size_t i = 1; i < button_states.size(); ++i) {
41 button_states[i] = Core::HID::NpadButton::None;
42 }
43}
44
45bool InputInterpreter::IsButtonPressed(Core::HID::NpadButton button) const {
46 return True(button_states[current_index] & button);
47}
48
49bool InputInterpreter::IsButtonPressedOnce(Core::HID::NpadButton button) const {
50 const bool current_press = True(button_states[current_index] & button);
51 const bool previous_press = True(button_states[previous_index] & button);
52
53 return current_press && !previous_press;
54}
55
56bool InputInterpreter::IsButtonHeld(Core::HID::NpadButton button) const {
57 Core::HID::NpadButton held_buttons{button_states[0]};
58
59 for (std::size_t i = 1; i < button_states.size(); ++i) {
60 held_buttons &= button_states[i];
61 }
62
63 return True(held_buttons & button);
64}
diff --git a/src/core/hid/input_interpreter.h b/src/core/hid/input_interpreter.h
deleted file mode 100644
index 3569aac93..000000000
--- a/src/core/hid/input_interpreter.h
+++ /dev/null
@@ -1,111 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "common/common_types.h"
9
10namespace Core {
11class System;
12}
13
14namespace Core::HID {
15enum class NpadButton : u64;
16}
17
18namespace Service::HID {
19class NPad;
20}
21
22/**
23 * The InputInterpreter class interfaces with HID to retrieve button press states.
24 * Input is intended to be polled every 50ms so that a button is considered to be
25 * held down after 400ms has elapsed since the initial button press and subsequent
26 * repeated presses occur every 50ms.
27 */
28class InputInterpreter {
29public:
30 explicit InputInterpreter(Core::System& system);
31 virtual ~InputInterpreter();
32
33 /// Gets a button state from HID and inserts it into the array of button states.
34 void PollInput();
35
36 /// Resets all the button states to their defaults.
37 void ResetButtonStates();
38
39 /**
40 * Checks whether the button is pressed.
41 *
42 * @param button The button to check.
43 *
44 * @returns True when the button is pressed.
45 */
46 [[nodiscard]] bool IsButtonPressed(Core::HID::NpadButton button) const;
47
48 /**
49 * Checks whether any of the buttons in the parameter list is pressed.
50 *
51 * @tparam HIDButton The buttons to check.
52 *
53 * @returns True when at least one of the buttons is pressed.
54 */
55 template <Core::HID::NpadButton... T>
56 [[nodiscard]] bool IsAnyButtonPressed() {
57 return (IsButtonPressed(T) || ...);
58 }
59
60 /**
61 * The specified button is considered to be pressed once
62 * if it is currently pressed and not pressed previously.
63 *
64 * @param button The button to check.
65 *
66 * @returns True when the button is pressed once.
67 */
68 [[nodiscard]] bool IsButtonPressedOnce(Core::HID::NpadButton button) const;
69
70 /**
71 * Checks whether any of the buttons in the parameter list is pressed once.
72 *
73 * @tparam T The buttons to check.
74 *
75 * @returns True when at least one of the buttons is pressed once.
76 */
77 template <Core::HID::NpadButton... T>
78 [[nodiscard]] bool IsAnyButtonPressedOnce() const {
79 return (IsButtonPressedOnce(T) || ...);
80 }
81
82 /**
83 * The specified button is considered to be held down if it is pressed in all 9 button states.
84 *
85 * @param button The button to check.
86 *
87 * @returns True when the button is held down.
88 */
89 [[nodiscard]] bool IsButtonHeld(Core::HID::NpadButton button) const;
90
91 /**
92 * Checks whether any of the buttons in the parameter list is held down.
93 *
94 * @tparam T The buttons to check.
95 *
96 * @returns True when at least one of the buttons is held down.
97 */
98 template <Core::HID::NpadButton... T>
99 [[nodiscard]] bool IsAnyButtonHeld() const {
100 return (IsButtonHeld(T) || ...);
101 }
102
103private:
104 std::shared_ptr<Service::HID::NPad> npad;
105
106 /// Stores 9 consecutive button states polled from HID.
107 std::array<Core::HID::NpadButton, 9> button_states{};
108
109 std::size_t previous_index{};
110 std::size_t current_index{};
111};
diff --git a/src/core/hid/irs_types.h b/src/core/hid/irs_types.h
deleted file mode 100644
index 0d1bfe53f..000000000
--- a/src/core/hid/irs_types.h
+++ /dev/null
@@ -1,301 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8#include "core/hid/hid_types.h"
9
10namespace Core::IrSensor {
11
12// This is nn::irsensor::CameraAmbientNoiseLevel
13enum class CameraAmbientNoiseLevel : u32 {
14 Low,
15 Medium,
16 High,
17 Unknown3, // This level can't be reached
18};
19
20// This is nn::irsensor::CameraLightTarget
21enum class CameraLightTarget : u32 {
22 AllLeds,
23 BrightLeds,
24 DimLeds,
25 None,
26};
27
28// This is nn::irsensor::PackedCameraLightTarget
29enum class PackedCameraLightTarget : u8 {
30 AllLeds,
31 BrightLeds,
32 DimLeds,
33 None,
34};
35
36// This is nn::irsensor::AdaptiveClusteringMode
37enum class AdaptiveClusteringMode : u32 {
38 StaticFov,
39 DynamicFov,
40};
41
42// This is nn::irsensor::AdaptiveClusteringTargetDistance
43enum class AdaptiveClusteringTargetDistance : u32 {
44 Near,
45 Middle,
46 Far,
47};
48
49// This is nn::irsensor::ImageTransferProcessorFormat
50enum class ImageTransferProcessorFormat : u32 {
51 Size320x240,
52 Size160x120,
53 Size80x60,
54 Size40x30,
55 Size20x15,
56};
57
58// This is nn::irsensor::PackedImageTransferProcessorFormat
59enum class PackedImageTransferProcessorFormat : u8 {
60 Size320x240,
61 Size160x120,
62 Size80x60,
63 Size40x30,
64 Size20x15,
65};
66
67// This is nn::irsensor::IrCameraStatus
68enum class IrCameraStatus : u32 {
69 Available,
70 Unsupported,
71 Unconnected,
72};
73
74// This is nn::irsensor::IrCameraInternalStatus
75enum class IrCameraInternalStatus : u32 {
76 Stopped,
77 FirmwareUpdateNeeded,
78 Unknown2,
79 Unknown3,
80 Unknown4,
81 FirmwareVersionRequested,
82 FirmwareVersionIsInvalid,
83 Ready,
84 Setting,
85};
86
87// This is nn::irsensor::detail::StatusManager::IrSensorMode
88enum class IrSensorMode : u64 {
89 None,
90 MomentProcessor,
91 ClusteringProcessor,
92 ImageTransferProcessor,
93 PointingProcessorMarker,
94 TeraPluginProcessor,
95 IrLedProcessor,
96};
97
98// This is nn::irsensor::ImageProcessorStatus
99enum ImageProcessorStatus : u32 {
100 Stopped,
101 Running,
102};
103
104// This is nn::irsensor::HandAnalysisMode
105enum class HandAnalysisMode : u32 {
106 None,
107 Silhouette,
108 Image,
109 SilhoueteAndImage,
110 SilhuetteOnly,
111};
112
113// This is nn::irsensor::IrSensorFunctionLevel
114enum class IrSensorFunctionLevel : u8 {
115 unknown0,
116 unknown1,
117 unknown2,
118 unknown3,
119 unknown4,
120};
121
122// This is nn::irsensor::MomentProcessorPreprocess
123enum class MomentProcessorPreprocess : u32 {
124 Unknown0,
125 Unknown1,
126};
127
128// This is nn::irsensor::PackedMomentProcessorPreprocess
129enum class PackedMomentProcessorPreprocess : u8 {
130 Unknown0,
131 Unknown1,
132};
133
134// This is nn::irsensor::PointingStatus
135enum class PointingStatus : u32 {
136 Unknown0,
137 Unknown1,
138};
139
140struct IrsRect {
141 s16 x;
142 s16 y;
143 s16 width;
144 s16 height;
145};
146
147struct IrsCentroid {
148 f32 x;
149 f32 y;
150};
151
152struct CameraConfig {
153 u64 exposure_time;
154 CameraLightTarget light_target;
155 u32 gain;
156 bool is_negative_used;
157 INSERT_PADDING_BYTES(7);
158};
159static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size");
160
161struct PackedCameraConfig {
162 u64 exposure_time;
163 PackedCameraLightTarget light_target;
164 u8 gain;
165 bool is_negative_used;
166 INSERT_PADDING_BYTES(5);
167};
168static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size");
169
170// This is nn::irsensor::IrCameraHandle
171struct IrCameraHandle {
172 u8 npad_id{};
173 Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
174 INSERT_PADDING_BYTES(2);
175};
176static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
177
178// This is nn::irsensor::PackedMcuVersion
179struct PackedMcuVersion {
180 u16 major;
181 u16 minor;
182};
183static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
184
185// This is nn::irsensor::PackedMomentProcessorConfig
186struct PackedMomentProcessorConfig {
187 PackedCameraConfig camera_config;
188 IrsRect window_of_interest;
189 PackedMcuVersion required_mcu_version;
190 PackedMomentProcessorPreprocess preprocess;
191 u8 preprocess_intensity_threshold;
192 INSERT_PADDING_BYTES(2);
193};
194static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
195 "PackedMomentProcessorConfig is an invalid size");
196
197// This is nn::irsensor::PackedClusteringProcessorConfig
198struct PackedClusteringProcessorConfig {
199 PackedCameraConfig camera_config;
200 IrsRect window_of_interest;
201 PackedMcuVersion required_mcu_version;
202 u32 pixel_count_min;
203 u32 pixel_count_max;
204 u8 object_intensity_min;
205 bool is_external_light_filter_enabled;
206 INSERT_PADDING_BYTES(2);
207};
208static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28,
209 "PackedClusteringProcessorConfig is an invalid size");
210
211// This is nn::irsensor::PackedImageTransferProcessorConfig
212struct PackedImageTransferProcessorConfig {
213 PackedCameraConfig camera_config;
214 PackedMcuVersion required_mcu_version;
215 PackedImageTransferProcessorFormat format;
216 INSERT_PADDING_BYTES(3);
217};
218static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
219 "PackedImageTransferProcessorConfig is an invalid size");
220
221// This is nn::irsensor::PackedTeraPluginProcessorConfig
222struct PackedTeraPluginProcessorConfig {
223 PackedMcuVersion required_mcu_version;
224 u8 mode;
225 u8 unknown_1;
226 u8 unknown_2;
227 u8 unknown_3;
228};
229static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
230 "PackedTeraPluginProcessorConfig is an invalid size");
231
232// This is nn::irsensor::PackedPointingProcessorConfig
233struct PackedPointingProcessorConfig {
234 IrsRect window_of_interest;
235 PackedMcuVersion required_mcu_version;
236};
237static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
238 "PackedPointingProcessorConfig is an invalid size");
239
240// This is nn::irsensor::PackedFunctionLevel
241struct PackedFunctionLevel {
242 IrSensorFunctionLevel function_level;
243 INSERT_PADDING_BYTES(3);
244};
245static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
246
247// This is nn::irsensor::PackedImageTransferProcessorExConfig
248struct PackedImageTransferProcessorExConfig {
249 PackedCameraConfig camera_config;
250 PackedMcuVersion required_mcu_version;
251 PackedImageTransferProcessorFormat origin_format;
252 PackedImageTransferProcessorFormat trimming_format;
253 u16 trimming_start_x;
254 u16 trimming_start_y;
255 bool is_external_light_filter_enabled;
256 INSERT_PADDING_BYTES(5);
257};
258static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
259 "PackedImageTransferProcessorExConfig is an invalid size");
260
261// This is nn::irsensor::PackedIrLedProcessorConfig
262struct PackedIrLedProcessorConfig {
263 PackedMcuVersion required_mcu_version;
264 u8 light_target;
265 INSERT_PADDING_BYTES(3);
266};
267static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
268 "PackedIrLedProcessorConfig is an invalid size");
269
270// This is nn::irsensor::HandAnalysisConfig
271struct HandAnalysisConfig {
272 HandAnalysisMode mode;
273};
274static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size");
275
276// This is nn::irsensor::detail::ProcessorState contents are different for each processor
277struct ProcessorState {
278 std::array<u8, 0xE20> processor_raw_data{};
279};
280static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size");
281
282// This is nn::irsensor::detail::DeviceFormat
283struct DeviceFormat {
284 Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected};
285 Core::IrSensor::IrCameraInternalStatus camera_internal_status{
286 Core::IrSensor::IrCameraInternalStatus::Ready};
287 Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None};
288 ProcessorState state{};
289};
290static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size");
291
292// This is nn::irsensor::ImageTransferProcessorState
293struct ImageTransferProcessorState {
294 u64 sampling_number;
295 Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
296 INSERT_PADDING_BYTES(4);
297};
298static_assert(sizeof(ImageTransferProcessorState) == 0x10,
299 "ImageTransferProcessorState is an invalid size");
300
301} // namespace Core::IrSensor
diff --git a/src/core/hid/motion_input.cpp b/src/core/hid/motion_input.cpp
deleted file mode 100644
index f56f2ae1d..000000000
--- a/src/core/hid/motion_input.cpp
+++ /dev/null
@@ -1,357 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cmath>
5
6#include "common/math_util.h"
7#include "core/hid/motion_input.h"
8
9namespace Core::HID {
10
11MotionInput::MotionInput() {
12 // Initialize PID constants with default values
13 SetPID(0.3f, 0.005f, 0.0f);
14 SetGyroThreshold(ThresholdStandard);
15 ResetQuaternion();
16 ResetRotations();
17}
18
19void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) {
20 kp = new_kp;
21 ki = new_ki;
22 kd = new_kd;
23}
24
25void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
26 accel = acceleration;
27
28 accel.x = std::clamp(accel.x, -AccelMaxValue, AccelMaxValue);
29 accel.y = std::clamp(accel.y, -AccelMaxValue, AccelMaxValue);
30 accel.z = std::clamp(accel.z, -AccelMaxValue, AccelMaxValue);
31}
32
33void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
34 gyro = gyroscope - gyro_bias;
35
36 gyro.x = std::clamp(gyro.x, -GyroMaxValue, GyroMaxValue);
37 gyro.y = std::clamp(gyro.y, -GyroMaxValue, GyroMaxValue);
38 gyro.z = std::clamp(gyro.z, -GyroMaxValue, GyroMaxValue);
39
40 // Auto adjust gyro_bias to minimize drift
41 if (!IsMoving(IsAtRestRelaxed)) {
42 gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f);
43 }
44
45 // Adjust drift when calibration mode is enabled
46 if (calibration_mode) {
47 gyro_bias = (gyro_bias * 0.99f) + (gyroscope * 0.01f);
48 StopCalibration();
49 }
50
51 if (gyro.Length() < gyro_threshold * user_gyro_threshold) {
52 gyro = {};
53 } else {
54 only_accelerometer = false;
55 }
56}
57
58void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
59 quat = quaternion;
60}
61
62void MotionInput::SetEulerAngles(const Common::Vec3f& euler_angles) {
63 const float cr = std::cos(euler_angles.x * 0.5f);
64 const float sr = std::sin(euler_angles.x * 0.5f);
65 const float cp = std::cos(euler_angles.y * 0.5f);
66 const float sp = std::sin(euler_angles.y * 0.5f);
67 const float cy = std::cos(euler_angles.z * 0.5f);
68 const float sy = std::sin(euler_angles.z * 0.5f);
69
70 quat.w = cr * cp * cy + sr * sp * sy;
71 quat.xyz.x = sr * cp * cy - cr * sp * sy;
72 quat.xyz.y = cr * sp * cy + sr * cp * sy;
73 quat.xyz.z = cr * cp * sy - sr * sp * cy;
74}
75
76void MotionInput::SetGyroBias(const Common::Vec3f& bias) {
77 gyro_bias = bias;
78}
79
80void MotionInput::SetGyroThreshold(f32 threshold) {
81 gyro_threshold = threshold;
82}
83
84void MotionInput::SetUserGyroThreshold(f32 threshold) {
85 user_gyro_threshold = threshold / ThresholdStandard;
86}
87
88void MotionInput::EnableReset(bool reset) {
89 reset_enabled = reset;
90}
91
92void MotionInput::ResetRotations() {
93 rotations = {};
94}
95
96void MotionInput::ResetQuaternion() {
97 quat = {{0.0f, 0.0f, -1.0f}, 0.0f};
98}
99
100bool MotionInput::IsMoving(f32 sensitivity) const {
101 return gyro.Length() >= sensitivity || accel.Length() <= 0.9f || accel.Length() >= 1.1f;
102}
103
104bool MotionInput::IsCalibrated(f32 sensitivity) const {
105 return real_error.Length() < sensitivity;
106}
107
108void MotionInput::UpdateRotation(u64 elapsed_time) {
109 const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
110 if (sample_period > 0.1f) {
111 return;
112 }
113 rotations += gyro * sample_period;
114}
115
116void MotionInput::Calibrate() {
117 calibration_mode = true;
118 calibration_counter = 0;
119}
120
121void MotionInput::StopCalibration() {
122 if (calibration_counter++ > CalibrationSamples) {
123 calibration_mode = false;
124 ResetQuaternion();
125 ResetRotations();
126 }
127}
128
129// Based on Madgwick's implementation of Mayhony's AHRS algorithm.
130// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
131void MotionInput::UpdateOrientation(u64 elapsed_time) {
132 if (!IsCalibrated(0.1f)) {
133 ResetOrientation();
134 }
135 // Short name local variable for readability
136 f32 q1 = quat.w;
137 f32 q2 = quat.xyz[0];
138 f32 q3 = quat.xyz[1];
139 f32 q4 = quat.xyz[2];
140 const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
141
142 // Ignore invalid elapsed time
143 if (sample_period > 0.1f) {
144 return;
145 }
146
147 const auto normal_accel = accel.Normalized();
148 auto rad_gyro = gyro * Common::PI * 2;
149 const f32 swap = rad_gyro.x;
150 rad_gyro.x = rad_gyro.y;
151 rad_gyro.y = -swap;
152 rad_gyro.z = -rad_gyro.z;
153
154 // Clear gyro values if there is no gyro present
155 if (only_accelerometer) {
156 rad_gyro.x = 0;
157 rad_gyro.y = 0;
158 rad_gyro.z = 0;
159 }
160
161 // Ignore drift correction if acceleration is not reliable
162 if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) {
163 const f32 ax = -normal_accel.x;
164 const f32 ay = normal_accel.y;
165 const f32 az = -normal_accel.z;
166
167 // Estimated direction of gravity
168 const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
169 const f32 vy = 2.0f * (q1 * q2 + q3 * q4);
170 const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
171
172 // Error is cross product between estimated direction and measured direction of gravity
173 const Common::Vec3f new_real_error = {
174 az * vx - ax * vz,
175 ay * vz - az * vy,
176 ax * vy - ay * vx,
177 };
178
179 derivative_error = new_real_error - real_error;
180 real_error = new_real_error;
181
182 // Prevent integral windup
183 if (ki != 0.0f && !IsCalibrated(0.05f)) {
184 integral_error += real_error;
185 } else {
186 integral_error = {};
187 }
188
189 // Apply feedback terms
190 if (!only_accelerometer) {
191 rad_gyro += kp * real_error;
192 rad_gyro += ki * integral_error;
193 rad_gyro += kd * derivative_error;
194 } else {
195 // Give more weight to accelerometer values to compensate for the lack of gyro
196 rad_gyro += 35.0f * kp * real_error;
197 rad_gyro += 10.0f * ki * integral_error;
198 rad_gyro += 10.0f * kd * derivative_error;
199
200 // Emulate gyro values for games that need them
201 gyro.x = -rad_gyro.y;
202 gyro.y = rad_gyro.x;
203 gyro.z = -rad_gyro.z;
204 UpdateRotation(elapsed_time);
205 }
206 }
207
208 const f32 gx = rad_gyro.y;
209 const f32 gy = rad_gyro.x;
210 const f32 gz = rad_gyro.z;
211
212 // Integrate rate of change of quaternion
213 const f32 pa = q2;
214 const f32 pb = q3;
215 const f32 pc = q4;
216 q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period);
217 q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period);
218 q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
219 q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
220
221 quat.w = q1;
222 quat.xyz[0] = q2;
223 quat.xyz[1] = q3;
224 quat.xyz[2] = q4;
225 quat = quat.Normalized();
226}
227
228std::array<Common::Vec3f, 3> MotionInput::GetOrientation() const {
229 const Common::Quaternion<float> quad{
230 .xyz = {-quat.xyz[1], -quat.xyz[0], -quat.w},
231 .w = -quat.xyz[2],
232 };
233 const std::array<float, 16> matrix4x4 = quad.ToMatrix();
234
235 return {Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
236 Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
237 Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10])};
238}
239
240Common::Vec3f MotionInput::GetAcceleration() const {
241 return accel;
242}
243
244Common::Vec3f MotionInput::GetGyroscope() const {
245 return gyro;
246}
247
248Common::Vec3f MotionInput::GetGyroBias() const {
249 return gyro_bias;
250}
251
252Common::Quaternion<f32> MotionInput::GetQuaternion() const {
253 return quat;
254}
255
256Common::Vec3f MotionInput::GetRotations() const {
257 return rotations;
258}
259
260Common::Vec3f MotionInput::GetEulerAngles() const {
261 // roll (x-axis rotation)
262 const float sinr_cosp = 2 * (quat.w * quat.xyz.x + quat.xyz.y * quat.xyz.z);
263 const float cosr_cosp = 1 - 2 * (quat.xyz.x * quat.xyz.x + quat.xyz.y * quat.xyz.y);
264
265 // pitch (y-axis rotation)
266 const float sinp = std::sqrt(1 + 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));
267 const float cosp = std::sqrt(1 - 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));
268
269 // yaw (z-axis rotation)
270 const float siny_cosp = 2 * (quat.w * quat.xyz.z + quat.xyz.x * quat.xyz.y);
271 const float cosy_cosp = 1 - 2 * (quat.xyz.y * quat.xyz.y + quat.xyz.z * quat.xyz.z);
272
273 return {
274 std::atan2(sinr_cosp, cosr_cosp),
275 2 * std::atan2(sinp, cosp) - Common::PI / 2,
276 std::atan2(siny_cosp, cosy_cosp),
277 };
278}
279
280void MotionInput::ResetOrientation() {
281 if (!reset_enabled || only_accelerometer) {
282 return;
283 }
284 if (!IsMoving(IsAtRestRelaxed) && accel.z <= -0.9f) {
285 ++reset_counter;
286 if (reset_counter > 900) {
287 quat.w = 0;
288 quat.xyz[0] = 0;
289 quat.xyz[1] = 0;
290 quat.xyz[2] = -1;
291 SetOrientationFromAccelerometer();
292 integral_error = {};
293 reset_counter = 0;
294 }
295 } else {
296 reset_counter = 0;
297 }
298}
299
300void MotionInput::SetOrientationFromAccelerometer() {
301 int iterations = 0;
302 const f32 sample_period = 0.015f;
303
304 const auto normal_accel = accel.Normalized();
305
306 while (!IsCalibrated(0.01f) && ++iterations < 100) {
307 // Short name local variable for readability
308 f32 q1 = quat.w;
309 f32 q2 = quat.xyz[0];
310 f32 q3 = quat.xyz[1];
311 f32 q4 = quat.xyz[2];
312
313 Common::Vec3f rad_gyro;
314 const f32 ax = -normal_accel.x;
315 const f32 ay = normal_accel.y;
316 const f32 az = -normal_accel.z;
317
318 // Estimated direction of gravity
319 const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
320 const f32 vy = 2.0f * (q1 * q2 + q3 * q4);
321 const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
322
323 // Error is cross product between estimated direction and measured direction of gravity
324 const Common::Vec3f new_real_error = {
325 az * vx - ax * vz,
326 ay * vz - az * vy,
327 ax * vy - ay * vx,
328 };
329
330 derivative_error = new_real_error - real_error;
331 real_error = new_real_error;
332
333 rad_gyro += 10.0f * kp * real_error;
334 rad_gyro += 5.0f * ki * integral_error;
335 rad_gyro += 10.0f * kd * derivative_error;
336
337 const f32 gx = rad_gyro.y;
338 const f32 gy = rad_gyro.x;
339 const f32 gz = rad_gyro.z;
340
341 // Integrate rate of change of quaternion
342 const f32 pa = q2;
343 const f32 pb = q3;
344 const f32 pc = q4;
345 q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period);
346 q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period);
347 q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
348 q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
349
350 quat.w = q1;
351 quat.xyz[0] = q2;
352 quat.xyz[1] = q3;
353 quat.xyz[2] = q4;
354 quat = quat.Normalized();
355 }
356}
357} // namespace Core::HID
diff --git a/src/core/hid/motion_input.h b/src/core/hid/motion_input.h
deleted file mode 100644
index 11678983d..000000000
--- a/src/core/hid/motion_input.h
+++ /dev/null
@@ -1,119 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "common/quaternion.h"
8#include "common/vector_math.h"
9
10namespace Core::HID {
11
12class MotionInput {
13public:
14 static constexpr float ThresholdLoose = 0.01f;
15 static constexpr float ThresholdStandard = 0.007f;
16 static constexpr float ThresholdThight = 0.002f;
17
18 static constexpr float IsAtRestRelaxed = 0.05f;
19 static constexpr float IsAtRestLoose = 0.02f;
20 static constexpr float IsAtRestStandard = 0.01f;
21 static constexpr float IsAtRestThight = 0.005f;
22
23 static constexpr float GyroMaxValue = 5.0f;
24 static constexpr float AccelMaxValue = 7.0f;
25
26 static constexpr std::size_t CalibrationSamples = 300;
27
28 explicit MotionInput();
29
30 MotionInput(const MotionInput&) = default;
31 MotionInput& operator=(const MotionInput&) = default;
32
33 MotionInput(MotionInput&&) = default;
34 MotionInput& operator=(MotionInput&&) = default;
35
36 void SetPID(f32 new_kp, f32 new_ki, f32 new_kd);
37 void SetAcceleration(const Common::Vec3f& acceleration);
38 void SetGyroscope(const Common::Vec3f& gyroscope);
39 void SetQuaternion(const Common::Quaternion<f32>& quaternion);
40 void SetEulerAngles(const Common::Vec3f& euler_angles);
41 void SetGyroBias(const Common::Vec3f& bias);
42 void SetGyroThreshold(f32 threshold);
43
44 /// Applies a modifier on top of the normal gyro threshold
45 void SetUserGyroThreshold(f32 threshold);
46
47 void EnableReset(bool reset);
48 void ResetRotations();
49 void ResetQuaternion();
50
51 void UpdateRotation(u64 elapsed_time);
52 void UpdateOrientation(u64 elapsed_time);
53
54 void Calibrate();
55
56 [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
57 [[nodiscard]] Common::Vec3f GetAcceleration() const;
58 [[nodiscard]] Common::Vec3f GetGyroscope() const;
59 [[nodiscard]] Common::Vec3f GetGyroBias() const;
60 [[nodiscard]] Common::Vec3f GetRotations() const;
61 [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
62 [[nodiscard]] Common::Vec3f GetEulerAngles() const;
63
64 [[nodiscard]] bool IsMoving(f32 sensitivity) const;
65 [[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
66
67private:
68 void StopCalibration();
69 void ResetOrientation();
70 void SetOrientationFromAccelerometer();
71
72 // PID constants
73 f32 kp;
74 f32 ki;
75 f32 kd;
76
77 // PID errors
78 Common::Vec3f real_error;
79 Common::Vec3f integral_error;
80 Common::Vec3f derivative_error;
81
82 // Quaternion containing the device orientation
83 Common::Quaternion<f32> quat;
84
85 // Number of full rotations in each axis
86 Common::Vec3f rotations;
87
88 // Acceleration vector measurement in G force
89 Common::Vec3f accel;
90
91 // Gyroscope vector measurement in radians/s.
92 Common::Vec3f gyro;
93
94 // Vector to be subtracted from gyro measurements
95 Common::Vec3f gyro_bias;
96
97 // Minimum gyro amplitude to detect if the device is moving
98 f32 gyro_threshold = 0.0f;
99
100 // Multiplies gyro_threshold by this value
101 f32 user_gyro_threshold = 0.0f;
102
103 // Number of invalid sequential data
104 u32 reset_counter = 0;
105
106 // If the provided data is invalid the device will be autocalibrated
107 bool reset_enabled = true;
108
109 // Use accelerometer values to calculate position
110 bool only_accelerometer = true;
111
112 // When enabled it will aggressively adjust for gyro drift
113 bool calibration_mode = false;
114
115 // Used to auto disable calibration mode
116 std::size_t calibration_counter = 0;
117};
118
119} // namespace Core::HID