diff options
Diffstat (limited to 'src/core/hid')
| -rw-r--r-- | src/core/hid/emulated_console.cpp | 324 | ||||
| -rw-r--r-- | src/core/hid/emulated_console.h | 192 | ||||
| -rw-r--r-- | src/core/hid/emulated_controller.cpp | 1972 | ||||
| -rw-r--r-- | src/core/hid/emulated_controller.h | 619 | ||||
| -rw-r--r-- | src/core/hid/emulated_devices.cpp | 483 | ||||
| -rw-r--r-- | src/core/hid/emulated_devices.h | 212 | ||||
| -rw-r--r-- | src/core/hid/hid_core.cpp | 222 | ||||
| -rw-r--r-- | src/core/hid/hid_core.h | 89 | ||||
| -rw-r--r-- | src/core/hid/hid_types.h | 736 | ||||
| -rw-r--r-- | src/core/hid/input_converter.cpp | 436 | ||||
| -rw-r--r-- | src/core/hid/input_converter.h | 119 | ||||
| -rw-r--r-- | src/core/hid/input_interpreter.cpp | 64 | ||||
| -rw-r--r-- | src/core/hid/input_interpreter.h | 111 | ||||
| -rw-r--r-- | src/core/hid/irs_types.h | 301 | ||||
| -rw-r--r-- | src/core/hid/motion_input.cpp | 357 | ||||
| -rw-r--r-- | src/core/hid/motion_input.h | 119 |
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 | |||
| 8 | namespace Core::HID { | ||
| 9 | EmulatedConsole::EmulatedConsole() = default; | ||
| 10 | |||
| 11 | EmulatedConsole::~EmulatedConsole() = default; | ||
| 12 | |||
| 13 | void 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 | |||
| 21 | void 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 | |||
| 73 | void 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 | |||
| 118 | void EmulatedConsole::UnloadInput() { | ||
| 119 | for (auto& motion : motion_devices) { | ||
| 120 | motion.reset(); | ||
| 121 | } | ||
| 122 | for (auto& touch : touch_devices) { | ||
| 123 | touch.reset(); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | void EmulatedConsole::EnableConfiguration() { | ||
| 128 | is_configuring = true; | ||
| 129 | SaveCurrentConfig(); | ||
| 130 | } | ||
| 131 | |||
| 132 | void EmulatedConsole::DisableConfiguration() { | ||
| 133 | is_configuring = false; | ||
| 134 | } | ||
| 135 | |||
| 136 | bool EmulatedConsole::IsConfiguring() const { | ||
| 137 | return is_configuring; | ||
| 138 | } | ||
| 139 | |||
| 140 | void EmulatedConsole::SaveCurrentConfig() { | ||
| 141 | if (!is_configuring) { | ||
| 142 | return; | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | void EmulatedConsole::RestoreConfig() { | ||
| 147 | if (!is_configuring) { | ||
| 148 | return; | ||
| 149 | } | ||
| 150 | ReloadFromSettings(); | ||
| 151 | } | ||
| 152 | |||
| 153 | Common::ParamPackage EmulatedConsole::GetMotionParam() const { | ||
| 154 | return motion_params[0]; | ||
| 155 | } | ||
| 156 | |||
| 157 | void EmulatedConsole::SetMotionParam(Common::ParamPackage param) { | ||
| 158 | motion_params[0] = std::move(param); | ||
| 159 | ReloadInput(); | ||
| 160 | } | ||
| 161 | |||
| 162 | void 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 | |||
| 202 | void 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 | |||
| 257 | ConsoleMotionValues EmulatedConsole::GetMotionValues() const { | ||
| 258 | std::scoped_lock lock{mutex}; | ||
| 259 | return console.motion_values; | ||
| 260 | } | ||
| 261 | |||
| 262 | TouchValues EmulatedConsole::GetTouchValues() const { | ||
| 263 | std::scoped_lock lock{mutex}; | ||
| 264 | return console.touch_values; | ||
| 265 | } | ||
| 266 | |||
| 267 | ConsoleMotion EmulatedConsole::GetMotion() const { | ||
| 268 | std::scoped_lock lock{mutex}; | ||
| 269 | return console.motion_state; | ||
| 270 | } | ||
| 271 | |||
| 272 | TouchFingerState EmulatedConsole::GetTouch() const { | ||
| 273 | std::scoped_lock lock{mutex}; | ||
| 274 | return console.touch_state; | ||
| 275 | } | ||
| 276 | |||
| 277 | std::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 | |||
| 290 | std::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 | |||
| 299 | void 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 | |||
| 309 | int 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 | |||
| 315 | void 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 | |||
| 23 | namespace Core::HID { | ||
| 24 | static constexpr std::size_t MaxTouchDevices = 32; | ||
| 25 | static constexpr std::size_t MaxActiveTouchInputs = 16; | ||
| 26 | |||
| 27 | struct ConsoleMotionInfo { | ||
| 28 | Common::Input::MotionStatus raw_status{}; | ||
| 29 | MotionInput emulated{}; | ||
| 30 | }; | ||
| 31 | |||
| 32 | using ConsoleMotionDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 2>; | ||
| 33 | using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, MaxTouchDevices>; | ||
| 34 | |||
| 35 | using ConsoleMotionParams = std::array<Common::ParamPackage, 2>; | ||
| 36 | using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>; | ||
| 37 | |||
| 38 | using ConsoleMotionValues = ConsoleMotionInfo; | ||
| 39 | using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>; | ||
| 40 | |||
| 41 | // Contains all motion related data that is used on the services | ||
| 42 | struct 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 | |||
| 53 | using TouchFingerState = std::array<TouchFinger, MaxActiveTouchInputs>; | ||
| 54 | |||
| 55 | struct 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 | |||
| 65 | enum class ConsoleTriggerType { | ||
| 66 | Motion, | ||
| 67 | Touch, | ||
| 68 | All, | ||
| 69 | }; | ||
| 70 | |||
| 71 | struct ConsoleUpdateCallback { | ||
| 72 | std::function<void(ConsoleTriggerType)> on_change; | ||
| 73 | }; | ||
| 74 | |||
| 75 | class EmulatedConsole { | ||
| 76 | public: | ||
| 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 | |||
| 147 | private: | ||
| 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 | |||
| 13 | namespace Core::HID { | ||
| 14 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | ||
| 15 | constexpr s32 HID_TRIGGER_MAX = 0x7fff; | ||
| 16 | constexpr u32 TURBO_BUTTON_DELAY = 4; | ||
| 17 | // Use a common UUID for TAS and Virtual Gamepad | ||
| 18 | constexpr Common::UUID TAS_UUID = | ||
| 19 | Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; | ||
| 20 | constexpr Common::UUID VIRTUAL_UUID = | ||
| 21 | Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; | ||
| 22 | |||
| 23 | EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {} | ||
| 24 | |||
| 25 | EmulatedController::~EmulatedController() = default; | ||
| 26 | |||
| 27 | NpadStyleIndex 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 | |||
| 56 | Settings::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 | |||
| 85 | void 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 | |||
| 121 | void 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 | |||
| 145 | void 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 | |||
| 218 | void 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 | |||
| 267 | void 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 | |||
| 322 | void 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 | |||
| 515 | void 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 | |||
| 564 | void 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 | |||
| 571 | void 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 | |||
| 598 | void EmulatedController::EnableSystemButtons() { | ||
| 599 | std::scoped_lock lock{mutex}; | ||
| 600 | system_buttons_enabled = true; | ||
| 601 | } | ||
| 602 | |||
| 603 | void 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 | |||
| 610 | void 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 | |||
| 616 | bool EmulatedController::IsConfiguring() const { | ||
| 617 | return is_configuring; | ||
| 618 | } | ||
| 619 | |||
| 620 | void 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 | |||
| 639 | void EmulatedController::RestoreConfig() { | ||
| 640 | if (!is_configuring) { | ||
| 641 | return; | ||
| 642 | } | ||
| 643 | ReloadFromSettings(); | ||
| 644 | } | ||
| 645 | |||
| 646 | std::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(), [¶m](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(), [¶m](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 | |||
| 697 | Common::ParamPackage EmulatedController::GetButtonParam(std::size_t index) const { | ||
| 698 | if (index >= button_params.size()) { | ||
| 699 | return {}; | ||
| 700 | } | ||
| 701 | return button_params[index]; | ||
| 702 | } | ||
| 703 | |||
| 704 | Common::ParamPackage EmulatedController::GetStickParam(std::size_t index) const { | ||
| 705 | if (index >= stick_params.size()) { | ||
| 706 | return {}; | ||
| 707 | } | ||
| 708 | return stick_params[index]; | ||
| 709 | } | ||
| 710 | |||
| 711 | Common::ParamPackage EmulatedController::GetMotionParam(std::size_t index) const { | ||
| 712 | if (index >= motion_params.size()) { | ||
| 713 | return {}; | ||
| 714 | } | ||
| 715 | return motion_params[index]; | ||
| 716 | } | ||
| 717 | |||
| 718 | void 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 | |||
| 726 | void 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 | |||
| 734 | void 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 | |||
| 742 | void EmulatedController::StartMotionCalibration() { | ||
| 743 | for (ControllerMotionInfo& motion : controller.motion_values) { | ||
| 744 | motion.emulated.Calibrate(); | ||
| 745 | } | ||
| 746 | } | ||
| 747 | |||
| 748 | void 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 | |||
| 916 | void 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 | |||
| 972 | void 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 | |||
| 1019 | void 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 | |||
| 1053 | void 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 | |||
| 1103 | void 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 | |||
| 1170 | void 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 | |||
| 1185 | void 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 | |||
| 1199 | void 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 | |||
| 1211 | bool 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 | |||
| 1246 | bool 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 | |||
| 1269 | Common::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 | |||
| 1310 | Common::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 | |||
| 1318 | bool 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 | |||
| 1339 | Common::ParamPackage EmulatedController::GetRingParam() const { | ||
| 1340 | return ring_params[0]; | ||
| 1341 | } | ||
| 1342 | |||
| 1343 | void EmulatedController::SetRingParam(Common::ParamPackage param) { | ||
| 1344 | ring_params[0] = std::move(param); | ||
| 1345 | ReloadInput(); | ||
| 1346 | } | ||
| 1347 | |||
| 1348 | bool 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 | |||
| 1374 | bool EmulatedController::AddNfcHandle() { | ||
| 1375 | nfc_handles++; | ||
| 1376 | return SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::NFC) == | ||
| 1377 | Common::Input::DriverResult::Success; | ||
| 1378 | } | ||
| 1379 | |||
| 1380 | bool 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 | |||
| 1390 | bool 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 | |||
| 1405 | bool 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 | |||
| 1420 | bool 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 | |||
| 1435 | bool 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 | |||
| 1452 | bool 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 | |||
| 1467 | bool 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 | |||
| 1482 | void 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 | |||
| 1503 | void 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 | |||
| 1523 | void 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 | |||
| 1576 | bool 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 | |||
| 1592 | bool 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 | |||
| 1623 | void 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 | |||
| 1645 | void 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 | |||
| 1661 | bool 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 | |||
| 1669 | NpadIdType EmulatedController::GetNpadIdType() const { | ||
| 1670 | std::scoped_lock lock{mutex}; | ||
| 1671 | return npad_id_type; | ||
| 1672 | } | ||
| 1673 | |||
| 1674 | NpadStyleIndex 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 | |||
| 1682 | void 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 | |||
| 1707 | LedPattern 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 | |||
| 1730 | ButtonValues EmulatedController::GetButtonsValues() const { | ||
| 1731 | std::scoped_lock lock{mutex}; | ||
| 1732 | return controller.button_values; | ||
| 1733 | } | ||
| 1734 | |||
| 1735 | SticksValues EmulatedController::GetSticksValues() const { | ||
| 1736 | std::scoped_lock lock{mutex}; | ||
| 1737 | return controller.stick_values; | ||
| 1738 | } | ||
| 1739 | |||
| 1740 | TriggerValues EmulatedController::GetTriggersValues() const { | ||
| 1741 | std::scoped_lock lock{mutex}; | ||
| 1742 | return controller.trigger_values; | ||
| 1743 | } | ||
| 1744 | |||
| 1745 | ControllerMotionValues EmulatedController::GetMotionValues() const { | ||
| 1746 | std::scoped_lock lock{mutex}; | ||
| 1747 | return controller.motion_values; | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | ColorValues EmulatedController::GetColorsValues() const { | ||
| 1751 | std::scoped_lock lock{mutex}; | ||
| 1752 | return controller.color_values; | ||
| 1753 | } | ||
| 1754 | |||
| 1755 | BatteryValues EmulatedController::GetBatteryValues() const { | ||
| 1756 | std::scoped_lock lock{mutex}; | ||
| 1757 | return controller.battery_values; | ||
| 1758 | } | ||
| 1759 | |||
| 1760 | CameraValues EmulatedController::GetCameraValues() const { | ||
| 1761 | std::scoped_lock lock{mutex}; | ||
| 1762 | return controller.camera_values; | ||
| 1763 | } | ||
| 1764 | |||
| 1765 | RingAnalogValue EmulatedController::GetRingSensorValues() const { | ||
| 1766 | return controller.ring_analog_value; | ||
| 1767 | } | ||
| 1768 | |||
| 1769 | HomeButtonState EmulatedController::GetHomeButtons() const { | ||
| 1770 | std::scoped_lock lock{mutex}; | ||
| 1771 | if (is_configuring) { | ||
| 1772 | return {}; | ||
| 1773 | } | ||
| 1774 | return controller.home_button_state; | ||
| 1775 | } | ||
| 1776 | |||
| 1777 | CaptureButtonState EmulatedController::GetCaptureButtons() const { | ||
| 1778 | std::scoped_lock lock{mutex}; | ||
| 1779 | if (is_configuring) { | ||
| 1780 | return {}; | ||
| 1781 | } | ||
| 1782 | return controller.capture_button_state; | ||
| 1783 | } | ||
| 1784 | |||
| 1785 | NpadButtonState 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 | |||
| 1793 | DebugPadButton 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 | |||
| 1801 | AnalogSticks 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 | |||
| 1811 | NpadGcTriggerState EmulatedController::GetTriggers() const { | ||
| 1812 | std::scoped_lock lock{mutex}; | ||
| 1813 | if (is_configuring) { | ||
| 1814 | return {}; | ||
| 1815 | } | ||
| 1816 | return controller.gc_trigger_state; | ||
| 1817 | } | ||
| 1818 | |||
| 1819 | MotionState EmulatedController::GetMotions() const { | ||
| 1820 | std::unique_lock lock{mutex}; | ||
| 1821 | return controller.motion_state; | ||
| 1822 | } | ||
| 1823 | |||
| 1824 | ControllerColors EmulatedController::GetColors() const { | ||
| 1825 | std::scoped_lock lock{mutex}; | ||
| 1826 | return controller.colors_state; | ||
| 1827 | } | ||
| 1828 | |||
| 1829 | BatteryLevelState EmulatedController::GetBattery() const { | ||
| 1830 | std::scoped_lock lock{mutex}; | ||
| 1831 | return controller.battery_state; | ||
| 1832 | } | ||
| 1833 | |||
| 1834 | const CameraState& EmulatedController::GetCamera() const { | ||
| 1835 | std::scoped_lock lock{mutex}; | ||
| 1836 | return controller.camera_state; | ||
| 1837 | } | ||
| 1838 | |||
| 1839 | RingSensorForce EmulatedController::GetRingSensorForce() const { | ||
| 1840 | return controller.ring_analog_state; | ||
| 1841 | } | ||
| 1842 | |||
| 1843 | const NfcState& EmulatedController::GetNfc() const { | ||
| 1844 | std::scoped_lock lock{mutex}; | ||
| 1845 | return controller.nfc_state; | ||
| 1846 | } | ||
| 1847 | |||
| 1848 | NpadColor 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 | |||
| 1857 | void 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 | |||
| 1870 | int 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 | |||
| 1876 | void 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 | |||
| 1886 | void 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 | |||
| 1903 | NpadButton 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 | |||
| 22 | namespace Core::HID { | ||
| 23 | const std::size_t max_emulated_controllers = 2; | ||
| 24 | const std::size_t output_devices_size = 4; | ||
| 25 | struct ControllerMotionInfo { | ||
| 26 | Common::Input::MotionStatus raw_status{}; | ||
| 27 | MotionInput emulated{}; | ||
| 28 | }; | ||
| 29 | |||
| 30 | using ButtonDevices = | ||
| 31 | std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeButton::NumButtons>; | ||
| 32 | using StickDevices = | ||
| 33 | std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeAnalog::NumAnalogs>; | ||
| 34 | using ControllerMotionDevices = | ||
| 35 | std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>; | ||
| 36 | using TriggerDevices = | ||
| 37 | std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>; | ||
| 38 | using ColorDevices = | ||
| 39 | std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; | ||
| 40 | using BatteryDevices = | ||
| 41 | std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; | ||
| 42 | using CameraDevices = | ||
| 43 | std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; | ||
| 44 | using RingAnalogDevices = | ||
| 45 | std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; | ||
| 46 | using NfcDevices = | ||
| 47 | std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; | ||
| 48 | using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>; | ||
| 49 | |||
| 50 | using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; | ||
| 51 | using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; | ||
| 52 | using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>; | ||
| 53 | using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; | ||
| 54 | using ColorParams = std::array<Common::ParamPackage, max_emulated_controllers>; | ||
| 55 | using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; | ||
| 56 | using CameraParams = std::array<Common::ParamPackage, max_emulated_controllers>; | ||
| 57 | using RingAnalogParams = std::array<Common::ParamPackage, max_emulated_controllers>; | ||
| 58 | using NfcParams = std::array<Common::ParamPackage, max_emulated_controllers>; | ||
| 59 | using OutputParams = std::array<Common::ParamPackage, output_devices_size>; | ||
| 60 | |||
| 61 | using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; | ||
| 62 | using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; | ||
| 63 | using TriggerValues = | ||
| 64 | std::array<Common::Input::TriggerStatus, Settings::NativeTrigger::NumTriggers>; | ||
| 65 | using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>; | ||
| 66 | using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; | ||
| 67 | using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; | ||
| 68 | using CameraValues = Common::Input::CameraStatus; | ||
| 69 | using RingAnalogValue = Common::Input::AnalogStatus; | ||
| 70 | using NfcValues = Common::Input::NfcStatus; | ||
| 71 | using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; | ||
| 72 | |||
| 73 | struct AnalogSticks { | ||
| 74 | AnalogStickState left{}; | ||
| 75 | AnalogStickState right{}; | ||
| 76 | }; | ||
| 77 | |||
| 78 | struct ControllerColors { | ||
| 79 | NpadControllerColor fullkey{}; | ||
| 80 | NpadControllerColor left{}; | ||
| 81 | NpadControllerColor right{}; | ||
| 82 | }; | ||
| 83 | |||
| 84 | struct BatteryLevelState { | ||
| 85 | NpadPowerInfo dual{}; | ||
| 86 | NpadPowerInfo left{}; | ||
| 87 | NpadPowerInfo right{}; | ||
| 88 | }; | ||
| 89 | |||
| 90 | struct CameraState { | ||
| 91 | Core::IrSensor::ImageTransferProcessorFormat format{}; | ||
| 92 | std::vector<u8> data{}; | ||
| 93 | std::size_t sample{}; | ||
| 94 | }; | ||
| 95 | |||
| 96 | struct RingSensorForce { | ||
| 97 | f32 force; | ||
| 98 | }; | ||
| 99 | |||
| 100 | using NfcState = Common::Input::NfcStatus; | ||
| 101 | |||
| 102 | struct 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 | |||
| 111 | enum EmulatedDeviceIndex : u8 { | ||
| 112 | LeftIndex, | ||
| 113 | RightIndex, | ||
| 114 | DualIndex, | ||
| 115 | AllDevices, | ||
| 116 | }; | ||
| 117 | |||
| 118 | using MotionState = std::array<ControllerMotion, 2>; | ||
| 119 | |||
| 120 | struct 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 | |||
| 150 | enum 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 | |||
| 167 | struct ControllerUpdateCallback { | ||
| 168 | std::function<void(ControllerTriggerType)> on_change; | ||
| 169 | bool is_npad_service; | ||
| 170 | }; | ||
| 171 | |||
| 172 | class EmulatedController { | ||
| 173 | public: | ||
| 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 | |||
| 454 | private: | ||
| 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 | |||
| 10 | namespace Core::HID { | ||
| 11 | |||
| 12 | EmulatedDevices::EmulatedDevices() = default; | ||
| 13 | |||
| 14 | EmulatedDevices::~EmulatedDevices() = default; | ||
| 15 | |||
| 16 | void EmulatedDevices::ReloadFromSettings() { | ||
| 17 | ReloadInput(); | ||
| 18 | } | ||
| 19 | |||
| 20 | void 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 | |||
| 130 | void 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 | |||
| 146 | void EmulatedDevices::EnableConfiguration() { | ||
| 147 | is_configuring = true; | ||
| 148 | SaveCurrentConfig(); | ||
| 149 | } | ||
| 150 | |||
| 151 | void EmulatedDevices::DisableConfiguration() { | ||
| 152 | is_configuring = false; | ||
| 153 | } | ||
| 154 | |||
| 155 | bool EmulatedDevices::IsConfiguring() const { | ||
| 156 | return is_configuring; | ||
| 157 | } | ||
| 158 | |||
| 159 | void EmulatedDevices::SaveCurrentConfig() { | ||
| 160 | if (!is_configuring) { | ||
| 161 | return; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | void EmulatedDevices::RestoreConfig() { | ||
| 166 | if (!is_configuring) { | ||
| 167 | return; | ||
| 168 | } | ||
| 169 | ReloadFromSettings(); | ||
| 170 | } | ||
| 171 | |||
| 172 | void 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 | |||
| 221 | void 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 | |||
| 232 | void 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 | |||
| 304 | void 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 | |||
| 368 | void 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 | |||
| 398 | void 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 | |||
| 418 | KeyboardValues EmulatedDevices::GetKeyboardValues() const { | ||
| 419 | std::scoped_lock lock{mutex}; | ||
| 420 | return device_status.keyboard_values; | ||
| 421 | } | ||
| 422 | |||
| 423 | KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const { | ||
| 424 | std::scoped_lock lock{mutex}; | ||
| 425 | return device_status.keyboard_moddifier_values; | ||
| 426 | } | ||
| 427 | |||
| 428 | MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const { | ||
| 429 | std::scoped_lock lock{mutex}; | ||
| 430 | return device_status.mouse_button_values; | ||
| 431 | } | ||
| 432 | |||
| 433 | KeyboardKey EmulatedDevices::GetKeyboard() const { | ||
| 434 | std::scoped_lock lock{mutex}; | ||
| 435 | return device_status.keyboard_state; | ||
| 436 | } | ||
| 437 | |||
| 438 | KeyboardModifier EmulatedDevices::GetKeyboardModifier() const { | ||
| 439 | std::scoped_lock lock{mutex}; | ||
| 440 | return device_status.keyboard_moddifier_state; | ||
| 441 | } | ||
| 442 | |||
| 443 | MouseButton EmulatedDevices::GetMouseButtons() const { | ||
| 444 | std::scoped_lock lock{mutex}; | ||
| 445 | return device_status.mouse_button_state; | ||
| 446 | } | ||
| 447 | |||
| 448 | MousePosition EmulatedDevices::GetMousePosition() const { | ||
| 449 | std::scoped_lock lock{mutex}; | ||
| 450 | return device_status.mouse_position_state; | ||
| 451 | } | ||
| 452 | |||
| 453 | AnalogStickState EmulatedDevices::GetMouseWheel() const { | ||
| 454 | std::scoped_lock lock{mutex}; | ||
| 455 | return device_status.mouse_wheel_state; | ||
| 456 | } | ||
| 457 | |||
| 458 | void 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 | |||
| 468 | int 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 | |||
| 474 | void 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 | |||
| 19 | namespace Core::HID { | ||
| 20 | using KeyboardDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, | ||
| 21 | Settings::NativeKeyboard::NumKeyboardKeys>; | ||
| 22 | using KeyboardModifierDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, | ||
| 23 | Settings::NativeKeyboard::NumKeyboardMods>; | ||
| 24 | using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, | ||
| 25 | Settings::NativeMouseButton::NumMouseButtons>; | ||
| 26 | using MouseWheelDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, | ||
| 27 | Settings::NativeMouseWheel::NumMouseWheels>; | ||
| 28 | using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>; | ||
| 29 | |||
| 30 | using MouseButtonParams = | ||
| 31 | std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>; | ||
| 32 | |||
| 33 | using KeyboardValues = | ||
| 34 | std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>; | ||
| 35 | using KeyboardModifierValues = | ||
| 36 | std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardMods>; | ||
| 37 | using MouseButtonValues = | ||
| 38 | std::array<Common::Input::ButtonStatus, Settings::NativeMouseButton::NumMouseButtons>; | ||
| 39 | using MouseWheelValues = | ||
| 40 | std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>; | ||
| 41 | using MouseStickValue = Common::Input::TouchStatus; | ||
| 42 | |||
| 43 | struct MousePosition { | ||
| 44 | f32 x; | ||
| 45 | f32 y; | ||
| 46 | }; | ||
| 47 | |||
| 48 | struct 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 | |||
| 64 | enum class DeviceTriggerType { | ||
| 65 | Keyboard, | ||
| 66 | KeyboardModdifier, | ||
| 67 | Mouse, | ||
| 68 | RingController, | ||
| 69 | }; | ||
| 70 | |||
| 71 | struct InterfaceUpdateCallback { | ||
| 72 | std::function<void(DeviceTriggerType)> on_change; | ||
| 73 | }; | ||
| 74 | |||
| 75 | class EmulatedDevices { | ||
| 76 | public: | ||
| 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 | |||
| 151 | private: | ||
| 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 | |||
| 11 | namespace Core::HID { | ||
| 12 | |||
| 13 | HIDCore::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 | |||
| 26 | HIDCore::~HIDCore() = default; | ||
| 27 | |||
| 28 | EmulatedController* 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 | |||
| 57 | const 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 | } | ||
| 85 | EmulatedConsole* HIDCore::GetEmulatedConsole() { | ||
| 86 | return console.get(); | ||
| 87 | } | ||
| 88 | |||
| 89 | const EmulatedConsole* HIDCore::GetEmulatedConsole() const { | ||
| 90 | return console.get(); | ||
| 91 | } | ||
| 92 | |||
| 93 | EmulatedDevices* HIDCore::GetEmulatedDevices() { | ||
| 94 | return devices.get(); | ||
| 95 | } | ||
| 96 | |||
| 97 | const EmulatedDevices* HIDCore::GetEmulatedDevices() const { | ||
| 98 | return devices.get(); | ||
| 99 | } | ||
| 100 | |||
| 101 | EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) { | ||
| 102 | return GetEmulatedController(Service::HID::IndexToNpadIdType(index)); | ||
| 103 | } | ||
| 104 | |||
| 105 | const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const { | ||
| 106 | return GetEmulatedController(Service::HID::IndexToNpadIdType(index)); | ||
| 107 | } | ||
| 108 | |||
| 109 | void 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 | |||
| 123 | NpadStyleTag HIDCore::GetSupportedStyleTag() const { | ||
| 124 | return supported_style_tag; | ||
| 125 | } | ||
| 126 | |||
| 127 | s8 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 | |||
| 138 | NpadIdType 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 | |||
| 148 | NpadIdType 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 | |||
| 158 | void HIDCore::SetLastActiveController(NpadIdType npad_id) { | ||
| 159 | last_active_controller = npad_id; | ||
| 160 | } | ||
| 161 | |||
| 162 | NpadIdType HIDCore::GetLastActiveController() const { | ||
| 163 | return last_active_controller; | ||
| 164 | } | ||
| 165 | |||
| 166 | void 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 | |||
| 179 | void 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 | |||
| 192 | void 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 | |||
| 207 | void 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 | |||
| 11 | namespace Core::HID { | ||
| 12 | class EmulatedConsole; | ||
| 13 | class EmulatedController; | ||
| 14 | class EmulatedDevices; | ||
| 15 | } // namespace Core::HID | ||
| 16 | |||
| 17 | namespace Core::HID { | ||
| 18 | |||
| 19 | class HIDCore { | ||
| 20 | public: | ||
| 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 | |||
| 72 | private: | ||
| 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 | |||
| 13 | namespace Core::HID { | ||
| 14 | |||
| 15 | enum class DeviceIndex : u8 { | ||
| 16 | Left = 0, | ||
| 17 | Right = 1, | ||
| 18 | None = 2, | ||
| 19 | MaxDeviceIndex = 3, | ||
| 20 | }; | ||
| 21 | |||
| 22 | // This is nn::hid::NpadButton | ||
| 23 | enum 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 | }; | ||
| 69 | DECLARE_ENUM_FLAG_OPERATORS(NpadButton); | ||
| 70 | |||
| 71 | enum 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 | ||
| 207 | enum 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 | |||
| 222 | enum class NpadInterfaceType : u8 { | ||
| 223 | Bluetooth = 1, | ||
| 224 | Rail = 2, | ||
| 225 | Usb = 3, | ||
| 226 | Embedded = 4, | ||
| 227 | }; | ||
| 228 | |||
| 229 | // This is nn::hid::NpadStyleIndex | ||
| 230 | enum 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 | ||
| 250 | enum 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 | }; | ||
| 269 | static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size"); | ||
| 270 | DECLARE_ENUM_FLAG_OPERATORS(NpadStyleSet) | ||
| 271 | |||
| 272 | // This is nn::hid::VibrationDevicePosition | ||
| 273 | enum class VibrationDevicePosition : u32 { | ||
| 274 | None = 0, | ||
| 275 | Left = 1, | ||
| 276 | Right = 2, | ||
| 277 | }; | ||
| 278 | |||
| 279 | // This is nn::hid::VibrationDeviceType | ||
| 280 | enum class VibrationDeviceType : u32 { | ||
| 281 | Unknown = 0, | ||
| 282 | LinearResonantActuator = 1, | ||
| 283 | GcErm = 2, | ||
| 284 | N64 = 3, | ||
| 285 | }; | ||
| 286 | |||
| 287 | // This is nn::hid::VibrationGcErmCommand | ||
| 288 | enum class VibrationGcErmCommand : u64 { | ||
| 289 | Stop = 0, | ||
| 290 | Start = 1, | ||
| 291 | StopHard = 2, | ||
| 292 | }; | ||
| 293 | |||
| 294 | // This is nn::hid::GyroscopeZeroDriftMode | ||
| 295 | enum class GyroscopeZeroDriftMode : u32 { | ||
| 296 | Loose = 0, | ||
| 297 | Standard = 1, | ||
| 298 | Tight = 2, | ||
| 299 | }; | ||
| 300 | |||
| 301 | // This is nn::settings::system::TouchScreenMode | ||
| 302 | enum class TouchScreenMode : u32 { | ||
| 303 | Stylus = 0, | ||
| 304 | Standard = 1, | ||
| 305 | }; | ||
| 306 | |||
| 307 | // This is nn::hid::TouchScreenModeForNx | ||
| 308 | enum class TouchScreenModeForNx : u8 { | ||
| 309 | UseSystemSetting, | ||
| 310 | Finger, | ||
| 311 | Heat2, | ||
| 312 | }; | ||
| 313 | |||
| 314 | // This is nn::hid::system::NpadBatteryLevel | ||
| 315 | enum class NpadBatteryLevel : u32 { | ||
| 316 | Empty, | ||
| 317 | Critical, | ||
| 318 | Low, | ||
| 319 | High, | ||
| 320 | Full, | ||
| 321 | }; | ||
| 322 | |||
| 323 | // This is nn::hid::NpadStyleTag | ||
| 324 | struct 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 | }; | ||
| 344 | static_assert(sizeof(NpadStyleTag) == 4, "NpadStyleTag is an invalid size"); | ||
| 345 | |||
| 346 | // This is nn::hid::TouchAttribute | ||
| 347 | struct TouchAttribute { | ||
| 348 | union { | ||
| 349 | u32 raw{}; | ||
| 350 | BitField<0, 1, u32> start_touch; | ||
| 351 | BitField<1, 1, u32> end_touch; | ||
| 352 | }; | ||
| 353 | }; | ||
| 354 | static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size"); | ||
| 355 | |||
| 356 | // This is nn::hid::TouchState | ||
| 357 | struct 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 | }; | ||
| 366 | static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); | ||
| 367 | |||
| 368 | struct 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 | ||
| 377 | struct TouchScreenConfigurationForNx { | ||
| 378 | TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting}; | ||
| 379 | INSERT_PADDING_BYTES(0xF); | ||
| 380 | }; | ||
| 381 | static_assert(sizeof(TouchScreenConfigurationForNx) == 0x10, | ||
| 382 | "TouchScreenConfigurationForNx is an invalid size"); | ||
| 383 | |||
| 384 | struct NpadColor { | ||
| 385 | u8 r{}; | ||
| 386 | u8 g{}; | ||
| 387 | u8 b{}; | ||
| 388 | u8 a{}; | ||
| 389 | }; | ||
| 390 | static_assert(sizeof(NpadColor) == 4, "NpadColor is an invalid size"); | ||
| 391 | |||
| 392 | // This is nn::hid::NpadControllerColor | ||
| 393 | struct NpadControllerColor { | ||
| 394 | NpadColor body{}; | ||
| 395 | NpadColor button{}; | ||
| 396 | }; | ||
| 397 | static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size"); | ||
| 398 | |||
| 399 | // This is nn::hid::AnalogStickState | ||
| 400 | struct AnalogStickState { | ||
| 401 | s32 x{}; | ||
| 402 | s32 y{}; | ||
| 403 | }; | ||
| 404 | static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size"); | ||
| 405 | |||
| 406 | // This is nn::hid::server::NpadGcTriggerState | ||
| 407 | struct NpadGcTriggerState { | ||
| 408 | s64 sampling_number{}; | ||
| 409 | s32 left{}; | ||
| 410 | s32 right{}; | ||
| 411 | }; | ||
| 412 | static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); | ||
| 413 | |||
| 414 | // This is nn::hid::system::NpadPowerInfo | ||
| 415 | struct NpadPowerInfo { | ||
| 416 | bool is_powered{}; | ||
| 417 | bool is_charging{}; | ||
| 418 | INSERT_PADDING_BYTES(0x6); | ||
| 419 | NpadBatteryLevel battery_level{NpadBatteryLevel::Full}; | ||
| 420 | }; | ||
| 421 | static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size"); | ||
| 422 | |||
| 423 | struct 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 | |||
| 439 | struct HomeButtonState { | ||
| 440 | union { | ||
| 441 | u64 raw{}; | ||
| 442 | |||
| 443 | // Buttons | ||
| 444 | BitField<0, 1, u64> home; | ||
| 445 | }; | ||
| 446 | }; | ||
| 447 | static_assert(sizeof(HomeButtonState) == 0x8, "HomeButtonState has incorrect size."); | ||
| 448 | |||
| 449 | struct CaptureButtonState { | ||
| 450 | union { | ||
| 451 | u64 raw{}; | ||
| 452 | |||
| 453 | // Buttons | ||
| 454 | BitField<0, 1, u64> capture; | ||
| 455 | }; | ||
| 456 | }; | ||
| 457 | static_assert(sizeof(CaptureButtonState) == 0x8, "CaptureButtonState has incorrect size."); | ||
| 458 | |||
| 459 | struct 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 | }; | ||
| 510 | static_assert(sizeof(NpadButtonState) == 0x8, "NpadButtonState has incorrect size."); | ||
| 511 | |||
| 512 | // This is nn::hid::DebugPadButton | ||
| 513 | struct 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 | }; | ||
| 532 | static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size"); | ||
| 533 | |||
| 534 | // This is nn::hid::ConsoleSixAxisSensorHandle | ||
| 535 | struct ConsoleSixAxisSensorHandle { | ||
| 536 | u8 unknown_1{}; | ||
| 537 | u8 unknown_2{}; | ||
| 538 | INSERT_PADDING_BYTES_NOINIT(2); | ||
| 539 | }; | ||
| 540 | static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4, | ||
| 541 | "ConsoleSixAxisSensorHandle is an invalid size"); | ||
| 542 | |||
| 543 | // This is nn::hid::SixAxisSensorHandle | ||
| 544 | struct SixAxisSensorHandle { | ||
| 545 | NpadStyleIndex npad_type{NpadStyleIndex::None}; | ||
| 546 | u8 npad_id{}; | ||
| 547 | DeviceIndex device_index{DeviceIndex::None}; | ||
| 548 | INSERT_PADDING_BYTES_NOINIT(1); | ||
| 549 | }; | ||
| 550 | static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size"); | ||
| 551 | |||
| 552 | // These parameters seem related to how much gyro/accelerometer is used | ||
| 553 | struct SixAxisSensorFusionParameters { | ||
| 554 | f32 parameter1{0.03f}; // Range 0.0 to 1.0, default 0.03 | ||
| 555 | f32 parameter2{0.4f}; // Default 0.4 | ||
| 556 | }; | ||
| 557 | static_assert(sizeof(SixAxisSensorFusionParameters) == 8, | ||
| 558 | "SixAxisSensorFusionParameters is an invalid size"); | ||
| 559 | |||
| 560 | // This is nn::hid::server::SixAxisSensorProperties | ||
| 561 | struct 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 | }; | ||
| 568 | static_assert(sizeof(SixAxisSensorProperties) == 1, "SixAxisSensorProperties is an invalid size"); | ||
| 569 | |||
| 570 | // This is nn::hid::SixAxisSensorCalibrationParameter | ||
| 571 | struct SixAxisSensorCalibrationParameter { | ||
| 572 | std::array<u8, 0x744> unknown_data{}; | ||
| 573 | }; | ||
| 574 | static_assert(sizeof(SixAxisSensorCalibrationParameter) == 0x744, | ||
| 575 | "SixAxisSensorCalibrationParameter is an invalid size"); | ||
| 576 | |||
| 577 | // This is nn::hid::SixAxisSensorIcInformation | ||
| 578 | struct 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 | }; | ||
| 600 | static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8, | ||
| 601 | "SixAxisSensorIcInformation is an invalid size"); | ||
| 602 | |||
| 603 | // This is nn::hid::SixAxisSensorAttribute | ||
| 604 | struct SixAxisSensorAttribute { | ||
| 605 | union { | ||
| 606 | u32 raw{}; | ||
| 607 | BitField<0, 1, u32> is_connected; | ||
| 608 | BitField<1, 1, u32> is_interpolated; | ||
| 609 | }; | ||
| 610 | }; | ||
| 611 | static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size"); | ||
| 612 | |||
| 613 | // This is nn::hid::SixAxisSensorState | ||
| 614 | struct 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 | }; | ||
| 624 | static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); | ||
| 625 | |||
| 626 | // This is nn::hid::VibrationDeviceHandle | ||
| 627 | struct VibrationDeviceHandle { | ||
| 628 | NpadStyleIndex npad_type{NpadStyleIndex::None}; | ||
| 629 | u8 npad_id{}; | ||
| 630 | DeviceIndex device_index{DeviceIndex::None}; | ||
| 631 | INSERT_PADDING_BYTES_NOINIT(1); | ||
| 632 | }; | ||
| 633 | static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size"); | ||
| 634 | |||
| 635 | // This is nn::hid::VibrationValue | ||
| 636 | struct VibrationValue { | ||
| 637 | f32 low_amplitude{}; | ||
| 638 | f32 low_frequency{}; | ||
| 639 | f32 high_amplitude{}; | ||
| 640 | f32 high_frequency{}; | ||
| 641 | }; | ||
| 642 | static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size."); | ||
| 643 | |||
| 644 | constexpr 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 | ||
| 652 | struct VibrationDeviceInfo { | ||
| 653 | VibrationDeviceType type{}; | ||
| 654 | VibrationDevicePosition position{}; | ||
| 655 | }; | ||
| 656 | static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size."); | ||
| 657 | |||
| 658 | // This is nn::hid::KeyboardModifier | ||
| 659 | struct 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 | |||
| 675 | static_assert(sizeof(KeyboardModifier) == 0x4, "KeyboardModifier is an invalid size"); | ||
| 676 | |||
| 677 | // This is nn::hid::KeyboardAttribute | ||
| 678 | struct KeyboardAttribute { | ||
| 679 | union { | ||
| 680 | u32 raw{}; | ||
| 681 | BitField<0, 1, u32> is_connected; | ||
| 682 | }; | ||
| 683 | }; | ||
| 684 | static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid size"); | ||
| 685 | |||
| 686 | // This is nn::hid::KeyboardKey | ||
| 687 | struct KeyboardKey { | ||
| 688 | // This should be a 256 bit flag | ||
| 689 | std::array<u8, 32> key{}; | ||
| 690 | }; | ||
| 691 | static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size"); | ||
| 692 | |||
| 693 | // This is nn::hid::MouseButton | ||
| 694 | struct 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 | }; | ||
| 704 | static_assert(sizeof(MouseButton) == 0x4, "MouseButton is an invalid size"); | ||
| 705 | |||
| 706 | // This is nn::hid::MouseAttribute | ||
| 707 | struct MouseAttribute { | ||
| 708 | union { | ||
| 709 | u32 raw{}; | ||
| 710 | BitField<0, 1, u32> transferable; | ||
| 711 | BitField<1, 1, u32> is_connected; | ||
| 712 | }; | ||
| 713 | }; | ||
| 714 | static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size"); | ||
| 715 | |||
| 716 | // This is nn::hid::detail::MouseState | ||
| 717 | struct 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 | }; | ||
| 729 | static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size"); | ||
| 730 | |||
| 731 | struct UniquePadId { | ||
| 732 | u64 id; | ||
| 733 | }; | ||
| 734 | static_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 | |||
| 10 | namespace Core::HID { | ||
| 11 | |||
| 12 | Common::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 | |||
| 51 | Common::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 | |||
| 80 | Common::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 | |||
| 151 | Common::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 | |||
| 178 | Common::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 | |||
| 214 | Common::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 | |||
| 260 | Common::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 | |||
| 281 | Common::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 | |||
| 298 | Common::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 | |||
| 311 | Common::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 | |||
| 323 | void 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 | |||
| 363 | void 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 | |||
| 6 | namespace Common::Input { | ||
| 7 | struct CallbackStatus; | ||
| 8 | enum class BatteryLevel : u32; | ||
| 9 | using BatteryStatus = BatteryLevel; | ||
| 10 | struct AnalogStatus; | ||
| 11 | struct ButtonStatus; | ||
| 12 | struct MotionStatus; | ||
| 13 | struct StickStatus; | ||
| 14 | struct TouchStatus; | ||
| 15 | struct TriggerStatus; | ||
| 16 | }; // namespace Common::Input | ||
| 17 | |||
| 18 | namespace 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 | */ | ||
| 26 | Common::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 | */ | ||
| 34 | Common::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 | */ | ||
| 42 | Common::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 | */ | ||
| 51 | Common::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 | */ | ||
| 59 | Common::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 | */ | ||
| 68 | Common::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 | */ | ||
| 77 | Common::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 | */ | ||
| 85 | Common::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 | */ | ||
| 93 | Common::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 | */ | ||
| 101 | Common::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 | */ | ||
| 108 | void 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 | */ | ||
| 116 | void 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 | |||
| 12 | InputInterpreter::InputInterpreter(Core::System& system) | ||
| 13 | : npad{system.ServiceManager() | ||
| 14 | .GetService<Service::HID::IHidServer>("hid") | ||
| 15 | ->GetResourceManager() | ||
| 16 | ->GetNpad()} { | ||
| 17 | ResetButtonStates(); | ||
| 18 | } | ||
| 19 | |||
| 20 | InputInterpreter::~InputInterpreter() = default; | ||
| 21 | |||
| 22 | void 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 | |||
| 34 | void 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 | |||
| 45 | bool InputInterpreter::IsButtonPressed(Core::HID::NpadButton button) const { | ||
| 46 | return True(button_states[current_index] & button); | ||
| 47 | } | ||
| 48 | |||
| 49 | bool 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 | |||
| 56 | bool 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 | |||
| 10 | namespace Core { | ||
| 11 | class System; | ||
| 12 | } | ||
| 13 | |||
| 14 | namespace Core::HID { | ||
| 15 | enum class NpadButton : u64; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace Service::HID { | ||
| 19 | class 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 | */ | ||
| 28 | class InputInterpreter { | ||
| 29 | public: | ||
| 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 | |||
| 103 | private: | ||
| 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 | |||
| 10 | namespace Core::IrSensor { | ||
| 11 | |||
| 12 | // This is nn::irsensor::CameraAmbientNoiseLevel | ||
| 13 | enum 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 | ||
| 21 | enum class CameraLightTarget : u32 { | ||
| 22 | AllLeds, | ||
| 23 | BrightLeds, | ||
| 24 | DimLeds, | ||
| 25 | None, | ||
| 26 | }; | ||
| 27 | |||
| 28 | // This is nn::irsensor::PackedCameraLightTarget | ||
| 29 | enum class PackedCameraLightTarget : u8 { | ||
| 30 | AllLeds, | ||
| 31 | BrightLeds, | ||
| 32 | DimLeds, | ||
| 33 | None, | ||
| 34 | }; | ||
| 35 | |||
| 36 | // This is nn::irsensor::AdaptiveClusteringMode | ||
| 37 | enum class AdaptiveClusteringMode : u32 { | ||
| 38 | StaticFov, | ||
| 39 | DynamicFov, | ||
| 40 | }; | ||
| 41 | |||
| 42 | // This is nn::irsensor::AdaptiveClusteringTargetDistance | ||
| 43 | enum class AdaptiveClusteringTargetDistance : u32 { | ||
| 44 | Near, | ||
| 45 | Middle, | ||
| 46 | Far, | ||
| 47 | }; | ||
| 48 | |||
| 49 | // This is nn::irsensor::ImageTransferProcessorFormat | ||
| 50 | enum class ImageTransferProcessorFormat : u32 { | ||
| 51 | Size320x240, | ||
| 52 | Size160x120, | ||
| 53 | Size80x60, | ||
| 54 | Size40x30, | ||
| 55 | Size20x15, | ||
| 56 | }; | ||
| 57 | |||
| 58 | // This is nn::irsensor::PackedImageTransferProcessorFormat | ||
| 59 | enum class PackedImageTransferProcessorFormat : u8 { | ||
| 60 | Size320x240, | ||
| 61 | Size160x120, | ||
| 62 | Size80x60, | ||
| 63 | Size40x30, | ||
| 64 | Size20x15, | ||
| 65 | }; | ||
| 66 | |||
| 67 | // This is nn::irsensor::IrCameraStatus | ||
| 68 | enum class IrCameraStatus : u32 { | ||
| 69 | Available, | ||
| 70 | Unsupported, | ||
| 71 | Unconnected, | ||
| 72 | }; | ||
| 73 | |||
| 74 | // This is nn::irsensor::IrCameraInternalStatus | ||
| 75 | enum 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 | ||
| 88 | enum 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 | ||
| 99 | enum ImageProcessorStatus : u32 { | ||
| 100 | Stopped, | ||
| 101 | Running, | ||
| 102 | }; | ||
| 103 | |||
| 104 | // This is nn::irsensor::HandAnalysisMode | ||
| 105 | enum class HandAnalysisMode : u32 { | ||
| 106 | None, | ||
| 107 | Silhouette, | ||
| 108 | Image, | ||
| 109 | SilhoueteAndImage, | ||
| 110 | SilhuetteOnly, | ||
| 111 | }; | ||
| 112 | |||
| 113 | // This is nn::irsensor::IrSensorFunctionLevel | ||
| 114 | enum class IrSensorFunctionLevel : u8 { | ||
| 115 | unknown0, | ||
| 116 | unknown1, | ||
| 117 | unknown2, | ||
| 118 | unknown3, | ||
| 119 | unknown4, | ||
| 120 | }; | ||
| 121 | |||
| 122 | // This is nn::irsensor::MomentProcessorPreprocess | ||
| 123 | enum class MomentProcessorPreprocess : u32 { | ||
| 124 | Unknown0, | ||
| 125 | Unknown1, | ||
| 126 | }; | ||
| 127 | |||
| 128 | // This is nn::irsensor::PackedMomentProcessorPreprocess | ||
| 129 | enum class PackedMomentProcessorPreprocess : u8 { | ||
| 130 | Unknown0, | ||
| 131 | Unknown1, | ||
| 132 | }; | ||
| 133 | |||
| 134 | // This is nn::irsensor::PointingStatus | ||
| 135 | enum class PointingStatus : u32 { | ||
| 136 | Unknown0, | ||
| 137 | Unknown1, | ||
| 138 | }; | ||
| 139 | |||
| 140 | struct IrsRect { | ||
| 141 | s16 x; | ||
| 142 | s16 y; | ||
| 143 | s16 width; | ||
| 144 | s16 height; | ||
| 145 | }; | ||
| 146 | |||
| 147 | struct IrsCentroid { | ||
| 148 | f32 x; | ||
| 149 | f32 y; | ||
| 150 | }; | ||
| 151 | |||
| 152 | struct CameraConfig { | ||
| 153 | u64 exposure_time; | ||
| 154 | CameraLightTarget light_target; | ||
| 155 | u32 gain; | ||
| 156 | bool is_negative_used; | ||
| 157 | INSERT_PADDING_BYTES(7); | ||
| 158 | }; | ||
| 159 | static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size"); | ||
| 160 | |||
| 161 | struct PackedCameraConfig { | ||
| 162 | u64 exposure_time; | ||
| 163 | PackedCameraLightTarget light_target; | ||
| 164 | u8 gain; | ||
| 165 | bool is_negative_used; | ||
| 166 | INSERT_PADDING_BYTES(5); | ||
| 167 | }; | ||
| 168 | static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size"); | ||
| 169 | |||
| 170 | // This is nn::irsensor::IrCameraHandle | ||
| 171 | struct IrCameraHandle { | ||
| 172 | u8 npad_id{}; | ||
| 173 | Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None}; | ||
| 174 | INSERT_PADDING_BYTES(2); | ||
| 175 | }; | ||
| 176 | static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size"); | ||
| 177 | |||
| 178 | // This is nn::irsensor::PackedMcuVersion | ||
| 179 | struct PackedMcuVersion { | ||
| 180 | u16 major; | ||
| 181 | u16 minor; | ||
| 182 | }; | ||
| 183 | static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size"); | ||
| 184 | |||
| 185 | // This is nn::irsensor::PackedMomentProcessorConfig | ||
| 186 | struct 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 | }; | ||
| 194 | static_assert(sizeof(PackedMomentProcessorConfig) == 0x20, | ||
| 195 | "PackedMomentProcessorConfig is an invalid size"); | ||
| 196 | |||
| 197 | // This is nn::irsensor::PackedClusteringProcessorConfig | ||
| 198 | struct 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 | }; | ||
| 208 | static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28, | ||
| 209 | "PackedClusteringProcessorConfig is an invalid size"); | ||
| 210 | |||
| 211 | // This is nn::irsensor::PackedImageTransferProcessorConfig | ||
| 212 | struct PackedImageTransferProcessorConfig { | ||
| 213 | PackedCameraConfig camera_config; | ||
| 214 | PackedMcuVersion required_mcu_version; | ||
| 215 | PackedImageTransferProcessorFormat format; | ||
| 216 | INSERT_PADDING_BYTES(3); | ||
| 217 | }; | ||
| 218 | static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18, | ||
| 219 | "PackedImageTransferProcessorConfig is an invalid size"); | ||
| 220 | |||
| 221 | // This is nn::irsensor::PackedTeraPluginProcessorConfig | ||
| 222 | struct PackedTeraPluginProcessorConfig { | ||
| 223 | PackedMcuVersion required_mcu_version; | ||
| 224 | u8 mode; | ||
| 225 | u8 unknown_1; | ||
| 226 | u8 unknown_2; | ||
| 227 | u8 unknown_3; | ||
| 228 | }; | ||
| 229 | static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8, | ||
| 230 | "PackedTeraPluginProcessorConfig is an invalid size"); | ||
| 231 | |||
| 232 | // This is nn::irsensor::PackedPointingProcessorConfig | ||
| 233 | struct PackedPointingProcessorConfig { | ||
| 234 | IrsRect window_of_interest; | ||
| 235 | PackedMcuVersion required_mcu_version; | ||
| 236 | }; | ||
| 237 | static_assert(sizeof(PackedPointingProcessorConfig) == 0xC, | ||
| 238 | "PackedPointingProcessorConfig is an invalid size"); | ||
| 239 | |||
| 240 | // This is nn::irsensor::PackedFunctionLevel | ||
| 241 | struct PackedFunctionLevel { | ||
| 242 | IrSensorFunctionLevel function_level; | ||
| 243 | INSERT_PADDING_BYTES(3); | ||
| 244 | }; | ||
| 245 | static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size"); | ||
| 246 | |||
| 247 | // This is nn::irsensor::PackedImageTransferProcessorExConfig | ||
| 248 | struct 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 | }; | ||
| 258 | static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20, | ||
| 259 | "PackedImageTransferProcessorExConfig is an invalid size"); | ||
| 260 | |||
| 261 | // This is nn::irsensor::PackedIrLedProcessorConfig | ||
| 262 | struct PackedIrLedProcessorConfig { | ||
| 263 | PackedMcuVersion required_mcu_version; | ||
| 264 | u8 light_target; | ||
| 265 | INSERT_PADDING_BYTES(3); | ||
| 266 | }; | ||
| 267 | static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8, | ||
| 268 | "PackedIrLedProcessorConfig is an invalid size"); | ||
| 269 | |||
| 270 | // This is nn::irsensor::HandAnalysisConfig | ||
| 271 | struct HandAnalysisConfig { | ||
| 272 | HandAnalysisMode mode; | ||
| 273 | }; | ||
| 274 | static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size"); | ||
| 275 | |||
| 276 | // This is nn::irsensor::detail::ProcessorState contents are different for each processor | ||
| 277 | struct ProcessorState { | ||
| 278 | std::array<u8, 0xE20> processor_raw_data{}; | ||
| 279 | }; | ||
| 280 | static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size"); | ||
| 281 | |||
| 282 | // This is nn::irsensor::detail::DeviceFormat | ||
| 283 | struct 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 | }; | ||
| 290 | static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size"); | ||
| 291 | |||
| 292 | // This is nn::irsensor::ImageTransferProcessorState | ||
| 293 | struct ImageTransferProcessorState { | ||
| 294 | u64 sampling_number; | ||
| 295 | Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; | ||
| 296 | INSERT_PADDING_BYTES(4); | ||
| 297 | }; | ||
| 298 | static_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 | |||
| 9 | namespace Core::HID { | ||
| 10 | |||
| 11 | MotionInput::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 | |||
| 19 | void 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 | |||
| 25 | void 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 | |||
| 33 | void 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 | |||
| 58 | void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) { | ||
| 59 | quat = quaternion; | ||
| 60 | } | ||
| 61 | |||
| 62 | void 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 | |||
| 76 | void MotionInput::SetGyroBias(const Common::Vec3f& bias) { | ||
| 77 | gyro_bias = bias; | ||
| 78 | } | ||
| 79 | |||
| 80 | void MotionInput::SetGyroThreshold(f32 threshold) { | ||
| 81 | gyro_threshold = threshold; | ||
| 82 | } | ||
| 83 | |||
| 84 | void MotionInput::SetUserGyroThreshold(f32 threshold) { | ||
| 85 | user_gyro_threshold = threshold / ThresholdStandard; | ||
| 86 | } | ||
| 87 | |||
| 88 | void MotionInput::EnableReset(bool reset) { | ||
| 89 | reset_enabled = reset; | ||
| 90 | } | ||
| 91 | |||
| 92 | void MotionInput::ResetRotations() { | ||
| 93 | rotations = {}; | ||
| 94 | } | ||
| 95 | |||
| 96 | void MotionInput::ResetQuaternion() { | ||
| 97 | quat = {{0.0f, 0.0f, -1.0f}, 0.0f}; | ||
| 98 | } | ||
| 99 | |||
| 100 | bool MotionInput::IsMoving(f32 sensitivity) const { | ||
| 101 | return gyro.Length() >= sensitivity || accel.Length() <= 0.9f || accel.Length() >= 1.1f; | ||
| 102 | } | ||
| 103 | |||
| 104 | bool MotionInput::IsCalibrated(f32 sensitivity) const { | ||
| 105 | return real_error.Length() < sensitivity; | ||
| 106 | } | ||
| 107 | |||
| 108 | void 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 | |||
| 116 | void MotionInput::Calibrate() { | ||
| 117 | calibration_mode = true; | ||
| 118 | calibration_counter = 0; | ||
| 119 | } | ||
| 120 | |||
| 121 | void 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 | ||
| 131 | void 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 | |||
| 228 | std::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 | |||
| 240 | Common::Vec3f MotionInput::GetAcceleration() const { | ||
| 241 | return accel; | ||
| 242 | } | ||
| 243 | |||
| 244 | Common::Vec3f MotionInput::GetGyroscope() const { | ||
| 245 | return gyro; | ||
| 246 | } | ||
| 247 | |||
| 248 | Common::Vec3f MotionInput::GetGyroBias() const { | ||
| 249 | return gyro_bias; | ||
| 250 | } | ||
| 251 | |||
| 252 | Common::Quaternion<f32> MotionInput::GetQuaternion() const { | ||
| 253 | return quat; | ||
| 254 | } | ||
| 255 | |||
| 256 | Common::Vec3f MotionInput::GetRotations() const { | ||
| 257 | return rotations; | ||
| 258 | } | ||
| 259 | |||
| 260 | Common::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 | |||
| 280 | void 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 | |||
| 300 | void 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 | |||
| 10 | namespace Core::HID { | ||
| 11 | |||
| 12 | class MotionInput { | ||
| 13 | public: | ||
| 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 | |||
| 67 | private: | ||
| 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 | ||