diff options
| author | 2021-11-27 11:52:08 +0100 | |
|---|---|---|
| committer | 2021-11-27 11:52:08 +0100 | |
| commit | 564f10527745f870621c08bbb5d16badee0ed861 (patch) | |
| tree | e8ac8dee60086facf1837393882865f5df18c95e /src/core/hid | |
| parent | Merge pull request #7431 from liushuyu/fix-linux-decoding (diff) | |
| parent | config: Remove vibration configuration (diff) | |
| download | yuzu-564f10527745f870621c08bbb5d16badee0ed861.tar.gz yuzu-564f10527745f870621c08bbb5d16badee0ed861.tar.xz yuzu-564f10527745f870621c08bbb5d16badee0ed861.zip | |
Merge pull request #7255 from german77/kraken
Project Kraken: Input rewrite
Diffstat (limited to 'src/core/hid')
| -rw-r--r-- | src/core/hid/emulated_console.cpp | 229 | ||||
| -rw-r--r-- | src/core/hid/emulated_console.h | 188 | ||||
| -rw-r--r-- | src/core/hid/emulated_controller.cpp | 1061 | ||||
| -rw-r--r-- | src/core/hid/emulated_controller.h | 392 | ||||
| -rw-r--r-- | src/core/hid/emulated_devices.cpp | 451 | ||||
| -rw-r--r-- | src/core/hid/emulated_devices.h | 209 | ||||
| -rw-r--r-- | src/core/hid/hid_core.cpp | 168 | ||||
| -rw-r--r-- | src/core/hid/hid_core.h | 73 | ||||
| -rw-r--r-- | src/core/hid/hid_types.h | 631 | ||||
| -rw-r--r-- | src/core/hid/input_converter.cpp | 383 | ||||
| -rw-r--r-- | src/core/hid/input_converter.h | 95 | ||||
| -rw-r--r-- | src/core/hid/input_interpreter.cpp | 61 | ||||
| -rw-r--r-- | src/core/hid/input_interpreter.h | 112 | ||||
| -rw-r--r-- | src/core/hid/motion_input.cpp | 280 | ||||
| -rw-r--r-- | src/core/hid/motion_input.h | 87 |
15 files changed, 4420 insertions, 0 deletions
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp new file mode 100644 index 000000000..80db8e9c6 --- /dev/null +++ b/src/core/hid/emulated_console.cpp | |||
| @@ -0,0 +1,229 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included | ||
| 4 | |||
| 5 | #include "common/settings.h" | ||
| 6 | #include "core/hid/emulated_console.h" | ||
| 7 | #include "core/hid/input_converter.h" | ||
| 8 | |||
| 9 | namespace Core::HID { | ||
| 10 | EmulatedConsole::EmulatedConsole() = default; | ||
| 11 | |||
| 12 | EmulatedConsole::~EmulatedConsole() = default; | ||
| 13 | |||
| 14 | void EmulatedConsole::ReloadFromSettings() { | ||
| 15 | // Using first motion device from player 1. No need to assign any unique config at the moment | ||
| 16 | const auto& player = Settings::values.players.GetValue()[0]; | ||
| 17 | motion_params = Common::ParamPackage(player.motions[0]); | ||
| 18 | |||
| 19 | ReloadInput(); | ||
| 20 | } | ||
| 21 | |||
| 22 | void EmulatedConsole::SetTouchParams() { | ||
| 23 | // TODO(german77): Support any number of fingers | ||
| 24 | std::size_t index = 0; | ||
| 25 | |||
| 26 | // Hardcode mouse, touchscreen and cemuhook parameters | ||
| 27 | if (!Settings::values.mouse_enabled) { | ||
| 28 | // We can't use mouse as touch if native mouse is enabled | ||
| 29 | touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"}; | ||
| 30 | } | ||
| 31 | touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"}; | ||
| 32 | touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"}; | ||
| 33 | touch_params[index++] = | ||
| 34 | Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"}; | ||
| 35 | touch_params[index++] = | ||
| 36 | Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"}; | ||
| 37 | |||
| 38 | const auto button_index = | ||
| 39 | static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue()); | ||
| 40 | const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons; | ||
| 41 | |||
| 42 | // Map the rest of the fingers from touch from button configuration | ||
| 43 | for (const auto& config_entry : touch_buttons) { | ||
| 44 | if (index >= touch_params.size()) { | ||
| 45 | continue; | ||
| 46 | } | ||
| 47 | Common::ParamPackage params{config_entry}; | ||
| 48 | Common::ParamPackage touch_button_params; | ||
| 49 | const int x = params.Get("x", 0); | ||
| 50 | const int y = params.Get("y", 0); | ||
| 51 | params.Erase("x"); | ||
| 52 | params.Erase("y"); | ||
| 53 | touch_button_params.Set("engine", "touch_from_button"); | ||
| 54 | touch_button_params.Set("button", params.Serialize()); | ||
| 55 | touch_button_params.Set("x", x); | ||
| 56 | touch_button_params.Set("y", y); | ||
| 57 | touch_button_params.Set("touch_id", static_cast<int>(index)); | ||
| 58 | touch_params[index] = touch_button_params; | ||
| 59 | index++; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | void EmulatedConsole::ReloadInput() { | ||
| 64 | // If you load any device here add the equivalent to the UnloadInput() function | ||
| 65 | SetTouchParams(); | ||
| 66 | |||
| 67 | motion_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(motion_params); | ||
| 68 | if (motion_devices) { | ||
| 69 | Common::Input::InputCallback motion_callback{ | ||
| 70 | [this](Common::Input::CallbackStatus callback) { SetMotion(callback); }}; | ||
| 71 | motion_devices->SetCallback(motion_callback); | ||
| 72 | } | ||
| 73 | |||
| 74 | // Unique index for identifying touch device source | ||
| 75 | std::size_t index = 0; | ||
| 76 | for (auto& touch_device : touch_devices) { | ||
| 77 | touch_device = Common::Input::CreateDevice<Common::Input::InputDevice>(touch_params[index]); | ||
| 78 | if (!touch_device) { | ||
| 79 | continue; | ||
| 80 | } | ||
| 81 | Common::Input::InputCallback touch_callback{ | ||
| 82 | [this, index](Common::Input::CallbackStatus callback) { SetTouch(callback, index); }}; | ||
| 83 | touch_device->SetCallback(touch_callback); | ||
| 84 | index++; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | void EmulatedConsole::UnloadInput() { | ||
| 89 | motion_devices.reset(); | ||
| 90 | for (auto& touch : touch_devices) { | ||
| 91 | touch.reset(); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | void EmulatedConsole::EnableConfiguration() { | ||
| 96 | is_configuring = true; | ||
| 97 | SaveCurrentConfig(); | ||
| 98 | } | ||
| 99 | |||
| 100 | void EmulatedConsole::DisableConfiguration() { | ||
| 101 | is_configuring = false; | ||
| 102 | } | ||
| 103 | |||
| 104 | bool EmulatedConsole::IsConfiguring() const { | ||
| 105 | return is_configuring; | ||
| 106 | } | ||
| 107 | |||
| 108 | void EmulatedConsole::SaveCurrentConfig() { | ||
| 109 | if (!is_configuring) { | ||
| 110 | return; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | void EmulatedConsole::RestoreConfig() { | ||
| 115 | if (!is_configuring) { | ||
| 116 | return; | ||
| 117 | } | ||
| 118 | ReloadFromSettings(); | ||
| 119 | } | ||
| 120 | |||
| 121 | Common::ParamPackage EmulatedConsole::GetMotionParam() const { | ||
| 122 | return motion_params; | ||
| 123 | } | ||
| 124 | |||
| 125 | void EmulatedConsole::SetMotionParam(Common::ParamPackage param) { | ||
| 126 | motion_params = param; | ||
| 127 | ReloadInput(); | ||
| 128 | } | ||
| 129 | |||
| 130 | void EmulatedConsole::SetMotion(Common::Input::CallbackStatus callback) { | ||
| 131 | std::lock_guard lock{mutex}; | ||
| 132 | auto& raw_status = console.motion_values.raw_status; | ||
| 133 | auto& emulated = console.motion_values.emulated; | ||
| 134 | |||
| 135 | raw_status = TransformToMotion(callback); | ||
| 136 | emulated.SetAcceleration(Common::Vec3f{ | ||
| 137 | raw_status.accel.x.value, | ||
| 138 | raw_status.accel.y.value, | ||
| 139 | raw_status.accel.z.value, | ||
| 140 | }); | ||
| 141 | emulated.SetGyroscope(Common::Vec3f{ | ||
| 142 | raw_status.gyro.x.value, | ||
| 143 | raw_status.gyro.y.value, | ||
| 144 | raw_status.gyro.z.value, | ||
| 145 | }); | ||
| 146 | emulated.UpdateRotation(raw_status.delta_timestamp); | ||
| 147 | emulated.UpdateOrientation(raw_status.delta_timestamp); | ||
| 148 | |||
| 149 | if (is_configuring) { | ||
| 150 | TriggerOnChange(ConsoleTriggerType::Motion); | ||
| 151 | return; | ||
| 152 | } | ||
| 153 | |||
| 154 | auto& motion = console.motion_state; | ||
| 155 | motion.accel = emulated.GetAcceleration(); | ||
| 156 | motion.gyro = emulated.GetGyroscope(); | ||
| 157 | motion.rotation = emulated.GetGyroscope(); | ||
| 158 | motion.orientation = emulated.GetOrientation(); | ||
| 159 | motion.quaternion = emulated.GetQuaternion(); | ||
| 160 | motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); | ||
| 161 | |||
| 162 | TriggerOnChange(ConsoleTriggerType::Motion); | ||
| 163 | } | ||
| 164 | |||
| 165 | void EmulatedConsole::SetTouch(Common::Input::CallbackStatus callback, | ||
| 166 | [[maybe_unused]] std::size_t index) { | ||
| 167 | if (index >= console.touch_values.size()) { | ||
| 168 | return; | ||
| 169 | } | ||
| 170 | std::lock_guard lock{mutex}; | ||
| 171 | |||
| 172 | console.touch_values[index] = TransformToTouch(callback); | ||
| 173 | |||
| 174 | if (is_configuring) { | ||
| 175 | TriggerOnChange(ConsoleTriggerType::Touch); | ||
| 176 | return; | ||
| 177 | } | ||
| 178 | |||
| 179 | // TODO(german77): Remap touch id in sequential order | ||
| 180 | console.touch_state[index] = { | ||
| 181 | .position = {console.touch_values[index].x.value, console.touch_values[index].y.value}, | ||
| 182 | .id = static_cast<u32>(console.touch_values[index].id), | ||
| 183 | .pressed = console.touch_values[index].pressed.value, | ||
| 184 | }; | ||
| 185 | |||
| 186 | TriggerOnChange(ConsoleTriggerType::Touch); | ||
| 187 | } | ||
| 188 | |||
| 189 | ConsoleMotionValues EmulatedConsole::GetMotionValues() const { | ||
| 190 | return console.motion_values; | ||
| 191 | } | ||
| 192 | |||
| 193 | TouchValues EmulatedConsole::GetTouchValues() const { | ||
| 194 | return console.touch_values; | ||
| 195 | } | ||
| 196 | |||
| 197 | ConsoleMotion EmulatedConsole::GetMotion() const { | ||
| 198 | return console.motion_state; | ||
| 199 | } | ||
| 200 | |||
| 201 | TouchFingerState EmulatedConsole::GetTouch() const { | ||
| 202 | return console.touch_state; | ||
| 203 | } | ||
| 204 | |||
| 205 | void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { | ||
| 206 | for (const auto& poller_pair : callback_list) { | ||
| 207 | const ConsoleUpdateCallback& poller = poller_pair.second; | ||
| 208 | if (poller.on_change) { | ||
| 209 | poller.on_change(type); | ||
| 210 | } | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) { | ||
| 215 | std::lock_guard lock{mutex}; | ||
| 216 | callback_list.insert_or_assign(last_callback_key, update_callback); | ||
| 217 | return last_callback_key++; | ||
| 218 | } | ||
| 219 | |||
| 220 | void EmulatedConsole::DeleteCallback(int key) { | ||
| 221 | std::lock_guard lock{mutex}; | ||
| 222 | const auto& iterator = callback_list.find(key); | ||
| 223 | if (iterator == callback_list.end()) { | ||
| 224 | LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); | ||
| 225 | return; | ||
| 226 | } | ||
| 227 | callback_list.erase(iterator); | ||
| 228 | } | ||
| 229 | } // namespace Core::HID | ||
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h new file mode 100644 index 000000000..25c183eee --- /dev/null +++ b/src/core/hid/emulated_console.h | |||
| @@ -0,0 +1,188 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <functional> | ||
| 9 | #include <memory> | ||
| 10 | #include <mutex> | ||
| 11 | #include <unordered_map> | ||
| 12 | |||
| 13 | #include "common/common_types.h" | ||
| 14 | #include "common/input.h" | ||
| 15 | #include "common/param_package.h" | ||
| 16 | #include "common/point.h" | ||
| 17 | #include "common/quaternion.h" | ||
| 18 | #include "common/vector_math.h" | ||
| 19 | #include "core/hid/hid_types.h" | ||
| 20 | #include "core/hid/motion_input.h" | ||
| 21 | |||
| 22 | namespace Core::HID { | ||
| 23 | |||
| 24 | struct ConsoleMotionInfo { | ||
| 25 | Common::Input::MotionStatus raw_status{}; | ||
| 26 | MotionInput emulated{}; | ||
| 27 | }; | ||
| 28 | |||
| 29 | using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>; | ||
| 30 | using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 16>; | ||
| 31 | |||
| 32 | using ConsoleMotionParams = Common::ParamPackage; | ||
| 33 | using TouchParams = std::array<Common::ParamPackage, 16>; | ||
| 34 | |||
| 35 | using ConsoleMotionValues = ConsoleMotionInfo; | ||
| 36 | using TouchValues = std::array<Common::Input::TouchStatus, 16>; | ||
| 37 | |||
| 38 | struct TouchFinger { | ||
| 39 | u64 last_touch{}; | ||
| 40 | Common::Point<float> position{}; | ||
| 41 | u32 id{}; | ||
| 42 | TouchAttribute attribute{}; | ||
| 43 | bool pressed{}; | ||
| 44 | }; | ||
| 45 | |||
| 46 | // Contains all motion related data that is used on the services | ||
| 47 | struct ConsoleMotion { | ||
| 48 | Common::Vec3f accel{}; | ||
| 49 | Common::Vec3f gyro{}; | ||
| 50 | Common::Vec3f rotation{}; | ||
| 51 | std::array<Common::Vec3f, 3> orientation{}; | ||
| 52 | Common::Quaternion<f32> quaternion{}; | ||
| 53 | bool is_at_rest{}; | ||
| 54 | }; | ||
| 55 | |||
| 56 | using TouchFingerState = std::array<TouchFinger, 16>; | ||
| 57 | |||
| 58 | struct ConsoleStatus { | ||
| 59 | // Data from input_common | ||
| 60 | ConsoleMotionValues motion_values{}; | ||
| 61 | TouchValues touch_values{}; | ||
| 62 | |||
| 63 | // Data for HID services | ||
| 64 | ConsoleMotion motion_state{}; | ||
| 65 | TouchFingerState touch_state{}; | ||
| 66 | }; | ||
| 67 | |||
| 68 | enum class ConsoleTriggerType { | ||
| 69 | Motion, | ||
| 70 | Touch, | ||
| 71 | All, | ||
| 72 | }; | ||
| 73 | |||
| 74 | struct ConsoleUpdateCallback { | ||
| 75 | std::function<void(ConsoleTriggerType)> on_change; | ||
| 76 | }; | ||
| 77 | |||
| 78 | class EmulatedConsole { | ||
| 79 | public: | ||
| 80 | /** | ||
| 81 | * Contains all input data related to the console like motion and touch input | ||
| 82 | */ | ||
| 83 | EmulatedConsole(); | ||
| 84 | ~EmulatedConsole(); | ||
| 85 | |||
| 86 | YUZU_NON_COPYABLE(EmulatedConsole); | ||
| 87 | YUZU_NON_MOVEABLE(EmulatedConsole); | ||
| 88 | |||
| 89 | /// Removes all callbacks created from input devices | ||
| 90 | void UnloadInput(); | ||
| 91 | |||
| 92 | /// Sets the emulated console into configuring mode. Locking all HID service events from being | ||
| 93 | /// moddified | ||
| 94 | void EnableConfiguration(); | ||
| 95 | |||
| 96 | /// Returns the emulated console to the normal behaivour | ||
| 97 | void DisableConfiguration(); | ||
| 98 | |||
| 99 | /// Returns true if the emulated console is on 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 current mapped motion device | ||
| 115 | Common::ParamPackage GetMotionParam() const; | ||
| 116 | |||
| 117 | /** | ||
| 118 | * Updates the current mapped motion device | ||
| 119 | * @param ParamPackage with controller data to be mapped | ||
| 120 | */ | ||
| 121 | void SetMotionParam(Common::ParamPackage param); | ||
| 122 | |||
| 123 | /// Returns the latest status of motion input from the console with parameters | ||
| 124 | ConsoleMotionValues GetMotionValues() const; | ||
| 125 | |||
| 126 | /// Returns the latest status of touch input from the console with parameters | ||
| 127 | TouchValues GetTouchValues() const; | ||
| 128 | |||
| 129 | /// Returns the latest status of motion input from the console | ||
| 130 | ConsoleMotion GetMotion() const; | ||
| 131 | |||
| 132 | /// Returns the latest status of touch input from the console | ||
| 133 | TouchFingerState GetTouch() const; | ||
| 134 | |||
| 135 | /** | ||
| 136 | * Adds a callback to the list of events | ||
| 137 | * @param ConsoleUpdateCallback that will be triggered | ||
| 138 | * @return an unique key corresponding to the callback index in the list | ||
| 139 | */ | ||
| 140 | int SetCallback(ConsoleUpdateCallback update_callback); | ||
| 141 | |||
| 142 | /** | ||
| 143 | * Removes a callback from the list stopping any future events to this object | ||
| 144 | * @param Key corresponding to the callback index in the list | ||
| 145 | */ | ||
| 146 | void DeleteCallback(int key); | ||
| 147 | |||
| 148 | private: | ||
| 149 | /// Creates and stores the touch params | ||
| 150 | void SetTouchParams(); | ||
| 151 | |||
| 152 | /** | ||
| 153 | * Updates the motion status of the console | ||
| 154 | * @param A CallbackStatus containing gyro and accelerometer data | ||
| 155 | */ | ||
| 156 | void SetMotion(Common::Input::CallbackStatus callback); | ||
| 157 | |||
| 158 | /** | ||
| 159 | * Updates the touch status of the console | ||
| 160 | * @param callback: A CallbackStatus containing the touch position | ||
| 161 | * @param index: Finger ID to be updated | ||
| 162 | */ | ||
| 163 | void SetTouch(Common::Input::CallbackStatus callback, std::size_t index); | ||
| 164 | |||
| 165 | /** | ||
| 166 | * Triggers a callback that something has changed on the console status | ||
| 167 | * @param Input type of the event to trigger | ||
| 168 | */ | ||
| 169 | void TriggerOnChange(ConsoleTriggerType type); | ||
| 170 | |||
| 171 | bool is_configuring{false}; | ||
| 172 | f32 motion_sensitivity{0.01f}; | ||
| 173 | |||
| 174 | ConsoleMotionParams motion_params; | ||
| 175 | TouchParams touch_params; | ||
| 176 | |||
| 177 | ConsoleMotionDevices motion_devices; | ||
| 178 | TouchDevices touch_devices; | ||
| 179 | |||
| 180 | mutable std::mutex mutex; | ||
| 181 | std::unordered_map<int, ConsoleUpdateCallback> callback_list; | ||
| 182 | int last_callback_key = 0; | ||
| 183 | |||
| 184 | // Stores the current status of all console input | ||
| 185 | ConsoleStatus console; | ||
| 186 | }; | ||
| 187 | |||
| 188 | } // namespace Core::HID | ||
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp new file mode 100644 index 000000000..06ae41c3e --- /dev/null +++ b/src/core/hid/emulated_controller.cpp | |||
| @@ -0,0 +1,1061 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included | ||
| 4 | |||
| 5 | #include "core/hid/emulated_controller.h" | ||
| 6 | #include "core/hid/input_converter.h" | ||
| 7 | |||
| 8 | namespace Core::HID { | ||
| 9 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | ||
| 10 | constexpr s32 HID_TRIGGER_MAX = 0x7fff; | ||
| 11 | |||
| 12 | EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {} | ||
| 13 | |||
| 14 | EmulatedController::~EmulatedController() = default; | ||
| 15 | |||
| 16 | NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) { | ||
| 17 | switch (type) { | ||
| 18 | case Settings::ControllerType::ProController: | ||
| 19 | return NpadStyleIndex::ProController; | ||
| 20 | case Settings::ControllerType::DualJoyconDetached: | ||
| 21 | return NpadStyleIndex::JoyconDual; | ||
| 22 | case Settings::ControllerType::LeftJoycon: | ||
| 23 | return NpadStyleIndex::JoyconLeft; | ||
| 24 | case Settings::ControllerType::RightJoycon: | ||
| 25 | return NpadStyleIndex::JoyconRight; | ||
| 26 | case Settings::ControllerType::Handheld: | ||
| 27 | return NpadStyleIndex::Handheld; | ||
| 28 | case Settings::ControllerType::GameCube: | ||
| 29 | return NpadStyleIndex::GameCube; | ||
| 30 | default: | ||
| 31 | return NpadStyleIndex::ProController; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) { | ||
| 36 | switch (type) { | ||
| 37 | case NpadStyleIndex::ProController: | ||
| 38 | return Settings::ControllerType::ProController; | ||
| 39 | case NpadStyleIndex::JoyconDual: | ||
| 40 | return Settings::ControllerType::DualJoyconDetached; | ||
| 41 | case NpadStyleIndex::JoyconLeft: | ||
| 42 | return Settings::ControllerType::LeftJoycon; | ||
| 43 | case NpadStyleIndex::JoyconRight: | ||
| 44 | return Settings::ControllerType::RightJoycon; | ||
| 45 | case NpadStyleIndex::Handheld: | ||
| 46 | return Settings::ControllerType::Handheld; | ||
| 47 | case NpadStyleIndex::GameCube: | ||
| 48 | return Settings::ControllerType::GameCube; | ||
| 49 | default: | ||
| 50 | return Settings::ControllerType::ProController; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | void EmulatedController::ReloadFromSettings() { | ||
| 55 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | ||
| 56 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 57 | |||
| 58 | for (std::size_t index = 0; index < player.buttons.size(); ++index) { | ||
| 59 | button_params[index] = Common::ParamPackage(player.buttons[index]); | ||
| 60 | } | ||
| 61 | for (std::size_t index = 0; index < player.analogs.size(); ++index) { | ||
| 62 | stick_params[index] = Common::ParamPackage(player.analogs[index]); | ||
| 63 | } | ||
| 64 | for (std::size_t index = 0; index < player.motions.size(); ++index) { | ||
| 65 | motion_params[index] = Common::ParamPackage(player.motions[index]); | ||
| 66 | } | ||
| 67 | |||
| 68 | controller.colors_state.left = { | ||
| 69 | .body = player.body_color_left, | ||
| 70 | .button = player.button_color_left, | ||
| 71 | }; | ||
| 72 | |||
| 73 | controller.colors_state.right = { | ||
| 74 | .body = player.body_color_right, | ||
| 75 | .button = player.button_color_right, | ||
| 76 | }; | ||
| 77 | |||
| 78 | controller.colors_state.fullkey = controller.colors_state.left; | ||
| 79 | |||
| 80 | // Other or debug controller should always be a pro controller | ||
| 81 | if (npad_id_type != NpadIdType::Other) { | ||
| 82 | SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); | ||
| 83 | } else { | ||
| 84 | SetNpadStyleIndex(NpadStyleIndex::ProController); | ||
| 85 | } | ||
| 86 | |||
| 87 | if (player.connected) { | ||
| 88 | Connect(); | ||
| 89 | } else { | ||
| 90 | Disconnect(); | ||
| 91 | } | ||
| 92 | |||
| 93 | ReloadInput(); | ||
| 94 | } | ||
| 95 | |||
| 96 | void EmulatedController::LoadDevices() { | ||
| 97 | // TODO(german77): Use more buttons to detect the correct device | ||
| 98 | const auto left_joycon = button_params[Settings::NativeButton::DRight]; | ||
| 99 | const auto right_joycon = button_params[Settings::NativeButton::A]; | ||
| 100 | |||
| 101 | // Triggers for GC controllers | ||
| 102 | trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL]; | ||
| 103 | trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR]; | ||
| 104 | |||
| 105 | battery_params[LeftIndex] = left_joycon; | ||
| 106 | battery_params[RightIndex] = right_joycon; | ||
| 107 | battery_params[LeftIndex].Set("battery", true); | ||
| 108 | battery_params[RightIndex].Set("battery", true); | ||
| 109 | |||
| 110 | output_params[LeftIndex] = left_joycon; | ||
| 111 | output_params[RightIndex] = right_joycon; | ||
| 112 | output_params[LeftIndex].Set("output", true); | ||
| 113 | output_params[RightIndex].Set("output", true); | ||
| 114 | |||
| 115 | LoadTASParams(); | ||
| 116 | |||
| 117 | std::transform(button_params.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, | ||
| 118 | button_params.begin() + Settings::NativeButton::BUTTON_NS_END, | ||
| 119 | button_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>); | ||
| 120 | std::transform(stick_params.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, | ||
| 121 | stick_params.begin() + Settings::NativeAnalog::STICK_HID_END, | ||
| 122 | stick_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>); | ||
| 123 | std::transform(motion_params.begin() + Settings::NativeMotion::MOTION_HID_BEGIN, | ||
| 124 | motion_params.begin() + Settings::NativeMotion::MOTION_HID_END, | ||
| 125 | motion_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>); | ||
| 126 | std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(), | ||
| 127 | Common::Input::CreateDevice<Common::Input::InputDevice>); | ||
| 128 | std::transform(battery_params.begin(), battery_params.begin(), battery_devices.end(), | ||
| 129 | Common::Input::CreateDevice<Common::Input::InputDevice>); | ||
| 130 | std::transform(output_params.begin(), output_params.end(), output_devices.begin(), | ||
| 131 | Common::Input::CreateDevice<Common::Input::OutputDevice>); | ||
| 132 | |||
| 133 | // Initialize TAS devices | ||
| 134 | std::transform(tas_button_params.begin(), tas_button_params.end(), tas_button_devices.begin(), | ||
| 135 | Common::Input::CreateDevice<Common::Input::InputDevice>); | ||
| 136 | std::transform(tas_stick_params.begin(), tas_stick_params.end(), tas_stick_devices.begin(), | ||
| 137 | Common::Input::CreateDevice<Common::Input::InputDevice>); | ||
| 138 | } | ||
| 139 | |||
| 140 | void EmulatedController::LoadTASParams() { | ||
| 141 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | ||
| 142 | Common::ParamPackage common_params{}; | ||
| 143 | common_params.Set("engine", "tas"); | ||
| 144 | common_params.Set("port", static_cast<int>(player_index)); | ||
| 145 | for (auto& param : tas_button_params) { | ||
| 146 | param = common_params; | ||
| 147 | } | ||
| 148 | for (auto& param : tas_stick_params) { | ||
| 149 | param = common_params; | ||
| 150 | } | ||
| 151 | |||
| 152 | // TODO(german77): Replace this with an input profile or something better | ||
| 153 | tas_button_params[Settings::NativeButton::A].Set("button", 0); | ||
| 154 | tas_button_params[Settings::NativeButton::B].Set("button", 1); | ||
| 155 | tas_button_params[Settings::NativeButton::X].Set("button", 2); | ||
| 156 | tas_button_params[Settings::NativeButton::Y].Set("button", 3); | ||
| 157 | tas_button_params[Settings::NativeButton::LStick].Set("button", 4); | ||
| 158 | tas_button_params[Settings::NativeButton::RStick].Set("button", 5); | ||
| 159 | tas_button_params[Settings::NativeButton::L].Set("button", 6); | ||
| 160 | tas_button_params[Settings::NativeButton::R].Set("button", 7); | ||
| 161 | tas_button_params[Settings::NativeButton::ZL].Set("button", 8); | ||
| 162 | tas_button_params[Settings::NativeButton::ZR].Set("button", 9); | ||
| 163 | tas_button_params[Settings::NativeButton::Plus].Set("button", 10); | ||
| 164 | tas_button_params[Settings::NativeButton::Minus].Set("button", 11); | ||
| 165 | tas_button_params[Settings::NativeButton::DLeft].Set("button", 12); | ||
| 166 | tas_button_params[Settings::NativeButton::DUp].Set("button", 13); | ||
| 167 | tas_button_params[Settings::NativeButton::DRight].Set("button", 14); | ||
| 168 | tas_button_params[Settings::NativeButton::DDown].Set("button", 15); | ||
| 169 | tas_button_params[Settings::NativeButton::SL].Set("button", 16); | ||
| 170 | tas_button_params[Settings::NativeButton::SR].Set("button", 17); | ||
| 171 | tas_button_params[Settings::NativeButton::Home].Set("button", 18); | ||
| 172 | tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19); | ||
| 173 | |||
| 174 | tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); | ||
| 175 | tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); | ||
| 176 | tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2); | ||
| 177 | tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3); | ||
| 178 | } | ||
| 179 | |||
| 180 | void EmulatedController::ReloadInput() { | ||
| 181 | // If you load any device here add the equivalent to the UnloadInput() function | ||
| 182 | LoadDevices(); | ||
| 183 | for (std::size_t index = 0; index < button_devices.size(); ++index) { | ||
| 184 | if (!button_devices[index]) { | ||
| 185 | continue; | ||
| 186 | } | ||
| 187 | const auto uuid = Common::UUID{button_params[index].Get("guid", "")}; | ||
| 188 | Common::Input::InputCallback button_callback{ | ||
| 189 | [this, index, uuid](Common::Input::CallbackStatus callback) { | ||
| 190 | SetButton(callback, index, uuid); | ||
| 191 | }}; | ||
| 192 | button_devices[index]->SetCallback(button_callback); | ||
| 193 | button_devices[index]->ForceUpdate(); | ||
| 194 | } | ||
| 195 | |||
| 196 | for (std::size_t index = 0; index < stick_devices.size(); ++index) { | ||
| 197 | if (!stick_devices[index]) { | ||
| 198 | continue; | ||
| 199 | } | ||
| 200 | const auto uuid = Common::UUID{stick_params[index].Get("guid", "")}; | ||
| 201 | Common::Input::InputCallback stick_callback{ | ||
| 202 | [this, index, uuid](Common::Input::CallbackStatus callback) { | ||
| 203 | SetStick(callback, index, uuid); | ||
| 204 | }}; | ||
| 205 | stick_devices[index]->SetCallback(stick_callback); | ||
| 206 | stick_devices[index]->ForceUpdate(); | ||
| 207 | } | ||
| 208 | |||
| 209 | for (std::size_t index = 0; index < trigger_devices.size(); ++index) { | ||
| 210 | if (!trigger_devices[index]) { | ||
| 211 | continue; | ||
| 212 | } | ||
| 213 | const auto uuid = Common::UUID{trigger_params[index].Get("guid", "")}; | ||
| 214 | Common::Input::InputCallback trigger_callback{ | ||
| 215 | [this, index, uuid](Common::Input::CallbackStatus callback) { | ||
| 216 | SetTrigger(callback, index, uuid); | ||
| 217 | }}; | ||
| 218 | trigger_devices[index]->SetCallback(trigger_callback); | ||
| 219 | trigger_devices[index]->ForceUpdate(); | ||
| 220 | } | ||
| 221 | |||
| 222 | for (std::size_t index = 0; index < battery_devices.size(); ++index) { | ||
| 223 | if (!battery_devices[index]) { | ||
| 224 | continue; | ||
| 225 | } | ||
| 226 | Common::Input::InputCallback battery_callback{ | ||
| 227 | [this, index](Common::Input::CallbackStatus callback) { SetBattery(callback, index); }}; | ||
| 228 | battery_devices[index]->SetCallback(battery_callback); | ||
| 229 | battery_devices[index]->ForceUpdate(); | ||
| 230 | } | ||
| 231 | |||
| 232 | for (std::size_t index = 0; index < motion_devices.size(); ++index) { | ||
| 233 | if (!motion_devices[index]) { | ||
| 234 | continue; | ||
| 235 | } | ||
| 236 | Common::Input::InputCallback motion_callback{ | ||
| 237 | [this, index](Common::Input::CallbackStatus callback) { SetMotion(callback, index); }}; | ||
| 238 | motion_devices[index]->SetCallback(motion_callback); | ||
| 239 | motion_devices[index]->ForceUpdate(); | ||
| 240 | } | ||
| 241 | |||
| 242 | // Use a common UUID for TAS | ||
| 243 | const auto tas_uuid = Common::UUID{0x0, 0x7A5}; | ||
| 244 | |||
| 245 | // Register TAS devices. No need to force update | ||
| 246 | for (std::size_t index = 0; index < tas_button_devices.size(); ++index) { | ||
| 247 | if (!tas_button_devices[index]) { | ||
| 248 | continue; | ||
| 249 | } | ||
| 250 | Common::Input::InputCallback button_callback{ | ||
| 251 | [this, index, tas_uuid](Common::Input::CallbackStatus callback) { | ||
| 252 | SetButton(callback, index, tas_uuid); | ||
| 253 | }}; | ||
| 254 | tas_button_devices[index]->SetCallback(button_callback); | ||
| 255 | } | ||
| 256 | |||
| 257 | for (std::size_t index = 0; index < tas_stick_devices.size(); ++index) { | ||
| 258 | if (!tas_stick_devices[index]) { | ||
| 259 | continue; | ||
| 260 | } | ||
| 261 | Common::Input::InputCallback stick_callback{ | ||
| 262 | [this, index, tas_uuid](Common::Input::CallbackStatus callback) { | ||
| 263 | SetStick(callback, index, tas_uuid); | ||
| 264 | }}; | ||
| 265 | tas_stick_devices[index]->SetCallback(stick_callback); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | void EmulatedController::UnloadInput() { | ||
| 270 | for (auto& button : button_devices) { | ||
| 271 | button.reset(); | ||
| 272 | } | ||
| 273 | for (auto& stick : stick_devices) { | ||
| 274 | stick.reset(); | ||
| 275 | } | ||
| 276 | for (auto& motion : motion_devices) { | ||
| 277 | motion.reset(); | ||
| 278 | } | ||
| 279 | for (auto& trigger : trigger_devices) { | ||
| 280 | trigger.reset(); | ||
| 281 | } | ||
| 282 | for (auto& battery : battery_devices) { | ||
| 283 | battery.reset(); | ||
| 284 | } | ||
| 285 | for (auto& output : output_devices) { | ||
| 286 | output.reset(); | ||
| 287 | } | ||
| 288 | for (auto& button : tas_button_devices) { | ||
| 289 | button.reset(); | ||
| 290 | } | ||
| 291 | for (auto& stick : tas_stick_devices) { | ||
| 292 | stick.reset(); | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | void EmulatedController::EnableConfiguration() { | ||
| 297 | is_configuring = true; | ||
| 298 | tmp_is_connected = is_connected; | ||
| 299 | tmp_npad_type = npad_type; | ||
| 300 | } | ||
| 301 | |||
| 302 | void EmulatedController::DisableConfiguration() { | ||
| 303 | is_configuring = false; | ||
| 304 | |||
| 305 | // Apply temporary npad type to the real controller | ||
| 306 | if (tmp_npad_type != npad_type) { | ||
| 307 | if (is_connected) { | ||
| 308 | Disconnect(); | ||
| 309 | } | ||
| 310 | SetNpadStyleIndex(tmp_npad_type); | ||
| 311 | } | ||
| 312 | |||
| 313 | // Apply temporary connected status to the real controller | ||
| 314 | if (tmp_is_connected != is_connected) { | ||
| 315 | if (tmp_is_connected) { | ||
| 316 | Connect(); | ||
| 317 | return; | ||
| 318 | } | ||
| 319 | Disconnect(); | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | bool EmulatedController::IsConfiguring() const { | ||
| 324 | return is_configuring; | ||
| 325 | } | ||
| 326 | |||
| 327 | void EmulatedController::SaveCurrentConfig() { | ||
| 328 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | ||
| 329 | auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 330 | player.connected = is_connected; | ||
| 331 | player.controller_type = MapNPadToSettingsType(npad_type); | ||
| 332 | for (std::size_t index = 0; index < player.buttons.size(); ++index) { | ||
| 333 | player.buttons[index] = button_params[index].Serialize(); | ||
| 334 | } | ||
| 335 | for (std::size_t index = 0; index < player.analogs.size(); ++index) { | ||
| 336 | player.analogs[index] = stick_params[index].Serialize(); | ||
| 337 | } | ||
| 338 | for (std::size_t index = 0; index < player.motions.size(); ++index) { | ||
| 339 | player.motions[index] = motion_params[index].Serialize(); | ||
| 340 | } | ||
| 341 | } | ||
| 342 | |||
| 343 | void EmulatedController::RestoreConfig() { | ||
| 344 | if (!is_configuring) { | ||
| 345 | return; | ||
| 346 | } | ||
| 347 | ReloadFromSettings(); | ||
| 348 | } | ||
| 349 | |||
| 350 | std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices( | ||
| 351 | EmulatedDeviceIndex device_index) const { | ||
| 352 | std::vector<Common::ParamPackage> devices; | ||
| 353 | for (const auto& param : button_params) { | ||
| 354 | if (!param.Has("engine")) { | ||
| 355 | continue; | ||
| 356 | } | ||
| 357 | const auto devices_it = std::find_if( | ||
| 358 | devices.begin(), devices.end(), [param](const Common::ParamPackage param_) { | ||
| 359 | return param.Get("engine", "") == param_.Get("engine", "") && | ||
| 360 | param.Get("guid", "") == param_.Get("guid", "") && | ||
| 361 | param.Get("port", 0) == param_.Get("port", 0); | ||
| 362 | }); | ||
| 363 | if (devices_it != devices.end()) { | ||
| 364 | continue; | ||
| 365 | } | ||
| 366 | Common::ParamPackage device{}; | ||
| 367 | device.Set("engine", param.Get("engine", "")); | ||
| 368 | device.Set("guid", param.Get("guid", "")); | ||
| 369 | device.Set("port", param.Get("port", 0)); | ||
| 370 | devices.push_back(device); | ||
| 371 | } | ||
| 372 | |||
| 373 | for (const auto& param : stick_params) { | ||
| 374 | if (!param.Has("engine")) { | ||
| 375 | continue; | ||
| 376 | } | ||
| 377 | if (param.Get("engine", "") == "analog_from_button") { | ||
| 378 | continue; | ||
| 379 | } | ||
| 380 | const auto devices_it = std::find_if( | ||
| 381 | devices.begin(), devices.end(), [param](const Common::ParamPackage param_) { | ||
| 382 | return param.Get("engine", "") == param_.Get("engine", "") && | ||
| 383 | param.Get("guid", "") == param_.Get("guid", "") && | ||
| 384 | param.Get("port", 0) == param_.Get("port", 0); | ||
| 385 | }); | ||
| 386 | if (devices_it != devices.end()) { | ||
| 387 | continue; | ||
| 388 | } | ||
| 389 | Common::ParamPackage device{}; | ||
| 390 | device.Set("engine", param.Get("engine", "")); | ||
| 391 | device.Set("guid", param.Get("guid", "")); | ||
| 392 | device.Set("port", param.Get("port", 0)); | ||
| 393 | devices.push_back(device); | ||
| 394 | } | ||
| 395 | return devices; | ||
| 396 | } | ||
| 397 | |||
| 398 | Common::ParamPackage EmulatedController::GetButtonParam(std::size_t index) const { | ||
| 399 | if (index >= button_params.size()) { | ||
| 400 | return {}; | ||
| 401 | } | ||
| 402 | return button_params[index]; | ||
| 403 | } | ||
| 404 | |||
| 405 | Common::ParamPackage EmulatedController::GetStickParam(std::size_t index) const { | ||
| 406 | if (index >= stick_params.size()) { | ||
| 407 | return {}; | ||
| 408 | } | ||
| 409 | return stick_params[index]; | ||
| 410 | } | ||
| 411 | |||
| 412 | Common::ParamPackage EmulatedController::GetMotionParam(std::size_t index) const { | ||
| 413 | if (index >= motion_params.size()) { | ||
| 414 | return {}; | ||
| 415 | } | ||
| 416 | return motion_params[index]; | ||
| 417 | } | ||
| 418 | |||
| 419 | void EmulatedController::SetButtonParam(std::size_t index, Common::ParamPackage param) { | ||
| 420 | if (index >= button_params.size()) { | ||
| 421 | return; | ||
| 422 | } | ||
| 423 | button_params[index] = param; | ||
| 424 | ReloadInput(); | ||
| 425 | } | ||
| 426 | |||
| 427 | void EmulatedController::SetStickParam(std::size_t index, Common::ParamPackage param) { | ||
| 428 | if (index >= stick_params.size()) { | ||
| 429 | return; | ||
| 430 | } | ||
| 431 | stick_params[index] = param; | ||
| 432 | ReloadInput(); | ||
| 433 | } | ||
| 434 | |||
| 435 | void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage param) { | ||
| 436 | if (index >= motion_params.size()) { | ||
| 437 | return; | ||
| 438 | } | ||
| 439 | motion_params[index] = param; | ||
| 440 | ReloadInput(); | ||
| 441 | } | ||
| 442 | |||
| 443 | void EmulatedController::SetButton(Common::Input::CallbackStatus callback, std::size_t index, | ||
| 444 | Common::UUID uuid) { | ||
| 445 | if (index >= controller.button_values.size()) { | ||
| 446 | return; | ||
| 447 | } | ||
| 448 | { | ||
| 449 | std::lock_guard lock{mutex}; | ||
| 450 | bool value_changed = false; | ||
| 451 | const auto new_status = TransformToButton(callback); | ||
| 452 | auto& current_status = controller.button_values[index]; | ||
| 453 | |||
| 454 | // Only read button values that have the same uuid or are pressed once | ||
| 455 | if (current_status.uuid != uuid) { | ||
| 456 | if (!new_status.value) { | ||
| 457 | return; | ||
| 458 | } | ||
| 459 | } | ||
| 460 | |||
| 461 | current_status.toggle = new_status.toggle; | ||
| 462 | current_status.uuid = uuid; | ||
| 463 | |||
| 464 | // Update button status with current | ||
| 465 | if (!current_status.toggle) { | ||
| 466 | current_status.locked = false; | ||
| 467 | if (current_status.value != new_status.value) { | ||
| 468 | current_status.value = new_status.value; | ||
| 469 | value_changed = true; | ||
| 470 | } | ||
| 471 | } else { | ||
| 472 | // Toggle button and lock status | ||
| 473 | if (new_status.value && !current_status.locked) { | ||
| 474 | current_status.locked = true; | ||
| 475 | current_status.value = !current_status.value; | ||
| 476 | value_changed = true; | ||
| 477 | } | ||
| 478 | |||
| 479 | // Unlock button ready for next press | ||
| 480 | if (!new_status.value && current_status.locked) { | ||
| 481 | current_status.locked = false; | ||
| 482 | } | ||
| 483 | } | ||
| 484 | |||
| 485 | if (!value_changed) { | ||
| 486 | return; | ||
| 487 | } | ||
| 488 | |||
| 489 | if (is_configuring) { | ||
| 490 | controller.npad_button_state.raw = NpadButton::None; | ||
| 491 | controller.debug_pad_button_state.raw = 0; | ||
| 492 | TriggerOnChange(ControllerTriggerType::Button, false); | ||
| 493 | return; | ||
| 494 | } | ||
| 495 | |||
| 496 | switch (index) { | ||
| 497 | case Settings::NativeButton::A: | ||
| 498 | controller.npad_button_state.a.Assign(current_status.value); | ||
| 499 | controller.debug_pad_button_state.a.Assign(current_status.value); | ||
| 500 | break; | ||
| 501 | case Settings::NativeButton::B: | ||
| 502 | controller.npad_button_state.b.Assign(current_status.value); | ||
| 503 | controller.debug_pad_button_state.b.Assign(current_status.value); | ||
| 504 | break; | ||
| 505 | case Settings::NativeButton::X: | ||
| 506 | controller.npad_button_state.x.Assign(current_status.value); | ||
| 507 | controller.debug_pad_button_state.x.Assign(current_status.value); | ||
| 508 | break; | ||
| 509 | case Settings::NativeButton::Y: | ||
| 510 | controller.npad_button_state.y.Assign(current_status.value); | ||
| 511 | controller.debug_pad_button_state.y.Assign(current_status.value); | ||
| 512 | break; | ||
| 513 | case Settings::NativeButton::LStick: | ||
| 514 | controller.npad_button_state.stick_l.Assign(current_status.value); | ||
| 515 | break; | ||
| 516 | case Settings::NativeButton::RStick: | ||
| 517 | controller.npad_button_state.stick_r.Assign(current_status.value); | ||
| 518 | break; | ||
| 519 | case Settings::NativeButton::L: | ||
| 520 | controller.npad_button_state.l.Assign(current_status.value); | ||
| 521 | controller.debug_pad_button_state.l.Assign(current_status.value); | ||
| 522 | break; | ||
| 523 | case Settings::NativeButton::R: | ||
| 524 | controller.npad_button_state.r.Assign(current_status.value); | ||
| 525 | controller.debug_pad_button_state.r.Assign(current_status.value); | ||
| 526 | break; | ||
| 527 | case Settings::NativeButton::ZL: | ||
| 528 | controller.npad_button_state.zl.Assign(current_status.value); | ||
| 529 | controller.debug_pad_button_state.zl.Assign(current_status.value); | ||
| 530 | break; | ||
| 531 | case Settings::NativeButton::ZR: | ||
| 532 | controller.npad_button_state.zr.Assign(current_status.value); | ||
| 533 | controller.debug_pad_button_state.zr.Assign(current_status.value); | ||
| 534 | break; | ||
| 535 | case Settings::NativeButton::Plus: | ||
| 536 | controller.npad_button_state.plus.Assign(current_status.value); | ||
| 537 | controller.debug_pad_button_state.plus.Assign(current_status.value); | ||
| 538 | break; | ||
| 539 | case Settings::NativeButton::Minus: | ||
| 540 | controller.npad_button_state.minus.Assign(current_status.value); | ||
| 541 | controller.debug_pad_button_state.minus.Assign(current_status.value); | ||
| 542 | break; | ||
| 543 | case Settings::NativeButton::DLeft: | ||
| 544 | controller.npad_button_state.left.Assign(current_status.value); | ||
| 545 | controller.debug_pad_button_state.d_left.Assign(current_status.value); | ||
| 546 | break; | ||
| 547 | case Settings::NativeButton::DUp: | ||
| 548 | controller.npad_button_state.up.Assign(current_status.value); | ||
| 549 | controller.debug_pad_button_state.d_up.Assign(current_status.value); | ||
| 550 | break; | ||
| 551 | case Settings::NativeButton::DRight: | ||
| 552 | controller.npad_button_state.right.Assign(current_status.value); | ||
| 553 | controller.debug_pad_button_state.d_right.Assign(current_status.value); | ||
| 554 | break; | ||
| 555 | case Settings::NativeButton::DDown: | ||
| 556 | controller.npad_button_state.down.Assign(current_status.value); | ||
| 557 | controller.debug_pad_button_state.d_down.Assign(current_status.value); | ||
| 558 | break; | ||
| 559 | case Settings::NativeButton::SL: | ||
| 560 | controller.npad_button_state.left_sl.Assign(current_status.value); | ||
| 561 | controller.npad_button_state.right_sl.Assign(current_status.value); | ||
| 562 | break; | ||
| 563 | case Settings::NativeButton::SR: | ||
| 564 | controller.npad_button_state.left_sr.Assign(current_status.value); | ||
| 565 | controller.npad_button_state.right_sr.Assign(current_status.value); | ||
| 566 | break; | ||
| 567 | case Settings::NativeButton::Home: | ||
| 568 | case Settings::NativeButton::Screenshot: | ||
| 569 | break; | ||
| 570 | } | ||
| 571 | } | ||
| 572 | if (!is_connected) { | ||
| 573 | if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) { | ||
| 574 | Connect(); | ||
| 575 | } | ||
| 576 | if (npad_id_type == NpadIdType::Handheld && npad_type == NpadStyleIndex::Handheld) { | ||
| 577 | Connect(); | ||
| 578 | } | ||
| 579 | } | ||
| 580 | TriggerOnChange(ControllerTriggerType::Button, true); | ||
| 581 | } | ||
| 582 | |||
| 583 | void EmulatedController::SetStick(Common::Input::CallbackStatus callback, std::size_t index, | ||
| 584 | Common::UUID uuid) { | ||
| 585 | if (index >= controller.stick_values.size()) { | ||
| 586 | return; | ||
| 587 | } | ||
| 588 | std::lock_guard lock{mutex}; | ||
| 589 | const auto stick_value = TransformToStick(callback); | ||
| 590 | |||
| 591 | // Only read stick values that have the same uuid or are over the threshold to avoid flapping | ||
| 592 | if (controller.stick_values[index].uuid != uuid) { | ||
| 593 | if (!stick_value.down && !stick_value.up && !stick_value.left && !stick_value.right) { | ||
| 594 | return; | ||
| 595 | } | ||
| 596 | } | ||
| 597 | |||
| 598 | controller.stick_values[index] = stick_value; | ||
| 599 | controller.stick_values[index].uuid = uuid; | ||
| 600 | |||
| 601 | if (is_configuring) { | ||
| 602 | controller.analog_stick_state.left = {}; | ||
| 603 | controller.analog_stick_state.right = {}; | ||
| 604 | TriggerOnChange(ControllerTriggerType::Stick, false); | ||
| 605 | return; | ||
| 606 | } | ||
| 607 | |||
| 608 | const AnalogStickState stick{ | ||
| 609 | .x = static_cast<s32>(controller.stick_values[index].x.value * HID_JOYSTICK_MAX), | ||
| 610 | .y = static_cast<s32>(controller.stick_values[index].y.value * HID_JOYSTICK_MAX), | ||
| 611 | }; | ||
| 612 | |||
| 613 | switch (index) { | ||
| 614 | case Settings::NativeAnalog::LStick: | ||
| 615 | controller.analog_stick_state.left = stick; | ||
| 616 | controller.npad_button_state.stick_l_left.Assign(controller.stick_values[index].left); | ||
| 617 | controller.npad_button_state.stick_l_up.Assign(controller.stick_values[index].up); | ||
| 618 | controller.npad_button_state.stick_l_right.Assign(controller.stick_values[index].right); | ||
| 619 | controller.npad_button_state.stick_l_down.Assign(controller.stick_values[index].down); | ||
| 620 | break; | ||
| 621 | case Settings::NativeAnalog::RStick: | ||
| 622 | controller.analog_stick_state.right = stick; | ||
| 623 | controller.npad_button_state.stick_r_left.Assign(controller.stick_values[index].left); | ||
| 624 | controller.npad_button_state.stick_r_up.Assign(controller.stick_values[index].up); | ||
| 625 | controller.npad_button_state.stick_r_right.Assign(controller.stick_values[index].right); | ||
| 626 | controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down); | ||
| 627 | break; | ||
| 628 | } | ||
| 629 | |||
| 630 | TriggerOnChange(ControllerTriggerType::Stick, true); | ||
| 631 | } | ||
| 632 | |||
| 633 | void EmulatedController::SetTrigger(Common::Input::CallbackStatus callback, std::size_t index, | ||
| 634 | Common::UUID uuid) { | ||
| 635 | if (index >= controller.trigger_values.size()) { | ||
| 636 | return; | ||
| 637 | } | ||
| 638 | std::lock_guard lock{mutex}; | ||
| 639 | const auto trigger_value = TransformToTrigger(callback); | ||
| 640 | |||
| 641 | // Only read trigger values that have the same uuid or are pressed once | ||
| 642 | if (controller.stick_values[index].uuid != uuid) { | ||
| 643 | if (!trigger_value.pressed.value) { | ||
| 644 | return; | ||
| 645 | } | ||
| 646 | } | ||
| 647 | |||
| 648 | controller.trigger_values[index] = trigger_value; | ||
| 649 | controller.trigger_values[index].uuid = uuid; | ||
| 650 | |||
| 651 | if (is_configuring) { | ||
| 652 | controller.gc_trigger_state.left = 0; | ||
| 653 | controller.gc_trigger_state.right = 0; | ||
| 654 | TriggerOnChange(ControllerTriggerType::Trigger, false); | ||
| 655 | return; | ||
| 656 | } | ||
| 657 | |||
| 658 | const auto trigger = controller.trigger_values[index]; | ||
| 659 | |||
| 660 | switch (index) { | ||
| 661 | case Settings::NativeTrigger::LTrigger: | ||
| 662 | controller.gc_trigger_state.left = static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX); | ||
| 663 | controller.npad_button_state.zl.Assign(trigger.pressed.value); | ||
| 664 | break; | ||
| 665 | case Settings::NativeTrigger::RTrigger: | ||
| 666 | controller.gc_trigger_state.right = | ||
| 667 | static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX); | ||
| 668 | controller.npad_button_state.zr.Assign(trigger.pressed.value); | ||
| 669 | break; | ||
| 670 | } | ||
| 671 | |||
| 672 | TriggerOnChange(ControllerTriggerType::Trigger, true); | ||
| 673 | } | ||
| 674 | |||
| 675 | void EmulatedController::SetMotion(Common::Input::CallbackStatus callback, std::size_t index) { | ||
| 676 | if (index >= controller.motion_values.size()) { | ||
| 677 | return; | ||
| 678 | } | ||
| 679 | std::lock_guard lock{mutex}; | ||
| 680 | auto& raw_status = controller.motion_values[index].raw_status; | ||
| 681 | auto& emulated = controller.motion_values[index].emulated; | ||
| 682 | |||
| 683 | raw_status = TransformToMotion(callback); | ||
| 684 | emulated.SetAcceleration(Common::Vec3f{ | ||
| 685 | raw_status.accel.x.value, | ||
| 686 | raw_status.accel.y.value, | ||
| 687 | raw_status.accel.z.value, | ||
| 688 | }); | ||
| 689 | emulated.SetGyroscope(Common::Vec3f{ | ||
| 690 | raw_status.gyro.x.value, | ||
| 691 | raw_status.gyro.y.value, | ||
| 692 | raw_status.gyro.z.value, | ||
| 693 | }); | ||
| 694 | emulated.UpdateRotation(raw_status.delta_timestamp); | ||
| 695 | emulated.UpdateOrientation(raw_status.delta_timestamp); | ||
| 696 | force_update_motion = raw_status.force_update; | ||
| 697 | |||
| 698 | if (is_configuring) { | ||
| 699 | TriggerOnChange(ControllerTriggerType::Motion, false); | ||
| 700 | return; | ||
| 701 | } | ||
| 702 | |||
| 703 | auto& motion = controller.motion_state[index]; | ||
| 704 | motion.accel = emulated.GetAcceleration(); | ||
| 705 | motion.gyro = emulated.GetGyroscope(); | ||
| 706 | motion.rotation = emulated.GetRotations(); | ||
| 707 | motion.orientation = emulated.GetOrientation(); | ||
| 708 | motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); | ||
| 709 | |||
| 710 | TriggerOnChange(ControllerTriggerType::Motion, true); | ||
| 711 | } | ||
| 712 | |||
| 713 | void EmulatedController::SetBattery(Common::Input::CallbackStatus callback, std::size_t index) { | ||
| 714 | if (index >= controller.battery_values.size()) { | ||
| 715 | return; | ||
| 716 | } | ||
| 717 | std::lock_guard lock{mutex}; | ||
| 718 | controller.battery_values[index] = TransformToBattery(callback); | ||
| 719 | |||
| 720 | if (is_configuring) { | ||
| 721 | TriggerOnChange(ControllerTriggerType::Battery, false); | ||
| 722 | return; | ||
| 723 | } | ||
| 724 | |||
| 725 | bool is_charging = false; | ||
| 726 | bool is_powered = false; | ||
| 727 | NpadBatteryLevel battery_level = 0; | ||
| 728 | switch (controller.battery_values[index]) { | ||
| 729 | case Common::Input::BatteryLevel::Charging: | ||
| 730 | is_charging = true; | ||
| 731 | is_powered = true; | ||
| 732 | battery_level = 6; | ||
| 733 | break; | ||
| 734 | case Common::Input::BatteryLevel::Medium: | ||
| 735 | battery_level = 6; | ||
| 736 | break; | ||
| 737 | case Common::Input::BatteryLevel::Low: | ||
| 738 | battery_level = 4; | ||
| 739 | break; | ||
| 740 | case Common::Input::BatteryLevel::Critical: | ||
| 741 | battery_level = 2; | ||
| 742 | break; | ||
| 743 | case Common::Input::BatteryLevel::Empty: | ||
| 744 | battery_level = 0; | ||
| 745 | break; | ||
| 746 | case Common::Input::BatteryLevel::None: | ||
| 747 | case Common::Input::BatteryLevel::Full: | ||
| 748 | default: | ||
| 749 | is_powered = true; | ||
| 750 | battery_level = 8; | ||
| 751 | break; | ||
| 752 | } | ||
| 753 | |||
| 754 | switch (index) { | ||
| 755 | case LeftIndex: | ||
| 756 | controller.battery_state.left = { | ||
| 757 | .is_powered = is_powered, | ||
| 758 | .is_charging = is_charging, | ||
| 759 | .battery_level = battery_level, | ||
| 760 | }; | ||
| 761 | break; | ||
| 762 | case RightIndex: | ||
| 763 | controller.battery_state.right = { | ||
| 764 | .is_powered = is_powered, | ||
| 765 | .is_charging = is_charging, | ||
| 766 | .battery_level = battery_level, | ||
| 767 | }; | ||
| 768 | break; | ||
| 769 | case DualIndex: | ||
| 770 | controller.battery_state.dual = { | ||
| 771 | .is_powered = is_powered, | ||
| 772 | .is_charging = is_charging, | ||
| 773 | .battery_level = battery_level, | ||
| 774 | }; | ||
| 775 | break; | ||
| 776 | } | ||
| 777 | TriggerOnChange(ControllerTriggerType::Battery, true); | ||
| 778 | } | ||
| 779 | |||
| 780 | bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { | ||
| 781 | if (device_index >= output_devices.size()) { | ||
| 782 | return false; | ||
| 783 | } | ||
| 784 | if (!output_devices[device_index]) { | ||
| 785 | return false; | ||
| 786 | } | ||
| 787 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | ||
| 788 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 789 | const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f; | ||
| 790 | |||
| 791 | if (!player.vibration_enabled) { | ||
| 792 | return false; | ||
| 793 | } | ||
| 794 | |||
| 795 | // Exponential amplification is too strong at low amplitudes. Switch to a linear | ||
| 796 | // amplification if strength is set below 0.7f | ||
| 797 | const Common::Input::VibrationAmplificationType type = | ||
| 798 | strength > 0.7f ? Common::Input::VibrationAmplificationType::Exponential | ||
| 799 | : Common::Input::VibrationAmplificationType::Linear; | ||
| 800 | |||
| 801 | const Common::Input::VibrationStatus status = { | ||
| 802 | .low_amplitude = std::min(vibration.low_amplitude * strength, 1.0f), | ||
| 803 | .low_frequency = vibration.low_frequency, | ||
| 804 | .high_amplitude = std::min(vibration.high_amplitude * strength, 1.0f), | ||
| 805 | .high_frequency = vibration.high_frequency, | ||
| 806 | .type = type, | ||
| 807 | }; | ||
| 808 | return output_devices[device_index]->SetVibration(status) == | ||
| 809 | Common::Input::VibrationError::None; | ||
| 810 | } | ||
| 811 | |||
| 812 | bool EmulatedController::TestVibration(std::size_t device_index) { | ||
| 813 | if (device_index >= output_devices.size()) { | ||
| 814 | return false; | ||
| 815 | } | ||
| 816 | if (!output_devices[device_index]) { | ||
| 817 | return false; | ||
| 818 | } | ||
| 819 | |||
| 820 | // Send a slight vibration to test for rumble support | ||
| 821 | constexpr Common::Input::VibrationStatus status = { | ||
| 822 | .low_amplitude = 0.001f, | ||
| 823 | .low_frequency = 160.0f, | ||
| 824 | .high_amplitude = 0.001f, | ||
| 825 | .high_frequency = 320.0f, | ||
| 826 | .type = Common::Input::VibrationAmplificationType::Linear, | ||
| 827 | }; | ||
| 828 | return output_devices[device_index]->SetVibration(status) == | ||
| 829 | Common::Input::VibrationError::None; | ||
| 830 | } | ||
| 831 | |||
| 832 | void EmulatedController::SetLedPattern() { | ||
| 833 | for (auto& device : output_devices) { | ||
| 834 | if (!device) { | ||
| 835 | continue; | ||
| 836 | } | ||
| 837 | |||
| 838 | const LedPattern pattern = GetLedPattern(); | ||
| 839 | const Common::Input::LedStatus status = { | ||
| 840 | .led_1 = pattern.position1 != 0, | ||
| 841 | .led_2 = pattern.position2 != 0, | ||
| 842 | .led_3 = pattern.position3 != 0, | ||
| 843 | .led_4 = pattern.position4 != 0, | ||
| 844 | }; | ||
| 845 | device->SetLED(status); | ||
| 846 | } | ||
| 847 | } | ||
| 848 | |||
| 849 | void EmulatedController::Connect() { | ||
| 850 | { | ||
| 851 | std::lock_guard lock{mutex}; | ||
| 852 | if (is_configuring) { | ||
| 853 | tmp_is_connected = true; | ||
| 854 | TriggerOnChange(ControllerTriggerType::Connected, false); | ||
| 855 | return; | ||
| 856 | } | ||
| 857 | |||
| 858 | if (is_connected) { | ||
| 859 | return; | ||
| 860 | } | ||
| 861 | is_connected = true; | ||
| 862 | } | ||
| 863 | TriggerOnChange(ControllerTriggerType::Connected, true); | ||
| 864 | } | ||
| 865 | |||
| 866 | void EmulatedController::Disconnect() { | ||
| 867 | { | ||
| 868 | std::lock_guard lock{mutex}; | ||
| 869 | if (is_configuring) { | ||
| 870 | tmp_is_connected = false; | ||
| 871 | TriggerOnChange(ControllerTriggerType::Disconnected, false); | ||
| 872 | return; | ||
| 873 | } | ||
| 874 | |||
| 875 | if (!is_connected) { | ||
| 876 | return; | ||
| 877 | } | ||
| 878 | is_connected = false; | ||
| 879 | } | ||
| 880 | TriggerOnChange(ControllerTriggerType::Disconnected, true); | ||
| 881 | } | ||
| 882 | |||
| 883 | bool EmulatedController::IsConnected(bool get_temporary_value) const { | ||
| 884 | if (get_temporary_value && is_configuring) { | ||
| 885 | return tmp_is_connected; | ||
| 886 | } | ||
| 887 | return is_connected; | ||
| 888 | } | ||
| 889 | |||
| 890 | bool EmulatedController::IsVibrationEnabled() const { | ||
| 891 | const auto player_index = NpadIdTypeToIndex(npad_id_type); | ||
| 892 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 893 | return player.vibration_enabled; | ||
| 894 | } | ||
| 895 | |||
| 896 | NpadIdType EmulatedController::GetNpadIdType() const { | ||
| 897 | return npad_id_type; | ||
| 898 | } | ||
| 899 | |||
| 900 | NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const { | ||
| 901 | if (get_temporary_value && is_configuring) { | ||
| 902 | return tmp_npad_type; | ||
| 903 | } | ||
| 904 | return npad_type; | ||
| 905 | } | ||
| 906 | |||
| 907 | void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { | ||
| 908 | { | ||
| 909 | std::lock_guard lock{mutex}; | ||
| 910 | |||
| 911 | if (is_configuring) { | ||
| 912 | if (tmp_npad_type == npad_type_) { | ||
| 913 | return; | ||
| 914 | } | ||
| 915 | tmp_npad_type = npad_type_; | ||
| 916 | TriggerOnChange(ControllerTriggerType::Type, false); | ||
| 917 | return; | ||
| 918 | } | ||
| 919 | |||
| 920 | if (npad_type == npad_type_) { | ||
| 921 | return; | ||
| 922 | } | ||
| 923 | if (is_connected) { | ||
| 924 | LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", | ||
| 925 | NpadIdTypeToIndex(npad_id_type)); | ||
| 926 | } | ||
| 927 | npad_type = npad_type_; | ||
| 928 | } | ||
| 929 | TriggerOnChange(ControllerTriggerType::Type, true); | ||
| 930 | } | ||
| 931 | |||
| 932 | LedPattern EmulatedController::GetLedPattern() const { | ||
| 933 | switch (npad_id_type) { | ||
| 934 | case NpadIdType::Player1: | ||
| 935 | return LedPattern{1, 0, 0, 0}; | ||
| 936 | case NpadIdType::Player2: | ||
| 937 | return LedPattern{1, 1, 0, 0}; | ||
| 938 | case NpadIdType::Player3: | ||
| 939 | return LedPattern{1, 1, 1, 0}; | ||
| 940 | case NpadIdType::Player4: | ||
| 941 | return LedPattern{1, 1, 1, 1}; | ||
| 942 | case NpadIdType::Player5: | ||
| 943 | return LedPattern{1, 0, 0, 1}; | ||
| 944 | case NpadIdType::Player6: | ||
| 945 | return LedPattern{1, 0, 1, 0}; | ||
| 946 | case NpadIdType::Player7: | ||
| 947 | return LedPattern{1, 0, 1, 1}; | ||
| 948 | case NpadIdType::Player8: | ||
| 949 | return LedPattern{0, 1, 1, 0}; | ||
| 950 | default: | ||
| 951 | return LedPattern{0, 0, 0, 0}; | ||
| 952 | } | ||
| 953 | } | ||
| 954 | |||
| 955 | ButtonValues EmulatedController::GetButtonsValues() const { | ||
| 956 | return controller.button_values; | ||
| 957 | } | ||
| 958 | |||
| 959 | SticksValues EmulatedController::GetSticksValues() const { | ||
| 960 | return controller.stick_values; | ||
| 961 | } | ||
| 962 | |||
| 963 | TriggerValues EmulatedController::GetTriggersValues() const { | ||
| 964 | return controller.trigger_values; | ||
| 965 | } | ||
| 966 | |||
| 967 | ControllerMotionValues EmulatedController::GetMotionValues() const { | ||
| 968 | return controller.motion_values; | ||
| 969 | } | ||
| 970 | |||
| 971 | ColorValues EmulatedController::GetColorsValues() const { | ||
| 972 | return controller.color_values; | ||
| 973 | } | ||
| 974 | |||
| 975 | BatteryValues EmulatedController::GetBatteryValues() const { | ||
| 976 | return controller.battery_values; | ||
| 977 | } | ||
| 978 | |||
| 979 | NpadButtonState EmulatedController::GetNpadButtons() const { | ||
| 980 | if (is_configuring) { | ||
| 981 | return {}; | ||
| 982 | } | ||
| 983 | return controller.npad_button_state; | ||
| 984 | } | ||
| 985 | |||
| 986 | DebugPadButton EmulatedController::GetDebugPadButtons() const { | ||
| 987 | if (is_configuring) { | ||
| 988 | return {}; | ||
| 989 | } | ||
| 990 | return controller.debug_pad_button_state; | ||
| 991 | } | ||
| 992 | |||
| 993 | AnalogSticks EmulatedController::GetSticks() const { | ||
| 994 | if (is_configuring) { | ||
| 995 | return {}; | ||
| 996 | } | ||
| 997 | // Some drivers like stick from buttons need constant refreshing | ||
| 998 | for (auto& device : stick_devices) { | ||
| 999 | if (!device) { | ||
| 1000 | continue; | ||
| 1001 | } | ||
| 1002 | device->SoftUpdate(); | ||
| 1003 | } | ||
| 1004 | return controller.analog_stick_state; | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | NpadGcTriggerState EmulatedController::GetTriggers() const { | ||
| 1008 | if (is_configuring) { | ||
| 1009 | return {}; | ||
| 1010 | } | ||
| 1011 | return controller.gc_trigger_state; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | MotionState EmulatedController::GetMotions() const { | ||
| 1015 | if (force_update_motion) { | ||
| 1016 | for (auto& device : motion_devices) { | ||
| 1017 | if (!device) { | ||
| 1018 | continue; | ||
| 1019 | } | ||
| 1020 | device->ForceUpdate(); | ||
| 1021 | } | ||
| 1022 | } | ||
| 1023 | return controller.motion_state; | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | ControllerColors EmulatedController::GetColors() const { | ||
| 1027 | return controller.colors_state; | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | BatteryLevelState EmulatedController::GetBattery() const { | ||
| 1031 | return controller.battery_state; | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { | ||
| 1035 | for (const auto& poller_pair : callback_list) { | ||
| 1036 | const ControllerUpdateCallback& poller = poller_pair.second; | ||
| 1037 | if (!is_npad_service_update && poller.is_npad_service) { | ||
| 1038 | continue; | ||
| 1039 | } | ||
| 1040 | if (poller.on_change) { | ||
| 1041 | poller.on_change(type); | ||
| 1042 | } | ||
| 1043 | } | ||
| 1044 | } | ||
| 1045 | |||
| 1046 | int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) { | ||
| 1047 | std::lock_guard lock{mutex}; | ||
| 1048 | callback_list.insert_or_assign(last_callback_key, update_callback); | ||
| 1049 | return last_callback_key++; | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | void EmulatedController::DeleteCallback(int key) { | ||
| 1053 | std::lock_guard lock{mutex}; | ||
| 1054 | const auto& iterator = callback_list.find(key); | ||
| 1055 | if (iterator == callback_list.end()) { | ||
| 1056 | LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); | ||
| 1057 | return; | ||
| 1058 | } | ||
| 1059 | callback_list.erase(iterator); | ||
| 1060 | } | ||
| 1061 | } // namespace Core::HID | ||
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h new file mode 100644 index 000000000..2c5d51bc8 --- /dev/null +++ b/src/core/hid/emulated_controller.h | |||
| @@ -0,0 +1,392 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <functional> | ||
| 9 | #include <memory> | ||
| 10 | #include <mutex> | ||
| 11 | #include <unordered_map> | ||
| 12 | |||
| 13 | #include "common/common_types.h" | ||
| 14 | #include "common/input.h" | ||
| 15 | #include "common/param_package.h" | ||
| 16 | #include "common/point.h" | ||
| 17 | #include "common/quaternion.h" | ||
| 18 | #include "common/settings.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 | const std::size_t max_emulated_controllers = 2; | ||
| 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 BatteryDevices = | ||
| 39 | std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; | ||
| 40 | using OutputDevices = | ||
| 41 | std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>; | ||
| 42 | |||
| 43 | using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; | ||
| 44 | using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; | ||
| 45 | using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>; | ||
| 46 | using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; | ||
| 47 | using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; | ||
| 48 | using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>; | ||
| 49 | |||
| 50 | using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; | ||
| 51 | using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; | ||
| 52 | using TriggerValues = | ||
| 53 | std::array<Common::Input::TriggerStatus, Settings::NativeTrigger::NumTriggers>; | ||
| 54 | using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>; | ||
| 55 | using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; | ||
| 56 | using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; | ||
| 57 | using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; | ||
| 58 | |||
| 59 | struct AnalogSticks { | ||
| 60 | AnalogStickState left{}; | ||
| 61 | AnalogStickState right{}; | ||
| 62 | }; | ||
| 63 | |||
| 64 | struct ControllerColors { | ||
| 65 | NpadControllerColor fullkey{}; | ||
| 66 | NpadControllerColor left{}; | ||
| 67 | NpadControllerColor right{}; | ||
| 68 | }; | ||
| 69 | |||
| 70 | struct BatteryLevelState { | ||
| 71 | NpadPowerInfo dual{}; | ||
| 72 | NpadPowerInfo left{}; | ||
| 73 | NpadPowerInfo right{}; | ||
| 74 | }; | ||
| 75 | |||
| 76 | struct ControllerMotion { | ||
| 77 | Common::Vec3f accel{}; | ||
| 78 | Common::Vec3f gyro{}; | ||
| 79 | Common::Vec3f rotation{}; | ||
| 80 | std::array<Common::Vec3f, 3> orientation{}; | ||
| 81 | bool is_at_rest{}; | ||
| 82 | }; | ||
| 83 | |||
| 84 | enum EmulatedDeviceIndex : u8 { | ||
| 85 | LeftIndex, | ||
| 86 | RightIndex, | ||
| 87 | DualIndex, | ||
| 88 | AllDevices, | ||
| 89 | }; | ||
| 90 | |||
| 91 | using MotionState = std::array<ControllerMotion, 2>; | ||
| 92 | |||
| 93 | struct ControllerStatus { | ||
| 94 | // Data from input_common | ||
| 95 | ButtonValues button_values{}; | ||
| 96 | SticksValues stick_values{}; | ||
| 97 | ControllerMotionValues motion_values{}; | ||
| 98 | TriggerValues trigger_values{}; | ||
| 99 | ColorValues color_values{}; | ||
| 100 | BatteryValues battery_values{}; | ||
| 101 | VibrationValues vibration_values{}; | ||
| 102 | |||
| 103 | // Data for HID serices | ||
| 104 | NpadButtonState npad_button_state{}; | ||
| 105 | DebugPadButton debug_pad_button_state{}; | ||
| 106 | AnalogSticks analog_stick_state{}; | ||
| 107 | MotionState motion_state{}; | ||
| 108 | NpadGcTriggerState gc_trigger_state{}; | ||
| 109 | ControllerColors colors_state{}; | ||
| 110 | BatteryLevelState battery_state{}; | ||
| 111 | }; | ||
| 112 | |||
| 113 | enum class ControllerTriggerType { | ||
| 114 | Button, | ||
| 115 | Stick, | ||
| 116 | Trigger, | ||
| 117 | Motion, | ||
| 118 | Color, | ||
| 119 | Battery, | ||
| 120 | Vibration, | ||
| 121 | Connected, | ||
| 122 | Disconnected, | ||
| 123 | Type, | ||
| 124 | All, | ||
| 125 | }; | ||
| 126 | |||
| 127 | struct ControllerUpdateCallback { | ||
| 128 | std::function<void(ControllerTriggerType)> on_change; | ||
| 129 | bool is_npad_service; | ||
| 130 | }; | ||
| 131 | |||
| 132 | class EmulatedController { | ||
| 133 | public: | ||
| 134 | /** | ||
| 135 | * Contains all input data related to this controller. Like buttons, joysticks, motion. | ||
| 136 | * @param Npad id type for this specific controller | ||
| 137 | */ | ||
| 138 | explicit EmulatedController(NpadIdType npad_id_type_); | ||
| 139 | ~EmulatedController(); | ||
| 140 | |||
| 141 | YUZU_NON_COPYABLE(EmulatedController); | ||
| 142 | YUZU_NON_MOVEABLE(EmulatedController); | ||
| 143 | |||
| 144 | /// Converts the controller type from settings to npad type | ||
| 145 | static NpadStyleIndex MapSettingsTypeToNPad(Settings::ControllerType type); | ||
| 146 | |||
| 147 | /// Converts npad type to the equivalent of controller type from settings | ||
| 148 | static Settings::ControllerType MapNPadToSettingsType(NpadStyleIndex type); | ||
| 149 | |||
| 150 | /// Gets the NpadIdType for this controller | ||
| 151 | NpadIdType GetNpadIdType() const; | ||
| 152 | |||
| 153 | /// Sets the NpadStyleIndex for this controller | ||
| 154 | void SetNpadStyleIndex(NpadStyleIndex npad_type_); | ||
| 155 | |||
| 156 | /** | ||
| 157 | * Gets the NpadStyleIndex for this controller | ||
| 158 | * @param If true tmp_npad_type will be returned | ||
| 159 | * @return NpadStyleIndex set on the controller | ||
| 160 | */ | ||
| 161 | NpadStyleIndex GetNpadStyleIndex(bool get_temporary_value = false) const; | ||
| 162 | |||
| 163 | /// Sets the connected status to true | ||
| 164 | void Connect(); | ||
| 165 | |||
| 166 | /// Sets the connected status to false | ||
| 167 | void Disconnect(); | ||
| 168 | |||
| 169 | /** | ||
| 170 | * Is the emulated connected | ||
| 171 | * @param If true tmp_is_connected will be returned | ||
| 172 | * @return true if the controller has the connected status | ||
| 173 | */ | ||
| 174 | bool IsConnected(bool get_temporary_value = false) const; | ||
| 175 | |||
| 176 | /// Returns true if vibration is enabled | ||
| 177 | bool IsVibrationEnabled() const; | ||
| 178 | |||
| 179 | /// Removes all callbacks created from input devices | ||
| 180 | void UnloadInput(); | ||
| 181 | |||
| 182 | /// Sets the emulated console into configuring mode. Locking all HID service events from being | ||
| 183 | /// moddified | ||
| 184 | void EnableConfiguration(); | ||
| 185 | |||
| 186 | /// Returns the emulated console to the normal behaivour | ||
| 187 | void DisableConfiguration(); | ||
| 188 | |||
| 189 | /// Returns true if the emulated device is on configuring mode | ||
| 190 | bool IsConfiguring() const; | ||
| 191 | |||
| 192 | /// Reload all input devices | ||
| 193 | void ReloadInput(); | ||
| 194 | |||
| 195 | /// Overrides current mapped devices with the stored configuration and reloads all input devices | ||
| 196 | void ReloadFromSettings(); | ||
| 197 | |||
| 198 | /// Saves the current mapped configuration | ||
| 199 | void SaveCurrentConfig(); | ||
| 200 | |||
| 201 | /// Reverts any mapped changes made that weren't saved | ||
| 202 | void RestoreConfig(); | ||
| 203 | |||
| 204 | /// Returns a vector of mapped devices from the mapped button and stick parameters | ||
| 205 | std::vector<Common::ParamPackage> GetMappedDevices(EmulatedDeviceIndex device_index) const; | ||
| 206 | |||
| 207 | // Returns the current mapped button device | ||
| 208 | Common::ParamPackage GetButtonParam(std::size_t index) const; | ||
| 209 | |||
| 210 | // Returns the current mapped stick device | ||
| 211 | Common::ParamPackage GetStickParam(std::size_t index) const; | ||
| 212 | |||
| 213 | // Returns the current mapped motion device | ||
| 214 | Common::ParamPackage GetMotionParam(std::size_t index) const; | ||
| 215 | |||
| 216 | /** | ||
| 217 | * Updates the current mapped button device | ||
| 218 | * @param ParamPackage with controller data to be mapped | ||
| 219 | */ | ||
| 220 | void SetButtonParam(std::size_t index, Common::ParamPackage param); | ||
| 221 | |||
| 222 | /** | ||
| 223 | * Updates the current mapped stick device | ||
| 224 | * @param ParamPackage with controller data to be mapped | ||
| 225 | */ | ||
| 226 | void SetStickParam(std::size_t index, Common::ParamPackage param); | ||
| 227 | |||
| 228 | /** | ||
| 229 | * Updates the current mapped motion device | ||
| 230 | * @param ParamPackage with controller data to be mapped | ||
| 231 | */ | ||
| 232 | void SetMotionParam(std::size_t index, Common::ParamPackage param); | ||
| 233 | |||
| 234 | /// Returns the latest button status from the controller with parameters | ||
| 235 | ButtonValues GetButtonsValues() const; | ||
| 236 | |||
| 237 | /// Returns the latest analog stick status from the controller with parameters | ||
| 238 | SticksValues GetSticksValues() const; | ||
| 239 | |||
| 240 | /// Returns the latest trigger status from the controller with parameters | ||
| 241 | TriggerValues GetTriggersValues() const; | ||
| 242 | |||
| 243 | /// Returns the latest motion status from the controller with parameters | ||
| 244 | ControllerMotionValues GetMotionValues() const; | ||
| 245 | |||
| 246 | /// Returns the latest color status from the controller with parameters | ||
| 247 | ColorValues GetColorsValues() const; | ||
| 248 | |||
| 249 | /// Returns the latest battery status from the controller with parameters | ||
| 250 | BatteryValues GetBatteryValues() const; | ||
| 251 | |||
| 252 | /// Returns the latest status of button input for the npad service | ||
| 253 | NpadButtonState GetNpadButtons() const; | ||
| 254 | |||
| 255 | /// Returns the latest status of button input for the debug pad service | ||
| 256 | DebugPadButton GetDebugPadButtons() const; | ||
| 257 | |||
| 258 | /// Returns the latest status of stick input from the mouse | ||
| 259 | AnalogSticks GetSticks() const; | ||
| 260 | |||
| 261 | /// Returns the latest status of trigger input from the mouse | ||
| 262 | NpadGcTriggerState GetTriggers() const; | ||
| 263 | |||
| 264 | /// Returns the latest status of motion input from the mouse | ||
| 265 | MotionState GetMotions() const; | ||
| 266 | |||
| 267 | /// Returns the latest color value from the controller | ||
| 268 | ControllerColors GetColors() const; | ||
| 269 | |||
| 270 | /// Returns the latest battery status from the controller | ||
| 271 | BatteryLevelState GetBattery() const; | ||
| 272 | |||
| 273 | /* | ||
| 274 | * Sends a specific vibration to the output device | ||
| 275 | * @return returns true if vibration had no errors | ||
| 276 | */ | ||
| 277 | bool SetVibration(std::size_t device_index, VibrationValue vibration); | ||
| 278 | |||
| 279 | /* | ||
| 280 | * Sends a small vibration to the output device | ||
| 281 | * @return returns true if SetVibration was successfull | ||
| 282 | */ | ||
| 283 | bool TestVibration(std::size_t device_index); | ||
| 284 | |||
| 285 | /// Returns the led pattern corresponding to this emulated controller | ||
| 286 | LedPattern GetLedPattern() const; | ||
| 287 | |||
| 288 | /// Asks the output device to change the player led pattern | ||
| 289 | void SetLedPattern(); | ||
| 290 | |||
| 291 | /** | ||
| 292 | * Adds a callback to the list of events | ||
| 293 | * @param ConsoleUpdateCallback that will be triggered | ||
| 294 | * @return an unique key corresponding to the callback index in the list | ||
| 295 | */ | ||
| 296 | int SetCallback(ControllerUpdateCallback update_callback); | ||
| 297 | |||
| 298 | /** | ||
| 299 | * Removes a callback from the list stopping any future events to this object | ||
| 300 | * @param Key corresponding to the callback index in the list | ||
| 301 | */ | ||
| 302 | void DeleteCallback(int key); | ||
| 303 | |||
| 304 | private: | ||
| 305 | /// creates input devices from params | ||
| 306 | void LoadDevices(); | ||
| 307 | |||
| 308 | /// Set the params for TAS devices | ||
| 309 | void LoadTASParams(); | ||
| 310 | |||
| 311 | /** | ||
| 312 | * Updates the button status of the controller | ||
| 313 | * @param callback: A CallbackStatus containing the button status | ||
| 314 | * @param index: Button ID of the to be updated | ||
| 315 | */ | ||
| 316 | void SetButton(Common::Input::CallbackStatus callback, std::size_t index, Common::UUID uuid); | ||
| 317 | |||
| 318 | /** | ||
| 319 | * Updates the analog stick status of the controller | ||
| 320 | * @param callback: A CallbackStatus containing the analog stick status | ||
| 321 | * @param index: stick ID of the to be updated | ||
| 322 | */ | ||
| 323 | void SetStick(Common::Input::CallbackStatus callback, std::size_t index, Common::UUID uuid); | ||
| 324 | |||
| 325 | /** | ||
| 326 | * Updates the trigger status of the controller | ||
| 327 | * @param callback: A CallbackStatus containing the trigger status | ||
| 328 | * @param index: trigger ID of the to be updated | ||
| 329 | */ | ||
| 330 | void SetTrigger(Common::Input::CallbackStatus callback, std::size_t index, Common::UUID uuid); | ||
| 331 | |||
| 332 | /** | ||
| 333 | * Updates the motion status of the controller | ||
| 334 | * @param callback: A CallbackStatus containing gyro and accelerometer data | ||
| 335 | * @param index: motion ID of the to be updated | ||
| 336 | */ | ||
| 337 | void SetMotion(Common::Input::CallbackStatus callback, std::size_t index); | ||
| 338 | |||
| 339 | /** | ||
| 340 | * Updates the battery status of the controller | ||
| 341 | * @param callback: A CallbackStatus containing the battery status | ||
| 342 | * @param index: Button ID of the to be updated | ||
| 343 | */ | ||
| 344 | void SetBattery(Common::Input::CallbackStatus callback, std::size_t index); | ||
| 345 | |||
| 346 | /** | ||
| 347 | * Triggers a callback that something has changed on the controller status | ||
| 348 | * @param type: Input type of the event to trigger | ||
| 349 | * @param is_service_update: indicates if this event should be sended to only services | ||
| 350 | */ | ||
| 351 | void TriggerOnChange(ControllerTriggerType type, bool is_service_update); | ||
| 352 | |||
| 353 | NpadIdType npad_id_type; | ||
| 354 | NpadStyleIndex npad_type{NpadStyleIndex::None}; | ||
| 355 | bool is_connected{false}; | ||
| 356 | bool is_configuring{false}; | ||
| 357 | f32 motion_sensitivity{0.01f}; | ||
| 358 | bool force_update_motion{false}; | ||
| 359 | |||
| 360 | // Temporary values to avoid doing changes while the controller is on configuration mode | ||
| 361 | NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; | ||
| 362 | bool tmp_is_connected{false}; | ||
| 363 | |||
| 364 | ButtonParams button_params; | ||
| 365 | StickParams stick_params; | ||
| 366 | ControllerMotionParams motion_params; | ||
| 367 | TriggerParams trigger_params; | ||
| 368 | BatteryParams battery_params; | ||
| 369 | OutputParams output_params; | ||
| 370 | |||
| 371 | ButtonDevices button_devices; | ||
| 372 | StickDevices stick_devices; | ||
| 373 | ControllerMotionDevices motion_devices; | ||
| 374 | TriggerDevices trigger_devices; | ||
| 375 | BatteryDevices battery_devices; | ||
| 376 | OutputDevices output_devices; | ||
| 377 | |||
| 378 | // TAS related variables | ||
| 379 | ButtonParams tas_button_params; | ||
| 380 | StickParams tas_stick_params; | ||
| 381 | ButtonDevices tas_button_devices; | ||
| 382 | StickDevices tas_stick_devices; | ||
| 383 | |||
| 384 | mutable std::mutex mutex; | ||
| 385 | std::unordered_map<int, ControllerUpdateCallback> callback_list; | ||
| 386 | int last_callback_key = 0; | ||
| 387 | |||
| 388 | // Stores the current status of all controller input | ||
| 389 | ControllerStatus controller; | ||
| 390 | }; | ||
| 391 | |||
| 392 | } // namespace Core::HID | ||
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp new file mode 100644 index 000000000..874780ec2 --- /dev/null +++ b/src/core/hid/emulated_devices.cpp | |||
| @@ -0,0 +1,451 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <fmt/format.h> | ||
| 7 | |||
| 8 | #include "core/hid/emulated_devices.h" | ||
| 9 | #include "core/hid/input_converter.h" | ||
| 10 | |||
| 11 | namespace Core::HID { | ||
| 12 | |||
| 13 | EmulatedDevices::EmulatedDevices() = default; | ||
| 14 | |||
| 15 | EmulatedDevices::~EmulatedDevices() = default; | ||
| 16 | |||
| 17 | void EmulatedDevices::ReloadFromSettings() { | ||
| 18 | ReloadInput(); | ||
| 19 | } | ||
| 20 | |||
| 21 | void EmulatedDevices::ReloadInput() { | ||
| 22 | // If you load any device here add the equivalent to the UnloadInput() function | ||
| 23 | std::size_t key_index = 0; | ||
| 24 | for (auto& mouse_device : mouse_button_devices) { | ||
| 25 | Common::ParamPackage mouse_params; | ||
| 26 | mouse_params.Set("engine", "mouse"); | ||
| 27 | mouse_params.Set("button", static_cast<int>(key_index)); | ||
| 28 | mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params); | ||
| 29 | key_index++; | ||
| 30 | } | ||
| 31 | |||
| 32 | mouse_stick_device = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( | ||
| 33 | "engine:mouse,axis_x:0,axis_y:1"); | ||
| 34 | |||
| 35 | // First two axis are reserved for mouse position | ||
| 36 | key_index = 2; | ||
| 37 | for (auto& mouse_device : mouse_analog_devices) { | ||
| 38 | Common::ParamPackage mouse_params; | ||
| 39 | mouse_params.Set("engine", "mouse"); | ||
| 40 | mouse_params.Set("axis", static_cast<int>(key_index)); | ||
| 41 | mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params); | ||
| 42 | key_index++; | ||
| 43 | } | ||
| 44 | |||
| 45 | key_index = 0; | ||
| 46 | for (auto& keyboard_device : keyboard_devices) { | ||
| 47 | // Keyboard keys are only mapped on port 1, pad 0 | ||
| 48 | Common::ParamPackage keyboard_params; | ||
| 49 | keyboard_params.Set("engine", "keyboard"); | ||
| 50 | keyboard_params.Set("button", static_cast<int>(key_index)); | ||
| 51 | keyboard_params.Set("port", 1); | ||
| 52 | keyboard_params.Set("pad", 0); | ||
| 53 | keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params); | ||
| 54 | key_index++; | ||
| 55 | } | ||
| 56 | |||
| 57 | key_index = 0; | ||
| 58 | for (auto& keyboard_device : keyboard_modifier_devices) { | ||
| 59 | // Keyboard moddifiers are only mapped on port 1, pad 1 | ||
| 60 | Common::ParamPackage keyboard_params; | ||
| 61 | keyboard_params.Set("engine", "keyboard"); | ||
| 62 | keyboard_params.Set("button", static_cast<int>(key_index)); | ||
| 63 | keyboard_params.Set("port", 1); | ||
| 64 | keyboard_params.Set("pad", 1); | ||
| 65 | keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params); | ||
| 66 | key_index++; | ||
| 67 | } | ||
| 68 | |||
| 69 | for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { | ||
| 70 | if (!mouse_button_devices[index]) { | ||
| 71 | continue; | ||
| 72 | } | ||
| 73 | Common::Input::InputCallback button_callback{ | ||
| 74 | [this, index](Common::Input::CallbackStatus callback) { | ||
| 75 | SetMouseButton(callback, index); | ||
| 76 | }}; | ||
| 77 | mouse_button_devices[index]->SetCallback(button_callback); | ||
| 78 | } | ||
| 79 | |||
| 80 | for (std::size_t index = 0; index < mouse_analog_devices.size(); ++index) { | ||
| 81 | if (!mouse_analog_devices[index]) { | ||
| 82 | continue; | ||
| 83 | } | ||
| 84 | Common::Input::InputCallback button_callback{ | ||
| 85 | [this, index](Common::Input::CallbackStatus callback) { | ||
| 86 | SetMouseAnalog(callback, index); | ||
| 87 | }}; | ||
| 88 | mouse_analog_devices[index]->SetCallback(button_callback); | ||
| 89 | } | ||
| 90 | |||
| 91 | if (mouse_stick_device) { | ||
| 92 | Common::Input::InputCallback button_callback{ | ||
| 93 | [this](Common::Input::CallbackStatus callback) { SetMouseStick(callback); }}; | ||
| 94 | mouse_stick_device->SetCallback(button_callback); | ||
| 95 | } | ||
| 96 | |||
| 97 | for (std::size_t index = 0; index < keyboard_devices.size(); ++index) { | ||
| 98 | if (!keyboard_devices[index]) { | ||
| 99 | continue; | ||
| 100 | } | ||
| 101 | Common::Input::InputCallback button_callback{ | ||
| 102 | [this, index](Common::Input::CallbackStatus callback) { | ||
| 103 | SetKeyboardButton(callback, index); | ||
| 104 | }}; | ||
| 105 | keyboard_devices[index]->SetCallback(button_callback); | ||
| 106 | } | ||
| 107 | |||
| 108 | for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) { | ||
| 109 | if (!keyboard_modifier_devices[index]) { | ||
| 110 | continue; | ||
| 111 | } | ||
| 112 | Common::Input::InputCallback button_callback{ | ||
| 113 | [this, index](Common::Input::CallbackStatus callback) { | ||
| 114 | SetKeyboardModifier(callback, index); | ||
| 115 | }}; | ||
| 116 | keyboard_modifier_devices[index]->SetCallback(button_callback); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | void EmulatedDevices::UnloadInput() { | ||
| 121 | for (auto& button : mouse_button_devices) { | ||
| 122 | button.reset(); | ||
| 123 | } | ||
| 124 | for (auto& analog : mouse_analog_devices) { | ||
| 125 | analog.reset(); | ||
| 126 | } | ||
| 127 | mouse_stick_device.reset(); | ||
| 128 | for (auto& button : keyboard_devices) { | ||
| 129 | button.reset(); | ||
| 130 | } | ||
| 131 | for (auto& button : keyboard_modifier_devices) { | ||
| 132 | button.reset(); | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | void EmulatedDevices::EnableConfiguration() { | ||
| 137 | is_configuring = true; | ||
| 138 | SaveCurrentConfig(); | ||
| 139 | } | ||
| 140 | |||
| 141 | void EmulatedDevices::DisableConfiguration() { | ||
| 142 | is_configuring = false; | ||
| 143 | } | ||
| 144 | |||
| 145 | bool EmulatedDevices::IsConfiguring() const { | ||
| 146 | return is_configuring; | ||
| 147 | } | ||
| 148 | |||
| 149 | void EmulatedDevices::SaveCurrentConfig() { | ||
| 150 | if (!is_configuring) { | ||
| 151 | return; | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | void EmulatedDevices::RestoreConfig() { | ||
| 156 | if (!is_configuring) { | ||
| 157 | return; | ||
| 158 | } | ||
| 159 | ReloadFromSettings(); | ||
| 160 | } | ||
| 161 | |||
| 162 | void EmulatedDevices::SetKeyboardButton(Common::Input::CallbackStatus callback, std::size_t index) { | ||
| 163 | if (index >= device_status.keyboard_values.size()) { | ||
| 164 | return; | ||
| 165 | } | ||
| 166 | std::lock_guard lock{mutex}; | ||
| 167 | bool value_changed = false; | ||
| 168 | const auto new_status = TransformToButton(callback); | ||
| 169 | auto& current_status = device_status.keyboard_values[index]; | ||
| 170 | current_status.toggle = new_status.toggle; | ||
| 171 | |||
| 172 | // Update button status with current status | ||
| 173 | if (!current_status.toggle) { | ||
| 174 | current_status.locked = false; | ||
| 175 | if (current_status.value != new_status.value) { | ||
| 176 | current_status.value = new_status.value; | ||
| 177 | value_changed = true; | ||
| 178 | } | ||
| 179 | } else { | ||
| 180 | // Toggle button and lock status | ||
| 181 | if (new_status.value && !current_status.locked) { | ||
| 182 | current_status.locked = true; | ||
| 183 | current_status.value = !current_status.value; | ||
| 184 | value_changed = true; | ||
| 185 | } | ||
| 186 | |||
| 187 | // Unlock button, ready for next press | ||
| 188 | if (!new_status.value && current_status.locked) { | ||
| 189 | current_status.locked = false; | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | if (!value_changed) { | ||
| 194 | return; | ||
| 195 | } | ||
| 196 | |||
| 197 | if (is_configuring) { | ||
| 198 | TriggerOnChange(DeviceTriggerType::Keyboard); | ||
| 199 | return; | ||
| 200 | } | ||
| 201 | |||
| 202 | // Index should be converted from NativeKeyboard to KeyboardKeyIndex | ||
| 203 | UpdateKey(index, current_status.value); | ||
| 204 | |||
| 205 | TriggerOnChange(DeviceTriggerType::Keyboard); | ||
| 206 | } | ||
| 207 | |||
| 208 | void EmulatedDevices::UpdateKey(std::size_t key_index, bool status) { | ||
| 209 | constexpr std::size_t KEYS_PER_BYTE = 8; | ||
| 210 | auto& entry = device_status.keyboard_state.key[key_index / KEYS_PER_BYTE]; | ||
| 211 | const u8 mask = static_cast<u8>(1 << (key_index % KEYS_PER_BYTE)); | ||
| 212 | if (status) { | ||
| 213 | entry = entry | mask; | ||
| 214 | } else { | ||
| 215 | entry = static_cast<u8>(entry & ~mask); | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | void EmulatedDevices::SetKeyboardModifier(Common::Input::CallbackStatus callback, | ||
| 220 | std::size_t index) { | ||
| 221 | if (index >= device_status.keyboard_moddifier_values.size()) { | ||
| 222 | return; | ||
| 223 | } | ||
| 224 | std::lock_guard lock{mutex}; | ||
| 225 | bool value_changed = false; | ||
| 226 | const auto new_status = TransformToButton(callback); | ||
| 227 | auto& current_status = device_status.keyboard_moddifier_values[index]; | ||
| 228 | current_status.toggle = new_status.toggle; | ||
| 229 | |||
| 230 | // Update button status with current | ||
| 231 | if (!current_status.toggle) { | ||
| 232 | current_status.locked = false; | ||
| 233 | if (current_status.value != new_status.value) { | ||
| 234 | current_status.value = new_status.value; | ||
| 235 | value_changed = true; | ||
| 236 | } | ||
| 237 | } else { | ||
| 238 | // Toggle button and lock status | ||
| 239 | if (new_status.value && !current_status.locked) { | ||
| 240 | current_status.locked = true; | ||
| 241 | current_status.value = !current_status.value; | ||
| 242 | value_changed = true; | ||
| 243 | } | ||
| 244 | |||
| 245 | // Unlock button ready for next press | ||
| 246 | if (!new_status.value && current_status.locked) { | ||
| 247 | current_status.locked = false; | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | if (!value_changed) { | ||
| 252 | return; | ||
| 253 | } | ||
| 254 | |||
| 255 | if (is_configuring) { | ||
| 256 | TriggerOnChange(DeviceTriggerType::KeyboardModdifier); | ||
| 257 | return; | ||
| 258 | } | ||
| 259 | |||
| 260 | switch (index) { | ||
| 261 | case Settings::NativeKeyboard::LeftControl: | ||
| 262 | case Settings::NativeKeyboard::RightControl: | ||
| 263 | device_status.keyboard_moddifier_state.control.Assign(current_status.value); | ||
| 264 | break; | ||
| 265 | case Settings::NativeKeyboard::LeftShift: | ||
| 266 | case Settings::NativeKeyboard::RightShift: | ||
| 267 | device_status.keyboard_moddifier_state.shift.Assign(current_status.value); | ||
| 268 | break; | ||
| 269 | case Settings::NativeKeyboard::LeftAlt: | ||
| 270 | device_status.keyboard_moddifier_state.left_alt.Assign(current_status.value); | ||
| 271 | break; | ||
| 272 | case Settings::NativeKeyboard::RightAlt: | ||
| 273 | device_status.keyboard_moddifier_state.right_alt.Assign(current_status.value); | ||
| 274 | break; | ||
| 275 | case Settings::NativeKeyboard::CapsLock: | ||
| 276 | device_status.keyboard_moddifier_state.caps_lock.Assign(current_status.value); | ||
| 277 | break; | ||
| 278 | case Settings::NativeKeyboard::ScrollLock: | ||
| 279 | device_status.keyboard_moddifier_state.scroll_lock.Assign(current_status.value); | ||
| 280 | break; | ||
| 281 | case Settings::NativeKeyboard::NumLock: | ||
| 282 | device_status.keyboard_moddifier_state.num_lock.Assign(current_status.value); | ||
| 283 | break; | ||
| 284 | } | ||
| 285 | |||
| 286 | TriggerOnChange(DeviceTriggerType::KeyboardModdifier); | ||
| 287 | } | ||
| 288 | |||
| 289 | void EmulatedDevices::SetMouseButton(Common::Input::CallbackStatus callback, std::size_t index) { | ||
| 290 | if (index >= device_status.mouse_button_values.size()) { | ||
| 291 | return; | ||
| 292 | } | ||
| 293 | std::lock_guard lock{mutex}; | ||
| 294 | bool value_changed = false; | ||
| 295 | const auto new_status = TransformToButton(callback); | ||
| 296 | auto& current_status = device_status.mouse_button_values[index]; | ||
| 297 | current_status.toggle = new_status.toggle; | ||
| 298 | |||
| 299 | // Update button status with current | ||
| 300 | if (!current_status.toggle) { | ||
| 301 | current_status.locked = false; | ||
| 302 | if (current_status.value != new_status.value) { | ||
| 303 | current_status.value = new_status.value; | ||
| 304 | value_changed = true; | ||
| 305 | } | ||
| 306 | } else { | ||
| 307 | // Toggle button and lock status | ||
| 308 | if (new_status.value && !current_status.locked) { | ||
| 309 | current_status.locked = true; | ||
| 310 | current_status.value = !current_status.value; | ||
| 311 | value_changed = true; | ||
| 312 | } | ||
| 313 | |||
| 314 | // Unlock button ready for next press | ||
| 315 | if (!new_status.value && current_status.locked) { | ||
| 316 | current_status.locked = false; | ||
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | if (!value_changed) { | ||
| 321 | return; | ||
| 322 | } | ||
| 323 | |||
| 324 | if (is_configuring) { | ||
| 325 | TriggerOnChange(DeviceTriggerType::Mouse); | ||
| 326 | return; | ||
| 327 | } | ||
| 328 | |||
| 329 | switch (index) { | ||
| 330 | case Settings::NativeMouseButton::Left: | ||
| 331 | device_status.mouse_button_state.left.Assign(current_status.value); | ||
| 332 | break; | ||
| 333 | case Settings::NativeMouseButton::Right: | ||
| 334 | device_status.mouse_button_state.right.Assign(current_status.value); | ||
| 335 | break; | ||
| 336 | case Settings::NativeMouseButton::Middle: | ||
| 337 | device_status.mouse_button_state.middle.Assign(current_status.value); | ||
| 338 | break; | ||
| 339 | case Settings::NativeMouseButton::Forward: | ||
| 340 | device_status.mouse_button_state.forward.Assign(current_status.value); | ||
| 341 | break; | ||
| 342 | case Settings::NativeMouseButton::Back: | ||
| 343 | device_status.mouse_button_state.back.Assign(current_status.value); | ||
| 344 | break; | ||
| 345 | } | ||
| 346 | |||
| 347 | TriggerOnChange(DeviceTriggerType::Mouse); | ||
| 348 | } | ||
| 349 | |||
| 350 | void EmulatedDevices::SetMouseAnalog(Common::Input::CallbackStatus callback, std::size_t index) { | ||
| 351 | if (index >= device_status.mouse_analog_values.size()) { | ||
| 352 | return; | ||
| 353 | } | ||
| 354 | std::lock_guard lock{mutex}; | ||
| 355 | const auto analog_value = TransformToAnalog(callback); | ||
| 356 | |||
| 357 | device_status.mouse_analog_values[index] = analog_value; | ||
| 358 | |||
| 359 | if (is_configuring) { | ||
| 360 | device_status.mouse_position_state = {}; | ||
| 361 | TriggerOnChange(DeviceTriggerType::Mouse); | ||
| 362 | return; | ||
| 363 | } | ||
| 364 | |||
| 365 | switch (index) { | ||
| 366 | case Settings::NativeMouseWheel::X: | ||
| 367 | device_status.mouse_wheel_state.x = static_cast<s32>(analog_value.value); | ||
| 368 | break; | ||
| 369 | case Settings::NativeMouseWheel::Y: | ||
| 370 | device_status.mouse_wheel_state.y = static_cast<s32>(analog_value.value); | ||
| 371 | break; | ||
| 372 | } | ||
| 373 | |||
| 374 | TriggerOnChange(DeviceTriggerType::Mouse); | ||
| 375 | } | ||
| 376 | |||
| 377 | void EmulatedDevices::SetMouseStick(Common::Input::CallbackStatus callback) { | ||
| 378 | std::lock_guard lock{mutex}; | ||
| 379 | const auto touch_value = TransformToTouch(callback); | ||
| 380 | |||
| 381 | device_status.mouse_stick_value = touch_value; | ||
| 382 | |||
| 383 | if (is_configuring) { | ||
| 384 | device_status.mouse_position_state = {}; | ||
| 385 | TriggerOnChange(DeviceTriggerType::Mouse); | ||
| 386 | return; | ||
| 387 | } | ||
| 388 | |||
| 389 | device_status.mouse_position_state.x = touch_value.x.value; | ||
| 390 | device_status.mouse_position_state.y = touch_value.y.value; | ||
| 391 | |||
| 392 | TriggerOnChange(DeviceTriggerType::Mouse); | ||
| 393 | } | ||
| 394 | |||
| 395 | KeyboardValues EmulatedDevices::GetKeyboardValues() const { | ||
| 396 | return device_status.keyboard_values; | ||
| 397 | } | ||
| 398 | |||
| 399 | KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const { | ||
| 400 | return device_status.keyboard_moddifier_values; | ||
| 401 | } | ||
| 402 | |||
| 403 | MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const { | ||
| 404 | return device_status.mouse_button_values; | ||
| 405 | } | ||
| 406 | |||
| 407 | KeyboardKey EmulatedDevices::GetKeyboard() const { | ||
| 408 | return device_status.keyboard_state; | ||
| 409 | } | ||
| 410 | |||
| 411 | KeyboardModifier EmulatedDevices::GetKeyboardModifier() const { | ||
| 412 | return device_status.keyboard_moddifier_state; | ||
| 413 | } | ||
| 414 | |||
| 415 | MouseButton EmulatedDevices::GetMouseButtons() const { | ||
| 416 | return device_status.mouse_button_state; | ||
| 417 | } | ||
| 418 | |||
| 419 | MousePosition EmulatedDevices::GetMousePosition() const { | ||
| 420 | return device_status.mouse_position_state; | ||
| 421 | } | ||
| 422 | |||
| 423 | AnalogStickState EmulatedDevices::GetMouseWheel() const { | ||
| 424 | return device_status.mouse_wheel_state; | ||
| 425 | } | ||
| 426 | |||
| 427 | void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { | ||
| 428 | for (const auto& poller_pair : callback_list) { | ||
| 429 | const InterfaceUpdateCallback& poller = poller_pair.second; | ||
| 430 | if (poller.on_change) { | ||
| 431 | poller.on_change(type); | ||
| 432 | } | ||
| 433 | } | ||
| 434 | } | ||
| 435 | |||
| 436 | int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) { | ||
| 437 | std::lock_guard lock{mutex}; | ||
| 438 | callback_list.insert_or_assign(last_callback_key, update_callback); | ||
| 439 | return last_callback_key++; | ||
| 440 | } | ||
| 441 | |||
| 442 | void EmulatedDevices::DeleteCallback(int key) { | ||
| 443 | std::lock_guard lock{mutex}; | ||
| 444 | const auto& iterator = callback_list.find(key); | ||
| 445 | if (iterator == callback_list.end()) { | ||
| 446 | LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); | ||
| 447 | return; | ||
| 448 | } | ||
| 449 | callback_list.erase(iterator); | ||
| 450 | } | ||
| 451 | } // namespace Core::HID | ||
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h new file mode 100644 index 000000000..05a945d08 --- /dev/null +++ b/src/core/hid/emulated_devices.h | |||
| @@ -0,0 +1,209 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <functional> | ||
| 9 | #include <memory> | ||
| 10 | #include <mutex> | ||
| 11 | #include <unordered_map> | ||
| 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 MouseAnalogDevices = 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 MouseAnalogValues = | ||
| 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 | MouseAnalogValues mouse_analog_values{}; | ||
| 54 | MouseStickValue mouse_stick_value{}; | ||
| 55 | |||
| 56 | // Data for HID serices | ||
| 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 | }; | ||
| 69 | |||
| 70 | struct InterfaceUpdateCallback { | ||
| 71 | std::function<void(DeviceTriggerType)> on_change; | ||
| 72 | }; | ||
| 73 | |||
| 74 | class EmulatedDevices { | ||
| 75 | public: | ||
| 76 | /** | ||
| 77 | * Contains all input data related to external devices that aren't necesarily a controller | ||
| 78 | * like keyboard and mouse | ||
| 79 | */ | ||
| 80 | EmulatedDevices(); | ||
| 81 | ~EmulatedDevices(); | ||
| 82 | |||
| 83 | YUZU_NON_COPYABLE(EmulatedDevices); | ||
| 84 | YUZU_NON_MOVEABLE(EmulatedDevices); | ||
| 85 | |||
| 86 | /// Removes all callbacks created from input devices | ||
| 87 | void UnloadInput(); | ||
| 88 | |||
| 89 | /// Sets the emulated console into configuring mode. Locking all HID service events from being | ||
| 90 | /// moddified | ||
| 91 | void EnableConfiguration(); | ||
| 92 | |||
| 93 | /// Returns the emulated console to the normal behaivour | ||
| 94 | void DisableConfiguration(); | ||
| 95 | |||
| 96 | /// Returns true if the emulated device is on configuring mode | ||
| 97 | bool IsConfiguring() const; | ||
| 98 | |||
| 99 | /// Reload all input devices | ||
| 100 | void ReloadInput(); | ||
| 101 | |||
| 102 | /// Overrides current mapped devices with the stored configuration and reloads all input devices | ||
| 103 | void ReloadFromSettings(); | ||
| 104 | |||
| 105 | /// Saves the current mapped configuration | ||
| 106 | void SaveCurrentConfig(); | ||
| 107 | |||
| 108 | /// Reverts any mapped changes made that weren't saved | ||
| 109 | void RestoreConfig(); | ||
| 110 | |||
| 111 | /// Returns the latest status of button input from the keyboard with parameters | ||
| 112 | KeyboardValues GetKeyboardValues() const; | ||
| 113 | |||
| 114 | /// Returns the latest status of button input from the keyboard modifiers with parameters | ||
| 115 | KeyboardModifierValues GetKeyboardModdifierValues() const; | ||
| 116 | |||
| 117 | /// Returns the latest status of button input from the mouse with parameters | ||
| 118 | MouseButtonValues GetMouseButtonsValues() const; | ||
| 119 | |||
| 120 | /// Returns the latest status of button input from the keyboard | ||
| 121 | KeyboardKey GetKeyboard() const; | ||
| 122 | |||
| 123 | /// Returns the latest status of button input from the keyboard modifiers | ||
| 124 | KeyboardModifier GetKeyboardModifier() const; | ||
| 125 | |||
| 126 | /// Returns the latest status of button input from the mouse | ||
| 127 | MouseButton GetMouseButtons() const; | ||
| 128 | |||
| 129 | /// Returns the latest mouse coordinates | ||
| 130 | MousePosition GetMousePosition() const; | ||
| 131 | |||
| 132 | /// Returns the latest mouse wheel change | ||
| 133 | AnalogStickState GetMouseWheel() const; | ||
| 134 | |||
| 135 | /** | ||
| 136 | * Adds a callback to the list of events | ||
| 137 | * @param InterfaceUpdateCallback that will be triggered | ||
| 138 | * @return an unique key corresponding to the callback index in the list | ||
| 139 | */ | ||
| 140 | int SetCallback(InterfaceUpdateCallback update_callback); | ||
| 141 | |||
| 142 | /** | ||
| 143 | * Removes a callback from the list stopping any future events to this object | ||
| 144 | * @param Key corresponding to the callback index in the list | ||
| 145 | */ | ||
| 146 | void DeleteCallback(int key); | ||
| 147 | |||
| 148 | private: | ||
| 149 | /// Helps assigning a value to keyboard_state | ||
| 150 | void UpdateKey(std::size_t key_index, bool status); | ||
| 151 | |||
| 152 | /** | ||
| 153 | * Updates the touch status of the keyboard device | ||
| 154 | * @param callback: A CallbackStatus containing the key status | ||
| 155 | * @param index: key ID to be updated | ||
| 156 | */ | ||
| 157 | void SetKeyboardButton(Common::Input::CallbackStatus callback, std::size_t index); | ||
| 158 | |||
| 159 | /** | ||
| 160 | * Updates the keyboard status of the keyboard device | ||
| 161 | * @param callback: A CallbackStatus containing the modifier key status | ||
| 162 | * @param index: modifier key ID to be updated | ||
| 163 | */ | ||
| 164 | void SetKeyboardModifier(Common::Input::CallbackStatus callback, std::size_t index); | ||
| 165 | |||
| 166 | /** | ||
| 167 | * Updates the mouse button status of the mouse device | ||
| 168 | * @param callback: A CallbackStatus containing the button status | ||
| 169 | * @param index: Button ID to be updated | ||
| 170 | */ | ||
| 171 | void SetMouseButton(Common::Input::CallbackStatus callback, std::size_t index); | ||
| 172 | |||
| 173 | /** | ||
| 174 | * Updates the mouse wheel status of the mouse device | ||
| 175 | * @param callback: A CallbackStatus containing the wheel status | ||
| 176 | * @param index: wheel ID to be updated | ||
| 177 | */ | ||
| 178 | void SetMouseAnalog(Common::Input::CallbackStatus callback, std::size_t index); | ||
| 179 | |||
| 180 | /** | ||
| 181 | * Updates the mouse position status of the mouse device | ||
| 182 | * @param callback: A CallbackStatus containing the position status | ||
| 183 | * @param index: stick ID to be updated | ||
| 184 | */ | ||
| 185 | void SetMouseStick(Common::Input::CallbackStatus callback); | ||
| 186 | |||
| 187 | /** | ||
| 188 | * Triggers a callback that something has changed on the device status | ||
| 189 | * @param Input type of the event to trigger | ||
| 190 | */ | ||
| 191 | void TriggerOnChange(DeviceTriggerType type); | ||
| 192 | |||
| 193 | bool is_configuring{false}; | ||
| 194 | |||
| 195 | KeyboardDevices keyboard_devices; | ||
| 196 | KeyboardModifierDevices keyboard_modifier_devices; | ||
| 197 | MouseButtonDevices mouse_button_devices; | ||
| 198 | MouseAnalogDevices mouse_analog_devices; | ||
| 199 | MouseStickDevice mouse_stick_device; | ||
| 200 | |||
| 201 | mutable std::mutex mutex; | ||
| 202 | std::unordered_map<int, InterfaceUpdateCallback> callback_list; | ||
| 203 | int last_callback_key = 0; | ||
| 204 | |||
| 205 | // Stores the current status of all external device input | ||
| 206 | DeviceStatus device_status; | ||
| 207 | }; | ||
| 208 | |||
| 209 | } // namespace Core::HID | ||
diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp new file mode 100644 index 000000000..741a69c3c --- /dev/null +++ b/src/core/hid/hid_core.cpp | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "core/hid/emulated_console.h" | ||
| 7 | #include "core/hid/emulated_controller.h" | ||
| 8 | #include "core/hid/emulated_devices.h" | ||
| 9 | #include "core/hid/hid_core.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 | UNREACHABLE_MSG("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 | UNREACHABLE_MSG("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(IndexToNpadIdType(index)); | ||
| 103 | } | ||
| 104 | |||
| 105 | const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const { | ||
| 106 | return GetEmulatedController(IndexToNpadIdType(index)); | ||
| 107 | } | ||
| 108 | |||
| 109 | void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) { | ||
| 110 | supported_style_tag.raw = style_tag.raw; | ||
| 111 | } | ||
| 112 | |||
| 113 | NpadStyleTag HIDCore::GetSupportedStyleTag() const { | ||
| 114 | return supported_style_tag; | ||
| 115 | } | ||
| 116 | |||
| 117 | s8 HIDCore::GetPlayerCount() const { | ||
| 118 | s8 active_players = 0; | ||
| 119 | for (std::size_t player_index = 0; player_index < available_controllers - 2; ++player_index) { | ||
| 120 | const auto* const controller = GetEmulatedControllerByIndex(player_index); | ||
| 121 | if (controller->IsConnected()) { | ||
| 122 | active_players++; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | return active_players; | ||
| 126 | } | ||
| 127 | |||
| 128 | NpadIdType HIDCore::GetFirstNpadId() const { | ||
| 129 | for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) { | ||
| 130 | const auto* const controller = GetEmulatedControllerByIndex(player_index); | ||
| 131 | if (controller->IsConnected()) { | ||
| 132 | return controller->GetNpadIdType(); | ||
| 133 | } | ||
| 134 | } | ||
| 135 | return NpadIdType::Player1; | ||
| 136 | } | ||
| 137 | |||
| 138 | void HIDCore::ReloadInputDevices() { | ||
| 139 | player_1->ReloadFromSettings(); | ||
| 140 | player_2->ReloadFromSettings(); | ||
| 141 | player_3->ReloadFromSettings(); | ||
| 142 | player_4->ReloadFromSettings(); | ||
| 143 | player_5->ReloadFromSettings(); | ||
| 144 | player_6->ReloadFromSettings(); | ||
| 145 | player_7->ReloadFromSettings(); | ||
| 146 | player_8->ReloadFromSettings(); | ||
| 147 | other->ReloadFromSettings(); | ||
| 148 | handheld->ReloadFromSettings(); | ||
| 149 | console->ReloadFromSettings(); | ||
| 150 | devices->ReloadFromSettings(); | ||
| 151 | } | ||
| 152 | |||
| 153 | void HIDCore::UnloadInputDevices() { | ||
| 154 | player_1->UnloadInput(); | ||
| 155 | player_2->UnloadInput(); | ||
| 156 | player_3->UnloadInput(); | ||
| 157 | player_4->UnloadInput(); | ||
| 158 | player_5->UnloadInput(); | ||
| 159 | player_6->UnloadInput(); | ||
| 160 | player_7->UnloadInput(); | ||
| 161 | player_8->UnloadInput(); | ||
| 162 | other->UnloadInput(); | ||
| 163 | handheld->UnloadInput(); | ||
| 164 | console->UnloadInput(); | ||
| 165 | devices->UnloadInput(); | ||
| 166 | } | ||
| 167 | |||
| 168 | } // namespace Core::HID | ||
diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h new file mode 100644 index 000000000..609f40f3b --- /dev/null +++ b/src/core/hid/hid_core.h | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | |||
| 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 | /// Reloads all input devices from settings | ||
| 49 | void ReloadInputDevices(); | ||
| 50 | |||
| 51 | /// Removes all callbacks from input common | ||
| 52 | void UnloadInputDevices(); | ||
| 53 | |||
| 54 | /// Number of emulated controllers | ||
| 55 | static constexpr std::size_t available_controllers{10}; | ||
| 56 | |||
| 57 | private: | ||
| 58 | std::unique_ptr<EmulatedController> player_1; | ||
| 59 | std::unique_ptr<EmulatedController> player_2; | ||
| 60 | std::unique_ptr<EmulatedController> player_3; | ||
| 61 | std::unique_ptr<EmulatedController> player_4; | ||
| 62 | std::unique_ptr<EmulatedController> player_5; | ||
| 63 | std::unique_ptr<EmulatedController> player_6; | ||
| 64 | std::unique_ptr<EmulatedController> player_7; | ||
| 65 | std::unique_ptr<EmulatedController> player_8; | ||
| 66 | std::unique_ptr<EmulatedController> other; | ||
| 67 | std::unique_ptr<EmulatedController> handheld; | ||
| 68 | std::unique_ptr<EmulatedConsole> console; | ||
| 69 | std::unique_ptr<EmulatedDevices> devices; | ||
| 70 | NpadStyleTag supported_style_tag; | ||
| 71 | }; | ||
| 72 | |||
| 73 | } // namespace Core::HID | ||
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h new file mode 100644 index 000000000..acf54e233 --- /dev/null +++ b/src/core/hid/hid_types.h | |||
| @@ -0,0 +1,631 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/bit_field.h" | ||
| 8 | #include "common/common_funcs.h" | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "common/point.h" | ||
| 11 | #include "common/uuid.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 | DECLARE_ENUM_FLAG_OPERATORS(NpadButton); | ||
| 68 | |||
| 69 | enum class KeyboardKeyIndex : u32 { | ||
| 70 | A = 4, | ||
| 71 | B = 5, | ||
| 72 | C = 6, | ||
| 73 | D = 7, | ||
| 74 | E = 8, | ||
| 75 | F = 9, | ||
| 76 | G = 10, | ||
| 77 | H = 11, | ||
| 78 | I = 12, | ||
| 79 | J = 13, | ||
| 80 | K = 14, | ||
| 81 | L = 15, | ||
| 82 | M = 16, | ||
| 83 | N = 17, | ||
| 84 | O = 18, | ||
| 85 | P = 19, | ||
| 86 | Q = 20, | ||
| 87 | R = 21, | ||
| 88 | S = 22, | ||
| 89 | T = 23, | ||
| 90 | U = 24, | ||
| 91 | V = 25, | ||
| 92 | W = 26, | ||
| 93 | X = 27, | ||
| 94 | Y = 28, | ||
| 95 | Z = 29, | ||
| 96 | D1 = 30, | ||
| 97 | D2 = 31, | ||
| 98 | D3 = 32, | ||
| 99 | D4 = 33, | ||
| 100 | D5 = 34, | ||
| 101 | D6 = 35, | ||
| 102 | D7 = 36, | ||
| 103 | D8 = 37, | ||
| 104 | D9 = 38, | ||
| 105 | D0 = 39, | ||
| 106 | Return = 40, | ||
| 107 | Escape = 41, | ||
| 108 | Backspace = 42, | ||
| 109 | Tab = 43, | ||
| 110 | Space = 44, | ||
| 111 | Minus = 45, | ||
| 112 | Plus = 46, | ||
| 113 | OpenBracket = 47, | ||
| 114 | CloseBracket = 48, | ||
| 115 | Pipe = 49, | ||
| 116 | Tilde = 50, | ||
| 117 | Semicolon = 51, | ||
| 118 | Quote = 52, | ||
| 119 | Backquote = 53, | ||
| 120 | Comma = 54, | ||
| 121 | Period = 55, | ||
| 122 | Slash = 56, | ||
| 123 | CapsLock = 57, | ||
| 124 | F1 = 58, | ||
| 125 | F2 = 59, | ||
| 126 | F3 = 60, | ||
| 127 | F4 = 61, | ||
| 128 | F5 = 62, | ||
| 129 | F6 = 63, | ||
| 130 | F7 = 64, | ||
| 131 | F8 = 65, | ||
| 132 | F9 = 66, | ||
| 133 | F10 = 67, | ||
| 134 | F11 = 68, | ||
| 135 | F12 = 69, | ||
| 136 | PrintScreen = 70, | ||
| 137 | ScrollLock = 71, | ||
| 138 | Pause = 72, | ||
| 139 | Insert = 73, | ||
| 140 | Home = 74, | ||
| 141 | PageUp = 75, | ||
| 142 | Delete = 76, | ||
| 143 | End = 77, | ||
| 144 | PageDown = 78, | ||
| 145 | RightArrow = 79, | ||
| 146 | LeftArrow = 80, | ||
| 147 | DownArrow = 81, | ||
| 148 | UpArrow = 82, | ||
| 149 | NumLock = 83, | ||
| 150 | NumPadDivide = 84, | ||
| 151 | NumPadMultiply = 85, | ||
| 152 | NumPadSubtract = 86, | ||
| 153 | NumPadAdd = 87, | ||
| 154 | NumPadEnter = 88, | ||
| 155 | NumPad1 = 89, | ||
| 156 | NumPad2 = 90, | ||
| 157 | NumPad3 = 91, | ||
| 158 | NumPad4 = 92, | ||
| 159 | NumPad5 = 93, | ||
| 160 | NumPad6 = 94, | ||
| 161 | NumPad7 = 95, | ||
| 162 | NumPad8 = 96, | ||
| 163 | NumPad9 = 97, | ||
| 164 | NumPad0 = 98, | ||
| 165 | NumPadDot = 99, | ||
| 166 | Backslash = 100, | ||
| 167 | Application = 101, | ||
| 168 | Power = 102, | ||
| 169 | NumPadEquals = 103, | ||
| 170 | F13 = 104, | ||
| 171 | F14 = 105, | ||
| 172 | F15 = 106, | ||
| 173 | F16 = 107, | ||
| 174 | F17 = 108, | ||
| 175 | F18 = 109, | ||
| 176 | F19 = 110, | ||
| 177 | F20 = 111, | ||
| 178 | F21 = 112, | ||
| 179 | F22 = 113, | ||
| 180 | F23 = 114, | ||
| 181 | F24 = 115, | ||
| 182 | NumPadComma = 133, | ||
| 183 | Ro = 135, | ||
| 184 | KatakanaHiragana = 136, | ||
| 185 | Yen = 137, | ||
| 186 | Henkan = 138, | ||
| 187 | Muhenkan = 139, | ||
| 188 | NumPadCommaPc98 = 140, | ||
| 189 | HangulEnglish = 144, | ||
| 190 | Hanja = 145, | ||
| 191 | Katakana = 146, | ||
| 192 | Hiragana = 147, | ||
| 193 | ZenkakuHankaku = 148, | ||
| 194 | LeftControl = 224, | ||
| 195 | LeftShift = 225, | ||
| 196 | LeftAlt = 226, | ||
| 197 | LeftGui = 227, | ||
| 198 | RightControl = 228, | ||
| 199 | RightShift = 229, | ||
| 200 | RightAlt = 230, | ||
| 201 | RightGui = 231, | ||
| 202 | }; | ||
| 203 | |||
| 204 | // This is nn::hid::NpadIdType | ||
| 205 | enum class NpadIdType : u32 { | ||
| 206 | Player1 = 0x0, | ||
| 207 | Player2 = 0x1, | ||
| 208 | Player3 = 0x2, | ||
| 209 | Player4 = 0x3, | ||
| 210 | Player5 = 0x4, | ||
| 211 | Player6 = 0x5, | ||
| 212 | Player7 = 0x6, | ||
| 213 | Player8 = 0x7, | ||
| 214 | Other = 0x10, | ||
| 215 | Handheld = 0x20, | ||
| 216 | |||
| 217 | Invalid = 0xFFFFFFFF, | ||
| 218 | }; | ||
| 219 | |||
| 220 | // This is nn::hid::NpadStyleIndex | ||
| 221 | enum class NpadStyleIndex : u8 { | ||
| 222 | None = 0, | ||
| 223 | ProController = 3, | ||
| 224 | Handheld = 4, | ||
| 225 | HandheldNES = 4, | ||
| 226 | JoyconDual = 5, | ||
| 227 | JoyconLeft = 6, | ||
| 228 | JoyconRight = 7, | ||
| 229 | GameCube = 8, | ||
| 230 | Pokeball = 9, | ||
| 231 | NES = 10, | ||
| 232 | SNES = 12, | ||
| 233 | N64 = 13, | ||
| 234 | SegaGenesis = 14, | ||
| 235 | SystemExt = 32, | ||
| 236 | System = 33, | ||
| 237 | MaxNpadType = 34, | ||
| 238 | }; | ||
| 239 | |||
| 240 | // This is nn::hid::NpadStyleSet | ||
| 241 | enum class NpadStyleSet : u32 { | ||
| 242 | None = 0, | ||
| 243 | Fullkey = 1U << 0, | ||
| 244 | Handheld = 1U << 1, | ||
| 245 | JoyDual = 1U << 2, | ||
| 246 | JoyLeft = 1U << 3, | ||
| 247 | JoyRight = 1U << 4, | ||
| 248 | Gc = 1U << 5, | ||
| 249 | Palma = 1U << 6, | ||
| 250 | Lark = 1U << 7, | ||
| 251 | HandheldLark = 1U << 8, | ||
| 252 | Lucia = 1U << 9, | ||
| 253 | Lagoon = 1U << 10, | ||
| 254 | Lager = 1U << 11, | ||
| 255 | SystemExt = 1U << 29, | ||
| 256 | System = 1U << 30, | ||
| 257 | }; | ||
| 258 | static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size"); | ||
| 259 | |||
| 260 | // This is nn::hid::VibrationDevicePosition | ||
| 261 | enum class VibrationDevicePosition : u32 { | ||
| 262 | None = 0, | ||
| 263 | Left = 1, | ||
| 264 | Right = 2, | ||
| 265 | }; | ||
| 266 | |||
| 267 | // This is nn::hid::VibrationDeviceType | ||
| 268 | enum class VibrationDeviceType : u32 { | ||
| 269 | Unknown = 0, | ||
| 270 | LinearResonantActuator = 1, | ||
| 271 | GcErm = 2, | ||
| 272 | }; | ||
| 273 | |||
| 274 | // This is nn::hid::VibrationGcErmCommand | ||
| 275 | enum class VibrationGcErmCommand : u64 { | ||
| 276 | Stop = 0, | ||
| 277 | Start = 1, | ||
| 278 | StopHard = 2, | ||
| 279 | }; | ||
| 280 | |||
| 281 | // This is nn::hid::NpadStyleTag | ||
| 282 | struct NpadStyleTag { | ||
| 283 | union { | ||
| 284 | NpadStyleSet raw{}; | ||
| 285 | |||
| 286 | BitField<0, 1, u32> fullkey; | ||
| 287 | BitField<1, 1, u32> handheld; | ||
| 288 | BitField<2, 1, u32> joycon_dual; | ||
| 289 | BitField<3, 1, u32> joycon_left; | ||
| 290 | BitField<4, 1, u32> joycon_right; | ||
| 291 | BitField<5, 1, u32> gamecube; | ||
| 292 | BitField<6, 1, u32> palma; | ||
| 293 | BitField<7, 1, u32> lark; | ||
| 294 | BitField<8, 1, u32> handheld_lark; | ||
| 295 | BitField<9, 1, u32> lucia; | ||
| 296 | BitField<10, 1, u32> lagoon; | ||
| 297 | BitField<11, 1, u32> lager; | ||
| 298 | BitField<29, 1, u32> system_ext; | ||
| 299 | BitField<30, 1, u32> system; | ||
| 300 | }; | ||
| 301 | }; | ||
| 302 | static_assert(sizeof(NpadStyleTag) == 4, "NpadStyleTag is an invalid size"); | ||
| 303 | |||
| 304 | // This is nn::hid::TouchAttribute | ||
| 305 | struct TouchAttribute { | ||
| 306 | union { | ||
| 307 | u32 raw{}; | ||
| 308 | BitField<0, 1, u32> start_touch; | ||
| 309 | BitField<1, 1, u32> end_touch; | ||
| 310 | }; | ||
| 311 | }; | ||
| 312 | static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size"); | ||
| 313 | |||
| 314 | // This is nn::hid::TouchState | ||
| 315 | struct TouchState { | ||
| 316 | u64 delta_time; | ||
| 317 | TouchAttribute attribute; | ||
| 318 | u32 finger; | ||
| 319 | Common::Point<u32> position; | ||
| 320 | u32 diameter_x; | ||
| 321 | u32 diameter_y; | ||
| 322 | u32 rotation_angle; | ||
| 323 | }; | ||
| 324 | static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); | ||
| 325 | |||
| 326 | // This is nn::hid::NpadControllerColor | ||
| 327 | struct NpadControllerColor { | ||
| 328 | u32 body; | ||
| 329 | u32 button; | ||
| 330 | }; | ||
| 331 | static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size"); | ||
| 332 | |||
| 333 | // This is nn::hid::AnalogStickState | ||
| 334 | struct AnalogStickState { | ||
| 335 | s32 x; | ||
| 336 | s32 y; | ||
| 337 | }; | ||
| 338 | static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size"); | ||
| 339 | |||
| 340 | // This is nn::hid::server::NpadGcTriggerState | ||
| 341 | struct NpadGcTriggerState { | ||
| 342 | s64 sampling_number{}; | ||
| 343 | s32 left{}; | ||
| 344 | s32 right{}; | ||
| 345 | }; | ||
| 346 | static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); | ||
| 347 | |||
| 348 | // This is nn::hid::system::NpadBatteryLevel | ||
| 349 | using NpadBatteryLevel = u32; | ||
| 350 | static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid size"); | ||
| 351 | |||
| 352 | // This is nn::hid::system::NpadPowerInfo | ||
| 353 | struct NpadPowerInfo { | ||
| 354 | bool is_powered; | ||
| 355 | bool is_charging; | ||
| 356 | INSERT_PADDING_BYTES(0x6); | ||
| 357 | NpadBatteryLevel battery_level; | ||
| 358 | }; | ||
| 359 | static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size"); | ||
| 360 | |||
| 361 | struct LedPattern { | ||
| 362 | explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) { | ||
| 363 | position1.Assign(light1); | ||
| 364 | position2.Assign(light2); | ||
| 365 | position3.Assign(light3); | ||
| 366 | position4.Assign(light4); | ||
| 367 | } | ||
| 368 | union { | ||
| 369 | u64 raw{}; | ||
| 370 | BitField<0, 1, u64> position1; | ||
| 371 | BitField<1, 1, u64> position2; | ||
| 372 | BitField<2, 1, u64> position3; | ||
| 373 | BitField<3, 1, u64> position4; | ||
| 374 | }; | ||
| 375 | }; | ||
| 376 | |||
| 377 | struct NpadButtonState { | ||
| 378 | union { | ||
| 379 | NpadButton raw{}; | ||
| 380 | |||
| 381 | // Buttons | ||
| 382 | BitField<0, 1, u64> a; | ||
| 383 | BitField<1, 1, u64> b; | ||
| 384 | BitField<2, 1, u64> x; | ||
| 385 | BitField<3, 1, u64> y; | ||
| 386 | BitField<4, 1, u64> stick_l; | ||
| 387 | BitField<5, 1, u64> stick_r; | ||
| 388 | BitField<6, 1, u64> l; | ||
| 389 | BitField<7, 1, u64> r; | ||
| 390 | BitField<8, 1, u64> zl; | ||
| 391 | BitField<9, 1, u64> zr; | ||
| 392 | BitField<10, 1, u64> plus; | ||
| 393 | BitField<11, 1, u64> minus; | ||
| 394 | |||
| 395 | // D-Pad | ||
| 396 | BitField<12, 1, u64> left; | ||
| 397 | BitField<13, 1, u64> up; | ||
| 398 | BitField<14, 1, u64> right; | ||
| 399 | BitField<15, 1, u64> down; | ||
| 400 | |||
| 401 | // Left JoyStick | ||
| 402 | BitField<16, 1, u64> stick_l_left; | ||
| 403 | BitField<17, 1, u64> stick_l_up; | ||
| 404 | BitField<18, 1, u64> stick_l_right; | ||
| 405 | BitField<19, 1, u64> stick_l_down; | ||
| 406 | |||
| 407 | // Right JoyStick | ||
| 408 | BitField<20, 1, u64> stick_r_left; | ||
| 409 | BitField<21, 1, u64> stick_r_up; | ||
| 410 | BitField<22, 1, u64> stick_r_right; | ||
| 411 | BitField<23, 1, u64> stick_r_down; | ||
| 412 | |||
| 413 | BitField<24, 1, u64> left_sl; | ||
| 414 | BitField<25, 1, u64> left_sr; | ||
| 415 | |||
| 416 | BitField<26, 1, u64> right_sl; | ||
| 417 | BitField<27, 1, u64> right_sr; | ||
| 418 | |||
| 419 | BitField<28, 1, u64> palma; | ||
| 420 | BitField<29, 1, u64> verification; | ||
| 421 | BitField<30, 1, u64> handheld_left_b; | ||
| 422 | BitField<31, 1, u64> lagon_c_left; | ||
| 423 | BitField<32, 1, u64> lagon_c_up; | ||
| 424 | BitField<33, 1, u64> lagon_c_right; | ||
| 425 | BitField<34, 1, u64> lagon_c_down; | ||
| 426 | }; | ||
| 427 | }; | ||
| 428 | static_assert(sizeof(NpadButtonState) == 0x8, "NpadButtonState has incorrect size."); | ||
| 429 | |||
| 430 | // This is nn::hid::DebugPadButton | ||
| 431 | struct DebugPadButton { | ||
| 432 | union { | ||
| 433 | u32 raw{}; | ||
| 434 | BitField<0, 1, u32> a; | ||
| 435 | BitField<1, 1, u32> b; | ||
| 436 | BitField<2, 1, u32> x; | ||
| 437 | BitField<3, 1, u32> y; | ||
| 438 | BitField<4, 1, u32> l; | ||
| 439 | BitField<5, 1, u32> r; | ||
| 440 | BitField<6, 1, u32> zl; | ||
| 441 | BitField<7, 1, u32> zr; | ||
| 442 | BitField<8, 1, u32> plus; | ||
| 443 | BitField<9, 1, u32> minus; | ||
| 444 | BitField<10, 1, u32> d_left; | ||
| 445 | BitField<11, 1, u32> d_up; | ||
| 446 | BitField<12, 1, u32> d_right; | ||
| 447 | BitField<13, 1, u32> d_down; | ||
| 448 | }; | ||
| 449 | }; | ||
| 450 | static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size"); | ||
| 451 | |||
| 452 | // This is nn::hid::ConsoleSixAxisSensorHandle | ||
| 453 | struct ConsoleSixAxisSensorHandle { | ||
| 454 | u8 unknown_1; | ||
| 455 | u8 unknown_2; | ||
| 456 | INSERT_PADDING_BYTES_NOINIT(2); | ||
| 457 | }; | ||
| 458 | static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4, | ||
| 459 | "ConsoleSixAxisSensorHandle is an invalid size"); | ||
| 460 | |||
| 461 | // This is nn::hid::SixAxisSensorHandle | ||
| 462 | struct SixAxisSensorHandle { | ||
| 463 | NpadStyleIndex npad_type; | ||
| 464 | u8 npad_id; | ||
| 465 | DeviceIndex device_index; | ||
| 466 | INSERT_PADDING_BYTES_NOINIT(1); | ||
| 467 | }; | ||
| 468 | static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size"); | ||
| 469 | |||
| 470 | struct SixAxisSensorFusionParameters { | ||
| 471 | f32 parameter1; | ||
| 472 | f32 parameter2; | ||
| 473 | }; | ||
| 474 | static_assert(sizeof(SixAxisSensorFusionParameters) == 8, | ||
| 475 | "SixAxisSensorFusionParameters is an invalid size"); | ||
| 476 | |||
| 477 | // This is nn::hid::VibrationDeviceHandle | ||
| 478 | struct VibrationDeviceHandle { | ||
| 479 | NpadStyleIndex npad_type; | ||
| 480 | u8 npad_id; | ||
| 481 | DeviceIndex device_index; | ||
| 482 | INSERT_PADDING_BYTES_NOINIT(1); | ||
| 483 | }; | ||
| 484 | static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size"); | ||
| 485 | |||
| 486 | // This is nn::hid::VibrationValue | ||
| 487 | struct VibrationValue { | ||
| 488 | f32 low_amplitude; | ||
| 489 | f32 low_frequency; | ||
| 490 | f32 high_amplitude; | ||
| 491 | f32 high_frequency; | ||
| 492 | }; | ||
| 493 | static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size."); | ||
| 494 | |||
| 495 | // This is nn::hid::VibrationDeviceInfo | ||
| 496 | struct VibrationDeviceInfo { | ||
| 497 | VibrationDeviceType type{}; | ||
| 498 | VibrationDevicePosition position{}; | ||
| 499 | }; | ||
| 500 | static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size."); | ||
| 501 | |||
| 502 | // This is nn::hid::KeyboardModifier | ||
| 503 | struct KeyboardModifier { | ||
| 504 | union { | ||
| 505 | u32 raw{}; | ||
| 506 | BitField<0, 1, u32> control; | ||
| 507 | BitField<1, 1, u32> shift; | ||
| 508 | BitField<2, 1, u32> left_alt; | ||
| 509 | BitField<3, 1, u32> right_alt; | ||
| 510 | BitField<4, 1, u32> gui; | ||
| 511 | BitField<8, 1, u32> caps_lock; | ||
| 512 | BitField<9, 1, u32> scroll_lock; | ||
| 513 | BitField<10, 1, u32> num_lock; | ||
| 514 | BitField<11, 1, u32> katakana; | ||
| 515 | BitField<12, 1, u32> hiragana; | ||
| 516 | }; | ||
| 517 | }; | ||
| 518 | |||
| 519 | static_assert(sizeof(KeyboardModifier) == 0x4, "KeyboardModifier is an invalid size"); | ||
| 520 | |||
| 521 | // This is nn::hid::KeyboardAttribute | ||
| 522 | struct KeyboardAttribute { | ||
| 523 | union { | ||
| 524 | u32 raw{}; | ||
| 525 | BitField<0, 1, u32> is_connected; | ||
| 526 | }; | ||
| 527 | }; | ||
| 528 | static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid size"); | ||
| 529 | |||
| 530 | // This is nn::hid::KeyboardKey | ||
| 531 | struct KeyboardKey { | ||
| 532 | // This should be a 256 bit flag | ||
| 533 | std::array<u8, 32> key; | ||
| 534 | }; | ||
| 535 | static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size"); | ||
| 536 | |||
| 537 | // This is nn::hid::MouseButton | ||
| 538 | struct MouseButton { | ||
| 539 | union { | ||
| 540 | u32_le raw{}; | ||
| 541 | BitField<0, 1, u32> left; | ||
| 542 | BitField<1, 1, u32> right; | ||
| 543 | BitField<2, 1, u32> middle; | ||
| 544 | BitField<3, 1, u32> forward; | ||
| 545 | BitField<4, 1, u32> back; | ||
| 546 | }; | ||
| 547 | }; | ||
| 548 | static_assert(sizeof(MouseButton) == 0x4, "MouseButton is an invalid size"); | ||
| 549 | |||
| 550 | // This is nn::hid::MouseAttribute | ||
| 551 | struct MouseAttribute { | ||
| 552 | union { | ||
| 553 | u32 raw{}; | ||
| 554 | BitField<0, 1, u32> transferable; | ||
| 555 | BitField<1, 1, u32> is_connected; | ||
| 556 | }; | ||
| 557 | }; | ||
| 558 | static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size"); | ||
| 559 | |||
| 560 | // This is nn::hid::detail::MouseState | ||
| 561 | struct MouseState { | ||
| 562 | s64 sampling_number; | ||
| 563 | s32 x; | ||
| 564 | s32 y; | ||
| 565 | s32 delta_x; | ||
| 566 | s32 delta_y; | ||
| 567 | // Axis Order in HW is switched for the wheel | ||
| 568 | s32 delta_wheel_y; | ||
| 569 | s32 delta_wheel_x; | ||
| 570 | MouseButton button; | ||
| 571 | MouseAttribute attribute; | ||
| 572 | }; | ||
| 573 | static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size"); | ||
| 574 | |||
| 575 | /// Converts a NpadIdType to an array index. | ||
| 576 | constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) { | ||
| 577 | switch (npad_id_type) { | ||
| 578 | case NpadIdType::Player1: | ||
| 579 | return 0; | ||
| 580 | case NpadIdType::Player2: | ||
| 581 | return 1; | ||
| 582 | case NpadIdType::Player3: | ||
| 583 | return 2; | ||
| 584 | case NpadIdType::Player4: | ||
| 585 | return 3; | ||
| 586 | case NpadIdType::Player5: | ||
| 587 | return 4; | ||
| 588 | case NpadIdType::Player6: | ||
| 589 | return 5; | ||
| 590 | case NpadIdType::Player7: | ||
| 591 | return 6; | ||
| 592 | case NpadIdType::Player8: | ||
| 593 | return 7; | ||
| 594 | case NpadIdType::Handheld: | ||
| 595 | return 8; | ||
| 596 | case NpadIdType::Other: | ||
| 597 | return 9; | ||
| 598 | default: | ||
| 599 | return 0; | ||
| 600 | } | ||
| 601 | } | ||
| 602 | |||
| 603 | /// Converts an array index to a NpadIdType | ||
| 604 | constexpr NpadIdType IndexToNpadIdType(size_t index) { | ||
| 605 | switch (index) { | ||
| 606 | case 0: | ||
| 607 | return NpadIdType::Player1; | ||
| 608 | case 1: | ||
| 609 | return NpadIdType::Player2; | ||
| 610 | case 2: | ||
| 611 | return NpadIdType::Player3; | ||
| 612 | case 3: | ||
| 613 | return NpadIdType::Player4; | ||
| 614 | case 4: | ||
| 615 | return NpadIdType::Player5; | ||
| 616 | case 5: | ||
| 617 | return NpadIdType::Player6; | ||
| 618 | case 6: | ||
| 619 | return NpadIdType::Player7; | ||
| 620 | case 7: | ||
| 621 | return NpadIdType::Player8; | ||
| 622 | case 8: | ||
| 623 | return NpadIdType::Handheld; | ||
| 624 | case 9: | ||
| 625 | return NpadIdType::Other; | ||
| 626 | default: | ||
| 627 | return NpadIdType::Invalid; | ||
| 628 | } | ||
| 629 | } | ||
| 630 | |||
| 631 | } // namespace Core::HID | ||
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp new file mode 100644 index 000000000..f5acff6e0 --- /dev/null +++ b/src/core/hid/input_converter.cpp | |||
| @@ -0,0 +1,383 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included | ||
| 4 | |||
| 5 | #include <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 >= 1.0f) { | ||
| 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 | case Common::Input::InputType::Trigger: | ||
| 56 | status.value = TransformToTrigger(callback).pressed.value; | ||
| 57 | break; | ||
| 58 | case Common::Input::InputType::Button: | ||
| 59 | status = callback.button_status; | ||
| 60 | break; | ||
| 61 | default: | ||
| 62 | LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type); | ||
| 63 | break; | ||
| 64 | } | ||
| 65 | |||
| 66 | if (status.inverted) { | ||
| 67 | status.value = !status.value; | ||
| 68 | } | ||
| 69 | |||
| 70 | return status; | ||
| 71 | } | ||
| 72 | |||
| 73 | Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback) { | ||
| 74 | Common::Input::MotionStatus status{}; | ||
| 75 | switch (callback.type) { | ||
| 76 | case Common::Input::InputType::Button: { | ||
| 77 | Common::Input::AnalogProperties properties{ | ||
| 78 | .deadzone = 0.0f, | ||
| 79 | .range = 1.0f, | ||
| 80 | .offset = 0.0f, | ||
| 81 | }; | ||
| 82 | status.delta_timestamp = 5000; | ||
| 83 | status.force_update = true; | ||
| 84 | status.accel.x = { | ||
| 85 | .value = 0.0f, | ||
| 86 | .raw_value = 0.0f, | ||
| 87 | .properties = properties, | ||
| 88 | }; | ||
| 89 | status.accel.y = { | ||
| 90 | .value = 0.0f, | ||
| 91 | .raw_value = 0.0f, | ||
| 92 | .properties = properties, | ||
| 93 | }; | ||
| 94 | status.accel.z = { | ||
| 95 | .value = 0.0f, | ||
| 96 | .raw_value = -1.0f, | ||
| 97 | .properties = properties, | ||
| 98 | }; | ||
| 99 | status.gyro.x = { | ||
| 100 | .value = 0.0f, | ||
| 101 | .raw_value = 0.0f, | ||
| 102 | .properties = properties, | ||
| 103 | }; | ||
| 104 | status.gyro.y = { | ||
| 105 | .value = 0.0f, | ||
| 106 | .raw_value = 0.0f, | ||
| 107 | .properties = properties, | ||
| 108 | }; | ||
| 109 | status.gyro.z = { | ||
| 110 | .value = 0.0f, | ||
| 111 | .raw_value = 0.0f, | ||
| 112 | .properties = properties, | ||
| 113 | }; | ||
| 114 | if (TransformToButton(callback).value) { | ||
| 115 | std::random_device device; | ||
| 116 | std::mt19937 gen(device()); | ||
| 117 | std::uniform_int_distribution<s16> distribution(-1000, 1000); | ||
| 118 | status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; | ||
| 119 | status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; | ||
| 120 | status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; | ||
| 121 | status.gyro.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; | ||
| 122 | status.gyro.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; | ||
| 123 | status.gyro.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f; | ||
| 124 | } | ||
| 125 | break; | ||
| 126 | } | ||
| 127 | case Common::Input::InputType::Motion: | ||
| 128 | status = callback.motion_status; | ||
| 129 | break; | ||
| 130 | default: | ||
| 131 | LOG_ERROR(Input, "Conversion from type {} to motion not implemented", callback.type); | ||
| 132 | break; | ||
| 133 | } | ||
| 134 | SanitizeAnalog(status.accel.x, false); | ||
| 135 | SanitizeAnalog(status.accel.y, false); | ||
| 136 | SanitizeAnalog(status.accel.z, false); | ||
| 137 | SanitizeAnalog(status.gyro.x, false); | ||
| 138 | SanitizeAnalog(status.gyro.y, false); | ||
| 139 | SanitizeAnalog(status.gyro.z, false); | ||
| 140 | |||
| 141 | return status; | ||
| 142 | } | ||
| 143 | |||
| 144 | Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback) { | ||
| 145 | Common::Input::StickStatus status{}; | ||
| 146 | |||
| 147 | switch (callback.type) { | ||
| 148 | case Common::Input::InputType::Stick: | ||
| 149 | status = callback.stick_status; | ||
| 150 | break; | ||
| 151 | default: | ||
| 152 | LOG_ERROR(Input, "Conversion from type {} to stick not implemented", callback.type); | ||
| 153 | break; | ||
| 154 | } | ||
| 155 | |||
| 156 | SanitizeStick(status.x, status.y, true); | ||
| 157 | const auto& properties_x = status.x.properties; | ||
| 158 | const auto& properties_y = status.y.properties; | ||
| 159 | const float x = status.x.value; | ||
| 160 | const float y = status.y.value; | ||
| 161 | |||
| 162 | // Set directional buttons | ||
| 163 | status.right = x > properties_x.threshold; | ||
| 164 | status.left = x < -properties_x.threshold; | ||
| 165 | status.up = y > properties_y.threshold; | ||
| 166 | status.down = y < -properties_y.threshold; | ||
| 167 | |||
| 168 | return status; | ||
| 169 | } | ||
| 170 | |||
| 171 | Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback) { | ||
| 172 | Common::Input::TouchStatus status{}; | ||
| 173 | |||
| 174 | switch (callback.type) { | ||
| 175 | case Common::Input::InputType::Touch: | ||
| 176 | status = callback.touch_status; | ||
| 177 | break; | ||
| 178 | case Common::Input::InputType::Stick: | ||
| 179 | status.x = callback.stick_status.x; | ||
| 180 | status.y = callback.stick_status.y; | ||
| 181 | break; | ||
| 182 | default: | ||
| 183 | LOG_ERROR(Input, "Conversion from type {} to touch not implemented", callback.type); | ||
| 184 | break; | ||
| 185 | } | ||
| 186 | |||
| 187 | SanitizeAnalog(status.x, true); | ||
| 188 | SanitizeAnalog(status.y, true); | ||
| 189 | float& x = status.x.value; | ||
| 190 | float& y = status.y.value; | ||
| 191 | |||
| 192 | // Adjust if value is inverted | ||
| 193 | x = status.x.properties.inverted ? 1.0f + x : x; | ||
| 194 | y = status.y.properties.inverted ? 1.0f + y : y; | ||
| 195 | |||
| 196 | // clamp value | ||
| 197 | x = std::clamp(x, 0.0f, 1.0f); | ||
| 198 | y = std::clamp(y, 0.0f, 1.0f); | ||
| 199 | |||
| 200 | if (status.pressed.inverted) { | ||
| 201 | status.pressed.value = !status.pressed.value; | ||
| 202 | } | ||
| 203 | |||
| 204 | return status; | ||
| 205 | } | ||
| 206 | |||
| 207 | Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback) { | ||
| 208 | Common::Input::TriggerStatus status{}; | ||
| 209 | float& raw_value = status.analog.raw_value; | ||
| 210 | bool calculate_button_value = true; | ||
| 211 | |||
| 212 | switch (callback.type) { | ||
| 213 | case Common::Input::InputType::Analog: | ||
| 214 | status.analog.properties = callback.analog_status.properties; | ||
| 215 | raw_value = callback.analog_status.raw_value; | ||
| 216 | break; | ||
| 217 | case Common::Input::InputType::Button: | ||
| 218 | status.analog.properties.range = 1.0f; | ||
| 219 | status.analog.properties.inverted = callback.button_status.inverted; | ||
| 220 | raw_value = callback.button_status.value ? 1.0f : 0.0f; | ||
| 221 | break; | ||
| 222 | case Common::Input::InputType::Trigger: | ||
| 223 | status = callback.trigger_status; | ||
| 224 | calculate_button_value = false; | ||
| 225 | break; | ||
| 226 | default: | ||
| 227 | LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type); | ||
| 228 | break; | ||
| 229 | } | ||
| 230 | |||
| 231 | SanitizeAnalog(status.analog, true); | ||
| 232 | const auto& properties = status.analog.properties; | ||
| 233 | float& value = status.analog.value; | ||
| 234 | |||
| 235 | // Set button status | ||
| 236 | if (calculate_button_value) { | ||
| 237 | status.pressed.value = value > properties.threshold; | ||
| 238 | } | ||
| 239 | |||
| 240 | // Adjust if value is inverted | ||
| 241 | value = properties.inverted ? 1.0f + value : value; | ||
| 242 | |||
| 243 | // clamp value | ||
| 244 | value = std::clamp(value, 0.0f, 1.0f); | ||
| 245 | |||
| 246 | return status; | ||
| 247 | } | ||
| 248 | |||
| 249 | Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback) { | ||
| 250 | Common::Input::AnalogStatus status{}; | ||
| 251 | |||
| 252 | switch (callback.type) { | ||
| 253 | case Common::Input::InputType::Analog: | ||
| 254 | status.properties = callback.analog_status.properties; | ||
| 255 | status.raw_value = callback.analog_status.raw_value; | ||
| 256 | break; | ||
| 257 | default: | ||
| 258 | LOG_ERROR(Input, "Conversion from type {} to analog not implemented", callback.type); | ||
| 259 | break; | ||
| 260 | } | ||
| 261 | |||
| 262 | SanitizeAnalog(status, false); | ||
| 263 | |||
| 264 | // Adjust if value is inverted | ||
| 265 | status.value = status.properties.inverted ? -status.value : status.value; | ||
| 266 | |||
| 267 | return status; | ||
| 268 | } | ||
| 269 | |||
| 270 | void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { | ||
| 271 | const auto& properties = analog.properties; | ||
| 272 | float& raw_value = analog.raw_value; | ||
| 273 | float& value = analog.value; | ||
| 274 | |||
| 275 | if (!std::isnormal(raw_value)) { | ||
| 276 | raw_value = 0; | ||
| 277 | } | ||
| 278 | |||
| 279 | // Apply center offset | ||
| 280 | raw_value -= properties.offset; | ||
| 281 | |||
| 282 | // Set initial values to be formated | ||
| 283 | value = raw_value; | ||
| 284 | |||
| 285 | // Calculate vector size | ||
| 286 | const float r = std::abs(value); | ||
| 287 | |||
| 288 | // Return zero if value is smaller than the deadzone | ||
| 289 | if (r <= properties.deadzone || properties.deadzone == 1.0f) { | ||
| 290 | analog.value = 0; | ||
| 291 | return; | ||
| 292 | } | ||
| 293 | |||
| 294 | // Adjust range of value | ||
| 295 | const float deadzone_factor = | ||
| 296 | 1.0f / r * (r - properties.deadzone) / (1.0f - properties.deadzone); | ||
| 297 | value = value * deadzone_factor / properties.range; | ||
| 298 | |||
| 299 | // Invert direction if needed | ||
| 300 | if (properties.inverted) { | ||
| 301 | value = -value; | ||
| 302 | } | ||
| 303 | |||
| 304 | // Clamp value | ||
| 305 | if (clamp_value) { | ||
| 306 | value = std::clamp(value, -1.0f, 1.0f); | ||
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y, | ||
| 311 | bool clamp_value) { | ||
| 312 | const auto& properties_x = analog_x.properties; | ||
| 313 | const auto& properties_y = analog_y.properties; | ||
| 314 | float& raw_x = analog_x.raw_value; | ||
| 315 | float& raw_y = analog_y.raw_value; | ||
| 316 | float& x = analog_x.value; | ||
| 317 | float& y = analog_y.value; | ||
| 318 | |||
| 319 | if (!std::isnormal(raw_x)) { | ||
| 320 | raw_x = 0; | ||
| 321 | } | ||
| 322 | if (!std::isnormal(raw_y)) { | ||
| 323 | raw_y = 0; | ||
| 324 | } | ||
| 325 | |||
| 326 | // Apply center offset | ||
| 327 | raw_x += properties_x.offset; | ||
| 328 | raw_y += properties_y.offset; | ||
| 329 | |||
| 330 | // Apply X scale correction from offset | ||
| 331 | if (std::abs(properties_x.offset) < 0.5f) { | ||
| 332 | if (raw_x > 0) { | ||
| 333 | raw_x /= 1 + properties_x.offset; | ||
| 334 | } else { | ||
| 335 | raw_x /= 1 - properties_x.offset; | ||
| 336 | } | ||
| 337 | } | ||
| 338 | |||
| 339 | // Apply Y scale correction from offset | ||
| 340 | if (std::abs(properties_y.offset) < 0.5f) { | ||
| 341 | if (raw_y > 0) { | ||
| 342 | raw_y /= 1 + properties_y.offset; | ||
| 343 | } else { | ||
| 344 | raw_y /= 1 - properties_y.offset; | ||
| 345 | } | ||
| 346 | } | ||
| 347 | |||
| 348 | // Invert direction if needed | ||
| 349 | raw_x = properties_x.inverted ? -raw_x : raw_x; | ||
| 350 | raw_y = properties_y.inverted ? -raw_y : raw_y; | ||
| 351 | |||
| 352 | // Set initial values to be formated | ||
| 353 | x = raw_x; | ||
| 354 | y = raw_y; | ||
| 355 | |||
| 356 | // Calculate vector size | ||
| 357 | float r = x * x + y * y; | ||
| 358 | r = std::sqrt(r); | ||
| 359 | |||
| 360 | // TODO(German77): Use deadzone and range of both axis | ||
| 361 | |||
| 362 | // Return zero if values are smaller than the deadzone | ||
| 363 | if (r <= properties_x.deadzone || properties_x.deadzone >= 1.0f) { | ||
| 364 | x = 0; | ||
| 365 | y = 0; | ||
| 366 | return; | ||
| 367 | } | ||
| 368 | |||
| 369 | // Adjust range of joystick | ||
| 370 | const float deadzone_factor = | ||
| 371 | 1.0f / r * (r - properties_x.deadzone) / (1.0f - properties_x.deadzone); | ||
| 372 | x = x * deadzone_factor / properties_x.range; | ||
| 373 | y = y * deadzone_factor / properties_x.range; | ||
| 374 | r = r * deadzone_factor / properties_x.range; | ||
| 375 | |||
| 376 | // Normalize joystick | ||
| 377 | if (clamp_value && r > 1.0f) { | ||
| 378 | x /= r; | ||
| 379 | y /= r; | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 383 | } // namespace Core::HID | ||
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h new file mode 100644 index 000000000..1492489d7 --- /dev/null +++ b/src/core/hid/input_converter.h | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | namespace Common::Input { | ||
| 8 | struct CallbackStatus; | ||
| 9 | enum class BatteryLevel : u32; | ||
| 10 | using BatteryStatus = BatteryLevel; | ||
| 11 | struct AnalogStatus; | ||
| 12 | struct ButtonStatus; | ||
| 13 | struct MotionStatus; | ||
| 14 | struct StickStatus; | ||
| 15 | struct TouchStatus; | ||
| 16 | struct TriggerStatus; | ||
| 17 | }; // namespace Common::Input | ||
| 18 | |||
| 19 | namespace Core::HID { | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Converts raw input data into a valid battery status. | ||
| 23 | * | ||
| 24 | * @param Supported callbacks: Analog, Battery, Trigger. | ||
| 25 | * @return A valid BatteryStatus object. | ||
| 26 | */ | ||
| 27 | Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback); | ||
| 28 | |||
| 29 | /** | ||
| 30 | * Converts raw input data into a valid button status. Applies invert properties to the output. | ||
| 31 | * | ||
| 32 | * @param Supported callbacks: Analog, Button, Trigger. | ||
| 33 | * @return A valid TouchStatus object. | ||
| 34 | */ | ||
| 35 | Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback); | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Converts raw input data into a valid motion status. | ||
| 39 | * | ||
| 40 | * @param Supported callbacks: Motion. | ||
| 41 | * @return A valid TouchStatus object. | ||
| 42 | */ | ||
| 43 | Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback); | ||
| 44 | |||
| 45 | /** | ||
| 46 | * Converts raw input data into a valid stick status. Applies offset, deadzone, range and invert | ||
| 47 | * properties to the output. | ||
| 48 | * | ||
| 49 | * @param Supported callbacks: Stick. | ||
| 50 | * @return A valid StickStatus object. | ||
| 51 | */ | ||
| 52 | Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback); | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Converts raw input data into a valid touch status. | ||
| 56 | * | ||
| 57 | * @param Supported callbacks: Touch. | ||
| 58 | * @return A valid TouchStatus object. | ||
| 59 | */ | ||
| 60 | Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback); | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Converts raw input data into a valid trigger status. Applies offset, deadzone, range and | ||
| 64 | * invert properties to the output. Button status uses the threshold property if necessary. | ||
| 65 | * | ||
| 66 | * @param Supported callbacks: Analog, Button, Trigger. | ||
| 67 | * @return A valid TriggerStatus object. | ||
| 68 | */ | ||
| 69 | Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback); | ||
| 70 | |||
| 71 | /** | ||
| 72 | * Converts raw input data into a valid analog status. Applies offset, deadzone, range and | ||
| 73 | * invert properties to the output. | ||
| 74 | * | ||
| 75 | * @param Supported callbacks: Analog. | ||
| 76 | * @return A valid AnalogStatus object. | ||
| 77 | */ | ||
| 78 | Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback); | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Converts raw analog data into a valid analog value | ||
| 82 | * @param An analog object containing raw data and properties, bool that determines if the value | ||
| 83 | * needs to be clamped between -1.0f and 1.0f. | ||
| 84 | */ | ||
| 85 | void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value); | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Converts raw stick data into a valid stick value | ||
| 89 | * @param Two analog objects containing raw data and properties, bool that determines if the value | ||
| 90 | * needs to be clamped into the unit circle. | ||
| 91 | */ | ||
| 92 | void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y, | ||
| 93 | bool clamp_value); | ||
| 94 | |||
| 95 | } // namespace Core::HID | ||
diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp new file mode 100644 index 000000000..870422d82 --- /dev/null +++ b/src/core/hid/input_interpreter.cpp | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/hid/hid_types.h" | ||
| 7 | #include "core/hid/input_interpreter.h" | ||
| 8 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 9 | #include "core/hle/service/hid/hid.h" | ||
| 10 | #include "core/hle/service/sm/sm.h" | ||
| 11 | |||
| 12 | InputInterpreter::InputInterpreter(Core::System& system) | ||
| 13 | : npad{system.ServiceManager() | ||
| 14 | .GetService<Service::HID::Hid>("hid") | ||
| 15 | ->GetAppletResource() | ||
| 16 | ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} { | ||
| 17 | ResetButtonStates(); | ||
| 18 | } | ||
| 19 | |||
| 20 | InputInterpreter::~InputInterpreter() = default; | ||
| 21 | |||
| 22 | void InputInterpreter::PollInput() { | ||
| 23 | const u64 button_state = npad.GetAndResetPressState(); | ||
| 24 | |||
| 25 | previous_index = current_index; | ||
| 26 | current_index = (current_index + 1) % button_states.size(); | ||
| 27 | |||
| 28 | button_states[current_index] = button_state; | ||
| 29 | } | ||
| 30 | |||
| 31 | void InputInterpreter::ResetButtonStates() { | ||
| 32 | previous_index = 0; | ||
| 33 | current_index = 0; | ||
| 34 | |||
| 35 | button_states[0] = 0xFFFFFFFFFFFFFFFF; | ||
| 36 | |||
| 37 | for (std::size_t i = 1; i < button_states.size(); ++i) { | ||
| 38 | button_states[i] = 0; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | bool InputInterpreter::IsButtonPressed(Core::HID::NpadButton button) const { | ||
| 43 | return (button_states[current_index] & static_cast<u64>(button)) != 0; | ||
| 44 | } | ||
| 45 | |||
| 46 | bool InputInterpreter::IsButtonPressedOnce(Core::HID::NpadButton button) const { | ||
| 47 | const bool current_press = (button_states[current_index] & static_cast<u64>(button)) != 0; | ||
| 48 | const bool previous_press = (button_states[previous_index] & static_cast<u64>(button)) != 0; | ||
| 49 | |||
| 50 | return current_press && !previous_press; | ||
| 51 | } | ||
| 52 | |||
| 53 | bool InputInterpreter::IsButtonHeld(Core::HID::NpadButton button) const { | ||
| 54 | u64 held_buttons{button_states[0]}; | ||
| 55 | |||
| 56 | for (std::size_t i = 1; i < button_states.size(); ++i) { | ||
| 57 | held_buttons &= button_states[i]; | ||
| 58 | } | ||
| 59 | |||
| 60 | return (held_buttons & static_cast<u64>(button)) != 0; | ||
| 61 | } | ||
diff --git a/src/core/hid/input_interpreter.h b/src/core/hid/input_interpreter.h new file mode 100644 index 000000000..1c2e02142 --- /dev/null +++ b/src/core/hid/input_interpreter.h | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Core::HID { | ||
| 16 | enum class NpadButton : u64; | ||
| 17 | } | ||
| 18 | |||
| 19 | namespace Service::HID { | ||
| 20 | class Controller_NPad; | ||
| 21 | } | ||
| 22 | |||
| 23 | /** | ||
| 24 | * The InputInterpreter class interfaces with HID to retrieve button press states. | ||
| 25 | * Input is intended to be polled every 50ms so that a button is considered to be | ||
| 26 | * held down after 400ms has elapsed since the initial button press and subsequent | ||
| 27 | * repeated presses occur every 50ms. | ||
| 28 | */ | ||
| 29 | class InputInterpreter { | ||
| 30 | public: | ||
| 31 | explicit InputInterpreter(Core::System& system); | ||
| 32 | virtual ~InputInterpreter(); | ||
| 33 | |||
| 34 | /// Gets a button state from HID and inserts it into the array of button states. | ||
| 35 | void PollInput(); | ||
| 36 | |||
| 37 | /// Resets all the button states to their defaults. | ||
| 38 | void ResetButtonStates(); | ||
| 39 | |||
| 40 | /** | ||
| 41 | * Checks whether the button is pressed. | ||
| 42 | * | ||
| 43 | * @param button The button to check. | ||
| 44 | * | ||
| 45 | * @returns True when the button is pressed. | ||
| 46 | */ | ||
| 47 | [[nodiscard]] bool IsButtonPressed(Core::HID::NpadButton button) const; | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Checks whether any of the buttons in the parameter list is pressed. | ||
| 51 | * | ||
| 52 | * @tparam HIDButton The buttons to check. | ||
| 53 | * | ||
| 54 | * @returns True when at least one of the buttons is pressed. | ||
| 55 | */ | ||
| 56 | template <Core::HID::NpadButton... T> | ||
| 57 | [[nodiscard]] bool IsAnyButtonPressed() { | ||
| 58 | return (IsButtonPressed(T) || ...); | ||
| 59 | } | ||
| 60 | |||
| 61 | /** | ||
| 62 | * The specified button is considered to be pressed once | ||
| 63 | * if it is currently pressed and not pressed previously. | ||
| 64 | * | ||
| 65 | * @param button The button to check. | ||
| 66 | * | ||
| 67 | * @returns True when the button is pressed once. | ||
| 68 | */ | ||
| 69 | [[nodiscard]] bool IsButtonPressedOnce(Core::HID::NpadButton button) const; | ||
| 70 | |||
| 71 | /** | ||
| 72 | * Checks whether any of the buttons in the parameter list is pressed once. | ||
| 73 | * | ||
| 74 | * @tparam T The buttons to check. | ||
| 75 | * | ||
| 76 | * @returns True when at least one of the buttons is pressed once. | ||
| 77 | */ | ||
| 78 | template <Core::HID::NpadButton... T> | ||
| 79 | [[nodiscard]] bool IsAnyButtonPressedOnce() const { | ||
| 80 | return (IsButtonPressedOnce(T) || ...); | ||
| 81 | } | ||
| 82 | |||
| 83 | /** | ||
| 84 | * The specified button is considered to be held down if it is pressed in all 9 button states. | ||
| 85 | * | ||
| 86 | * @param button The button to check. | ||
| 87 | * | ||
| 88 | * @returns True when the button is held down. | ||
| 89 | */ | ||
| 90 | [[nodiscard]] bool IsButtonHeld(Core::HID::NpadButton button) const; | ||
| 91 | |||
| 92 | /** | ||
| 93 | * Checks whether any of the buttons in the parameter list is held down. | ||
| 94 | * | ||
| 95 | * @tparam T The buttons to check. | ||
| 96 | * | ||
| 97 | * @returns True when at least one of the buttons is held down. | ||
| 98 | */ | ||
| 99 | template <Core::HID::NpadButton... T> | ||
| 100 | [[nodiscard]] bool IsAnyButtonHeld() const { | ||
| 101 | return (IsButtonHeld(T) || ...); | ||
| 102 | } | ||
| 103 | |||
| 104 | private: | ||
| 105 | Service::HID::Controller_NPad& npad; | ||
| 106 | |||
| 107 | /// Stores 9 consecutive button states polled from HID. | ||
| 108 | std::array<u64, 9> button_states{}; | ||
| 109 | |||
| 110 | std::size_t previous_index{}; | ||
| 111 | std::size_t current_index{}; | ||
| 112 | }; | ||
diff --git a/src/core/hid/motion_input.cpp b/src/core/hid/motion_input.cpp new file mode 100644 index 000000000..c25fea966 --- /dev/null +++ b/src/core/hid/motion_input.cpp | |||
| @@ -0,0 +1,280 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included | ||
| 4 | |||
| 5 | #include "common/math_util.h" | ||
| 6 | #include "core/hid/motion_input.h" | ||
| 7 | |||
| 8 | namespace Core::HID { | ||
| 9 | |||
| 10 | MotionInput::MotionInput() { | ||
| 11 | // Initialize PID constants with default values | ||
| 12 | SetPID(0.3f, 0.005f, 0.0f); | ||
| 13 | } | ||
| 14 | |||
| 15 | void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) { | ||
| 16 | kp = new_kp; | ||
| 17 | ki = new_ki; | ||
| 18 | kd = new_kd; | ||
| 19 | } | ||
| 20 | |||
| 21 | void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) { | ||
| 22 | accel = acceleration; | ||
| 23 | } | ||
| 24 | |||
| 25 | void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) { | ||
| 26 | gyro = gyroscope - gyro_drift; | ||
| 27 | |||
| 28 | // Auto adjust drift to minimize drift | ||
| 29 | if (!IsMoving(0.1f)) { | ||
| 30 | gyro_drift = (gyro_drift * 0.9999f) + (gyroscope * 0.0001f); | ||
| 31 | } | ||
| 32 | |||
| 33 | if (gyro.Length2() < gyro_threshold) { | ||
| 34 | gyro = {}; | ||
| 35 | } else { | ||
| 36 | only_accelerometer = false; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) { | ||
| 41 | quat = quaternion; | ||
| 42 | } | ||
| 43 | |||
| 44 | void MotionInput::SetGyroDrift(const Common::Vec3f& drift) { | ||
| 45 | gyro_drift = drift; | ||
| 46 | } | ||
| 47 | |||
| 48 | void MotionInput::SetGyroThreshold(f32 threshold) { | ||
| 49 | gyro_threshold = threshold; | ||
| 50 | } | ||
| 51 | |||
| 52 | void MotionInput::EnableReset(bool reset) { | ||
| 53 | reset_enabled = reset; | ||
| 54 | } | ||
| 55 | |||
| 56 | void MotionInput::ResetRotations() { | ||
| 57 | rotations = {}; | ||
| 58 | } | ||
| 59 | |||
| 60 | bool MotionInput::IsMoving(f32 sensitivity) const { | ||
| 61 | return gyro.Length() >= sensitivity || accel.Length() <= 0.9f || accel.Length() >= 1.1f; | ||
| 62 | } | ||
| 63 | |||
| 64 | bool MotionInput::IsCalibrated(f32 sensitivity) const { | ||
| 65 | return real_error.Length() < sensitivity; | ||
| 66 | } | ||
| 67 | |||
| 68 | void MotionInput::UpdateRotation(u64 elapsed_time) { | ||
| 69 | const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f; | ||
| 70 | if (sample_period > 0.1f) { | ||
| 71 | return; | ||
| 72 | } | ||
| 73 | rotations += gyro * sample_period; | ||
| 74 | } | ||
| 75 | |||
| 76 | // Based on Madgwick's implementation of Mayhony's AHRS algorithm. | ||
| 77 | // 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 | ||
| 78 | void MotionInput::UpdateOrientation(u64 elapsed_time) { | ||
| 79 | if (!IsCalibrated(0.1f)) { | ||
| 80 | ResetOrientation(); | ||
| 81 | } | ||
| 82 | // Short name local variable for readability | ||
| 83 | f32 q1 = quat.w; | ||
| 84 | f32 q2 = quat.xyz[0]; | ||
| 85 | f32 q3 = quat.xyz[1]; | ||
| 86 | f32 q4 = quat.xyz[2]; | ||
| 87 | const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f; | ||
| 88 | |||
| 89 | // Ignore invalid elapsed time | ||
| 90 | if (sample_period > 0.1f) { | ||
| 91 | return; | ||
| 92 | } | ||
| 93 | |||
| 94 | const auto normal_accel = accel.Normalized(); | ||
| 95 | auto rad_gyro = gyro * Common::PI * 2; | ||
| 96 | const f32 swap = rad_gyro.x; | ||
| 97 | rad_gyro.x = rad_gyro.y; | ||
| 98 | rad_gyro.y = -swap; | ||
| 99 | rad_gyro.z = -rad_gyro.z; | ||
| 100 | |||
| 101 | // Clear gyro values if there is no gyro present | ||
| 102 | if (only_accelerometer) { | ||
| 103 | rad_gyro.x = 0; | ||
| 104 | rad_gyro.y = 0; | ||
| 105 | rad_gyro.z = 0; | ||
| 106 | } | ||
| 107 | |||
| 108 | // Ignore drift correction if acceleration is not reliable | ||
| 109 | if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) { | ||
| 110 | const f32 ax = -normal_accel.x; | ||
| 111 | const f32 ay = normal_accel.y; | ||
| 112 | const f32 az = -normal_accel.z; | ||
| 113 | |||
| 114 | // Estimated direction of gravity | ||
| 115 | const f32 vx = 2.0f * (q2 * q4 - q1 * q3); | ||
| 116 | const f32 vy = 2.0f * (q1 * q2 + q3 * q4); | ||
| 117 | const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; | ||
| 118 | |||
| 119 | // Error is cross product between estimated direction and measured direction of gravity | ||
| 120 | const Common::Vec3f new_real_error = { | ||
| 121 | az * vx - ax * vz, | ||
| 122 | ay * vz - az * vy, | ||
| 123 | ax * vy - ay * vx, | ||
| 124 | }; | ||
| 125 | |||
| 126 | derivative_error = new_real_error - real_error; | ||
| 127 | real_error = new_real_error; | ||
| 128 | |||
| 129 | // Prevent integral windup | ||
| 130 | if (ki != 0.0f && !IsCalibrated(0.05f)) { | ||
| 131 | integral_error += real_error; | ||
| 132 | } else { | ||
| 133 | integral_error = {}; | ||
| 134 | } | ||
| 135 | |||
| 136 | // Apply feedback terms | ||
| 137 | if (!only_accelerometer) { | ||
| 138 | rad_gyro += kp * real_error; | ||
| 139 | rad_gyro += ki * integral_error; | ||
| 140 | rad_gyro += kd * derivative_error; | ||
| 141 | } else { | ||
| 142 | // Give more weight to accelerometer values to compensate for the lack of gyro | ||
| 143 | rad_gyro += 35.0f * kp * real_error; | ||
| 144 | rad_gyro += 10.0f * ki * integral_error; | ||
| 145 | rad_gyro += 10.0f * kd * derivative_error; | ||
| 146 | |||
| 147 | // Emulate gyro values for games that need them | ||
| 148 | gyro.x = -rad_gyro.y; | ||
| 149 | gyro.y = rad_gyro.x; | ||
| 150 | gyro.z = -rad_gyro.z; | ||
| 151 | UpdateRotation(elapsed_time); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | const f32 gx = rad_gyro.y; | ||
| 156 | const f32 gy = rad_gyro.x; | ||
| 157 | const f32 gz = rad_gyro.z; | ||
| 158 | |||
| 159 | // Integrate rate of change of quaternion | ||
| 160 | const f32 pa = q2; | ||
| 161 | const f32 pb = q3; | ||
| 162 | const f32 pc = q4; | ||
| 163 | q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period); | ||
| 164 | q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period); | ||
| 165 | q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period); | ||
| 166 | q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period); | ||
| 167 | |||
| 168 | quat.w = q1; | ||
| 169 | quat.xyz[0] = q2; | ||
| 170 | quat.xyz[1] = q3; | ||
| 171 | quat.xyz[2] = q4; | ||
| 172 | quat = quat.Normalized(); | ||
| 173 | } | ||
| 174 | |||
| 175 | std::array<Common::Vec3f, 3> MotionInput::GetOrientation() const { | ||
| 176 | const Common::Quaternion<float> quad{ | ||
| 177 | .xyz = {-quat.xyz[1], -quat.xyz[0], -quat.w}, | ||
| 178 | .w = -quat.xyz[2], | ||
| 179 | }; | ||
| 180 | const std::array<float, 16> matrix4x4 = quad.ToMatrix(); | ||
| 181 | |||
| 182 | return {Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]), | ||
| 183 | Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]), | ||
| 184 | Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10])}; | ||
| 185 | } | ||
| 186 | |||
| 187 | Common::Vec3f MotionInput::GetAcceleration() const { | ||
| 188 | return accel; | ||
| 189 | } | ||
| 190 | |||
| 191 | Common::Vec3f MotionInput::GetGyroscope() const { | ||
| 192 | return gyro; | ||
| 193 | } | ||
| 194 | |||
| 195 | Common::Quaternion<f32> MotionInput::GetQuaternion() const { | ||
| 196 | return quat; | ||
| 197 | } | ||
| 198 | |||
| 199 | Common::Vec3f MotionInput::GetRotations() const { | ||
| 200 | return rotations; | ||
| 201 | } | ||
| 202 | |||
| 203 | void MotionInput::ResetOrientation() { | ||
| 204 | if (!reset_enabled || only_accelerometer) { | ||
| 205 | return; | ||
| 206 | } | ||
| 207 | if (!IsMoving(0.5f) && accel.z <= -0.9f) { | ||
| 208 | ++reset_counter; | ||
| 209 | if (reset_counter > 900) { | ||
| 210 | quat.w = 0; | ||
| 211 | quat.xyz[0] = 0; | ||
| 212 | quat.xyz[1] = 0; | ||
| 213 | quat.xyz[2] = -1; | ||
| 214 | SetOrientationFromAccelerometer(); | ||
| 215 | integral_error = {}; | ||
| 216 | reset_counter = 0; | ||
| 217 | } | ||
| 218 | } else { | ||
| 219 | reset_counter = 0; | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | void MotionInput::SetOrientationFromAccelerometer() { | ||
| 224 | int iterations = 0; | ||
| 225 | const f32 sample_period = 0.015f; | ||
| 226 | |||
| 227 | const auto normal_accel = accel.Normalized(); | ||
| 228 | |||
| 229 | while (!IsCalibrated(0.01f) && ++iterations < 100) { | ||
| 230 | // Short name local variable for readability | ||
| 231 | f32 q1 = quat.w; | ||
| 232 | f32 q2 = quat.xyz[0]; | ||
| 233 | f32 q3 = quat.xyz[1]; | ||
| 234 | f32 q4 = quat.xyz[2]; | ||
| 235 | |||
| 236 | Common::Vec3f rad_gyro; | ||
| 237 | const f32 ax = -normal_accel.x; | ||
| 238 | const f32 ay = normal_accel.y; | ||
| 239 | const f32 az = -normal_accel.z; | ||
| 240 | |||
| 241 | // Estimated direction of gravity | ||
| 242 | const f32 vx = 2.0f * (q2 * q4 - q1 * q3); | ||
| 243 | const f32 vy = 2.0f * (q1 * q2 + q3 * q4); | ||
| 244 | const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; | ||
| 245 | |||
| 246 | // Error is cross product between estimated direction and measured direction of gravity | ||
| 247 | const Common::Vec3f new_real_error = { | ||
| 248 | az * vx - ax * vz, | ||
| 249 | ay * vz - az * vy, | ||
| 250 | ax * vy - ay * vx, | ||
| 251 | }; | ||
| 252 | |||
| 253 | derivative_error = new_real_error - real_error; | ||
| 254 | real_error = new_real_error; | ||
| 255 | |||
| 256 | rad_gyro += 10.0f * kp * real_error; | ||
| 257 | rad_gyro += 5.0f * ki * integral_error; | ||
| 258 | rad_gyro += 10.0f * kd * derivative_error; | ||
| 259 | |||
| 260 | const f32 gx = rad_gyro.y; | ||
| 261 | const f32 gy = rad_gyro.x; | ||
| 262 | const f32 gz = rad_gyro.z; | ||
| 263 | |||
| 264 | // Integrate rate of change of quaternion | ||
| 265 | const f32 pa = q2; | ||
| 266 | const f32 pb = q3; | ||
| 267 | const f32 pc = q4; | ||
| 268 | q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period); | ||
| 269 | q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period); | ||
| 270 | q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period); | ||
| 271 | q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period); | ||
| 272 | |||
| 273 | quat.w = q1; | ||
| 274 | quat.xyz[0] = q2; | ||
| 275 | quat.xyz[1] = q3; | ||
| 276 | quat.xyz[2] = q4; | ||
| 277 | quat = quat.Normalized(); | ||
| 278 | } | ||
| 279 | } | ||
| 280 | } // namespace Core::HID | ||
diff --git a/src/core/hid/motion_input.h b/src/core/hid/motion_input.h new file mode 100644 index 000000000..5b5b420bb --- /dev/null +++ b/src/core/hid/motion_input.h | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/quaternion.h" | ||
| 9 | #include "common/vector_math.h" | ||
| 10 | |||
| 11 | namespace Core::HID { | ||
| 12 | |||
| 13 | class MotionInput { | ||
| 14 | public: | ||
| 15 | explicit MotionInput(); | ||
| 16 | |||
| 17 | MotionInput(const MotionInput&) = default; | ||
| 18 | MotionInput& operator=(const MotionInput&) = default; | ||
| 19 | |||
| 20 | MotionInput(MotionInput&&) = default; | ||
| 21 | MotionInput& operator=(MotionInput&&) = default; | ||
| 22 | |||
| 23 | void SetPID(f32 new_kp, f32 new_ki, f32 new_kd); | ||
| 24 | void SetAcceleration(const Common::Vec3f& acceleration); | ||
| 25 | void SetGyroscope(const Common::Vec3f& gyroscope); | ||
| 26 | void SetQuaternion(const Common::Quaternion<f32>& quaternion); | ||
| 27 | void SetGyroDrift(const Common::Vec3f& drift); | ||
| 28 | void SetGyroThreshold(f32 threshold); | ||
| 29 | |||
| 30 | void EnableReset(bool reset); | ||
| 31 | void ResetRotations(); | ||
| 32 | |||
| 33 | void UpdateRotation(u64 elapsed_time); | ||
| 34 | void UpdateOrientation(u64 elapsed_time); | ||
| 35 | |||
| 36 | [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const; | ||
| 37 | [[nodiscard]] Common::Vec3f GetAcceleration() const; | ||
| 38 | [[nodiscard]] Common::Vec3f GetGyroscope() const; | ||
| 39 | [[nodiscard]] Common::Vec3f GetRotations() const; | ||
| 40 | [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const; | ||
| 41 | |||
| 42 | [[nodiscard]] bool IsMoving(f32 sensitivity) const; | ||
| 43 | [[nodiscard]] bool IsCalibrated(f32 sensitivity) const; | ||
| 44 | |||
| 45 | private: | ||
| 46 | void ResetOrientation(); | ||
| 47 | void SetOrientationFromAccelerometer(); | ||
| 48 | |||
| 49 | // PID constants | ||
| 50 | f32 kp; | ||
| 51 | f32 ki; | ||
| 52 | f32 kd; | ||
| 53 | |||
| 54 | // PID errors | ||
| 55 | Common::Vec3f real_error; | ||
| 56 | Common::Vec3f integral_error; | ||
| 57 | Common::Vec3f derivative_error; | ||
| 58 | |||
| 59 | // Quaternion containing the device orientation | ||
| 60 | Common::Quaternion<f32> quat{{0.0f, 0.0f, -1.0f}, 0.0f}; | ||
| 61 | |||
| 62 | // Number of full rotations in each axis | ||
| 63 | Common::Vec3f rotations; | ||
| 64 | |||
| 65 | // Acceleration vector measurement in G force | ||
| 66 | Common::Vec3f accel; | ||
| 67 | |||
| 68 | // Gyroscope vector measurement in radians/s. | ||
| 69 | Common::Vec3f gyro; | ||
| 70 | |||
| 71 | // Vector to be substracted from gyro measurements | ||
| 72 | Common::Vec3f gyro_drift; | ||
| 73 | |||
| 74 | // Minimum gyro amplitude to detect if the device is moving | ||
| 75 | f32 gyro_threshold = 0.0f; | ||
| 76 | |||
| 77 | // Number of invalid sequential data | ||
| 78 | u32 reset_counter = 0; | ||
| 79 | |||
| 80 | // If the provided data is invalid the device will be autocalibrated | ||
| 81 | bool reset_enabled = true; | ||
| 82 | |||
| 83 | // Use accelerometer values to calculate position | ||
| 84 | bool only_accelerometer = true; | ||
| 85 | }; | ||
| 86 | |||
| 87 | } // namespace Core::HID | ||